diff --git a/Cargo.lock b/Cargo.lock index c85e2dce6..7958d1363 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1197,7 +1197,7 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "beacon" version = "0.1.0" -source = "git+https://github.com/helium/proto?branch=master#585704c871d32846a6e35d186a08443883545687" +source = "git+https://github.com/helium/proto?branch=master#e4b935efc2d6743d0506198d2208c49540762235" dependencies = [ "base64 0.21.0", "byteorder", @@ -3047,7 +3047,7 @@ dependencies = [ [[package]] name = "helium-proto" version = "0.1.0" -source = "git+https://github.com/helium/proto?branch=master#585704c871d32846a6e35d186a08443883545687" +source = "git+https://github.com/helium/proto?branch=master#e4b935efc2d6743d0506198d2208c49540762235" dependencies = [ "bytes", "prost", diff --git a/file_store/src/cli/dump.rs b/file_store/src/cli/dump.rs index 7175e83b6..6f9f0e494 100644 --- a/file_store/src/cli/dump.rs +++ b/file_store/src/cli/dump.rs @@ -205,7 +205,6 @@ impl Cmd { print_json(&json!({ "cbsd_id": heartbeat.cbsd_id, "pub_key": PublicKey::try_from(heartbeat.pub_key)?, - "reward_multiplier": heartbeat.reward_multiplier, "timestamp": heartbeat.timestamp, "cell_type": heartbeat.cell_type, "validity": heartbeat.validity, diff --git a/file_store/src/heartbeat.rs b/file_store/src/heartbeat.rs index 11510c39f..f61834dfc 100644 --- a/file_store/src/heartbeat.rs +++ b/file_store/src/heartbeat.rs @@ -95,7 +95,6 @@ pub mod cli { pub struct ValidatedHeartbeat { pub cbsd_id: String, pub pub_key: PublicKeyBinary, - pub reward_multiplier: f32, pub timestamp: DateTime, pub cell_type: CellType, pub validity: HeartbeatValidity, @@ -113,7 +112,6 @@ pub mod cli { Ok(Self { cbsd_id: v.cbsd_id.clone(), pub_key: v.pub_key.clone().into(), - reward_multiplier: v.reward_multiplier, timestamp: Utc .timestamp_opt(v.timestamp as i64, 0) .single() diff --git a/mobile_verifier/migrations/24_location_trust_multiplier.sql b/mobile_verifier/migrations/24_location_trust_multiplier.sql new file mode 100644 index 000000000..f80bd4397 --- /dev/null +++ b/mobile_verifier/migrations/24_location_trust_multiplier.sql @@ -0,0 +1,18 @@ +ALTER TABLE wifi_heartbeats ADD COLUMN location_trust_score_multiplier DECIMAL; + +UPDATE wifi_heartbeats SET location_trust_score_multiplier = + CASE WHEN location_validation_timestamp IS NULL THEN + 0.25 + WHEN distance_to_asserted > 100 THEN + 0.25 + ELSE + 1.0 + END; + +ALTER TABLE wifi_heartbeats ALTER COLUMN location_trust_score_multiplier SET NOT NULL; + +ALTER TABLE cbrs_heartbeats ADD COLUMN location_trust_score_multiplier DECIMAL; + +UPDATE cbrs_heartbeats SET location_trust_score_multiplier = 1.0; + +ALTER TABLE cbrs_heartbeats ALTER COLUMN location_trust_score_multiplier SET NOT NULL; diff --git a/mobile_verifier/src/cli/reward_from_db.rs b/mobile_verifier/src/cli/reward_from_db.rs index 7ea923d1d..e484abfef 100644 --- a/mobile_verifier/src/cli/reward_from_db.rs +++ b/mobile_verifier/src/cli/reward_from_db.rs @@ -35,8 +35,7 @@ impl Cmd { let (shutdown_trigger, _shutdown_listener) = triggered::trigger(); let pool = settings.database.connect(env!("CARGO_PKG_NAME")).await?; - let heartbeats = - HeartbeatReward::validated(&pool, &epoch, settings.max_asserted_distance_deviation); + let heartbeats = HeartbeatReward::validated(&pool, &epoch); let speedtest_averages = SpeedtestAverages::aggregate_epoch_averages(epoch.end, &pool).await?; let reward_shares = diff --git a/mobile_verifier/src/cli/server.rs b/mobile_verifier/src/cli/server.rs index cac404c9e..054b072d1 100644 --- a/mobile_verifier/src/cli/server.rs +++ b/mobile_verifier/src/cli/server.rs @@ -114,6 +114,7 @@ impl Cmd { gateway_client.clone(), cbrs_heartbeats, settings.modeled_coverage_start(), + settings.max_asserted_distance_deviation, valid_heartbeats.clone(), seniority_updates.clone(), ); @@ -123,6 +124,7 @@ impl Cmd { gateway_client.clone(), wifi_heartbeats, settings.modeled_coverage_start(), + settings.max_asserted_distance_deviation, valid_heartbeats, seniority_updates, ); @@ -227,7 +229,6 @@ impl Cmd { mobile_rewards, reward_manifests, price_tracker, - settings.max_asserted_distance_deviation, ); // subscriber location diff --git a/mobile_verifier/src/data_session.rs b/mobile_verifier/src/data_session.rs index 6e2edf636..3f4a24679 100644 --- a/mobile_verifier/src/data_session.rs +++ b/mobile_verifier/src/data_session.rs @@ -15,13 +15,19 @@ pub struct DataSessionIngestor { pub pool: PgPool, } +#[derive(Default)] +pub struct HotspotReward { + pub rewardable_bytes: u64, + pub rewardable_dc: u64, +} + #[derive(Clone, Debug)] pub struct ServiceProviderDataSession { pub service_provider: ServiceProvider, pub total_dcs: Decimal, } -pub type HotspotMap = HashMap; +pub type HotspotMap = HashMap; impl DataSessionIngestor { pub fn new(pool: sqlx::Pool) -> Self { @@ -180,7 +186,9 @@ pub async fn data_sessions_to_dc<'a>( tokio::pin!(stream); let mut map = HotspotMap::new(); while let Some(session) = stream.try_next().await? { - *map.entry(session.pub_key).or_default() += session.num_dcs as u64 + let rewards = map.entry(session.pub_key).or_default(); + rewards.rewardable_dc += session.num_dcs as u64; + rewards.rewardable_bytes += session.upload_bytes as u64 + session.download_bytes as u64; } Ok(map) } diff --git a/mobile_verifier/src/heartbeats/cbrs.rs b/mobile_verifier/src/heartbeats/cbrs.rs index 9dd56793e..45f0ab327 100644 --- a/mobile_verifier/src/heartbeats/cbrs.rs +++ b/mobile_verifier/src/heartbeats/cbrs.rs @@ -22,6 +22,7 @@ pub struct HeartbeatDaemon { gateway_info_resolver: GIR, heartbeats: Receiver>, modeled_coverage_start: DateTime, + max_distance_to_asserted: u32, heartbeat_sink: FileSinkClient, seniority_sink: FileSinkClient, } @@ -35,6 +36,7 @@ where gateway_info_resolver: GIR, heartbeats: Receiver>, modeled_coverage_start: DateTime, + max_distance_to_asserted: u32, heartbeat_sink: FileSinkClient, seniority_sink: FileSinkClient, ) -> Self { @@ -43,6 +45,7 @@ where gateway_info_resolver, heartbeats, modeled_coverage_start, + max_distance_to_asserted, heartbeat_sink, seniority_sink, } @@ -111,6 +114,7 @@ where &self.gateway_info_resolver, heartbeats, coverage_objects, + self.max_distance_to_asserted, &epoch, ), heartbeat_cache, diff --git a/mobile_verifier/src/heartbeats/mod.rs b/mobile_verifier/src/heartbeats/mod.rs index 0e0d003da..c7fd15a3f 100644 --- a/mobile_verifier/src/heartbeats/mod.rs +++ b/mobile_verifier/src/heartbeats/mod.rs @@ -17,7 +17,8 @@ use h3o::{CellIndex, LatLng}; use helium_crypto::PublicKeyBinary; use helium_proto::services::poc_mobile as proto; use retainer::Cache; -use rust_decimal::Decimal; +use rust_decimal::{prelude::ToPrimitive, Decimal}; +use rust_decimal_macros::dec; use sqlx::{postgres::PgTypeInfo, Decode, Encode, Postgres, Transaction, Type}; use std::{ops::Range, pin::pin, time}; use uuid::Uuid; @@ -268,7 +269,7 @@ pub struct HeartbeatReward { // cell hb only pub cbsd_id: Option, pub cell_type: CellType, - pub location_trust_multiplier: Decimal, + pub location_trust_score_multiplier: Decimal, pub coverage_object: Uuid, } @@ -292,19 +293,17 @@ impl HeartbeatReward { } pub fn reward_weight(&self) -> Decimal { - self.location_trust_multiplier + self.location_trust_score_multiplier } pub fn validated<'a>( exec: impl sqlx::PgExecutor<'a> + Copy + 'a, epoch: &'a Range>, - max_distance_to_asserted: u32, ) -> impl Stream> + 'a { sqlx::query_as::<_, HeartbeatReward>(include_str!("valid_radios.sql")) .bind(epoch.start) .bind(epoch.end) .bind(MINIMUM_HEARTBEAT_COUNT) - .bind(max_distance_to_asserted as i32) .fetch(exec) } } @@ -313,6 +312,7 @@ impl HeartbeatReward { pub struct ValidatedHeartbeat { pub heartbeat: Heartbeat, pub cell_type: CellType, + pub location_trust_score_multiplier: Decimal, pub distance_to_asserted: Option, pub coverage_summary: Option, pub validity: proto::HeartbeatValidity, @@ -327,24 +327,182 @@ impl ValidatedHeartbeat { self.heartbeat.timestamp.duration_trunc(Duration::hours(1)) } + pub fn new( + heartbeat: Heartbeat, + cell_type: CellType, + location_trust_score_multiplier: Decimal, + distance_to_asserted: Option, + coverage_summary: Option, + validity: proto::HeartbeatValidity, + ) -> Self { + Self { + heartbeat, + cell_type, + location_trust_score_multiplier, + distance_to_asserted, + coverage_summary, + validity, + } + } + + /// Validate a heartbeat in the given epoch. + pub async fn validate( + heartbeat: Heartbeat, + gateway_info_resolver: &impl GatewayResolver, + coverage_cache: &CoverageObjects, + max_distance_to_asserted: u32, + epoch: &Range>, + ) -> anyhow::Result { + let Some(coverage_object) = heartbeat.coverage_object else { + return Ok(Self::new( + heartbeat, + CellType::CellTypeNone, + dec!(0), + None, + None, + proto::HeartbeatValidity::BadCoverageObject, + )); + }; + + let Some(coverage_summary) = coverage_cache + .coverage_summary(&coverage_object, heartbeat.key()) + .await? + else { + return Ok(Self::new( + heartbeat, + CellType::CellTypeNone, + dec!(0), + None, + None, + proto::HeartbeatValidity::NoSuchCoverageObject, + )); + }; + + let cell_type = match heartbeat.hb_type { + HbType::Cbrs => match heartbeat.cbsd_id.as_ref() { + Some(cbsd_id) => match CellType::from_cbsd_id(cbsd_id) { + Some(ty) => ty, + _ => { + return Ok(Self::new( + heartbeat, + CellType::CellTypeNone, + dec!(0), + None, + Some(coverage_summary), + proto::HeartbeatValidity::BadCbsdId, + )); + } + }, + None => { + return Ok(Self::new( + heartbeat, + CellType::CellTypeNone, + dec!(0), + None, + Some(coverage_summary), + proto::HeartbeatValidity::BadCbsdId, + )); + } + }, + HbType::Wifi => { + if coverage_summary.indoor { + CellType::NovaGenericWifiIndoor + } else { + CellType::NovaGenericWifiOutdoor + } + } + }; + + if !heartbeat.operation_mode { + return Ok(Self::new( + heartbeat, + cell_type, + dec!(0), + None, + Some(coverage_summary), + proto::HeartbeatValidity::NotOperational, + )); + } + + if !epoch.contains(&heartbeat.timestamp) { + return Ok(Self::new( + heartbeat, + cell_type, + dec!(0), + None, + Some(coverage_summary), + proto::HeartbeatValidity::HeartbeatOutsideRange, + )); + } + + match gateway_info_resolver + .resolve_gateway(&heartbeat.hotspot_key) + .await? + { + GatewayResolution::GatewayNotFound => Ok(Self::new( + heartbeat, + cell_type, + dec!(0), + None, + Some(coverage_summary), + proto::HeartbeatValidity::GatewayNotFound, + )), + GatewayResolution::GatewayNotAsserted if heartbeat.hb_type == HbType::Wifi => { + Ok(Self::new( + heartbeat, + cell_type, + dec!(0), + None, + Some(coverage_summary), + proto::HeartbeatValidity::GatewayNotAsserted, + )) + } + GatewayResolution::AssertedLocation(location) if heartbeat.hb_type == HbType::Wifi => { + let distance_to_asserted = heartbeat.asserted_distance(location)?; + let location_trust_score_multiplier = + if heartbeat.location_validation_timestamp.is_some() + && distance_to_asserted <= max_distance_to_asserted as i64 + { + dec!(1.0) + } else { + dec!(0.25) + }; + Ok(Self::new( + heartbeat, + cell_type, + location_trust_score_multiplier, + Some(distance_to_asserted), + Some(coverage_summary), + proto::HeartbeatValidity::Valid, + )) + } + _ => Ok(Self::new( + heartbeat, + cell_type, + dec!(1.0), + None, + Some(coverage_summary), + proto::HeartbeatValidity::Valid, + )), + } + } + pub fn validate_heartbeats<'a>( gateway_info_resolver: &'a impl GatewayResolver, heartbeats: impl Stream + 'a, coverage_cache: &'a CoverageObjects, + max_distance_to_asserted: u32, epoch: &'a Range>, ) -> impl Stream> + 'a { heartbeats.then(move |heartbeat| async move { - let (cell_type, distance_to_asserted, coverage_summary, validity) = - validate_heartbeat(&heartbeat, gateway_info_resolver, coverage_cache, epoch) - .await?; - - Ok(Self { + Self::validate( heartbeat, - cell_type, - distance_to_asserted, - coverage_summary, - validity, - }) + gateway_info_resolver, + coverage_cache, + max_distance_to_asserted, + epoch, + ) + .await }) } @@ -354,10 +512,13 @@ impl ValidatedHeartbeat { proto::Heartbeat { cbsd_id: self.heartbeat.cbsd_id.clone().unwrap_or_default(), pub_key: self.heartbeat.hotspot_key.as_ref().into(), - reward_multiplier: 1.0, cell_type: self.cell_type as i32, validity: self.validity as i32, timestamp: self.heartbeat.timestamp.timestamp() as u64, + location_trust_score_multiplier: (self.location_trust_score_multiplier + * dec!(1000)) + .to_u32() + .unwrap_or_default(), coverage_object: self .heartbeat .coverage_object @@ -370,6 +531,7 @@ impl ValidatedHeartbeat { .location_validation_timestamp .map_or(0, |v| v.timestamp() as u64), distance_to_asserted: self.distance_to_asserted.map_or(0, |v| v as u64), + ..Default::default() }, &[("validity", self.validity.as_str_name())], ) @@ -405,8 +567,8 @@ impl ValidatedHeartbeat { let truncated_timestamp = self.truncated_timestamp()?; sqlx::query( r#" - INSERT INTO cbrs_heartbeats (cbsd_id, hotspot_key, cell_type, latest_timestamp, truncated_timestamp, coverage_object) - VALUES ($1, $2, $3, $4, $5, $6) + INSERT INTO cbrs_heartbeats (cbsd_id, hotspot_key, cell_type, latest_timestamp, truncated_timestamp, coverage_object, location_trust_score_multiplier) + VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (cbsd_id, truncated_timestamp) DO UPDATE SET latest_timestamp = EXCLUDED.latest_timestamp, coverage_object = EXCLUDED.coverage_object @@ -418,6 +580,7 @@ impl ValidatedHeartbeat { .bind(self.heartbeat.timestamp) .bind(truncated_timestamp) .bind(self.heartbeat.coverage_object) + .bind(self.location_trust_score_multiplier) .execute(&mut *exec) .await?; Ok(()) @@ -427,9 +590,8 @@ impl ValidatedHeartbeat { let truncated_timestamp = self.truncated_timestamp()?; sqlx::query( r#" - INSERT INTO wifi_heartbeats (hotspot_key, cell_type, location_validation_timestamp, distance_to_asserted, - latest_timestamp, truncated_timestamp, coverage_object) - VALUES ($1, $2, $3, $4, $5, $6, $7) + INSERT INTO wifi_heartbeats (hotspot_key, cell_type, latest_timestamp, truncated_timestamp, coverage_object, location_trust_score_multiplier) + VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (hotspot_key, truncated_timestamp) DO UPDATE SET latest_timestamp = EXCLUDED.latest_timestamp, coverage_object = EXCLUDED.coverage_object @@ -437,134 +599,17 @@ impl ValidatedHeartbeat { ) .bind(self.heartbeat.hotspot_key) .bind(self.cell_type) - .bind(self.heartbeat.location_validation_timestamp) - .bind(self.distance_to_asserted) .bind(self.heartbeat.timestamp) .bind(truncated_timestamp) .bind(self.heartbeat.coverage_object) + .bind(self.location_trust_score_multiplier) .execute(&mut *exec) .await?; Ok(()) } } -/// Validate a heartbeat in the given epoch. -// TODO(map): This needs to be changed to provide a struct instead of a tuple. -pub async fn validate_heartbeat( - heartbeat: &Heartbeat, - gateway_info_resolver: &impl GatewayResolver, - coverage_cache: &CoverageObjects, - epoch: &Range>, -) -> anyhow::Result<( - CellType, - Option, - Option, - proto::HeartbeatValidity, -)> { - let Some(coverage_object) = heartbeat.coverage_object else { - return Ok(( - CellType::CellTypeNone, - None, - None, - proto::HeartbeatValidity::BadCoverageObject, - )); - }; - - let Some(coverage_summary) = coverage_cache - .coverage_summary(&coverage_object, heartbeat.key()) - .await? - else { - return Ok(( - CellType::CellTypeNone, - None, - None, - proto::HeartbeatValidity::NoSuchCoverageObject, - )); - }; - - let cell_type = match heartbeat.hb_type { - HbType::Cbrs => match heartbeat.cbsd_id.as_ref() { - Some(cbsd_id) => match CellType::from_cbsd_id(cbsd_id) { - Some(ty) => ty, - _ => { - return Ok(( - CellType::CellTypeNone, - None, - Some(coverage_summary), - proto::HeartbeatValidity::BadCbsdId, - )) - } - }, - None => { - return Ok(( - CellType::CellTypeNone, - None, - Some(coverage_summary), - proto::HeartbeatValidity::BadCbsdId, - )) - } - }, - HbType::Wifi => { - if coverage_summary.indoor { - CellType::NovaGenericWifiIndoor - } else { - CellType::NovaGenericWifiOutdoor - } - } - }; - - if !heartbeat.operation_mode { - return Ok(( - cell_type, - None, - Some(coverage_summary), - proto::HeartbeatValidity::NotOperational, - )); - } - - if !epoch.contains(&heartbeat.timestamp) { - return Ok(( - cell_type, - None, - Some(coverage_summary), - proto::HeartbeatValidity::HeartbeatOutsideRange, - )); - } - - let distance_to_asserted = match gateway_info_resolver - .resolve_gateway(&heartbeat.hotspot_key) - .await? - { - GatewayResolution::GatewayNotFound => { - return Ok(( - cell_type, - None, - Some(coverage_summary), - proto::HeartbeatValidity::GatewayNotFound, - )) - } - GatewayResolution::GatewayNotAsserted if heartbeat.hb_type == HbType::Wifi => { - return Ok(( - cell_type, - None, - Some(coverage_summary), - proto::HeartbeatValidity::GatewayNotAsserted, - )) - } - GatewayResolution::AssertedLocation(location) if heartbeat.hb_type == HbType::Wifi => { - Some(heartbeat.asserted_distance(location)?) - } - _ => None, - }; - - Ok(( - cell_type, - distance_to_asserted, - Some(coverage_summary), - proto::HeartbeatValidity::Valid, - )) -} - +#[allow(clippy::too_many_arguments)] pub(crate) async fn process_validated_heartbeats( validated_heartbeats: impl Stream>, heartbeat_cache: &Cache<(String, DateTime), ()>, @@ -807,6 +852,7 @@ mod test { location_validation_timestamp: None, }, validity: Default::default(), + location_trust_score_multiplier: dec!(1.0), distance_to_asserted: None, coverage_summary: None, } diff --git a/mobile_verifier/src/heartbeats/valid_radios.sql b/mobile_verifier/src/heartbeats/valid_radios.sql index 4527554d1..3c4a8e522 100644 --- a/mobile_verifier/src/heartbeats/valid_radios.sql +++ b/mobile_verifier/src/heartbeats/valid_radios.sql @@ -21,7 +21,7 @@ heartbeats AS ( ELSE 0.0 END AS heartbeat_multiplier, - 1.0 AS location_trust_multiplier + AVG(ch.location_trust_score_multiplier) as location_trust_score_multiplier FROM cbrs_heartbeats ch INNER JOIN latest_cbrs_hotspot lch ON ch.cbsd_id = lch.cbsd_id @@ -42,14 +42,7 @@ heartbeats AS ( ELSE 0.0 END AS heartbeat_multiplier, - avg( - CASE WHEN location_validation_timestamp IS NULL THEN - 0.25 - WHEN distance_to_asserted > $4 THEN - 0.25 - ELSE - 1.0 - END) AS location_trust_multiplier + AVG(location_trust_score_multiplier) as location_trust_score_multiplier FROM wifi_heartbeats WHERE @@ -89,7 +82,7 @@ SELECT hb.hotspot_key, hb.cbsd_id, hb.cell_type, - hb.location_trust_multiplier, + hb.location_trust_score_multiplier, u.coverage_object FROM heartbeats hb diff --git a/mobile_verifier/src/heartbeats/wifi.rs b/mobile_verifier/src/heartbeats/wifi.rs index 7095e1d1f..bf75e717e 100644 --- a/mobile_verifier/src/heartbeats/wifi.rs +++ b/mobile_verifier/src/heartbeats/wifi.rs @@ -21,6 +21,7 @@ pub struct HeartbeatDaemon { gateway_info_resolver: GIR, heartbeats: Receiver>, modeled_coverage_start: DateTime, + max_distance_to_asserted: u32, heartbeat_sink: FileSinkClient, seniority_sink: FileSinkClient, } @@ -34,6 +35,7 @@ where gateway_info_resolver: GIR, heartbeats: Receiver>, modeled_coverage_start: DateTime, + max_distance_to_asserted: u32, heartbeat_sink: FileSinkClient, seniority_sink: FileSinkClient, ) -> Self { @@ -42,6 +44,7 @@ where gateway_info_resolver, heartbeats, modeled_coverage_start, + max_distance_to_asserted, heartbeat_sink, seniority_sink, } @@ -110,6 +113,7 @@ where &self.gateway_info_resolver, heartbeats, coverage_objects, + self.max_distance_to_asserted, &epoch, ), heartbeat_cache, diff --git a/mobile_verifier/src/reward_shares.rs b/mobile_verifier/src/reward_shares.rs index b2425dc41..57c336284 100644 --- a/mobile_verifier/src/reward_shares.rs +++ b/mobile_verifier/src/reward_shares.rs @@ -53,8 +53,15 @@ const ORACLES_PERCENT: Decimal = dec!(0.04); #[derive(Debug)] pub struct TransferRewards { reward_scale: Decimal, - rewards: HashMap, + rewards: HashMap, reward_sum: Decimal, + mobile_bone_price: Decimal, +} + +#[derive(Copy, Clone, Debug)] +pub struct TransferReward { + bones: Decimal, + bytes_rewarded: u64, } impl TransferRewards { @@ -68,11 +75,19 @@ impl TransferRewards { #[cfg(test)] fn reward(&self, hotspot: &PublicKeyBinary) -> Decimal { - self.rewards.get(hotspot).copied().unwrap_or(Decimal::ZERO) * self.reward_scale + self.rewards + .get(hotspot) + .copied() + .map(|x| x.bones) + .unwrap_or(Decimal::ZERO) + * self.reward_scale } pub fn total(&self) -> Decimal { - self.rewards.values().map(|v| v * self.reward_scale).sum() + self.rewards + .values() + .map(|v| v.bones * self.reward_scale) + .sum() } pub async fn from_transfer_sessions( @@ -84,10 +99,17 @@ impl TransferRewards { let rewards = transfer_sessions .into_iter() // Calculate rewards per hotspot - .map(|(pub_key, dc_amount)| { - let bones = dc_to_mobile_bones(Decimal::from(dc_amount), mobile_bone_price); + .map(|(pub_key, rewardable)| { + let bones = + dc_to_mobile_bones(Decimal::from(rewardable.rewardable_dc), mobile_bone_price); reward_sum += bones; - (pub_key, bones) + ( + pub_key, + TransferReward { + bones, + bytes_rewarded: rewardable.rewardable_bytes, + }, + ) }) .collect(); @@ -119,6 +141,7 @@ impl TransferRewards { reward_scale, rewards, reward_sum: reward_sum * reward_scale, + mobile_bone_price, } } @@ -136,7 +159,7 @@ impl TransferRewards { rewards .into_iter() .map(move |(hotspot_key, reward)| { - let dc_transfer_reward = (reward * reward_scale) + let dc_transfer_reward = (reward.bones * reward_scale) .round_dp_with_strategy(0, RoundingStrategy::ToZero) .to_u64() .unwrap_or(0); @@ -149,6 +172,10 @@ impl TransferRewards { proto::GatewayReward { hotspot_key: hotspot_key.into(), dc_transfer_reward, + rewardable_bytes: reward.bytes_rewarded, + price: (self.mobile_bone_price * dec!(1_000_000) * dec!(1_000_000)) + .to_u64() + .unwrap_or_default(), }, )), }, @@ -201,7 +228,7 @@ impl MapperShares { discovery_location_amount: (DISCOVERY_MAPPING_SHARES * reward_per_share) .round_dp_with_strategy(0, RoundingStrategy::ToZero) .to_u64() - .unwrap_or(0), + .unwrap_or_default(), }) .filter(|subscriber_reward| subscriber_reward.discovery_location_amount > 0) .map(|subscriber_reward| { @@ -347,16 +374,20 @@ pub fn dc_to_mobile_bones(dc_amount: Decimal, mobile_bone_price: Decimal) -> Dec #[derive(Debug)] struct RadioPoints { - heartbeat_multiplier: Decimal, + location_trust_score_multiplier: Decimal, coverage_object: Uuid, seniority: DateTime, points: Decimal, } impl RadioPoints { - fn new(heartbeat_multiplier: Decimal, coverage_object: Uuid, seniority: DateTime) -> Self { + fn new( + location_trust_score_multiplier: Decimal, + coverage_object: Uuid, + seniority: DateTime, + ) -> Self { Self { - heartbeat_multiplier, + location_trust_score_multiplier, seniority, coverage_object, points: Decimal::ZERO, @@ -364,7 +395,7 @@ impl RadioPoints { } fn points(&self) -> Decimal { - (self.heartbeat_multiplier * self.points).max(Decimal::ZERO) + (self.location_trust_score_multiplier * self.points).max(Decimal::ZERO) } } @@ -432,7 +463,7 @@ impl CoveragePoints { .insert( opt_cbsd_id, RadioPoints::new( - heartbeat.location_trust_multiplier, + heartbeat.location_trust_score_multiplier, heartbeat.coverage_object, seniority.seniority_ts, ), @@ -535,7 +566,7 @@ fn new_radio_reward( ) -> (u64, proto::MobileRewardShare) { let poc_reward = poc_rewards_per_share * speedtest_multiplier - * radio_points.heartbeat_multiplier + * radio_points.location_trust_score_multiplier * radio_points.points; let hotspot_key: Vec = hotspot_key.clone().into(); let cbsd_id = cbsd_id.unwrap_or_default(); @@ -556,6 +587,13 @@ fn new_radio_reward( coverage_points: radio_points.points.to_u64().unwrap_or(0), seniority_timestamp: radio_points.seniority.encode_timestamp(), coverage_object: Vec::from(radio_points.coverage_object.into_bytes()), + location_trust_score_multiplier: (radio_points.location_trust_score_multiplier + * dec!(1000)) + .to_u32() + .unwrap_or_default(), + speedtest_multiplier: (speedtest_multiplier * dec!(1000)) + .to_u32() + .unwrap_or_default(), ..Default::default() }, )), @@ -590,8 +628,8 @@ mod test { use crate::{ cell_type::CellType, coverage::{CoveredHexStream, HexCoverage, Seniority}, - data_session, data_session::HotspotDataSession, + data_session::{self, HotspotReward}, heartbeats::{HeartbeatReward, KeyType, OwnedKeyType}, reward_shares, speedtests::Speedtest, @@ -713,7 +751,10 @@ mod test { let mut data_transfer_map = HotspotMap::new(); data_transfer_map.insert( data_transfer_session.pub_key, - data_transfer_session.num_dcs as u64, + HotspotReward { + rewardable_bytes: 0, // Not used + rewardable_dc: data_transfer_session.num_dcs as u64, + }, ); let now = Utc::now(); @@ -1010,105 +1051,105 @@ mod test { hotspot_key: gw2.clone(), coverage_object: cov_obj_2, cell_type: CellType::from_cbsd_id(&c2).unwrap(), - location_trust_multiplier: dec!(1.0), + location_trust_score_multiplier: dec!(1.0), }, HeartbeatReward { cbsd_id: Some(c4.clone()), hotspot_key: gw3.clone(), coverage_object: cov_obj_4, cell_type: CellType::from_cbsd_id(&c4).unwrap(), - location_trust_multiplier: dec!(1.0), + location_trust_score_multiplier: dec!(1.0), }, HeartbeatReward { cbsd_id: Some(c5.clone()), hotspot_key: gw4.clone(), coverage_object: cov_obj_5, cell_type: CellType::from_cbsd_id(&c5).unwrap(), - location_trust_multiplier: dec!(1.0), + location_trust_score_multiplier: dec!(1.0), }, HeartbeatReward { cbsd_id: Some(c6.clone()), hotspot_key: gw4.clone(), coverage_object: cov_obj_6, cell_type: CellType::from_cbsd_id(&c6).unwrap(), - location_trust_multiplier: dec!(1.0), + location_trust_score_multiplier: dec!(1.0), }, HeartbeatReward { cbsd_id: Some(c7.clone()), hotspot_key: gw4.clone(), coverage_object: cov_obj_7, cell_type: CellType::from_cbsd_id(&c7).unwrap(), - location_trust_multiplier: dec!(1.0), + location_trust_score_multiplier: dec!(1.0), }, HeartbeatReward { cbsd_id: Some(c8.clone()), hotspot_key: gw4.clone(), coverage_object: cov_obj_8, cell_type: CellType::from_cbsd_id(&c8).unwrap(), - location_trust_multiplier: dec!(1.0), + location_trust_score_multiplier: dec!(1.0), }, HeartbeatReward { cbsd_id: Some(c9.clone()), hotspot_key: gw4.clone(), coverage_object: cov_obj_9, cell_type: CellType::from_cbsd_id(&c9).unwrap(), - location_trust_multiplier: dec!(1.0), + location_trust_score_multiplier: dec!(1.0), }, HeartbeatReward { cbsd_id: Some(c10.clone()), hotspot_key: gw4.clone(), coverage_object: cov_obj_10, cell_type: CellType::from_cbsd_id(&c10).unwrap(), - location_trust_multiplier: dec!(1.0), + location_trust_score_multiplier: dec!(1.0), }, HeartbeatReward { cbsd_id: Some(c11.clone()), hotspot_key: gw4.clone(), coverage_object: cov_obj_11, cell_type: CellType::from_cbsd_id(&c11).unwrap(), - location_trust_multiplier: dec!(1.0), + location_trust_score_multiplier: dec!(1.0), }, HeartbeatReward { cbsd_id: Some(c12.clone()), hotspot_key: gw5.clone(), coverage_object: cov_obj_12, cell_type: CellType::from_cbsd_id(&c12).unwrap(), - location_trust_multiplier: dec!(1.0), + location_trust_score_multiplier: dec!(1.0), }, HeartbeatReward { cbsd_id: Some(c13.clone()), hotspot_key: gw6.clone(), coverage_object: cov_obj_13, cell_type: CellType::from_cbsd_id(&c13).unwrap(), - location_trust_multiplier: dec!(1.0), + location_trust_score_multiplier: dec!(1.0), }, HeartbeatReward { cbsd_id: Some(c14.clone()), hotspot_key: gw7.clone(), coverage_object: cov_obj_14, cell_type: CellType::from_cbsd_id(&c14).unwrap(), - location_trust_multiplier: dec!(1.0), + location_trust_score_multiplier: dec!(1.0), }, HeartbeatReward { cbsd_id: None, hotspot_key: gw9.clone(), cell_type: CellType::NovaGenericWifiIndoor, coverage_object: cov_obj_15, - location_trust_multiplier: dec!(1.0), + location_trust_score_multiplier: dec!(1.0), }, HeartbeatReward { cbsd_id: None, hotspot_key: gw10.clone(), cell_type: CellType::NovaGenericWifiIndoor, coverage_object: cov_obj_16, - location_trust_multiplier: dec!(0.25), + location_trust_score_multiplier: dec!(0.25), }, HeartbeatReward { cbsd_id: None, hotspot_key: gw11.clone(), cell_type: CellType::NovaGenericWifiIndoor, coverage_object: cov_obj_17, - location_trust_multiplier: dec!(0.25), + location_trust_score_multiplier: dec!(0.25), }, ] .into_iter() @@ -1375,7 +1416,7 @@ mod test { hotspot_key: gw1.clone(), cell_type: CellType::NovaGenericWifiIndoor, coverage_object: g1_cov_obj, - location_trust_multiplier: dec!(1.0), + location_trust_score_multiplier: dec!(1.0), }, // add sercomm indoor HB HeartbeatReward { @@ -1383,7 +1424,7 @@ mod test { hotspot_key: gw2.clone(), cell_type: CellType::from_cbsd_id(&c2).unwrap(), coverage_object: g2_cov_obj, - location_trust_multiplier: dec!(1.0), + location_trust_score_multiplier: dec!(1.0), }, ] .into_iter() @@ -1502,7 +1543,7 @@ mod test { hotspot_key: gw1.clone(), cell_type: CellType::NovaGenericWifiIndoor, coverage_object: g1_cov_obj, - location_trust_multiplier: dec!(0.25), + location_trust_score_multiplier: dec!(0.25), }, // add sercomm indoor HB HeartbeatReward { @@ -1510,7 +1551,7 @@ mod test { hotspot_key: gw2.clone(), coverage_object: g2_cov_obj, cell_type: CellType::from_cbsd_id(&c2).unwrap(), - location_trust_multiplier: dec!(1.0), + location_trust_score_multiplier: dec!(1.0), }, ] .into_iter() @@ -1629,7 +1670,7 @@ mod test { hotspot_key: gw1.clone(), cell_type: CellType::NovaGenericWifiOutdoor, coverage_object: g1_cov_obj, - location_trust_multiplier: dec!(1.0), + location_trust_score_multiplier: dec!(1.0), }, // add sercomm indoor HB HeartbeatReward { @@ -1637,7 +1678,7 @@ mod test { hotspot_key: gw2.clone(), cell_type: CellType::from_cbsd_id(&c2).unwrap(), coverage_object: g2_cov_obj, - location_trust_multiplier: dec!(1.0), + location_trust_score_multiplier: dec!(1.0), }, ] .into_iter() @@ -1740,7 +1781,7 @@ mod test { radio_points: vec![( Some(c1), RadioPoints { - heartbeat_multiplier: dec!(1.0), + location_trust_score_multiplier: dec!(1.0), seniority: DateTime::default(), coverage_object: Uuid::new_v4(), points: dec!(10.0), @@ -1758,7 +1799,7 @@ mod test { ( Some(c2), RadioPoints { - heartbeat_multiplier: dec!(1.0), + location_trust_score_multiplier: dec!(1.0), seniority: DateTime::default(), coverage_object: Uuid::new_v4(), points: dec!(-1.0), @@ -1767,7 +1808,7 @@ mod test { ( Some(c3), RadioPoints { - heartbeat_multiplier: dec!(1.0), + location_trust_score_multiplier: dec!(1.0), points: dec!(0.0), seniority: DateTime::default(), coverage_object: Uuid::new_v4(), diff --git a/mobile_verifier/src/rewarder.rs b/mobile_verifier/src/rewarder.rs index e188af686..283b74b22 100644 --- a/mobile_verifier/src/rewarder.rs +++ b/mobile_verifier/src/rewarder.rs @@ -35,7 +35,6 @@ pub struct Rewarder { pub mobile_rewards: FileSinkClient, reward_manifests: FileSinkClient, price_tracker: PriceTracker, - max_distance_to_asserted: u32, } impl Rewarder @@ -51,7 +50,6 @@ where mobile_rewards: FileSinkClient, reward_manifests: FileSinkClient, price_tracker: PriceTracker, - max_distance_to_asserted: u32, ) -> Self { Self { pool, @@ -61,7 +59,6 @@ where mobile_rewards, reward_manifests, price_tracker, - max_distance_to_asserted, } } @@ -182,7 +179,6 @@ where &self.mobile_rewards, reward_period, mobile_bone_price, - self.max_distance_to_asserted, ) .await?; @@ -241,7 +237,6 @@ pub async fn reward_poc_and_dc( mobile_rewards: &FileSinkClient, reward_period: &Range>, mobile_bone_price: Decimal, - max_distance_to_asserted: u32, ) -> anyhow::Result<()> { let transfer_rewards = TransferRewards::from_transfer_sessions( mobile_bone_price, @@ -257,14 +252,7 @@ pub async fn reward_poc_and_dc( }; telemetry::data_transfer_rewards_scale(scale); - reward_poc( - pool, - mobile_rewards, - reward_period, - transfer_rewards_sum, - max_distance_to_asserted, - ) - .await?; + reward_poc(pool, mobile_rewards, reward_period, transfer_rewards_sum).await?; reward_dc(mobile_rewards, reward_period, transfer_rewards).await?; @@ -276,13 +264,12 @@ async fn reward_poc( mobile_rewards: &FileSinkClient, reward_period: &Range>, transfer_reward_sum: Decimal, - max_distance_to_asserted: u32, ) -> anyhow::Result<()> { let total_poc_rewards = reward_shares::get_scheduled_tokens_for_poc(reward_period.end - reward_period.start) - transfer_reward_sum; - let heartbeats = HeartbeatReward::validated(pool, reward_period, max_distance_to_asserted); + let heartbeats = HeartbeatReward::validated(pool, reward_period); let speedtest_averages = SpeedtestAverages::aggregate_epoch_averages(reward_period.end, pool).await?; let coverage_points = diff --git a/mobile_verifier/tests/heartbeats.rs b/mobile_verifier/tests/heartbeats.rs index 4f9d41ecd..9b5700fbb 100644 --- a/mobile_verifier/tests/heartbeats.rs +++ b/mobile_verifier/tests/heartbeats.rs @@ -30,6 +30,7 @@ async fn test_save_wifi_heartbeat(pool: PgPool) -> anyhow::Result<()> { cell_type: CellType::SercommIndoor, distance_to_asserted: None, coverage_summary: None, + location_trust_score_multiplier: dec!(1.0), validity: HeartbeatValidity::Valid, }; @@ -67,6 +68,7 @@ async fn test_save_cbrs_heartbeat(pool: PgPool) -> anyhow::Result<()> { cell_type: CellType::SercommIndoor, distance_to_asserted: None, coverage_summary: None, + location_trust_score_multiplier: dec!(1.0), validity: HeartbeatValidity::Valid, }; @@ -95,32 +97,32 @@ async fn only_fetch_latest_hotspot(pool: PgPool) -> anyhow::Result<()> { "11sctWiP9r5wDJVuDe1Th4XSL2vaawaLLSQF8f8iokAoMAJHxqp".parse()?; sqlx::query( r#" -INSERT INTO cbrs_heartbeats (cbsd_id, hotspot_key, cell_type, latest_timestamp, truncated_timestamp, coverage_object) +INSERT INTO cbrs_heartbeats (cbsd_id, hotspot_key, cell_type, latest_timestamp, truncated_timestamp, coverage_object, location_trust_score_multiplier) VALUES - ($1, $2, 'sercommindoor', '2023-08-25 00:00:00+00', '2023-08-25 00:00:00+00', $4), - ($1, $3, 'sercommindoor', '2023-08-25 01:00:00+00', '2023-08-25 01:00:00+00', $4), - ($1, $2, 'sercommindoor', '2023-08-25 02:00:00+00', '2023-08-25 02:00:00+00', $4), - ($1, $3, 'sercommindoor', '2023-08-25 03:00:00+00', '2023-08-25 03:00:00+00', $4), - ($1, $2, 'sercommindoor', '2023-08-25 04:00:00+00', '2023-08-25 04:00:00+00', $4), - ($1, $3, 'sercommindoor', '2023-08-25 05:00:00+00', '2023-08-25 05:00:00+00', $4), - ($1, $2, 'sercommindoor', '2023-08-25 06:00:00+00', '2023-08-25 06:00:00+00', $4), - ($1, $3, 'sercommindoor', '2023-08-25 07:00:00+00', '2023-08-25 07:00:00+00', $4), - ($1, $2, 'sercommindoor', '2023-08-25 08:00:00+00', '2023-08-25 08:00:00+00', $4), - ($1, $3, 'sercommindoor', '2023-08-25 09:00:00+00', '2023-08-25 09:00:00+00', $4), - ($1, $2, 'sercommindoor', '2023-08-25 10:00:00+00', '2023-08-25 10:00:00+00', $4), - ($1, $3, 'sercommindoor', '2023-08-25 11:00:00+00', '2023-08-25 11:00:00+00', $4), - ($1, $2, 'sercommindoor', '2023-08-25 12:00:00+00', '2023-08-25 12:00:00+00', $4), - ($1, $3, 'sercommindoor', '2023-08-25 13:00:00+00', '2023-08-25 13:00:00+00', $4), - ($1, $2, 'sercommindoor', '2023-08-25 14:00:00+00', '2023-08-25 14:00:00+00', $4), - ($1, $3, 'sercommindoor', '2023-08-25 15:00:00+00', '2023-08-25 15:00:00+00', $4), - ($1, $2, 'sercommindoor', '2023-08-25 16:00:00+00', '2023-08-25 16:00:00+00', $4), - ($1, $3, 'sercommindoor', '2023-08-25 17:00:00+00', '2023-08-25 17:00:00+00', $4), - ($1, $2, 'sercommindoor', '2023-08-25 18:00:00+00', '2023-08-25 18:00:00+00', $4), - ($1, $3, 'sercommindoor', '2023-08-25 19:00:00+00', '2023-08-25 19:00:00+00', $4), - ($1, $2, 'sercommindoor', '2023-08-25 20:00:00+00', '2023-08-25 20:00:00+00', $4), - ($1, $3, 'sercommindoor', '2023-08-25 21:00:00+00', '2023-08-25 21:00:00+00', $4), - ($1, $2, 'sercommindoor', '2023-08-25 22:00:00+00', '2023-08-25 22:00:00+00', $4), - ($1, $3, 'sercommindoor', '2023-08-25 23:00:00+00', '2023-08-25 23:00:00+00', $4) + ($1, $2, 'sercommindoor', '2023-08-25 00:00:00+00', '2023-08-25 00:00:00+00', $4, 1.0), + ($1, $3, 'sercommindoor', '2023-08-25 01:00:00+00', '2023-08-25 01:00:00+00', $4, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 02:00:00+00', '2023-08-25 02:00:00+00', $4, 1.0), + ($1, $3, 'sercommindoor', '2023-08-25 03:00:00+00', '2023-08-25 03:00:00+00', $4, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 04:00:00+00', '2023-08-25 04:00:00+00', $4, 1.0), + ($1, $3, 'sercommindoor', '2023-08-25 05:00:00+00', '2023-08-25 05:00:00+00', $4, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 06:00:00+00', '2023-08-25 06:00:00+00', $4, 1.0), + ($1, $3, 'sercommindoor', '2023-08-25 07:00:00+00', '2023-08-25 07:00:00+00', $4, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 08:00:00+00', '2023-08-25 08:00:00+00', $4, 1.0), + ($1, $3, 'sercommindoor', '2023-08-25 09:00:00+00', '2023-08-25 09:00:00+00', $4, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 10:00:00+00', '2023-08-25 10:00:00+00', $4, 1.0), + ($1, $3, 'sercommindoor', '2023-08-25 11:00:00+00', '2023-08-25 11:00:00+00', $4, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 12:00:00+00', '2023-08-25 12:00:00+00', $4, 1.0), + ($1, $3, 'sercommindoor', '2023-08-25 13:00:00+00', '2023-08-25 13:00:00+00', $4, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 14:00:00+00', '2023-08-25 14:00:00+00', $4, 1.0), + ($1, $3, 'sercommindoor', '2023-08-25 15:00:00+00', '2023-08-25 15:00:00+00', $4, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 16:00:00+00', '2023-08-25 16:00:00+00', $4, 1.0), + ($1, $3, 'sercommindoor', '2023-08-25 17:00:00+00', '2023-08-25 17:00:00+00', $4, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 18:00:00+00', '2023-08-25 18:00:00+00', $4, 1.0), + ($1, $3, 'sercommindoor', '2023-08-25 19:00:00+00', '2023-08-25 19:00:00+00', $4, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 20:00:00+00', '2023-08-25 20:00:00+00', $4, 1.0), + ($1, $3, 'sercommindoor', '2023-08-25 21:00:00+00', '2023-08-25 21:00:00+00', $4, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 22:00:00+00', '2023-08-25 22:00:00+00', $4, 1.0), + ($1, $3, 'sercommindoor', '2023-08-25 23:00:00+00', '2023-08-25 23:00:00+00', $4, 1.0) "#, ) .bind(&cbsd_id) @@ -132,15 +134,9 @@ VALUES let start_period: DateTime = "2023-08-25 00:00:00.000000000 UTC".parse()?; let end_period: DateTime = "2023-08-26 00:00:00.000000000 UTC".parse()?; - let max_asserted_distance_deviation: u32 = 300; - - let heartbeat_reward: Vec<_> = HeartbeatReward::validated( - &pool, - &(start_period..end_period), - max_asserted_distance_deviation, - ) - .try_collect() - .await?; + let heartbeat_reward: Vec<_> = HeartbeatReward::validated(&pool, &(start_period..end_period)) + .try_collect() + .await?; assert_eq!( heartbeat_reward, @@ -148,7 +144,7 @@ VALUES hotspot_key: hotspot_2, cell_type, cbsd_id: Some(cbsd_id), - location_trust_multiplier: Decimal::ONE, + location_trust_score_multiplier: Decimal::ONE, coverage_object, }] ); @@ -168,20 +164,20 @@ async fn ensure_hotspot_does_not_affect_count(pool: PgPool) -> anyhow::Result<() "11sctWiP9r5wDJVuDe1Th4XSL2vaawaLLSQF8f8iokAoMAJHxqp".parse()?; sqlx::query( r#" -INSERT INTO cbrs_heartbeats (cbsd_id, hotspot_key, cell_type, latest_timestamp, truncated_timestamp, coverage_object) +INSERT INTO cbrs_heartbeats (cbsd_id, hotspot_key, cell_type, latest_timestamp, truncated_timestamp, coverage_object, location_trust_score_multiplier) VALUES - ($1, $2, 'sercommindoor', '2023-08-25 00:00:00+00', '2023-08-25 00:00:00+00', $4), - ($1, $2, 'sercommindoor', '2023-08-25 01:00:00+00', '2023-08-25 01:00:00+00', $4), - ($1, $2, 'sercommindoor', '2023-08-25 02:00:00+00', '2023-08-25 02:00:00+00', $4), - ($1, $2, 'sercommindoor', '2023-08-25 03:00:00+00', '2023-08-25 03:00:00+00', $4), - ($1, $2, 'sercommindoor', '2023-08-25 04:00:00+00', '2023-08-25 04:00:00+00', $4), - ($1, $2, 'sercommindoor', '2023-08-25 05:00:00+00', '2023-08-25 05:00:00+00', $4), - ($1, $2, 'sercommindoor', '2023-08-25 06:00:00+00', '2023-08-25 06:00:00+00', $4), - ($1, $2, 'sercommindoor', '2023-08-25 07:00:00+00', '2023-08-25 07:00:00+00', $4), - ($1, $2, 'sercommindoor', '2023-08-25 08:00:00+00', '2023-08-25 08:00:00+00', $4), - ($1, $2, 'sercommindoor', '2023-08-25 09:00:00+00', '2023-08-25 09:00:00+00', $4), - ($1, $2, 'sercommindoor', '2023-08-25 10:00:00+00', '2023-08-25 10:00:00+00', $4), - ($1, $3, 'sercommindoor', '2023-08-25 11:00:00+00', '2023-08-25 11:00:00+00', $4) + ($1, $2, 'sercommindoor', '2023-08-25 00:00:00+00', '2023-08-25 00:00:00+00', $4, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 01:00:00+00', '2023-08-25 01:00:00+00', $4, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 02:00:00+00', '2023-08-25 02:00:00+00', $4, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 03:00:00+00', '2023-08-25 03:00:00+00', $4, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 04:00:00+00', '2023-08-25 04:00:00+00', $4, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 05:00:00+00', '2023-08-25 05:00:00+00', $4, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 06:00:00+00', '2023-08-25 06:00:00+00', $4, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 07:00:00+00', '2023-08-25 07:00:00+00', $4, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 08:00:00+00', '2023-08-25 08:00:00+00', $4, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 09:00:00+00', '2023-08-25 09:00:00+00', $4, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 10:00:00+00', '2023-08-25 10:00:00+00', $4, 1.0), + ($1, $3, 'sercommindoor', '2023-08-25 11:00:00+00', '2023-08-25 11:00:00+00', $4, 1.0) "#, ) .bind(&cbsd_id) @@ -193,14 +189,9 @@ VALUES let start_period: DateTime = "2023-08-25 00:00:00.000000000 UTC".parse()?; let end_period: DateTime = "2023-08-26 00:00:00.000000000 UTC".parse()?; - let max_asserted_distance_deviation: u32 = 300; - let heartbeat_reward: Vec<_> = HeartbeatReward::validated( - &pool, - &(start_period..end_period), - max_asserted_distance_deviation, - ) - .try_collect() - .await?; + let heartbeat_reward: Vec<_> = HeartbeatReward::validated(&pool, &(start_period..end_period)) + .try_collect() + .await?; assert_eq!( heartbeat_reward, @@ -208,7 +199,7 @@ VALUES hotspot_key: hotspot_2, cell_type, cbsd_id: Some(cbsd_id), - location_trust_multiplier: Decimal::ONE, + location_trust_score_multiplier: Decimal::ONE, coverage_object, }] ); @@ -225,19 +216,19 @@ async fn ensure_minimum_count(pool: PgPool) -> anyhow::Result<()> { "112NqN2WWMwtK29PMzRby62fDydBJfsCLkCAf392stdok48ovNT6".parse()?; sqlx::query( r#" -INSERT INTO cbrs_heartbeats (cbsd_id, hotspot_key, cell_type, latest_timestamp, truncated_timestamp, coverage_object) +INSERT INTO cbrs_heartbeats (cbsd_id, hotspot_key, cell_type, latest_timestamp, truncated_timestamp, coverage_object, location_trust_score_multiplier) VALUES - ($1, $2, 'sercommindoor', '2023-08-25 00:00:00+00', '2023-08-25 00:00:00+00', $3), - ($1, $2, 'sercommindoor', '2023-08-25 01:00:00+00', '2023-08-25 01:00:00+00', $3), - ($1, $2, 'sercommindoor', '2023-08-25 02:00:00+00', '2023-08-25 02:00:00+00', $3), - ($1, $2, 'sercommindoor', '2023-08-25 03:00:00+00', '2023-08-25 03:00:00+00', $3), - ($1, $2, 'sercommindoor', '2023-08-25 04:00:00+00', '2023-08-25 04:00:00+00', $3), - ($1, $2, 'sercommindoor', '2023-08-25 05:00:00+00', '2023-08-25 05:00:00+00', $3), - ($1, $2, 'sercommindoor', '2023-08-25 06:00:00+00', '2023-08-25 06:00:00+00', $3), - ($1, $2, 'sercommindoor', '2023-08-25 07:00:00+00', '2023-08-25 07:00:00+00', $3), - ($1, $2, 'sercommindoor', '2023-08-25 08:00:00+00', '2023-08-25 08:00:00+00', $3), - ($1, $2, 'sercommindoor', '2023-08-25 09:00:00+00', '2023-08-25 09:00:00+00', $3), - ($1, $2, 'sercommindoor', '2023-08-25 10:00:00+00', '2023-08-25 10:00:00+00', $3) + ($1, $2, 'sercommindoor', '2023-08-25 00:00:00+00', '2023-08-25 00:00:00+00', $3, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 01:00:00+00', '2023-08-25 01:00:00+00', $3, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 02:00:00+00', '2023-08-25 02:00:00+00', $3, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 03:00:00+00', '2023-08-25 03:00:00+00', $3, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 04:00:00+00', '2023-08-25 04:00:00+00', $3, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 05:00:00+00', '2023-08-25 05:00:00+00', $3, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 06:00:00+00', '2023-08-25 06:00:00+00', $3, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 07:00:00+00', '2023-08-25 07:00:00+00', $3, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 08:00:00+00', '2023-08-25 08:00:00+00', $3, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 09:00:00+00', '2023-08-25 09:00:00+00', $3, 1.0), + ($1, $2, 'sercommindoor', '2023-08-25 10:00:00+00', '2023-08-25 10:00:00+00', $3, 1.0) "#, ) .bind(&cbsd_id) @@ -248,15 +239,9 @@ VALUES let start_period: DateTime = "2023-08-25 00:00:00.000000000 UTC".parse()?; let end_period: DateTime = "2023-08-26 00:00:00.000000000 UTC".parse()?; - let max_asserted_distance_deviation: u32 = 300; - - let heartbeat_reward: Vec<_> = HeartbeatReward::validated( - &pool, - &(start_period..end_period), - max_asserted_distance_deviation, - ) - .try_collect() - .await?; + let heartbeat_reward: Vec<_> = HeartbeatReward::validated(&pool, &(start_period..end_period)) + .try_collect() + .await?; assert!(heartbeat_reward.is_empty()); @@ -272,20 +257,20 @@ async fn ensure_wifi_hotspots_are_rewarded(pool: PgPool) -> anyhow::Result<()> { "112NqN2WWMwtK29PMzRby62fDydBJfsCLkCAf392stdok48ovNT6".parse()?; sqlx::query( r#" -INSERT INTO wifi_heartbeats (hotspot_key, cell_type, latest_timestamp, truncated_timestamp, location_validation_timestamp, distance_to_asserted, coverage_object) +INSERT INTO wifi_heartbeats (hotspot_key, cell_type, latest_timestamp, truncated_timestamp, coverage_object, location_trust_score_multiplier) VALUES - ($1, 'novagenericwifiindoor', '2023-08-25 00:00:00+00', '2023-08-25 00:00:00+00', NOW(), 300, $2), - ($1, 'novagenericwifiindoor', '2023-08-25 01:00:00+00', '2023-08-25 01:00:00+00', NOW(), 300, $2), - ($1, 'novagenericwifiindoor', '2023-08-25 02:00:00+00', '2023-08-25 02:00:00+00', NOW(), 300, $2), - ($1, 'novagenericwifiindoor', '2023-08-25 03:00:00+00', '2023-08-25 03:00:00+00', NOW(), 300, $2), - ($1, 'novagenericwifiindoor', '2023-08-25 04:00:00+00', '2023-08-25 04:00:00+00', NOW(), 300, $2), - ($1, 'novagenericwifiindoor', '2023-08-25 05:00:00+00', '2023-08-25 05:00:00+00', NOW(), 300, $2), - ($1, 'novagenericwifiindoor', '2023-08-25 06:00:00+00', '2023-08-25 06:00:00+00', NOW(), 300, $2), - ($1, 'novagenericwifiindoor', '2023-08-25 07:00:00+00', '2023-08-25 07:00:00+00', NOW(), 300, $2), - ($1, 'novagenericwifiindoor', '2023-08-25 08:00:00+00', '2023-08-25 08:00:00+00', NOW(), 300, $2), - ($1, 'novagenericwifiindoor', '2023-08-25 09:00:00+00', '2023-08-25 09:00:00+00', NOW(), 300, $2), - ($1, 'novagenericwifiindoor', '2023-08-25 10:00:00+00', '2023-08-25 10:00:00+00', NOW(), 300, $2), - ($1, 'novagenericwifiindoor', '2023-08-25 11:00:00+00', '2023-08-25 11:00:00+00', NOW(), 300, $3) + ($1, 'novagenericwifiindoor', '2023-08-25 00:00:00+00', '2023-08-25 00:00:00+00', $2, 1.0), + ($1, 'novagenericwifiindoor', '2023-08-25 01:00:00+00', '2023-08-25 01:00:00+00', $2, 1.0), + ($1, 'novagenericwifiindoor', '2023-08-25 02:00:00+00', '2023-08-25 02:00:00+00', $2, 1.0), + ($1, 'novagenericwifiindoor', '2023-08-25 03:00:00+00', '2023-08-25 03:00:00+00', $2, 1.0), + ($1, 'novagenericwifiindoor', '2023-08-25 04:00:00+00', '2023-08-25 04:00:00+00', $2, 1.0), + ($1, 'novagenericwifiindoor', '2023-08-25 05:00:00+00', '2023-08-25 05:00:00+00', $2, 1.0), + ($1, 'novagenericwifiindoor', '2023-08-25 06:00:00+00', '2023-08-25 06:00:00+00', $2, 1.0), + ($1, 'novagenericwifiindoor', '2023-08-25 07:00:00+00', '2023-08-25 07:00:00+00', $2, 1.0), + ($1, 'novagenericwifiindoor', '2023-08-25 08:00:00+00', '2023-08-25 08:00:00+00', $2, 1.0), + ($1, 'novagenericwifiindoor', '2023-08-25 09:00:00+00', '2023-08-25 09:00:00+00', $2, 1.0), + ($1, 'novagenericwifiindoor', '2023-08-25 10:00:00+00', '2023-08-25 10:00:00+00', $2, 1.0), + ($1, 'novagenericwifiindoor', '2023-08-25 11:00:00+00', '2023-08-25 11:00:00+00', $3, 1.0) "#, ) .bind(&hotspot) @@ -296,14 +281,9 @@ VALUES let start_period: DateTime = "2023-08-25 00:00:00.000000000 UTC".parse()?; let end_period: DateTime = "2023-08-26 00:00:00.000000000 UTC".parse()?; - let max_asserted_distance_deviation: u32 = 300; - let heartbeat_reward: Vec<_> = HeartbeatReward::validated( - &pool, - &(start_period..end_period), - max_asserted_distance_deviation, - ) - .try_collect() - .await?; + let heartbeat_reward: Vec<_> = HeartbeatReward::validated(&pool, &(start_period..end_period)) + .try_collect() + .await?; assert_eq!( heartbeat_reward, @@ -311,7 +291,7 @@ VALUES hotspot_key: hotspot, cell_type: CellType::NovaGenericWifiIndoor, cbsd_id: None, - location_trust_multiplier: dec!(1.0), + location_trust_score_multiplier: dec!(1.0), coverage_object: latest_coverage_object, }] ); @@ -328,20 +308,20 @@ async fn ensure_wifi_hotspots_use_average_location_trust_score(pool: PgPool) -> "112NqN2WWMwtK29PMzRby62fDydBJfsCLkCAf392stdok48ovNT6".parse()?; sqlx::query( r#" -INSERT INTO wifi_heartbeats (hotspot_key, cell_type, latest_timestamp, truncated_timestamp, location_validation_timestamp, distance_to_asserted, coverage_object) +INSERT INTO wifi_heartbeats (hotspot_key, cell_type, latest_timestamp, truncated_timestamp, coverage_object, location_trust_score_multiplier) VALUES - ($1, 'novagenericwifiindoor', '2023-08-25 00:00:00+00', '2023-08-25 00:00:00+00', NOW(), 300, $2), - ($1, 'novagenericwifiindoor', '2023-08-25 01:00:00+00', '2023-08-25 01:00:00+00', NOW(), 300, $2), - ($1, 'novagenericwifiindoor', '2023-08-25 02:00:00+00', '2023-08-25 02:00:00+00', NOW(), 300, $2), - ($1, 'novagenericwifiindoor', '2023-08-25 03:00:00+00', '2023-08-25 03:00:00+00', NOW(), 300, $2), - ($1, 'novagenericwifiindoor', '2023-08-25 04:00:00+00', '2023-08-25 04:00:00+00', NOW(), 300, $2), - ($1, 'novagenericwifiindoor', '2023-08-25 05:00:00+00', '2023-08-25 05:00:00+00', NOW(), 300, $2), - ($1, 'novagenericwifiindoor', '2023-08-25 06:00:00+00', '2023-08-25 06:00:00+00', NOW(), 300, $2), - ($1, 'novagenericwifiindoor', '2023-08-25 07:00:00+00', '2023-08-25 07:00:00+00', NOW(), 300, $2), - ($1, 'novagenericwifiindoor', '2023-08-25 08:00:00+00', '2023-08-25 08:00:00+00', null, 300, $2), - ($1, 'novagenericwifiindoor', '2023-08-25 09:00:00+00', '2023-08-25 09:00:00+00', null, 300, $2), - ($1, 'novagenericwifiindoor', '2023-08-25 10:00:00+00', '2023-08-25 10:00:00+00', null, 300, $2), - ($1, 'novagenericwifiindoor', '2023-08-25 11:00:00+00', '2023-08-25 11:00:00+00', null, 300, $3) + ($1, 'novagenericwifiindoor', '2023-08-25 00:00:00+00', '2023-08-25 00:00:00+00', $2, 1.0), + ($1, 'novagenericwifiindoor', '2023-08-25 01:00:00+00', '2023-08-25 01:00:00+00', $2, 1.0), + ($1, 'novagenericwifiindoor', '2023-08-25 02:00:00+00', '2023-08-25 02:00:00+00', $2, 1.0), + ($1, 'novagenericwifiindoor', '2023-08-25 03:00:00+00', '2023-08-25 03:00:00+00', $2, 1.0), + ($1, 'novagenericwifiindoor', '2023-08-25 04:00:00+00', '2023-08-25 04:00:00+00', $2, 1.0), + ($1, 'novagenericwifiindoor', '2023-08-25 05:00:00+00', '2023-08-25 05:00:00+00', $2, 1.0), + ($1, 'novagenericwifiindoor', '2023-08-25 06:00:00+00', '2023-08-25 06:00:00+00', $2, 1.0), + ($1, 'novagenericwifiindoor', '2023-08-25 07:00:00+00', '2023-08-25 07:00:00+00', $2, 1.0), + ($1, 'novagenericwifiindoor', '2023-08-25 08:00:00+00', '2023-08-25 08:00:00+00', $2, 0.25), + ($1, 'novagenericwifiindoor', '2023-08-25 09:00:00+00', '2023-08-25 09:00:00+00', $2, 0.25), + ($1, 'novagenericwifiindoor', '2023-08-25 10:00:00+00', '2023-08-25 10:00:00+00', $2, 0.25), + ($1, 'novagenericwifiindoor', '2023-08-25 11:00:00+00', '2023-08-25 11:00:00+00', $3, 0.25) "#, ) .bind(&hotspot) @@ -352,14 +332,9 @@ VALUES let start_period: DateTime = "2023-08-25 00:00:00.000000000 UTC".parse()?; let end_period: DateTime = "2023-08-26 00:00:00.000000000 UTC".parse()?; - let max_asserted_distance_deviation: u32 = 300; - let heartbeat_reward: Vec<_> = HeartbeatReward::validated( - &pool, - &(start_period..end_period), - max_asserted_distance_deviation, - ) - .try_collect() - .await?; + let heartbeat_reward: Vec<_> = HeartbeatReward::validated(&pool, &(start_period..end_period)) + .try_collect() + .await?; assert_eq!( heartbeat_reward, @@ -367,7 +342,7 @@ VALUES hotspot_key: hotspot, cell_type: CellType::NovaGenericWifiIndoor, cbsd_id: None, - location_trust_multiplier: dec!(0.75), + location_trust_score_multiplier: dec!(0.75), coverage_object: latest_coverage_object, }] ); diff --git a/mobile_verifier/tests/modeled_coverage.rs b/mobile_verifier/tests/modeled_coverage.rs index eca4d9aaa..c70ce8589 100644 --- a/mobile_verifier/tests/modeled_coverage.rs +++ b/mobile_verifier/tests/modeled_coverage.rs @@ -392,6 +392,7 @@ async fn process_input( &AllOwnersValid, stream::iter(heartbeats.map(Heartbeat::from)), &coverage_objects, + 2000, epoch, )); while let Some(heartbeat) = heartbeats.next().await.transpose()? { @@ -466,7 +467,7 @@ async fn scenario_one(pool: PgPool) -> anyhow::Result<()> { let speedtest_avgs = SpeedtestAverages { averages }; let reward_period = start..end; - let heartbeats = HeartbeatReward::validated(&pool, &reward_period, 1000); + let heartbeats = HeartbeatReward::validated(&pool, &reward_period); let coverage_points = CoveragePoints::aggregate_points(&pool, heartbeats, &speedtest_avgs, end).await?; @@ -559,7 +560,7 @@ async fn scenario_two(pool: PgPool) -> anyhow::Result<()> { let speedtest_avgs = SpeedtestAverages { averages }; let reward_period = start..end; - let heartbeats = HeartbeatReward::validated(&pool, &reward_period, 1000); + let heartbeats = HeartbeatReward::validated(&pool, &reward_period); let coverage_points = CoveragePoints::aggregate_points(&pool, heartbeats, &speedtest_avgs, end).await?; @@ -793,7 +794,7 @@ async fn scenario_three(pool: PgPool) -> anyhow::Result<()> { let speedtest_avgs = SpeedtestAverages { averages }; let reward_period = start..end; - let heartbeats = HeartbeatReward::validated(&pool, &reward_period, 1000); + let heartbeats = HeartbeatReward::validated(&pool, &reward_period); let coverage_points = CoveragePoints::aggregate_points(&pool, heartbeats, &speedtest_avgs, end).await?; @@ -858,7 +859,7 @@ async fn scenario_four(pool: PgPool) -> anyhow::Result<()> { let speedtest_avgs = SpeedtestAverages { averages }; let reward_period = start..end; - let heartbeats = HeartbeatReward::validated(&pool, &reward_period, 1000); + let heartbeats = HeartbeatReward::validated(&pool, &reward_period); let coverage_points = CoveragePoints::aggregate_points(&pool, heartbeats, &speedtest_avgs, end).await?; @@ -950,7 +951,7 @@ async fn scenario_five(pool: PgPool) -> anyhow::Result<()> { let speedtest_avgs = SpeedtestAverages { averages }; let reward_period = start..end; - let heartbeats = HeartbeatReward::validated(&pool, &reward_period, 1000); + let heartbeats = HeartbeatReward::validated(&pool, &reward_period); let coverage_points = CoveragePoints::aggregate_points(&pool, heartbeats, &speedtest_avgs, end).await?; @@ -1190,7 +1191,7 @@ async fn scenario_six(pool: PgPool) -> anyhow::Result<()> { let speedtest_avgs = SpeedtestAverages { averages }; let reward_period = start..end; - let heartbeats = HeartbeatReward::validated(&pool, &reward_period, 1000); + let heartbeats = HeartbeatReward::validated(&pool, &reward_period); let coverage_points = CoveragePoints::aggregate_points(&pool, heartbeats, &speedtest_avgs, end).await?; diff --git a/mobile_verifier/tests/rewarder_poc_dc.rs b/mobile_verifier/tests/rewarder_poc_dc.rs index 39a0ab9a9..43f354654 100644 --- a/mobile_verifier/tests/rewarder_poc_dc.rs +++ b/mobile_verifier/tests/rewarder_poc_dc.rs @@ -42,7 +42,7 @@ async fn test_poc_and_dc_rewards(pool: PgPool) -> anyhow::Result<()> { let (_, rewards) = tokio::join!( // run rewards for poc and dc - rewarder::reward_poc_and_dc(&pool, &mobile_rewards_client, &epoch, dec!(0.0001), 100,), + rewarder::reward_poc_and_dc(&pool, &mobile_rewards_client, &epoch, dec!(0.0001)), receive_expected_rewards(&mut mobile_rewards) ); if let Ok((poc_rewards, dc_rewards, unallocated_poc_reward)) = rewards { @@ -167,6 +167,7 @@ async fn seed_heartbeats( cell_type: CellType::SercommIndoor, distance_to_asserted: None, coverage_summary: None, + location_trust_score_multiplier: dec!(1.0), validity: HeartbeatValidity::Valid, }; @@ -194,6 +195,7 @@ async fn seed_heartbeats( cell_type: CellType::SercommOutdoor, distance_to_asserted: None, coverage_summary: None, + location_trust_score_multiplier: dec!(1.0), validity: HeartbeatValidity::Valid, }; @@ -220,6 +222,7 @@ async fn seed_heartbeats( cell_type: CellType::NovaGenericWifiIndoor, distance_to_asserted: Some(10), coverage_summary: None, + location_trust_score_multiplier: dec!(1.0), validity: HeartbeatValidity::Valid, }; diff --git a/mobile_verifier/tests/seniority.rs b/mobile_verifier/tests/seniority.rs index 0ff634b75..9c1139d52 100644 --- a/mobile_verifier/tests/seniority.rs +++ b/mobile_verifier/tests/seniority.rs @@ -5,6 +5,7 @@ use mobile_verifier::coverage::Seniority; use mobile_verifier::heartbeats::{ HbType, Heartbeat, SeniorityUpdate, SeniorityUpdateAction, ValidatedHeartbeat, }; +use rust_decimal_macros::dec; use sqlx::PgPool; use uuid::Uuid; @@ -29,6 +30,7 @@ async fn test_seniority_updates(pool: PgPool) -> anyhow::Result<()> { cell_type: CellType::SercommIndoor, distance_to_asserted: None, coverage_summary: None, + location_trust_score_multiplier: dec!(1.0), validity: HeartbeatValidity::Valid, }; let mut transaction = pool.begin().await?;