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-119: Location Trust Score from maximum asserted distance difference #840

Merged
merged 19 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
4616f1b
Calculate Location Trust Score from asserted distance in heartbeat
michaeldjeffrey Jul 8, 2024
f48cbb2
Remove use of max_distance_to_asserted
michaeldjeffrey Jul 9, 2024
8e9b254
Update test for increased allowable location trust multipliers
michaeldjeffrey Jul 9, 2024
48167e5
typo in test name
michaeldjeffrey Jul 9, 2024
5f3d97c
Use lowest possible location trust multiplier value for bad case scen…
michaeldjeffrey Jul 9, 2024
63d2881
Pass location trust to determine boost eligibility
michaeldjeffrey Jul 9, 2024
41222c0
namespace location consts to provide more context
michaeldjeffrey Jul 9, 2024
480690f
make service provider boosting module
michaeldjeffrey Jul 9, 2024
f16bfa8
Remove trust score tests that expect score modification from boosting
michaeldjeffrey Jul 9, 2024
159b0b3
Test Boosting does not apply when too far away
michaeldjeffrey Jul 9, 2024
e5b68cd
consolidate seeding heartbeats v1 and v3
michaeldjeffrey Jul 9, 2024
fbfeb65
Test being too far from asserted location removes service provider bo…
michaeldjeffrey Jul 9, 2024
051f289
add hip-125 mentions in the docs
michaeldjeffrey Jul 10, 2024
adb6439
Try to link to relevant hips when possible
michaeldjeffrey Jul 10, 2024
3d7a1cd
Active boosting ineligibility takes precendence over passive ineligib…
michaeldjeffrey Jul 11, 2024
1a0d007
Merged origin/main into mj/hip-119-location-trust
michaeldjeffrey Jul 16, 2024
dab79b8
Remove answered questions
michaeldjeffrey Jul 16, 2024
c3e8194
New location scores apply to Wifi only
michaeldjeffrey Jul 16, 2024
861bd8f
ServiceProvider -> SP for brevity
michaeldjeffrey Jul 16, 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
144 changes: 119 additions & 25 deletions coverage_point_calculator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
//! - [HIP-98][qos-score]
//! - states 30m requirement for boosted hexes [HIP-107][prevent-gaming]
//! - increase Boosted hex restriction, 30m -> 50m [Pull Request][boosted-hex-restriction]
//! - Maximum Asserted Distance Difference [HIP-119][location-gaming]
//!
//! - [CoveragePoints::speedtest_multiplier]
//! - [HIP-74][modeled-coverage]
Expand All @@ -32,7 +33,7 @@
//!
//! ## Notable Conditions:
//! - [LocationTrust]
//! - If a Radio covers any boosted hexes, [LocationTrust] scores must meet distance requirements, or be degraded.
//! - The average distance to asserted must be <=50m to be eligible for boosted rewards.
//! - CBRS Radio's location is always trusted because of GPS.
//!
//! - [Speedtest]
Expand All @@ -43,6 +44,10 @@
//! - If a Radio is not [BoostedHexStatus::Eligible], boost values are removed before calculations.
//! - If a Hex is boosted by a Provider, the Oracle Assignment multiplier is automatically 1x.
//!
//! - [ServiceProviderBoostedRewardEligibility]
//! - Radio must pass at least 1mb of data from 3 unique phones [HIP-84][provider-boosting]
//! - Service Provider can invalidate boosted rewards of a hotspot [HIP-125][provider-banning]
//!
//! [modeled-coverage]: https://github.com/helium/HIP/blob/main/0074-mobile-poc-modeled-coverage-rewards.md#outdoor-radios
//! [provider-boosting]: https://github.com/helium/HIP/blob/main/0084-service-provider-hex-boosting.md
//! [wifi-aps]: https://github.com/helium/HIP/blob/main/0093-addition-of-wifi-aps-to-mobile-subdao.md
Expand All @@ -53,18 +58,23 @@
//! [cbrs-experimental]: https://github.com/helium/HIP/blob/main/0113-reward-cbrs-as-experimental.md
//! [mobile-poc-blog]: https://docs.helium.com/mobile/proof-of-coverage
//! [boosted-hex-restriction]: https://github.com/helium/oracles/pull/808
//! [location-gaming]: https://github.com/helium/HIP/blob/main/0119-closing-gaming-loopholes-within-the-mobile-network.md
//! [provider-banning]: https://github.com/helium/HIP/blob/main/0125-temporary-anti-gaming-measures-for-boosted-hexes.md
//!
pub use crate::{
hexes::{CoveredHex, HexPoints},
location::LocationTrust,
location::{asserted_distance_to_trust_multiplier, LocationTrust},
service_provider_boosting::ServiceProviderBoostedRewardEligibility,
speedtest::{BytesPs, Speedtest, SpeedtestTier},
};
use coverage_map::SignalLevel;
use rust_decimal::Decimal;
use rust_decimal_macros::dec;
use service_provider_boosting::{MAX_AVERAGE_DISTANCE, MIN_WIFI_TRUST_MULTIPLIER};

mod hexes;
mod location;
mod service_provider_boosting;
mod speedtest;

pub type Result<T = ()> = std::result::Result<T, Error>;
Expand Down Expand Up @@ -138,16 +148,16 @@ impl CoveragePoints {
radio_type: RadioType,
service_provider_boosted_reward_eligibility: ServiceProviderBoostedRewardEligibility,
speedtests: Vec<Speedtest>,
trust_scores: Vec<LocationTrust>,
location_trust_scores: Vec<LocationTrust>,
ranked_coverage: Vec<coverage_map::RankedCoverage>,
) -> Result<CoveragePoints> {
let location_trust_scores = location::clean_trust_scores(trust_scores, &ranked_coverage);
let location_trust_multiplier = location::multiplier(radio_type, &location_trust_scores);

let boost_eligibility = BoostedHexStatus::new(
&radio_type,
radio_type,
location_trust_multiplier,
&service_provider_boosted_reward_eligibility,
&location_trust_scores,
service_provider_boosted_reward_eligibility,
);

let covered_hexes =
Expand Down Expand Up @@ -181,7 +191,7 @@ impl CoveragePoints {
/// value referred to as "shares".
///
/// Ref:
/// https://github.com/helium/proto/blob/master/src/service/poc_mobile.proto
/// <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();
Expand Down Expand Up @@ -209,40 +219,53 @@ impl CoveragePoints {
match self.boosted_hex_eligibility {
BoostedHexStatus::Eligible => self.coverage_points.boosted,
BoostedHexStatus::WifiLocationScoreBelowThreshold(_) => dec!(0),
BoostedHexStatus::AverageAssertedDistanceOverLimit(_) => dec!(0),
BoostedHexStatus::RadioThresholdNotMet => dec!(0),
BoostedHexStatus::ServiceProviderBanned => dec!(0),
}
}
}

#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BoostedHexStatus {
Eligible,
WifiLocationScoreBelowThreshold(Decimal),
AverageAssertedDistanceOverLimit(Decimal),
RadioThresholdNotMet,
ServiceProviderBanned,
}

impl BoostedHexStatus {
fn new(
michaeldjeffrey marked this conversation as resolved.
Show resolved Hide resolved
radio_type: &RadioType,
location_trust_score: Decimal,
service_provider_boosted_reward_eligibility: &ServiceProviderBoostedRewardEligibility,
radio_type: RadioType,
location_trust_multiplier: Decimal,
location_trust_scores: &[LocationTrust],
service_provider_boosted_reward_eligibility: ServiceProviderBoostedRewardEligibility,
) -> Self {
// hip-93: if radio is wifi & location_trust score multiplier < 0.75, no boosting
if radio_type.is_wifi() && location_trust_score < dec!(0.75) {
return Self::WifiLocationScoreBelowThreshold(location_trust_score);
}

// hip-84: if radio has not met minimum data and subscriber thresholds, no boosting
match service_provider_boosted_reward_eligibility {
ServiceProviderBoostedRewardEligibility::Eligible => Self::Eligible,
// hip-125: if radio has been banned by service provider, no boosting
ServiceProviderBoostedRewardEligibility::ServiceProviderBanned => {
Self::ServiceProviderBanned
}
// hip-84: if radio has not met minimum data and subscriber thresholds, no boosting
ServiceProviderBoostedRewardEligibility::RadioThresholdNotMet => {
Self::RadioThresholdNotMet
}
ServiceProviderBoostedRewardEligibility::Eligible => {
// hip-93: if radio is wifi & location_trust score multiplier < 0.75, no boosting
if radio_type.is_wifi() && location_trust_multiplier < MIN_WIFI_TRUST_MULTIPLIER {
return Self::WifiLocationScoreBelowThreshold(location_trust_multiplier);
}

// hip-119: if the average distance to asserted is beyond 50m, no boosting
let average_distance =
location::average_distance(radio_type, location_trust_scores);
if average_distance > MAX_AVERAGE_DISTANCE {
return Self::AverageAssertedDistanceOverLimit(average_distance);
}

Self::Eligible
}
}
}

Expand Down Expand Up @@ -315,13 +338,6 @@ impl RadioType {
}
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ServiceProviderBoostedRewardEligibility {
Eligible,
ServiceProviderBanned,
RadioThresholdNotMet,
}

#[cfg(test)]
mod tests {

Expand Down Expand Up @@ -441,6 +457,42 @@ mod tests {
assert!(untrusted_wifi.coverage_points_v1() < base_points);
}

#[test]
fn hip_119_radio_with_past_50m_from_asserted_receives_no_boosted_hexes() {
let calculate_wifi = |location_trust_scores: Vec<LocationTrust>| {
CoveragePoints::new(
RadioType::IndoorWifi,
ServiceProviderBoostedRewardEligibility::Eligible,
speedtest_maximum(),
location_trust_scores,
vec![RankedCoverage {
hotspot_key: pubkey(),
cbsd_id: None,
hex: hex_location(),
rank: 1,
signal_level: SignalLevel::High,
assignments: assignments_maximum(),
boosted: NonZeroU32::new(5),
}],
)
.expect("indoor wifi with location scores")
};

let base_points = RadioType::IndoorWifi
.base_coverage_points(&SignalLevel::High)
.unwrap();

// Radio with distance to asserted under the limit is eligible for boosted hexes.
// Boosted hex provides radio with more than base_points.
let trusted_wifi = calculate_wifi(location_trust_with_asserted_distance(&[0, 49]));
assert!(trusted_wifi.total_shares() > base_points);

// Radio with distance to asserted over the limit is not eligible for boosted hexes.
// Boost from hex is not applied.
let untrusted_wifi = calculate_wifi(location_trust_with_asserted_distance(&[50, 51]));
assert_eq!(untrusted_wifi.total_shares(), base_points);
}

#[test]
fn speedtests_effect_reward_shares() {
let calculate_indoor_cbrs = |speedtests: Vec<Speedtest>| {
Expand Down Expand Up @@ -831,6 +883,36 @@ mod tests {
assert_eq!(expected, indoor_wifi.coverage_points_v1());
}

#[test]
fn wifi_with_bad_location_boosted_hex_status_prioritizes_service_provider_statuses() {
let bad_location = vec![LocationTrust {
meters_to_asserted: 100,
trust_score: dec!(0.0),
}];

let wifi_bad_trust_score = |sp_status: ServiceProviderBoostedRewardEligibility| {
BoostedHexStatus::new(
RadioType::IndoorWifi,
location::multiplier(RadioType::IndoorWifi, &bad_location),
&bad_location,
sp_status,
)
};

assert_eq!(
wifi_bad_trust_score(ServiceProviderBoostedRewardEligibility::Eligible),
BoostedHexStatus::WifiLocationScoreBelowThreshold(dec!(0)),
);
assert_eq!(
wifi_bad_trust_score(ServiceProviderBoostedRewardEligibility::ServiceProviderBanned),
BoostedHexStatus::ServiceProviderBanned
);
assert_eq!(
wifi_bad_trust_score(ServiceProviderBoostedRewardEligibility::RadioThresholdNotMet),
BoostedHexStatus::RadioThresholdNotMet
);
}

fn hex_location() -> hextree::Cell {
hextree::Cell::from_raw(0x8c2681a3064edff).unwrap()
}
Expand Down Expand Up @@ -896,6 +978,18 @@ mod tests {
.collect()
}

fn location_trust_with_asserted_distance(distances_to_asserted: &[u32]) -> Vec<LocationTrust> {
distances_to_asserted
.to_owned()
.iter()
.copied()
.map(|meters_to_asserted| LocationTrust {
meters_to_asserted,
trust_score: dec!(1.0),
})
.collect()
}

fn pubkey() -> Vec<u8> {
vec![1]
}
Expand Down
Loading