Skip to content

Commit

Permalink
Merge pull request #853 from helium/kurotych/speedtest_avg_radio_rewa…
Browse files Browse the repository at this point in the history
…rd_v2

[ORA-426] Add speedtest average to radio_reward_v2
  • Loading branch information
kurotych authored Aug 15, 2024
2 parents 8dc6c15 + 601b4f1 commit be53f87
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 14 deletions.
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions coverage_point_calculator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ pub struct CoveragePoints {
///
/// Coverage trust of a Radio
pub location_trust_multiplier: Decimal,
/// Speedtest Mulitplier, maximum of 1
/// Speedtest Multiplier, maximum of 1
///
/// Backhaul of a Radio
pub speedtest_multiplier: Decimal,
Expand All @@ -135,12 +135,14 @@ pub struct CoveragePoints {
pub service_provider_boosted_reward_eligibility: SPBoostedRewardEligibility,
/// Derived Eligibility for Boosted Hex Rewards
pub boosted_hex_eligibility: BoostedHexStatus,
/// Speedtests used in calculcation
/// Speedtests used in calculation
pub speedtests: Vec<Speedtest>,
/// Location Trust Scores used in calculation
pub location_trust_scores: Vec<LocationTrust>,
/// Covered Hexes used in calculation
pub covered_hexes: Vec<CoveredHex>,
/// Average speedtest used in calculation
pub speedtest_avg: Speedtest,
}

impl CoveragePoints {
Expand All @@ -165,12 +167,13 @@ impl CoveragePoints {
let hex_coverage_points = hexes::calculated_coverage_points(&covered_hexes);

let speedtests = speedtest::clean_speedtests(speedtests);
let speedtest_multiplier = speedtest::multiplier(&speedtests);
let (speedtest_multiplier, speedtest_avg) = speedtest::multiplier(&speedtests);

Ok(CoveragePoints {
coverage_points: hex_coverage_points,
location_trust_multiplier,
speedtest_multiplier,
speedtest_avg,
radio_type,
service_provider_boosted_reward_eligibility,
boosted_hex_eligibility: boost_eligibility,
Expand Down
32 changes: 25 additions & 7 deletions coverage_point_calculator/src/speedtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@ pub(crate) fn clean_speedtests(speedtests: Vec<Speedtest>) -> Vec<Speedtest> {
cleaned
}

pub(crate) fn multiplier(speedtests: &[Speedtest]) -> Decimal {
// Returns multiplier and speedtest (average)
pub(crate) fn multiplier(speedtests: &[Speedtest]) -> (Decimal, Speedtest) {
let avg = Speedtest::avg(speedtests);

if speedtests.len() < MIN_REQUIRED_SPEEDTEST_SAMPLES {
return dec!(0);
return (dec!(0), avg);
}

let avg = Speedtest::avg(speedtests);
avg.multiplier()
(avg.multiplier(), avg)
}

#[derive(Debug, Clone, Copy, PartialEq)]
Expand Down Expand Up @@ -97,6 +99,14 @@ impl Speedtest {
}

pub fn avg(speedtests: &[Self]) -> Self {
if speedtests.is_empty() {
return Self {
upload_speed: BytesPs::new(0),
download_speed: BytesPs::new(0),
latency_millis: 0,
timestamp: Utc::now(),
};
}
let mut download = 0;
let mut upload = 0;
let mut latency = 0;
Expand Down Expand Up @@ -197,6 +207,14 @@ mod tests {
assert_eq!(Fail, SpeedtestTier::from_latency(101));
}

#[test]
fn speedtest_avg_should_not_panic() {
let avg = Speedtest::avg(&[]);
assert_eq!(avg.upload_speed, BytesPs(0));
assert_eq!(avg.download_speed, BytesPs(0));
assert_eq!(avg.latency_millis, 0);
}

#[test]
fn minimum_required_speedtests_provided_for_multiplier_above_zero() {
let speedtest = Speedtest {
Expand All @@ -209,11 +227,11 @@ mod tests {

assert_eq!(
dec!(0),
multiplier(&speedtests(MIN_REQUIRED_SPEEDTEST_SAMPLES - 1))
multiplier(&speedtests(MIN_REQUIRED_SPEEDTEST_SAMPLES - 1)).0
);
assert_eq!(
dec!(1),
multiplier(&speedtests(MIN_REQUIRED_SPEEDTEST_SAMPLES))
multiplier(&speedtests(MIN_REQUIRED_SPEEDTEST_SAMPLES)).0
);
}

Expand Down Expand Up @@ -264,7 +282,7 @@ mod tests {
]);

// Old speedtests should be unused
assert_eq!(dec!(1), multiplier(&speedtests));
assert_eq!(dec!(1), multiplier(&speedtests).0);
}

#[test]
Expand Down
95 changes: 95 additions & 0 deletions mobile_verifier/src/reward_shares.rs
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@ pub fn coverage_point_to_mobile_reward_share(
speedtest_multiplier: Some(coverage_points.speedtest_multiplier.proto_decimal()),
boosted_hex_status: coverage_points.proto_boosted_hex_status().into(),
covered_hexes: coverage_points.proto_covered_hexes(),
speedtest_average: Some(coverage_points.proto_speedtest_avg()),
});

let base = proto::MobileRewardShare {
Expand Down Expand Up @@ -1231,6 +1232,100 @@ mod test {
}]
}

#[tokio::test]
async fn check_speedtest_avg_in_radio_reward_v2() {
let owner1: PublicKeyBinary = "112NqN2WWMwtK29PMzRby62fDydBJfsCLkCAf392stdok48ovNT6"
.parse()
.expect("failed owner1 parse");
let gw1: PublicKeyBinary = "112NqN2WWMwtK29PMzRby62fDydBJfsCLkCAf392stdok48ovNT6"
.parse()
.expect("failed gw1 parse");
let mut owners = HashMap::new();
owners.insert(gw1.clone(), owner1.clone());
let c1 = "P27-SCE4255W2107CW5000015".to_string();
let cov_obj_1 = Uuid::new_v4();

let now = Utc::now();
let timestamp = now - Duration::minutes(20);

let heartbeat_rewards = vec![HeartbeatReward {
cbsd_id: Some(c1.clone()),
hotspot_key: gw1.clone(),
coverage_object: cov_obj_1,
cell_type: CellType::from_cbsd_id(&c1).unwrap(),
distances_to_asserted: None,
trust_score_multipliers: vec![dec!(1.0)],
}]
.into_iter()
.map(Ok)
.collect::<Vec<Result<HeartbeatReward, _>>>();

let mut hex_coverage = HashMap::new();
hex_coverage.insert(
(OwnedKeyType::from(c1.clone()), cov_obj_1),
simple_hex_coverage(&c1, 0x8a1fb46622dffff),
);

let st1 = Speedtest {
report: CellSpeedtest {
pubkey: gw1.clone(),
timestamp,
upload_speed: bytes_per_s(10),
download_speed: bytes_per_s(100),
latency: 50,
serial: "".to_string(),
},
};
let st2 = Speedtest {
report: CellSpeedtest {
pubkey: gw1.clone(),
timestamp,
upload_speed: bytes_per_s(20),
download_speed: bytes_per_s(200),
latency: 100,
serial: "".to_string(),
},
};

let gw1_speedtests = vec![st1, st2];

let gw1_average = SpeedtestAverage::from(gw1_speedtests);
let mut averages = HashMap::new();
averages.insert(gw1.clone(), gw1_average);

let speedtest_avgs = SpeedtestAverages { averages };

let duration = Duration::hours(1);
let epoch = (now - duration)..now;
let reward_shares = DataTransferAndPocAllocatedRewardBuckets::new_poc_only(&epoch);

let epoch = (now - Duration::hours(1))..now;
let (_reward_amount, _mobile_reward_v1, mobile_reward_v2) = CoverageShares::new(
&hex_coverage,
stream::iter(heartbeat_rewards),
&speedtest_avgs,
&BoostedHexes::default(),
&BoostedHexEligibility::default(),
&epoch,
)
.await
.unwrap()
.into_rewards(reward_shares, &epoch)
.unwrap()
.1
.next()
.unwrap();

let radio_reward = match mobile_reward_v2.reward {
Some(MobileReward::RadioRewardV2(radio_reward)) => radio_reward,
_ => unreachable!(),
};
let speedtest_avg = radio_reward.speedtest_average.unwrap();
assert_eq!(speedtest_avg.upload_speed_bps, bytes_per_s(15));
assert_eq!(speedtest_avg.download_speed_bps, bytes_per_s(150));
assert_eq!(speedtest_avg.latency_ms, 75);
}

/// Test to ensure that different speedtest averages correctly afferct reward shares.
#[tokio::test]
async fn ensure_speedtest_averages_affect_reward_shares() {
Expand Down
11 changes: 11 additions & 0 deletions mobile_verifier/src/reward_shares/radio_reward_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ impl ToProtoDecimal for rust_decimal::Decimal {
pub trait RadioRewardV2Ext {
fn proto_location_trust_scores(&self) -> Vec<LocationTrustScore>;
fn proto_speedtests(&self) -> Vec<Speedtest>;
fn proto_speedtest_avg(&self) -> Speedtest;
fn proto_boosted_hex_status(&self) -> BoostedHexStatus;
fn proto_covered_hexes(&self) -> Vec<CoveredHex>;
}
Expand All @@ -34,6 +35,16 @@ impl RadioRewardV2Ext for coverage_point_calculator::CoveragePoints {
.collect()
}

fn proto_speedtest_avg(&self) -> Speedtest {
let st = self.speedtest_avg;
helium_proto::services::poc_mobile::Speedtest {
upload_speed_bps: st.upload_speed.as_bps(),
download_speed_bps: st.download_speed.as_bps(),
latency_ms: st.latency_millis,
timestamp: st.timestamp.encode_timestamp(),
}
}

fn proto_speedtests(&self) -> Vec<Speedtest> {
self.speedtests
.iter()
Expand Down

0 comments on commit be53f87

Please sign in to comment.