diff --git a/Cargo.lock b/Cargo.lock index ca88fa849..8a9faaf56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -475,7 +475,7 @@ dependencies = [ "ark-std", "derivative", "hashbrown 0.13.1", - "itertools 0.10.5", + "itertools", "num-traits", "zeroize", ] @@ -492,7 +492,7 @@ dependencies = [ "ark-std", "derivative", "digest 0.10.7", - "itertools 0.10.5", + "itertools", "num-bigint 0.4.3", "num-traits", "paste", @@ -1421,7 +1421,7 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "beacon" version = "0.1.0" -source = "git+https://github.com/helium/proto?branch=master#e8d84a8a4a05f069a3ce4f90d67a525aa92b52f9" +source = "git+https://github.com/helium/proto?branch=master#fdb83a38fbe8aead4ff6cb39d1e996f0ad0646b7" dependencies = [ "base64 0.21.7", "byteorder", @@ -3385,7 +3385,7 @@ dependencies = [ [[package]] name = "helium-proto" version = "0.1.0" -source = "git+https://github.com/helium/proto?branch=master#e8d84a8a4a05f069a3ce4f90d67a525aa92b52f9" +source = "git+https://github.com/helium/proto?branch=master#fdb83a38fbe8aead4ff6cb39d1e996f0ad0646b7" dependencies = [ "bytes", "prost", @@ -3938,7 +3938,7 @@ dependencies = [ "helium-proto", "http-serde", "iot-config", - "itertools 0.12.0", + "itertools", "lazy_static", "metrics", "once_cell", @@ -3982,15 +3982,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "0.4.8" @@ -5334,7 +5325,7 @@ checksum = "80b776a1b2dc779f5ee0641f8ade0125bc1298dd41a9a0c16d8bd57b42d222b1" dependencies = [ "bytes", "heck 0.4.0", - "itertools 0.12.0", + "itertools", "log", "multimap", "once_cell", @@ -5354,7 +5345,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" dependencies = [ "anyhow", - "itertools 0.12.0", + "itertools", "proc-macro2 1.0.69", "quote 1.0.33", "syn 2.0.38", @@ -6475,7 +6466,7 @@ dependencies = [ "futures", "helium-anchor-gen", "helium-crypto", - "itertools 0.12.0", + "itertools", "metrics", "serde", "sha2 0.10.6", @@ -6774,7 +6765,7 @@ dependencies = [ "console_log", "curve25519-dalek", "getrandom 0.2.10", - "itertools 0.10.5", + "itertools", "js-sys", "lazy_static", "libc", @@ -6814,7 +6805,7 @@ dependencies = [ "bincode", "eager", "enum-iterator", - "itertools 0.10.5", + "itertools", "libc", "log", "num-derive", @@ -6866,7 +6857,7 @@ dependencies = [ "async-mutex", "async-trait", "futures", - "itertools 0.10.5", + "itertools", "lazy_static", "log", "quinn", @@ -6996,7 +6987,7 @@ dependencies = [ "ed25519-dalek-bip32", "generic-array", "hmac 0.12.1", - "itertools 0.10.5", + "itertools", "js-sys", "lazy_static", "libsecp256k1", @@ -7053,7 +7044,7 @@ dependencies = [ "futures-util", "histogram", "indexmap 1.9.3", - "itertools 0.10.5", + "itertools", "libc", "log", "nix", @@ -7206,7 +7197,7 @@ dependencies = [ "byteorder", "curve25519-dalek", "getrandom 0.1.16", - "itertools 0.10.5", + "itertools", "lazy_static", "merlin", "num-derive", @@ -7330,7 +7321,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f87e292b4291f154971a43c3774364e2cbcaec599d3f5bf6fa9d122885dbc38a" dependencies = [ - "itertools 0.10.5", + "itertools", "nom", "unicode_categories", ] diff --git a/mobile_verifier/migrations/32_landtype.sql b/mobile_verifier/migrations/32_landtype.sql new file mode 100644 index 000000000..86dd12ed4 --- /dev/null +++ b/mobile_verifier/migrations/32_landtype.sql @@ -0,0 +1 @@ +ALTER TABLE hexes ADD COLUMN landtype oracle_assignment; \ No newline at end of file diff --git a/mobile_verifier/src/boosting_oracles/assignment.rs b/mobile_verifier/src/boosting_oracles/assignment.rs index 4be121708..b02f219ac 100644 --- a/mobile_verifier/src/boosting_oracles/assignment.rs +++ b/mobile_verifier/src/boosting_oracles/assignment.rs @@ -1,8 +1,15 @@ -use helium_proto::services::poc_mobile::oracle_boosting_hex_assignment; +use helium_proto::services::poc_mobile::oracle_boosting_hex_assignment::Assignment as ProtoAssignment; use rust_decimal::Decimal; use rust_decimal_macros::dec; use std::fmt; +#[derive(Debug, Clone, PartialEq, Eq, sqlx::FromRow)] +pub struct HexAssignments { + pub footfall: Assignment, + pub landtype: Assignment, + pub urbanized: Assignment, +} + #[derive(Copy, Clone, PartialEq, Eq, Debug, sqlx::Type)] #[sqlx(type_name = "oracle_assignment")] #[sqlx(rename_all = "lowercase")] @@ -12,7 +19,7 @@ pub enum Assignment { C, } -impl From for oracle_boosting_hex_assignment::Assignment { +impl From for ProtoAssignment { fn from(assignment: Assignment) -> Self { match assignment { Assignment::A => Self::A, @@ -24,7 +31,17 @@ impl From for oracle_boosting_hex_assignment::Assignment { impl From for i32 { fn from(assignment: Assignment) -> i32 { - oracle_boosting_hex_assignment::Assignment::from(assignment) as i32 + ProtoAssignment::from(assignment) as i32 + } +} + +impl From for Assignment { + fn from(value: ProtoAssignment) -> Self { + match value { + ProtoAssignment::A => Assignment::A, + ProtoAssignment::B => Assignment::B, + ProtoAssignment::C => Assignment::C, + } } } @@ -40,59 +57,53 @@ impl fmt::Display for Assignment { } } -pub fn footfall_and_urbanization_multiplier( - footfall: Assignment, - urbanization: Assignment, -) -> Decimal { - use Assignment::*; +impl HexAssignments { + pub fn boosting_multiplier(&self) -> Decimal { + let HexAssignments { + footfall, + urbanized, + landtype, + } = self; - match (footfall, urbanization) { - (A, A) => dec!(1.0), - (A, B) => dec!(1.0), - (B, A) => dec!(0.75), - (B, B) => dec!(0.50), - (C, A) => dec!(0.40), - (C, B) => dec!(0.10), - (A, C) => dec!(0.00), - (B, C) => dec!(0.00), - (C, C) => dec!(0.00), + use Assignment::*; + match (footfall, landtype, urbanized) { + // yellow - POI ≥ 1 Urbanized + (A, A, A) => dec!(1.00), + (A, B, A) => dec!(1.00), + (A, C, A) => dec!(1.00), + // orange - POI ≥ 1 Not Urbanized + (A, A, B) => dec!(1.00), + (A, B, B) => dec!(1.00), + (A, C, B) => dec!(1.00), + // light green - Point of Interest Urbanized + (B, A, A) => dec!(0.70), + (B, B, A) => dec!(0.70), + (B, C, A) => dec!(0.70), + // dark green - Point of Interest Not Urbanized + (B, A, B) => dec!(0.50), + (B, B, B) => dec!(0.50), + (B, C, B) => dec!(0.50), + // light blue - No POI Urbanized + (C, A, A) => dec!(0.40), + (C, B, A) => dec!(0.30), + (C, C, A) => dec!(0.05), + // dark blue - No POI Not Urbanized + (C, A, B) => dec!(0.20), + (C, B, B) => dec!(0.15), + (C, C, B) => dec!(0.03), + // gray - Outside of USA + (_, _, C) => dec!(0.00), + } } } -#[allow(dead_code)] -pub fn boosting_oracles_multiplier( - footfall: Assignment, - landtype: Assignment, - urbanization: Assignment, -) -> Decimal { - use Assignment::*; - - match (footfall, landtype, urbanization) { - // POI ≥ 1 Urbanized - (A, A, A) => dec!(1.00), - (A, B, A) => dec!(1.00), - (A, C, A) => dec!(1.00), - // POI ≥ 1 Not Urbanized - (A, A, B) => dec!(1.00), - (A, B, B) => dec!(1.00), - (A, C, B) => dec!(1.00), - // Point of Interest Urbanized - (B, A, A) => dec!(0.70), - (B, B, A) => dec!(0.70), - (B, C, A) => dec!(0.70), - // Point of Interest Not Urbanized - (B, A, B) => dec!(0.50), - (B, B, B) => dec!(0.50), - (B, C, B) => dec!(0.50), - // No POI Urbanized - (C, A, A) => dec!(0.40), - (C, B, A) => dec!(0.30), - (C, C, A) => dec!(0.05), - // No POI Not Urbanized - (C, A, B) => dec!(0.20), - (C, B, B) => dec!(0.15), - (C, C, B) => dec!(0.03), - // Outside of USA - (_, _, C) => dec!(0.00), +#[cfg(test)] +impl HexAssignments { + pub fn test_best() -> Self { + Self { + footfall: Assignment::A, + urbanized: Assignment::A, + landtype: Assignment::A, + } } } diff --git a/mobile_verifier/src/boosting_oracles/mod.rs b/mobile_verifier/src/boosting_oracles/mod.rs index 882089cbe..146ddf59f 100644 --- a/mobile_verifier/src/boosting_oracles/mod.rs +++ b/mobile_verifier/src/boosting_oracles/mod.rs @@ -1,95 +1,56 @@ pub mod assignment; -use std::collections::HashMap; - use crate::{ geofence::{Geofence, GeofenceValidator}, Settings, }; -pub use assignment::Assignment; +pub use assignment::{Assignment, HexAssignments}; use hextree::disktree::DiskTreeMap; +pub trait BoostedHexAssignments: Send + Sync { + fn assignments(&self, cell: hextree::Cell) -> anyhow::Result; +} + +pub struct HexBoostData { + urbanized: DiskTreeMap, + usa_geofence: Geofence, + footfall: DiskTreeMap, + landtype: DiskTreeMap, +} + pub fn make_hex_boost_data( settings: &Settings, usa_geofence: Geofence, -) -> anyhow::Result> { +) -> anyhow::Result { let urban_disktree = DiskTreeMap::open(&settings.urbanization_data_set)?; let footfall_disktree = DiskTreeMap::open(&settings.footfall_data_set)?; + let landtype_disktree = DiskTreeMap::open(&settings.landtype_data_set)?; - let urbanization = UrbanizationData::new(urban_disktree, usa_geofence); - let footfall_data = FootfallData::new(footfall_disktree); - let hex_boost_data = HexBoostData::new(urbanization, footfall_data); + let hex_boost_data = HexBoostData { + urbanized: urban_disktree, + usa_geofence, + footfall: footfall_disktree, + landtype: landtype_disktree, + }; Ok(hex_boost_data) } +impl BoostedHexAssignments for HexBoostData { + fn assignments(&self, cell: hextree::Cell) -> anyhow::Result { + let footfall = self.footfall_assignment(cell)?; + let urbanized = self.urbanized_assignment(cell)?; + let landtype = self.landtype_assignment(cell)?; -pub trait HexAssignment: Send + Sync { - fn assignment(&self, cell: hextree::Cell) -> anyhow::Result; -} - -pub struct HexBoostData { - pub urbanization: Urban, - pub footfall: Foot, -} - -pub struct UrbanizationData { - urbanized: Urban, - usa_geofence: Geo, -} - -pub struct FootfallData { - footfall: Foot, -} - -impl HexBoostData { - pub fn new(urbanization: Urban, footfall: Foot) -> Self { - Self { - urbanization, + Ok(HexAssignments { footfall, - } - } -} - -impl UrbanizationData { - pub fn new(urbanized: Urban, usa_geofence: Geo) -> Self { - Self { urbanized, - usa_geofence, - } - } -} - -impl FootfallData { - pub fn new(footfall: Foot) -> Self { - Self { footfall } - } -} - -trait DiskTreeLike: Send + Sync { - fn get(&self, cell: hextree::Cell) -> hextree::Result>; -} - -impl DiskTreeLike for DiskTreeMap { - fn get(&self, cell: hextree::Cell) -> hextree::Result> { - self.get(cell) + landtype, + }) } } -impl DiskTreeLike for std::collections::HashSet { - fn get(&self, cell: hextree::Cell) -> hextree::Result> { - match self.contains(&cell) { - true => Ok(Some((cell, &[]))), - false => Ok(None), - } - } -} - -impl HexAssignment for UrbanizationData -where - Urban: DiskTreeLike, - Geo: GeofenceValidator, -{ - fn assignment(&self, cell: hextree::Cell) -> anyhow::Result { +impl HexBoostData { + fn urbanized_assignment(&self, cell: hextree::Cell) -> anyhow::Result { if !self.usa_geofence.in_valid_region(&cell) { return Ok(Assignment::C); } @@ -99,13 +60,8 @@ where false => Ok(Assignment::B), } } -} -impl HexAssignment for FootfallData -where - Foot: DiskTreeLike, -{ - fn assignment(&self, cell: hextree::Cell) -> anyhow::Result { + fn footfall_assignment(&self, cell: hextree::Cell) -> anyhow::Result { let Some((_, vals)) = self.footfall.get(cell)? else { return Ok(Assignment::C); }; @@ -113,24 +69,317 @@ where match vals { &[x] if x >= 1 => Ok(Assignment::A), &[0] => Ok(Assignment::B), - other => anyhow::bail!("unexpected disktree data: {cell:?} {other:?}"), + other => anyhow::bail!("unexpected footfall disktree data: {cell:?} {other:?}"), } } + + fn landtype_assignment(&self, cell: hextree::Cell) -> anyhow::Result { + let Some((_, vals)) = self.landtype.get(cell)? else { + return Ok(Assignment::C); + }; + + anyhow::ensure!( + vals.len() == 1, + "unexpected landtype disktree data: {cell:?} {vals:?}" + ); + + let cover = Landtype::try_from(vals[0])?; + Ok(cover.into()) + } +} + +impl From for Assignment { + fn from(value: Landtype) -> Self { + match value { + Landtype::Built => Assignment::A, + // + Landtype::Tree => Assignment::B, + Landtype::Shrub => Assignment::B, + Landtype::Grass => Assignment::B, + // + Landtype::Bare => Assignment::C, + Landtype::Crop => Assignment::C, + Landtype::Frozen => Assignment::C, + Landtype::Water => Assignment::C, + Landtype::Wet => Assignment::C, + Landtype::Mangrove => Assignment::C, + Landtype::Moss => Assignment::C, + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Landtype { + Tree = 10, + Shrub = 20, + Grass = 30, + Crop = 40, + Built = 50, + Bare = 60, + Frozen = 70, + Water = 80, + Wet = 90, + Mangrove = 95, + Moss = 100, +} + +impl std::fmt::Display for Landtype { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.to_str()) + } } -impl HexAssignment for Assignment { - fn assignment(&self, _cell: hextree::Cell) -> anyhow::Result { - Ok(*self) +impl Landtype { + pub(crate) fn to_str(self) -> &'static str { + match self { + Landtype::Tree => "TreeCover", + Landtype::Shrub => "Shrubland", + Landtype::Grass => "Grassland", + Landtype::Crop => "Cropland", + Landtype::Built => "BuiltUp", + Landtype::Bare => "BareOrSparseVeg", + Landtype::Frozen => "SnowAndIce", + Landtype::Water => "Water", + Landtype::Wet => "HerbaceousWetland", + Landtype::Mangrove => "Mangroves", + Landtype::Moss => "MossAndLichen", + } } } -impl HexAssignment for HashMap { - fn assignment(&self, cell: hextree::Cell) -> anyhow::Result { - let assignment = match self.get(&cell) { - Some(true) => Assignment::A, - Some(false) => Assignment::B, - None => Assignment::C, +impl TryFrom for Landtype { + type Error = anyhow::Error; + fn try_from(other: u8) -> anyhow::Result { + let val = match other { + 10 => Landtype::Tree, + 20 => Landtype::Shrub, + 30 => Landtype::Grass, + 40 => Landtype::Crop, + 50 => Landtype::Built, + 60 => Landtype::Bare, + 70 => Landtype::Frozen, + 80 => Landtype::Water, + 90 => Landtype::Wet, + 95 => Landtype::Mangrove, + 100 => Landtype::Moss, + other => anyhow::bail!("unexpected landtype disktree value: {other:?}"), + }; + Ok(val) + } +} + +#[cfg(test)] +mod tests { + + use std::io::Cursor; + + use hextree::{HexTreeMap, HexTreeSet}; + + use super::*; + + #[test] + fn test_hex_boost_data() -> anyhow::Result<()> { + // This test will break if any of the logic deriving Assignments from + // the underlying DiskTreeMap's changes. + + let unknown_cell = hextree::Cell::from_raw(0x8c2681a3064d9ff)?; + + // Types of Cells + // yellow - POI ≥ 1 Urbanized + let poi_built_urbanized = hextree::Cell::from_raw(0x8c2681a3064dbff)?; + let poi_grass_urbanized = hextree::Cell::from_raw(0x8c2681a3064ddff)?; + let poi_water_urbanized = hextree::Cell::from_raw(0x8c2681a3064e1ff)?; + // orange - POI ≥ 1 Not Urbanized + let poi_built_not_urbanized = hextree::Cell::from_raw(0x8c2681a3064e3ff)?; + let poi_grass_not_urbanized = hextree::Cell::from_raw(0x8c2681a3064e5ff)?; + let poi_water_not_urbanized = hextree::Cell::from_raw(0x8c2681a3064e7ff)?; + // light green - Point of Interest Urbanized + let poi_no_data_built_urbanized = hextree::Cell::from_raw(0x8c2681a3064e9ff)?; + let poi_no_data_grass_urbanized = hextree::Cell::from_raw(0x8c2681a3064ebff)?; + let poi_no_data_water_urbanized = hextree::Cell::from_raw(0x8c2681a3064edff)?; + // dark green - Point of Interest Not Urbanized + let poi_no_data_built_not_urbanized = hextree::Cell::from_raw(0x8c2681a306501ff)?; + let poi_no_data_grass_not_urbanized = hextree::Cell::from_raw(0x8c2681a306503ff)?; + let poi_no_data_water_not_urbanized = hextree::Cell::from_raw(0x8c2681a306505ff)?; + // light blue - No POI Urbanized + let no_poi_built_urbanized = hextree::Cell::from_raw(0x8c2681a306507ff)?; + let no_poi_grass_urbanized = hextree::Cell::from_raw(0x8c2681a306509ff)?; + let no_poi_water_urbanized = hextree::Cell::from_raw(0x8c2681a30650bff)?; + // dark blue - No POI Not Urbanized + let no_poi_built_not_urbanized = hextree::Cell::from_raw(0x8c2681a30650dff)?; + let no_poi_grass_not_urbanized = hextree::Cell::from_raw(0x8c2681a306511ff)?; + let no_poi_water_not_urbanized = hextree::Cell::from_raw(0x8c2681a306513ff)?; + // gray - Outside of USA + let poi_built_outside_us = hextree::Cell::from_raw(0x8c2681a306515ff)?; + let poi_grass_outside_us = hextree::Cell::from_raw(0x8c2681a306517ff)?; + let poi_water_outside_us = hextree::Cell::from_raw(0x8c2681a306519ff)?; + let poi_no_data_built_outside_us = hextree::Cell::from_raw(0x8c2681a30651bff)?; + let poi_no_data_grass_outside_us = hextree::Cell::from_raw(0x8c2681a30651dff)?; + let poi_no_data_water_outside_us = hextree::Cell::from_raw(0x8c2681a306521ff)?; + let no_poi_built_outside_us = hextree::Cell::from_raw(0x8c2681a306523ff)?; + let no_poi_grass_outside_us = hextree::Cell::from_raw(0x8c2681a306525ff)?; + let no_poi_water_outside_us = hextree::Cell::from_raw(0x8c2681a306527ff)?; + + // Footfall Data + // POI - footfalls > 1 for a POI across hexes + // POI No Data - No footfalls for a POI across any hexes + // NO POI - Does not exist + let mut footfall = HexTreeMap::::new(); + footfall.insert(poi_built_urbanized, 42); + footfall.insert(poi_grass_urbanized, 42); + footfall.insert(poi_water_urbanized, 42); + footfall.insert(poi_built_not_urbanized, 42); + footfall.insert(poi_grass_not_urbanized, 42); + footfall.insert(poi_water_not_urbanized, 42); + footfall.insert(poi_no_data_built_urbanized, 0); + footfall.insert(poi_no_data_grass_urbanized, 0); + footfall.insert(poi_no_data_water_urbanized, 0); + footfall.insert(poi_no_data_built_not_urbanized, 0); + footfall.insert(poi_no_data_grass_not_urbanized, 0); + footfall.insert(poi_no_data_water_not_urbanized, 0); + footfall.insert(poi_built_outside_us, 42); + footfall.insert(poi_grass_outside_us, 42); + footfall.insert(poi_water_outside_us, 42); + footfall.insert(poi_no_data_built_outside_us, 0); + footfall.insert(poi_no_data_grass_outside_us, 0); + footfall.insert(poi_no_data_water_outside_us, 0); + + // Landtype Data + // Map to enum values for Landtype + // An unknown cell is considered Assignment::C + let mut landtype = HexTreeMap::::new(); + landtype.insert(poi_built_urbanized, 50); + landtype.insert(poi_grass_urbanized, 30); + landtype.insert(poi_water_urbanized, 80); + landtype.insert(poi_built_not_urbanized, 50); + landtype.insert(poi_grass_not_urbanized, 30); + landtype.insert(poi_water_not_urbanized, 80); + landtype.insert(poi_no_data_built_urbanized, 50); + landtype.insert(poi_no_data_grass_urbanized, 30); + landtype.insert(poi_no_data_water_urbanized, 80); + landtype.insert(poi_no_data_built_not_urbanized, 50); + landtype.insert(poi_no_data_grass_not_urbanized, 30); + landtype.insert(poi_no_data_water_not_urbanized, 80); + landtype.insert(no_poi_built_urbanized, 50); + landtype.insert(no_poi_grass_urbanized, 30); + landtype.insert(no_poi_water_urbanized, 80); + landtype.insert(no_poi_built_not_urbanized, 50); + landtype.insert(no_poi_grass_not_urbanized, 30); + landtype.insert(no_poi_water_not_urbanized, 80); + landtype.insert(poi_built_outside_us, 50); + landtype.insert(poi_grass_outside_us, 30); + landtype.insert(poi_water_outside_us, 80); + landtype.insert(poi_no_data_built_outside_us, 50); + landtype.insert(poi_no_data_grass_outside_us, 30); + landtype.insert(poi_no_data_water_outside_us, 80); + landtype.insert(no_poi_built_outside_us, 50); + landtype.insert(no_poi_grass_outside_us, 30); + landtype.insert(no_poi_water_outside_us, 80); + + // Urbanized data + // Urban - something in the map, and in the geofence + // Not Urban - nothing in the map, but in the geofence + // Outside - not in the geofence, urbanized hex never considered + let mut urbanized = HexTreeMap::::new(); + urbanized.insert(poi_built_urbanized, 0); + urbanized.insert(poi_grass_urbanized, 0); + urbanized.insert(poi_water_urbanized, 0); + urbanized.insert(poi_no_data_built_urbanized, 0); + urbanized.insert(poi_no_data_grass_urbanized, 0); + urbanized.insert(poi_no_data_water_urbanized, 0); + urbanized.insert(no_poi_built_urbanized, 0); + urbanized.insert(no_poi_grass_urbanized, 0); + urbanized.insert(no_poi_water_urbanized, 0); + + let inside_usa = [ + poi_built_urbanized, + poi_grass_urbanized, + poi_water_urbanized, + poi_built_not_urbanized, + poi_grass_not_urbanized, + poi_water_not_urbanized, + poi_no_data_built_urbanized, + poi_no_data_grass_urbanized, + poi_no_data_water_urbanized, + poi_no_data_built_not_urbanized, + poi_no_data_grass_not_urbanized, + poi_no_data_water_not_urbanized, + no_poi_built_urbanized, + no_poi_grass_urbanized, + no_poi_water_urbanized, + no_poi_built_not_urbanized, + no_poi_grass_not_urbanized, + no_poi_water_not_urbanized, + ]; + let geofence_set: HexTreeSet = inside_usa.iter().collect(); + let usa_geofence = Geofence::new(geofence_set, h3o::Resolution::Twelve); + + // These vectors are a standin for the file system + let mut urbanized_buf = vec![]; + let mut footfall_buff = vec![]; + let mut landtype_buf = vec![]; + + // Turn the HexTrees into DiskTrees + urbanized.to_disktree(Cursor::new(&mut urbanized_buf), |w, v| w.write_all(&[*v]))?; + footfall.to_disktree(Cursor::new(&mut footfall_buff), |w, v| w.write_all(&[*v]))?; + landtype.to_disktree(Cursor::new(&mut landtype_buf), |w, v| w.write_all(&[*v]))?; + + let urbanized = DiskTreeMap::with_buf(urbanized_buf)?; + let footfall = DiskTreeMap::with_buf(footfall_buff)?; + let landtype = DiskTreeMap::with_buf(landtype_buf)?; + + // Let the testing commence + let data = HexBoostData { + urbanized, + usa_geofence, + footfall, + landtype, + }; + + // NOTE(mj): formatting ignored to make it easier to see the expected change in assignments. + // NOTE(mj): The semicolon at the end of the block is there to keep rust from + // complaining about attributes on expression being experimental. + #[rustfmt::skip] + { + use Assignment::*; + // yellow + assert_eq!(HexAssignments { footfall: A, landtype: A, urbanized: A }, data.assignments(poi_built_urbanized)?); + assert_eq!(HexAssignments { footfall: A, landtype: B, urbanized: A }, data.assignments(poi_grass_urbanized)?); + assert_eq!(HexAssignments { footfall: A, landtype: C, urbanized: A }, data.assignments(poi_water_urbanized)?); + // orange + assert_eq!(HexAssignments { footfall: A, landtype: A, urbanized: B }, data.assignments(poi_built_not_urbanized)?); + assert_eq!(HexAssignments { footfall: A, landtype: B, urbanized: B }, data.assignments(poi_grass_not_urbanized)?); + assert_eq!(HexAssignments { footfall: A, landtype: C, urbanized: B }, data.assignments(poi_water_not_urbanized)?); + // light green + assert_eq!(HexAssignments { footfall: B, landtype: A, urbanized: A }, data.assignments(poi_no_data_built_urbanized)?); + assert_eq!(HexAssignments { footfall: B, landtype: B, urbanized: A }, data.assignments(poi_no_data_grass_urbanized)?); + assert_eq!(HexAssignments { footfall: B, landtype: C, urbanized: A }, data.assignments(poi_no_data_water_urbanized)?); + // green + assert_eq!(HexAssignments { footfall: B, landtype: A, urbanized: B }, data.assignments(poi_no_data_built_not_urbanized)?); + assert_eq!(HexAssignments { footfall: B, landtype: B, urbanized: B }, data.assignments(poi_no_data_grass_not_urbanized)?); + assert_eq!(HexAssignments { footfall: B, landtype: C, urbanized: B }, data.assignments(poi_no_data_water_not_urbanized)?); + // light blue + assert_eq!(HexAssignments { footfall: C, landtype: A, urbanized: A }, data.assignments(no_poi_built_urbanized)?); + assert_eq!(HexAssignments { footfall: C, landtype: B, urbanized: A }, data.assignments(no_poi_grass_urbanized)?); + assert_eq!(HexAssignments { footfall: C, landtype: C, urbanized: A }, data.assignments(no_poi_water_urbanized)?); + // dark blue + assert_eq!(HexAssignments { footfall: C, landtype: A, urbanized: B }, data.assignments(no_poi_built_not_urbanized)?); + assert_eq!(HexAssignments { footfall: C, landtype: B, urbanized: B }, data.assignments(no_poi_grass_not_urbanized)?); + assert_eq!(HexAssignments { footfall: C, landtype: C, urbanized: B }, data.assignments(no_poi_water_not_urbanized)?); + // gray + assert_eq!(HexAssignments { footfall: A, landtype: A, urbanized: C }, data.assignments(poi_built_outside_us)?); + assert_eq!(HexAssignments { footfall: A, landtype: B, urbanized: C }, data.assignments(poi_grass_outside_us)?); + assert_eq!(HexAssignments { footfall: A, landtype: C, urbanized: C }, data.assignments(poi_water_outside_us)?); + assert_eq!(HexAssignments { footfall: B, landtype: A, urbanized: C }, data.assignments(poi_no_data_built_outside_us)?); + assert_eq!(HexAssignments { footfall: B, landtype: B, urbanized: C }, data.assignments(poi_no_data_grass_outside_us)?); + assert_eq!(HexAssignments { footfall: B, landtype: C, urbanized: C }, data.assignments(poi_no_data_water_outside_us)?); + assert_eq!(HexAssignments { footfall: C, landtype: A, urbanized: C }, data.assignments(no_poi_built_outside_us)?); + assert_eq!(HexAssignments { footfall: C, landtype: B, urbanized: C }, data.assignments(no_poi_grass_outside_us)?); + assert_eq!(HexAssignments { footfall: C, landtype: C, urbanized: C }, data.assignments(no_poi_water_outside_us)?); + // never inserted + assert_eq!(HexAssignments { footfall: C, landtype: C, urbanized: C }, data.assignments(unknown_cell)?); }; - Ok(assignment) + + Ok(()) } } diff --git a/mobile_verifier/src/cli/mod.rs b/mobile_verifier/src/cli/mod.rs index 8ce1540a4..611db001b 100644 --- a/mobile_verifier/src/cli/mod.rs +++ b/mobile_verifier/src/cli/mod.rs @@ -1,2 +1,3 @@ pub mod reward_from_db; pub mod server; +pub mod verify_disktree; diff --git a/mobile_verifier/src/cli/server.rs b/mobile_verifier/src/cli/server.rs index 8c9a9c00a..d75323f9d 100644 --- a/mobile_verifier/src/cli/server.rs +++ b/mobile_verifier/src/cli/server.rs @@ -108,7 +108,8 @@ impl Cmd { let usa_region_paths = settings.usa_region_paths()?; tracing::info!(?usa_region_paths, "usa_geofence_regions"); - let usa_geofence = Geofence::new(usa_region_paths, settings.usa_fencing_resolution()?)?; + let usa_geofence = + Geofence::from_paths(usa_region_paths, settings.usa_fencing_resolution()?)?; let cbrs_heartbeat_daemon = CellHeartbeatDaemon::new( pool.clone(), @@ -128,7 +129,7 @@ impl Cmd { "usa_and_mexico_geofence_regions" ); - let usa_and_mexico_geofence = Geofence::new( + let usa_and_mexico_geofence = Geofence::from_paths( usa_and_mexico_region_paths, settings.usa_and_mexico_fencing_resolution()?, )?; diff --git a/mobile_verifier/src/cli/verify_disktree.rs b/mobile_verifier/src/cli/verify_disktree.rs new file mode 100644 index 000000000..4c2aa4529 --- /dev/null +++ b/mobile_verifier/src/cli/verify_disktree.rs @@ -0,0 +1,61 @@ +use std::{collections::HashMap, path::PathBuf}; + +use hextree::disktree::DiskTreeMap; + +use crate::{ + boosting_oracles::{Assignment, Landtype}, + Settings, +}; + +#[derive(Debug, clap::Args)] +pub struct Cmd { + /// Path to the unzipped .h3tree file + #[clap(long)] + path: PathBuf, + + /// Expected type of the .h3tree file + #[clap(long)] + r#type: DisktreeType, +} + +#[derive(Debug, Clone, clap::ValueEnum)] +enum DisktreeType { + Landtype, +} + +impl Cmd { + pub async fn run(self, _settings: &Settings) -> anyhow::Result<()> { + let disktree = DiskTreeMap::open(&self.path)?; + + let mut value_counts = HashMap::::new(); + let mut idx: u128 = 0; + let start = tokio::time::Instant::now(); + + println!("Checking {}, this may take a while...", self.path.display()); + for x in disktree.iter()? { + idx += 1; + if idx % 100_000_000 == 0 { + println!("Processed {} cells after {:?}", idx, start.elapsed()); + } + let (_cell, vals) = x.unwrap(); + *value_counts.entry(vals[0]).or_insert(0) += 1; + } + + println!("REPORT {}", "=".repeat(50)); + match self.r#type { + DisktreeType::Landtype => { + for (key, count) in value_counts { + let landtype = Landtype::try_from(key); + let assignment = landtype.as_ref().map(|lt| Assignment::from(*lt)); + // cover is formatted twice to allow for padding a result + println!( + "| {key:<4} | {count:<12} | {:<20} | {assignment:?} |", + format!("{landtype:?}") + ); + } + } + } + + Ok(()) + } +} diff --git a/mobile_verifier/src/coverage.rs b/mobile_verifier/src/coverage.rs index 8a47caa12..d312cdd7e 100644 --- a/mobile_verifier/src/coverage.rs +++ b/mobile_verifier/src/coverage.rs @@ -1,7 +1,5 @@ use crate::{ - boosting_oracles::{ - assignment::footfall_and_urbanization_multiplier, Assignment, HexAssignment, HexBoostData, - }, + boosting_oracles::{assignment::HexAssignments, BoostedHexAssignments, HexBoostData}, heartbeats::{HbType, KeyType, OwnedKeyType}, IsAuthorized, }; @@ -66,29 +64,21 @@ impl From for SignalLevel { } } -pub struct CoverageDaemon -where - Urban: HexAssignment, - Foot: HexAssignment, -{ +pub struct CoverageDaemon { pool: Pool, auth_client: AuthorizationClient, - hex_boost_data: HexBoostData, + hex_boost_data: HexBoostData, coverage_objs: Receiver>, initial_boosting_reports: Option>, coverage_obj_sink: FileSinkClient, oracle_boosting_sink: FileSinkClient, } -impl CoverageDaemon -where - Urban: HexAssignment, - Foot: HexAssignment, -{ +impl CoverageDaemon { pub async fn new( pool: PgPool, auth_client: AuthorizationClient, - hex_boost_data: HexBoostData, + hex_boost_data: HexBoostData, coverage_objs: Receiver>, coverage_obj_sink: FileSinkClient, oracle_boosting_sink: FileSinkClient, @@ -192,7 +182,14 @@ pub struct UnassignedHex { impl UnassignedHex { pub fn fetch(pool: &PgPool) -> impl Stream> + '_ { sqlx::query_as( - "SELECT uuid, hex, signal_level, signal_power FROM hexes WHERE urbanized IS NULL OR footfall IS NULL", + "SELECT + uuid, hex, signal_level, signal_power + FROM + hexes + WHERE + urbanized IS NULL + OR footfall IS NULL + OR landtype IS NULL", ) .fetch(pool) } @@ -204,7 +201,7 @@ impl UnassignedHex { pub async fn set_oracle_boosting_assignments( unassigned_urbinization_hexes: impl Stream>, - hex_boost_data: &HexBoostData, + hex_boost_data: &impl BoostedHexAssignments, pool: &PgPool, ) -> anyhow::Result> { let now = Utc::now(); @@ -225,10 +222,10 @@ pub async fn set_oracle_boosting_assignments( async fn initialize_unassigned_hexes( unassigned_urbinization_hexes: impl Stream>, - hex_boost_data: &HexBoostData, + hex_boost_data: &impl BoostedHexAssignments, pool: &Pool, ) -> Result>, anyhow::Error> { - const NUMBER_OF_FIELDS_IN_QUERY: u16 = 6; + const NUMBER_OF_FIELDS_IN_QUERY: u16 = 7; const ASSIGNMENTS_MAX_BATCH_ENTRIES: usize = (u16::MAX / NUMBER_OF_FIELDS_IN_QUERY) as usize; let mut boost_results = HashMap::>::new(); @@ -241,44 +238,45 @@ async fn initialize_unassigned_hexes( .into_iter() .map(|hex| { let cell = hextree::Cell::from_raw(hex.hex)?; - let urbanized = hex_boost_data.urbanization.assignment(cell)?; - let footfall = hex_boost_data.footfall.assignment(cell)?; + let assignments = hex_boost_data.assignments(cell)?; let location = hex.to_location_string(); - let assignment_multiplier = - (footfall_and_urbanization_multiplier(footfall, urbanized) * dec!(1000)) - .to_u32() - .unwrap_or(0); + let assignment_multiplier = (assignments.boosting_multiplier() * dec!(1000)) + .to_u32() + .unwrap_or(0); boost_results.entry(hex.uuid).or_default().push( proto::OracleBoostingHexAssignment { location, - urbanized: urbanized.into(), - footfall: footfall.into(), + urbanized: assignments.urbanized.into(), + footfall: assignments.footfall.into(), + landtype: assignments.landtype.into(), assignment_multiplier, }, ); - Ok((hex, urbanized, footfall)) + Ok((hex, assignments)) }) .collect(); QueryBuilder::new( - "INSERT INTO hexes (uuid, hex, signal_level, signal_power, urbanized, footfall)", + "INSERT INTO hexes (uuid, hex, signal_level, signal_power, urbanized, footfall, landtype)", ) - .push_values(hexes?, |mut b, (hex, urbanized, footfall)| { + .push_values(hexes?, |mut b, (hex, assignments)| { b.push_bind(hex.uuid) .push_bind(hex.hex as i64) .push_bind(hex.signal_level) .push_bind(hex.signal_power) - .push_bind(urbanized) - .push_bind(footfall); + .push_bind(assignments.urbanized) + .push_bind(assignments.footfall) + .push_bind(assignments.landtype); }) .push( r#" ON CONFLICT (uuid, hex) DO UPDATE SET urbanized = EXCLUDED.urbanized, - footfall = EXCLUDED.footfall + footfall = EXCLUDED.footfall, + landtype = EXCLUDED.landtype "#, ) .build() @@ -289,11 +287,7 @@ async fn initialize_unassigned_hexes( Ok(boost_results) } -impl ManagedTask for CoverageDaemon -where - Urban: HexAssignment + 'static, - Foot: HexAssignment + 'static, -{ +impl ManagedTask for CoverageDaemon { fn start_task( self: Box, shutdown: triggered::Listener, @@ -451,8 +445,8 @@ pub struct HexCoverage { pub signal_power: i32, pub coverage_claim_time: DateTime, pub inserted_at: DateTime, - pub urbanized: Assignment, - pub footfall: Assignment, + #[sqlx(flatten)] + pub assignments: HexAssignments, } #[derive(Eq, Debug)] @@ -461,8 +455,7 @@ struct IndoorCoverageLevel { seniority_timestamp: DateTime, hotspot: PublicKeyBinary, signal_level: SignalLevel, - urbanized: Assignment, - footfall: Assignment, + hex_assignments: HexAssignments, } impl PartialEq for IndoorCoverageLevel { @@ -500,8 +493,7 @@ struct OutdoorCoverageLevel { hotspot: PublicKeyBinary, signal_power: i32, signal_level: SignalLevel, - urbanized: Assignment, - footfall: Assignment, + hex_assignments: HexAssignments, } impl PartialEq for OutdoorCoverageLevel { @@ -555,8 +547,7 @@ impl CoverageReward { pub struct CoverageRewardPoints { pub boost_multiplier: NonZeroU32, pub coverage_points: Decimal, - pub urbanized: Assignment, - pub footfall: Assignment, + pub hex_assignments: HexAssignments, pub rank: Option, } @@ -565,7 +556,7 @@ impl CoverageRewardPoints { let oracle_multiplier = if self.boost_multiplier.get() > 1 { dec!(1.0) } else { - footfall_and_urbanization_multiplier(self.footfall, self.urbanized) + self.hex_assignments.boosting_multiplier() }; let points = self.coverage_points * oracle_multiplier; @@ -636,7 +627,7 @@ impl CoveredHexStream for Pool { Ok( sqlx::query_as( r#" - SELECT co.uuid, h.hex, co.indoor, co.radio_key, h.signal_level, h.signal_power, co.coverage_claim_time, co.inserted_at, h.urbanized, h.footfall + SELECT co.uuid, h.hex, co.indoor, co.radio_key, h.signal_level, h.signal_power, co.coverage_claim_time, co.inserted_at, h.urbanized, h.footfall, h.landtype FROM coverage_objects co INNER JOIN hexes h on co.uuid = h.uuid WHERE co.radio_key = $1 @@ -783,8 +774,7 @@ fn insert_indoor_coverage( seniority_timestamp: hex_coverage.coverage_claim_time, signal_level: hex_coverage.signal_level, hotspot: hotspot.clone(), - urbanized: hex_coverage.urbanized, - footfall: hex_coverage.footfall, + hex_assignments: hex_coverage.assignments, }) } @@ -802,8 +792,7 @@ fn insert_outdoor_coverage( signal_level: hex_coverage.signal_level, signal_power: hex_coverage.signal_power, hotspot: hotspot.clone(), - urbanized: hex_coverage.urbanized, - footfall: hex_coverage.footfall, + hex_assignments: hex_coverage.assignments, }); } @@ -827,8 +816,7 @@ fn into_outdoor_rewards( points: CoverageRewardPoints { boost_multiplier, coverage_points: cl.coverage_points(), - urbanized: cl.urbanized, - footfall: cl.footfall, + hex_assignments: cl.hex_assignments, rank: Some(rank), }, hotspot: cl.hotspot, @@ -864,8 +852,7 @@ fn into_indoor_rewards( points: CoverageRewardPoints { boost_multiplier, coverage_points: cl.coverage_points(), - urbanized: cl.urbanized, - footfall: cl.footfall, + hex_assignments: cl.hex_assignments, rank: None, }, hotspot: cl.hotspot, @@ -1077,8 +1064,7 @@ mod test { points: CoverageRewardPoints { coverage_points: dec!(400), boost_multiplier: NonZeroU32::new(1).unwrap(), - urbanized: Assignment::A, - footfall: Assignment::A, + hex_assignments: HexAssignments::test_best(), rank: None }, boosted_hex_info: BoostedHex { @@ -1112,8 +1098,7 @@ mod test { signal_power: 0, coverage_claim_time, inserted_at: DateTime::::MIN_UTC, - urbanized: Assignment::A, - footfall: Assignment::A, + assignments: HexAssignments::test_best(), } } @@ -1195,8 +1180,7 @@ mod test { points: CoverageRewardPoints { coverage_points: dec!(400), boost_multiplier: NonZeroU32::new(1).unwrap(), - urbanized: Assignment::A, - footfall: Assignment::A, + hex_assignments: HexAssignments::test_best(), rank: None }, boosted_hex_info: BoostedHex { @@ -1240,8 +1224,7 @@ mod test { coverage_points: dec!(16), rank: Some(dec!(1.0)), boost_multiplier: NonZeroU32::new(1).unwrap(), - urbanized: Assignment::A, - footfall: Assignment::A + hex_assignments: HexAssignments::test_best(), }, boosted_hex_info: BoostedHex { location: 0x8a1fb46622dffff_u64, @@ -1255,8 +1238,7 @@ mod test { coverage_points: dec!(16), rank: Some(dec!(0.50)), boost_multiplier: NonZeroU32::new(1).unwrap(), - urbanized: Assignment::A, - footfall: Assignment::A + hex_assignments: HexAssignments::test_best(), }, boosted_hex_info: BoostedHex { location: 0x8a1fb46622dffff_u64, @@ -1270,8 +1252,7 @@ mod test { coverage_points: dec!(16), rank: Some(dec!(0.25)), boost_multiplier: NonZeroU32::new(1).unwrap(), - urbanized: Assignment::A, - footfall: Assignment::A + hex_assignments: HexAssignments::test_best(), }, boosted_hex_info: BoostedHex { location: 0x8a1fb46622dffff_u64, @@ -1542,8 +1523,7 @@ mod test { signal_power: 0, coverage_claim_time: coverage_claim_time.unwrap_or(DateTime::::MIN_UTC), inserted_at: DateTime::::MIN_UTC, - urbanized: Assignment::A, - footfall: Assignment::A, + assignments: HexAssignments::test_best(), } } @@ -1561,8 +1541,7 @@ mod test { signal_level: SignalLevel::High, coverage_claim_time, inserted_at: DateTime::::MIN_UTC, - urbanized: Assignment::A, - footfall: Assignment::A, + assignments: HexAssignments::test_best(), } } @@ -1580,8 +1559,7 @@ mod test { signal_level: SignalLevel::High, coverage_claim_time, inserted_at: DateTime::::MIN_UTC, - urbanized: Assignment::A, - footfall: Assignment::A, + assignments: HexAssignments::test_best(), } } @@ -1599,8 +1577,7 @@ mod test { signal_level, coverage_claim_time, inserted_at: DateTime::::MIN_UTC, - urbanized: Assignment::A, - footfall: Assignment::A, + assignments: HexAssignments::test_best(), } } } diff --git a/mobile_verifier/src/geofence.rs b/mobile_verifier/src/geofence.rs index 7b6559e12..100aded3e 100644 --- a/mobile_verifier/src/geofence.rs +++ b/mobile_verifier/src/geofence.rs @@ -22,11 +22,19 @@ pub struct Geofence { } impl Geofence { - pub fn new(paths: Vec, resolution: Resolution) -> anyhow::Result { - Ok(Self { - regions: Arc::new(valid_mapping_regions(paths)?), + pub fn new(hextree: HexTreeSet, resolution: Resolution) -> Self { + Self { + regions: Arc::new(hextree), resolution, - }) + } + } + + pub fn from_paths( + paths: Vec, + resolution: Resolution, + ) -> anyhow::Result { + let hextree = valid_mapping_regions(paths)?; + Ok(Self::new(hextree, resolution)) } } diff --git a/mobile_verifier/src/main.rs b/mobile_verifier/src/main.rs index e57abd4b7..6d597b45b 100644 --- a/mobile_verifier/src/main.rs +++ b/mobile_verifier/src/main.rs @@ -1,7 +1,7 @@ use anyhow::Result; use clap::Parser; use mobile_verifier::{ - cli::{reward_from_db, server}, + cli::{reward_from_db, server, verify_disktree}, Settings, }; use std::path; @@ -36,6 +36,11 @@ impl Cli { pub enum Cmd { Server(server::Cmd), RewardFromDb(reward_from_db::Cmd), + /// Verify a Disktree file for HexBoosting. + /// + /// Go through every cell and ensure it's value can be turned into an Assignment. + /// NOTE: This can take a very long time. Run with a --release binary. + VerifyDisktree(verify_disktree::Cmd), } impl Cmd { @@ -43,6 +48,7 @@ impl Cmd { match self { Self::Server(cmd) => cmd.run(&settings).await, Self::RewardFromDb(cmd) => cmd.run(&settings).await, + Self::VerifyDisktree(cmd) => cmd.run(&settings).await, } } } diff --git a/mobile_verifier/src/reward_shares.rs b/mobile_verifier/src/reward_shares.rs index b152682f1..9adcac463 100644 --- a/mobile_verifier/src/reward_shares.rs +++ b/mobile_verifier/src/reward_shares.rs @@ -697,11 +697,10 @@ pub fn get_scheduled_tokens_for_oracles(duration: Duration) -> Decimal { mod test { use super::*; use crate::{ - boosting_oracles::Assignment, + boosting_oracles::assignment::HexAssignments, cell_type::CellType, coverage::{CoveredHexStream, HexCoverage, Seniority}, - data_session::HotspotDataSession, - data_session::{self, HotspotReward}, + data_session::{self, HotspotDataSession, HotspotReward}, heartbeats::{HeartbeatReward, KeyType, OwnedKeyType}, reward_shares, speedtests::Speedtest, @@ -1004,8 +1003,7 @@ mod test { signal_power: 0, coverage_claim_time: DateTime::::MIN_UTC, inserted_at: DateTime::::MIN_UTC, - urbanized: Assignment::A, - footfall: Assignment::C, + assignments: HexAssignments::test_best(), }] } @@ -1890,8 +1888,7 @@ mod test { coverage_points: CoverageRewardPoints { boost_multiplier: NonZeroU32::new(1).unwrap(), coverage_points: dec!(10.0), - urbanized: Assignment::A, - footfall: Assignment::A, + hex_assignments: HexAssignments::test_best(), rank: None, }, boosted_hex: BoostedHex { diff --git a/mobile_verifier/src/settings.rs b/mobile_verifier/src/settings.rs index 7f0698fef..27ca2ffa3 100644 --- a/mobile_verifier/src/settings.rs +++ b/mobile_verifier/src/settings.rs @@ -44,6 +44,7 @@ pub struct Settings { pub usa_fencing_resolution: u8, pub urbanization_data_set: PathBuf, pub footfall_data_set: PathBuf, + pub landtype_data_set: PathBuf, } fn default_fencing_resolution() -> u8 { diff --git a/mobile_verifier/tests/boosting_oracles.rs b/mobile_verifier/tests/boosting_oracles.rs index c42fd38eb..a05eaf64b 100644 --- a/mobile_verifier/tests/boosting_oracles.rs +++ b/mobile_verifier/tests/boosting_oracles.rs @@ -1,3 +1,5 @@ +mod common; +use anyhow::Context; use chrono::{DateTime, Duration, Utc}; use file_store::{ coverage::RadioHexSignalLevel, @@ -5,13 +7,14 @@ use file_store::{ speedtest::CellSpeedtest, }; use futures::stream::{self, StreamExt}; +use h3o::CellIndex; use helium_crypto::PublicKeyBinary; use helium_proto::services::poc_mobile::{ CoverageObjectValidity, OracleBoostingHexAssignment, SignalLevel, }; use mobile_config::boosted_hex_info::BoostedHexes; use mobile_verifier::{ - boosting_oracles::{Assignment, HexBoostData, UrbanizationData}, + boosting_oracles::Assignment, coverage::{ set_oracle_boosting_assignments, CoverageClaimTimeCache, CoverageObject, CoverageObjectCache, Seniority, UnassignedHex, @@ -24,12 +27,10 @@ use mobile_verifier::{ speedtests_average::{SpeedtestAverage, SpeedtestAverages}, GatewayResolution, GatewayResolver, }; +use rust_decimal::Decimal; use rust_decimal_macros::dec; use sqlx::PgPool; -use std::{ - collections::{HashMap, HashSet}, - pin::pin, -}; +use std::{collections::HashMap, pin::pin}; use uuid::Uuid; #[derive(Clone)] @@ -126,59 +127,64 @@ async fn test_footfall_and_urbanization_report(pool: PgPool) -> anyhow::Result<( let uuid = Uuid::new_v4(); let cbsd_id = "P27-SCE4255W120200039521XGB0102".to_string(); - let hex1 = OracleBoostingHexAssignment { - location: "8c2681a3064d9ff".to_string(), - assignment_multiplier: 1000, - urbanized: Assignment::A.into(), - footfall: Assignment::A.into(), - }; - let hex2 = OracleBoostingHexAssignment { - location: "8c2681a3064d1ff".to_string(), - assignment_multiplier: 1000, - urbanized: Assignment::B.into(), - footfall: Assignment::A.into(), - }; - let hex3 = OracleBoostingHexAssignment { - location: "8c450e64dc899ff".to_string(), - assignment_multiplier: 0, - urbanized: Assignment::C.into(), - footfall: Assignment::A.into(), - }; - let hex4 = OracleBoostingHexAssignment { - location: "8c2681a3064dbff".to_string(), - assignment_multiplier: 750, - urbanized: Assignment::A.into(), - footfall: Assignment::B.into(), - }; - let hex5 = OracleBoostingHexAssignment { - location: "8c2681a339365ff".to_string(), - assignment_multiplier: 500, - urbanized: Assignment::B.into(), - footfall: Assignment::B.into(), - }; - let hex6 = OracleBoostingHexAssignment { - location: "8c450e64dc89dff".to_string(), - assignment_multiplier: 0, - urbanized: Assignment::C.into(), - footfall: Assignment::B.into(), - }; - let hex7 = OracleBoostingHexAssignment { - location: "8c2681a3066b3ff".to_string(), - assignment_multiplier: 400, - urbanized: Assignment::A.into(), - footfall: Assignment::C.into(), - }; - let hex8 = OracleBoostingHexAssignment { - location: "8c2681a3066b7ff".to_string(), - assignment_multiplier: 100, - urbanized: Assignment::B.into(), - footfall: Assignment::C.into(), - }; - let hex9 = OracleBoostingHexAssignment { - location: "8c450e64dc883ff".to_string(), - assignment_multiplier: 0, - urbanized: Assignment::C.into(), - footfall: Assignment::C.into(), + fn new_hex_assingment( + cell: &mut CellIndex, + footfall: Assignment, + landtype: Assignment, + urbanized: Assignment, + assignment_multiplier: u32, + ) -> OracleBoostingHexAssignment { + let loc = cell.to_string(); + *cell = cell.succ().unwrap(); + OracleBoostingHexAssignment { + location: loc, + assignment_multiplier, + urbanized: urbanized.into(), + footfall: footfall.into(), + landtype: landtype.into(), + } + } + + let hexes = { + // NOTE(mj): Cell is mutated in constructor to keep elements aligned for readability + let mut cell = CellIndex::try_from(0x8c2681a3064d9ff)?; + use Assignment::*; + vec![ + // yellow - POI ≥ 1 Urbanized + new_hex_assingment(&mut cell, A, A, A, 1000), + new_hex_assingment(&mut cell, A, B, A, 1000), + new_hex_assingment(&mut cell, A, C, A, 1000), + // orange - POI ≥ 1 Not Urbanized + new_hex_assingment(&mut cell, A, A, B, 1000), + new_hex_assingment(&mut cell, A, B, B, 1000), + new_hex_assingment(&mut cell, A, C, B, 1000), + // light green - Point of Interest Urbanized + new_hex_assingment(&mut cell, B, A, A, 700), + new_hex_assingment(&mut cell, B, B, A, 700), + new_hex_assingment(&mut cell, B, C, A, 700), + // dark green - Point of Interest Not Urbanized + new_hex_assingment(&mut cell, B, A, B, 500), + new_hex_assingment(&mut cell, B, B, B, 500), + new_hex_assingment(&mut cell, B, C, B, 500), + // light blue - No POI Urbanized + new_hex_assingment(&mut cell, C, A, A, 400), + new_hex_assingment(&mut cell, C, B, A, 300), + new_hex_assingment(&mut cell, C, C, A, 50), + // dark blue - No POI Not Urbanized + new_hex_assingment(&mut cell, C, A, B, 200), + new_hex_assingment(&mut cell, C, B, B, 150), + new_hex_assingment(&mut cell, C, C, B, 30), + // gray - Outside of USA + new_hex_assingment(&mut cell, A, A, C, 0), + new_hex_assingment(&mut cell, A, B, C, 0), + new_hex_assingment(&mut cell, A, C, C, 0), + new_hex_assingment(&mut cell, B, A, C, 0), + new_hex_assingment(&mut cell, B, B, C, 0), + new_hex_assingment(&mut cell, B, C, C, 0), + new_hex_assingment(&mut cell, C, A, C, 0), + new_hex_assingment(&mut cell, C, B, C, 0), + new_hex_assingment(&mut cell, C, C, C, 0), + ] }; let coverage_object = file_store::coverage::CoverageObject { @@ -188,40 +194,21 @@ async fn test_footfall_and_urbanization_report(pool: PgPool) -> anyhow::Result<( coverage_claim_time: "2022-01-01 00:00:00.000000000 UTC".parse()?, indoor: true, signature: Vec::new(), - coverage: vec![ - signal_level(&hex1.location, SignalLevel::High)?, - signal_level(&hex2.location, SignalLevel::High)?, - signal_level(&hex3.location, SignalLevel::High)?, - signal_level(&hex4.location, SignalLevel::High)?, - signal_level(&hex5.location, SignalLevel::High)?, - signal_level(&hex6.location, SignalLevel::High)?, - signal_level(&hex7.location, SignalLevel::High)?, - signal_level(&hex8.location, SignalLevel::High)?, - signal_level(&hex9.location, SignalLevel::High)?, - ], + coverage: hexes + .iter() + .map(|hex| signal_level(&hex.location, SignalLevel::High).unwrap()) + .collect(), trust_score: 1000, }; - let mut footfall = HashMap::new(); - footfall.insert(hex_cell(&hex1.location), true); - footfall.insert(hex_cell(&hex2.location), true); - footfall.insert(hex_cell(&hex3.location), true); - footfall.insert(hex_cell(&hex4.location), false); - footfall.insert(hex_cell(&hex5.location), false); - footfall.insert(hex_cell(&hex6.location), false); - - let mut urbanized = HashSet::new(); - urbanized.insert(hex_cell(&hex1.location)); - urbanized.insert(hex_cell(&hex4.location)); - urbanized.insert(hex_cell(&hex7.location)); - - let mut geofence = HashSet::new(); - geofence.insert(hex_cell(&hex1.location)); - geofence.insert(hex_cell(&hex2.location)); - geofence.insert(hex_cell(&hex4.location)); - geofence.insert(hex_cell(&hex5.location)); - geofence.insert(hex_cell(&hex7.location)); - geofence.insert(hex_cell(&hex8.location)); + let mut footfall = HashMap::::new(); + let mut urbanized = HashMap::::new(); + let mut landtype = HashMap::::new(); + for hex in hexes.iter() { + urbanized.insert(hex_cell(&hex.location), hex.urbanized().into()); + footfall.insert(hex_cell(&hex.location), hex.footfall().into()); + landtype.insert(hex_cell(&hex.location), hex.landtype().into()); + } let mut transaction = pool.begin().await?; CoverageObject { @@ -233,83 +220,108 @@ async fn test_footfall_and_urbanization_report(pool: PgPool) -> anyhow::Result<( transaction.commit().await?; let unassigned_hexes = UnassignedHex::fetch(&pool); - let urbanization = UrbanizationData::new(urbanized, geofence); - let hex_boost_data = HexBoostData::new(urbanization, footfall); + let hex_boost_data = common::MockHexAssignments::new(footfall, urbanized, landtype); let oba = set_oracle_boosting_assignments(unassigned_hexes, &hex_boost_data, &pool) .await? .collect::>(); assert_eq!(oba.len(), 1); - assert_eq!( - oba[0].assignments, - vec![hex1, hex2, hex3, hex4, hex5, hex6, hex7, hex8, hex9] - ); + assert_eq!(oba[0].assignments, hexes); Ok(()) } #[sqlx::test] -async fn test_footfall_and_urbanization(pool: PgPool) -> anyhow::Result<()> { +async fn test_footfall_and_urbanization_and_landtype(pool: PgPool) -> anyhow::Result<()> { let start: DateTime = "2022-01-01 00:00:00.000000000 UTC".parse()?; let end: DateTime = "2022-01-02 00:00:00.000000000 UTC".parse()?; + struct TestHex { + loc: String, + landtype: Assignment, + footfall: Assignment, + urbanized: Assignment, + expected_score: Decimal, + } + + impl TestHex { + fn new( + cell: &mut CellIndex, + footfall: Assignment, + landtype: Assignment, + urbanized: Assignment, + expected_score: usize, + ) -> Self { + let loc = cell.to_string(); + *cell = cell.succ().unwrap(); + Self { + loc, + landtype, + footfall, + urbanized, + expected_score: Decimal::from(expected_score), + } + } + } + + let hexes = { + // NOTE(mj): Cell is mutated in constructor to keep elements aligned for readability + let mut cell = CellIndex::try_from(0x8c2681a3064d9ff)?; + use Assignment::*; + vec![ + // yellow - POI ≥ 1 Urbanized + TestHex::new(&mut cell, A, A, A, 400), + TestHex::new(&mut cell, A, B, A, 400), + TestHex::new(&mut cell, A, C, A, 400), + // orange - POI ≥ 1 Not Urbanized + TestHex::new(&mut cell, A, A, B, 400), + TestHex::new(&mut cell, A, B, B, 400), + TestHex::new(&mut cell, A, C, B, 400), + // light green - Point of Interest Urbanized + TestHex::new(&mut cell, B, A, A, 280), + TestHex::new(&mut cell, B, B, A, 280), + TestHex::new(&mut cell, B, C, A, 280), + // dark green - Point of Interest Not Urbanized + TestHex::new(&mut cell, B, A, B, 200), + TestHex::new(&mut cell, B, B, B, 200), + TestHex::new(&mut cell, B, C, B, 200), + // light blue - No POI Urbanized + TestHex::new(&mut cell, C, A, A, 160), + TestHex::new(&mut cell, C, B, A, 120), + TestHex::new(&mut cell, C, C, A, 20), + // dark blue - No POI Not Urbanized + TestHex::new(&mut cell, C, A, B, 80), + TestHex::new(&mut cell, C, B, B, 60), + TestHex::new(&mut cell, C, C, B, 12), + // gray - Outside of USA + TestHex::new(&mut cell, A, A, C, 0), + TestHex::new(&mut cell, A, B, C, 0), + TestHex::new(&mut cell, A, C, C, 0), + TestHex::new(&mut cell, B, A, C, 0), + TestHex::new(&mut cell, B, B, C, 0), + TestHex::new(&mut cell, B, C, C, 0), + TestHex::new(&mut cell, C, A, C, 0), + TestHex::new(&mut cell, C, B, C, 0), + TestHex::new(&mut cell, C, C, C, 0), + ] + }; + let sum = hexes.iter().map(|h| h.expected_score).sum::(); + + assert_eq!(27, hexes.len()); + assert_eq!(dec!(4292), sum); + + let mut footfall = HashMap::new(); + let mut landtype = HashMap::new(); + let mut urbanized = HashMap::new(); + for hex in hexes.iter() { + footfall.insert(hex_cell(&hex.loc), hex.footfall); + urbanized.insert(hex_cell(&hex.loc), hex.urbanized); + landtype.insert(hex_cell(&hex.loc), hex.landtype); + } + let uuid = Uuid::new_v4(); let cbsd_id = "P27-SCE4255W120200039521XGB0102".to_string(); let owner: PublicKeyBinary = "11xtYwQYnvkFYnJ9iZ8kmnetYKwhdi87Mcr36e1pVLrhBMPLjV9".parse()?; - let hex1 = OracleBoostingHexAssignment { - location: "8c2681a3064d9ff".to_string(), - assignment_multiplier: 1000, - urbanized: Assignment::A.into(), - footfall: Assignment::A.into(), - }; - let hex2 = OracleBoostingHexAssignment { - location: "8c2681a3064d1ff".to_string(), - assignment_multiplier: 1000, - urbanized: Assignment::B.into(), - footfall: Assignment::A.into(), - }; - let hex3 = OracleBoostingHexAssignment { - location: "8c450e64dc899ff".to_string(), - assignment_multiplier: 0, - urbanized: Assignment::C.into(), - footfall: Assignment::A.into(), - }; - let hex4 = OracleBoostingHexAssignment { - location: "8c2681a3064dbff".to_string(), - assignment_multiplier: 750, - urbanized: Assignment::A.into(), - footfall: Assignment::B.into(), - }; - let hex5 = OracleBoostingHexAssignment { - location: "8c2681a339365ff".to_string(), - assignment_multiplier: 500, - urbanized: Assignment::B.into(), - footfall: Assignment::B.into(), - }; - let hex6 = OracleBoostingHexAssignment { - location: "8c450e64dc89dff".to_string(), - assignment_multiplier: 0, - urbanized: Assignment::C.into(), - footfall: Assignment::B.into(), - }; - let hex7 = OracleBoostingHexAssignment { - location: "8c2681a3066b3ff".to_string(), - assignment_multiplier: 400, - urbanized: Assignment::A.into(), - footfall: Assignment::C.into(), - }; - let hex8 = OracleBoostingHexAssignment { - location: "8c2681a3066b7ff".to_string(), - assignment_multiplier: 100, - urbanized: Assignment::B.into(), - footfall: Assignment::C.into(), - }; - let hex9 = OracleBoostingHexAssignment { - location: "8c450e64dc883ff".to_string(), - assignment_multiplier: 0, - urbanized: Assignment::C.into(), - footfall: Assignment::C.into(), - }; let coverage_object = file_store::coverage::CoverageObject { pub_key: PublicKeyBinary::from(vec![1]), @@ -318,41 +330,13 @@ async fn test_footfall_and_urbanization(pool: PgPool) -> anyhow::Result<()> { coverage_claim_time: "2022-01-01 00:00:00.000000000 UTC".parse()?, indoor: true, signature: Vec::new(), - coverage: vec![ - signal_level(&hex1.location, SignalLevel::High)?, - signal_level(&hex2.location, SignalLevel::High)?, - signal_level(&hex3.location, SignalLevel::High)?, - signal_level(&hex4.location, SignalLevel::High)?, - signal_level(&hex5.location, SignalLevel::High)?, - signal_level(&hex6.location, SignalLevel::High)?, - signal_level(&hex7.location, SignalLevel::High)?, - signal_level(&hex8.location, SignalLevel::High)?, - signal_level(&hex9.location, SignalLevel::High)?, - ], + coverage: hexes + .iter() + .map(|hex| signal_level(&hex.loc, SignalLevel::High).unwrap()) + .collect(), trust_score: 1000, }; - let mut footfall = HashMap::new(); - footfall.insert(hex_cell(&hex1.location), true); - footfall.insert(hex_cell(&hex2.location), true); - footfall.insert(hex_cell(&hex3.location), true); - footfall.insert(hex_cell(&hex4.location), false); - footfall.insert(hex_cell(&hex5.location), false); - footfall.insert(hex_cell(&hex6.location), false); - - let mut urbanized = HashSet::new(); - urbanized.insert(hex_cell(&hex1.location)); - urbanized.insert(hex_cell(&hex4.location)); - urbanized.insert(hex_cell(&hex7.location)); - - let mut geofence = HashSet::new(); - geofence.insert(hex_cell(&hex1.location)); - geofence.insert(hex_cell(&hex2.location)); - geofence.insert(hex_cell(&hex4.location)); - geofence.insert(hex_cell(&hex5.location)); - geofence.insert(hex_cell(&hex7.location)); - geofence.insert(hex_cell(&hex8.location)); - let mut transaction = pool.begin().await?; CoverageObject { coverage_object, @@ -363,8 +347,7 @@ async fn test_footfall_and_urbanization(pool: PgPool) -> anyhow::Result<()> { transaction.commit().await?; let unassigned_hexes = UnassignedHex::fetch(&pool); - let urbanization = UrbanizationData::new(urbanized, geofence); - let hex_boost_data = HexBoostData::new(urbanization, footfall); + let hex_boost_data = common::MockHexAssignments::new(footfall, urbanized, landtype); let _ = set_oracle_boosting_assignments(unassigned_hexes, &hex_boost_data, &pool).await?; let heartbeats = heartbeats(12, start, &owner, &cbsd_id, 0.0, 0.0, uuid); @@ -424,23 +407,50 @@ async fn test_footfall_and_urbanization(pool: PgPool) -> anyhow::Result<()> { &VerifiedRadioThresholds::default(), &epoch, ) - .await?; - - // Hex | Assignment | Points Equation | Sum - // -------------------------------------------- - // hex1 | A, A | 400 * 1 | 400 - // hex2 | A, B | 400 * 1 | 400 - // hex3 | B, A | 400 * 0.75 | 300 - // hex4 | B, B | 400 * 0.50 | 200 - // hex5 | C, A | 400 * 0.40 | 160 - // hex6 | C, B | 400 * 0.10 | 40 - // hex7 | A, C | 400 * 0.00 | 0 - // hex8 | B, C | 400 * 0.00 | 0 - // hex9 | C, C | 400 * 0.00 | 0 - // ------------------------------------------- - // = 1,500 - - assert_eq!(coverage_points.hotspot_points(&owner), dec!(1500)); + .await + .context("aggregating points")?; + + // (Footfall, Landtype, Urbanized) + // Hex | Assignment | Points Equation | Sum + // ----------------------------------------------- + // == yellow - POI ≥ 1 Urbanized + // hex1 | A, A, A | 400 * 1 | 400 + // hex2 | A, B, A | 400 * 1 | 400 + // hex3 | A, C, A | 400 * 1 | 400 + // == orange - POI ≥ 1 Not Urbanized + // hex4 | A, A, B | 400 * 1 | 400 + // hex5 | A, B, B | 400 * 1 | 400 + // hex6 | A, C, B | 400 * 1 | 400 + // == light green - Point of Interest Urbanized + // hex7 | B, A, A | 400 * 0.70 | 280 + // hex8 | B, B, A | 400 * 0.70 | 280 + // hex9 | B, C, A | 400 * 0.70 | 280 + // == dark green - Point of Interest Not Urbanized + // hex10 | B, A, B | 400 * 0.50 | 200 + // hex11 | B, B, B | 400 * 0.50 | 200 + // hex12 | B, C, B | 400 * 0.50 | 200 + // == light blue - No POI Urbanized + // hex13 | C, A, A | 400 * 0.40 | 160 + // hex14 | C, B, A | 400 * 0.30 | 120 + // hex15 | C, C, A | 400 * 0.05 | 20 + // == dark blue - No POI Not Urbanized + // hex16 | C, A, B | 400 * 0.20 | 80 + // hex17 | C, B, B | 400 * 0.15 | 60 + // hex18 | C, C, B | 400 * 0.03 | 12 + // == gray - Outside of USA + // hex19 | A, A, C | 400 * 0.00 | 0 + // hex20 | A, B, C | 400 * 0.00 | 0 + // hex21 | A, C, C | 400 * 0.00 | 0 + // hex22 | B, A, C | 400 * 0.00 | 0 + // hex23 | B, B, C | 400 * 0.00 | 0 + // hex24 | B, C, C | 400 * 0.00 | 0 + // hex25 | C, A, C | 400 * 0.00 | 0 + // hex26 | C, B, C | 400 * 0.00 | 0 + // hex27 | C, C, C | 400 * 0.00 | 0 + // ----------------------------------------------- + // = 4,292 + + assert_eq!(coverage_points.hotspot_points(&owner), dec!(4292.0)); Ok(()) } diff --git a/mobile_verifier/tests/common/mod.rs b/mobile_verifier/tests/common/mod.rs index c3ae9b8f6..e96ecc411 100644 --- a/mobile_verifier/tests/common/mod.rs +++ b/mobile_verifier/tests/common/mod.rs @@ -7,7 +7,7 @@ use helium_proto::{ Message, }; use mobile_config::boosted_hex_info::BoostedHexInfo; -use mobile_verifier::boosting_oracles::{Assignment, HexAssignment, HexBoostData}; +use mobile_verifier::boosting_oracles::{Assignment, BoostedHexAssignments, HexAssignments}; use std::collections::HashMap; use tokio::{sync::mpsc::error::TryRecvError, time::timeout}; @@ -170,14 +170,36 @@ pub fn seconds(s: u64) -> std::time::Duration { std::time::Duration::from_secs(s) } -pub struct MockHexAssignments; +type MockAssignmentMap = HashMap; + +#[derive(Default)] +pub struct MockHexAssignments { + footfall: MockAssignmentMap, + urbanized: MockAssignmentMap, + landtype: MockAssignmentMap, +} -#[allow(dead_code)] impl MockHexAssignments { - pub fn best() -> HexBoostData { - HexBoostData { - urbanization: Assignment::A, - footfall: Assignment::A, + #[allow(dead_code)] + pub fn new( + footfall: MockAssignmentMap, + urbanized: MockAssignmentMap, + landtype: MockAssignmentMap, + ) -> Self { + Self { + footfall, + urbanized, + landtype, } } } + +impl BoostedHexAssignments for MockHexAssignments { + fn assignments(&self, cell: hextree::Cell) -> anyhow::Result { + Ok(HexAssignments { + footfall: self.footfall.get(&cell).cloned().unwrap_or(Assignment::A), + urbanized: self.urbanized.get(&cell).cloned().unwrap_or(Assignment::A), + landtype: self.landtype.get(&cell).cloned().unwrap_or(Assignment::A), + }) + } +} diff --git a/mobile_verifier/tests/hex_boosting.rs b/mobile_verifier/tests/hex_boosting.rs index e57708ef6..026acf4ca 100644 --- a/mobile_verifier/tests/hex_boosting.rs +++ b/mobile_verifier/tests/hex_boosting.rs @@ -64,7 +64,7 @@ async fn update_assignments(pool: &PgPool) -> anyhow::Result<()> { let unassigned_hexes = UnassignedHex::fetch(pool); let _ = set_oracle_boosting_assignments( unassigned_hexes, - &common::MockHexAssignments::best(), + &common::MockHexAssignments::default(), pool, ) .await?; diff --git a/mobile_verifier/tests/modeled_coverage.rs b/mobile_verifier/tests/modeled_coverage.rs index 0a40b0f22..9af3822d0 100644 --- a/mobile_verifier/tests/modeled_coverage.rs +++ b/mobile_verifier/tests/modeled_coverage.rs @@ -415,7 +415,7 @@ async fn process_input( let unassigned_hexes = UnassignedHex::fetch(pool); let _ = set_oracle_boosting_assignments( unassigned_hexes, - &common::MockHexAssignments::best(), + &common::MockHexAssignments::default(), pool, ) .await?; diff --git a/mobile_verifier/tests/rewarder_poc_dc.rs b/mobile_verifier/tests/rewarder_poc_dc.rs index 25092cc82..2928310b3 100644 --- a/mobile_verifier/tests/rewarder_poc_dc.rs +++ b/mobile_verifier/tests/rewarder_poc_dc.rs @@ -307,7 +307,7 @@ async fn update_assignments(pool: &PgPool) -> anyhow::Result<()> { let unassigned_hexes = UnassignedHex::fetch(pool); let _ = set_oracle_boosting_assignments( unassigned_hexes, - &common::MockHexAssignments::best(), + &common::MockHexAssignments::default(), pool, ) .await?;