diff --git a/Cargo.lock b/Cargo.lock index d5c06de6c..5893b47cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1734,8 +1734,8 @@ dependencies = [ [[package]] name = "dapi-grpc" -version = "3.0.0-dev.11" -source = "git+https://github.com/dashpay/platform.git?rev=eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167#eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167" +version = "3.0.0-rc.1" +source = "git+https://github.com/dashpay/platform?rev=a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17#a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17" dependencies = [ "dash-platform-macros", "futures-core", @@ -1802,8 +1802,8 @@ dependencies = [ [[package]] name = "dash-context-provider" -version = "3.0.0-dev.11" -source = "git+https://github.com/dashpay/platform.git?rev=eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167#eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167" +version = "3.0.0-rc.1" +source = "git+https://github.com/dashpay/platform?rev=a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17#a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17" dependencies = [ "dpp", "drive", @@ -1891,8 +1891,8 @@ dependencies = [ [[package]] name = "dash-platform-macros" -version = "3.0.0-dev.11" -source = "git+https://github.com/dashpay/platform.git?rev=eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167#eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167" +version = "3.0.0-rc.1" +source = "git+https://github.com/dashpay/platform?rev=a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17#a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17" dependencies = [ "heck", "quote", @@ -1901,8 +1901,8 @@ dependencies = [ [[package]] name = "dash-sdk" -version = "3.0.0-dev.11" -source = "git+https://github.com/dashpay/platform.git?rev=eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167#eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167" +version = "3.0.0-rc.1" +source = "git+https://github.com/dashpay/platform?rev=a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17#a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17" dependencies = [ "arc-swap", "async-trait", @@ -2051,8 +2051,8 @@ dependencies = [ [[package]] name = "dashpay-contract" -version = "3.0.0-dev.11" -source = "git+https://github.com/dashpay/platform.git?rev=eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167#eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167" +version = "3.0.0-rc.1" +source = "git+https://github.com/dashpay/platform?rev=a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17#a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17" dependencies = [ "platform-value", "platform-version", @@ -2062,8 +2062,8 @@ dependencies = [ [[package]] name = "data-contracts" -version = "3.0.0-dev.11" -source = "git+https://github.com/dashpay/platform.git?rev=eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167#eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167" +version = "3.0.0-rc.1" +source = "git+https://github.com/dashpay/platform?rev=a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17#a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17" dependencies = [ "dashpay-contract", "dpns-contract", @@ -2324,8 +2324,8 @@ checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" [[package]] name = "dpns-contract" -version = "3.0.0-dev.11" -source = "git+https://github.com/dashpay/platform.git?rev=eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167#eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167" +version = "3.0.0-rc.1" +source = "git+https://github.com/dashpay/platform?rev=a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17#a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17" dependencies = [ "platform-value", "platform-version", @@ -2335,8 +2335,8 @@ dependencies = [ [[package]] name = "dpp" -version = "3.0.0-dev.11" -source = "git+https://github.com/dashpay/platform.git?rev=eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167#eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167" +version = "3.0.0-rc.1" +source = "git+https://github.com/dashpay/platform?rev=a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17#a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17" dependencies = [ "anyhow", "async-trait", @@ -2384,8 +2384,8 @@ dependencies = [ [[package]] name = "drive" -version = "3.0.0-dev.11" -source = "git+https://github.com/dashpay/platform.git?rev=eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167#eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167" +version = "3.0.0-rc.1" +source = "git+https://github.com/dashpay/platform?rev=a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17#a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17" dependencies = [ "bincode 2.0.0-rc.3", "byteorder", @@ -2409,8 +2409,8 @@ dependencies = [ [[package]] name = "drive-proof-verifier" -version = "3.0.0-dev.11" -source = "git+https://github.com/dashpay/platform.git?rev=eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167#eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167" +version = "3.0.0-rc.1" +source = "git+https://github.com/dashpay/platform?rev=a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17#a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17" dependencies = [ "bincode 2.0.0-rc.3", "dapi-grpc", @@ -2985,8 +2985,8 @@ dependencies = [ [[package]] name = "feature-flags-contract" -version = "3.0.0-dev.11" -source = "git+https://github.com/dashpay/platform.git?rev=eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167#eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167" +version = "3.0.0-rc.1" +source = "git+https://github.com/dashpay/platform?rev=a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17#a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17" dependencies = [ "platform-value", "platform-version", @@ -4552,8 +4552,8 @@ dependencies = [ [[package]] name = "keyword-search-contract" -version = "3.0.0-dev.11" -source = "git+https://github.com/dashpay/platform.git?rev=eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167#eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167" +version = "3.0.0-rc.1" +source = "git+https://github.com/dashpay/platform?rev=a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17#a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17" dependencies = [ "platform-value", "platform-version", @@ -4778,8 +4778,8 @@ dependencies = [ [[package]] name = "masternode-reward-shares-contract" -version = "3.0.0-dev.11" -source = "git+https://github.com/dashpay/platform.git?rev=eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167#eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167" +version = "3.0.0-rc.1" +source = "git+https://github.com/dashpay/platform?rev=a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17#a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17" dependencies = [ "platform-value", "platform-version", @@ -5844,8 +5844,8 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "platform-serialization" -version = "3.0.0-dev.11" -source = "git+https://github.com/dashpay/platform.git?rev=eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167#eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167" +version = "3.0.0-rc.1" +source = "git+https://github.com/dashpay/platform?rev=a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17#a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17" dependencies = [ "bincode 2.0.0-rc.3", "platform-version", @@ -5853,8 +5853,8 @@ dependencies = [ [[package]] name = "platform-serialization-derive" -version = "3.0.0-dev.11" -source = "git+https://github.com/dashpay/platform.git?rev=eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167#eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167" +version = "3.0.0-rc.1" +source = "git+https://github.com/dashpay/platform?rev=a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17#a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17" dependencies = [ "proc-macro2", "quote", @@ -5864,8 +5864,8 @@ dependencies = [ [[package]] name = "platform-value" -version = "3.0.0-dev.11" -source = "git+https://github.com/dashpay/platform.git?rev=eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167#eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167" +version = "3.0.0-rc.1" +source = "git+https://github.com/dashpay/platform?rev=a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17#a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17" dependencies = [ "base64 0.22.1", "bincode 2.0.0-rc.3", @@ -5884,8 +5884,8 @@ dependencies = [ [[package]] name = "platform-version" -version = "3.0.0-dev.11" -source = "git+https://github.com/dashpay/platform.git?rev=eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167#eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167" +version = "3.0.0-rc.1" +source = "git+https://github.com/dashpay/platform?rev=a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17#a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17" dependencies = [ "bincode 2.0.0-rc.3", "grovedb-version 4.0.0", @@ -5896,8 +5896,8 @@ dependencies = [ [[package]] name = "platform-versioning" -version = "3.0.0-dev.11" -source = "git+https://github.com/dashpay/platform.git?rev=eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167#eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167" +version = "3.0.0-rc.1" +source = "git+https://github.com/dashpay/platform?rev=a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17#a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17" dependencies = [ "proc-macro2", "quote", @@ -6507,8 +6507,8 @@ checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" [[package]] name = "rs-dapi-client" -version = "3.0.0-dev.11" -source = "git+https://github.com/dashpay/platform.git?rev=eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167#eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167" +version = "3.0.0-rc.1" +source = "git+https://github.com/dashpay/platform?rev=a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17#a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17" dependencies = [ "backon", "chrono", @@ -7636,8 +7636,8 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "token-history-contract" -version = "3.0.0-dev.11" -source = "git+https://github.com/dashpay/platform.git?rev=eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167#eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167" +version = "3.0.0-rc.1" +source = "git+https://github.com/dashpay/platform?rev=a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17#a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17" dependencies = [ "platform-value", "platform-version", @@ -8376,8 +8376,8 @@ dependencies = [ [[package]] name = "wallet-utils-contract" -version = "3.0.0-dev.11" -source = "git+https://github.com/dashpay/platform.git?rev=eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167#eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167" +version = "3.0.0-rc.1" +source = "git+https://github.com/dashpay/platform?rev=a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17#a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17" dependencies = [ "platform-value", "platform-version", @@ -9631,8 +9631,8 @@ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "withdrawals-contract" -version = "3.0.0-dev.11" -source = "git+https://github.com/dashpay/platform.git?rev=eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167#eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167" +version = "3.0.0-rc.1" +source = "git+https://github.com/dashpay/platform?rev=a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17#a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17" dependencies = [ "num_enum 0.5.11", "platform-value", diff --git a/Cargo.toml b/Cargo.toml index acae0381e..76f09ec19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ qrcode = "0.14.1" nix = { version = "0.30.1", features = ["signal"] } eframe = { version = "0.32.0", features = ["persistence"] } base64 = "0.22.1" -dash-sdk = { git = "https://github.com/dashpay/platform.git", rev = "eace6d1c4563c3d9d58e6a12f4d5ed8bdd53d167", features = [ +dash-sdk = { git = "https://github.com/dashpay/platform", rev = "a3d135c0e4baa5e6690cea99d1c79e2e5ad50e17", features = [ "core_key_wallet", "core_key_wallet_manager", "core_bincode", diff --git a/src/backend_task/identity/register_identity.rs b/src/backend_task/identity/register_identity.rs index 3674d1990..ae015bb2d 100644 --- a/src/backend_task/identity/register_identity.rs +++ b/src/backend_task/identity/register_identity.rs @@ -4,18 +4,22 @@ use crate::context::AppContext; use crate::model::fee_estimation::PlatformFeeEstimator; use crate::model::proof_log_item::{ProofLogItem, RequestType}; use crate::model::qualified_identity::{IdentityStatus, IdentityType, QualifiedIdentity}; +use dash_sdk::dash_spv::Network; use dash_sdk::dashcore_rpc::RpcApi; use dash_sdk::dpp::ProtocolError; +use dash_sdk::dpp::address_funds::PlatformAddress; use dash_sdk::dpp::block::extended_epoch_info::ExtendedEpochInfo; use dash_sdk::dpp::dashcore::hashes::Hash; use dash_sdk::dpp::dashcore::{OutPoint, PrivateKey}; +use dash_sdk::dpp::fee::Credits; use dash_sdk::dpp::identity::state_transition::asset_lock_proof::chain::ChainAssetLockProof; use dash_sdk::dpp::native_bls::NativeBlsModule; -use dash_sdk::dpp::prelude::AssetLockProof; +use dash_sdk::dpp::prelude::{AddressNonce, AssetLockProof}; use dash_sdk::dpp::state_transition::identity_create_transition::IdentityCreateTransition; use dash_sdk::dpp::state_transition::identity_create_transition::methods::IdentityCreateTransitionMethodsV0; use dash_sdk::platform::transition::put_identity::PutIdentity; use dash_sdk::platform::{Fetch, Identity}; +use dash_sdk::query_types::AddressInfo; use dash_sdk::{Error, Sdk}; use std::collections::BTreeMap; use std::time::Duration; @@ -223,14 +227,25 @@ impl AppContext { inputs, wallet_seed_hash, } => { - // This is a separate flow - we call a dedicated function for Platform address funding + // inputs with nonces, incremented by 1 from current nonce + let inputs_with_nonces = inputs + .into_iter() + .map(|(addr, credits)| { + self.get_platform_address_best_info(&addr, self.network) + .map(|info| (addr, (info.nonce.saturating_add(1), credits))) + }) + .collect::>>() + .ok_or(String::from( + "Each input platform address must be present in at least one wallet", + ))?; + return self .register_identity_from_platform_addresses( alias_input, keys, wallet, wallet_identity_index, - inputs, + inputs_with_nonces, wallet_seed_hash, ) .await; @@ -674,7 +689,10 @@ impl AppContext { } } - /// Register a new identity funded by Platform addresses + /// Register a new identity funded by Platform addresses. + /// + /// `inputs` is a map of Platform addresses to (nonce, credits) tuples. Nonces must be incremented by 1 + /// from the current nonce of the address. async fn register_identity_from_platform_addresses( &self, alias_input: String, @@ -683,7 +701,7 @@ impl AppContext { wallet_identity_index: u32, inputs: BTreeMap< dash_sdk::dpp::address_funds::PlatformAddress, - dash_sdk::dpp::fee::Credits, + (AddressNonce, dash_sdk::dpp::fee::Credits), >, wallet_seed_hash: super::WalletSeedHash, ) -> Result { @@ -708,17 +726,12 @@ impl AppContext { // Clone the wallet for use as the address signer (needed across async boundary) let wallet_clone = { wallet.read().map_err(|e| e.to_string())?.clone() }; - // For Platform address funding, we need to compute the identity ID from the inputs - // The SDK will handle this internally when creating the identity - // We create a temporary identity with a placeholder ID, which will be computed correctly - // during the state transition creation - - // Create a temporary identity ID - will be replaced by the actual one from Platform - let temp_identity_id = dash_sdk::platform::Identifier::random(); - - let identity = - Identity::new_with_id_and_keys(temp_identity_id, public_keys.clone(), sdk.version()) - .map_err(|e| format!("Failed to create identity: {}", e))?; + let identity = Identity::new_with_input_addresses_and_keys( + &inputs, + public_keys.clone(), + sdk.version(), + ) + .map_err(|e| format!("Failed to create identity: {}", e))?; let wallet_seed_hash_actual = { wallet.read().unwrap().seed_hash() }; let mut qualified_identity = QualifiedIdentity { @@ -825,4 +838,39 @@ impl AppContext { } } } + + /// Get the best (most recent nonce) AddressInfo from all wallets for the given [PlatformAddress] in current [Self::network]. + /// + /// Returns `None`` if no info is found. + fn get_platform_address_best_info( + &self, + platform_address: &PlatformAddress, + network: Network, + ) -> Option { + let generic_address = platform_address.to_address_with_network(network); + let wallets = self + .wallets + .read() + .inspect_err(|e| tracing::error!(err=%e, "wallet lock poisoned")) + .ok()?; + + let mut recent_info: Option = None; + for wallet in wallets.values() { + let wallet_guard = wallet.read().ok()?; + + if let Some(new_info) = wallet_guard.get_platform_address_info(&generic_address) + && recent_info + .as_ref() + .is_none_or(|recent| new_info.nonce > recent.nonce) + { + recent_info = Some(AddressInfo { + address: *platform_address, + balance: new_info.balance, + nonce: new_info.nonce, + }); + } + } + + recent_info + } } diff --git a/src/backend_task/mod.rs b/src/backend_task/mod.rs index 07428534e..7afd08192 100644 --- a/src/backend_task/mod.rs +++ b/src/backend_task/mod.rs @@ -9,6 +9,8 @@ use crate::backend_task::platform_info::{PlatformInfoTaskRequestType, PlatformIn use crate::backend_task::system_task::SystemTask; use crate::backend_task::wallet::WalletTask; use crate::context::AppContext; +use dash_sdk::dpp::dashcore::Address; +use dash_sdk::dpp::dashcore::address::NetworkChecked; use dash_sdk::dpp::dashcore::bls_sig_utils::BLSSignature; use dash_sdk::dpp::dashcore::network::message_qrinfo::QRInfo; use dash_sdk::dpp::dashcore::BlockHash; @@ -173,8 +175,8 @@ pub enum BackendTaskSuccessResult { /// Platform address balances fetched from Platform PlatformAddressBalances { seed_hash: WalletSeedHash, - /// Map of address string to (balance, nonce) - balances: BTreeMap, + /// Map of address to (balance, nonce) + balances: BTreeMap, (u64, u32)>, }, /// Platform credits transferred between addresses PlatformCreditsTransferred { diff --git a/src/backend_task/wallet/fetch_platform_address_balances.rs b/src/backend_task/wallet/fetch_platform_address_balances.rs index b95c48426..65ead0b35 100644 --- a/src/backend_task/wallet/fetch_platform_address_balances.rs +++ b/src/backend_task/wallet/fetch_platform_address_balances.rs @@ -149,15 +149,16 @@ impl AppContext { ); // Log the found balances from provider - for (addr, balance) in provider.found_balances() { + for (addr, funds) in provider.found_balances() { use dash_sdk::dpp::address_funds::PlatformAddress; let platform_addr_str = PlatformAddress::try_from(addr.clone()) .map(|p| p.to_bech32m_string(self.network)) .unwrap_or_else(|_| addr.to_string()); tracing::info!( - "Sync found address: {} with balance: {}", + "Sync found address: {} with balance: {}, nonce: {}", platform_addr_str, - balance + funds.balance, + funds.nonce ); } @@ -248,7 +249,7 @@ impl AppContext { provider.apply_results_to_wallet(&mut wallet); // Persist addresses and balances to database - for (index, (address, balance)) in provider.found_balances_with_indices() { + for (index, (address, funds)) in provider.found_balances_with_indices() { // Persist the address to wallet_addresses table if not already there let derivation_path = DerivationPath::platform_payment_path( self.network, @@ -269,34 +270,23 @@ impl AppContext { } // Persist balance to platform_address_balances table - let nonce = wallet - .platform_address_info - .get(address) - .map(|info| info.nonce) - .unwrap_or(0); + // Use the nonce from AddressFunds which comes directly from SDK sync if let Err(e) = self.db.set_platform_address_info( &seed_hash, address, - *balance, - nonce, + funds.balance, + funds.nonce, &self.network, ) { tracing::warn!("Failed to persist Platform address info: {}", e); } } - // Return balances for result (nonce preserved from existing info or 0) + // Return balances for result (use nonce from AddressFunds) provider .found_balances() .iter() - .map(|(addr, bal)| { - let nonce = wallet - .platform_address_info - .get(addr) - .map(|info| info.nonce) - .unwrap_or(0); - (addr.to_string(), (*bal, nonce)) - }) + .map(|(addr, funds)| (addr.clone(), (funds.balance, funds.nonce))) .collect() }; @@ -408,7 +398,7 @@ impl AppContext { let current_balance = provider .found_balances() .get(&core_addr) - .copied() + .map(|funds| funds.balance) .unwrap_or(0); let new_balance = match credit_op { @@ -495,7 +485,7 @@ impl AppContext { let current_balance = provider .found_balances() .get(&core_addr) - .copied() + .map(|funds| funds.balance) .unwrap_or(0); let new_balance = match credit_op { diff --git a/src/database/wallet.rs b/src/database/wallet.rs index 5a4f4f4fd..eae4c5b3a 100644 --- a/src/database/wallet.rs +++ b/src/database/wallet.rs @@ -583,9 +583,20 @@ impl Database { total_received, ) = row?; if let Some(wallet) = wallets_map.get_mut(&seed_array) { + // Canonicalize Platform addresses to avoid duplicate representations + let canonical_address = Wallet::canonical_address(&address, *network); + // Update the address balance if available. if let Some(balance) = balance { - wallet.address_balances.insert(address.clone(), balance); + wallet + .address_balances + .insert(canonical_address.clone(), balance); + } + // Update total received if available. + if let Some(total_received) = total_received { + wallet + .address_total_received + .insert(canonical_address.clone(), total_received); } // Update total received if available. if let Some(total_received) = total_received { @@ -597,16 +608,16 @@ impl Database { // Add the address to the `known_addresses` map. wallet .known_addresses - .insert(address.clone(), derivation_path.clone()); + .insert(canonical_address.clone(), derivation_path.clone()); tracing::trace!( - address = ?address, + address = ?canonical_address, network = address.network().to_string(), expected_network = network.to_string(), "loaded address from database"); // Add the address to the `watched_addresses` map with AddressInfo. let address_info = AddressInfo { - address: address.clone(), + address: canonical_address.clone(), path_reference, path_type, }; @@ -843,9 +854,18 @@ impl Database { && let Some(wallet) = wallets_map.get_mut(&seed_hash) && let Ok(address) = Address::::from_str(&address_str) { - let address = address.assume_checked(); + let address_checked = address.require_network(*network).map_err(|e| { + tracing::error!(address = %address_str, error = ?e, "Failed to validate Platform address for network"); + rusqlite::Error::FromSqlConversionFailure( + 1, + rusqlite::types::Type::Text, + Box::new(std::fmt::Error), + ) + })?; + let canonical_address = Wallet::canonical_address(&address_checked, *network); + wallet.platform_address_info.insert( - address, + canonical_address, crate::model::wallet::PlatformAddressInfo { balance, nonce, @@ -870,7 +890,8 @@ impl Database { network: &Network, ) -> rusqlite::Result<()> { let network_str = network.to_string(); - let address_str = address.to_string(); + let canonical_address = Wallet::canonical_address(address, *network); + let address_str = canonical_address.to_string(); let updated_at = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap_or_default() @@ -901,7 +922,8 @@ impl Database { ) -> rusqlite::Result> { let conn = self.conn.lock().unwrap(); let network_str = network.to_string(); - let address_str = address.to_string(); + let canonical_address = Wallet::canonical_address(address, *network); + let address_str = canonical_address.to_string(); let mut stmt = conn.prepare( "SELECT balance, nonce FROM platform_address_balances @@ -946,8 +968,15 @@ impl Database { for row in rows { let (address_str, balance, nonce) = row?; if let Ok(address) = Address::::from_str(&address_str) { - let address = address.assume_checked(); - results.push((address, balance, nonce)); + let address_checked = address.require_network(*network).map_err(|e| { + rusqlite::Error::FromSqlConversionFailure( + 1, + rusqlite::types::Type::Text, + Box::new(e), + ) + })?; + let canonical_address = Wallet::canonical_address(&address_checked, *network); + results.push((canonical_address, balance, nonce)); } } diff --git a/src/model/wallet/mod.rs b/src/model/wallet/mod.rs index 2efd99089..9be4def6e 100644 --- a/src/model/wallet/mod.rs +++ b/src/model/wallet/mod.rs @@ -12,7 +12,7 @@ use dash_sdk::dpp::key_wallet::bip32::{ }; use dash_sdk::dpp::key_wallet::psbt::serialize::Serialize; use dash_sdk::dpp::prelude::AddressNonce; -use dash_sdk::platform::address_sync::{AddressIndex, AddressKey, AddressProvider}; +use dash_sdk::platform::address_sync::{AddressFunds, AddressIndex, AddressKey, AddressProvider}; use dash_sdk::dpp::dashcore::secp256k1::{Message, Secp256k1}; use dash_sdk::dpp::dashcore::sighash::SighashCache; @@ -464,6 +464,16 @@ impl Drop for WalletSeed { } impl Wallet { + /// Convert a Platform address to a canonical Core address representation for map keys. + /// + /// This ensures we always use the same `dashcore::Address` instance for a given Platform + /// address, avoiding duplicate map entries caused by different internal representations. + pub(crate) fn canonical_address(address: &Address, network: Network) -> Address { + PlatformAddress::try_from(address.clone()) + .map(|pa| pa.to_address_with_network(network)) + .unwrap_or_else(|_| address.clone()) + } + pub fn is_open(&self) -> bool { matches!(self.wallet_seed, WalletSeed::Open(_)) } @@ -1260,13 +1270,15 @@ impl Wallet { path_reference: DerivationPathReference, app_context: &AppContext, ) -> Result<(), String> { + let canonical_address = Wallet::canonical_address(&address, app_context.network); + // Store the address in known_addresses and watched_addresses // Note: We don't import to Core wallet since Platform addresses are not valid there app_context .db .add_address_if_not_exists( &self.seed_hash(), - &address, + &canonical_address, &app_context.network, derivation_path, path_reference, @@ -1276,11 +1288,11 @@ impl Wallet { .map_err(|e| e.to_string())?; self.known_addresses - .insert(address.clone(), derivation_path.clone()); + .insert(canonical_address.clone(), derivation_path.clone()); self.watched_addresses.insert( derivation_path.clone(), AddressInfo { - address: address.clone(), + address: canonical_address.clone(), path_type, path_reference, }, @@ -2092,7 +2104,7 @@ pub struct WalletAddressProvider { /// Highest index found with a non-zero balance highest_found: Option, /// Results: address -> balance for addresses found with balance - found_balances: BTreeMap, + found_balances: BTreeMap, } impl WalletAddressProvider { @@ -2144,7 +2156,7 @@ impl WalletAddressProvider { /// Get the found balances after sync is complete. /// /// Returns a map of Core Address -> balance (in credits). - pub fn found_balances(&self) -> &BTreeMap { + pub fn found_balances(&self) -> &BTreeMap { &self.found_balances } @@ -2154,7 +2166,7 @@ impl WalletAddressProvider { /// The index can be used to reconstruct the derivation path. pub fn found_balances_with_indices( &self, - ) -> impl Iterator { + ) -> impl Iterator { // Build a reverse lookup from address to index let address_to_index: BTreeMap<&Address, AddressIndex> = self .pending @@ -2175,7 +2187,16 @@ impl WalletAddressProvider { /// /// This allows applying balance changes discovered after the initial sync. pub fn update_balance(&mut self, address: &Address, balance: u64) { - self.found_balances.insert(address.clone(), balance); + let canonical_address = Wallet::canonical_address(address, self.network); + + let nonce = self + .found_balances + .get(&canonical_address) + .map(|funds| funds.nonce) + .unwrap_or(0); + + self.found_balances + .insert(canonical_address, AddressFunds { nonce, balance }); } /// Apply the sync results to a wallet, updating Platform address info. @@ -2183,29 +2204,28 @@ impl WalletAddressProvider { /// This updates the wallet's `platform_address_info` with the balances found during sync. /// Also ensures addresses are registered in `known_addresses` and `watched_addresses` /// so they appear in the UI. - /// Note: This does not update nonces - those should be fetched separately if needed. + /// Nonces are taken directly from the SDK sync results. pub fn apply_results_to_wallet(&self, wallet: &mut Wallet) { // Build a reverse lookup from address to index - let address_to_index: BTreeMap<&Address, AddressIndex> = self + let address_to_index: BTreeMap = self .pending .iter() - .map(|(idx, (_, addr))| (addr, *idx)) + .map(|(idx, (_, addr))| (Wallet::canonical_address(addr, self.network), *idx)) .collect(); - for (address, balance) in &self.found_balances { - // Get existing nonce or default to 0 - let nonce = wallet - .platform_address_info - .get(address) - .map(|info| info.nonce) - .unwrap_or(0); + for (address, funds) in &self.found_balances { + let canonical_address = Wallet::canonical_address(address, self.network); // Use sync-specific method that also updates last_synced_balance - wallet.set_platform_address_info_from_sync(address.clone(), *balance, nonce); + wallet.set_platform_address_info_from_sync( + canonical_address.clone(), + funds.balance, + funds.nonce, + ); // Also register in known_addresses and watched_addresses if not already present - if !wallet.known_addresses.contains_key(address) - && let Some(&index) = address_to_index.get(address) + if !wallet.known_addresses.contains_key(&canonical_address) + && let Some(&index) = address_to_index.get(&canonical_address) { let derivation_path = DerivationPath::platform_payment_path( self.network, @@ -2216,12 +2236,12 @@ impl WalletAddressProvider { wallet .known_addresses - .insert(address.clone(), derivation_path.clone()); + .insert(canonical_address.clone(), derivation_path.clone()); wallet.watched_addresses.insert( derivation_path, AddressInfo { - address: address.clone(), + address: canonical_address.clone(), path_type: DerivationPathType::CLEAR_FUNDS, path_reference: DerivationPathReference::PlatformPayment, }, @@ -2296,7 +2316,7 @@ impl AddressProvider for WalletAddressProvider { .collect() } - fn on_address_found(&mut self, index: AddressIndex, _key: &[u8], balance: u64) { + fn on_address_found(&mut self, index: AddressIndex, _key: &[u8], funds: AddressFunds) { self.resolved.insert(index); // Log what the SDK is returning @@ -2306,29 +2326,30 @@ impl AddressProvider for WalletAddressProvider { .map(|p| p.to_bech32m_string(self.network)) .unwrap_or_else(|_| "conversion failed".to_string()); tracing::info!( - "on_address_found: index={}, core_address={}, platform_address={}, balance={}", + "on_address_found: index={}, core_address={}, platform_address={}, balance={}, nonce={}", index, core_address, platform_addr_str, - balance + funds.balance, + funds.nonce ); } else { tracing::warn!( "on_address_found: index={} not in pending! balance={}", index, - balance + funds.balance ); } - if balance > 0 { + if let Some((_, core_address)) = self.pending.get(&index) { + let canonical_address = Wallet::canonical_address(core_address, self.network); + self.found_balances.insert(canonical_address, funds); + } + + if funds.balance > 0 { // Update highest found self.highest_found = Some(self.highest_found.map(|h| h.max(index)).unwrap_or(index)); - // Store the balance result - if let Some((_, core_address)) = self.pending.get(&index) { - self.found_balances.insert(core_address.clone(), balance); - } - // Extend the address range based on gap limit if let Err(e) = self.extend_for_gap_limit(index) { tracing::warn!("Failed to extend addresses for gap limit: {}", e); diff --git a/src/sdk_wrapper.rs b/src/sdk_wrapper.rs index 58c62c426..36be904c9 100644 --- a/src/sdk_wrapper.rs +++ b/src/sdk_wrapper.rs @@ -18,6 +18,7 @@ pub fn initialize_sdk( timeout: Some(Duration::from_secs(10)), retries: Some(6), ban_failed_address: Some(true), + max_decoding_message_size: None, }; let platform_version = default_platform_version(&network); diff --git a/src/ui/wallets/wallets_screen/mod.rs b/src/ui/wallets/wallets_screen/mod.rs index 114926dce..719b6c40f 100644 --- a/src/ui/wallets/wallets_screen/mod.rs +++ b/src/ui/wallets/wallets_screen/mod.rs @@ -3846,16 +3846,8 @@ impl ScreenLike for WalletsBalancesScreen { && wallet.seed_hash() == seed_hash { // Update balances in the wallet - for (addr_str, (balance, nonce)) in balances { - // Find the address that matches the string - if let Some((addr, _)) = wallet - .platform_address_info - .iter() - .find(|(a, _)| a.to_string() == addr_str) - { - let addr = addr.clone(); - wallet.set_platform_address_info(addr, balance, nonce); - } + for (addr, (balance, nonce)) in balances { + wallet.set_platform_address_info(addr, balance, nonce); } } self.message = Some((