diff --git a/Cargo.lock b/Cargo.lock index 4fcba3b7f..71fbe17db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1130,7 +1130,7 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "beacon" version = "0.1.0" -source = "git+https://github.com/helium/proto?branch=master#9fc57133ed1e760c3f1b65dd22d55c09c84832da" +source = "git+https://github.com/helium/proto?branch=andymck/verify-hb-cell-type#16ada190a336eab3adaa5dc783c9d8117e26380c" dependencies = [ "base64 0.21.0", "byteorder", @@ -1140,7 +1140,7 @@ dependencies = [ "rand_chacha 0.3.0", "rust_decimal", "serde", - "sha2 0.9.9", + "sha2 0.10.6", "thiserror", ] @@ -3003,7 +3003,7 @@ dependencies = [ [[package]] name = "helium-proto" version = "0.1.0" -source = "git+https://github.com/helium/proto?branch=master#9fc57133ed1e760c3f1b65dd22d55c09c84832da" +source = "git+https://github.com/helium/proto?branch=andymck/verify-hb-cell-type#16ada190a336eab3adaa5dc783c9d8117e26380c" dependencies = [ "bytes", "prost", @@ -8282,7 +8282,7 @@ dependencies = [ "rand 0.8.5", "serde", "serde_json", - "sha2 0.9.9", + "sha2 0.10.6", "thiserror", "twox-hash", "xorf", diff --git a/Cargo.toml b/Cargo.toml index 5b0d23551..bf7842357 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,14 +61,14 @@ sqlx = {version = "0", features = [ ]} helium-crypto = {version = "0.8.1", features=["sqlx-postgres", "multisig"]} -helium-proto = {git = "https://github.com/helium/proto", branch = "master", features = ["services"]} +helium-proto = {git = "https://github.com/helium/proto", branch = "andymck/verify-hb-cell-type", features = ["services"]} hextree = "*" solana-client = "1.14" solana-sdk = "1.14" solana-program = "1.11" spl-token = "3.5.0" reqwest = {version = "0", default-features=false, features = ["gzip", "json", "rustls-tls"]} -beacon = { git = "https://github.com/helium/proto", branch = "master" } +beacon = { git = "https://github.com/helium/proto", branch = "andymck/verify-hb-cell-type" } humantime = "2" metrics = "0" metrics-exporter-prometheus = "0" diff --git a/mobile_config/src/gateway_info.rs b/mobile_config/src/gateway_info.rs index 2126d2efb..f594d2fb0 100644 --- a/mobile_config/src/gateway_info.rs +++ b/mobile_config/src/gateway_info.rs @@ -3,12 +3,12 @@ use helium_crypto::PublicKeyBinary; use helium_proto::services::mobile_config::{ GatewayInfo as GatewayInfoProto, GatewayMetadata as GatewayMetadataProto, }; - pub type GatewayInfoStream = BoxStream<'static, GatewayInfo>; #[derive(Clone, Debug)] pub struct GatewayMetadata { pub location: u64, + pub device_type: String, } #[derive(Clone, Debug)] @@ -33,11 +33,15 @@ impl From for GatewayInfo { fn from(info: GatewayInfoProto) -> Self { let metadata = if let Some(metadata) = info.metadata { u64::from_str_radix(&metadata.location, 16) - .map(|location| GatewayMetadata { location }) + .map(|location| GatewayMetadata { + location, + device_type: metadata.device_type, + }) .ok() } else { None }; + Self { address: info.address.into(), metadata, @@ -52,6 +56,7 @@ impl TryFrom for GatewayInfoProto { let metadata = if let Some(metadata) = info.metadata { Some(GatewayMetadataProto { location: hextree::Cell::from_raw(metadata.location)?.to_string(), + device_type: metadata.device_type, }) } else { None @@ -67,11 +72,12 @@ pub(crate) mod db { use super::{GatewayInfo, GatewayMetadata}; use futures::stream::{Stream, StreamExt}; use helium_crypto::PublicKeyBinary; + use sqlx::types::Json; use sqlx::{PgExecutor, Row}; use std::str::FromStr; const GET_METADATA_SQL: &str = r#" - select kta.entity_key, infos.location::bigint + select kta.entity_key, infos.location::bigint, infos.device_type from mobile_hotspot_infos infos join key_to_assets kta on infos.asset = kta.asset "#; @@ -102,10 +108,12 @@ pub(crate) mod db { impl sqlx::FromRow<'_, sqlx::postgres::PgRow> for GatewayInfo { fn from_row(row: &sqlx::postgres::PgRow) -> sqlx::Result { + let device_type = row.get::, &str>("device_type").to_string(); let metadata = row .get::, &str>("location") .map(|loc| GatewayMetadata { location: loc as u64, + device_type, }); Ok(Self { address: PublicKeyBinary::from_str( diff --git a/mobile_verifier/src/cell_type.rs b/mobile_verifier/src/cell_type.rs index ad105754e..864198e44 100644 --- a/mobile_verifier/src/cell_type.rs +++ b/mobile_verifier/src/cell_type.rs @@ -3,6 +3,7 @@ use helium_proto::services::poc_mobile::CellType as CellTypeProto; use rust_decimal::Decimal; use rust_decimal_macros::dec; use serde::Serialize; +use std::str::FromStr; pub const CELLTYPE_NOVA_436H: &str = "2AG32MBS3100196N"; pub const CELLTYPE_NOVA_430I: &str = "2AG32PBS3101S"; @@ -27,7 +28,7 @@ pub enum CellType { pub enum CellTypeLabel { CellTypeLabelNone = 0, CBRS = 1, - Wifi = 2, + WifiIndoor = 2, } impl CellType { @@ -50,7 +51,7 @@ impl CellType { Self::SercommIndoor => CellTypeLabel::CBRS, Self::SercommOutdoor => CellTypeLabel::CBRS, Self::CellTypeNone => CellTypeLabel::CellTypeLabelNone, - Self::NovaGenericWifiIndoor => CellTypeLabel::Wifi, + Self::NovaGenericWifiIndoor => CellTypeLabel::WifiIndoor, } } @@ -102,3 +103,14 @@ impl From for CellTypeProto { } } } + +impl FromStr for CellTypeLabel { + type Err = anyhow::Error; + fn from_str(s: &str) -> anyhow::Result { + match s { + "wifiIndoor" => Ok(CellTypeLabel::WifiIndoor), + "cbrs" => Ok(CellTypeLabel::CBRS), + _ => anyhow::bail!("unknown cell type"), + } + } +} diff --git a/mobile_verifier/src/heartbeats/mod.rs b/mobile_verifier/src/heartbeats/mod.rs index b45e9c4e9..847d8c309 100644 --- a/mobile_verifier/src/heartbeats/mod.rs +++ b/mobile_verifier/src/heartbeats/mod.rs @@ -19,7 +19,7 @@ use helium_proto::services::poc_mobile as proto; use retainer::Cache; use rust_decimal::{prelude::ToPrimitive, Decimal}; use sqlx::{postgres::PgTypeInfo, Decode, Encode, Postgres, Transaction, Type}; -use std::{ops::Range, pin::pin, time}; +use std::{ops::Range, pin::pin, str::FromStr, time}; use uuid::Uuid; /// Minimum number of heartbeats required to give a reward to the hotspot. @@ -247,7 +247,7 @@ impl HeartbeatReward { .cbsd_id .clone() .ok_or_else(|| anyhow!("expected cbsd_id, found none"))?), - CellTypeLabel::Wifi => Ok(self.hotspot_key.to_string()), + CellTypeLabel::WifiIndoor => Ok(self.hotspot_key.to_string()), _ => Err(anyhow!("failed to derive label from cell type")), } } @@ -504,8 +504,15 @@ pub async fn validate_heartbeat( proto::HeartbeatValidity::GatewayNotAsserted, )) } - GatewayResolution::AssertedLocation(location) if heartbeat.hb_type == HbType::Wifi => { - Some(heartbeat.asserted_distance(location)?) + GatewayResolution::GatewayAsserted(metadata) if heartbeat.hb_type == HbType::Wifi => { + // for wifi HBs, check the asserted device type matches that defined in the HB + let asserted_celltype_label = CellTypeLabel::from_str(&metadata.device_type)?; + if asserted_celltype_label == CellTypeLabel::WifiIndoor + && cell_type.to_label() != asserted_celltype_label + { + return Ok((cell_type, None, None, proto::HeartbeatValidity::BadCellType)); + }; + Some(heartbeat.asserted_distance(metadata.location)?) } _ => None, }; @@ -529,7 +536,6 @@ pub async fn validate_heartbeat( let Ok(latlng) = LatLng::new(heartbeat.report.lat, heartbeat.report.lon) else { return Ok((cell_type, None, proto::HeartbeatValidity::InvalidLatLon)); - }; if coverage.max_distance_km(latlng) > max_distance { return Ok(( diff --git a/mobile_verifier/src/lib.rs b/mobile_verifier/src/lib.rs index d9d9ff121..e648f834c 100644 --- a/mobile_verifier/src/lib.rs +++ b/mobile_verifier/src/lib.rs @@ -15,10 +15,11 @@ pub use settings::Settings; use async_trait::async_trait; +use mobile_config::gateway_info::GatewayMetadata; pub enum GatewayResolution { GatewayNotFound, GatewayNotAsserted, - AssertedLocation(u64), + GatewayAsserted(GatewayMetadata), } #[async_trait] @@ -45,7 +46,7 @@ impl GatewayResolver for mobile_config::GatewayClient { Some(GatewayInfo { metadata: Some(metadata), .. - }) => Ok(GatewayResolution::AssertedLocation(metadata.location)), + }) => Ok(GatewayResolution::GatewayAsserted(metadata)), Some(_) => Ok(GatewayResolution::GatewayNotAsserted), } }