Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HIP-122 restrict boosting from poc bucket #836

Merged
merged 23 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
06d655e
Store coverage points received by boosting separately
michaeldjeffrey Jun 26, 2024
5bbdeda
Add struct for containing allocated Rewards for Data transfer and POC
michaeldjeffrey Jun 27, 2024
b9e360b
Move calucating reward shares for poc to coverage point calculator
michaeldjeffrey Jun 27, 2024
29a4351
Correct points for share calculation alway includes multipliers
michaeldjeffrey Jun 27, 2024
97d44eb
Use the AllocatedRewardShare struct for calculating unallocated data …
michaeldjeffrey Jun 27, 2024
64c9c0f
Add caller to timeout message for file sink during debugging
michaeldjeffrey Jun 27, 2024
78ca743
Fix first hex_boosting intregration test
michaeldjeffrey Jun 27, 2024
68ab48a
Fix second hex_boosting integration test
michaeldjeffrey Jun 27, 2024
eeebb65
Fix third hex_boosting integration test
michaeldjeffrey Jun 27, 2024
63eb9eb
Fix fourth hex_boosting integration test
michaeldjeffrey Jun 28, 2024
c96c9a1
Overview all changed tests
michaeldjeffrey Jun 28, 2024
870e653
Move reward share calculation where it belongs
michaeldjeffrey Jun 28, 2024
a329c82
Add doc for CoveragePoints::coverage_points
michaeldjeffrey Jun 28, 2024
192e2a5
remove whitespace
michaeldjeffrey Jun 28, 2024
4c92270
Unwrap calculating expected rewards in tests
michaeldjeffrey Jun 28, 2024
cc7a514
Update formatting of comment for Doc.rs readability
michaeldjeffrey Jul 1, 2024
758cb41
Fix broken doclink
michaeldjeffrey Jul 1, 2024
5a41baf
fully expose `HexPoints` members
michaeldjeffrey Jul 1, 2024
d9ae734
remove public from helper member
michaeldjeffrey Jul 3, 2024
2b021e0
rename points -> shares for poc rewards
michaeldjeffrey Jul 3, 2024
406cd4e
coverage_points() -> coverage_points_v1() to call out location differ…
michaeldjeffrey Jul 3, 2024
d1b8908
Round POC rewards individually
michaeldjeffrey Jul 8, 2024
002d2ac
Round POC rewards individually
michaeldjeffrey Jul 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 55 additions & 11 deletions coverage_point_calculator/src/hexes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,46 @@ use rust_decimal_macros::dec;

use crate::{BoostedHexStatus, RadioType, Result};

/// Breakdown of points for a hex.
///
/// Example:
/// - Outdoor Wifi
/// - 1 hex boosted at `5x`
/// - Rank 2
/// - Assignment: `AAA`
/// <pre>
/// SplitPoints {
/// modeled: 16,
/// base: 8,
/// boosted: 32
/// }
/// </pre>
/// Rank 2 splits modeled points in half.
/// Boost at `5x` adds 32 points `( (8 * 5) - 8 )`
#[derive(Debug, Default, Clone)]
pub struct HexPoints {
michaeldjeffrey marked this conversation as resolved.
Show resolved Hide resolved
/// Default points received for hex
///
/// (RadioType, SignalLevel) points
///
/// This is a convenience field for debugging, hexes can reach similar
/// values through different means, it helps to know the starting value.
modeled: Decimal,
/// Points including Coverage affected multipliers
///
/// modeled + (Rank * Assignment)
pub base: Decimal,
/// Points _over_ normal received from hex boosting.
///
/// (base * Boost multiplier) - base
pub boosted: Decimal,
}

#[derive(Debug, Clone)]
pub struct CoveredHex {
pub hex: hextree::Cell,
/// Default points received from (RadioType, SignalLevel) pair.
pub base_coverage_points: Decimal,
michaeldjeffrey marked this conversation as resolved.
Show resolved Hide resolved
/// Coverage points including assignment, rank, and boosted hex multipliers.
pub calculated_coverage_points: Decimal,
/// Breakdown of points for a hex
pub points: HexPoints,
/// Oracle boosted Assignments
pub assignments: HexAssignments,
pub assignment_multiplier: Decimal,
Expand All @@ -32,7 +65,7 @@ pub(crate) fn clean_covered_hexes(
let covered_hexes = ranked_coverage
.into_iter()
.map(|ranked| {
let base_coverage_points = radio_type.base_coverage_points(&ranked.signal_level)?;
let modeled_coverage_points = radio_type.base_coverage_points(&ranked.signal_level)?;
let rank_multiplier = radio_type.rank_multiplier(ranked.rank);

let boosted_multiplier = if boosted_hex_status.is_eligible() {
Expand All @@ -49,15 +82,23 @@ pub(crate) fn clean_covered_hexes(
ranked.assignments.boosting_multiplier()
};

let calculated_coverage_points = base_coverage_points
let base_coverage_points =
modeled_coverage_points * assignment_multiplier * rank_multiplier;

let calculated_coverage_points = modeled_coverage_points
* assignment_multiplier
* rank_multiplier
* boosted_multiplier.unwrap_or(dec!(1));

let boosted_coverage_points = calculated_coverage_points - base_coverage_points;

Ok(CoveredHex {
hex: ranked.hex,
base_coverage_points,
calculated_coverage_points,
points: HexPoints {
modeled: modeled_coverage_points,
base: base_coverage_points,
boosted: boosted_coverage_points,
},
assignments: ranked.assignments,
assignment_multiplier,
rank: ranked.rank,
Expand All @@ -70,11 +111,14 @@ pub(crate) fn clean_covered_hexes(
Ok(covered_hexes)
}

pub(crate) fn calculated_coverage_points(covered_hexes: &[CoveredHex]) -> Decimal {
pub(crate) fn calculated_coverage_points(covered_hexes: &[CoveredHex]) -> HexPoints {
covered_hexes
.iter()
.map(|hex| hex.calculated_coverage_points)
.sum()
.fold(HexPoints::default(), |acc, hex| HexPoints {
modeled: acc.modeled + hex.points.modeled,
base: acc.base + hex.points.base,
boosted: acc.boosted + hex.points.boosted,
})
}

#[cfg(test)]
Expand Down
127 changes: 86 additions & 41 deletions coverage_point_calculator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//! place to start.
//!
//! ## Important Fields
//! - [CoveredHex::base_coverage_points]
//! - [CoveredHex::points]
//! - [HIP-74][modeled-coverage]
//! - reduced cbrs radio coverage points [HIP-113][cbrs-experimental]
//!
Expand Down Expand Up @@ -55,7 +55,7 @@
//! [boosted-hex-restriction]: https://github.com/helium/oracles/pull/808
//!
pub use crate::{
hexes::CoveredHex,
hexes::{CoveredHex, HexPoints},
location::LocationTrust,
speedtest::{BytesPs, Speedtest, SpeedtestTier},
};
Expand Down Expand Up @@ -88,22 +88,29 @@ pub enum Error {
///
/// - When a radio is not eligible for boosted hex rewards, [CoveragePoints::covered_hexes] will
/// have no boosted_multiplier values.
///
/// #### Terminology
///
/// *Points*
///
/// The value provided from covering hexes. These include hex related modifiers.
/// - Rank
/// - Oracle boosting.
///
/// *Multipliers*
///
/// Values relating to a radio that modify it's points.
/// - Location Trust
/// - Speedtest
///
/// *Shares*
///
/// The result of multiplying Points and Multipliers together.
///
#[derive(Debug, Clone)]
pub struct CoveragePoints {
/// Total Rewards Shares earned by the Radio.
///
/// Includes Coverage and Backhaul.
/// Hex Coverage points * location trust multiplier * speedtest trust multiplier
pub reward_shares: Decimal,
/// Total Points of Coverage for a Radio.
///
/// Does not include Backhaul.
/// Hex coverage points * location trust multiplier
pub total_coverage_points: Decimal,
/// Coverage Points collected from each Covered Hex
///
/// Before location trust multiplier is applied.
pub hex_coverage_points: Decimal,
/// Breakdown of coverage points by source
pub coverage_points: HexPoints,
/// Location Trust Multiplier, maximum of 1
///
/// Coverage trust of a Radio
Expand Down Expand Up @@ -147,13 +154,8 @@ impl CoveragePoints {
let speedtests = speedtest::clean_speedtests(speedtests);
let speedtest_multiplier = speedtest::multiplier(&speedtests);

let reward_shares = hex_coverage_points * location_trust_multiplier * speedtest_multiplier;
let total_coverage_points = hex_coverage_points * location_trust_multiplier;

Ok(CoveragePoints {
reward_shares,
total_coverage_points,
hex_coverage_points,
coverage_points: hex_coverage_points,
location_trust_multiplier,
speedtest_multiplier,
radio_type,
Expand All @@ -164,6 +166,49 @@ impl CoveragePoints {
covered_hexes,
})
}

/// Accumulated points related only to coverage.
/// (Hex * Rank * Assignment) * Location Trust
/// Used for reporting.
///
/// NOTE:
/// Coverage Points includes Location Trust multiplier. In a future version,
/// coverage points will refer only to points received by covering a hex,
/// and multipliers like location trust will be applied later to reach a
/// value referred to as "shares".
///
/// Ref:
/// https://github.com/helium/proto/blob/master/src/service/poc_mobile.proto
/// `message radio_reward`
pub fn coverage_points_v1(&self) -> Decimal {
let total_coverage_points = self.coverage_points.base + self.boosted_points();
total_coverage_points * self.location_trust_multiplier
}

/// Accumulated points related to entire radio.
/// coverage points * speedtest
/// Used in calculating rewards
pub fn total_shares(&self) -> Decimal {
self.total_base_shares() + self.total_boosted_shares()
}

/// Useful for grabbing only base points when calculating reward shares
pub fn total_base_shares(&self) -> Decimal {
self.coverage_points.base * self.speedtest_multiplier * self.location_trust_multiplier
}

/// Useful for grabbing only boost points when calculating reward shares
pub fn total_boosted_shares(&self) -> Decimal {
self.boosted_points() * self.speedtest_multiplier * self.location_trust_multiplier
}

fn boosted_points(&self) -> Decimal {
match self.boosted_hex_eligibility {
BoostedHexStatus::Eligible => self.coverage_points.boosted,
BoostedHexStatus::WifiLocationScoreBelowThreshold(_) => dec!(0),
BoostedHexStatus::RadioThresholdNotMet => dec!(0),
}
}
}

#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -314,7 +359,7 @@ mod tests {

// A Hex with the worst possible oracle boosting assignment.
// The boosting assignment multiplier will be 1x when the hex is provider boosted.
assert_eq!(expected_points, wifi.total_coverage_points);
assert_eq!(expected_points, wifi.coverage_points_v1());
}

#[test]
Expand Down Expand Up @@ -345,12 +390,12 @@ mod tests {
// Radio meeting the threshold is eligible for boosted hexes.
// Boosted hex provides radio with more than base_points.
let verified_wifi = calculate_wifi(RadioThreshold::Verified);
assert_eq!(base_points * dec!(5), verified_wifi.total_coverage_points);
assert_eq!(base_points * dec!(5), verified_wifi.coverage_points_v1());

// Radio not meeting the threshold is not eligible for boosted hexes.
// Boost from hex is not applied, radio receives base points.
let unverified_wifi = calculate_wifi(RadioThreshold::Unverified);
assert_eq!(base_points, unverified_wifi.total_coverage_points);
assert_eq!(base_points, unverified_wifi.coverage_points_v1());
}

#[test]
Expand Down Expand Up @@ -382,13 +427,13 @@ mod tests {
// Boosted hex provides radio with more than base_points.
let trusted_wifi = calculate_wifi(location_trust_with_scores(&[dec!(1), dec!(1)]));
assert!(trusted_wifi.location_trust_multiplier > dec!(0.75));
assert!(trusted_wifi.total_coverage_points > base_points);
assert!(trusted_wifi.coverage_points_v1() > base_points);

// Radio with poor trust score is not eligible for boosted hexes.
// Boost from hex is not applied, and points are further lowered by poor trust score.
let untrusted_wifi = calculate_wifi(location_trust_with_scores(&[dec!(0.1), dec!(0.2)]));
assert!(untrusted_wifi.location_trust_multiplier < dec!(0.75));
assert!(untrusted_wifi.total_coverage_points < base_points);
assert!(untrusted_wifi.coverage_points_v1() < base_points);
}

#[test]
Expand Down Expand Up @@ -419,7 +464,7 @@ mod tests {
let indoor_cbrs = calculate_indoor_cbrs(speedtest_maximum());
assert_eq!(
base_coverage_points * SpeedtestTier::Good.multiplier(),
indoor_cbrs.reward_shares
indoor_cbrs.total_shares()
);

let indoor_cbrs = calculate_indoor_cbrs(vec![
Expand All @@ -428,7 +473,7 @@ mod tests {
]);
assert_eq!(
base_coverage_points * SpeedtestTier::Acceptable.multiplier(),
indoor_cbrs.reward_shares
indoor_cbrs.total_shares()
);

let indoor_cbrs = calculate_indoor_cbrs(vec![
Expand All @@ -437,7 +482,7 @@ mod tests {
]);
assert_eq!(
base_coverage_points * SpeedtestTier::Degraded.multiplier(),
indoor_cbrs.reward_shares
indoor_cbrs.total_shares()
);

let indoor_cbrs = calculate_indoor_cbrs(vec![
Expand All @@ -446,7 +491,7 @@ mod tests {
]);
assert_eq!(
base_coverage_points * SpeedtestTier::Poor.multiplier(),
indoor_cbrs.reward_shares
indoor_cbrs.total_shares()
);

let indoor_cbrs = calculate_indoor_cbrs(vec![
Expand All @@ -455,7 +500,7 @@ mod tests {
]);
assert_eq!(
base_coverage_points * SpeedtestTier::Fail.multiplier(),
indoor_cbrs.reward_shares
indoor_cbrs.total_shares()
);
}

Expand Down Expand Up @@ -526,7 +571,7 @@ mod tests {
)
.expect("indoor cbrs");

assert_eq!(dec!(1073), indoor_cbrs.total_coverage_points);
assert_eq!(dec!(1073), indoor_cbrs.coverage_points_v1());
}

#[rstest]
Expand Down Expand Up @@ -556,7 +601,7 @@ mod tests {
)
.expect("outdoor wifi");

assert_eq!(expected_points, outdoor_wifi.total_coverage_points);
assert_eq!(expected_points, outdoor_wifi.coverage_points_v1());
}

#[rstest]
Expand Down Expand Up @@ -605,7 +650,7 @@ mod tests {
)
.expect("indoor wifi");

assert_eq!(expected_points, indoor_wifi.total_coverage_points);
assert_eq!(expected_points, indoor_wifi.coverage_points_v1());
}

#[test]
Expand All @@ -630,7 +675,7 @@ mod tests {

// Location trust scores is 1/4
// (0.1 + 0.2 + 0.3 + 0.4) / 4
assert_eq!(dec!(100), indoor_wifi.total_coverage_points);
assert_eq!(dec!(100), indoor_wifi.coverage_points_v1());
}

#[test]
Expand Down Expand Up @@ -666,7 +711,7 @@ mod tests {

// The hex with a low signal_level is boosted to the same level as a
// signal_level of High.
assert_eq!(dec!(800), indoor_wifi.total_coverage_points);
assert_eq!(dec!(800), indoor_wifi.coverage_points_v1());
}

#[rstest]
Expand Down Expand Up @@ -695,7 +740,7 @@ mod tests {
)
.expect("outdoor cbrs");

assert_eq!(expected, outdoor_cbrs.total_coverage_points);
assert_eq!(expected, outdoor_cbrs.coverage_points_v1());
}

#[rstest]
Expand All @@ -722,7 +767,7 @@ mod tests {
)
.expect("indoor cbrs");

assert_eq!(expected, indoor_cbrs.total_coverage_points);
assert_eq!(expected, indoor_cbrs.coverage_points_v1());
}

#[rstest]
Expand Down Expand Up @@ -751,7 +796,7 @@ mod tests {
)
.expect("indoor cbrs");

assert_eq!(expected, outdoor_wifi.total_coverage_points);
assert_eq!(expected, outdoor_wifi.coverage_points_v1());
}

#[rstest]
Expand All @@ -778,7 +823,7 @@ mod tests {
)
.expect("indoor wifi");

assert_eq!(expected, indoor_wifi.total_coverage_points);
assert_eq!(expected, indoor_wifi.coverage_points_v1());
}

fn hex_location() -> hextree::Cell {
Expand Down
Loading