diff --git a/packages/rs-sdk/src/mock/sdk.rs b/packages/rs-sdk/src/mock/sdk.rs index aef9960758..a6d75d829f 100644 --- a/packages/rs-sdk/src/mock/sdk.rs +++ b/packages/rs-sdk/src/mock/sdk.rs @@ -244,6 +244,9 @@ impl MockDashPlatformSdk { &mut dapi, filename, )? } + "GetAddressesTrunkStateRequest" => { + load_expectation::(&mut dapi, filename)? + } _ => { return Err(Error::Config(format!( "unknown request type {} in {}, missing match arm in load_expectations?", diff --git a/packages/rs-sdk/src/sdk.rs b/packages/rs-sdk/src/sdk.rs index adb4a35ded..5c6b913aab 100644 --- a/packages/rs-sdk/src/sdk.rs +++ b/packages/rs-sdk/src/sdk.rs @@ -261,11 +261,16 @@ impl Sdk { } /// Return freshness criteria (height tolerance and time tolerance) for given request method. + /// + /// Note that if self.metadata_height_tolerance or self.metadata_time_tolerance_ms is None, + /// respective tolerance will be None regardless of method, to allow disabling staleness checks globally. fn freshness_criteria(&self, method_name: &str) -> (Option, Option) { match method_name { - "get_addresses_trunk_state" | "get_addresses_branch_state" => { - (None, Some(ADDRESS_STATE_TIME_TOLERANCE_MS)) - } + "get_addresses_trunk_state" | "get_addresses_branch_state" => ( + None, + self.metadata_time_tolerance_ms + .and(Some(ADDRESS_STATE_TIME_TOLERANCE_MS)), + ), _ => ( self.metadata_height_tolerance, self.metadata_time_tolerance_ms, diff --git a/packages/rs-sdk/tests/fetch/address_sync.rs b/packages/rs-sdk/tests/fetch/address_sync.rs new file mode 100644 index 0000000000..5ecf20f1d4 --- /dev/null +++ b/packages/rs-sdk/tests/fetch/address_sync.rs @@ -0,0 +1,103 @@ +use dash_sdk::platform::address_sync::{ + sync_address_balances, AddressIndex, AddressKey, AddressProvider, +}; +use std::collections::{BTreeMap, BTreeSet}; + +use super::{ + common::setup_logs, + config::Config, + generated_data::{ + PLATFORM_ADDRESS_1, PLATFORM_ADDRESS_1_BALANCE, PLATFORM_ADDRESS_2, + PLATFORM_ADDRESS_2_BALANCE, UNKNOWN_PLATFORM_ADDRESS, + }, +}; + +struct TestAddressProvider { + gap_limit: AddressIndex, + pending: BTreeMap, + found: BTreeMap<(AddressIndex, AddressKey), u64>, + absent: BTreeSet<(AddressIndex, AddressKey)>, + highest_found_index: Option, +} + +impl TestAddressProvider { + fn new(pending: Vec<(AddressIndex, AddressKey)>) -> Self { + Self { + gap_limit: 0, + pending: pending.into_iter().collect(), + found: BTreeMap::new(), + absent: BTreeSet::new(), + highest_found_index: None, + } + } +} + +impl AddressProvider for TestAddressProvider { + fn gap_limit(&self) -> AddressIndex { + self.gap_limit + } + + fn pending_addresses(&self) -> Vec<(AddressIndex, AddressKey)> { + self.pending + .iter() + .map(|(index, key)| (*index, key.clone())) + .collect() + } + + fn on_address_found(&mut self, index: AddressIndex, key: &[u8], balance: u64) { + self.found.insert((index, key.to_vec()), balance); + self.pending.remove(&index); + self.highest_found_index = Some(self.highest_found_index.map_or(index, |v| v.max(index))); + } + + fn on_address_absent(&mut self, index: AddressIndex, key: &[u8]) { + self.absent.insert((index, key.to_vec())); + self.pending.remove(&index); + } + + fn highest_found_index(&self) -> Option { + self.highest_found_index + } +} + +/// Given several platform addresses, when I sync balances, I get found and absent entries. +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn test_sync_address_balances() { + setup_logs(); + + let cfg = Config::new(); + let sdk = cfg.setup_api("test_sync_address_balances").await; + + let key_1 = PLATFORM_ADDRESS_1.to_bytes(); + let key_2 = PLATFORM_ADDRESS_2.to_bytes(); + let key_unknown = UNKNOWN_PLATFORM_ADDRESS.to_bytes(); + + let mut provider = TestAddressProvider::new(vec![ + (0, key_1.clone()), + (1, key_2.clone()), + (2, key_unknown.clone()), + ]); + + let result = sync_address_balances(&sdk, &mut provider, None) + .await + .expect("sync address balances"); + + assert_eq!(result.found.len(), 2); + assert_eq!(result.absent.len(), 1); + + assert_eq!( + result.found.get(&(0, key_1.clone())), + Some(&PLATFORM_ADDRESS_1_BALANCE) + ); + assert_eq!( + result.found.get(&(1, key_2.clone())), + Some(&PLATFORM_ADDRESS_2_BALANCE) + ); + assert!(result.absent.contains(&(2, key_unknown.clone()))); + + assert_eq!( + result.total_balance(), + PLATFORM_ADDRESS_1_BALANCE + PLATFORM_ADDRESS_2_BALANCE + ); + assert_eq!(result.highest_found_index, Some(1)); +} diff --git a/packages/rs-sdk/tests/fetch/mod.rs b/packages/rs-sdk/tests/fetch/mod.rs index 4f11872553..83fc9a97b0 100644 --- a/packages/rs-sdk/tests/fetch/mod.rs +++ b/packages/rs-sdk/tests/fetch/mod.rs @@ -7,6 +7,7 @@ compile_error!("network-testing or offline-testing must be enabled for tests"); #[cfg(feature = "mocks")] mod address_funds; +mod address_sync; mod broadcast; mod common; mod config; diff --git a/packages/rs-sdk/tests/vectors/test_sync_address_balances/.gitkeep b/packages/rs-sdk/tests/vectors/test_sync_address_balances/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/rs-sdk/tests/vectors/test_sync_address_balances/msg_GetAddressesTrunkStateRequest_c49aeef007d9b94685bb7a38fa0daa7f11dfff63969ad07d74a36a45514c3d33.json b/packages/rs-sdk/tests/vectors/test_sync_address_balances/msg_GetAddressesTrunkStateRequest_c49aeef007d9b94685bb7a38fa0daa7f11dfff63969ad07d74a36a45514c3d33.json new file mode 100644 index 0000000000..1b1ac88315 Binary files /dev/null and b/packages/rs-sdk/tests/vectors/test_sync_address_balances/msg_GetAddressesTrunkStateRequest_c49aeef007d9b94685bb7a38fa0daa7f11dfff63969ad07d74a36a45514c3d33.json differ diff --git a/packages/rs-sdk/tests/vectors/test_sync_address_balances/quorum_pubkey-106-498b4b317d090a9f3e372986bed30f7154967d852f194ec6441fa5a1c233380e.json b/packages/rs-sdk/tests/vectors/test_sync_address_balances/quorum_pubkey-106-498b4b317d090a9f3e372986bed30f7154967d852f194ec6441fa5a1c233380e.json new file mode 100644 index 0000000000..2e0f932198 --- /dev/null +++ b/packages/rs-sdk/tests/vectors/test_sync_address_balances/quorum_pubkey-106-498b4b317d090a9f3e372986bed30f7154967d852f194ec6441fa5a1c233380e.json @@ -0,0 +1 @@ +ac2e6c1fbb7b15aa464d03a86801ca28c2e3cc341189d1bff1bc8c3b26ab5c137fddb7b9ca6a9fb828d07b10a64b3a25 \ No newline at end of file