From 1eb2d2d0afda56f93b7031e37f16549eecb53123 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Mon, 11 May 2026 14:20:48 +0200 Subject: [PATCH 1/6] feat(platform-wallet): add optional serde derives behind serde feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new `serde` Cargo feature on `platform-wallet`. When enabled, every type carried in a `PlatformWalletChangeSet` gains `serde::Serialize` / `serde::Deserialize` derives via `#[cfg_attr(feature = "serde", derive(...))]`: - `CoreChangeSet`, `IdentityChangeSet`, `IdentityEntry`, `IdentityKeysChangeSet`, `IdentityKeyEntry`, `IdentityKeyDerivationIndices`, `ContactChangeSet`, `ContactRequestEntry`, `SentContactRequestKey`, `ReceivedContactRequestKey`, `PlatformAddressChangeSet`, `PlatformAddressBalanceEntry`, `AssetLockChangeSet`, `AssetLockEntry`, `TokenBalanceChangeSet`, `WalletMetadataEntry`, `AccountRegistrationEntry`, `AccountAddressPoolEntry`, and the top-level `PlatformWalletChangeSet`. - Per-identity / DashPay leaf types referenced inside those changesets: `BlockTime`, `IdentityStatus`, `DpnsNameInfo`, `DashPayProfile`, `ContactRequest`, `EstablishedContact`, `PaymentEntry`, `PaymentDirection`, `PaymentStatus`, `AssetLockStatus`. The feature activates `key-wallet/serde` (which transitively flips `dashcore/serde` and `dash-network/serde`) so every upstream leaf type already wired with `#[cfg_attr(feature = "serde", ...)]` (TransactionRecord, Utxo, InstantLock, AccountType, AddressInfo, AddressPoolType, ExtendedPubKey, Network) round-trips cleanly. Two upstream types lack their own serde feature and use `#[serde(with = ...)]` adapters in the new `src/changeset/serde_adapters.rs` module: - `AssetLockFundingType` (key-wallet, no `serde` derive) — encoded as a stable u8 tag matching the prior hand-rolled blob layout. - `AddressFunds` (dash-sdk re-export, no serde derive) — encoded as a `(nonce, balance)` shadow struct. One field is marked `#[serde(skip)]`: - `CoreChangeSet::addresses_derived` carries `key_wallet_manager::DerivedAddress`, which has no serde derive AND no `key-wallet-manager/serde` feature to activate. The breadcrumb is written to a typed table by persisters, not via a changeset blob, so skipping costs nothing. `cargo build -p platform-wallet` (no features) and `cargo build -p platform-wallet --features serde` both build clean. `cargo test -p platform-wallet` passes (8 lib tests, 121 integration tests) with and without the new feature. The change is opt-in; the default-feature build is byte-identical to its prior shape. Co-Authored-By: Claude Opus 4.7 (1M context) --- Cargo.lock | 1 + packages/rs-platform-wallet/Cargo.toml | 17 ++++ .../src/changeset/changeset.rs | 36 ++++++++ .../rs-platform-wallet/src/changeset/mod.rs | 2 + .../src/changeset/serde_adapters.rs | 88 +++++++++++++++++++ .../src/wallet/asset_lock/tracked.rs | 1 + .../src/wallet/identity/types/block_time.rs | 1 + .../identity/types/dashpay/contact_request.rs | 1 + .../types/dashpay/established_contact.rs | 1 + .../wallet/identity/types/dashpay/payment.rs | 3 + .../wallet/identity/types/dashpay/profile.rs | 1 + .../src/wallet/identity/types/key_storage.rs | 2 + 12 files changed, 154 insertions(+) create mode 100644 packages/rs-platform-wallet/src/changeset/serde_adapters.rs diff --git a/Cargo.lock b/Cargo.lock index 8262a058813..1dcf3231ce4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4848,6 +4848,7 @@ dependencies = [ "key-wallet-manager", "platform-encryption", "rand 0.8.6", + "serde", "serde_json", "sha2", "static_assertions", diff --git a/packages/rs-platform-wallet/Cargo.toml b/packages/rs-platform-wallet/Cargo.toml index c766d7759ef..f517e133ae2 100644 --- a/packages/rs-platform-wallet/Cargo.toml +++ b/packages/rs-platform-wallet/Cargo.toml @@ -38,6 +38,7 @@ tracing = "0.1" # Encoding hex = "0.4" bs58 = "0.5" +serde = { version = "1", default-features = false, features = ["derive"], optional = true } serde_json = "1.0" # Image processing (DIP-15 avatar hash + fingerprint) @@ -65,6 +66,22 @@ default = ["bls", "eddsa"] bls = ["key-wallet/bls", "key-wallet-manager/bls"] eddsa = ["key-wallet/eddsa", "key-wallet-manager/eddsa"] shielded = ["dep:grovedb-commitment-tree", "dep:zip32", "dash-sdk/shielded", "dpp/shielded-client"] +# Opt-in serde derives on the changeset types in `src/changeset/` plus +# the per-identity / DashPay scalar types those changesets carry. +# Activates `key-wallet/serde` (which transitively activates +# `dashcore/serde` and `dash-network/serde`) so every leaf type in a +# changeset payload — `TransactionRecord`, `Utxo`, `InstantLock`, +# `AccountType`, `AddressInfo`, `ExtendedPubKey`, `Network` — has a +# working `serde::Serialize`/`Deserialize` impl. `dpp` already derives +# serde unconditionally; `key-wallet-manager` has no `serde` feature +# of its own, so the lone non-serde changeset field +# (`CoreChangeSet::addresses_derived`, carrying a +# `key_wallet_manager::DerivedAddress`) is `#[serde(skip)]` — +# documented inline at the field. +serde = [ + "dep:serde", + "key-wallet/serde", +] # Forward to the upstream `key-wallet` / `key-wallet-manager` # `keep-finalized-transactions` feature. With it OFF (the default), # chainlocked transactions are evicted from the in-memory diff --git a/packages/rs-platform-wallet/src/changeset/changeset.rs b/packages/rs-platform-wallet/src/changeset/changeset.rs index 40af538a08f..f97562a49aa 100644 --- a/packages/rs-platform-wallet/src/changeset/changeset.rs +++ b/packages/rs-platform-wallet/src/changeset/changeset.rs @@ -78,6 +78,7 @@ use crate::wallet::identity::{ContactRequest, DashPayProfile, EstablishedContact /// upstream type. Tests that need to inspect a changeset's contents /// reach into individual fields directly. #[derive(Debug, Clone, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CoreChangeSet { /// Transaction records produced by this batch. /// @@ -134,6 +135,15 @@ pub struct CoreChangeSet { /// upstream `project_derived_addresses` uses, so two records in /// the same flush both pushing the same gap-limit boundary /// collapse to one entry. + /// + /// `#[serde(skip)]`: `key_wallet_manager::DerivedAddress` has no + /// serde derive upstream and there's no `key-wallet-manager/serde` + /// feature to activate. Persisters that need the breadcrumb write + /// it to a dedicated typed table (see + /// `rs-platform-wallet-sqlite::schema::core_state`) rather than + /// serialising the parent changeset wholesale, so a `skip` here + /// has no functional cost. + #[cfg_attr(feature = "serde", serde(skip))] pub addresses_derived: Vec, } @@ -228,6 +238,7 @@ impl Merge for CoreChangeSet { /// call [`IdentityEntry::from_managed`] to produce a fresh scalar /// snapshot so the merge can resolve the latest state by last-write-wins. #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct IdentityEntry { /// Identity identifier. pub id: Identifier, @@ -302,6 +313,7 @@ impl IdentityEntry { /// path — platform-wallet itself never carries or persists the key /// bytes. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct IdentityKeyDerivationIndices { /// DIP-9 identity index (hardened). pub identity_index: u32, @@ -322,6 +334,7 @@ pub struct IdentityKeyDerivationIndices { /// persist it. When either is `None` the key is watch-only from /// this wallet's point of view. #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct IdentityKeyEntry { /// Owning identity. pub identity_id: Identifier, @@ -350,6 +363,7 @@ pub struct IdentityKeyEntry { /// `{upsert, remove}` per key per mutation — the merge does not resolve /// insert-vs-tombstone for the same key. #[derive(Debug, Clone, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct IdentityKeysChangeSet { /// Inserted or updated identity keys keyed by (identity_id, key_id). pub upserts: BTreeMap<(Identifier, KeyID), IdentityKeyEntry>, @@ -386,6 +400,7 @@ impl Merge for IdentityKeysChangeSet { /// [`ContactChangeSet`]; same mitigation: every current emitter /// produces only one of {insert, tombstone} per key per mutation. #[derive(Debug, Clone, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct IdentityChangeSet { /// Inserted or updated identities keyed by identifier. pub identities: BTreeMap, @@ -471,6 +486,7 @@ impl Merge for IdentityChangeSet { /// /// Modelled after [`crate::wallet::identity::ContactRequest`]. #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ContactRequestEntry { /// The contact request. pub request: ContactRequest, @@ -479,6 +495,7 @@ pub struct ContactRequestEntry { /// Key for sent contact requests: the **owner** sent a request TO the /// **recipient**. Used for `sent_requests` and `removed_sent`. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct SentContactRequestKey { /// The identity owned by this wallet (the sender). pub owner_id: Identifier, @@ -490,6 +507,7 @@ pub struct SentContactRequestKey { /// FROM the **sender**. Used for `incoming_requests` and /// `removed_incoming`. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ReceivedContactRequestKey { /// The identity owned by this wallet (the recipient). pub owner_id: Identifier, @@ -538,6 +556,7 @@ pub struct ReceivedContactRequestKey { /// semantics, the merge impl should resolve `sent_requests ∩ /// removed_sent` by last-seen rather than carrying both. #[derive(Debug, Clone, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ContactChangeSet { /// Sent contact requests keyed by (owner → recipient). pub sent_requests: BTreeMap, @@ -600,15 +619,21 @@ impl Merge for ContactChangeSet { /// persisters can apply the entry without guessing which account or /// HD slot it belongs to. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct PlatformAddressBalanceEntry { pub wallet_id: WalletId, pub account_index: u32, pub address_index: u32, pub address: PlatformP2PKHAddress, + #[cfg_attr( + feature = "serde", + serde(with = "crate::changeset::serde_adapters::address_funds") + )] pub funds: AddressFunds, } #[derive(Debug, Clone, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct PlatformAddressChangeSet { /// Updated platform addresses produced by the last sync pass. /// A `Vec` rather than a map because the diff already deduplicates @@ -665,6 +690,7 @@ impl Merge for PlatformAddressChangeSet { /// Changes to the asset lock store. #[derive(Debug, Clone, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct AssetLockChangeSet { /// Asset lock entries keyed by outpoint (txid + output index). /// @@ -681,6 +707,7 @@ pub struct AssetLockChangeSet { /// Contains all fields needed to fully reconstruct a /// [`TrackedAssetLock`](crate::wallet::asset_lock::tracked::TrackedAssetLock). #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct AssetLockEntry { /// The outpoint identifying this credit output (txid + vout). pub out_point: OutPoint, @@ -689,6 +716,10 @@ pub struct AssetLockEntry { /// BIP44 account index that funded this asset lock (UTXO source). pub account_index: u32, /// Which funding account to derive the one-time key from. + #[cfg_attr( + feature = "serde", + serde(with = "crate::changeset::serde_adapters::asset_lock_funding_type") + )] pub funding_type: AssetLockFundingType, /// Identity index used during creation. pub identity_index: u32, @@ -723,6 +754,7 @@ impl Merge for AssetLockChangeSet { /// purely in the manager's in-memory cache. Persistence carries only /// the post-sync balance updates and tombstones. #[derive(Debug, Clone, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TokenBalanceChangeSet { /// Updated token balances keyed by `(identity_id, token_id)`. /// Last write wins on merge. @@ -763,6 +795,7 @@ impl Merge for TokenBalanceChangeSet { /// time; the parent `Option<...>` field stays `None` for every other /// flush. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct WalletMetadataEntry { /// Network the wallet is bound to. pub network: Network, @@ -787,6 +820,7 @@ pub struct WalletMetadataEntry { /// is simple `extend` and dedup is the apply-side caller's /// responsibility if it ever matters. #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct AccountRegistrationEntry { /// The account variant being registered. pub account_type: AccountType, @@ -820,6 +854,7 @@ pub struct AccountRegistrationEntry { /// the upstream type. Tests that need to inspect snapshot contents /// reach into the `addresses` vec by index instead. #[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct AccountAddressPoolEntry { /// Which account this pool belongs to. pub account_type: AccountType, @@ -847,6 +882,7 @@ pub struct AccountAddressPoolEntry { /// Not `PartialEq` because [`CoreChangeSet`] isn't (its `records` carry /// `TransactionRecord`, which is `Debug + Clone` only upstream). #[derive(Debug, Clone, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct PlatformWalletChangeSet { /// Core-wallet deltas projected from upstream `WalletEvent`s: /// transaction records, UTXO add/remove, height checkpoints, IS-lock diff --git a/packages/rs-platform-wallet/src/changeset/mod.rs b/packages/rs-platform-wallet/src/changeset/mod.rs index bd6650431fe..364a2ca3e3b 100644 --- a/packages/rs-platform-wallet/src/changeset/mod.rs +++ b/packages/rs-platform-wallet/src/changeset/mod.rs @@ -16,6 +16,8 @@ pub mod core_bridge; pub mod identity_manager_start_state; pub mod merge; pub mod platform_address_sync_start_state; +#[cfg(feature = "serde")] +pub mod serde_adapters; pub mod traits; pub use changeset::{ diff --git a/packages/rs-platform-wallet/src/changeset/serde_adapters.rs b/packages/rs-platform-wallet/src/changeset/serde_adapters.rs new file mode 100644 index 00000000000..330fab55c80 --- /dev/null +++ b/packages/rs-platform-wallet/src/changeset/serde_adapters.rs @@ -0,0 +1,88 @@ +//! `serde::with` adapters for upstream types that don't (yet) derive +//! their own `Serialize`/`Deserialize`. +//! +//! Compiled only when the crate's `serde` feature is on (see the +//! `#[cfg(feature = "serde")]` gate on the `pub mod` line in +//! `changeset/mod.rs`). + +use dash_sdk::platform::address_sync::AddressFunds; +use dpp::balances::credits::Credits; +use dpp::prelude::AddressNonce; +use key_wallet::wallet::managed_wallet_info::asset_lock_builder::AssetLockFundingType; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +/// Adapter for `AssetLockFundingType` (upstream has no serde derive). +/// +/// Encodes each variant as a stable u8 tag — same tag space the +/// hand-rolled `BlobWriter` used before the serde swap, kept for +/// forward/backward compatibility of on-disk blobs. +pub mod asset_lock_funding_type { + use super::*; + + pub fn serialize( + value: &AssetLockFundingType, + serializer: S, + ) -> Result { + let tag: u8 = match value { + AssetLockFundingType::IdentityRegistration => 0, + AssetLockFundingType::IdentityTopUp => 1, + AssetLockFundingType::IdentityTopUpNotBound => 2, + AssetLockFundingType::IdentityInvitation => 3, + AssetLockFundingType::AssetLockAddressTopUp => 4, + AssetLockFundingType::AssetLockShieldedAddressTopUp => 5, + }; + tag.serialize(serializer) + } + + pub fn deserialize<'de, D: Deserializer<'de>>( + deserializer: D, + ) -> Result { + let tag = u8::deserialize(deserializer)?; + Ok(match tag { + 0 => AssetLockFundingType::IdentityRegistration, + 1 => AssetLockFundingType::IdentityTopUp, + 2 => AssetLockFundingType::IdentityTopUpNotBound, + 3 => AssetLockFundingType::IdentityInvitation, + 4 => AssetLockFundingType::AssetLockAddressTopUp, + 5 => AssetLockFundingType::AssetLockShieldedAddressTopUp, + other => { + return Err(serde::de::Error::custom(format!( + "unknown AssetLockFundingType tag: {other}" + ))) + } + }) + } +} + +/// Adapter for `AddressFunds` (re-exported from `dash-sdk`; no serde +/// derive there). Encodes the two scalar fields side-by-side. +pub mod address_funds { + use super::*; + + #[derive(Serialize, Deserialize)] + struct Wire { + nonce: AddressNonce, + balance: Credits, + } + + pub fn serialize( + value: &AddressFunds, + serializer: S, + ) -> Result { + Wire { + nonce: value.nonce, + balance: value.balance, + } + .serialize(serializer) + } + + pub fn deserialize<'de, D: Deserializer<'de>>( + deserializer: D, + ) -> Result { + let w = Wire::deserialize(deserializer)?; + Ok(AddressFunds { + nonce: w.nonce, + balance: w.balance, + }) + } +} diff --git a/packages/rs-platform-wallet/src/wallet/asset_lock/tracked.rs b/packages/rs-platform-wallet/src/wallet/asset_lock/tracked.rs index 7939e67d03c..6a06632d119 100644 --- a/packages/rs-platform-wallet/src/wallet/asset_lock/tracked.rs +++ b/packages/rs-platform-wallet/src/wallet/asset_lock/tracked.rs @@ -14,6 +14,7 @@ use crate::changeset::AssetLockEntry; /// Asset lock status on Core chain. Tracked until consumed, then removed. #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum AssetLockStatus { Built, Broadcast, diff --git a/packages/rs-platform-wallet/src/wallet/identity/types/block_time.rs b/packages/rs-platform-wallet/src/wallet/identity/types/block_time.rs index 7c6e28d039b..b4291e5b57a 100644 --- a/packages/rs-platform-wallet/src/wallet/identity/types/block_time.rs +++ b/packages/rs-platform-wallet/src/wallet/identity/types/block_time.rs @@ -7,6 +7,7 @@ use dpp::prelude::{BlockHeight, CoreBlockHeight, TimestampMillis}; /// Block time information containing height, core height, and timestamp #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct BlockTime { /// Platform block height pub height: BlockHeight, diff --git a/packages/rs-platform-wallet/src/wallet/identity/types/dashpay/contact_request.rs b/packages/rs-platform-wallet/src/wallet/identity/types/dashpay/contact_request.rs index 73a2c45337f..d0b1540a3ce 100644 --- a/packages/rs-platform-wallet/src/wallet/identity/types/dashpay/contact_request.rs +++ b/packages/rs-platform-wallet/src/wallet/identity/types/dashpay/contact_request.rs @@ -8,6 +8,7 @@ use dpp::prelude::{CoreBlockHeight, Identifier}; /// A contact request represents a one-way relationship between two identities #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ContactRequest { /// The unique id of the sender (owner of the contact request) pub sender_id: Identifier, diff --git a/packages/rs-platform-wallet/src/wallet/identity/types/dashpay/established_contact.rs b/packages/rs-platform-wallet/src/wallet/identity/types/dashpay/established_contact.rs index b1be89ef227..49cfe288d5b 100644 --- a/packages/rs-platform-wallet/src/wallet/identity/types/dashpay/established_contact.rs +++ b/packages/rs-platform-wallet/src/wallet/identity/types/dashpay/established_contact.rs @@ -10,6 +10,7 @@ use dpp::prelude::Identifier; /// /// This is formed when both identities have sent contact requests to each other. #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct EstablishedContact { /// The contact's identity unique identifier pub contact_identity_id: Identifier, diff --git a/packages/rs-platform-wallet/src/wallet/identity/types/dashpay/payment.rs b/packages/rs-platform-wallet/src/wallet/identity/types/dashpay/payment.rs index fd1044c0c20..976b64acad3 100644 --- a/packages/rs-platform-wallet/src/wallet/identity/types/dashpay/payment.rs +++ b/packages/rs-platform-wallet/src/wallet/identity/types/dashpay/payment.rs @@ -19,6 +19,7 @@ use dpp::prelude::Identifier; /// Direction of a DashPay payment, from the owner's point of view. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum PaymentDirection { /// The owner sent this payment to the counterparty. Sent, @@ -28,6 +29,7 @@ pub enum PaymentDirection { /// Status of a DashPay payment on Core chain. #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum PaymentStatus { /// Broadcast but not yet confirmed. #[default] @@ -63,6 +65,7 @@ pub struct DashpayAddressMatch { /// Keyed by transaction id (hex string, matching evo-tool's /// `dashpay_payments.tx_id` column which is `TEXT UNIQUE NOT NULL`). #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct PaymentEntry { /// The other identity in this payment. Whether they're the /// sender or receiver is encoded in `direction`. diff --git a/packages/rs-platform-wallet/src/wallet/identity/types/dashpay/profile.rs b/packages/rs-platform-wallet/src/wallet/identity/types/dashpay/profile.rs index 4082f42035f..06d6d415529 100644 --- a/packages/rs-platform-wallet/src/wallet/identity/types/dashpay/profile.rs +++ b/packages/rs-platform-wallet/src/wallet/identity/types/dashpay/profile.rs @@ -21,6 +21,7 @@ use sha2::{Digest, Sha256}; /// User-facing DashPay profile data published via the DashPay data /// contract. This is the **output/stored** model — no raw image bytes. #[derive(Debug, Clone, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct DashPayProfile { /// Display name (publicly visible, max 25 chars per DIP-15). pub display_name: Option, diff --git a/packages/rs-platform-wallet/src/wallet/identity/types/key_storage.rs b/packages/rs-platform-wallet/src/wallet/identity/types/key_storage.rs index 4813dca6de5..8dee1a702b9 100644 --- a/packages/rs-platform-wallet/src/wallet/identity/types/key_storage.rs +++ b/packages/rs-platform-wallet/src/wallet/identity/types/key_storage.rs @@ -27,6 +27,7 @@ pub enum PrivateKeyData { /// Identity lifecycle status on Platform. #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum IdentityStatus { #[default] Unknown, @@ -38,6 +39,7 @@ pub enum IdentityStatus { /// DPNS username associated with an identity. #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct DpnsNameInfo { pub label: String, pub acquired_at: Option, From 9485e542af86d220ed5faacd4b743b4bfb361cb0 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Wed, 13 May 2026 15:10:33 +0200 Subject: [PATCH 2/6] refactor(rs-platform-wallet): drop redundant address_funds serde adapter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per audit: `AddressFunds` is first-party (lives in `rs-sdk/src/platform/address_sync/types.rs`) and its inner fields (`AddressNonce = u32`, `Credits = u64`) come from `dpp`, which derives serde unconditionally. The `#[serde(with = "serde_adapters::address_funds")]` adapter was pure boilerplate re-implementing what `derive(Serialize, Deserialize)` would produce. Replaced with a direct `#[cfg_attr(feature = "serde", derive(...))]` on `AddressFunds`. The `asset_lock_funding_type` adapter is retained — the upstream `AssetLockFundingType` in `rust-dashcore` key-wallet has no serde derives, and the adapter also encodes a stable `u8` wire-tag for on-disk blob compatibility that's worth keeping documented. Net: -~30 LOC. `platform-wallet`'s `serde` feature now propagates `dash-sdk/serde` so the gated derive on `AddressFunds` is activated when the parent feature is on. (Stray `cargo fmt -p platform-wallet` hit one pre-existing formatting nit in `wallet/platform_addresses/wallet.rs` — included since the quality gate runs fmt.) Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/rs-platform-wallet/Cargo.toml | 1 + .../src/changeset/changeset.rs | 4 --- .../src/changeset/serde_adapters.rs | 36 ------------------- .../src/wallet/platform_addresses/wallet.rs | 6 +--- .../rs-sdk/src/platform/address_sync/types.rs | 1 + 5 files changed, 3 insertions(+), 45 deletions(-) diff --git a/packages/rs-platform-wallet/Cargo.toml b/packages/rs-platform-wallet/Cargo.toml index f517e133ae2..e83e50bd2e5 100644 --- a/packages/rs-platform-wallet/Cargo.toml +++ b/packages/rs-platform-wallet/Cargo.toml @@ -81,6 +81,7 @@ shielded = ["dep:grovedb-commitment-tree", "dep:zip32", "dash-sdk/shielded", "dp serde = [ "dep:serde", "key-wallet/serde", + "dash-sdk/serde", ] # Forward to the upstream `key-wallet` / `key-wallet-manager` # `keep-finalized-transactions` feature. With it OFF (the default), diff --git a/packages/rs-platform-wallet/src/changeset/changeset.rs b/packages/rs-platform-wallet/src/changeset/changeset.rs index f97562a49aa..2247299879a 100644 --- a/packages/rs-platform-wallet/src/changeset/changeset.rs +++ b/packages/rs-platform-wallet/src/changeset/changeset.rs @@ -625,10 +625,6 @@ pub struct PlatformAddressBalanceEntry { pub account_index: u32, pub address_index: u32, pub address: PlatformP2PKHAddress, - #[cfg_attr( - feature = "serde", - serde(with = "crate::changeset::serde_adapters::address_funds") - )] pub funds: AddressFunds, } diff --git a/packages/rs-platform-wallet/src/changeset/serde_adapters.rs b/packages/rs-platform-wallet/src/changeset/serde_adapters.rs index 330fab55c80..7b090999f2e 100644 --- a/packages/rs-platform-wallet/src/changeset/serde_adapters.rs +++ b/packages/rs-platform-wallet/src/changeset/serde_adapters.rs @@ -5,9 +5,6 @@ //! `#[cfg(feature = "serde")]` gate on the `pub mod` line in //! `changeset/mod.rs`). -use dash_sdk::platform::address_sync::AddressFunds; -use dpp::balances::credits::Credits; -use dpp::prelude::AddressNonce; use key_wallet::wallet::managed_wallet_info::asset_lock_builder::AssetLockFundingType; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -53,36 +50,3 @@ pub mod asset_lock_funding_type { }) } } - -/// Adapter for `AddressFunds` (re-exported from `dash-sdk`; no serde -/// derive there). Encodes the two scalar fields side-by-side. -pub mod address_funds { - use super::*; - - #[derive(Serialize, Deserialize)] - struct Wire { - nonce: AddressNonce, - balance: Credits, - } - - pub fn serialize( - value: &AddressFunds, - serializer: S, - ) -> Result { - Wire { - nonce: value.nonce, - balance: value.balance, - } - .serialize(serializer) - } - - pub fn deserialize<'de, D: Deserializer<'de>>( - deserializer: D, - ) -> Result { - let w = Wire::deserialize(deserializer)?; - Ok(AddressFunds { - nonce: w.nonce, - balance: w.balance, - }) - } -} diff --git a/packages/rs-platform-wallet/src/wallet/platform_addresses/wallet.rs b/packages/rs-platform-wallet/src/wallet/platform_addresses/wallet.rs index f7d83a2fff3..aec6d5b4f9d 100644 --- a/packages/rs-platform-wallet/src/wallet/platform_addresses/wallet.rs +++ b/packages/rs-platform-wallet/src/wallet/platform_addresses/wallet.rs @@ -117,11 +117,7 @@ impl PlatformAddressWallet { .platform_payment_managed_account_at_index_mut(*account_index) { for (p2pkh, funds) in account_state.found() { - account.set_address_credit_balance( - *p2pkh, - funds.balance, - None, - ); + account.set_address_credit_balance(*p2pkh, funds.balance, None); } } } diff --git a/packages/rs-sdk/src/platform/address_sync/types.rs b/packages/rs-sdk/src/platform/address_sync/types.rs index 15ce6d82eb7..74daea4609a 100644 --- a/packages/rs-sdk/src/platform/address_sync/types.rs +++ b/packages/rs-sdk/src/platform/address_sync/types.rs @@ -49,6 +49,7 @@ pub type LeafBoundaryKey = Vec; /// Funds stored for a platform address. #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct AddressFunds { /// Address nonce used for anti-replay. pub nonce: AddressNonce, From 6d82a99520ffa30b7cb15bf76caeef8d6df33dce Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Wed, 13 May 2026 15:24:49 +0200 Subject: [PATCH 3/6] docs(rs-platform-wallet): tighten comments per coding-best-practices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Scope: PR #3637's own diff against v3.1-dev only (the serde-feature PR). Pre-existing v3.1-dev comments left untouched. Drivers: - Verbosity: the Cargo.toml `serde` feature note was 12 lines enumerating every leaf type that gains a derive — collapsed to 6 lines covering the meaningful "what activates" + "which field is skipped" bits. - Verbosity: the `addresses_derived` `#[serde(skip)]` paragraph trimmed from 7 lines to 4, keeping the external-constraint citation (upstream type has no serde derive, no feature to activate) and the pointer to where persisters actually store the breadcrumb. - Present-state, not history: the `asset_lock_funding_type` adapter docstring referenced "the hand-rolled BlobWriter used before the serde swap" — that's PR history, dropped. Kept the meaningful "stable u8 tag for on-disk blob compat" rationale. - Verbosity / redundant-with-cfg: the `serde_adapters.rs` module header said "compiled only when the serde feature is on" with a paragraph pointing back to the `#[cfg(feature = "serde")]` line that imports it. The cfg gate is self-documenting; trimmed from 6 lines to 2 and rephrased "upstream types that don't (yet) derive" to "upstream types lacking their own derives" now that only one adapter remains. No tombstones added. No code semantics touched. cargo fmt + check (both feature flag states) + clippy --all-features + lib tests (121 passing) all green. Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/rs-platform-wallet/Cargo.toml | 18 ++++++------------ .../src/changeset/changeset.rs | 11 ++++------- .../src/changeset/serde_adapters.rs | 13 ++++--------- 3 files changed, 14 insertions(+), 28 deletions(-) diff --git a/packages/rs-platform-wallet/Cargo.toml b/packages/rs-platform-wallet/Cargo.toml index e83e50bd2e5..4a027a17acc 100644 --- a/packages/rs-platform-wallet/Cargo.toml +++ b/packages/rs-platform-wallet/Cargo.toml @@ -66,18 +66,12 @@ default = ["bls", "eddsa"] bls = ["key-wallet/bls", "key-wallet-manager/bls"] eddsa = ["key-wallet/eddsa", "key-wallet-manager/eddsa"] shielded = ["dep:grovedb-commitment-tree", "dep:zip32", "dash-sdk/shielded", "dpp/shielded-client"] -# Opt-in serde derives on the changeset types in `src/changeset/` plus -# the per-identity / DashPay scalar types those changesets carry. -# Activates `key-wallet/serde` (which transitively activates -# `dashcore/serde` and `dash-network/serde`) so every leaf type in a -# changeset payload — `TransactionRecord`, `Utxo`, `InstantLock`, -# `AccountType`, `AddressInfo`, `ExtendedPubKey`, `Network` — has a -# working `serde::Serialize`/`Deserialize` impl. `dpp` already derives -# serde unconditionally; `key-wallet-manager` has no `serde` feature -# of its own, so the lone non-serde changeset field -# (`CoreChangeSet::addresses_derived`, carrying a -# `key_wallet_manager::DerivedAddress`) is `#[serde(skip)]` — -# documented inline at the field. +# Opt-in serde derives on the changeset types and the scalar types they +# carry. Activates upstream `key-wallet/serde` (transitively `dashcore` +# and `dash-network`); `dpp` derives serde unconditionally. The lone +# non-serde field (`CoreChangeSet::addresses_derived`, from +# `key-wallet-manager` which has no serde feature) is `#[serde(skip)]`, +# documented at the field. serde = [ "dep:serde", "key-wallet/serde", diff --git a/packages/rs-platform-wallet/src/changeset/changeset.rs b/packages/rs-platform-wallet/src/changeset/changeset.rs index 2247299879a..895e541e202 100644 --- a/packages/rs-platform-wallet/src/changeset/changeset.rs +++ b/packages/rs-platform-wallet/src/changeset/changeset.rs @@ -136,13 +136,10 @@ pub struct CoreChangeSet { /// the same flush both pushing the same gap-limit boundary /// collapse to one entry. /// - /// `#[serde(skip)]`: `key_wallet_manager::DerivedAddress` has no - /// serde derive upstream and there's no `key-wallet-manager/serde` - /// feature to activate. Persisters that need the breadcrumb write - /// it to a dedicated typed table (see - /// `rs-platform-wallet-sqlite::schema::core_state`) rather than - /// serialising the parent changeset wholesale, so a `skip` here - /// has no functional cost. + /// `#[serde(skip)]` because `key_wallet_manager::DerivedAddress` has + /// no serde derive upstream and no `key-wallet-manager/serde` feature + /// exists. Persisters store this breadcrumb in a dedicated typed + /// table (e.g. `rs-platform-wallet-sqlite::schema::core_state`). #[cfg_attr(feature = "serde", serde(skip))] pub addresses_derived: Vec, } diff --git a/packages/rs-platform-wallet/src/changeset/serde_adapters.rs b/packages/rs-platform-wallet/src/changeset/serde_adapters.rs index 7b090999f2e..5fecf1d297a 100644 --- a/packages/rs-platform-wallet/src/changeset/serde_adapters.rs +++ b/packages/rs-platform-wallet/src/changeset/serde_adapters.rs @@ -1,18 +1,13 @@ -//! `serde::with` adapters for upstream types that don't (yet) derive -//! their own `Serialize`/`Deserialize`. -//! -//! Compiled only when the crate's `serde` feature is on (see the -//! `#[cfg(feature = "serde")]` gate on the `pub mod` line in -//! `changeset/mod.rs`). +//! `serde::with` adapters for upstream types lacking their own +//! `Serialize`/`Deserialize` derives. use key_wallet::wallet::managed_wallet_info::asset_lock_builder::AssetLockFundingType; use serde::{Deserialize, Deserializer, Serialize, Serializer}; /// Adapter for `AssetLockFundingType` (upstream has no serde derive). /// -/// Encodes each variant as a stable u8 tag — same tag space the -/// hand-rolled `BlobWriter` used before the serde swap, kept for -/// forward/backward compatibility of on-disk blobs. +/// Each variant is encoded as a stable `u8` tag so on-disk blobs remain +/// forward/backward compatible across releases. pub mod asset_lock_funding_type { use super::*; From 561ccb4902f2f7fbad28103c7eb50c8ca793721f Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Wed, 13 May 2026 16:20:35 +0200 Subject: [PATCH 4/6] feat(rs-platform-wallet): consume rust-dashcore#761 via branch pin, drop serde workarounds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Workspace `Cargo.toml` swapped `rev = "53130869..."` for `branch = "feat/key-wallet-serde-derives"` on all 9 rust-dashcore crates to consume PR #761's serde derives ahead of upstream merge. Cargo's `[patch]` mechanism doesn't support same-source-different-branch redirects (rust-lang/cargo#5478, #10756); direct branch pinning is the working mechanism. Cargo.lock now resolves the 12 dashcore-sourced crates at `56a84402c8cb006d65b12e400785a62d67441930`, which the branch has been rebased onto so it carries ONLY the serde additions (no v0.42-dev drift). Dropped `serde_adapters::asset_lock_funding_type` adapter — type now derives upstream via #761. `serde_adapters.rs` deleted and removed from `changeset/mod.rs`. Dropped `#[serde(skip)]` on `CoreChangeSet::addresses_derived` — `DerivedAddress` now derives upstream (with `serde_pubkey33` adapter packaged inside #761 for the 33-byte pubkey field). Added `"key-wallet-manager/serde"` to platform-wallet's serde feature. Wire-format note: the upstream derive on `AssetLockFundingType` produces externally-tagged enum representation; the dropped custom adapter produced a stable `u8` wire-tag. PR #3637 is unreleased and the feature is opt-in — no consumer was persisting blobs with the prior format. Follow-up after #761 merges to v0.42-dev: swap `branch = "..."` back to `rev = ""` and remove the temp comment in workspace Cargo.toml. Co-Authored-By: Claude Opus 4.7 (1M context) --- Cargo.lock | 45 +++++++++--------- Cargo.toml | 23 +++++---- packages/rs-platform-wallet/Cargo.toml | 10 ++-- .../src/changeset/changeset.rs | 10 ---- .../rs-platform-wallet/src/changeset/mod.rs | 2 - .../src/changeset/serde_adapters.rs | 47 ------------------- 6 files changed, 41 insertions(+), 96 deletions(-) delete mode 100644 packages/rs-platform-wallet/src/changeset/serde_adapters.rs diff --git a/Cargo.lock b/Cargo.lock index 1dcf3231ce4..d8afc3f4f04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1132,7 +1132,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1550,7 +1550,7 @@ dependencies = [ [[package]] name = "dash-network" version = "0.42.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=53130869e5b9343ae59016323e5e5269e717a8fd#53130869e5b9343ae59016323e5e5269e717a8fd" +source = "git+https://github.com/dashpay/rust-dashcore?branch=feat%2Fkey-wallet-serde-derives#56a84402c8cb006d65b12e400785a62d67441930" dependencies = [ "bincode", "bincode_derive", @@ -1561,7 +1561,7 @@ dependencies = [ [[package]] name = "dash-network-seeds" version = "0.42.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=53130869e5b9343ae59016323e5e5269e717a8fd#53130869e5b9343ae59016323e5e5269e717a8fd" +source = "git+https://github.com/dashpay/rust-dashcore?branch=feat%2Fkey-wallet-serde-derives#56a84402c8cb006d65b12e400785a62d67441930" dependencies = [ "dash-network", ] @@ -1638,7 +1638,7 @@ dependencies = [ [[package]] name = "dash-spv" version = "0.42.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=53130869e5b9343ae59016323e5e5269e717a8fd#53130869e5b9343ae59016323e5e5269e717a8fd" +source = "git+https://github.com/dashpay/rust-dashcore?branch=feat%2Fkey-wallet-serde-derives#56a84402c8cb006d65b12e400785a62d67441930" dependencies = [ "async-trait", "chrono", @@ -1666,7 +1666,7 @@ dependencies = [ [[package]] name = "dash-spv-ffi" version = "0.42.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=53130869e5b9343ae59016323e5e5269e717a8fd#53130869e5b9343ae59016323e5e5269e717a8fd" +source = "git+https://github.com/dashpay/rust-dashcore?branch=feat%2Fkey-wallet-serde-derives#56a84402c8cb006d65b12e400785a62d67441930" dependencies = [ "cbindgen 0.29.2", "clap", @@ -1685,7 +1685,7 @@ dependencies = [ [[package]] name = "dashcore" version = "0.42.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=53130869e5b9343ae59016323e5e5269e717a8fd#53130869e5b9343ae59016323e5e5269e717a8fd" +source = "git+https://github.com/dashpay/rust-dashcore?branch=feat%2Fkey-wallet-serde-derives#56a84402c8cb006d65b12e400785a62d67441930" dependencies = [ "anyhow", "base64-compat", @@ -1711,12 +1711,12 @@ dependencies = [ [[package]] name = "dashcore-private" version = "0.42.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=53130869e5b9343ae59016323e5e5269e717a8fd#53130869e5b9343ae59016323e5e5269e717a8fd" +source = "git+https://github.com/dashpay/rust-dashcore?branch=feat%2Fkey-wallet-serde-derives#56a84402c8cb006d65b12e400785a62d67441930" [[package]] name = "dashcore-rpc" version = "0.42.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=53130869e5b9343ae59016323e5e5269e717a8fd#53130869e5b9343ae59016323e5e5269e717a8fd" +source = "git+https://github.com/dashpay/rust-dashcore?branch=feat%2Fkey-wallet-serde-derives#56a84402c8cb006d65b12e400785a62d67441930" dependencies = [ "dashcore-rpc-json", "hex", @@ -1729,7 +1729,7 @@ dependencies = [ [[package]] name = "dashcore-rpc-json" version = "0.42.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=53130869e5b9343ae59016323e5e5269e717a8fd#53130869e5b9343ae59016323e5e5269e717a8fd" +source = "git+https://github.com/dashpay/rust-dashcore?branch=feat%2Fkey-wallet-serde-derives#56a84402c8cb006d65b12e400785a62d67441930" dependencies = [ "bincode", "dashcore", @@ -1744,7 +1744,7 @@ dependencies = [ [[package]] name = "dashcore_hashes" version = "0.42.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=53130869e5b9343ae59016323e5e5269e717a8fd#53130869e5b9343ae59016323e5e5269e717a8fd" +source = "git+https://github.com/dashpay/rust-dashcore?branch=feat%2Fkey-wallet-serde-derives#56a84402c8cb006d65b12e400785a62d67441930" dependencies = [ "bincode", "dashcore-private", @@ -2279,7 +2279,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3562,7 +3562,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3793,7 +3793,7 @@ dependencies = [ [[package]] name = "key-wallet" version = "0.42.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=53130869e5b9343ae59016323e5e5269e717a8fd#53130869e5b9343ae59016323e5e5269e717a8fd" +source = "git+https://github.com/dashpay/rust-dashcore?branch=feat%2Fkey-wallet-serde-derives#56a84402c8cb006d65b12e400785a62d67441930" dependencies = [ "aes", "async-trait", @@ -3821,7 +3821,7 @@ dependencies = [ [[package]] name = "key-wallet-ffi" version = "0.42.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=53130869e5b9343ae59016323e5e5269e717a8fd#53130869e5b9343ae59016323e5e5269e717a8fd" +source = "git+https://github.com/dashpay/rust-dashcore?branch=feat%2Fkey-wallet-serde-derives#56a84402c8cb006d65b12e400785a62d67441930" dependencies = [ "cbindgen 0.29.2", "dash-network", @@ -3837,13 +3837,14 @@ dependencies = [ [[package]] name = "key-wallet-manager" version = "0.42.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=53130869e5b9343ae59016323e5e5269e717a8fd#53130869e5b9343ae59016323e5e5269e717a8fd" +source = "git+https://github.com/dashpay/rust-dashcore?branch=feat%2Fkey-wallet-serde-derives#56a84402c8cb006d65b12e400785a62d67441930" dependencies = [ "async-trait", "bincode", "dashcore", "key-wallet", "rayon", + "serde", "tokio", "tracing", "zeroize", @@ -4313,7 +4314,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -5320,7 +5321,7 @@ dependencies = [ "once_cell", "socket2 0.5.10", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -6043,7 +6044,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -6056,7 +6057,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -6115,7 +6116,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -6955,7 +6956,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -8357,7 +8358,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 078306a8b88..4efc3d93d47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,15 +49,20 @@ members = [ ] [workspace.dependencies] -dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "53130869e5b9343ae59016323e5e5269e717a8fd" } -dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "53130869e5b9343ae59016323e5e5269e717a8fd" } -dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "53130869e5b9343ae59016323e5e5269e717a8fd" } -dash-spv-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "53130869e5b9343ae59016323e5e5269e717a8fd" } -key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "53130869e5b9343ae59016323e5e5269e717a8fd" } -key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "53130869e5b9343ae59016323e5e5269e717a8fd" } -key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "53130869e5b9343ae59016323e5e5269e717a8fd" } -dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "53130869e5b9343ae59016323e5e5269e717a8fd" } -dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "53130869e5b9343ae59016323e5e5269e717a8fd" } +# Temporary: branch-pinned to dashpay/rust-dashcore#761 (`feat/key-wallet-serde-derives`) +# for serde derives on AssetLockFundingType + DerivedAddress. The branch has been +# rebased onto 53130869 so it carries ONLY the serde additions (no v0.42-dev drift). +# Drop the branch= override and restore rev= once #761 merges to v0.42-dev and a +# subsequent dashcore rev bump flows the new rev in. +dashcore = { git = "https://github.com/dashpay/rust-dashcore", branch = "feat/key-wallet-serde-derives" } +dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", branch = "feat/key-wallet-serde-derives" } +dash-spv = { git = "https://github.com/dashpay/rust-dashcore", branch = "feat/key-wallet-serde-derives" } +dash-spv-ffi = { git = "https://github.com/dashpay/rust-dashcore", branch = "feat/key-wallet-serde-derives" } +key-wallet = { git = "https://github.com/dashpay/rust-dashcore", branch = "feat/key-wallet-serde-derives" } +key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", branch = "feat/key-wallet-serde-derives" } +key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", branch = "feat/key-wallet-serde-derives" } +dash-network = { git = "https://github.com/dashpay/rust-dashcore", branch = "feat/key-wallet-serde-derives" } +dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", branch = "feat/key-wallet-serde-derives" } # Optimize heavy crypto crates even in dev/test builds so that # Halo 2 proof generation and verification run at near-release speed. diff --git a/packages/rs-platform-wallet/Cargo.toml b/packages/rs-platform-wallet/Cargo.toml index 4a027a17acc..402a476840b 100644 --- a/packages/rs-platform-wallet/Cargo.toml +++ b/packages/rs-platform-wallet/Cargo.toml @@ -66,15 +66,13 @@ default = ["bls", "eddsa"] bls = ["key-wallet/bls", "key-wallet-manager/bls"] eddsa = ["key-wallet/eddsa", "key-wallet-manager/eddsa"] shielded = ["dep:grovedb-commitment-tree", "dep:zip32", "dash-sdk/shielded", "dpp/shielded-client"] -# Opt-in serde derives on the changeset types and the scalar types they -# carry. Activates upstream `key-wallet/serde` (transitively `dashcore` -# and `dash-network`); `dpp` derives serde unconditionally. The lone -# non-serde field (`CoreChangeSet::addresses_derived`, from -# `key-wallet-manager` which has no serde feature) is `#[serde(skip)]`, -# documented at the field. +# Opt-in serde derives on the changeset types. Activates `key-wallet/serde`, +# `key-wallet-manager/serde` (both via dashpay/rust-dashcore#761, branch-pinned in +# workspace Cargo.toml), and `dash-sdk/serde`. `dpp` derives serde unconditionally. serde = [ "dep:serde", "key-wallet/serde", + "key-wallet-manager/serde", "dash-sdk/serde", ] # Forward to the upstream `key-wallet` / `key-wallet-manager` diff --git a/packages/rs-platform-wallet/src/changeset/changeset.rs b/packages/rs-platform-wallet/src/changeset/changeset.rs index 895e541e202..88ce9ec0ceb 100644 --- a/packages/rs-platform-wallet/src/changeset/changeset.rs +++ b/packages/rs-platform-wallet/src/changeset/changeset.rs @@ -135,12 +135,6 @@ pub struct CoreChangeSet { /// upstream `project_derived_addresses` uses, so two records in /// the same flush both pushing the same gap-limit boundary /// collapse to one entry. - /// - /// `#[serde(skip)]` because `key_wallet_manager::DerivedAddress` has - /// no serde derive upstream and no `key-wallet-manager/serde` feature - /// exists. Persisters store this breadcrumb in a dedicated typed - /// table (e.g. `rs-platform-wallet-sqlite::schema::core_state`). - #[cfg_attr(feature = "serde", serde(skip))] pub addresses_derived: Vec, } @@ -709,10 +703,6 @@ pub struct AssetLockEntry { /// BIP44 account index that funded this asset lock (UTXO source). pub account_index: u32, /// Which funding account to derive the one-time key from. - #[cfg_attr( - feature = "serde", - serde(with = "crate::changeset::serde_adapters::asset_lock_funding_type") - )] pub funding_type: AssetLockFundingType, /// Identity index used during creation. pub identity_index: u32, diff --git a/packages/rs-platform-wallet/src/changeset/mod.rs b/packages/rs-platform-wallet/src/changeset/mod.rs index 364a2ca3e3b..bd6650431fe 100644 --- a/packages/rs-platform-wallet/src/changeset/mod.rs +++ b/packages/rs-platform-wallet/src/changeset/mod.rs @@ -16,8 +16,6 @@ pub mod core_bridge; pub mod identity_manager_start_state; pub mod merge; pub mod platform_address_sync_start_state; -#[cfg(feature = "serde")] -pub mod serde_adapters; pub mod traits; pub use changeset::{ diff --git a/packages/rs-platform-wallet/src/changeset/serde_adapters.rs b/packages/rs-platform-wallet/src/changeset/serde_adapters.rs deleted file mode 100644 index 5fecf1d297a..00000000000 --- a/packages/rs-platform-wallet/src/changeset/serde_adapters.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! `serde::with` adapters for upstream types lacking their own -//! `Serialize`/`Deserialize` derives. - -use key_wallet::wallet::managed_wallet_info::asset_lock_builder::AssetLockFundingType; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; - -/// Adapter for `AssetLockFundingType` (upstream has no serde derive). -/// -/// Each variant is encoded as a stable `u8` tag so on-disk blobs remain -/// forward/backward compatible across releases. -pub mod asset_lock_funding_type { - use super::*; - - pub fn serialize( - value: &AssetLockFundingType, - serializer: S, - ) -> Result { - let tag: u8 = match value { - AssetLockFundingType::IdentityRegistration => 0, - AssetLockFundingType::IdentityTopUp => 1, - AssetLockFundingType::IdentityTopUpNotBound => 2, - AssetLockFundingType::IdentityInvitation => 3, - AssetLockFundingType::AssetLockAddressTopUp => 4, - AssetLockFundingType::AssetLockShieldedAddressTopUp => 5, - }; - tag.serialize(serializer) - } - - pub fn deserialize<'de, D: Deserializer<'de>>( - deserializer: D, - ) -> Result { - let tag = u8::deserialize(deserializer)?; - Ok(match tag { - 0 => AssetLockFundingType::IdentityRegistration, - 1 => AssetLockFundingType::IdentityTopUp, - 2 => AssetLockFundingType::IdentityTopUpNotBound, - 3 => AssetLockFundingType::IdentityInvitation, - 4 => AssetLockFundingType::AssetLockAddressTopUp, - 5 => AssetLockFundingType::AssetLockShieldedAddressTopUp, - other => { - return Err(serde::de::Error::custom(format!( - "unknown AssetLockFundingType tag: {other}" - ))) - } - }) - } -} From 6c11ecefb6e23bd879977b744e085f8fef085172 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Wed, 13 May 2026 12:05:33 +0200 Subject: [PATCH 5/6] fix(rs-platform-wallet-ffi): drop stale ptr arg from integration_tests `platform_wallet_info_create_from_mnemonic` now takes 3 args (network, mnemonic, out_handle); the integration tests still passed a 4th `std::ptr::null()` between `mnemonic` and `out_handle`, breaking the mac nextest job. Sync both call sites with the current FFI signature. Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/rs-platform-wallet-ffi/tests/integration_tests.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/rs-platform-wallet-ffi/tests/integration_tests.rs b/packages/rs-platform-wallet-ffi/tests/integration_tests.rs index 09adb69a20f..de826eddf43 100644 --- a/packages/rs-platform-wallet-ffi/tests/integration_tests.rs +++ b/packages/rs-platform-wallet-ffi/tests/integration_tests.rs @@ -50,7 +50,6 @@ fn test_wallet_from_mnemonic() { let result = platform_wallet_info_create_from_mnemonic( Network::Testnet.into(), mnemonic.as_ptr(), - std::ptr::null(), &mut handle, ); @@ -266,7 +265,6 @@ fn test_full_workflow() { let result = platform_wallet_info_create_from_mnemonic( Network::Testnet.into(), mnemonic.as_ptr(), - std::ptr::null(), &mut wallet_handle, ); assert_eq!(result.code, PlatformWalletFFIResultCode::Success); From f23183c778ef6dcfbcaef37b03eab2da586ec968 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Wed, 13 May 2026 12:24:45 +0200 Subject: [PATCH 6/6] chore(rs-platform-wallet-ffi): use Result::is_err in group_info tests Pre-existing `matches!(result, Err(_))` patterns trip `clippy::redundant_pattern_matching` under the workspace's `-D warnings` gate. Swap to `result.is_err()` so the clippy step stays green for the crates this PR touches. Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/rs-platform-wallet-ffi/src/tokens/group_info.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/rs-platform-wallet-ffi/src/tokens/group_info.rs b/packages/rs-platform-wallet-ffi/src/tokens/group_info.rs index 78595b5050c..b5c75a01a09 100644 --- a/packages/rs-platform-wallet-ffi/src/tokens/group_info.rs +++ b/packages/rs-platform-wallet-ffi/src/tokens/group_info.rs @@ -94,7 +94,7 @@ mod tests { fn test_decode_other_signer_null_action_id() { unsafe { let result = decode_group_info(2, 0, std::ptr::null(), false); - assert!(matches!(result, Err(_)), "expected Err(NullPointer)"); + assert!(result.is_err(), "expected Err(NullPointer)"); } } @@ -120,7 +120,7 @@ mod tests { fn test_decode_invalid_kind() { unsafe { let result = decode_group_info(99, 0, std::ptr::null(), false); - assert!(matches!(result, Err(_)), "expected Err(InvalidParameter)"); + assert!(result.is_err(), "expected Err(InvalidParameter)"); } } }