diff --git a/Cargo.lock b/Cargo.lock index 14a81bc501d..79c7b155ace 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3831,6 +3831,7 @@ dependencies = [ "dashcore", "key-wallet", "rayon", + "serde", "tokio", "tracing", "zeroize", @@ -4835,6 +4836,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 846e736e94a..d812c764b9b 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) @@ -69,6 +70,14 @@ 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. Activates `key-wallet/serde`, +# `key-wallet-manager/serde`, 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` # `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 04f3ebe254a..00a9e39706b 100644 --- a/packages/rs-platform-wallet/src/changeset/changeset.rs +++ b/packages/rs-platform-wallet/src/changeset/changeset.rs @@ -79,6 +79,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. /// @@ -257,6 +258,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, @@ -331,6 +333,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, @@ -351,6 +354,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, @@ -379,6 +383,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>, @@ -415,6 +420,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, @@ -500,6 +506,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, @@ -508,6 +515,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, @@ -519,6 +527,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, @@ -567,6 +576,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, @@ -629,6 +639,7 @@ 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, @@ -638,6 +649,7 @@ pub struct PlatformAddressBalanceEntry { } #[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 @@ -694,6 +706,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). /// @@ -710,6 +723,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, @@ -752,6 +766,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. @@ -792,6 +807,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, @@ -816,6 +832,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, @@ -849,6 +866,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, @@ -876,6 +894,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/shielded_changeset.rs b/packages/rs-platform-wallet/src/changeset/shielded_changeset.rs index f6839ae16f1..65d0189d911 100644 --- a/packages/rs-platform-wallet/src/changeset/shielded_changeset.rs +++ b/packages/rs-platform-wallet/src/changeset/shielded_changeset.rs @@ -22,6 +22,7 @@ use crate::wallet::shielded::{ShieldedNote, SubwalletId}; /// Aggregated delta of shielded state for one persister flush. #[derive(Debug, Clone, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ShieldedChangeSet { /// Notes discovered (or re-saved with updated state) per /// subwallet. Keyed by `(wallet_id, account_index)`. Order 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 83657e4b9fd..766b3f0510d 100644 --- a/packages/rs-platform-wallet/src/wallet/asset_lock/tracked.rs +++ b/packages/rs-platform-wallet/src/wallet/asset_lock/tracked.rs @@ -30,6 +30,7 @@ use crate::changeset::AssetLockEntry; /// Marking the enum `#[non_exhaustive]` would force wildcard arms /// and silently lose that signal. #[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, diff --git a/packages/rs-platform-wallet/src/wallet/shielded/store.rs b/packages/rs-platform-wallet/src/wallet/shielded/store.rs index 7268a4f0d0f..038f691cd8b 100644 --- a/packages/rs-platform-wallet/src/wallet/shielded/store.rs +++ b/packages/rs-platform-wallet/src/wallet/shielded/store.rs @@ -34,6 +34,7 @@ use crate::wallet::platform_wallet::WalletId; /// sync watermarks inside a [`ShieldedStore`] so a single store /// can hold state for many wallets/accounts without leakage. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct SubwalletId { /// 32-byte wallet identifier (matches `PlatformWallet::wallet_id`). pub wallet_id: [u8; 32], @@ -58,6 +59,7 @@ impl SubwalletId { /// `orchard::Note` is in `note_data` as 115 bytes /// (`recipient(43) || value(8 LE) || rho(32) || rseed(32)`). #[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ShieldedNote { /// Global position in the commitment tree. pub position: u64, 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,