From 49ad468a215fc4935eb43d74e4e8662a52b73f69 Mon Sep 17 00:00:00 2001 From: pasta Date: Tue, 22 Sep 2026 21:18:02 -0500 Subject: [PATCH 01/22] chore(platform)!: bump rust-dashcore to 719de34b (secp256k1 0.33) Bump every rust-dashcore git dependency from e4208c90 to 719de34b (dashpay/rust-dashcore dev, the merge of #1049). The breaking change in range is rust-dashcore #1042: secp256k1 0.30 -> 0.33, rand 0.9, getrandom 0.4, and the secp256k1 context argument dropped API-wide. The range also carries key-wallet fixes #1035 (DIP-15 contact pools extend as payments arrive), #1009 (provider transactions consult every fund-bearing account), #1048, and test-only #920/#1038. Mechanical call-site migration: drop Secp256k1 contexts (derive_priv/from_priv/public_key/from_secret_key/Keypair::new take no context); secp.sign_ecdsa/recover_ecdsa/verify_ecdsa become methods on the key/signature; SecretKey::from_slice/from_byte_array become from_secret_bytes (slices go through <[u8; 32]>::try_from mapped to Error::InvalidSecretKey, the error from_slice returned); secret_bytes -> to_secret_bytes; SecretKey/SharedSecret lost AsRef<[u8]>, so use as_secret_bytes; thread_rng -> rng; StdRng::from_entropy -> from_os_rng. The secp256k1::hashes re-export is gone, so hex rendering uses the hex crate (same output). Seeded RNG bridging (rand 0.8 caller -> secp's rand 0.9 StdRng) now fills a 32-byte seed and calls from_seed, which is what rand_core 0.6 from_rng did, so seeded keys are unchanged; drive-abci tests that feed seed_from_u64 into Keypair::new use the secp re-exported StdRng (seed_from_u64 and ChaCha12 are identical across rand_core 0.6/0.9). rs-platform-encryption moves its own secp256k1 to 0.33.1 so only one secp256k1 is in the graph. simple-signer takes a direct rand 0.8 dep for the RngCore its callers pass. rs-dpp enables getrandom 0.3/0.4 wasm_js on wasm32-unknown-unknown, which the new transitive getrandom versions require there. Rust toolchain already at 1.98.1, matching rust-dashcore. Co-Authored-By: Claude Opus 5.5 (1M context) --- Cargo.lock | 85 ++++++------- Cargo.toml | 16 +-- packages/rs-dpp/Cargo.toml | 10 ++ .../src/address_funds/platform_address.rs | 19 +-- .../identity/identity_public_key/key_type.rs | 51 ++++---- .../identity_public_key/v0/methods/mod.rs | 11 +- .../v0/mod.rs | 10 +- packages/rs-dpp/src/state_transition/mod.rs | 17 +-- .../signing_tests.rs | 17 ++- .../signing_tests.rs | 9 +- .../signing_tests.rs | 10 +- .../state_transition_identity_signed.rs | 7 +- .../instant_asset_lock_proof_fixture.rs | 10 +- .../src/execution/check_tx/v0/mod.rs | 17 ++- .../address_funding_from_asset_lock/tests.rs | 4 +- .../batch/tests/token/direct_selling/mod.rs | 9 +- .../identity_create_from_addresses/tests.rs | 51 ++++---- .../state_transitions/identity_update/mod.rs | 117 ++++++++---------- .../shield_from_asset_lock/tests.rs | 2 +- .../state_transitions/test_helpers.rs | 15 +-- .../tests/strategy_tests/strategy.rs | 4 +- .../src/proof/token_direct_purchase.rs | 7 +- packages/rs-platform-encryption/Cargo.toml | 6 +- .../src/account_label.rs | 11 +- packages/rs-platform-encryption/src/aes.rs | 4 +- .../src/compact_xpub.rs | 11 +- packages/rs-platform-encryption/src/ecdh.rs | 25 ++-- .../rs-platform-wallet-ffi/src/dashpay.rs | 4 +- .../rs-platform-wallet-ffi/src/derivation.rs | 8 +- .../src/derive_identity_key_at_slot.rs | 3 +- .../src/identity_derive_and_persist.rs | 8 +- .../src/identity_key_preview.rs | 6 +- .../src/identity_keys_from_mnemonic.rs | 8 +- .../src/identity_registration_with_signer.rs | 2 +- .../rs-platform-wallet-ffi/src/invitation.rs | 2 +- .../rs-platform-wallet-ffi/src/persistence.rs | 10 +- .../src/secp256k1_primitives.rs | 14 +-- .../src/sign_with_mnemonic_resolver.rs | 22 ++-- packages/rs-platform-wallet-ffi/src/utils.rs | 21 ++-- .../examples/dpns_marketplace_testnet.rs | 7 +- .../src/masternode/locator.rs | 15 +-- .../rs-platform-wallet/src/test_support.rs | 11 +- .../src/wallet/core/sign_message.rs | 15 +-- .../src/wallet/core_address_key.rs | 14 +-- .../src/wallet/identity/crypto/auto_accept.rs | 35 +++--- .../wallet/identity/crypto/contact_info.rs | 2 +- .../src/wallet/identity/crypto/dip14.rs | 5 +- .../src/wallet/identity/crypto/invitation.rs | 20 +-- .../src/wallet/identity/crypto/validation.rs | 7 +- .../wallet/identity/network/contact_info.rs | 4 +- .../identity/network/contact_requests.rs | 11 +- .../src/wallet/identity/network/discovery.rs | 2 +- .../identity/network/identity_handle.rs | 14 +-- .../src/wallet/identity/network/invitation.rs | 4 +- .../src/wallet/identity/network/payments.rs | 23 ++-- .../src/wallet/masternode_withdrawal.rs | 28 ++--- .../src/wallet/platform_addresses/provider.rs | 4 +- .../src/wallet/provider_ecdsa_key_tests.rs | 7 +- .../src/wallet/provider_key_at_index.rs | 31 ++--- .../rs-scripts/src/bin/register_identity.rs | 6 +- .../src/address/transitions/transfer.rs | 15 ++- .../src/address/transitions/withdraw.rs | 5 +- .../transitions/cast_vote.rs | 7 +- .../src/identity/create_from_addresses.rs | 5 +- packages/rs-sdk-ffi/src/identity/helpers.rs | 2 +- .../src/identity/top_up_from_addresses.rs | 5 +- .../src/mnemonic_resolver_core_signer.rs | 50 ++++---- packages/rs-sdk-ffi/src/signer_simple.rs | 6 +- .../src/platform/dashpay/contact_request.rs | 26 ++-- .../rs-sdk/src/platform/dpns_usernames/mod.rs | 4 +- .../platform/transition/broadcast_identity.rs | 2 +- .../src/platform/transition/put_document.rs | 4 +- .../src/platform/transition/top_up_address.rs | 2 +- .../platform/transition/top_up_identity.rs | 2 +- packages/rs-unified-sdk-jni/src/tx_decode.rs | 7 +- packages/simple-signer/Cargo.toml | 3 + packages/simple-signer/src/signer.rs | 15 +-- .../simple-signer/src/single_key_signer.rs | 16 +-- packages/strategy-tests/src/lib.rs | 4 +- packages/strategy-tests/src/transitions.rs | 13 +- packages/wasm-dpp2/src/core/private_key.rs | 12 +- packages/wasm-dpp2/src/identity/public_key.rs | 7 +- packages/wasm-dpp2/src/public_key.rs | 8 +- .../base/state_transition.rs | 4 +- packages/wasm-sdk/src/wallet/dip14.rs | 21 ++-- .../src/wallet/extended_derivation.rs | 10 +- .../wasm-sdk/src/wallet/key_derivation.rs | 19 +-- .../wasm-sdk/src/wallet/key_generation.rs | 14 +-- 88 files changed, 542 insertions(+), 674 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b0e4a6ff648..51bc3a52007 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -568,7 +568,7 @@ dependencies = [ "bitflags 2.13.0", "cexpr", "clang-sys", - "itertools 0.10.5", + "itertools 0.13.0", "proc-macro2", "quote", "regex", @@ -631,19 +631,12 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" -[[package]] -name = "bitcoin-io" -version = "0.1.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11301df0b06f22dea7bb1916403fdd88a371031e495c49b8f96931b28189e175" - [[package]] name = "bitcoin_hashes" version = "0.14.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c9901a56e133a1fc86eeb1113e2591f45f4682451ca893bff494d2f88918e3f" dependencies = [ - "bitcoin-io", "hex-conservative", ] @@ -1221,7 +1214,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -1654,7 +1647,7 @@ dependencies = [ [[package]] name = "dash-network" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e4208c90786a6854bd498315bcb571ef24182c15#e4208c90786a6854bd498315bcb571ef24182c15" +source = "git+https://github.com/dashpay/rust-dashcore?rev=719de34bd90792efa77aa5e1065ab21ac09206ee#719de34bd90792efa77aa5e1065ab21ac09206ee" dependencies = [ "cbindgen 0.29.4", "grovedb-bincode", @@ -1665,7 +1658,7 @@ dependencies = [ [[package]] name = "dash-network-seeds" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e4208c90786a6854bd498315bcb571ef24182c15#e4208c90786a6854bd498315bcb571ef24182c15" +source = "git+https://github.com/dashpay/rust-dashcore?rev=719de34bd90792efa77aa5e1065ab21ac09206ee#719de34bd90792efa77aa5e1065ab21ac09206ee" dependencies = [ "dash-network", ] @@ -1760,7 +1753,7 @@ dependencies = [ [[package]] name = "dash-spv" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e4208c90786a6854bd498315bcb571ef24182c15#e4208c90786a6854bd498315bcb571ef24182c15" +source = "git+https://github.com/dashpay/rust-dashcore?rev=719de34bd90792efa77aa5e1065ab21ac09206ee#719de34bd90792efa77aa5e1065ab21ac09206ee" dependencies = [ "async-trait", "chrono", @@ -1773,7 +1766,7 @@ dependencies = [ "hex", "key-wallet", "key-wallet-manager", - "rand 0.8.6", + "rand 0.9.4", "rayon", "serde", "serde_json", @@ -1789,7 +1782,7 @@ dependencies = [ [[package]] name = "dashcore" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e4208c90786a6854bd498315bcb571ef24182c15#e4208c90786a6854bd498315bcb571ef24182c15" +source = "git+https://github.com/dashpay/rust-dashcore?rev=719de34bd90792efa77aa5e1065ab21ac09206ee#719de34bd90792efa77aa5e1065ab21ac09206ee" dependencies = [ "anyhow", "base64-compat", @@ -1815,12 +1808,12 @@ dependencies = [ [[package]] name = "dashcore-private" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e4208c90786a6854bd498315bcb571ef24182c15#e4208c90786a6854bd498315bcb571ef24182c15" +source = "git+https://github.com/dashpay/rust-dashcore?rev=719de34bd90792efa77aa5e1065ab21ac09206ee#719de34bd90792efa77aa5e1065ab21ac09206ee" [[package]] name = "dashcore-rpc" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e4208c90786a6854bd498315bcb571ef24182c15#e4208c90786a6854bd498315bcb571ef24182c15" +source = "git+https://github.com/dashpay/rust-dashcore?rev=719de34bd90792efa77aa5e1065ab21ac09206ee#719de34bd90792efa77aa5e1065ab21ac09206ee" dependencies = [ "dashcore-rpc-json", "hex", @@ -1833,7 +1826,7 @@ dependencies = [ [[package]] name = "dashcore-rpc-json" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e4208c90786a6854bd498315bcb571ef24182c15#e4208c90786a6854bd498315bcb571ef24182c15" +source = "git+https://github.com/dashpay/rust-dashcore?rev=719de34bd90792efa77aa5e1065ab21ac09206ee#719de34bd90792efa77aa5e1065ab21ac09206ee" dependencies = [ "dashcore", "grovedb-bincode", @@ -1848,7 +1841,7 @@ dependencies = [ [[package]] name = "dashcore_hashes" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e4208c90786a6854bd498315bcb571ef24182c15#e4208c90786a6854bd498315bcb571ef24182c15" +source = "git+https://github.com/dashpay/rust-dashcore?rev=719de34bd90792efa77aa5e1065ab21ac09206ee#719de34bd90792efa77aa5e1065ab21ac09206ee" dependencies = [ "dashcore-private", "grovedb-bincode", @@ -2125,6 +2118,8 @@ dependencies = [ "dpp-json-convertible-derive", "env_logger", "getrandom 0.2.17", + "getrandom 0.3.4", + "getrandom 0.4.2", "grovedb-bincode", "grovedb-commitment-tree", "hex", @@ -2491,7 +2486,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2552,7 +2547,7 @@ checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" dependencies = [ "cfg-if", "rustix 1.1.4", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2890,11 +2885,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 6.0.0", "rand_core 0.10.1", "wasip2", "wasip3", + "wasm-bindgen", ] [[package]] @@ -2921,7 +2918,7 @@ dependencies = [ [[package]] name = "git-state" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e4208c90786a6854bd498315bcb571ef24182c15#e4208c90786a6854bd498315bcb571ef24182c15" +source = "git+https://github.com/dashpay/rust-dashcore?rev=719de34bd90792efa77aa5e1065ab21ac09206ee#719de34bd90792efa77aa5e1065ab21ac09206ee" [[package]] name = "glob" @@ -3897,7 +3894,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -4153,7 +4150,7 @@ dependencies = [ [[package]] name = "key-wallet" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e4208c90786a6854bd498315bcb571ef24182c15#e4208c90786a6854bd498315bcb571ef24182c15" +source = "git+https://github.com/dashpay/rust-dashcore?rev=719de34bd90792efa77aa5e1065ab21ac09206ee#719de34bd90792efa77aa5e1065ab21ac09206ee" dependencies = [ "aes", "async-trait", @@ -4164,11 +4161,11 @@ dependencies = [ "dashcore", "dashcore-private", "dashcore_hashes", - "getrandom 0.2.17", + "getrandom 0.4.2", "grovedb-bincode", "grovedb-bincode-derive", "hex", - "rand 0.8.6", + "rand 0.9.4", "scrypt", "secp256k1", "serde", @@ -4182,7 +4179,7 @@ dependencies = [ [[package]] name = "key-wallet-ffi" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e4208c90786a6854bd498315bcb571ef24182c15#e4208c90786a6854bd498315bcb571ef24182c15" +source = "git+https://github.com/dashpay/rust-dashcore?rev=719de34bd90792efa77aa5e1065ab21ac09206ee#719de34bd90792efa77aa5e1065ab21ac09206ee" dependencies = [ "cbindgen 0.29.4", "dash-network", @@ -4198,7 +4195,7 @@ dependencies = [ [[package]] name = "key-wallet-manager" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e4208c90786a6854bd498315bcb571ef24182c15#e4208c90786a6854bd498315bcb571ef24182c15" +source = "git+https://github.com/dashpay/rust-dashcore?rev=719de34bd90792efa77aa5e1065ab21ac09206ee#719de34bd90792efa77aa5e1065ab21ac09206ee" dependencies = [ "async-trait", "dashcore", @@ -4722,7 +4719,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -5617,8 +5614,8 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042" dependencies = [ - "heck 0.5.0", - "itertools 0.10.5", + "heck 0.4.1", + "itertools 0.14.0", "log", "multimap", "petgraph", @@ -5639,7 +5636,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.14.0", "proc-macro2", "quote", "syn 2.0.117", @@ -5652,7 +5649,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.14.0", "proc-macro2", "quote", "syn 2.0.117", @@ -5828,7 +5825,7 @@ dependencies = [ "once_cell", "socket2 0.5.10", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -6644,7 +6641,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -6657,7 +6654,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -6716,7 +6713,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -6875,21 +6872,20 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.30.0" +version = "0.33.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" +checksum = "d7f404a8dab7a7a5a631e741d8699aa9c8e1d689fc26ccf897c7187565490b69" dependencies = [ - "bitcoin_hashes", - "rand 0.8.6", + "rand 0.9.4", "secp256k1-sys", "serde", ] [[package]] name = "secp256k1-sys" -version = "0.10.1" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +checksum = "f6b2992d4a3cd244539a7d5d0966aadbe5ca7fb868a5d7e38c29499b9e709bbd" dependencies = [ "cc", ] @@ -7269,6 +7265,7 @@ dependencies = [ "dpp", "grovedb-bincode", "hex", + "rand 0.8.6", "tracing", "zeroize", ] @@ -7578,7 +7575,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix 1.1.4", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -9024,7 +9021,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 09d41d94b47..8068ff3d235 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,14 +64,14 @@ grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "74818ceb9 grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "74818ceb95805f56f141d24ca41f58b75c031a7f" } grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "74818ceb95805f56f141d24ca41f58b75c031a7f" } grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "74818ceb95805f56f141d24ca41f58b75c031a7f" } -dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "e4208c90786a6854bd498315bcb571ef24182c15" } -dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "e4208c90786a6854bd498315bcb571ef24182c15" } -dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "e4208c90786a6854bd498315bcb571ef24182c15" } -key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "e4208c90786a6854bd498315bcb571ef24182c15" } -key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "e4208c90786a6854bd498315bcb571ef24182c15" } -key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "e4208c90786a6854bd498315bcb571ef24182c15" } -dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "e4208c90786a6854bd498315bcb571ef24182c15" } -dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "e4208c90786a6854bd498315bcb571ef24182c15" } +dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "719de34bd90792efa77aa5e1065ab21ac09206ee" } +dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "719de34bd90792efa77aa5e1065ab21ac09206ee" } +dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "719de34bd90792efa77aa5e1065ab21ac09206ee" } +key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "719de34bd90792efa77aa5e1065ab21ac09206ee" } +key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "719de34bd90792efa77aa5e1065ab21ac09206ee" } +key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "719de34bd90792efa77aa5e1065ab21ac09206ee" } +dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "719de34bd90792efa77aa5e1065ab21ac09206ee" } +dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "719de34bd90792efa77aa5e1065ab21ac09206ee" } tokio-metrics = "0.5" # Size-tuned profile for the iOS `rs-unified-sdk-ffi` staticlib, which diff --git a/packages/rs-dpp/Cargo.toml b/packages/rs-dpp/Cargo.toml index d98e26f03b6..d85c3e3fdcb 100644 --- a/packages/rs-dpp/Cargo.toml +++ b/packages/rs-dpp/Cargo.toml @@ -90,6 +90,16 @@ once_cell = "1.7" env_logger = { version = "0.11.8" } log = { version = "0.4.27" } +# secp256k1 0.33 pulls getrandom 0.3 (via rand 0.9) and key-wallet pulls +# getrandom 0.4; on wasm32-unknown-unknown both refuse to build without their +# `wasm_js` feature, so enable it here where every wasm crate picks it up. +[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] +getrandom_03 = { package = "getrandom", version = "0.3", features = ["wasm_js"] } +getrandom_04 = { package = "getrandom", version = "0.4", features = ["wasm_js"] } + +[package.metadata.cargo-machete] +ignored = ["getrandom_03", "getrandom_04"] + [features] default = ["state-transitions"] core_bincode = ["dashcore/bincode"] diff --git a/packages/rs-dpp/src/address_funds/platform_address.rs b/packages/rs-dpp/src/address_funds/platform_address.rs index 77a94d5b724..3eb72d2bbab 100644 --- a/packages/rs-dpp/src/address_funds/platform_address.rs +++ b/packages/rs-dpp/src/address_funds/platform_address.rs @@ -7,7 +7,6 @@ use bincode::{Decode, DecodeUntrusted, Encode}; use dashcore::address::Payload; use dashcore::blockdata::script::ScriptBuf; use dashcore::hashes::{sha256d, Hash}; -use dashcore::key::Secp256k1; use dashcore::secp256k1::ecdsa::RecoverableSignature; use dashcore::secp256k1::Message; use dashcore::signer::CompactSignature; @@ -243,8 +242,7 @@ impl From<&PrivateKey> for PlatformAddress { /// The address is derived as: P2PKH(Hash160(compressed_public_key)) /// where Hash160 = RIPEMD160(SHA256(x)), which is the standard Bitcoin P2PKH derivation. fn from(private_key: &PrivateKey) -> Self { - let secp = Secp256k1::new(); - let pubkey_hash = private_key.public_key(&secp).pubkey_hash(); + let pubkey_hash = private_key.public_key().pubkey_hash(); PlatformAddress::P2pkh(*pubkey_hash.as_byte_array()) } } @@ -551,7 +549,6 @@ impl PlatformAddress { let signable_bytes_hash = sha256d::Hash::hash(signable_bytes).to_byte_array(); let msg = Message::from_digest(signable_bytes_hash); - let secp = Secp256k1::new(); while sig_idx < valid_signatures.len() && pubkey_idx < pubkeys.len() { signature_verifications += 1; @@ -570,10 +567,7 @@ impl PlatformAddress { ProtocolError::AddressWitnessError(format!("Invalid public key: {}", e)) })?; - if secp - .verify_ecdsa(&msg, &sig.to_standard(), &pub_key.inner) - .is_ok() - { + if sig.to_standard().verify(msg, &pub_key.inner).is_ok() { matched += 1; sig_idx += 1; } @@ -794,7 +788,7 @@ mod tests { use super::*; use dashcore::blockdata::opcodes::all::*; use dashcore::hashes::Hash; - use dashcore::secp256k1::{PublicKey as RawPublicKey, Secp256k1, SecretKey as RawSecretKey}; + use dashcore::secp256k1::{PublicKey as RawPublicKey, SecretKey as RawSecretKey}; use dashcore::PublicKey; use platform_value::BinaryData; @@ -818,16 +812,15 @@ mod tests { /// Helper to create a keypair from a 32-byte seed fn create_keypair(seed: [u8; 32]) -> (RawSecretKey, PublicKey) { - let secp = Secp256k1::new(); - let secret_key = RawSecretKey::from_byte_array(&seed).expect("valid secret key"); - let raw_public_key = RawPublicKey::from_secret_key(&secp, &secret_key); + let secret_key = RawSecretKey::from_secret_bytes(seed).expect("valid secret key"); + let raw_public_key = RawPublicKey::from_secret_key(&secret_key); let public_key = PublicKey::new(raw_public_key); (secret_key, public_key) } /// Helper to sign data with a secret key fn sign_data(data: &[u8], secret_key: &RawSecretKey) -> Vec { - dashcore::signer::sign(data, secret_key.as_ref()) + dashcore::signer::sign(data, secret_key.as_secret_bytes()) .expect("signing should succeed") .to_vec() } diff --git a/packages/rs-dpp/src/identity/identity_public_key/key_type.rs b/packages/rs-dpp/src/identity/identity_public_key/key_type.rs index 0914d245a04..76ab962ae5e 100644 --- a/packages/rs-dpp/src/identity/identity_public_key/key_type.rs +++ b/packages/rs-dpp/src/identity/identity_public_key/key_type.rs @@ -7,7 +7,6 @@ use ciborium::value::Value as CborValue; use dashcore::secp256k1::rand::rngs::StdRng as EcdsaRng; #[cfg(feature = "random-public-keys")] use dashcore::secp256k1::rand::SeedableRng; -use dashcore::secp256k1::Secp256k1; use dashcore::Network; use itertools::Itertools; use lazy_static::lazy_static; @@ -161,11 +160,12 @@ impl KeyType { fn random_public_key_data_v0(&self, rng: &mut StdRng) -> Vec { match self { KeyType::ECDSA_SECP256K1 => { - let secp = Secp256k1::new(); - let mut rng = EcdsaRng::from_rng(rng).unwrap(); + let mut seed = [0u8; 32]; + rng.fill(&mut seed); + let mut rng = EcdsaRng::from_seed(seed); let secret_key = dashcore::secp256k1::SecretKey::new(&mut rng); let private_key = dashcore::PrivateKey::new(secret_key, Network::Mainnet); - private_key.public_key(&secp).to_bytes() + private_key.public_key().to_bytes() } KeyType::BLS12_381 => { let private_key = bls_signatures::SecretKey::::random(rng); @@ -207,12 +207,12 @@ impl KeyType { ) -> Result, ProtocolError> { match self { KeyType::ECDSA_SECP256K1 => { - let secp = Secp256k1::new(); - let secret_key = dashcore::secp256k1::SecretKey::from_byte_array(private_key_bytes) - .map_err(|e| ProtocolError::Generic(e.to_string()))?; + let secret_key = + dashcore::secp256k1::SecretKey::from_secret_bytes(*private_key_bytes) + .map_err(|e| ProtocolError::Generic(e.to_string()))?; let private_key = dashcore::PrivateKey::new(secret_key, network); - Ok(private_key.public_key(&secp).to_bytes()) + Ok(private_key.public_key().to_bytes()) } KeyType::BLS12_381 => { #[cfg(feature = "bls-signatures")] @@ -237,12 +237,12 @@ impl KeyType { )); } KeyType::ECDSA_HASH160 => { - let secp = Secp256k1::new(); - let secret_key = dashcore::secp256k1::SecretKey::from_byte_array(private_key_bytes) - .map_err(|e| ProtocolError::Generic(e.to_string()))?; + let secret_key = + dashcore::secp256k1::SecretKey::from_secret_bytes(*private_key_bytes) + .map_err(|e| ProtocolError::Generic(e.to_string()))?; let private_key = dashcore::PrivateKey::new(secret_key, network); - Ok(ripemd160_sha256(private_key.public_key(&secp).to_bytes().as_slice()).to_vec()) + Ok(ripemd160_sha256(private_key.public_key().to_bytes().as_slice()).to_vec()) } KeyType::EDDSA_25519_HASH160 => { #[cfg(feature = "ed25519-dalek")] @@ -267,13 +267,14 @@ impl KeyType { pub fn random_public_and_private_key_data_v0(&self, rng: &mut StdRng) -> (Vec, [u8; 32]) { match self { KeyType::ECDSA_SECP256K1 => { - let secp = Secp256k1::new(); - let mut rng = EcdsaRng::from_rng(rng).unwrap(); + let mut seed = [0u8; 32]; + rng.fill(&mut seed); + let mut rng = EcdsaRng::from_seed(seed); let secret_key = dashcore::secp256k1::SecretKey::new(&mut rng); let private_key = dashcore::PrivateKey::new(secret_key, Network::Mainnet); ( - private_key.public_key(&secp).to_bytes(), - private_key.inner.secret_bytes(), + private_key.public_key().to_bytes(), + private_key.inner.to_secret_bytes(), ) } KeyType::BLS12_381 => { @@ -282,13 +283,14 @@ impl KeyType { (public_key_bytes, private_key.0.to_be_bytes()) } KeyType::ECDSA_HASH160 => { - let secp = Secp256k1::new(); - let mut rng = EcdsaRng::from_rng(rng).unwrap(); + let mut seed = [0u8; 32]; + rng.fill(&mut seed); + let mut rng = EcdsaRng::from_seed(seed); let secret_key = dashcore::secp256k1::SecretKey::new(&mut rng); let private_key = dashcore::PrivateKey::new(secret_key, Network::Mainnet); ( - ripemd160_sha256(private_key.public_key(&secp).to_bytes().as_slice()).to_vec(), - private_key.inner.secret_bytes(), + ripemd160_sha256(private_key.public_key().to_bytes().as_slice()).to_vec(), + private_key.inner.to_secret_bytes(), ) } KeyType::EDDSA_25519_HASH160 => { @@ -300,13 +302,14 @@ impl KeyType { } KeyType::BIP13_SCRIPT_HASH => { //todo (using ECDSA_HASH160 for now) - let secp = Secp256k1::new(); - let mut rng = EcdsaRng::from_rng(rng).unwrap(); + let mut seed = [0u8; 32]; + rng.fill(&mut seed); + let mut rng = EcdsaRng::from_seed(seed); let secret_key = dashcore::secp256k1::SecretKey::new(&mut rng); let private_key = dashcore::PrivateKey::new(secret_key, Network::Mainnet); ( - ripemd160_sha256(private_key.public_key(&secp).to_bytes().as_slice()).to_vec(), - private_key.inner.secret_bytes(), + ripemd160_sha256(private_key.public_key().to_bytes().as_slice()).to_vec(), + private_key.inner.to_secret_bytes(), ) } } diff --git a/packages/rs-dpp/src/identity/identity_public_key/v0/methods/mod.rs b/packages/rs-dpp/src/identity/identity_public_key/v0/methods/mod.rs index f1faa9d14e3..78613737f52 100644 --- a/packages/rs-dpp/src/identity/identity_public_key/v0/methods/mod.rs +++ b/packages/rs-dpp/src/identity/identity_public_key/v0/methods/mod.rs @@ -7,7 +7,6 @@ use anyhow::anyhow; #[cfg(feature = "ed25519-dalek")] use dashcore::ed25519_dalek; use dashcore::hashes::Hash; -use dashcore::key::Secp256k1; use dashcore::secp256k1::SecretKey; use dashcore::{Network, PublicKey as ECDSAPublicKey}; use platform_value::{BinaryData, Bytes20}; @@ -83,14 +82,13 @@ pub(in crate::identity::identity_public_key) fn validate_private_key_bytes_for_k ) -> Result { match key_type { KeyType::ECDSA_SECP256K1 => { - let secp = Secp256k1::new(); - let secret_key = match SecretKey::from_byte_array(private_key_bytes) { + let secret_key = match SecretKey::from_secret_bytes(*private_key_bytes) { Ok(secret_key) => secret_key, Err(_) => return Ok(false), }; let private_key = dashcore::PrivateKey::new(secret_key, network); - Ok(private_key.public_key(&secp).to_bytes() == data.as_slice()) + Ok(private_key.public_key().to_bytes() == data.as_slice()) } KeyType::BLS12_381 => { #[cfg(feature = "bls-signatures")] @@ -111,15 +109,14 @@ pub(in crate::identity::identity_public_key) fn validate_private_key_bytes_for_k )); } KeyType::ECDSA_HASH160 => { - let secp = Secp256k1::new(); - let secret_key = match SecretKey::from_byte_array(private_key_bytes) { + let secret_key = match SecretKey::from_secret_bytes(*private_key_bytes) { Ok(secret_key) => secret_key, Err(_) => return Ok(false), }; let private_key = dashcore::PrivateKey::new(secret_key, network); Ok( - ripemd160_sha256(private_key.public_key(&secp).to_bytes().as_slice()).as_slice() + ripemd160_sha256(private_key.public_key().to_bytes().as_slice()).as_slice() == data.as_slice(), ) } diff --git a/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/validate_asset_lock_transaction_structure/v0/mod.rs b/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/validate_asset_lock_transaction_structure/v0/mod.rs index fe354a865a2..8b2c1560226 100644 --- a/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/validate_asset_lock_transaction_structure/v0/mod.rs +++ b/packages/rs-dpp/src/identity/state_transition/asset_lock_proof/validate_asset_lock_transaction_structure/v0/mod.rs @@ -58,24 +58,22 @@ pub(super) fn validate_asset_lock_transaction_structure_v0( #[cfg(test)] mod tests { use super::*; - use dashcore::secp256k1::rand::thread_rng; - use dashcore::secp256k1::Secp256k1; + use dashcore::secp256k1::rand::rng; use dashcore::transaction::special_transaction::asset_lock::AssetLockPayload; use dashcore::{Network, OutPoint, PrivateKey, ScriptBuf, TxIn, Txid}; use std::str::FromStr; fn make_asset_lock_transaction(num_inputs: usize) -> Transaction { - let secp = Secp256k1::new(); - let mut rng = thread_rng(); + let mut rng = rng(); let input_secret_key = dashcore::secp256k1::SecretKey::new(&mut rng); let private_key = PrivateKey::new(input_secret_key, Network::Testnet); - let public_key = private_key.public_key(&secp); + let public_key = private_key.public_key(); let public_key_hash = public_key.pubkey_hash(); let secret_key = dashcore::secp256k1::SecretKey::new(&mut rng); let one_time_private_key = PrivateKey::new(secret_key, Network::Testnet); - let one_time_public_key = one_time_private_key.public_key(&secp); + let one_time_public_key = one_time_private_key.public_key(); let one_time_key_hash = one_time_public_key.pubkey_hash(); let base_txid = diff --git a/packages/rs-dpp/src/state_transition/mod.rs b/packages/rs-dpp/src/state_transition/mod.rs index af8612a1f0d..576c06230e4 100644 --- a/packages/rs-dpp/src/state_transition/mod.rs +++ b/packages/rs-dpp/src/state_transition/mod.rs @@ -2040,7 +2040,7 @@ impl StateTransition { signer: &S, ) -> Result<(), ProtocolError> { use dashcore::secp256k1::ecdsa::{RecoverableSignature, RecoveryId}; - use dashcore::secp256k1::{Message, Secp256k1}; + use dashcore::secp256k1::Message; use dashcore::signer::{double_sha, CompactSignature}; let data = self.signable_bytes()?; @@ -2065,7 +2065,6 @@ impl StateTransition { // `r||s` payload is bit-identical to what `dashcore::signer::sign` // produces. let compact_64 = signature.serialize_compact(); - let secp = Secp256k1::new(); let msg = Message::from_digest(digest); let mut found: Option = None; @@ -2078,7 +2077,7 @@ impl StateTransition { Ok(s) => s, Err(_) => continue, }; - if let Ok(recovered) = secp.recover_ecdsa(&msg, &candidate) { + if let Ok(recovered) = candidate.recover_ecdsa(msg) { if recovered == public_key { found = Some(candidate); break; @@ -4346,9 +4345,7 @@ mod tests { #[tokio::test] async fn sign_with_core_signer_matches_sign_by_private_key_byte_for_byte() { use async_trait::async_trait; - use dashcore::secp256k1::{ - ecdsa, rand::rngs::OsRng, Message, PublicKey, Secp256k1, SecretKey, - }; + use dashcore::secp256k1::{self, ecdsa, rand, Message, PublicKey, SecretKey}; use key_wallet::bip32::{DerivationPath, ExtendedPubKey}; use key_wallet::signer::{ExtendedPubKeySigner, Signer as KwSigner, SignerMethod}; @@ -4375,9 +4372,8 @@ mod tests { _path: &DerivationPath, sighash: [u8; 32], ) -> Result<(ecdsa::Signature, PublicKey), Self::Error> { - let secp = Secp256k1::new(); let msg = Message::from_digest(sighash); - let sig = secp.sign_ecdsa(&msg, &self.secret); + let sig = self.secret.sign_ecdsa(msg); Ok((sig, self.public)) } @@ -4400,9 +4396,8 @@ mod tests { // load-bearing: the legacy path signs raw bytes, the signer path // derives + signs inside the trust boundary. If the digest pre-image // or compact-encoding differs, the bytes will diverge. - let secp = Secp256k1::new(); - let (secret_key, public_key) = secp.generate_keypair(&mut OsRng); - let private_key_bytes = secret_key.secret_bytes(); + let (secret_key, public_key) = secp256k1::generate_keypair(&mut rand::rng()); + let private_key_bytes = secret_key.to_secret_bytes(); let signer = FixedKeySigner { secret: secret_key, diff --git a/packages/rs-dpp/src/state_transition/state_transitions/address_funds/address_funding_from_asset_lock_transition/signing_tests.rs b/packages/rs-dpp/src/state_transition/state_transitions/address_funds/address_funding_from_asset_lock_transition/signing_tests.rs index 8719b18ea90..fa7c6f13398 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/address_funds/address_funding_from_asset_lock_transition/signing_tests.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/address_funds/address_funding_from_asset_lock_transition/signing_tests.rs @@ -14,7 +14,7 @@ use std::collections::{BTreeMap, HashMap}; use dashcore::hashes::Hash; -use dashcore::secp256k1::{PublicKey as RawPublicKey, Secp256k1, SecretKey as RawSecretKey}; +use dashcore::secp256k1::{PublicKey as RawPublicKey, SecretKey as RawSecretKey}; use dashcore::{OutPoint, PublicKey}; use platform_value::BinaryData; use platform_version::version::PlatformVersion; @@ -38,9 +38,8 @@ struct TestAddressSigner { impl TestAddressSigner { fn add_p2pkh(&mut self, seed: [u8; 32]) -> PlatformAddress { - let secp = Secp256k1::new(); - let secret = RawSecretKey::from_byte_array(&seed).expect("valid secret key"); - let public = PublicKey::new(RawPublicKey::from_secret_key(&secp, &secret)); + let secret = RawSecretKey::from_secret_bytes(seed).expect("valid secret key"); + let public = PublicKey::new(RawPublicKey::from_secret_key(&secret)); let hash = *public.pubkey_hash().as_byte_array(); self.keys.insert(hash, (secret, public)); PlatformAddress::P2pkh(hash) @@ -59,7 +58,7 @@ impl Signer for TestAddressSigner { .keys .get(hash) .ok_or_else(|| ProtocolError::Generic(format!("unknown key {}", hex::encode(hash))))?; - let sig = dashcore::signer::sign(data, secret.as_ref()) + let sig = dashcore::signer::sign(data, secret.as_secret_bytes()) .map_err(|e| ProtocolError::Generic(e.to_string()))?; Ok(BinaryData::new(sig.to_vec())) } @@ -229,9 +228,8 @@ async fn try_from_asset_lock_with_signers_produces_matching_signature() { _path: &DerivationPath, sighash: [u8; 32], ) -> Result<(ecdsa::Signature, RawPublicKey), Self::Error> { - let secp = Secp256k1::new(); let msg = Message::from_digest(sighash); - Ok((secp.sign_ecdsa(&msg, &self.secret), self.public)) + Ok((self.secret.sign_ecdsa(msg), self.public)) } async fn public_key(&self, _path: &DerivationPath) -> Result { @@ -249,9 +247,8 @@ async fn try_from_asset_lock_with_signers_produces_matching_signature() { } } - let secp = Secp256k1::new(); - let asset_lock_secret = RawSecretKey::from_byte_array(&[7u8; 32]).expect("valid secret"); - let asset_lock_public = RawPublicKey::from_secret_key(&secp, &asset_lock_secret); + let asset_lock_secret = RawSecretKey::from_secret_bytes([7u8; 32]).expect("valid secret"); + let asset_lock_public = RawPublicKey::from_secret_key(&asset_lock_secret); let mut input_signer = TestAddressSigner::default(); let input_addr = input_signer.add_p2pkh([1u8; 32]); diff --git a/packages/rs-dpp/src/state_transition/state_transitions/address_funds/address_funds_transfer_transition/signing_tests.rs b/packages/rs-dpp/src/state_transition/state_transitions/address_funds/address_funds_transfer_transition/signing_tests.rs index 866e39f123c..6ac8d35825d 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/address_funds/address_funds_transfer_transition/signing_tests.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/address_funds/address_funds_transfer_transition/signing_tests.rs @@ -13,7 +13,7 @@ use std::collections::{BTreeMap, HashMap}; use dashcore::blockdata::opcodes::all::*; use dashcore::blockdata::script::ScriptBuf; use dashcore::hashes::Hash; -use dashcore::secp256k1::{PublicKey as RawPublicKey, Secp256k1, SecretKey as RawSecretKey}; +use dashcore::secp256k1::{PublicKey as RawPublicKey, SecretKey as RawSecretKey}; use dashcore::PublicKey; use platform_value::BinaryData; @@ -64,16 +64,15 @@ impl TestAddressSigner { /// Creates a keypair from a 32-byte seed fn create_keypair(seed: [u8; 32]) -> (RawSecretKey, PublicKey) { - let secp = Secp256k1::new(); - let secret_key = RawSecretKey::from_byte_array(&seed).expect("valid secret key"); - let raw_public_key = RawPublicKey::from_secret_key(&secp, &secret_key); + let secret_key = RawSecretKey::from_secret_bytes(seed).expect("valid secret key"); + let raw_public_key = RawPublicKey::from_secret_key(&secret_key); let public_key = PublicKey::new(raw_public_key); (secret_key, public_key) } /// Signs data with a secret key fn sign_data(data: &[u8], secret_key: &RawSecretKey) -> Vec { - dashcore::signer::sign(data, secret_key.as_ref()) + dashcore::signer::sign(data, secret_key.as_secret_bytes()) .expect("signing should succeed") .to_vec() } diff --git a/packages/rs-dpp/src/state_transition/state_transitions/shielded/shield_from_asset_lock_transition/signing_tests.rs b/packages/rs-dpp/src/state_transition/state_transitions/shielded/shield_from_asset_lock_transition/signing_tests.rs index 719074e382e..37c590d971b 100644 --- a/packages/rs-dpp/src/state_transition/state_transitions/shielded/shield_from_asset_lock_transition/signing_tests.rs +++ b/packages/rs-dpp/src/state_transition/state_transitions/shielded/shield_from_asset_lock_transition/signing_tests.rs @@ -32,7 +32,7 @@ use dashcore::OutPoint; use platform_version::version::PlatformVersion; use async_trait::async_trait; -use dashcore::secp256k1::{ecdsa, Message, PublicKey, Secp256k1, SecretKey}; +use dashcore::secp256k1::{ecdsa, Message, PublicKey, SecretKey}; use key_wallet::bip32::{DerivationPath, ExtendedPubKey}; use key_wallet::signer::{ExtendedPubKeySigner, Signer as KwSigner, SignerMethod}; @@ -49,9 +49,8 @@ struct FixedKeySigner { impl FixedKeySigner { fn new(seed: [u8; 32]) -> Self { - let secp = Secp256k1::new(); - let secret = SecretKey::from_byte_array(&seed).expect("valid secret"); - let public = PublicKey::from_secret_key(&secp, &secret); + let secret = SecretKey::from_secret_bytes(seed).expect("valid secret"); + let public = PublicKey::from_secret_key(&secret); Self { secret, public } } } @@ -69,9 +68,8 @@ impl KwSigner for FixedKeySigner { _path: &DerivationPath, sighash: [u8; 32], ) -> Result<(ecdsa::Signature, PublicKey), Self::Error> { - let secp = Secp256k1::new(); let msg = Message::from_digest(sighash); - Ok((secp.sign_ecdsa(&msg, &self.secret), self.public)) + Ok((self.secret.sign_ecdsa(msg), self.public)) } async fn public_key(&self, _path: &DerivationPath) -> Result { diff --git a/packages/rs-dpp/src/state_transition/traits/state_transition_identity_signed.rs b/packages/rs-dpp/src/state_transition/traits/state_transition_identity_signed.rs index f005be86412..ff38671e537 100644 --- a/packages/rs-dpp/src/state_transition/traits/state_transition_identity_signed.rs +++ b/packages/rs-dpp/src/state_transition/traits/state_transition_identity_signed.rs @@ -106,11 +106,12 @@ pub trait StateTransitionIdentitySigned: StateTransitionLike { } pub fn get_compressed_public_ec_key(private_key: &[u8]) -> Result<[u8; 33], ProtocolError> { - let sk = RawSecretKey::from_slice(private_key) + let sk = <[u8; 32]>::try_from(private_key) + .map_err(|_| dashcore::secp256k1::Error::InvalidSecretKey) + .and_then(RawSecretKey::from_secret_bytes) .map_err(|e| anyhow!("Invalid ECDSA private key: {}", e))?; - let secp = dashcore::secp256k1::Secp256k1::new(); - let public_key_compressed = RawPublicKey::from_secret_key(&secp, &sk).serialize(); + let public_key_compressed = RawPublicKey::from_secret_key(&sk).serialize(); Ok(public_key_compressed) } diff --git a/packages/rs-dpp/src/tests/fixtures/instant_asset_lock_proof_fixture.rs b/packages/rs-dpp/src/tests/fixtures/instant_asset_lock_proof_fixture.rs index f55590029df..23e620c6f81 100644 --- a/packages/rs-dpp/src/tests/fixtures/instant_asset_lock_proof_fixture.rs +++ b/packages/rs-dpp/src/tests/fixtures/instant_asset_lock_proof_fixture.rs @@ -4,8 +4,7 @@ use dashcore::bls_sig_utils::BLSSignature; use dashcore::hash_types::CycleHash; use crate::balances::credits::Duffs; -use dashcore::secp256k1::rand::thread_rng; -use dashcore::secp256k1::Secp256k1; +use dashcore::secp256k1::rand::rng; use dashcore::transaction::special_transaction::asset_lock::AssetLockPayload; use dashcore::transaction::special_transaction::TransactionPayload; use dashcore::{ @@ -46,18 +45,17 @@ pub fn instant_asset_lock_proof_transaction_fixture( one_time_private_key: Option, amount: Option, ) -> Transaction { - let mut rng = thread_rng(); - let secp = Secp256k1::new(); + let mut rng = rng(); let private_key_hex = "cSBnVM4xvxarwGQuAfQFwqDg9k5tErHUHzgWsEfD4zdwUasvqRVY"; let private_key = PrivateKey::from_str(private_key_hex).unwrap(); - let public_key = private_key.public_key(&secp); + let public_key = private_key.public_key(); let public_key_hash = public_key.pubkey_hash(); //let from_address = Address::p2pkh(&public_key, Network::Testnet); let secret_key = SecretKey::new(&mut rng); let one_time_private_key = one_time_private_key.unwrap_or_else(|| PrivateKey::new(secret_key, Network::Testnet)); - let one_time_public_key = one_time_private_key.public_key(&secp); + let one_time_public_key = one_time_private_key.public_key(); // We are going to fund 1 Dash and // assume that input has 100005000 diff --git a/packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs b/packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs index da4dbce479c..181a4e2f5b5 100644 --- a/packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs @@ -257,7 +257,6 @@ mod tests { use simple_signer::signer::SimpleSigner; use dpp::consensus::ConsensusError; - use dpp::dashcore::secp256k1::Secp256k1; use dpp::dashcore::{key::Keypair, signer, Network, PrivateKey}; use dpp::data_contract::accessors::v0::{DataContractV0Getters, DataContractV0Setters}; @@ -300,6 +299,8 @@ mod tests { use assert_matches::assert_matches; use dpp::consensus::state::state_error::StateError; use dpp::dash_to_credits; + use dpp::dashcore::secp256k1::rand::rngs::StdRng as SecpStdRng; + use dpp::dashcore::secp256k1::rand::SeedableRng as _; use dpp::data_contract::associated_token::token_configuration::accessors::v0::TokenConfigurationV0Setters; use dpp::data_contract::change_control_rules::authorized_action_takers::AuthorizedActionTakers; use dpp::data_contract::change_control_rules::v0::ChangeControlRulesV0; @@ -3868,15 +3869,13 @@ mod tests { .build_with_mock_rpc() .set_genesis_state(); - let mut rng = StdRng::seed_from_u64(433); + let mut rng = SecpStdRng::seed_from_u64(433); let platform_state = platform.state.load(); let (identity, signer, key) = setup_identity(&mut platform, 958, dash_to_credits!(0.1)); - let secp = Secp256k1::new(); - - let new_key_pair = Keypair::new(&secp, &mut rng); + let new_key_pair = Keypair::new(&mut rng); let mut new_key = IdentityPublicKeyInCreationV0 { id: 2, @@ -3894,7 +3893,7 @@ mod tests { .expect("expected to get signable bytes"); let secret = new_key_pair.secret_key(); let signature = - signer::sign(&signable_bytes, &secret.secret_bytes()).expect("expected to sign"); + signer::sign(&signable_bytes, &secret.to_secret_bytes()).expect("expected to sign"); new_key.signature = signature.to_vec().into(); @@ -3972,13 +3971,11 @@ mod tests { let (identity, signer, _, key) = setup_identity_return_master_key(&mut platform, 958, dash_to_credits!(0.1)); - let mut rng = StdRng::seed_from_u64(1); - - let secp = Secp256k1::new(); + let mut rng = SecpStdRng::seed_from_u64(1); let platform_state = platform.state.load(); - let new_key_pair = Keypair::new(&secp, &mut rng); + let new_key_pair = Keypair::new(&mut rng); let new_key = IdentityPublicKeyInCreationV0 { id: 2, diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/address_funding_from_asset_lock/tests.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/address_funding_from_asset_lock/tests.rs index 7a002a2c7e8..53664f399df 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/address_funding_from_asset_lock/tests.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/address_funding_from_asset_lock/tests.rs @@ -21,7 +21,6 @@ mod tests { use dpp::consensus::ConsensusError; use dpp::dash_to_credits; use dpp::dashcore::blockdata::script::ScriptBuf; - use dpp::dashcore::secp256k1::Secp256k1; use dpp::dashcore::transaction::special_transaction::asset_lock::AssetLockPayload; use dpp::dashcore::transaction::special_transaction::TransactionPayload; use dpp::dashcore::{BlockHash, Network, OutPoint, PrivateKey, Transaction, TxIn, TxOut, Txid}; @@ -137,7 +136,6 @@ mod tests { use dpp::identity::state_transition::asset_lock_proof::chain::ChainAssetLockProof; let platform_version = PlatformVersion::latest(); - let secp = Secp256k1::new(); // Generate the one-time key that will receive the asset lock funds let (_, pk) = ECDSA_SECP256K1 @@ -145,7 +143,7 @@ mod tests { .unwrap(); let one_time_private_key = PrivateKey::from_byte_array(&pk, Network::Testnet).unwrap(); - let one_time_public_key = one_time_private_key.public_key(&secp); + let one_time_public_key = one_time_private_key.public_key(); let one_time_key_hash = one_time_public_key.pubkey_hash(); // Create a fake input (doesn't need to be real for our tests) diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/direct_selling/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/direct_selling/mod.rs index f20e9b5abf9..62b086cb01c 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/direct_selling/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/direct_selling/mod.rs @@ -9,7 +9,6 @@ mod token_selling_tests { use super::*; use dpp::{ - dashcore::secp256k1::hashes::hex::{Case, DisplayHex}, prelude::{DataContract, Identity, IdentityPublicKey}, tokens::token_pricing_schedule::TokenPricingSchedule, }; @@ -635,7 +634,7 @@ mod token_selling_tests { expected_price, format!( "price in proof mismatch for token {}", - token_id.to_hex_string(Case::Lower) + hex::encode(token_id) ) .as_str(), ); @@ -645,11 +644,7 @@ mod token_selling_tests { fetched_prices.clone(), token_id, expected_price, - format!( - "fetched price mismatch for token {}", - token_id.to_hex_string(Case::Lower) - ) - .as_str(), + format!("fetched price mismatch for token {}", hex::encode(token_id)).as_str(), ); } } diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_create_from_addresses/tests.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_create_from_addresses/tests.rs index f07845366b1..2404f2032a5 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_create_from_addresses/tests.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_create_from_addresses/tests.rs @@ -5,7 +5,6 @@ mod tests { use crate::execution::validation::state_transition::state_transitions::test_helpers::{ create_dummy_witness, create_platform_address, setup_address_with_balance, TestAddressSigner, TestHash as Hash, TestPublicKey as PublicKey, - TestSecp256k1 as Secp256k1, }; use crate::platform_types::state_transitions_processing_result::StateTransitionExecutionResult; use crate::test::helpers::setup::TestPlatformBuilder; @@ -9377,7 +9376,7 @@ mod tests { mod actual_signature_verification { use super::*; use dpp::dashcore::hashes::Hash; - use dpp::dashcore::secp256k1::{PublicKey as RawSecp256k1PublicKey, Secp256k1}; + use dpp::dashcore::secp256k1::PublicKey as RawSecp256k1PublicKey; use dpp::serialization::Signable; /// Helper to create a properly signed P2PKH witness @@ -9386,15 +9385,15 @@ mod tests { secret_key: &dpp::dashcore::secp256k1::SecretKey, signable_bytes: &[u8], ) -> (PlatformAddress, AddressWitness) { - let secp = Secp256k1::new(); - let raw_pubkey = RawSecp256k1PublicKey::from_secret_key(&secp, secret_key); + let raw_pubkey = RawSecp256k1PublicKey::from_secret_key(secret_key); let pubkey = PublicKey::new(raw_pubkey); let pubkey_hash = dpp::dashcore::hashes::hash160::Hash::hash(&pubkey.to_bytes()); let address = PlatformAddress::P2pkh(pubkey_hash.to_byte_array()); // Sign using dashcore::signer which creates a recoverable signature - let signature = dpp::dashcore::signer::sign(signable_bytes, secret_key.as_ref()) - .expect("signing should succeed"); + let signature = + dpp::dashcore::signer::sign(signable_bytes, secret_key.as_secret_bytes()) + .expect("signing should succeed"); let witness = AddressWitness::P2pkh { signature: BinaryData::new(signature.to_vec()), @@ -9411,15 +9410,14 @@ mod tests { let public_keys = create_default_public_keys(&mut rng, platform_version); // Create a real secret key - let secret_key = dpp::dashcore::secp256k1::SecretKey::from_slice(&[ + let secret_key = dpp::dashcore::secp256k1::SecretKey::from_secret_bytes([ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, ]) .expect("valid secret key"); - let secp = Secp256k1::new(); - let raw_pubkey = RawSecp256k1PublicKey::from_secret_key(&secp, &secret_key); + let raw_pubkey = RawSecp256k1PublicKey::from_secret_key(&secret_key); let pubkey = PublicKey::new(raw_pubkey); let pubkey_hash = dpp::dashcore::hashes::hash160::Hash::hash(&pubkey.to_bytes()); let address = PlatformAddress::P2pkh(pubkey_hash.to_byte_array()); @@ -9447,8 +9445,9 @@ mod tests { .expect("should get signable bytes"); // Now create the signature using recoverable signing - let signature = dpp::dashcore::signer::sign(&signable_bytes, secret_key.as_ref()) - .expect("signing should succeed"); + let signature = + dpp::dashcore::signer::sign(&signable_bytes, secret_key.as_secret_bytes()) + .expect("signing should succeed"); // Create the signed transition let signed_transition = IdentityCreateFromAddressesTransition::V0( @@ -9487,17 +9486,16 @@ mod tests { let public_keys = create_default_public_keys(&mut rng, platform_version); // Create address from one key - let correct_secret = dpp::dashcore::secp256k1::SecretKey::from_slice(&[1u8; 32]) + let correct_secret = dpp::dashcore::secp256k1::SecretKey::from_secret_bytes([1u8; 32]) .expect("valid secret key"); - let secp = Secp256k1::new(); - let raw_correct_pubkey = RawSecp256k1PublicKey::from_secret_key(&secp, &correct_secret); + let raw_correct_pubkey = RawSecp256k1PublicKey::from_secret_key(&correct_secret); let correct_pubkey = PublicKey::new(raw_correct_pubkey); let pubkey_hash = dpp::dashcore::hashes::hash160::Hash::hash(&correct_pubkey.to_bytes()); let address = PlatformAddress::P2pkh(pubkey_hash.to_byte_array()); // But sign with different key - let wrong_secret = dpp::dashcore::secp256k1::SecretKey::from_slice(&[2u8; 32]) + let wrong_secret = dpp::dashcore::secp256k1::SecretKey::from_secret_bytes([2u8; 32]) .expect("valid secret key"); let mut inputs = BTreeMap::new(); @@ -9525,7 +9523,7 @@ mod tests { // Sign with WRONG key - this will produce a signature that when recovered // will give a different public key than expected let wrong_signature = - dpp::dashcore::signer::sign(&signable_bytes, wrong_secret.as_ref()) + dpp::dashcore::signer::sign(&signable_bytes, wrong_secret.as_secret_bytes()) .expect("signing should succeed"); // Create transition with mismatched signature (signed by wrong key) @@ -9553,21 +9551,21 @@ mod tests { let mut rng = StdRng::seed_from_u64(6002); let public_keys = create_default_public_keys(&mut rng, platform_version); - let secp = Secp256k1::new(); // Create multiple addresses with their secret keys let secrets: Vec<_> = (1..=3) .map(|i| { let mut key_bytes = [0u8; 32]; key_bytes[0] = i; - dpp::dashcore::secp256k1::SecretKey::from_slice(&key_bytes).expect("valid") + dpp::dashcore::secp256k1::SecretKey::from_secret_bytes(key_bytes) + .expect("valid") }) .collect(); let addresses: Vec<_> = secrets .iter() .map(|secret| { - let raw_pubkey = RawSecp256k1PublicKey::from_secret_key(&secp, secret); + let raw_pubkey = RawSecp256k1PublicKey::from_secret_key(secret); let pubkey = PublicKey::new(raw_pubkey); let pubkey_hash = dpp::dashcore::hashes::hash160::Hash::hash(&pubkey.to_bytes()); @@ -9611,8 +9609,9 @@ mod tests { .position(|a| a == addr) .expect("should find"); let secret = &secrets[idx]; - let signature = dpp::dashcore::signer::sign(&signable_bytes, secret.as_ref()) - .expect("signing should succeed"); + let signature = + dpp::dashcore::signer::sign(&signable_bytes, secret.as_secret_bytes()) + .expect("signing should succeed"); witnesses.push(AddressWitness::P2pkh { signature: BinaryData::new(signature.to_vec()), @@ -9641,21 +9640,21 @@ mod tests { let mut rng = StdRng::seed_from_u64(6003); let public_keys = create_default_public_keys(&mut rng, platform_version); - let secp = Secp256k1::new(); // Create 3 keys for 2-of-3 multisig let secrets: Vec<_> = (1..=3) .map(|i| { let mut key_bytes = [0u8; 32]; key_bytes[0] = i + 10; - dpp::dashcore::secp256k1::SecretKey::from_slice(&key_bytes).expect("valid") + dpp::dashcore::secp256k1::SecretKey::from_secret_bytes(key_bytes) + .expect("valid") }) .collect(); let pubkeys: Vec<[u8; 33]> = secrets .iter() .map(|secret| { - let raw_pubkey = RawSecp256k1PublicKey::from_secret_key(&secp, secret); + let raw_pubkey = RawSecp256k1PublicKey::from_secret_key(secret); raw_pubkey.serialize() }) .collect(); @@ -9700,9 +9699,9 @@ mod tests { .expect("should get signable bytes"); // Sign with first 2 keys (2-of-3) using DER signatures for P2SH - let sig1 = dpp::dashcore::signer::sign(&signable_bytes, secrets[0].as_ref()) + let sig1 = dpp::dashcore::signer::sign(&signable_bytes, secrets[0].as_secret_bytes()) .expect("signing should succeed"); - let sig2 = dpp::dashcore::signer::sign(&signable_bytes, secrets[1].as_ref()) + let sig2 = dpp::dashcore::signer::sign(&signable_bytes, secrets[1].as_secret_bytes()) .expect("signing should succeed"); let _signed_transition = IdentityCreateFromAddressesTransition::V0( diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_update/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_update/mod.rs index 96fddc39cd7..25d5ca0b898 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_update/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_update/mod.rs @@ -139,7 +139,9 @@ mod tests { use dpp::consensus::codes::ErrorWithCode; use dpp::consensus::ConsensusError; use dpp::dash_to_credits; - use dpp::dashcore::key::{Keypair, Secp256k1}; + use dpp::dashcore::key::Keypair; + use dpp::dashcore::secp256k1::rand::rngs::StdRng as SecpStdRng; + use dpp::dashcore::secp256k1::rand::SeedableRng as _; use dpp::dashcore::signer; use dpp::data_contract::accessors::v0::DataContractV0Getters; use dpp::identifier::Identifier; @@ -257,11 +259,9 @@ mod tests { let platform_state = platform.state.load(); - let secp = Secp256k1::new(); + let mut rng = SecpStdRng::seed_from_u64(292); - let mut rng = StdRng::seed_from_u64(292); - - let new_key_pair = Keypair::new(&secp, &mut rng); + let new_key_pair = Keypair::new(&mut rng); let mut new_key = IdentityPublicKeyInCreationV0 { id: 2, @@ -294,7 +294,7 @@ mod tests { let secret = new_key_pair.secret_key(); let signature = - signer::sign(&signable_bytes, &secret.secret_bytes()).expect("expected to sign"); + signer::sign(&signable_bytes, &secret.to_secret_bytes()).expect("expected to sign"); new_key.signature = signature.to_vec().into(); @@ -540,9 +540,8 @@ mod tests { document_type_name: "profile".into(), }; let platform_state = platform.state.load(); - let secp = Secp256k1::new(); - let mut rng = StdRng::seed_from_u64(292); - let new_key_pair = Keypair::new(&secp, &mut rng); + let mut rng = SecpStdRng::seed_from_u64(292); + let new_key_pair = Keypair::new(&mut rng); let mut new_key = IdentityPublicKeyInCreationV0 { id: 2, purpose: Purpose::AUTHENTICATION, @@ -567,11 +566,13 @@ mod tests { .into() }; let signable_bytes = build(new_key.clone()).signable_bytes().unwrap(); - new_key.signature = - signer::sign(&signable_bytes, &new_key_pair.secret_key().secret_bytes()) - .unwrap() - .to_vec() - .into(); + new_key.signature = signer::sign( + &signable_bytes, + &new_key_pair.secret_key().to_secret_bytes(), + ) + .unwrap() + .to_vec() + .into(); let mut update_transition = build(new_key); update_transition .set_signature(signer.sign(&key, signable_bytes.as_slice()).await.unwrap()); @@ -749,9 +750,8 @@ mod tests { id: contract_group_id, }; let platform_state = platform.state.load(); - let secp = Secp256k1::new(); - let mut rng = StdRng::seed_from_u64(293); - let new_key_pair = Keypair::new(&secp, &mut rng); + let mut rng = SecpStdRng::seed_from_u64(293); + let new_key_pair = Keypair::new(&mut rng); let build = |revision: u64, nonce: u64, add: Vec, disable| { StateTransition::from(IdentityUpdateTransition::from(IdentityUpdateTransitionV0 { @@ -783,11 +783,13 @@ mod tests { let signable_bytes = build(revision, nonce, vec![new_key.clone()], vec![]) .signable_bytes() .unwrap(); - new_key.signature = - signer::sign(&signable_bytes, &new_key_pair.secret_key().secret_bytes()) - .unwrap() - .to_vec() - .into(); + new_key.signature = signer::sign( + &signable_bytes, + &new_key_pair.secret_key().to_secret_bytes(), + ) + .unwrap() + .to_vec() + .into(); let mut update = build(revision, nonce, vec![new_key], vec![]); update.set_signature(signer.sign(&key, signable_bytes.as_slice()).await.unwrap()); signed_updates.push(update); @@ -1107,11 +1109,10 @@ mod tests { .load_dashpay(version) .unwrap(); let bounds = ContractBounds::SingleContract { id: dashpay.id() }; - let secp = Secp256k1::new(); - let mut rng = StdRng::seed_from_u64(292); + let mut rng = SecpStdRng::seed_from_u64(292); let pairs: BTreeMap = [2u32, 3, 4] .into_iter() - .map(|id| (id, Keypair::new(&secp, &mut rng))) + .map(|id| (id, Keypair::new(&mut rng))) .collect(); let bound_key = |id: u32| IdentityPublicKeyInCreationV0 { id, @@ -1226,7 +1227,7 @@ mod tests { let mut adds = vec![bound_key(3), bound_key(2)]; let signable = unsigned(1, adds.clone(), vec![]).signable_bytes().unwrap(); for key in &mut adds { - key.signature = signer::sign(&signable, &pairs[&key.id].secret_key().secret_bytes()) + key.signature = signer::sign(&signable, &pairs[&key.id].secret_key().to_secret_bytes()) .unwrap() .to_vec() .into(); @@ -1241,7 +1242,7 @@ mod tests { let mut adds = vec![bound_key(4)]; let signable = unsigned(2, adds.clone(), vec![3]).signable_bytes().unwrap(); for key in &mut adds { - key.signature = signer::sign(&signable, &pairs[&key.id].secret_key().secret_bytes()) + key.signature = signer::sign(&signable, &pairs[&key.id].secret_key().to_secret_bytes()) .unwrap() .to_vec() .into(); @@ -1673,11 +1674,9 @@ mod tests { let platform_state = platform.state.load(); - let secp = Secp256k1::new(); + let mut rng = SecpStdRng::seed_from_u64(1292); - let mut rng = StdRng::seed_from_u64(1292); - - let new_key_pair = Keypair::new(&secp, &mut rng); + let new_key_pair = Keypair::new(&mut rng); let new_key = IdentityPublicKeyInCreationV0 { id: 2, @@ -1862,11 +1861,9 @@ mod tests { ) .await; - let secp = Secp256k1::new(); - - let mut rng = StdRng::seed_from_u64(1292); + let mut rng = SecpStdRng::seed_from_u64(1292); - let new_key_pair = Keypair::new(&secp, &mut rng); + let new_key_pair = Keypair::new(&mut rng); let mut new_key = IdentityPublicKeyInCreationV0 { id: 2, @@ -1902,7 +1899,7 @@ mod tests { // Sign the new key with its own private key let secret = new_key_pair.secret_key(); let signature = - signer::sign(&signable_bytes, &secret.secret_bytes()).expect("expected to sign"); + signer::sign(&signable_bytes, &secret.to_secret_bytes()).expect("expected to sign"); new_key.signature = signature.to_vec().into(); @@ -2147,14 +2144,13 @@ mod tests { let bounds = ContractBounds::SingleContract { id: data_contract.id(), }; - let secp = Secp256k1::new(); - let mut rng = StdRng::seed_from_u64(1292); + let mut rng = SecpStdRng::seed_from_u64(1292); // Two encryption keys, the newer one listed first: both write the encryption // current-key alias, and the one naming the highest key id must win. let pairs: Vec<(u32, Purpose, Keypair)> = vec![ - (4, Purpose::ENCRYPTION, Keypair::new(&secp, &mut rng)), - (2, Purpose::ENCRYPTION, Keypair::new(&secp, &mut rng)), - (3, Purpose::DECRYPTION, Keypair::new(&secp, &mut rng)), + (4, Purpose::ENCRYPTION, Keypair::new(&mut rng)), + (2, Purpose::ENCRYPTION, Keypair::new(&mut rng)), + (3, Purpose::DECRYPTION, Keypair::new(&mut rng)), ]; let mut adds: Vec = pairs .iter() @@ -2270,7 +2266,7 @@ mod tests { .signable_bytes() .expect("expected signable bytes"); for (key, (_, _, pair)) in adds.iter_mut().zip(&pairs) { - key.signature = signer::sign(&signable, &pair.secret_key().secret_bytes()) + key.signature = signer::sign(&signable, &pair.secret_key().to_secret_bytes()) .expect("expected to sign") .to_vec() .into(); @@ -2637,11 +2633,9 @@ mod tests { ) .await; - let secp = Secp256k1::new(); + let mut rng = SecpStdRng::seed_from_u64(1292); - let mut rng = StdRng::seed_from_u64(1292); - - let new_key_pair = Keypair::new(&secp, &mut rng); + let new_key_pair = Keypair::new(&mut rng); let mut new_key = IdentityPublicKeyInCreationV0 { id: 2, @@ -2678,7 +2672,7 @@ mod tests { // Sign the new key with its own private key let secret = new_key_pair.secret_key(); let signature = - signer::sign(&signable_bytes, &secret.secret_bytes()).expect("expected to sign"); + signer::sign(&signable_bytes, &secret.to_secret_bytes()).expect("expected to sign"); new_key.signature = signature.to_vec().into(); @@ -3011,9 +3005,8 @@ mod tests { let platform_state = platform.state.load(); - let secp = Secp256k1::new(); - let mut rng = StdRng::seed_from_u64(292); - let new_key_pair = Keypair::new(&secp, &mut rng); + let mut rng = SecpStdRng::seed_from_u64(292); + let new_key_pair = Keypair::new(&mut rng); // Add a key with id 2 and also disable key id 2 in the same transition let new_key = IdentityPublicKeyInCreationV0 { @@ -3241,9 +3234,8 @@ mod tests { let platform_state = platform.state.load(); - let secp = Secp256k1::new(); - let mut rng = StdRng::seed_from_u64(292); - let new_key_pair = Keypair::new(&secp, &mut rng); + let mut rng = SecpStdRng::seed_from_u64(292); + let new_key_pair = Keypair::new(&mut rng); let signable_transition: IdentityUpdateTransition = IdentityUpdateTransitionV0 { identity_id: identity.id(), @@ -3276,7 +3268,7 @@ mod tests { // Sign the new key let secret = new_key_pair.secret_key(); let key_sig = - signer::sign(&signable_bytes, &secret.secret_bytes()).expect("expected to sign"); + signer::sign(&signable_bytes, &secret.to_secret_bytes()).expect("expected to sign"); let mut new_key = IdentityPublicKeyInCreationV0 { id: 1, // existing key ID @@ -3377,9 +3369,8 @@ mod tests { setup_identity_return_master_key(&mut platform, 958, dash_to_credits!(0.1)); let platform_state = platform.state.load(); - let secp = Secp256k1::new(); - let mut rng = StdRng::seed_from_u64(292); - let new_key_pair = Keypair::new(&secp, &mut rng); + let mut rng = SecpStdRng::seed_from_u64(292); + let new_key_pair = Keypair::new(&mut rng); let mut new_key = IdentityPublicKeyInCreationV1 { id: NEW_KEY_ID, purpose: added_key.purpose, @@ -3413,11 +3404,13 @@ mod tests { let signable_bytes = transition_adding(new_key.clone()) .signable_bytes() .expect("expected signable bytes"); - new_key.signature = - signer::sign(&signable_bytes, &new_key_pair.secret_key().secret_bytes()) - .expect("expected to sign") - .to_vec() - .into(); + new_key.signature = signer::sign( + &signable_bytes, + &new_key_pair.secret_key().to_secret_bytes(), + ) + .expect("expected to sign") + .to_vec() + .into(); let mut update_transition = transition_adding(new_key); update_transition.set_signature( diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shield_from_asset_lock/tests.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shield_from_asset_lock/tests.rs index a4337de4fb7..e9839851020 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shield_from_asset_lock/tests.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shield_from_asset_lock/tests.rs @@ -609,7 +609,7 @@ mod tests { let transition = create_signed_shield_from_asset_lock_transition( asset_lock_proof, - &wrong_private_key.inner.secret_bytes(), // Wrong key + &wrong_private_key.inner.to_secret_bytes(), // Wrong key vec![create_dummy_serialized_action()], 5000, [42u8; 32], diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/test_helpers.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/test_helpers.rs index 1e19a410e66..9ff1ddf3825 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/test_helpers.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/test_helpers.rs @@ -12,7 +12,7 @@ use dpp::address_funds::{AddressWitness, PlatformAddress}; use dpp::block::block_info::BlockInfo; use dpp::dashcore::blockdata::script::ScriptBuf; use dpp::dashcore::hashes::{sha256, Hash}; -use dpp::dashcore::secp256k1::{PublicKey as RawPublicKey, Secp256k1, SecretKey as RawSecretKey}; +use dpp::dashcore::secp256k1::{PublicKey as RawPublicKey, SecretKey as RawSecretKey}; use dpp::dashcore::PublicKey; use dpp::identity::signer::Signer; use dpp::platform_value::BinaryData; @@ -37,8 +37,6 @@ pub use dpp::dashcore::blockdata::opcodes::all::{ pub use dpp::dashcore::blockdata::script::ScriptBuf as TestScriptBuf; pub use dpp::dashcore::hashes::Hash as TestHash; #[allow(unused_imports)] -pub use dpp::dashcore::secp256k1::Secp256k1 as TestSecp256k1; -#[allow(unused_imports)] pub use dpp::dashcore::PublicKey as TestPublicKey; pub use dpp::ProtocolError as TestProtocolError; @@ -73,19 +71,18 @@ impl TestAddressSigner { } pub fn create_keypair(seed: [u8; 32]) -> (RawSecretKey, PublicKey) { - let secp = Secp256k1::new(); // Hash the seed to ensure it's always a valid secret key // (non-zero, less than curve order). Raw seeds like [0u8; 32] are invalid. let hashed_seed = sha256::Hash::hash(&seed); - let secret_key = - RawSecretKey::from_byte_array(hashed_seed.as_byte_array()).expect("valid secret key"); - let raw_public_key = RawPublicKey::from_secret_key(&secp, &secret_key); + let secret_key = RawSecretKey::from_secret_bytes(*hashed_seed.as_byte_array()) + .expect("valid secret key"); + let raw_public_key = RawPublicKey::from_secret_key(&secret_key); let public_key = PublicKey::new(raw_public_key); (secret_key, public_key) } pub fn sign_data(data: &[u8], secret_key: &RawSecretKey) -> Vec { - dpp::dashcore::signer::sign(data, secret_key.as_ref()) + dpp::dashcore::signer::sign(data, secret_key.as_secret_bytes()) .expect("signing should succeed") .to_vec() } @@ -139,7 +136,7 @@ impl TestAddressSigner { PlatformAddress::P2pkh(hash) => self .p2pkh_keys .get(hash) - .map(|entry| entry.secret_key.secret_bytes()), + .map(|entry| entry.secret_key.to_secret_bytes()), _ => None, } } diff --git a/packages/rs-drive-abci/tests/strategy_tests/strategy.rs b/packages/rs-drive-abci/tests/strategy_tests/strategy.rs index 96820a70270..bf491224e93 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/strategy.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/strategy.rs @@ -2246,7 +2246,7 @@ impl NetworkStrategy { IdentityTopUpTransition::try_from_identity_with_private_key( identity, asset_lock_proof, - secret_key.as_ref(), + secret_key.as_secret_bytes(), 0, platform_version, None, @@ -2320,7 +2320,7 @@ impl NetworkStrategy { ( asset_lock_proof, - secret_key.secret_bytes().to_vec(), + secret_key.to_secret_bytes().to_vec(), funded_amount, ) } diff --git a/packages/rs-drive-proof-verifier/src/proof/token_direct_purchase.rs b/packages/rs-drive-proof-verifier/src/proof/token_direct_purchase.rs index 0a4d2256791..5584e0eef93 100644 --- a/packages/rs-drive-proof-verifier/src/proof/token_direct_purchase.rs +++ b/packages/rs-drive-proof-verifier/src/proof/token_direct_purchase.rs @@ -9,10 +9,7 @@ use dapi_grpc::platform::{ }, VersionedGrpcResponse, }; -use dpp::{ - dashcore::{secp256k1::hashes::hex::DisplayHex, Network}, - version::PlatformVersion, -}; +use dpp::{dashcore::Network, version::PlatformVersion}; use drive::drive::Drive; use crate::{ @@ -51,7 +48,7 @@ impl FromProof for TokenDirectPurchasePrice .map(<[u8; 32]>::try_from) .collect::, _>>() // BTreeSet to make it unique .map_err(|e| Error::RequestError { - error: format!("token id {} has invalid length", e.to_lower_hex_string()), + error: format!("token id {} has invalid length", hex::encode(e)), })? .into_iter() .collect::>(); diff --git a/packages/rs-platform-encryption/Cargo.toml b/packages/rs-platform-encryption/Cargo.toml index 7c6757d23a0..32c8f7a74ab 100644 --- a/packages/rs-platform-encryption/Cargo.toml +++ b/packages/rs-platform-encryption/Cargo.toml @@ -9,9 +9,9 @@ description = "Cryptographic utilities for Dash Platform (DIP-15 DashPay encrypt [dependencies] # Cryptography — direct deps only, so a consumer that needs just this crate # doesn't pull in/compile all of dashcore. `secp256k1` is pinned to the same -# 0.30 dashcore re-exports, so the public `SecretKey`/`PublicKey` types unify +# 0.33 dashcore re-exports, so the public `SecretKey`/`PublicKey` types unify # with dashcore-typed callers (platform-wallet, rs-sdk-ffi). -secp256k1 = { version = "0.30.0", features = ["std"] } +secp256k1 = { version = "0.33.1", features = ["std"] } aes = "0.8" cbc = "0.1" hmac = "0.12" @@ -21,4 +21,4 @@ thiserror = "1.0" [dev-dependencies] # Tests generate keypairs via secp256k1's RNG helpers (`generate_keypair`, # `secp256k1::rand`), gated behind the `rand` feature. -secp256k1 = { version = "0.30.0", features = ["std", "rand"] } +secp256k1 = { version = "0.33.1", features = ["std", "rand"] } diff --git a/packages/rs-platform-encryption/src/account_label.rs b/packages/rs-platform-encryption/src/account_label.rs index 9f281be8c60..4c2abc4a43c 100644 --- a/packages/rs-platform-encryption/src/account_label.rs +++ b/packages/rs-platform-encryption/src/account_label.rs @@ -113,21 +113,20 @@ pub fn decrypt_account_label( mod tests { use super::*; use crate::ecdh::derive_shared_key_ecdh; - use secp256k1::rand::{thread_rng, RngCore}; - use secp256k1::Secp256k1; + use secp256k1::generate_keypair; + use secp256k1::rand::{rng, RngCore}; #[test] fn test_account_label_encryption() { - let secp = Secp256k1::new(); - let (secret1, _public1) = secp.generate_keypair(&mut thread_rng()); - let (_secret2, public2) = secp.generate_keypair(&mut thread_rng()); + let (secret1, _public1) = generate_keypair(&mut rng()); + let (_secret2, public2) = generate_keypair(&mut rng()); // Derive shared key let shared_key = derive_shared_key_ecdh(&secret1, &public2); // Generate random IV let mut iv = [0u8; 16]; - thread_rng().fill_bytes(&mut iv); + rng().fill_bytes(&mut iv); let label = "My DashPay Account"; diff --git a/packages/rs-platform-encryption/src/aes.rs b/packages/rs-platform-encryption/src/aes.rs index c1609592fd7..5ea3ba3a60f 100644 --- a/packages/rs-platform-encryption/src/aes.rs +++ b/packages/rs-platform-encryption/src/aes.rs @@ -63,13 +63,13 @@ pub fn decrypt_aes_256_cbc( #[cfg(test)] mod tests { use super::*; - use secp256k1::rand::{thread_rng, RngCore}; + use secp256k1::rand::{rng, RngCore}; #[test] fn test_aes_encryption_decryption() { let key = [0u8; 32]; let mut iv = [0u8; 16]; - thread_rng().fill_bytes(&mut iv); + rng().fill_bytes(&mut iv); let plaintext = b"Hello, DashPay!"; diff --git a/packages/rs-platform-encryption/src/compact_xpub.rs b/packages/rs-platform-encryption/src/compact_xpub.rs index 01816f34006..b13dd1934c6 100644 --- a/packages/rs-platform-encryption/src/compact_xpub.rs +++ b/packages/rs-platform-encryption/src/compact_xpub.rs @@ -139,21 +139,20 @@ pub fn parse_compact_xpub(bytes: &[u8]) -> Result { mod tests { use super::*; use crate::ecdh::derive_shared_key_ecdh; - use secp256k1::rand::{thread_rng, RngCore}; - use secp256k1::Secp256k1; + use secp256k1::generate_keypair; + use secp256k1::rand::{rng, RngCore}; #[test] fn test_extended_public_key_encryption() { - let secp = Secp256k1::new(); - let (secret1, _public1) = secp.generate_keypair(&mut thread_rng()); - let (_secret2, public2) = secp.generate_keypair(&mut thread_rng()); + let (secret1, _public1) = generate_keypair(&mut rng()); + let (_secret2, public2) = generate_keypair(&mut rng()); // Derive shared key let shared_key = derive_shared_key_ecdh(&secret1, &public2); // Generate random IV let mut iv = [0u8; 16]; - thread_rng().fill_bytes(&mut iv); + rng().fill_bytes(&mut iv); // DIP-15 compact xpub plaintext (69 bytes). 69 → PKCS7 → 80, + 16-byte // IV = exactly 96 bytes, matching the contract's minItems/maxItems: 96. diff --git a/packages/rs-platform-encryption/src/ecdh.rs b/packages/rs-platform-encryption/src/ecdh.rs index 7e165365eec..54806ea3c4a 100644 --- a/packages/rs-platform-encryption/src/ecdh.rs +++ b/packages/rs-platform-encryption/src/ecdh.rs @@ -21,23 +21,21 @@ pub fn derive_shared_key_ecdh(private_key: &SecretKey, public_key: &PublicKey) - let shared_secret = SharedSecret::new(public_key, private_key); let mut key = [0u8; 32]; - key.copy_from_slice(shared_secret.as_ref()); + key.copy_from_slice(shared_secret.as_secret_bytes()); key } #[cfg(test)] mod tests { use super::*; - use secp256k1::rand::thread_rng; - use secp256k1::Secp256k1; + use secp256k1::generate_keypair; + use secp256k1::rand::rng; #[test] fn test_ecdh_key_derivation() { - let secp = Secp256k1::new(); - // Generate two key pairs - let (secret1, public1) = secp.generate_keypair(&mut thread_rng()); - let (secret2, public2) = secp.generate_keypair(&mut thread_rng()); + let (secret1, public1) = generate_keypair(&mut rng()); + let (secret2, public2) = generate_keypair(&mut rng()); // Derive shared keys from both sides let shared1 = derive_shared_key_ecdh(&secret1, &public2); @@ -55,14 +53,13 @@ mod tests { /// (b) the exact compressed-y-prefix-‖-x preimage convention. #[test] fn ecdh_matches_sha256_y_parity_prefix_convention() { - use secp256k1::{Scalar, Secp256k1}; + use secp256k1::Scalar; use sha2::{Digest, Sha256}; - let secp = Secp256k1::new(); - let priv_a = SecretKey::from_slice(&[0xC0u8; 32]).expect("valid scalar"); - let priv_b = SecretKey::from_slice(&[0x0Du8; 32]).expect("valid scalar"); - let pub_a = PublicKey::from_secret_key(&secp, &priv_a); - let pub_b = PublicKey::from_secret_key(&secp, &priv_b); + let priv_a = SecretKey::from_secret_bytes([0xC0u8; 32]).expect("valid scalar"); + let priv_b = SecretKey::from_secret_bytes([0x0Du8; 32]).expect("valid scalar"); + let pub_a = PublicKey::from_secret_key(&priv_a); + let pub_b = PublicKey::from_secret_key(&priv_b); let ab = derive_shared_key_ecdh(&priv_a, &pub_b); let ba = derive_shared_key_ecdh(&priv_b, &pub_a); @@ -73,7 +70,7 @@ mod tests { // SHA256( (0x02 | (P.y & 1)) ‖ P.x ). Pins that it's the compressed-y // prefix + x, NOT x‖y or some other layout. let scalar_a = Scalar::from_be_bytes([0xC0u8; 32]).expect("scalar in range"); - let shared_point = pub_b.mul_tweak(&secp, &scalar_a).expect("point mul"); + let shared_point = pub_b.mul_tweak(&scalar_a).expect("point mul"); let uncompressed = shared_point.serialize_uncompressed(); // 0x04 ‖ x(32) ‖ y(32) let prefix = 0x02u8 | (uncompressed[64] & 1); // y parity from the last y byte let mut preimage = Vec::with_capacity(33); diff --git a/packages/rs-platform-wallet-ffi/src/dashpay.rs b/packages/rs-platform-wallet-ffi/src/dashpay.rs index b3e7418b090..49aea2b6300 100644 --- a/packages/rs-platform-wallet-ffi/src/dashpay.rs +++ b/packages/rs-platform-wallet-ffi/src/dashpay.rs @@ -769,7 +769,7 @@ impl platform_wallet::ContactCryptoProvider for ResolverContactCryptoProvider { .map_err(|e| { platform_wallet::PlatformWalletError::InvalidIdentityData(e.to_string()) })?; - dashcore::secp256k1::SecretKey::from_slice(scalar.as_ref()) + dashcore::secp256k1::SecretKey::from_secret_bytes(*scalar) .map_err(|e| platform_wallet::PlatformWalletError::InvalidIdentityData(e.to_string())) } @@ -783,7 +783,7 @@ impl platform_wallet::ContactCryptoProvider for ResolverContactCryptoProvider { .map_err(|e| { platform_wallet::PlatformWalletError::InvalidIdentityData(e.to_string()) })?; - dashcore::secp256k1::SecretKey::from_slice(scalar.as_ref()) + dashcore::secp256k1::SecretKey::from_secret_bytes(*scalar) .map_err(|e| platform_wallet::PlatformWalletError::InvalidIdentityData(e.to_string())) } diff --git a/packages/rs-platform-wallet-ffi/src/derivation.rs b/packages/rs-platform-wallet-ffi/src/derivation.rs index 81b1020276d..5d30ac8ef6a 100644 --- a/packages/rs-platform-wallet-ffi/src/derivation.rs +++ b/packages/rs-platform-wallet-ffi/src/derivation.rs @@ -4,7 +4,6 @@ use std::ffi::CStr; use std::os::raw::c_char; use std::str::FromStr; -use dashcore::secp256k1::Secp256k1; use key_wallet::bip32::{DerivationPath, ExtendedPrivKey}; use key_wallet::mnemonic::Mnemonic; use zeroize::Zeroizing; @@ -90,16 +89,15 @@ pub unsafe extern "C" fn platform_wallet_derive_ext_priv_key_from_mnemonic( let master = unwrap_result_or_return!(ExtendedPrivKey::new_master(network, &*seed)); - let secp = Secp256k1::new(); - let derived = unwrap_result_or_return!(master.derive_priv(&secp, &path)); + let derived = unwrap_result_or_return!(master.derive_priv(&path)); - let secret = Zeroizing::new(derived.private_key.secret_bytes()); + let secret = Zeroizing::new(derived.private_key.to_secret_bytes()); std::ptr::copy_nonoverlapping(secret.as_ptr(), out_secret_key, 32); std::ptr::copy_nonoverlapping(derived.chain_code.as_ref().as_ptr(), out_chain_code, 32); if !out_public_key.is_null() { - let pubkey_bytes = derived.private_key.public_key(&secp).serialize(); + let pubkey_bytes = derived.private_key.public_key().serialize(); std::ptr::copy_nonoverlapping(pubkey_bytes.as_ptr(), out_public_key, 33); } diff --git a/packages/rs-platform-wallet-ffi/src/derive_identity_key_at_slot.rs b/packages/rs-platform-wallet-ffi/src/derive_identity_key_at_slot.rs index ed2463e3cdc..d2d9f0da1ec 100644 --- a/packages/rs-platform-wallet-ffi/src/derive_identity_key_at_slot.rs +++ b/packages/rs-platform-wallet-ffi/src/derive_identity_key_at_slot.rs @@ -81,8 +81,7 @@ unsafe fn derive_at_slot_inner( let pub_len = pub_box.len(); std::mem::forget(pub_box); - let secret_key = match dashcore::secp256k1::SecretKey::from_slice(derived.private_key.as_ref()) - { + let secret_key = match dashcore::secp256k1::SecretKey::from_secret_bytes(*derived.private_key) { Ok(k) => k, Err(e) => { drop(Box::from_raw(std::ptr::slice_from_raw_parts_mut( diff --git a/packages/rs-platform-wallet-ffi/src/identity_derive_and_persist.rs b/packages/rs-platform-wallet-ffi/src/identity_derive_and_persist.rs index 9ce63c81f56..7beb37acde8 100644 --- a/packages/rs-platform-wallet-ffi/src/identity_derive_and_persist.rs +++ b/packages/rs-platform-wallet-ffi/src/identity_derive_and_persist.rs @@ -93,7 +93,6 @@ use std::ptr; use crate::types::{FFINetwork, Network}; use dashcore::hashes::Hash; -use dashcore::secp256k1::Secp256k1; use key_wallet::bip32::{ExtendedPrivKey, ExtendedPubKey}; use zeroize::{Zeroize, Zeroizing}; @@ -260,7 +259,6 @@ pub unsafe extern "C" fn dash_sdk_derive_and_persist_identity_keys( let kw_network: Network = network.into(); let master = unwrap_result_or_return!(ExtendedPrivKey::new_master(kw_network, seed.as_ref())); - let secp = Secp256k1::new(); // ---- Walk derivation paths, persist, build pubkey-only rows -------------- let persister = &*persister_handle; @@ -298,7 +296,7 @@ pub unsafe extern "C" fn dash_sdk_derive_and_persist_identity_keys( } }; - let derived = match master.derive_priv(&secp, &path) { + let derived = match master.derive_priv(&path) { Ok(d) => d, Err(e) => { cleanup(rows); @@ -313,13 +311,13 @@ pub unsafe extern "C" fn dash_sdk_derive_and_persist_identity_keys( }; // Materialize pubkey + hash160 once. - let extended_pub = ExtendedPubKey::from_priv(&secp, &derived); + let extended_pub = ExtendedPubKey::from_priv(&derived); let pub_bytes: [u8; 33] = extended_pub.public_key.serialize(); let pub_hash: [u8; 20] = dashcore::hashes::hash160::Hash::hash(&pub_bytes).to_byte_array(); // Hold the secret scalar in a buffer that drops with // `zeroize::Zeroize::zeroize` at scope end. - let mut priv_scalar: [u8; 32] = derived.private_key.secret_bytes(); + let mut priv_scalar: [u8; 32] = derived.private_key.to_secret_bytes(); let path_cstring = match CString::new(path.to_string()) { Ok(s) => s, diff --git a/packages/rs-platform-wallet-ffi/src/identity_key_preview.rs b/packages/rs-platform-wallet-ffi/src/identity_key_preview.rs index cecc569c051..837722a5dbd 100644 --- a/packages/rs-platform-wallet-ffi/src/identity_key_preview.rs +++ b/packages/rs-platform-wallet-ffi/src/identity_key_preview.rs @@ -366,8 +366,8 @@ unsafe fn preview_identity_registration_keys_inner( // WIF: network-aware (mainnet → 0xCC, testnet/devnet/ // regtest → 0xEF) and compressed. Same construction // `key_wallet::derive_private_key_as_wif` performs. - let secret_key = dashcore::secp256k1::SecretKey::from_slice( - material.private_key.as_ref(), + let secret_key = dashcore::secp256k1::SecretKey::from_secret_bytes( + *material.private_key, ) .map_err(|e| { PlatformWalletFFIResult::err( @@ -455,7 +455,7 @@ unsafe fn preview_identity_registration_keys_inner( identity_index, key_index, )?; - let private_key = Zeroizing::new(ext_priv.private_key.secret_bytes()); + let private_key = Zeroizing::new(ext_priv.private_key.to_secret_bytes()); // Belt-and-braces: the pinned key-wallet rev zeroizes // `ExtendedPrivKey` on Drop; erase explicitly anyway // (cheap, and robust to an upstream Drop regression). diff --git a/packages/rs-platform-wallet-ffi/src/identity_keys_from_mnemonic.rs b/packages/rs-platform-wallet-ffi/src/identity_keys_from_mnemonic.rs index 2afc08840fc..0ac906848c1 100644 --- a/packages/rs-platform-wallet-ffi/src/identity_keys_from_mnemonic.rs +++ b/packages/rs-platform-wallet-ffi/src/identity_keys_from_mnemonic.rs @@ -2,7 +2,6 @@ use std::ffi::CString; -use dashcore::secp256k1::Secp256k1; use dashcore::PrivateKey as DashPrivateKey; use key_wallet::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey}; use key_wallet::dip9::{ @@ -319,7 +318,6 @@ pub unsafe extern "C" fn dash_sdk_derive_identity_keys_from_mnemonic( let kw_network: Network = network.into(); let master = unwrap_result_or_return!(ExtendedPrivKey::new_master(kw_network, seed.as_ref())); - let secp = Secp256k1::new(); let mut rows: Vec = Vec::with_capacity(key_count as usize); @@ -347,7 +345,7 @@ pub unsafe extern "C" fn dash_sdk_derive_identity_keys_from_mnemonic( } }; - let derived = match master.derive_priv(&secp, &path) { + let derived = match master.derive_priv(&path) { Ok(d) => d, Err(e) => { cleanup(rows); @@ -360,7 +358,7 @@ pub unsafe extern "C" fn dash_sdk_derive_identity_keys_from_mnemonic( ); } }; - let extended_pub = ExtendedPubKey::from_priv(&secp, &derived); + let extended_pub = ExtendedPubKey::from_priv(&derived); let public_key = extended_pub.public_key; let path_cstring = match CString::new(path.to_string()) { @@ -404,7 +402,7 @@ pub unsafe extern "C" fn dash_sdk_derive_identity_keys_from_mnemonic( public_key: pub_ptr, public_key_len: pub_len, private_key_wif: wif_cstring.into_raw(), - private_key_bytes: derived.private_key.secret_bytes(), + private_key_bytes: derived.private_key.to_secret_bytes(), }); } diff --git a/packages/rs-platform-wallet-ffi/src/identity_registration_with_signer.rs b/packages/rs-platform-wallet-ffi/src/identity_registration_with_signer.rs index 7dbda21ff29..e9cdf4bce92 100644 --- a/packages/rs-platform-wallet-ffi/src/identity_registration_with_signer.rs +++ b/packages/rs-platform-wallet-ffi/src/identity_registration_with_signer.rs @@ -704,7 +704,7 @@ pub unsafe extern "C" fn platform_wallet_derive_identity_keys_for_index( public_key: pub_ptr, public_key_len: pub_len, private_key_wif: wif_cstring.into_raw(), - private_key_bytes: ext_priv.private_key.secret_bytes(), + private_key_bytes: ext_priv.private_key.to_secret_bytes(), }); } Ok(rows) diff --git a/packages/rs-platform-wallet-ffi/src/invitation.rs b/packages/rs-platform-wallet-ffi/src/invitation.rs index 089e95bc6c5..8e6600c7ed4 100644 --- a/packages/rs-platform-wallet-ffi/src/invitation.rs +++ b/packages/rs-platform-wallet-ffi/src/invitation.rs @@ -747,7 +747,7 @@ mod tests { /// unknown wallet is looked up, and no signer/network work is attempted. #[test] fn claim_invitation_rejects_duplicate_key_ids_before_wallet_lookup() { - let voucher = SecretKey::from_slice(&[0x11u8; 32]).expect("valid scalar"); + let voucher = SecretKey::from_secret_bytes([0x11u8; 32]).expect("valid scalar"); let wif = PrivateKey::new(voucher, Network::Testnet).to_wif(); let uri = std::ffi::CString::new(format!("dashpay://invite?assetlocktx=aa&pk={wif}")).unwrap(); diff --git a/packages/rs-platform-wallet-ffi/src/persistence.rs b/packages/rs-platform-wallet-ffi/src/persistence.rs index d95a1f31db8..9334cde2789 100644 --- a/packages/rs-platform-wallet-ffi/src/persistence.rs +++ b/packages/rs-platform-wallet-ffi/src/persistence.rs @@ -8968,7 +8968,6 @@ mod tests { use dashcore::blockdata::transaction::txout::TxOut; use dashcore::blockdata::transaction::Transaction; use dashcore::consensus::encode::serialize; - use dashcore::secp256k1::Secp256k1; use dashcore::{Network, ScriptBuf}; use key_wallet::account::{Account, AccountType, StandardAccountType}; use key_wallet::bip32::{ExtendedPrivKey, ExtendedPubKey}; @@ -9127,8 +9126,7 @@ mod tests { let seed = mnemonic.to_seed(""); let master = ExtendedPrivKey::new_master(Network::Testnet, &seed) .expect("master derivation must succeed"); - let secp = Secp256k1::new(); - let xpub = ExtendedPubKey::from_priv(&secp, &master); + let xpub = ExtendedPubKey::from_priv(&master); let account = Account::from_xpub( None, AccountType::Standard { @@ -9160,8 +9158,7 @@ mod tests { let seed = mnemonic.to_seed(""); let master = ExtendedPrivKey::new_master(Network::Testnet, &seed) .expect("master derivation must succeed"); - let secp = Secp256k1::new(); - let xpub = ExtendedPubKey::from_priv(&secp, &master); + let xpub = ExtendedPubKey::from_priv(&master); let account = Account::from_xpub(None, account_type, xpub, Network::Testnet) .expect("Account::from_xpub on a valid xpub must succeed"); let mut accounts = key_wallet::AccountCollection::new(); @@ -9275,8 +9272,7 @@ mod tests { let seed = mnemonic.to_seed(""); let master = ExtendedPrivKey::new_master(Network::Testnet, &seed) .expect("master derivation must succeed"); - let secp = Secp256k1::new(); - let xpub = ExtendedPubKey::from_priv(&secp, &master); + let xpub = ExtendedPubKey::from_priv(&master); let account = Account::from_xpub(None, AccountType::ProviderOwnerKeys, xpub, Network::Testnet) .expect("Account::from_xpub on a valid xpub must succeed"); diff --git a/packages/rs-platform-wallet-ffi/src/secp256k1_primitives.rs b/packages/rs-platform-wallet-ffi/src/secp256k1_primitives.rs index d97d5e5f149..5615d7b2259 100644 --- a/packages/rs-platform-wallet-ffi/src/secp256k1_primitives.rs +++ b/packages/rs-platform-wallet-ffi/src/secp256k1_primitives.rs @@ -3,7 +3,7 @@ use std::slice; use dashcore::secp256k1::ecdh::shared_secret_point; -use dashcore::secp256k1::{PublicKey, Secp256k1, SecretKey}; +use dashcore::secp256k1::{PublicKey, SecretKey}; use zeroize::Zeroizing; use crate::error::*; @@ -34,14 +34,14 @@ fn parse_secret_key( ))); } - // `SecretKey::from_slice` converts the slice into an unguarded local - // `[u8; 32]` on its way to the returned key, so parsing straight from the - // caller's slice would leave a second, unscrubbed copy of the scalar on - // the stack. Staging it here keeps every copy this side owns wiped. + // Converting the caller's slice straight into the `[u8; 32]` + // `SecretKey::from_secret_bytes` takes would leave an unguarded copy of + // the scalar on the stack. Staging it here keeps every copy this side + // owns wiped. let mut staged = Zeroizing::new([0u8; 32]); staged.copy_from_slice(unsafe { slice::from_raw_parts(private_key, private_key_len) }); - SecretKey::from_byte_array(&staged) + SecretKey::from_secret_bytes(*staged) .map(WipingSecretKey) .map_err(|error| invalid_parameter(format!("Invalid secp256k1 private key: {error}"))) } @@ -94,7 +94,7 @@ pub unsafe extern "C" fn platform_wallet_secp256k1_compressed_public_key( check_ptr!(out_pubkey); let secret_key = unwrap_result_or_return!(parse_secret_key(seckey, seckey_len)); - let compressed = PublicKey::from_secret_key(&Secp256k1::new(), &secret_key.0).serialize(); + let compressed = PublicKey::from_secret_key(&secret_key.0).serialize(); std::ptr::copy_nonoverlapping(compressed.as_ptr(), out_pubkey, compressed.len()); PlatformWalletFFIResult::ok() } diff --git a/packages/rs-platform-wallet-ffi/src/sign_with_mnemonic_resolver.rs b/packages/rs-platform-wallet-ffi/src/sign_with_mnemonic_resolver.rs index 0225e653a77..c2ea005a996 100644 --- a/packages/rs-platform-wallet-ffi/src/sign_with_mnemonic_resolver.rs +++ b/packages/rs-platform-wallet-ffi/src/sign_with_mnemonic_resolver.rs @@ -41,7 +41,6 @@ use std::os::raw::c_char; use std::str::FromStr; use crate::types::{FFINetwork, Network}; -use dashcore::secp256k1::Secp256k1; use key_wallet::bip32::{DerivationPath, ExtendedPrivKey}; use zeroize::Zeroizing; @@ -274,12 +273,11 @@ pub unsafe extern "C" fn dash_sdk_sign_with_mnemonic_resolver_and_path( Ok(m) => m, Err(_) => return fail(SIGN_WITH_RESOLVER_ERR_DERIVATION), }; - let secp = Secp256k1::new(); - let derived = match master.derive_priv(&secp, &path) { + let derived = match master.derive_priv(&path) { Ok(d) => d, Err(_) => return fail(SIGN_WITH_RESOLVER_ERR_DERIVATION), }; - let secret_bytes: Zeroizing<[u8; 32]> = Zeroizing::new(derived.private_key.secret_bytes()); + let secret_bytes: Zeroizing<[u8; 32]> = Zeroizing::new(derived.private_key.to_secret_bytes()); // ---- Bind the derived key to the expected on-chain key ------------------- // Reject before signing if the key derived here doesn't reproduce the @@ -290,7 +288,7 @@ pub unsafe extern "C" fn dash_sdk_sign_with_mnemonic_resolver_and_path( // `validate_private_key_bytes` decision (33-byte expected = compressed // pubkey equality; 20-byte expected = `ripemd160_sha256` of it). if !expected_key_data.is_null() && expected_key_data_len > 0 { - let derived_pubkey = key_wallet::bip32::ExtendedPubKey::from_priv(&secp, &derived) + let derived_pubkey = key_wallet::bip32::ExtendedPubKey::from_priv(&derived) .public_key .serialize(); // Build the expected slice only for the two lengths the binding @@ -494,14 +492,11 @@ mod tests { // same mnemonic the resolver returns. let mnemonic = parse_mnemonic_any_language(ENGLISH_PHRASE).expect("mnemonic"); let seed = mnemonic.to_seed(""); - let secp = Secp256k1::new(); let master = ExtendedPrivKey::new_master(Network::Testnet, &seed).expect("master"); let derived = master - .derive_priv(&secp, &DerivationPath::from_str(path_str).unwrap()) + .derive_priv(&DerivationPath::from_str(path_str).unwrap()) .expect("derive"); - let expected_pubkey = ExtendedPubKey::from_priv(&secp, &derived) - .public_key - .serialize(); + let expected_pubkey = ExtendedPubKey::from_priv(&derived).public_key.serialize(); let mut sig_buf = [0u8; 128]; let mut sig_len: usize = 0; @@ -590,14 +585,11 @@ mod tests { let mnemonic = parse_mnemonic_any_language(ENGLISH_PHRASE).expect("mnemonic"); let seed = mnemonic.to_seed(""); - let secp = Secp256k1::new(); let master = ExtendedPrivKey::new_master(Network::Testnet, &seed).expect("master"); let derived = master - .derive_priv(&secp, &DerivationPath::from_str(path_str).unwrap()) + .derive_priv(&DerivationPath::from_str(path_str).unwrap()) .expect("derive"); - let pubkey = ExtendedPubKey::from_priv(&secp, &derived) - .public_key - .serialize(); + let pubkey = ExtendedPubKey::from_priv(&derived).public_key.serialize(); let expected_hash = dash_sdk::dpp::util::hash::ripemd160_sha256(&pubkey); let mut sig_buf = [0u8; 128]; diff --git a/packages/rs-platform-wallet-ffi/src/utils.rs b/packages/rs-platform-wallet-ffi/src/utils.rs index bf84c88170e..770ae09777c 100644 --- a/packages/rs-platform-wallet-ffi/src/utils.rs +++ b/packages/rs-platform-wallet-ffi/src/utils.rs @@ -2,7 +2,7 @@ use crate::error::*; use crate::{check_ptr, unwrap_result_or_return}; use std::os::raw::{c_char, c_uchar}; -/// RAII guard that scrubs a `secp256k1::SecretKey`'s scalar on drop. `from_slice` +/// RAII guard that scrubs a `secp256k1::SecretKey`'s scalar on drop. `from_secret_bytes` /// allocates a 32-byte scalar copy of the caller's private key, and `SecretKey` /// has no `Drop` wipe of its own — so without this the copy would survive on the /// stack after the call returns. Mirrors `WipingSecretKey` in @@ -202,19 +202,21 @@ pub unsafe extern "C" fn platform_wallet_pubkey_hash_from_private_key( return -1; } use dashcore::hashes::Hash; - use dashcore::secp256k1::{PublicKey, Secp256k1, SecretKey}; + use dashcore::secp256k1::{PublicKey, SecretKey}; let sk_bytes = std::slice::from_raw_parts(private_key, 32); - let secp = Secp256k1::new(); - // `WipingSecretKey` scrubs the `from_slice`-allocated scalar copy on every + // `WipingSecretKey` scrubs the `from_secret_bytes`-allocated scalar copy on every // exit path (the success return below, the `Err` early return, and any // panic) — the caller's `private_key` bytes are theirs to manage, but this // copy must not linger. - let secret_key = match SecretKey::from_slice(sk_bytes) { + let secret_key = match <[u8; 32]>::try_from(sk_bytes) + .map_err(|_| dashcore::secp256k1::Error::InvalidSecretKey) + .and_then(SecretKey::from_secret_bytes) + { Ok(sk) => WipingSecretKey(sk), Err(_) => return -1, }; - let pubkey = PublicKey::from_secret_key(&secp, &secret_key.0).serialize(); + let pubkey = PublicKey::from_secret_key(&secret_key.0).serialize(); let hash = dashcore::hashes::hash160::Hash::hash(&pubkey); let h: [u8; 20] = hash.to_byte_array(); std::ptr::copy_nonoverlapping(h.as_ptr(), out_hash, 20); @@ -263,7 +265,7 @@ mod tests { #[test] fn test_pubkey_hash_from_private_key_matches_canonical_derivation() { use dashcore::hashes::Hash; - use dashcore::secp256k1::{PublicKey, Secp256k1, SecretKey}; + use dashcore::secp256k1::{PublicKey, SecretKey}; // A fixed, in-range scalar. let mut scalar = [0u8; 32]; @@ -275,9 +277,8 @@ mod tests { }; assert_eq!(rc, 0); - let secp = Secp256k1::new(); - let sk = SecretKey::from_slice(&scalar).expect("in-range scalar"); - let pubkey = PublicKey::from_secret_key(&secp, &sk).serialize(); + let sk = SecretKey::from_secret_bytes(scalar).expect("in-range scalar"); + let pubkey = PublicKey::from_secret_key(&sk).serialize(); let expected: [u8; 20] = dashcore::hashes::hash160::Hash::hash(&pubkey).to_byte_array(); assert_eq!(out, expected); } diff --git a/packages/rs-platform-wallet/examples/dpns_marketplace_testnet.rs b/packages/rs-platform-wallet/examples/dpns_marketplace_testnet.rs index 9db1d6a312a..ec012b74803 100644 --- a/packages/rs-platform-wallet/examples/dpns_marketplace_testnet.rs +++ b/packages/rs-platform-wallet/examples/dpns_marketplace_testnet.rs @@ -115,11 +115,10 @@ fn dapi_addresses() -> AddressList { /// Whether `sk_bytes` is the private key for `key` (33-byte pubkey for /// ECDSA_SECP256K1, hash160 for ECDSA_HASH160). fn private_key_matches(key: &dpp::identity::IdentityPublicKey, sk_bytes: &[u8; 32]) -> bool { - let secp = dashcore::secp256k1::Secp256k1::new(); - let Ok(sk) = dashcore::secp256k1::SecretKey::from_byte_array(sk_bytes) else { + let Ok(sk) = dashcore::secp256k1::SecretKey::from_secret_bytes(*sk_bytes) else { return false; }; - let pubkey = dashcore::secp256k1::PublicKey::from_secret_key(&secp, &sk).serialize(); + let pubkey = dashcore::secp256k1::PublicKey::from_secret_key(&sk).serialize(); match key.key_type() { KeyType::ECDSA_SECP256K1 => key.data().as_slice() == pubkey.as_slice(), KeyType::ECDSA_HASH160 => { @@ -174,7 +173,7 @@ fn parse_private_key(raw: &str) -> Option<[u8; 32]> { } dashcore::PrivateKey::from_wif(trimmed) .ok() - .map(|pk| pk.inner.secret_bytes()) + .map(|pk| pk.inner.to_secret_bytes()) } async fn discover( diff --git a/packages/rs-platform-wallet/src/masternode/locator.rs b/packages/rs-platform-wallet/src/masternode/locator.rs index faf0513f8f8..1a07ff91f7e 100644 --- a/packages/rs-platform-wallet/src/masternode/locator.rs +++ b/packages/rs-platform-wallet/src/masternode/locator.rs @@ -37,7 +37,7 @@ use dashcore::blsful::{ }; use dashcore::ed25519_dalek::SigningKey; use dashcore::hashes::{hash160, Hash}; -use dashcore::secp256k1::{PublicKey as SecpPublicKey, Secp256k1, SecretKey as SecpSecretKey}; +use dashcore::secp256k1::{PublicKey as SecpPublicKey, SecretKey as SecpSecretKey}; use dashcore::{Network, PlatformNodeId, PrivateKey}; use dpp::identifier::MasternodeIdentifiers; use dpp::identity::accessors::IdentityGettersV0; @@ -223,7 +223,7 @@ fn is_mainnet(network: Network) -> bool { /// only when below the group order; ed25519 accepts any 32 bytes as a seed. fn secret_candidates(bytes: &[u8; 32]) -> Vec { let mut out = Vec::with_capacity(3); - if SecpSecretKey::from_slice(bytes).is_ok() { + if SecpSecretKey::from_secret_bytes(*bytes).is_ok() { out.push(MasternodeLocatorInput::Secret(LocatorSecret::Ecdsa { secret: Zeroizing::new(*bytes), compressed: true, @@ -314,7 +314,7 @@ pub fn parse_locator_input( } return Ok(ParsedLocatorInput { candidates: vec![MasternodeLocatorInput::Secret(LocatorSecret::Ecdsa { - secret: Zeroizing::new(key.inner.secret_bytes()), + secret: Zeroizing::new(key.inner.to_secret_bytes()), compressed: key.compressed, })], }); @@ -353,8 +353,8 @@ pub fn parse_locator_input( /// hash160 of the secp256k1 public key for `secret`, or `None` when the /// scalar is out of range. pub fn ecdsa_key_id(secret: &[u8; 32], compressed: bool) -> Option<[u8; 20]> { - let sk = SecpSecretKey::from_slice(secret).ok()?; - let pk = SecpPublicKey::from_secret_key(&Secp256k1::signing_only(), &sk); + let sk = SecpSecretKey::from_secret_bytes(*secret).ok()?; + let pk = SecpPublicKey::from_secret_key(&sk); let bytes: Vec = if compressed { pk.serialize().to_vec() } else { @@ -1135,10 +1135,7 @@ mod tests { #[test] fn ecdsa_key_id_matches_dashcore_address_hash() { let key = PrivateKey::from_byte_array(&SECP_SECRET, Network::Mainnet).unwrap(); - let expected: [u8; 20] = key - .public_key(&Secp256k1::new()) - .pubkey_hash() - .to_byte_array(); + let expected: [u8; 20] = key.public_key().pubkey_hash().to_byte_array(); assert_eq!(secp_key_id(), expected); assert_ne!( ecdsa_key_id(&SECP_SECRET, false).unwrap(), diff --git a/packages/rs-platform-wallet/src/test_support.rs b/packages/rs-platform-wallet/src/test_support.rs index b9446432ee0..63e42594fc6 100644 --- a/packages/rs-platform-wallet/src/test_support.rs +++ b/packages/rs-platform-wallet/src/test_support.rs @@ -11,7 +11,7 @@ use std::sync::Arc; use async_trait::async_trait; use dashcore::hashes::Hash; -use dashcore::secp256k1::{ecdsa, Message, PublicKey, Secp256k1}; +use dashcore::secp256k1::{ecdsa, Message, PublicKey}; use dashcore::BlockHash; #[cfg(test)] use dashcore::Txid; @@ -135,25 +135,20 @@ impl Signer for WalletSigner { path: &DerivationPath, sighash: [u8; 32], ) -> Result<(ecdsa::Signature, PublicKey), Self::Error> { - let secp = Secp256k1::new(); let key = self .wallet .derive_private_key(path) .map_err(|e| e.to_string())?; let message = Message::from_digest(sighash); - Ok(( - secp.sign_ecdsa(&message, &key), - PublicKey::from_secret_key(&secp, &key), - )) + Ok((key.sign_ecdsa(message), PublicKey::from_secret_key(&key))) } async fn public_key(&self, path: &DerivationPath) -> Result { - let secp = Secp256k1::new(); let key = self .wallet .derive_private_key(path) .map_err(|e| e.to_string())?; - Ok(PublicKey::from_secret_key(&secp, &key)) + Ok(PublicKey::from_secret_key(&key)) } } diff --git a/packages/rs-platform-wallet/src/wallet/core/sign_message.rs b/packages/rs-platform-wallet/src/wallet/core/sign_message.rs index 65321a9d099..9316053883b 100644 --- a/packages/rs-platform-wallet/src/wallet/core/sign_message.rs +++ b/packages/rs-platform-wallet/src/wallet/core/sign_message.rs @@ -46,7 +46,7 @@ use std::str::FromStr; use dashcore::address::Payload; use dashcore::hashes::Hash; use dashcore::secp256k1::ecdsa::{RecoverableSignature, RecoveryId}; -use dashcore::secp256k1::{Message, Secp256k1}; +use dashcore::secp256k1::Message; use dashcore::sign_message::{signed_msg_hash, MessageSignature}; use dashcore::{Address as DashAddress, AddressType, PublicKey as DashPublicKey}; use key_wallet::managed_account::managed_account_trait::ManagedAccountTrait; @@ -274,10 +274,6 @@ impl CoreWallet { }); } - // `recover_ecdsa` needs only a `Verification` context — the signing - // tables a full `Secp256k1::new()` would also allocate are dead weight - // here, since the signature itself came from the signer. - let secp = Secp256k1::verification_only(); let digest = Message::from_digest(hash.to_byte_array()); let compact = signature.serialize_compact(); @@ -286,7 +282,8 @@ impl CoreWallet { .filter_map(|id| RecoveryId::try_from(id).ok()) .filter_map(|recid| RecoverableSignature::from_compact(&compact, recid).ok()) .find(|candidate| { - secp.recover_ecdsa(&digest, candidate) + candidate + .recover_ecdsa(digest) .is_ok_and(|recovered| recovered == public_key) }) .ok_or_else(|| PlatformWalletError::MessageSigningFailed { @@ -303,7 +300,7 @@ mod tests { use std::str::FromStr; use std::sync::Arc; - use dashcore::secp256k1::{ecdsa, PublicKey, Secp256k1}; + use dashcore::secp256k1::{ecdsa, PublicKey}; use dashcore::sign_message::{signed_msg_hash, MessageSignature}; use dashcore::{Address as DashAddress, Network}; use key_wallet::signer::{Signer, SignerMethod, TransactionCategory}; @@ -349,7 +346,7 @@ mod tests { fn verifies_for(signature_base64: &str, address: &DashAddress) -> bool { MessageSignature::from_base64(signature_base64) .expect("signature is valid base64 of a 65-byte recoverable signature") - .is_signed_by_address(&Secp256k1::new(), address, signed_msg_hash(MESSAGE)) + .is_signed_by_address(address, signed_msg_hash(MESSAGE)) .expect("P2PKH address is a supported verification target") } @@ -403,7 +400,7 @@ mod tests { assert!( MessageSignature::from_base64(&signature) .expect("valid base64 of a 65-byte recoverable signature") - .is_signed_by_address(&Secp256k1::new(), &address, signed_msg_hash("")) + .is_signed_by_address(&address, signed_msg_hash("")) .expect("P2PKH address is a supported verification target"), "a signature over the empty message must verify against signed_msg_hash(\"\")" ); diff --git a/packages/rs-platform-wallet/src/wallet/core_address_key.rs b/packages/rs-platform-wallet/src/wallet/core_address_key.rs index f7cbc1e8299..7adb517e769 100644 --- a/packages/rs-platform-wallet/src/wallet/core_address_key.rs +++ b/packages/rs-platform-wallet/src/wallet/core_address_key.rs @@ -22,7 +22,6 @@ use std::str::FromStr; -use dashcore::secp256k1::Secp256k1; use dashcore::{Address, PrivateKey as DashPrivateKey}; use key_wallet::bip32::{DerivationPath, ExtendedPrivKey}; use zeroize::Zeroizing; @@ -115,13 +114,12 @@ impl PlatformWallet { // Derive the raw scalar from the selected key source. let secret_bytes: Zeroizing<[u8; 32]> = match resolved_master { Some(master) => { - let secp = Secp256k1::new(); - let derived = master.derive_priv(&secp, &path).map_err(|e| { + let derived = master.derive_priv(&path).map_err(|e| { PlatformWalletError::KeyDerivation(format!( "failed to derive private key at {path}: {e}" )) })?; - Zeroizing::new(derived.private_key.secret_bytes()) + Zeroizing::new(derived.private_key.to_secret_bytes()) } None => { // Resident key-bearing wallet — derive from its own root. @@ -133,15 +131,15 @@ impl PlatformWallet { "failed to derive private key at {path} from resident wallet: {e}" )) })?; - Zeroizing::new(secret_key.secret_bytes()) + Zeroizing::new(secret_key.to_secret_bytes()) } }; - // Build the network-aware compressed WIF. `SecretKey::from_slice` + // Build the network-aware compressed WIF. `SecretKey::from_secret_bytes` // over the just-derived bytes is infallible, but map its error // rather than unwrap to keep the boundary panic-free. - let secret_key = dashcore::secp256k1::SecretKey::from_slice(secret_bytes.as_ref()) - .map_err(|e| { + let secret_key = + dashcore::secp256k1::SecretKey::from_secret_bytes(*secret_bytes).map_err(|e| { PlatformWalletError::KeyDerivation(format!( "derived private key bytes were not a valid secp256k1 scalar: {e}" )) diff --git a/packages/rs-platform-wallet/src/wallet/identity/crypto/auto_accept.rs b/packages/rs-platform-wallet/src/wallet/identity/crypto/auto_accept.rs index ed20b65f11e..f86e120c84b 100644 --- a/packages/rs-platform-wallet/src/wallet/identity/crypto/auto_accept.rs +++ b/packages/rs-platform-wallet/src/wallet/identity/crypto/auto_accept.rs @@ -25,7 +25,7 @@ //! `m/9'/coin'/16'/timestamp'` (all segments hardened) use dashcore::hashes::{sha256, Hash, HashEngine}; -use dashcore::secp256k1::{ecdsa::Signature, Message, Secp256k1, SecretKey}; +use dashcore::secp256k1::{ecdsa::Signature, Message, SecretKey}; use dpp::prelude::Identifier; use key_wallet::bip32::{ChildNumber, DerivationPath}; use key_wallet::dip9::{ @@ -112,9 +112,9 @@ pub fn derive_auto_accept_private_key( PlatformWalletError::InvalidIdentityData(format!("Failed to derive auto-accept key: {}", e)) })?; - let secret_bytes = zeroize::Zeroizing::new(ext_priv.private_key.secret_bytes()); + let secret_bytes = zeroize::Zeroizing::new(ext_priv.private_key.to_secret_bytes()); - SecretKey::from_slice(&*secret_bytes).map_err(|e| { + SecretKey::from_secret_bytes(*secret_bytes).map_err(|e| { PlatformWalletError::InvalidIdentityData(format!( "Invalid derived auto-accept private key: {}", e @@ -147,8 +147,7 @@ pub fn sign_auto_accept_proof( let msg_hash = build_message_hash(sender_id, recipient_id, account_reference); let message = Message::from_digest(msg_hash); - let secp = Secp256k1::new(); - let signature = secp.sign_ecdsa(&message, secret_key); + let signature = secret_key.sign_ecdsa(message); let sig_bytes = signature.serialize_compact(); let mut proof = Vec::with_capacity(1 + 4 + 1 + 64); @@ -240,9 +239,7 @@ pub fn verify_auto_accept_proof_with_pubkey( Err(_) => return false, }; - Secp256k1::new() - .verify_ecdsa(&message, &signature, pubkey) - .is_ok() + signature.verify(message, pubkey).is_ok() } /// Verify an auto-accept proof by re-deriving the expected key from `wallet`. @@ -265,7 +262,7 @@ pub fn verify_auto_accept_proof( return Ok(false); }; let secret_key = derive_auto_accept_private_key(wallet, network, timestamp)?; - let pubkey = dashcore::secp256k1::PublicKey::from_secret_key(&Secp256k1::new(), &secret_key); + let pubkey = dashcore::secp256k1::PublicKey::from_secret_key(&secret_key); Ok(verify_auto_accept_proof_with_pubkey( &pubkey, proof_bytes, @@ -294,7 +291,7 @@ pub fn encode_auto_accept_key_blob(secret_key: &SecretKey, expiry: u32) -> Vec Result<(SecretKey, u32), Plat if blob[5] != ECDSA_KEY_SIZE { return Err(invalid("auto-accept key size must be 32")); } - let secret_key = SecretKey::from_slice(&blob[6..KEY_BLOB_LEN]) + let secret_key = <[u8; 32]>::try_from(&blob[6..KEY_BLOB_LEN]) + .map_err(|_| dashcore::secp256k1::Error::InvalidSecretKey) + .and_then(SecretKey::from_secret_bytes) .map_err(|e| invalid(format!("invalid auto-accept private key: {e}")))?; Ok((secret_key, expiry)) } @@ -571,7 +570,7 @@ mod tests { assert_eq!(proof.len(), 70); assert_eq!(auto_accept_proof_expiry(&proof), Some(expiry)); - let pubkey = dashcore::secp256k1::PublicKey::from_secret_key(&Secp256k1::new(), &owner_key); + let pubkey = dashcore::secp256k1::PublicKey::from_secret_key(&owner_key); assert!( verify_auto_accept_proof_with_pubkey(&pubkey, &proof, &scanner, &owner, account_ref), "owner verifies the scanner's proof against its own re-derived pubkey" @@ -593,14 +592,14 @@ mod tests { #[test] fn key_blob_round_trip_and_rejects_malformed() { - let key = SecretKey::from_slice(&[0x07u8; 32]).unwrap(); + let key = SecretKey::from_secret_bytes([0x07u8; 32]).unwrap(); let blob = encode_auto_accept_key_blob(&key, 12345); assert_eq!(blob.len(), 38); assert_eq!(blob[0], 0x00); // key type assert_eq!(blob[5], 0x20); // key size let (k2, e2) = decode_auto_accept_key_blob(&blob).expect("decode"); - assert_eq!(k2.secret_bytes(), key.secret_bytes()); + assert_eq!(k2.to_secret_bytes(), key.to_secret_bytes()); assert_eq!(e2, 12345); assert!(decode_auto_accept_key_blob(&blob[..37]).is_err(), "short"); @@ -614,7 +613,7 @@ mod tests { #[test] fn uri_round_trip_and_rejects_malformed() { - let key = SecretKey::from_slice(&[0x09u8; 32]).unwrap(); + let key = SecretKey::from_secret_bytes([0x09u8; 32]).unwrap(); let blob = encode_auto_accept_key_blob(&key, 999); let uri = encode_dashpay_contact_uri("bobspizza", &blob); assert!(uri.starts_with("dash:?du=bobspizza&dapk=")); @@ -662,7 +661,7 @@ mod tests { ); // The cap must not reject a normal-length (valid) dapk. - let key = SecretKey::from_slice(&[0x09u8; 32]).unwrap(); + let key = SecretKey::from_secret_bytes([0x09u8; 32]).unwrap(); let blob = encode_auto_accept_key_blob(&key, 1); let uri = encode_dashpay_contact_uri("alice", &blob); assert!( @@ -673,8 +672,8 @@ mod tests { #[test] fn verify_with_pubkey_rejects_truncated_and_no_expiry() { - let key = SecretKey::from_slice(&[0x05u8; 32]).unwrap(); - let pubkey = dashcore::secp256k1::PublicKey::from_secret_key(&Secp256k1::new(), &key); + let key = SecretKey::from_secret_bytes([0x05u8; 32]).unwrap(); + let pubkey = dashcore::secp256k1::PublicKey::from_secret_key(&key); let (s, r) = test_ids(); assert!(!verify_auto_accept_proof_with_pubkey( &pubkey, &[0u8; 3], &s, &r, 0 diff --git a/packages/rs-platform-wallet/src/wallet/identity/crypto/contact_info.rs b/packages/rs-platform-wallet/src/wallet/identity/crypto/contact_info.rs index 63b07a87f11..015fb085795 100644 --- a/packages/rs-platform-wallet/src/wallet/identity/crypto/contact_info.rs +++ b/packages/rs-platform-wallet/src/wallet/identity/crypto/contact_info.rs @@ -121,7 +121,7 @@ pub fn derive_contact_info_keys( "Failed to derive contactInfo key: {e}" )) })?; - Ok(Zeroizing::new(ext.private_key.secret_bytes())) + Ok(Zeroizing::new(ext.private_key.to_secret_bytes())) }; Ok(ContactInfoKeys { diff --git a/packages/rs-platform-wallet/src/wallet/identity/crypto/dip14.rs b/packages/rs-platform-wallet/src/wallet/identity/crypto/dip14.rs index 9145403be8f..4287f0ca2a9 100644 --- a/packages/rs-platform-wallet/src/wallet/identity/crypto/dip14.rs +++ b/packages/rs-platform-wallet/src/wallet/identity/crypto/dip14.rs @@ -26,7 +26,6 @@ //! - [DIP-14](https://github.com/dashpay/dips/blob/master/dip-0014.md) //! - [DIP-15](https://github.com/dashpay/dips/blob/master/dip-0015.md) -use dashcore::secp256k1::Secp256k1; use dashcore::{Address, Network, PublicKey}; use dpp::prelude::Identifier; use key_wallet::account::AccountType; @@ -210,13 +209,11 @@ pub fn derive_contact_payment_address( index: u32, network: Network, ) -> Result { - let secp = Secp256k1::new(); - let child_number = ChildNumber::from_normal_idx(index).map_err(|e| { PlatformWalletError::InvalidIdentityData(format!("Invalid payment address index: {}", e)) })?; - let address_key = contact_xpub.ckd_pub(&secp, child_number).map_err(|e| { + let address_key = contact_xpub.ckd_pub(child_number).map_err(|e| { PlatformWalletError::InvalidIdentityData(format!( "Failed to derive contact payment key at index {}: {}", index, e diff --git a/packages/rs-platform-wallet/src/wallet/identity/crypto/invitation.rs b/packages/rs-platform-wallet/src/wallet/identity/crypto/invitation.rs index 6060013024a..75812e041eb 100644 --- a/packages/rs-platform-wallet/src/wallet/identity/crypto/invitation.rs +++ b/packages/rs-platform-wallet/src/wallet/identity/crypto/invitation.rs @@ -41,7 +41,7 @@ //! and its network is validated against the wallet at claim (a wrong-network WIF //! is a valid key on the wrong chain, caught before the funding fetch). -use dashcore::secp256k1::{PublicKey, Secp256k1, SecretKey}; +use dashcore::secp256k1::{PublicKey, SecretKey}; use dashcore::transaction::special_transaction::TransactionPayload; use dashcore::{Network, PrivateKey, ScriptBuf, Transaction}; use dpp::prelude::AssetLockProof; @@ -160,8 +160,7 @@ fn invalid(msg: impl Into) -> PlatformWalletError { /// is the selector that binds the voucher key to its funded credit output. /// `pub(crate)` so the claim-side proof-assembly tests can build a funding tx. pub(crate) fn voucher_credit_script(voucher_key: &SecretKey) -> ScriptBuf { - let secp = Secp256k1::new(); - let pubkey = PublicKey::from_secret_key(&secp, voucher_key); + let pubkey = PublicKey::from_secret_key(voucher_key); let hash = dashcore::PublicKey::new(pubkey).pubkey_hash(); ScriptBuf::new_p2pkh(&hash) } @@ -541,7 +540,7 @@ mod tests { use dpp::identity::state_transition::asset_lock_proof::InstantAssetLockProof; fn voucher() -> SecretKey { - SecretKey::from_slice(&[0x11u8; 32]).expect("valid scalar") + SecretKey::from_secret_bytes([0x11u8; 32]).expect("valid scalar") } fn inviter_info() -> InviterInfo { @@ -555,7 +554,7 @@ mod tests { /// Build an asset-lock tx whose credit output at `index` pays the voucher /// key (and `index` decoy outputs before it that do not). fn asset_lock_tx_paying_voucher_at(key: &SecretKey, index: usize) -> Transaction { - let decoy = SecretKey::from_slice(&[0x22u8; 32]).unwrap(); + let decoy = SecretKey::from_secret_bytes([0x22u8; 32]).unwrap(); let mut credit_outputs = Vec::new(); for _ in 0..index { credit_outputs.push(TxOut { @@ -593,7 +592,7 @@ mod tests { let decoded = PrivateKey::from_wif(&wif).expect("wif decodes"); assert!(decoded.compressed, "voucher WIF must be compressed"); assert_eq!(decoded.network, network, "network preserved"); - assert_eq!(decoded.inner.secret_bytes(), voucher().secret_bytes()); + assert_eq!(decoded.inner.to_secret_bytes(), voucher().to_secret_bytes()); // Network byte matches bitcoinj/legacy (0xCC mainnet, 0xEF testnet). let raw = bs58::decode(&wif).into_vec().unwrap(); assert_eq!(raw[0], first_byte); @@ -609,7 +608,10 @@ mod tests { assert!(uri.starts_with("dashpay://invite?")); let parsed = parse_invitation_uri(&uri).expect("parse"); - assert_eq!(parsed.voucher_key.secret_bytes(), voucher().secret_bytes()); + assert_eq!( + parsed.voucher_key.to_secret_bytes(), + voucher().to_secret_bytes() + ); assert_eq!(parsed.inviter, Some(info)); assert!(parsed.islock_hex.is_some()); // The parsed txid matches the proof's transaction id (big-endian). @@ -771,7 +773,7 @@ mod tests { #[test] fn voucher_output_index_rejects_no_match() { let key = voucher(); - let other = SecretKey::from_slice(&[0x33u8; 32]).unwrap(); + let other = SecretKey::from_secret_bytes([0x33u8; 32]).unwrap(); let tx = asset_lock_tx_paying_voucher_at(&other, 0); let err = voucher_output_index(&tx, &key).unwrap_err(); assert!(err.to_string().contains("does not control")); @@ -811,7 +813,7 @@ mod tests { fn parse_rejects_duplicate_required_keys() { let wif = PrivateKey::new(voucher(), Network::Testnet).to_wif(); let other = PrivateKey::new( - SecretKey::from_slice(&[0x44u8; 32]).unwrap(), + SecretKey::from_secret_bytes([0x44u8; 32]).unwrap(), Network::Testnet, ) .to_wif(); diff --git a/packages/rs-platform-wallet/src/wallet/identity/crypto/validation.rs b/packages/rs-platform-wallet/src/wallet/identity/crypto/validation.rs index 374bd425440..c14757f7d56 100644 --- a/packages/rs-platform-wallet/src/wallet/identity/crypto/validation.rs +++ b/packages/rs-platform-wallet/src/wallet/identity/crypto/validation.rs @@ -851,12 +851,11 @@ mod tests { /// Derive the compressed secp256k1 pubkey (`[u8; 33]`) for a fixed /// in-range scalar — the shape a breadcrumb re-derivation produces. fn fixed_scalar_and_compressed_pubkey() -> ([u8; 32], [u8; 33]) { - use dashcore::secp256k1::{PublicKey, Secp256k1, SecretKey}; + use dashcore::secp256k1::{PublicKey, SecretKey}; let mut scalar = [0u8; 32]; scalar[31] = 7; - let secp = Secp256k1::new(); - let sk = SecretKey::from_slice(&scalar).expect("in-range scalar"); - let pubkey = PublicKey::from_secret_key(&secp, &sk).serialize(); + let sk = SecretKey::from_secret_bytes(scalar).expect("in-range scalar"); + let pubkey = PublicKey::from_secret_key(&sk).serialize(); (scalar, pubkey) } diff --git a/packages/rs-platform-wallet/src/wallet/identity/network/contact_info.rs b/packages/rs-platform-wallet/src/wallet/identity/network/contact_info.rs index 965742e2c45..55dfd8402da 100644 --- a/packages/rs-platform-wallet/src/wallet/identity/network/contact_info.rs +++ b/packages/rs-platform-wallet/src/wallet/identity/network/contact_info.rs @@ -514,7 +514,7 @@ impl DashPayView<'_, B> { S: Signer + Send + Sync, C: super::ContactCryptoProvider + Sync, { - use dashcore::secp256k1::rand::{thread_rng, RngCore}; + use dashcore::secp256k1::rand::{rng, RngCore}; use dpp::data_contract::accessors::v0::DataContractV0Getters; // Build the decrypted-payload struct once: it is both the local @@ -696,7 +696,7 @@ impl DashPayView<'_, B> { write_root_id, )?; let mut iv = [0u8; 16]; - thread_rng().fill_bytes(&mut iv); + rng().fill_bytes(&mut iv); let sealed = crypto .contact_info_seal( &root_path, diff --git a/packages/rs-platform-wallet/src/wallet/identity/network/contact_requests.rs b/packages/rs-platform-wallet/src/wallet/identity/network/contact_requests.rs index 8f95fed9279..50d41630dd8 100644 --- a/packages/rs-platform-wallet/src/wallet/identity/network/contact_requests.rs +++ b/packages/rs-platform-wallet/src/wallet/identity/network/contact_requests.rs @@ -241,7 +241,7 @@ impl ContactCryptoProvider for SeedCryptoProvider { PlatformWalletError::InvalidIdentityData(format!("test accountRef derive: {e}")) })?; Ok(platform_encryption::calculate_account_reference( - &xprv.private_key.secret_bytes(), + &xprv.private_key.to_secret_bytes(), compact_xpub, account_index, version, @@ -259,7 +259,7 @@ impl ContactCryptoProvider for SeedCryptoProvider { })?; Ok(platform_encryption::unmask_account_reference( account_reference, - &xprv.private_key.secret_bytes(), + &xprv.private_key.to_secret_bytes(), compact_xpub, )) } @@ -340,7 +340,7 @@ impl SeedCryptoProvider { .map_err(|e| { PlatformWalletError::InvalidIdentityData(format!("test contactInfo derive: {e}")) })?; - Ok(xprv.private_key.secret_bytes()) + Ok(xprv.private_key.to_secret_bytes()) } } @@ -5551,7 +5551,7 @@ mod contact_info_provider_tests { /// the resident `derive_shared_key_ecdh` at the same path. #[tokio::test] async fn ecdh_shared_secret_returns_zeroizing_matching_resident_derivation() { - use dashcore::secp256k1::{PublicKey, Secp256k1, SecretKey}; + use dashcore::secp256k1::{PublicKey, SecretKey}; let seed = Mnemonic::from_phrase(PHRASE) .expect("valid mnemonic") @@ -5563,8 +5563,7 @@ mod contact_info_provider_tests { .expect("auth path"); let peer = PublicKey::from_secret_key( - &Secp256k1::new(), - &SecretKey::from_slice(&[0x42u8; 32]).expect("peer secret"), + &SecretKey::from_secret_bytes([0x42u8; 32]).expect("peer secret"), ); let provider = SeedCryptoProvider::from_seed(seed, network); diff --git a/packages/rs-platform-wallet/src/wallet/identity/network/discovery.rs b/packages/rs-platform-wallet/src/wallet/identity/network/discovery.rs index 6117a170f7b..8b3c33e150d 100644 --- a/packages/rs-platform-wallet/src/wallet/identity/network/discovery.rs +++ b/packages/rs-platform-wallet/src/wallet/identity/network/discovery.rs @@ -304,7 +304,7 @@ impl IdentityWallet { { candidate_scalars.insert( *key_id, - zeroize::Zeroizing::new(xpriv.private_key.secret_bytes()), + zeroize::Zeroizing::new(xpriv.private_key.to_secret_bytes()), ); } } diff --git a/packages/rs-platform-wallet/src/wallet/identity/network/identity_handle.rs b/packages/rs-platform-wallet/src/wallet/identity/network/identity_handle.rs index 402b6930737..7b568a5bf78 100644 --- a/packages/rs-platform-wallet/src/wallet/identity/network/identity_handle.rs +++ b/packages/rs-platform-wallet/src/wallet/identity/network/identity_handle.rs @@ -147,7 +147,6 @@ pub fn derive_ecdsa_identity_auth_keypair_from_master( identity_index: u32, key_index: u32, ) -> Result { - use dashcore::secp256k1::Secp256k1; use key_wallet::bip32::ExtendedPubKey; let path = identity_auth_derivation_path_for_type( @@ -156,7 +155,6 @@ pub fn derive_ecdsa_identity_auth_keypair_from_master( identity_index, key_index, )?; - let secp = Secp256k1::new(); // `ExtendedPrivKey` doesn't implement `Zeroize`, so we can't // wrap it in `Zeroizing` directly — but its inner // `secp256k1::SecretKey` does implement `Drop` with a memzero, @@ -167,16 +165,16 @@ pub fn derive_ecdsa_identity_auth_keypair_from_master( // returned `private_key` is wrapped in `Zeroizing` below so // the 32-byte scalar copy crossing the function boundary is // also scrubbed on the caller's drop. - let derived = master.derive_priv(&secp, &path).map_err(|e| { + let derived = master.derive_priv(&path).map_err(|e| { PlatformWalletError::InvalidIdentityData(format!( "Failed to derive private key at (identity={identity_index}, key={key_index}): {e}" )) })?; - let extended_pub = ExtendedPubKey::from_priv(&secp, &derived); + let extended_pub = ExtendedPubKey::from_priv(&derived); Ok(DerivedIdentityAuthKey { derivation_path: path, - private_key: Zeroizing::new(derived.private_key.secret_bytes()), + private_key: Zeroizing::new(derived.private_key.to_secret_bytes()), public_key: extended_pub.public_key.serialize(), }) } @@ -196,7 +194,6 @@ pub fn derive_identity_auth_keypair( identity_index: u32, key_index: u32, ) -> Result<(DerivationPath, ExtendedPrivKey, PublicKey), PlatformWalletError> { - use dashcore::secp256k1::Secp256k1; use key_wallet::bip32::ExtendedPubKey; let full_path = identity_auth_derivation_path(network, identity_index, key_index)?; @@ -209,8 +206,7 @@ pub fn derive_identity_auth_keypair( )) })?; - let secp = Secp256k1::new(); - let extended_pub = ExtendedPubKey::from_priv(&secp, &auth_key); + let extended_pub = ExtendedPubKey::from_priv(&auth_key); Ok((full_path, auth_key, extended_pub.public_key)) } @@ -434,7 +430,7 @@ impl IdentityWallet { )) })?; - Ok(Zeroizing::new(secret_key.secret_bytes())) + Ok(Zeroizing::new(secret_key.to_secret_bytes())) } /// Get a read-lock handle to the shared [`WalletManager`]. diff --git a/packages/rs-platform-wallet/src/wallet/identity/network/invitation.rs b/packages/rs-platform-wallet/src/wallet/identity/network/invitation.rs index 76ccd55ce9e..4e17646ad22 100644 --- a/packages/rs-platform-wallet/src/wallet/identity/network/invitation.rs +++ b/packages/rs-platform-wallet/src/wallet/identity/network/invitation.rs @@ -940,7 +940,7 @@ mod tests { use dpp::dashcore::{Network, TxOut}; fn voucher_secret() -> SecretKey { - SecretKey::from_slice(&[0x11u8; 32]).unwrap() + SecretKey::from_secret_bytes([0x11u8; 32]).unwrap() } /// An asset-lock tx whose single credit output pays the voucher key. @@ -1071,7 +1071,7 @@ mod tests { #[test] fn prospective_id_follows_the_selected_credit_output() { let key = voucher_secret(); - let decoy = SecretKey::from_slice(&[0x22u8; 32]).unwrap(); + let decoy = SecretKey::from_secret_bytes([0x22u8; 32]).unwrap(); let payload = AssetLockPayload { version: 1, credit_outputs: vec![ diff --git a/packages/rs-platform-wallet/src/wallet/identity/network/payments.rs b/packages/rs-platform-wallet/src/wallet/identity/network/payments.rs index 940b8da96ec..ad136c8ae71 100644 --- a/packages/rs-platform-wallet/src/wallet/identity/network/payments.rs +++ b/packages/rs-platform-wallet/src/wallet/identity/network/payments.rs @@ -1856,10 +1856,9 @@ mod tests { .wallet .derive_extended_private_key(path) .map_err(|e| e.to_string())?; - let secp = dashcore::secp256k1::Secp256k1::new(); let msg = dashcore::secp256k1::Message::from_digest(sighash); - let sig = secp.sign_ecdsa(&msg, &xprv.private_key); - let pk = dashcore::secp256k1::PublicKey::from_secret_key(&secp, &xprv.private_key); + let sig = xprv.private_key.sign_ecdsa(msg); + let pk = dashcore::secp256k1::PublicKey::from_secret_key(&xprv.private_key); Ok((sig, pk)) } @@ -1871,9 +1870,7 @@ mod tests { .wallet .derive_extended_private_key(path) .map_err(|e| e.to_string())?; - let secp = dashcore::secp256k1::Secp256k1::new(); Ok(dashcore::secp256k1::PublicKey::from_secret_key( - &secp, &xprv.private_key, )) } @@ -2028,8 +2025,7 @@ mod tests { use dpp::identity::identity_public_key::v0::IdentityPublicKeyV0; use dpp::identity::{IdentityPublicKey, SecurityLevel}; let data = dashcore::secp256k1::PublicKey::from_secret_key( - &dashcore::secp256k1::Secp256k1::new(), - &dashcore::secp256k1::SecretKey::from_slice(&[0x37u8; 32]).expect("secret"), + &dashcore::secp256k1::SecretKey::from_secret_bytes([0x37u8; 32]).expect("secret"), ) .serialize() .to_vec(); @@ -5493,8 +5489,7 @@ mod tests { key_type: KeyType::ECDSA_SECP256K1, read_only: false, data: dashcore::secp256k1::PublicKey::from_secret_key( - &dashcore::secp256k1::Secp256k1::new(), - &dashcore::secp256k1::SecretKey::from_slice(&[0x24u8; 32]).expect("secret"), + &dashcore::secp256k1::SecretKey::from_secret_bytes([0x24u8; 32]).expect("secret"), ) .serialize() .to_vec() @@ -5762,11 +5757,9 @@ mod tests { let provider = SeedCryptoProvider::from_seed(seed, Network::Testnet); // The contact's encryption keypair (the "sender" of the request). - let secp = dashcore::secp256k1::Secp256k1::new(); - let contact_secret = dashcore::secp256k1::SecretKey::from_slice(&[0x42u8; 32]) + let contact_secret = dashcore::secp256k1::SecretKey::from_secret_bytes([0x42u8; 32]) .expect("valid contact secret"); - let contact_public = - dashcore::secp256k1::PublicKey::from_secret_key(&secp, &contact_secret); + let contact_public = dashcore::secp256k1::PublicKey::from_secret_key(&contact_secret); // Our side, through the production provider. let ours = provider @@ -5850,7 +5843,6 @@ mod tests { let owner = Identifier::from([0xAA; 32]); let contact = Identifier::from([0xBB; 32]); - let secp = dashcore::secp256k1::Secp256k1::new(); let key_at = |id: u32, purpose: Purpose, byte: u8| { IdentityPublicKey::V0(IdentityPublicKeyV0 { id, @@ -5860,8 +5852,7 @@ mod tests { key_type: KeyType::ECDSA_SECP256K1, read_only: false, data: dashcore::secp256k1::PublicKey::from_secret_key( - &secp, - &dashcore::secp256k1::SecretKey::from_slice(&[byte; 32]).expect("secret"), + &dashcore::secp256k1::SecretKey::from_secret_bytes([byte; 32]).expect("secret"), ) .serialize() .to_vec() diff --git a/packages/rs-platform-wallet/src/wallet/masternode_withdrawal.rs b/packages/rs-platform-wallet/src/wallet/masternode_withdrawal.rs index 4ecf07f3b14..85e99301f1a 100644 --- a/packages/rs-platform-wallet/src/wallet/masternode_withdrawal.rs +++ b/packages/rs-platform-wallet/src/wallet/masternode_withdrawal.rs @@ -30,7 +30,7 @@ use std::fmt; use async_trait::async_trait; use dashcore::hashes::{hash160, sha256d, Hash}; use dashcore::secp256k1::ecdsa::{RecoverableSignature, RecoveryId}; -use dashcore::secp256k1::{Message, Secp256k1}; +use dashcore::secp256k1::Message; use dashcore::signer::CompactSignature; use dashcore::{Address as DashAddress, AddressType, Network, ScriptBuf}; use dpp::address_funds::AddressWitness; @@ -533,7 +533,7 @@ pub struct RawSecretCoreSigner { impl RawSecretCoreSigner { /// `secret` must be a valid secp256k1 scalar (32 bytes). pub fn from_bytes(secret: &[u8; 32]) -> Result { - let secret = dashcore::secp256k1::SecretKey::from_slice(secret).map_err(|_| { + let secret = dashcore::secp256k1::SecretKey::from_secret_bytes(*secret).map_err(|_| { PlatformWalletError::InvalidParameter("not a valid secp256k1 private key".to_string()) })?; Ok(Self { secret }) @@ -541,8 +541,7 @@ impl RawSecretCoreSigner { /// hash160 of this key's compressed public key. pub fn public_key_hash160(&self) -> [u8; 20] { - let secp = Secp256k1::signing_only(); - let public = dashcore::secp256k1::PublicKey::from_secret_key(&secp, &self.secret); + let public = dashcore::secp256k1::PublicKey::from_secret_key(&self.secret); hash160::Hash::hash(&public.serialize()).to_byte_array() } } @@ -573,11 +572,10 @@ impl CoreSigner for RawSecretCoreSigner { ), Self::Error, > { - let secp = Secp256k1::new(); let msg = Message::from_digest(sighash); Ok(( - secp.sign_ecdsa(&msg, &self.secret), - dashcore::secp256k1::PublicKey::from_secret_key(&secp, &self.secret), + self.secret.sign_ecdsa(msg), + dashcore::secp256k1::PublicKey::from_secret_key(&self.secret), )) } @@ -586,7 +584,6 @@ impl CoreSigner for RawSecretCoreSigner { _path: &DerivationPath, ) -> Result { Ok(dashcore::secp256k1::PublicKey::from_secret_key( - &Secp256k1::new(), &self.secret, )) } @@ -703,14 +700,14 @@ where ))); } - let secp = Secp256k1::verification_only(); let msg = Message::from_digest(digest); let compact = signature.serialize_compact(); let recoverable = (0..4i32) .filter_map(|id| RecoveryId::try_from(id).ok()) .filter_map(|recid| RecoverableSignature::from_compact(&compact, recid).ok()) .find(|candidate| { - secp.recover_ecdsa(&msg, candidate) + candidate + .recover_ecdsa(msg) .is_ok_and(|recovered| recovered == public_key) }) .ok_or_else(|| { @@ -763,22 +760,21 @@ mod tests { _path: &DerivationPath, sighash: [u8; 32], ) -> Result<(dashcore::secp256k1::ecdsa::Signature, PublicKey), Self::Error> { - let secp = Secp256k1::new(); let msg = Message::from_digest(sighash); Ok(( - secp.sign_ecdsa(&msg, &self.secret), - PublicKey::from_secret_key(&secp, &self.secret), + self.secret.sign_ecdsa(msg), + PublicKey::from_secret_key(&self.secret), )) } async fn public_key(&self, _path: &DerivationPath) -> Result { - Ok(PublicKey::from_secret_key(&Secp256k1::new(), &self.secret)) + Ok(PublicKey::from_secret_key(&self.secret)) } } fn fixture() -> (FixedKeySigner, [u8; 20], IdentityPublicKey) { - let secret = SecretKey::from_slice(&[0x42u8; 32]).expect("valid scalar"); - let pubkey = PublicKey::from_secret_key(&Secp256k1::new(), &secret); + let secret = SecretKey::from_secret_bytes([0x42u8; 32]).expect("valid scalar"); + let pubkey = PublicKey::from_secret_key(&secret); let hash: [u8; 20] = hash160::Hash::hash(&pubkey.serialize()).to_byte_array(); let key = IdentityPublicKey::V0(IdentityPublicKeyV0 { id: 0, diff --git a/packages/rs-platform-wallet/src/wallet/platform_addresses/provider.rs b/packages/rs-platform-wallet/src/wallet/platform_addresses/provider.rs index 6eb140ab58b..d522927468f 100644 --- a/packages/rs-platform-wallet/src/wallet/platform_addresses/provider.rs +++ b/packages/rs-platform-wallet/src/wallet/platform_addresses/provider.rs @@ -1142,7 +1142,6 @@ impl PlatformPaymentAddressProvider { #[cfg(test)] mod tests { use super::*; - use dashcore::secp256k1::Secp256k1; use key_wallet::bip32::ExtendedPrivKey; use key_wallet::Network; use key_wallet_manager::WalletManager; @@ -1151,10 +1150,9 @@ mod tests { const ACCOUNT: u32 = 0; fn test_xpub() -> ExtendedPubKey { - let secp = Secp256k1::new(); let seed = [42u8; 32]; let xprv = ExtendedPrivKey::new_master(Network::Testnet, &seed).expect("master xprv"); - ExtendedPubKey::from_priv(&secp, &xprv) + ExtendedPubKey::from_priv(&xprv) } fn p2pkh(byte: u8) -> PlatformP2PKHAddress { diff --git a/packages/rs-platform-wallet/src/wallet/provider_ecdsa_key_tests.rs b/packages/rs-platform-wallet/src/wallet/provider_ecdsa_key_tests.rs index 2e38a340120..6cd3bd07510 100644 --- a/packages/rs-platform-wallet/src/wallet/provider_ecdsa_key_tests.rs +++ b/packages/rs-platform-wallet/src/wallet/provider_ecdsa_key_tests.rs @@ -100,7 +100,6 @@ async fn private_reveal_is_internally_consistent() { let wallet = platform_wallet().await; let network = wallet_network(&wallet).await; - let secp = dashcore::key::Secp256k1::new(); for kind in [ProviderKeyKind::Owner, ProviderKeyKind::Voting] { for index in [0u32, 1, 19] { @@ -115,21 +114,21 @@ async fn private_reveal_is_internally_consistent() { // WIF and raw scalar must be the same key. let from_wif = PrivateKey::from_wif(wif).expect("valid WIF"); assert_eq!( - from_wif.inner.secret_bytes().to_vec(), + from_wif.inner.to_secret_bytes().to_vec(), **scalar, "{kind:?}#{index}: WIF and raw scalar disagree" ); // The reported public key must be this private key's. assert_eq!( - from_wif.public_key(&secp).to_bytes(), + from_wif.public_key().to_bytes(), derived.public_key_bytes, "{kind:?}#{index}: public key does not belong to the returned private key" ); // ...and the reported address must be that public key's P2PKH on // this wallet's own network, not a hardcoded chain. - let expected = dashcore::Address::p2pkh(&from_wif.public_key(&secp), network); + let expected = dashcore::Address::p2pkh(&from_wif.public_key(), network); assert_eq!( address, &expected.to_string(), diff --git a/packages/rs-platform-wallet/src/wallet/provider_key_at_index.rs b/packages/rs-platform-wallet/src/wallet/provider_key_at_index.rs index 7568ab06cfe..6d86b5946a9 100644 --- a/packages/rs-platform-wallet/src/wallet/provider_key_at_index.rs +++ b/packages/rs-platform-wallet/src/wallet/provider_key_at_index.rs @@ -635,7 +635,6 @@ impl PlatformWallet { "failed to build master xpriv: {e}" )) })?; - let secp = dashcore::key::Secp256k1::new(); // The ACCOUNT's own path, not `account_type.derivation_path(network)`. // The public side above comes off `account.account_xpub`, built from // this path using the ACCOUNT's network; resolving it again from the @@ -654,11 +653,11 @@ impl PlatformWallet { )) })?; let mut child_xpriv = master - .derive_priv(&secp, &account_path) + .derive_priv(&account_path) .and_then(|acct| { let child_path: key_wallet::bip32::DerivationPath = vec![child].into(); - acct.derive_priv(&secp, &child_path) + acct.derive_priv(&child_path) }) .map_err(|e| { PlatformWalletError::KeyDerivation(format!( @@ -666,7 +665,7 @@ impl PlatformWallet { )) })?; let mut private = child_xpriv.to_priv(); - let derived_public = private.public_key(&secp); + let derived_public = private.public_key(); // Produce every output while the scalar is still live, // then erase it. `dashcore::PrivateKey` is `Copy` and @@ -677,7 +676,7 @@ impl PlatformWallet { // the `PrivateKey` itself. let outputs = if derived_public == public_key { Some(( - Zeroizing::new(private.inner.secret_bytes().to_vec()), + Zeroizing::new(private.inner.to_secret_bytes().to_vec()), Zeroizing::new(private.to_wif()), )) } else { @@ -1096,7 +1095,6 @@ mod tests { let wallet = seed_bearing_wallet(network); let seed = wallet.wallet_seed_bytes().expect("resident seed"); let master = ExtendedPrivKey::new_master(network, &seed).expect("master xpriv"); - let secp = dashcore::key::Secp256k1::new(); for (kind, family) in [ (ProviderKeyKind::Owner, "2'"), @@ -1115,11 +1113,11 @@ mod tests { let path = DerivationPath::from_str(&format!("m/9'/{coin}/3'/{family}/{index}")) .expect("explicit DIP-3 path"); - let expected = master.derive_priv(&secp, &path).expect("path derivation"); + let expected = master.derive_priv(&path).expect("path derivation"); assert_eq!( - derived.inner.secret_bytes(), - expected.private_key.secret_bytes(), + derived.inner.to_secret_bytes(), + expected.private_key.to_secret_bytes(), "{kind:?} key at index {index} on {network:?} must come from \ m/9'/{coin}/3'/{family}/{index}" ); @@ -1150,14 +1148,13 @@ mod tests { let master = ExtendedPrivKey::new_master(Network::Mainnet, &seed).expect("master"); let doubled = master .derive_priv( - &dashcore::key::Secp256k1::new(), &DerivationPath::from_str("m/9'/5'/3'/1'/9'/5'/3'/1'/19").expect("doubled path"), ) .expect("doubled derivation"); assert_ne!( - derived.inner.secret_bytes(), - doubled.private_key.secret_bytes(), + derived.inner.to_secret_bytes(), + doubled.private_key.to_secret_bytes(), "the account derivation path is being applied twice again" ); } @@ -1181,7 +1178,6 @@ mod tests { let wallet = seed_bearing_wallet(Network::Mainnet); let seed = wallet.wallet_seed_bytes().expect("resident seed"); - let secp = dashcore::key::Secp256k1::new(); let master = ExtendedPrivKey::new_master(Network::Mainnet, &seed).expect("master"); for (kind, family) in [ @@ -1216,22 +1212,21 @@ mod tests { let child = ChildNumber::from_normal_idx(19).expect("child index"); let child_path: DerivationPath = vec![child].into(); let derived = master - .derive_priv(&secp, &account_path) - .and_then(|acct| acct.derive_priv(&secp, &child_path)) + .derive_priv(&account_path) + .and_then(|acct| acct.derive_priv(&child_path)) .expect("gate-free derivation") .to_priv(); let expected = master .derive_priv( - &secp, &DerivationPath::from_str(&format!("m/9'/5'/3'/{family}/19")) .expect("explicit path"), ) .expect("path derivation"); assert_eq!( - derived.inner.secret_bytes(), - expected.private_key.secret_bytes(), + derived.inner.to_secret_bytes(), + expected.private_key.to_secret_bytes(), "{kind:?} watch-only derivation must match m/9'/5'/3'/{family}/19" ); } diff --git a/packages/rs-scripts/src/bin/register_identity.rs b/packages/rs-scripts/src/bin/register_identity.rs index f8c5f873b34..5caa3060869 100644 --- a/packages/rs-scripts/src/bin/register_identity.rs +++ b/packages/rs-scripts/src/bin/register_identity.rs @@ -88,7 +88,6 @@ use dash_sdk::platform::types::epoch::Epoch; use dash_sdk::{Sdk, SdkBuilder}; use dpp::balances::credits::CREDITS_PER_DUFF; use dpp::dashcore::consensus::encode::{deserialize, serialize}; -use dpp::dashcore::secp256k1::Secp256k1; use dpp::dashcore::transaction::special_transaction::asset_lock::AssetLockPayload; use dpp::dashcore::transaction::special_transaction::TransactionPayload; use dpp::dashcore::{ @@ -515,11 +514,10 @@ async fn run() -> Result<(), String> { } else { // FRESH: generate a one-time key, build + Core-sign + broadcast the // asset lock, capturing all recovery credentials BEFORE broadcast. - let secp = Secp256k1::new(); - let mut secp_rng = dpp::dashcore::secp256k1::rand::thread_rng(); + let mut secp_rng = dpp::dashcore::secp256k1::rand::rng(); let one_time_secret = dpp::dashcore::secp256k1::SecretKey::new(&mut secp_rng); let one_time_private_key = PrivateKey::new(one_time_secret, network); - let one_time_public_key = one_time_private_key.public_key(&secp); + let one_time_public_key = one_time_private_key.public_key(); let one_time_key_hash = one_time_public_key.pubkey_hash(); let one_time_address = Address::p2pkh(&one_time_public_key, network); diff --git a/packages/rs-sdk-ffi/src/address/transitions/transfer.rs b/packages/rs-sdk-ffi/src/address/transitions/transfer.rs index 7e8523710eb..a3ef56455f6 100644 --- a/packages/rs-sdk-ffi/src/address/transitions/transfer.rs +++ b/packages/rs-sdk-ffi/src/address/transitions/transfer.rs @@ -59,8 +59,9 @@ impl Signer for AddressSigner { })?; // Sign the data using dashcore signer - let signature = dash_sdk::dpp::dashcore::signer::sign(data, private_key.inner.as_ref()) - .map_err(|e| ProtocolError::Generic(format!("Signing failed: {}", e)))?; + let signature = + dash_sdk::dpp::dashcore::signer::sign(data, private_key.inner.as_secret_bytes()) + .map_err(|e| ProtocolError::Generic(format!("Signing failed: {}", e)))?; Ok(BinaryData::new(signature.to_vec())) } @@ -83,8 +84,9 @@ impl Signer for AddressSigner { })?; // Sign the data - let signature = dash_sdk::dpp::dashcore::signer::sign(data, private_key.inner.as_ref()) - .map_err(|e| ProtocolError::Generic(format!("Signing failed: {}", e)))?; + let signature = + dash_sdk::dpp::dashcore::signer::sign(data, private_key.inner.as_secret_bytes()) + .map_err(|e| ProtocolError::Generic(format!("Signing failed: {}", e)))?; // Create P2PKH witness (most common for single key) Ok(AddressWitness::P2pkh { @@ -236,7 +238,10 @@ unsafe fn dash_sdk_address_transfer_funds_inner( // Parse private key (32 bytes) let pk_bytes = std::slice::from_raw_parts(input.private_key, 32); - let secret_key = match SecretKey::from_slice(pk_bytes) { + let secret_key = match <[u8; 32]>::try_from(pk_bytes) + .map_err(|_| dash_sdk::dpp::dashcore::secp256k1::Error::InvalidSecretKey) + .and_then(SecretKey::from_secret_bytes) + { Ok(sk) => sk, Err(e) => { return DashSDKResult::error(DashSDKError::new( diff --git a/packages/rs-sdk-ffi/src/address/transitions/withdraw.rs b/packages/rs-sdk-ffi/src/address/transitions/withdraw.rs index 54f7d590fb3..db685b4bf0b 100644 --- a/packages/rs-sdk-ffi/src/address/transitions/withdraw.rs +++ b/packages/rs-sdk-ffi/src/address/transitions/withdraw.rs @@ -210,7 +210,10 @@ unsafe fn dash_sdk_address_withdraw_funds_inner( // Parse private key (32 bytes) let pk_bytes = std::slice::from_raw_parts(input.private_key, 32); - let secret_key = match SecretKey::from_slice(pk_bytes) { + let secret_key = match <[u8; 32]>::try_from(pk_bytes) + .map_err(|_| dash_sdk::dpp::dashcore::secp256k1::Error::InvalidSecretKey) + .and_then(SecretKey::from_secret_bytes) + { Ok(sk) => sk, Err(e) => { return DashSDKResult::error(DashSDKError::new( diff --git a/packages/rs-sdk-ffi/src/contested_resource/transitions/cast_vote.rs b/packages/rs-sdk-ffi/src/contested_resource/transitions/cast_vote.rs index 6a8570f0309..2451b3ce171 100644 --- a/packages/rs-sdk-ffi/src/contested_resource/transitions/cast_vote.rs +++ b/packages/rs-sdk-ffi/src/contested_resource/transitions/cast_vote.rs @@ -61,7 +61,7 @@ use crate::sdk::SDKWrapper; use crate::types::{FFINetwork, Network, SDKHandle}; use crate::{DashSDKResult, FFIError}; use dash_sdk::dpp::dashcore::hashes::{hash160, Hash}; -use dash_sdk::dpp::dashcore::secp256k1::{PublicKey, Secp256k1, SecretKey}; +use dash_sdk::dpp::dashcore::secp256k1::{PublicKey, SecretKey}; use dash_sdk::dpp::dashcore::ProTxHash; use dash_sdk::dpp::platform_value::{Identifier, Value}; use dash_sdk::dpp::voting::vote_choices::resource_vote_choice::ResourceVoteChoice; @@ -341,10 +341,9 @@ unsafe fn cast_vote_inner( // `masternode_voting_key.public_key_hash()` to derive the voter // identifier, and `SingleKeySigner::can_sign_with` recomputes the same // hash160 from the private key, so the two agree by construction. - let secp = Secp256k1::new(); - let secret_key = SecretKey::from_byte_array(&key_array) + let secret_key = SecretKey::from_secret_bytes(*key_array) .map_err(|e| invalid(&format!("Invalid voting private key: {}", e)))?; - let public_key = PublicKey::from_secret_key(&secp, &secret_key); + let public_key = PublicKey::from_secret_key(&secret_key); let voting_address = hash160::Hash::hash(&public_key.serialize()).to_byte_array(); // ---- Broadcast, then diagnose only on failure --------------------------- diff --git a/packages/rs-sdk-ffi/src/identity/create_from_addresses.rs b/packages/rs-sdk-ffi/src/identity/create_from_addresses.rs index c90e2e3dae3..cc32e028811 100644 --- a/packages/rs-sdk-ffi/src/identity/create_from_addresses.rs +++ b/packages/rs-sdk-ffi/src/identity/create_from_addresses.rs @@ -170,7 +170,10 @@ unsafe fn dash_sdk_identity_create_from_addresses_inner( // Parse private key (32 bytes) let pk_bytes = std::slice::from_raw_parts(input.private_key, 32); - let secret_key = match SecretKey::from_slice(pk_bytes) { + let secret_key = match <[u8; 32]>::try_from(pk_bytes) + .map_err(|_| dash_sdk::dpp::dashcore::secp256k1::Error::InvalidSecretKey) + .and_then(SecretKey::from_secret_bytes) + { Ok(sk) => sk, Err(e) => { return DashSDKResult::error(DashSDKError::new( diff --git a/packages/rs-sdk-ffi/src/identity/helpers.rs b/packages/rs-sdk-ffi/src/identity/helpers.rs index e92ec59259d..3dcb3e647d6 100644 --- a/packages/rs-sdk-ffi/src/identity/helpers.rs +++ b/packages/rs-sdk-ffi/src/identity/helpers.rs @@ -88,7 +88,7 @@ pub unsafe fn parse_private_key( private_key_bytes: *const [u8; 32], ) -> Result { let key_bytes = *private_key_bytes; - let secret_key = dashcore::secp256k1::SecretKey::from_byte_array(&key_bytes) + let secret_key = dashcore::secp256k1::SecretKey::from_secret_bytes(key_bytes) .map_err(|e| FFIError::InternalError(format!("Invalid private key: {}", e)))?; Ok(PrivateKey::new(secret_key, Network::Mainnet)) } diff --git a/packages/rs-sdk-ffi/src/identity/top_up_from_addresses.rs b/packages/rs-sdk-ffi/src/identity/top_up_from_addresses.rs index c40346e102b..9cc74ccd280 100644 --- a/packages/rs-sdk-ffi/src/identity/top_up_from_addresses.rs +++ b/packages/rs-sdk-ffi/src/identity/top_up_from_addresses.rs @@ -151,7 +151,10 @@ unsafe fn dash_sdk_identity_top_up_from_addresses_inner( // Parse private key (32 bytes) let pk_bytes = std::slice::from_raw_parts(input.private_key, 32); - let secret_key = match SecretKey::from_slice(pk_bytes) { + let secret_key = match <[u8; 32]>::try_from(pk_bytes) + .map_err(|_| dash_sdk::dpp::dashcore::secp256k1::Error::InvalidSecretKey) + .and_then(SecretKey::from_secret_bytes) + { Ok(sk) => sk, Err(e) => { return DashSDKResult::error(DashSDKError::new( diff --git a/packages/rs-sdk-ffi/src/mnemonic_resolver_core_signer.rs b/packages/rs-sdk-ffi/src/mnemonic_resolver_core_signer.rs index e6a9367c4ab..c4be3c24dc6 100644 --- a/packages/rs-sdk-ffi/src/mnemonic_resolver_core_signer.rs +++ b/packages/rs-sdk-ffi/src/mnemonic_resolver_core_signer.rs @@ -52,7 +52,7 @@ //! seed, and the final derived 32-byte scalar. //! - **The `WipingSecretKey` RAII guard** scrubs the raw //! [`secp256k1::SecretKey`] copies at the two sign sites, where the -//! scalar comes back out of `SecretKey::from_slice`. `SecretKey` is an +//! scalar comes back out of `SecretKey::from_secret_bytes`. `SecretKey` is an //! upstream secp256k1 type with no `Zeroize` impl (only //! `non_secure_erase()`), so it can't ride a `Zeroizing` wrapper; the //! guard wipes it on every exit path — normal return, `?`-early-return, @@ -67,7 +67,7 @@ use std::os::raw::c_char; use async_trait::async_trait; use key_wallet::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey}; -use key_wallet::dashcore::secp256k1::{self, Secp256k1}; +use key_wallet::dashcore::secp256k1; use key_wallet::dip9::{ DASHPAY_CONTACT_INFO_ENC_TO_USER_ID_CHILD, DASHPAY_CONTACT_INFO_PRIVATE_DATA_CHILD, FEATURE_PURPOSE, FEATURE_PURPOSE_DASHPAY_AUTO_ACCEPT, FEATURE_PURPOSE_IDENTITIES, @@ -330,11 +330,10 @@ impl MnemonicResolverCoreSigner { let seed: Zeroizing<[u8; 64]> = Zeroizing::new(mnemonic.to_seed("")); drop(mnemonic); - let secp = Secp256k1::new(); let master = ExtendedPrivKey::new_master(self.network, seed.as_ref()) .map_err(|e| MnemonicResolverSignerError::DerivationFailed(format!("master: {e}")))?; let derived = master - .derive_priv(&secp, path) + .derive_priv(path) .map_err(|e| MnemonicResolverSignerError::DerivationFailed(format!("path: {e}")))?; Ok(extract(&derived)) @@ -350,10 +349,10 @@ impl MnemonicResolverCoreSigner { &self, path: &DerivationPath, ) -> Result, MnemonicResolverSignerError> { - // `secret_bytes()` copies the scalar out of the borrowed key; the + // `to_secret_bytes()` copies the scalar out of the borrowed key; the // `ExtendedPrivKey` itself never leaves `resolve_and_derive`. self.resolve_and_derive(path, |derived| { - Zeroizing::new(derived.private_key.secret_bytes()) + Zeroizing::new(derived.private_key.to_secret_bytes()) }) } @@ -607,7 +606,7 @@ pub struct ContactInfoOpened { /// /// `SecretKey` is an upstream secp256k1 type with no `Zeroize` impl (only /// `non_secure_erase()`), so it can't ride a `Zeroizing` wrapper. Wrapping the -/// `SecretKey::from_slice` copy here wipes it on every exit path — normal +/// `SecretKey::from_secret_bytes` copy here wipes it on every exit path — normal /// return, `?`-early-return, and panic-unwind — closing the leak window a bare /// inline `non_secure_erase()` would leave open between construction and the /// manual scrub. This is the one key intermediate that upstream key-wallet's @@ -637,31 +636,29 @@ impl Signer for MnemonicResolverCoreSigner { sighash: [u8; 32], ) -> Result<(secp256k1::ecdsa::Signature, secp256k1::PublicKey), Self::Error> { let secret_bytes = self.derive_priv(path)?; - let secp = Secp256k1::new(); - // `SecretKey::from_slice` validates the 32-byte scalar is a + // `SecretKey::from_secret_bytes` validates the 32-byte scalar is a // legitimate field element. The `WipingSecretKey` guard scrubs this // separate copy on every exit path, including a panic between here and // the return — `Zeroizing<[u8;32]>` already covers `secret_bytes`. let secret = WipingSecretKey( - secp256k1::SecretKey::from_slice(secret_bytes.as_ref()) + secp256k1::SecretKey::from_secret_bytes(*secret_bytes) .map_err(|e| MnemonicResolverSignerError::InvalidScalar(e.to_string()))?, ); let msg = secp256k1::Message::from_digest(sighash); - let signature = secp.sign_ecdsa(&msg, &secret.0); - let pubkey = secp256k1::PublicKey::from_secret_key(&secp, &secret.0); + let signature = secret.0.sign_ecdsa(msg); + let pubkey = secp256k1::PublicKey::from_secret_key(&secret.0); Ok((signature, pubkey)) } async fn public_key(&self, path: &DerivationPath) -> Result { let secret_bytes = self.derive_priv(path)?; - let secp = Secp256k1::new(); - // `WipingSecretKey` scrubs this `from_slice` copy on every exit path, + // `WipingSecretKey` scrubs this `from_secret_bytes` copy on every exit path, // including panic-unwind — `Zeroizing<[u8;32]>` covers `secret_bytes`. let secret = WipingSecretKey( - secp256k1::SecretKey::from_slice(secret_bytes.as_ref()) + secp256k1::SecretKey::from_secret_bytes(*secret_bytes) .map_err(|e| MnemonicResolverSignerError::InvalidScalar(e.to_string()))?, ); - let pubkey = secp256k1::PublicKey::from_secret_key(&secp, &secret.0); + let pubkey = secp256k1::PublicKey::from_secret_key(&secret.0); Ok(pubkey) } } @@ -680,10 +677,9 @@ impl ExtendedPubKeySigner for MnemonicResolverCoreSigner { &self, path: &DerivationPath, ) -> Result { - let secp = Secp256k1::new(); // `ExtendedPubKey` carries only public material (chain code + point); // the borrowed private key never leaves `resolve_and_derive`. - self.resolve_and_derive(path, |derived| ExtendedPubKey::from_priv(&secp, derived)) + self.resolve_and_derive(path, ExtendedPubKey::from_priv) } } @@ -750,9 +746,8 @@ mod tests { .await .expect("signing succeeds"); - let secp = Secp256k1::new(); let msg = secp256k1::Message::from_digest(sighash); - secp.verify_ecdsa(&msg, &sig, &pk) + sig.verify(msg, &pk) .expect("signature must verify against returned pubkey"); unsafe { dash_sdk_mnemonic_resolver_destroy(resolver) }; @@ -899,12 +894,11 @@ mod tests { // Independently derive the expected xpub straight from the known // BIP-39 vector — same network + path, no resolver in the loop. - let secp = Secp256k1::new(); let mnemonic = parse_mnemonic_any_language(ENGLISH_PHRASE).expect("valid phrase"); let master = ExtendedPrivKey::new_master(Network::Testnet, &mnemonic.to_seed("")) .expect("master derivation"); - let derived = master.derive_priv(&secp, &path).expect("path derivation"); - let expected = ExtendedPubKey::from_priv(&secp, &derived); + let derived = master.derive_priv(&path).expect("path derivation"); + let expected = ExtendedPubKey::from_priv(&derived); // Field-level checks run first so a silently-dropped BIP-32 metadatum // fails here with a precise message — not just the public point. The @@ -1005,9 +999,9 @@ mod tests { let path = test_path(); // A fixed peer keypair (the contact's encryption key). - let secp = Secp256k1::new(); - let peer_sk = secp256k1::SecretKey::from_slice(&[0x42u8; 32]).expect("peer secret key"); - let peer_pk = secp256k1::PublicKey::from_secret_key(&secp, &peer_sk); + let peer_sk = + secp256k1::SecretKey::from_secret_bytes([0x42u8; 32]).expect("peer secret key"); + let peer_pk = secp256k1::PublicKey::from_secret_key(&peer_sk); // Old route: resident-seed wallet from the same mnemonic → derive the // scalar at `path` → ECDH through the single crypto source. @@ -1072,7 +1066,7 @@ mod tests { .derive_extended_private_key(&path) .expect("wallet derives the private key at path") .private_key - .secret_bytes(); + .to_secret_bytes(); let expected = platform_encryption::calculate_account_reference( &secret, &compact_xpub, @@ -1164,7 +1158,7 @@ mod tests { .derive_extended_private_key(&path) .expect("derive encToUserId key") .private_key - .secret_bytes() + .to_secret_bytes() }; let expected_enc = platform_encryption::encrypt_enc_to_user_id(&enc_key, &contact_id); assert_eq!( diff --git a/packages/rs-sdk-ffi/src/signer_simple.rs b/packages/rs-sdk-ffi/src/signer_simple.rs index e3b3d34e2ba..3ad020582d8 100644 --- a/packages/rs-sdk-ffi/src/signer_simple.rs +++ b/packages/rs-sdk-ffi/src/signer_simple.rs @@ -311,7 +311,6 @@ pub unsafe extern "C" fn dash_sdk_sign_with_mnemonic_and_path( out_signature_len: *mut usize, out_error: *mut u8, ) -> i32 { - use dash_sdk::dpp::dashcore::secp256k1::Secp256k1; use dash_sdk::dpp::identity::KeyType; use key_wallet::bip32::{DerivationPath, ExtendedPrivKey}; use std::ffi::CStr; @@ -396,8 +395,7 @@ pub unsafe extern "C" fn dash_sdk_sign_with_mnemonic_and_path( Ok(m) => m, Err(_) => return fail(SIGN_WITH_MNEMONIC_ERR_DERIVATION), }; - let secp = Secp256k1::new(); - let derived = match master.derive_priv(&secp, &path) { + let derived = match master.derive_priv(&path) { Ok(d) => d, Err(_) => return fail(SIGN_WITH_MNEMONIC_ERR_DERIVATION), }; @@ -406,7 +404,7 @@ pub unsafe extern "C" fn dash_sdk_sign_with_mnemonic_and_path( // returns. `derived` self-wipes separately: `ExtendedPrivKey` // zeroizes on `Drop`. let secret_bytes: zeroize::Zeroizing<[u8; 32]> = - zeroize::Zeroizing::new(derived.private_key.secret_bytes()); + zeroize::Zeroizing::new(derived.private_key.to_secret_bytes()); // ---- Sign --------------------------------------------------------------- // `dashcore::signer::sign` returns a 65-byte compact recoverable diff --git a/packages/rs-sdk/src/platform/dashpay/contact_request.rs b/packages/rs-sdk/src/platform/dashpay/contact_request.rs index e40be3284e4..a435dad8d79 100644 --- a/packages/rs-sdk/src/platform/dashpay/contact_request.rs +++ b/packages/rs-sdk/src/platform/dashpay/contact_request.rs @@ -6,7 +6,7 @@ use crate::platform::transition::put_document::PutDocument; use crate::platform::Document; use crate::{Error, Sdk}; use dpp::dashcore::secp256k1::rand::rngs::StdRng; -use dpp::dashcore::secp256k1::rand::{RngCore, SeedableRng}; +use dpp::dashcore::secp256k1::rand::{Rng, RngCore, SeedableRng}; use dpp::dashcore::secp256k1::{PublicKey, SecretKey}; use dpp::data_contract::accessors::v0::DataContractV0Getters; use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; @@ -365,7 +365,7 @@ impl Sdk { } // Generate random IVs for encryption - let mut rng = StdRng::from_entropy(); + let mut rng = StdRng::from_os_rng(); let mut xpub_iv = [0u8; 16]; rng.fill_bytes(&mut xpub_iv); @@ -410,8 +410,8 @@ impl Sdk { })?; // Generate entropy for document ID - let mut rng = StdRng::from_entropy(); - let entropy = Bytes32::random_with_rng(&mut rng); + let mut rng = StdRng::from_os_rng(); + let entropy = Bytes32::new(rng.random()); // Generate document ID let sender_id = input.sender_identity.id().to_owned(); @@ -573,15 +573,14 @@ impl Sdk { #[cfg(test)] mod tests { use super::*; + use dpp::dashcore::secp256k1; use dpp::dashcore::secp256k1::rand::{self, RngCore}; - use dpp::dashcore::secp256k1::Secp256k1; #[test] fn test_ecdh_encryption_produces_correct_size() { // Test that ECDH encryption produces the correct output sizes - let secp = Secp256k1::new(); - let (secret1, _public1) = secp.generate_keypair(&mut rand::thread_rng()); - let (_secret2, public2) = secp.generate_keypair(&mut rand::thread_rng()); + let (secret1, _public1) = secp256k1::generate_keypair(&mut rand::rng()); + let (_secret2, public2) = secp256k1::generate_keypair(&mut rand::rng()); // Derive shared key let shared_key = derive_shared_key_ecdh(&secret1, &public2); @@ -589,8 +588,8 @@ mod tests { // Generate random IVs let mut xpub_iv = [0u8; 16]; let mut label_iv = [0u8; 16]; - rand::thread_rng().fill_bytes(&mut xpub_iv); - rand::thread_rng().fill_bytes(&mut label_iv); + rand::rng().fill_bytes(&mut xpub_iv); + rand::rng().fill_bytes(&mut label_iv); // Test extended public key encryption: the DIP-15 compact plaintext is // 69 bytes (parentFingerprint ‖ chainCode ‖ pubKey) → 96 bytes with IV @@ -652,7 +651,7 @@ mod tests { // send_contact_request would generate fresh entropy E2 != E1 and this // invariant could not even be expressed. This test pins it. let mut rng = StdRng::seed_from_u64(0x6732_4732); // deterministic, no network - let entropy = Bytes32::random_with_rng(&mut rng); + let entropy = Bytes32::new(rng.random()); let contract_id = Identifier::from([1u8; 32]); let owner_id = Identifier::from([2u8; 32]); @@ -770,9 +769,8 @@ mod tests { #[test] fn test_ecdh_shared_secret_symmetry() { // Test that both parties derive the same shared secret - let secp = Secp256k1::new(); - let (secret_alice, public_alice) = secp.generate_keypair(&mut rand::thread_rng()); - let (secret_bob, public_bob) = secp.generate_keypair(&mut rand::thread_rng()); + let (secret_alice, public_alice) = secp256k1::generate_keypair(&mut rand::rng()); + let (secret_bob, public_bob) = secp256k1::generate_keypair(&mut rand::rng()); // Alice derives shared secret using her private key and Bob's public key let shared_alice = derive_shared_key_ecdh(&secret_alice, &public_bob); diff --git a/packages/rs-sdk/src/platform/dpns_usernames/mod.rs b/packages/rs-sdk/src/platform/dpns_usernames/mod.rs index a8e02b29175..077cba9a7b8 100644 --- a/packages/rs-sdk/src/platform/dpns_usernames/mod.rs +++ b/packages/rs-sdk/src/platform/dpns_usernames/mod.rs @@ -161,8 +161,8 @@ impl Sdk { .map_err(|_| Error::Generic("DPNS domain document type not found".to_string()))?; // Generate the preorder salt - let mut rng = StdRng::from_entropy(); - let salt: [u8; 32] = rng.gen(); + let mut rng = StdRng::from_os_rng(); + let salt: [u8; 32] = rng.random(); // The id of a new document commits to the identity contract nonce of // its create transition, so it only exists once `put_to_platform` has diff --git a/packages/rs-sdk/src/platform/transition/broadcast_identity.rs b/packages/rs-sdk/src/platform/transition/broadcast_identity.rs index 3b07883f310..319fa8d5269 100644 --- a/packages/rs-sdk/src/platform/transition/broadcast_identity.rs +++ b/packages/rs-sdk/src/platform/transition/broadcast_identity.rs @@ -154,7 +154,7 @@ impl> IdentityCreateTransition::try_from_identity_with_signer_and_private_key( self, asset_lock_proof, - asset_lock_proof_private_key.inner.as_ref(), + asset_lock_proof_private_key.inner.as_secret_bytes(), signer, &NativeBlsModule, user_fee_increase, diff --git a/packages/rs-sdk/src/platform/transition/put_document.rs b/packages/rs-sdk/src/platform/transition/put_document.rs index 792b356c7a3..22ad2719917 100644 --- a/packages/rs-sdk/src/platform/transition/put_document.rs +++ b/packages/rs-sdk/src/platform/transition/put_document.rs @@ -148,9 +148,9 @@ impl> PutDocument for Document { (document, entropy) } None => { - let mut rng = StdRng::from_entropy(); + let mut rng = StdRng::from_os_rng(); let mut document = document; - let entropy = rng.gen::<[u8; 32]>(); + let entropy = rng.random::<[u8; 32]>(); document.set_id(Document::generate_document_id( &document_type.data_contract_id(), &document.owner_id(), diff --git a/packages/rs-sdk/src/platform/transition/top_up_address.rs b/packages/rs-sdk/src/platform/transition/top_up_address.rs index 3e7bfa58826..e8de2cc33c6 100644 --- a/packages/rs-sdk/src/platform/transition/top_up_address.rs +++ b/packages/rs-sdk/src/platform/transition/top_up_address.rs @@ -159,7 +159,7 @@ impl> TopUpAddress for AddressesWithBalances { let state_transition = create_address_funding_from_asset_lock_transition( asset_lock_proof, - asset_lock_private_key.inner.as_ref(), + asset_lock_private_key.inner.as_secret_bytes(), BTreeMap::new(), self.clone(), fee_strategy, diff --git a/packages/rs-sdk/src/platform/transition/top_up_identity.rs b/packages/rs-sdk/src/platform/transition/top_up_identity.rs index aa98bf00219..3962ff00fcf 100644 --- a/packages/rs-sdk/src/platform/transition/top_up_identity.rs +++ b/packages/rs-sdk/src/platform/transition/top_up_identity.rs @@ -81,7 +81,7 @@ impl TopUpIdentity for Identity { let state_transition = IdentityTopUpTransition::try_from_identity_with_private_key( self, asset_lock_proof, - asset_lock_proof_private_key.inner.as_ref(), + asset_lock_proof_private_key.inner.as_secret_bytes(), user_fee_increase, sdk.version(), None, diff --git a/packages/rs-unified-sdk-jni/src/tx_decode.rs b/packages/rs-unified-sdk-jni/src/tx_decode.rs index 015a88a8bc6..f5b5772e440 100644 --- a/packages/rs-unified-sdk-jni/src/tx_decode.rs +++ b/packages/rs-unified-sdk-jni/src/tx_decode.rs @@ -210,7 +210,7 @@ mod tests { use super::*; use dashcore::consensus::serialize; use dashcore::hashes::Hash; - use dashcore::secp256k1::{Secp256k1, SecretKey}; + use dashcore::secp256k1::SecretKey; use dashcore::{ Address, Network, OutPoint, PublicKey, ScriptBuf, Transaction, TxIn, TxOut, Txid, Witness, }; @@ -247,9 +247,8 @@ mod tests { } fn test_pubkey() -> PublicKey { - let secp = Secp256k1::new(); - let sk = SecretKey::from_slice(&[0x42u8; 32]).expect("valid secret key"); - PublicKey::new(sk.public_key(&secp)) + let sk = SecretKey::from_secret_bytes([0x42u8; 32]).expect("valid secret key"); + PublicKey::new(sk.public_key()) } /// The same deterministic fixture as key-wallet-ffi's diff --git a/packages/simple-signer/Cargo.toml b/packages/simple-signer/Cargo.toml index 089fae064ff..d09a1910d2a 100644 --- a/packages/simple-signer/Cargo.toml +++ b/packages/simple-signer/Cargo.toml @@ -23,6 +23,9 @@ async-trait = { version = "0.1.79" } bincode = { workspace = true, features = ["serde"] } base64 = { version = "0.22.1" } hex = { version = "0.4.3" } +# rand 0.8 `RngCore`, the RNG type strategy-tests/drive-abci pass in; the +# secp256k1 re-export is rand 0.9. +rand = { version = "0.8", default-features = false } tracing = "0.1.41" zeroize = "1" diff --git a/packages/simple-signer/src/signer.rs b/packages/simple-signer/src/signer.rs index c7fc229e551..da4baa0c3c1 100644 --- a/packages/simple-signer/src/signer.rs +++ b/packages/simple-signer/src/signer.rs @@ -4,8 +4,8 @@ use base64::Engine; use dpp::address_funds::{AddressWitness, PlatformAddress}; use dpp::bincode::{Decode, Encode}; use dpp::bls_signatures::{Bls12381G2Impl, SignatureSchemes}; -use dpp::dashcore::secp256k1::rand::{RngCore, SeedableRng}; -use dpp::dashcore::secp256k1::{PublicKey, Secp256k1, SecretKey}; +use dpp::dashcore::secp256k1::rand::SeedableRng; +use dpp::dashcore::secp256k1::{PublicKey, SecretKey}; use dpp::dashcore::signer; use dpp::ed25519_dalek::Signer as BlsSigner; use dpp::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0; @@ -15,6 +15,7 @@ use dpp::platform_value::BinaryData; use dpp::state_transition::errors::InvalidIdentityPublicKeyTypeError; use dpp::util::hash::ripemd160_sha256; use dpp::{bls_signatures, dashcore, ed25519_dalek, ProtocolError}; +use rand::RngCore; use std::collections::BTreeMap; use std::fmt::{Debug, Formatter}; @@ -95,14 +96,14 @@ impl SimpleSigner { /// /// This is only for tests. pub fn add_random_address_key(&mut self, rng: &mut R) -> PlatformAddress { - let secp = Secp256k1::new(); - // Generate a valid secp256k1 secret key from random bytes - let mut ecdsa_rng = dashcore::secp256k1::rand::rngs::StdRng::from_rng(rng).unwrap(); + let mut seed = [0u8; 32]; + rng.fill_bytes(&mut seed); + let mut ecdsa_rng = dashcore::secp256k1::rand::rngs::StdRng::from_seed(seed); let secret_key = SecretKey::new(&mut ecdsa_rng); // Derive compressed public key - let public_key = PublicKey::from_secret_key(&secp, &secret_key); + let public_key = PublicKey::from_secret_key(&secret_key); let pubkey_ser = public_key.serialize(); // 33-byte compressed let address_hash = ripemd160_sha256(&pubkey_ser); @@ -110,7 +111,7 @@ impl SimpleSigner { // Store private key so this signer can later sign for this address // (use *_in_creation to mirror your identity key behavior) self.address_private_keys_in_creation - .insert(address_hash, secret_key.secret_bytes()); + .insert(address_hash, secret_key.to_secret_bytes()); PlatformAddress::P2pkh(address_hash) } diff --git a/packages/simple-signer/src/single_key_signer.rs b/packages/simple-signer/src/single_key_signer.rs index ad76b20af51..30cd749ed28 100644 --- a/packages/simple-signer/src/single_key_signer.rs +++ b/packages/simple-signer/src/single_key_signer.rs @@ -114,7 +114,7 @@ impl Signer for SingleKeySigner { KeyType::ECDSA_SECP256K1 | KeyType::ECDSA_HASH160 => { // Do not log private key material. Log data fingerprint only. debug!(data_hex = %hex::encode(data), "SingleKeySigner: signing data"); - let secret_bytes = Zeroizing::new(self.private_key.inner.secret_bytes()); + let secret_bytes = Zeroizing::new(self.private_key.inner.to_secret_bytes()); let signature = signer::sign(data, &secret_bytes[..])?; Ok(signature.to_vec().into()) } @@ -156,9 +156,8 @@ impl Signer for SingleKeySigner { match identity_public_key.key_type() { KeyType::ECDSA_SECP256K1 => { // Compare full public key - let secp = dashcore::secp256k1::Secp256k1::new(); let public_key = - dashcore::secp256k1::PublicKey::from_secret_key(&secp, &self.private_key.inner); + dashcore::secp256k1::PublicKey::from_secret_key(&self.private_key.inner); let public_key_bytes = public_key.serialize(); identity_public_key.data().as_slice() == public_key_bytes @@ -167,9 +166,8 @@ impl Signer for SingleKeySigner { // Compare hash160 of public key use dpp::dashcore::hashes::{hash160, Hash}; - let secp = dashcore::secp256k1::Secp256k1::new(); let public_key = - dashcore::secp256k1::PublicKey::from_secret_key(&secp, &self.private_key.inner); + dashcore::secp256k1::PublicKey::from_secret_key(&self.private_key.inner); let public_key_bytes = public_key.serialize(); let public_key_hash160 = hash160::Hash::hash(&public_key_bytes) .to_byte_array() @@ -222,7 +220,7 @@ mod tests { let hex = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; let signer = SingleKeySigner::from_hex(hex, Network::Testnet) .map_err(|e| format!("signer init failed: {}", e))?; - assert_eq!(signer.private_key().inner.secret_bytes().len(), 32); + assert_eq!(signer.private_key().inner.to_secret_bytes().len(), 32); Ok(()) } @@ -250,10 +248,8 @@ mod tests { use dpp::dashcore::hashes::{hash160, Hash}; let signer = SingleKeySigner::new_from_slice(&[0x03; 32], Network::Testnet)?; - let secp = dashcore::secp256k1::Secp256k1::new(); let public_key = - dashcore::secp256k1::PublicKey::from_secret_key(&secp, &signer.private_key.inner) - .serialize(); + dashcore::secp256k1::PublicKey::from_secret_key(&signer.private_key.inner).serialize(); let full_key = identity_key(KeyType::ECDSA_SECP256K1, public_key.to_vec()); assert!(signer.can_sign_with(&full_key)); @@ -275,7 +271,7 @@ mod tests { let hex = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; let signer = SingleKeySigner::from_string(hex, Network::Testnet) .map_err(|e| format!("signer init failed: {}", e))?; - assert_eq!(signer.private_key().inner.secret_bytes().len(), 32); + assert_eq!(signer.private_key().inner.to_secret_bytes().len(), 32); // Test WIF detection let private_key = PrivateKey::from_byte_array(&[0x02; 32], Network::Testnet) diff --git a/packages/strategy-tests/src/lib.rs b/packages/strategy-tests/src/lib.rs index f471c417d5c..9a8ad37941f 100644 --- a/packages/strategy-tests/src/lib.rs +++ b/packages/strategy-tests/src/lib.rs @@ -761,7 +761,7 @@ impl Strategy { let funding_transition = AddressFundingFromAssetLockTransitionV0::try_from_asset_lock_with_signer_and_private_key( asset_lock_proof, - private_key.inner.secret_bytes().as_slice(), + private_key.inner.to_secret_bytes().as_slice(), BTreeMap::new(), // no additional inputs outputs, vec![AddressFundsFeeStrategyStep::ReduceOutput(0)], @@ -2206,7 +2206,7 @@ impl Strategy { let funding_transition = AddressFundingFromAssetLockTransitionV0::try_from_asset_lock_with_signer_and_private_key( asset_lock_proof, - private_key.inner.secret_bytes().as_slice(), + private_key.inner.to_secret_bytes().as_slice(), BTreeMap::new(), // no additional inputs outputs, vec![AddressFundsFeeStrategyStep::ReduceOutput(0)], diff --git a/packages/strategy-tests/src/transitions.rs b/packages/strategy-tests/src/transitions.rs index d954e502009..bf678e6b412 100644 --- a/packages/strategy-tests/src/transitions.rs +++ b/packages/strategy-tests/src/transitions.rs @@ -32,7 +32,6 @@ //! each action. use dpp::address_funds::PlatformAddress; -use dpp::dashcore::secp256k1::Secp256k1; use dpp::dashcore::secp256k1::SecretKey; use dpp::dashcore::{ bls_sig_utils::BLSSignature, hash_types::CycleHash, InstantLock, OutPoint, ScriptBuf, @@ -173,14 +172,12 @@ pub fn instant_asset_lock_proof_fixture_with_dynamic_range( pub fn instant_asset_lock_proof_transaction_fixture( one_time_private_key: PrivateKey, ) -> Transaction { - let secp = Secp256k1::new(); - let private_key_hex = "cSBnVM4xvxarwGQuAfQFwqDg9k5tErHUHzgWsEfD4zdwUasvqRVY"; let private_key = PrivateKey::from_str(private_key_hex).unwrap(); - let public_key = private_key.public_key(&secp); + let public_key = private_key.public_key(); let public_key_hash = public_key.pubkey_hash(); //let from_address = Address::p2pkh(&public_key, Network::Testnet); - let one_time_public_key = one_time_private_key.public_key(&secp); + let one_time_public_key = one_time_private_key.public_key(); // We are going to fund 1 Dash and // assume that input has 100005000 @@ -251,14 +248,12 @@ pub fn instant_asset_lock_proof_transaction_fixture_with_dynamic_amount( amount_range: &AmountRange, rng: &mut StdRng, ) -> Transaction { - let secp = Secp256k1::new(); - let private_key_hex = "cSBnVM4xvxarwGQuAfQFwqDg9k5tErHUHzgWsEfD4zdwUasvqRVY"; let private_key = PrivateKey::from_str(private_key_hex).unwrap(); - let public_key = private_key.public_key(&secp); + let public_key = private_key.public_key(); let public_key_hash = public_key.pubkey_hash(); //let from_address = Address::p2pkh(&public_key, Network::Testnet); - let one_time_public_key = one_time_private_key.public_key(&secp); + let one_time_public_key = one_time_private_key.public_key(); // We are going to fund 1 Dash and // assume that input has 100005000 diff --git a/packages/wasm-dpp2/src/core/private_key.rs b/packages/wasm-dpp2/src/core/private_key.rs index 17a41c969e8..fbb099162e4 100644 --- a/packages/wasm-dpp2/src/core/private_key.rs +++ b/packages/wasm-dpp2/src/core/private_key.rs @@ -9,8 +9,6 @@ use crate::public_key::PublicKeyWasm; use crate::utils::try_vec_to_fixed_bytes; use dpp::dashcore::PrivateKey; use dpp::dashcore::hashes::hex::FromHex; -use dpp::dashcore::key::Secp256k1; -use dpp::dashcore::secp256k1::hashes::hex::{Case, DisplayHex}; use wasm_bindgen::prelude::wasm_bindgen; #[wasm_bindgen(js_name = "PrivateKey")] @@ -78,9 +76,7 @@ impl PrivateKeyWasm { #[wasm_bindgen(js_name = "getPublicKey")] pub fn get_public_key(&self) -> PublicKeyWasm { - let secp = Secp256k1::new(); - - let public_key = self.0.public_key(&secp); + let public_key = self.0.public_key(); public_key.into() } @@ -100,14 +96,12 @@ impl PrivateKeyWasm { #[wasm_bindgen(js_name = "toHex")] pub fn to_hex(&self) -> String { - self.0.to_bytes().to_hex_string(Case::Upper) + hex::encode_upper(self.0.to_bytes()) } #[wasm_bindgen(js_name = "getPublicKeyHash")] pub fn get_public_key_hash(&self) -> String { - let secp = Secp256k1::new(); - - self.0.public_key(&secp).pubkey_hash().to_hex() + self.0.public_key().pubkey_hash().to_hex() } } diff --git a/packages/wasm-dpp2/src/identity/public_key.rs b/packages/wasm-dpp2/src/identity/public_key.rs index 8e2c1b42fe8..578dba11293 100644 --- a/packages/wasm-dpp2/src/identity/public_key.rs +++ b/packages/wasm-dpp2/src/identity/public_key.rs @@ -14,7 +14,6 @@ use crate::utils::{ }; use crate::version::PlatformVersionLikeJs; use dpp::dashcore::Network; -use dpp::dashcore::secp256k1::hashes::hex::{Case, DisplayHex}; use dpp::fee::Credits; use dpp::identity::contract_bounds::ContractBounds; use dpp::identity::hash::IdentityPublicKeyHashMethodsV0; @@ -397,11 +396,7 @@ impl IdentityPublicKeyWasm { #[wasm_bindgen(js_name = "getPublicKeyHash")] pub fn public_key_hash(&self) -> WasmDppResult { - let hash = self - .0 - .public_key_hash()? - .to_vec() - .to_hex_string(Case::Lower); + let hash = hex::encode(self.0.public_key_hash()?); Ok(hash) } diff --git a/packages/wasm-dpp2/src/public_key.rs b/packages/wasm-dpp2/src/public_key.rs index abd1dfc8d01..a5da3562345 100644 --- a/packages/wasm-dpp2/src/public_key.rs +++ b/packages/wasm-dpp2/src/public_key.rs @@ -39,7 +39,7 @@ impl PublicKeyWasm { public_key_bytes.try_into().map_err(|_| { WasmDppError::invalid_argument("compressed public key must contain 33 bytes") })?; - secp256k1::PublicKey::from_byte_array_compressed(&bytes) + secp256k1::PublicKey::from_byte_array_compressed(bytes) } else { if public_key_bytes.len() != constants::UNCOMPRESSED_PUBLIC_KEY_SIZE { return Err(WasmDppError::invalid_argument(format!( @@ -53,7 +53,7 @@ impl PublicKeyWasm { WasmDppError::invalid_argument("uncompressed public key must contain 65 bytes") })?; - secp256k1::PublicKey::from_byte_array_uncompressed(&bytes) + secp256k1::PublicKey::from_byte_array_uncompressed(bytes) } .map_err(|err| WasmDppError::invalid_argument(err.to_string()))?; @@ -87,7 +87,7 @@ impl PublicKeyWasm { })?; self.0.compressed = true; - self.0.inner = secp256k1::PublicKey::from_byte_array_compressed(&bytes) + self.0.inner = secp256k1::PublicKey::from_byte_array_compressed(bytes) .map_err(|err| WasmDppError::invalid_argument(err.to_string()))?; } else { if inner.len() != constants::UNCOMPRESSED_PUBLIC_KEY_SIZE { @@ -103,7 +103,7 @@ impl PublicKeyWasm { })?; self.0.compressed = false; - self.0.inner = secp256k1::PublicKey::from_byte_array_uncompressed(&bytes) + self.0.inner = secp256k1::PublicKey::from_byte_array_uncompressed(bytes) .map_err(|err| WasmDppError::invalid_argument(err.to_string()))?; } diff --git a/packages/wasm-dpp2/src/state_transitions/base/state_transition.rs b/packages/wasm-dpp2/src/state_transitions/base/state_transition.rs index cda6a045da5..d657865075e 100644 --- a/packages/wasm-dpp2/src/state_transitions/base/state_transition.rs +++ b/packages/wasm-dpp2/src/state_transitions/base/state_transition.rs @@ -7,8 +7,6 @@ use crate::identifier::{IdentifierLikeJs, IdentifierWasm}; use crate::identity::public_key::IdentityPublicKeyWasm; use crate::impl_wasm_type_info; use crate::mock_bls::MockBLS; -use dpp::dashcore::secp256k1::hashes::hex::Case::Lower; -use dpp::dashcore::secp256k1::hashes::hex::DisplayHex; use dpp::data_contract::serialized_version::DataContractInSerializationFormat; use dpp::identity::{KeyID, KeyType}; use dpp::platform_value::BinaryData; @@ -330,7 +328,7 @@ impl StateTransitionWasm { dpp::serialization::PlatformSerializable::serialize_to_bytes(&self.0)? }; - Ok(Sha256::digest(payload).to_hex_string(Lower)) + Ok(hex::encode(Sha256::digest(payload))) } #[wasm_bindgen(getter = "actionType")] diff --git a/packages/wasm-sdk/src/wallet/dip14.rs b/packages/wasm-sdk/src/wallet/dip14.rs index a1a5c3a3b56..1deb3fb80e1 100644 --- a/packages/wasm-sdk/src/wallet/dip14.rs +++ b/packages/wasm-sdk/src/wallet/dip14.rs @@ -4,7 +4,7 @@ //! instead of the standard 31-bit limitation. use dash_sdk::dpp::dashcore::hashes::{sha256, Hash}; -use dash_sdk::dpp::dashcore::secp256k1::{self, PublicKey, Scalar, Secp256k1, SecretKey}; +use dash_sdk::dpp::dashcore::secp256k1::{self, PublicKey, Scalar, SecretKey}; use dash_sdk::dpp::dashcore::Network; use dash_sdk::dpp::key_wallet; use dash_sdk::dpp::key_wallet::bip32::{ExtendedPrivKey, ExtendedPubKey}; @@ -97,8 +97,6 @@ impl Dip14ExtendedPrivKey { /// Derive a child key using DIP14 extended derivation pub fn derive_child(&self, index: &[u8; 32], hardened: bool) -> Result { - let secp = Secp256k1::new(); - // Prepare HMAC input based on hardened flag let mut hmac = HmacSha512::new_from_slice(&self.chain_code) .map_err(|_| Dip14Error::DerivationFailed("Invalid chain code".to_string()))?; @@ -106,11 +104,11 @@ impl Dip14ExtendedPrivKey { if hardened { // Hardened: 0x00 || ser256(k_parent) || ser256(i) hmac.update(&[0x00]); - hmac.update(&self.private_key.secret_bytes()); + hmac.update(&self.private_key.to_secret_bytes()); hmac.update(&ser256(index)); } else { // Non-hardened: ser_P(point(k_parent)) || ser256(i) - let public_key = PublicKey::from_secret_key(&secp, &self.private_key); + let public_key = PublicKey::from_secret_key(&self.private_key); hmac.update(&public_key.serialize()); hmac.update(&ser256(index)); } @@ -122,7 +120,10 @@ impl Dip14ExtendedPrivKey { // This is the core of BIP32/DIP14 child key derivation // First, try to create a secret key from IL - let il_scalar = match SecretKey::from_slice(il_bytes) { + let il_scalar = match <[u8; 32]>::try_from(il_bytes) + .map_err(|_| secp256k1::Error::InvalidSecretKey) + .and_then(SecretKey::from_secret_bytes) + { Ok(key) => key, Err(_) => { return Err(Dip14Error::DerivationFailed( @@ -134,7 +135,7 @@ impl Dip14ExtendedPrivKey { // Add parent key to IL // In secp256k1, we perform scalar addition: child_key = parent_key + IL (mod n) // Convert IL to a Scalar for the tweak operation - let il_scalar_bytes = il_scalar.secret_bytes(); + let il_scalar_bytes = il_scalar.to_secret_bytes(); let tweak = Scalar::from_be_bytes(il_scalar_bytes).map_err(|_| { Dip14Error::DerivationFailed("Failed to convert IL to scalar".to_string()) })?; @@ -146,7 +147,7 @@ impl Dip14ExtendedPrivKey { .map_err(|e| Dip14Error::DerivationFailed(format!("Failed to add tweak: {}", e)))?; // Calculate parent fingerprint (first 4 bytes of parent pubkey hash160) - let parent_pubkey = PublicKey::from_secret_key(&secp, &self.private_key); + let parent_pubkey = PublicKey::from_secret_key(&self.private_key); // Use sha256 then ripemd160 to create hash160 let sha256_hash = sha256::Hash::hash(&parent_pubkey.serialize()); let parent_pubkey_hash = @@ -167,8 +168,8 @@ impl Dip14ExtendedPrivKey { } /// Get the extended public key - pub fn to_extended_pub_key(&self, secp: &Secp256k1) -> Dip14ExtendedPubKey { - let public_key = PublicKey::from_secret_key(secp, &self.private_key); + pub fn to_extended_pub_key(&self) -> Dip14ExtendedPubKey { + let public_key = PublicKey::from_secret_key(&self.private_key); Dip14ExtendedPubKey { network: self.network, diff --git a/packages/wasm-sdk/src/wallet/extended_derivation.rs b/packages/wasm-sdk/src/wallet/extended_derivation.rs index b8a929605df..8a1de840261 100644 --- a/packages/wasm-sdk/src/wallet/extended_derivation.rs +++ b/packages/wasm-sdk/src/wallet/extended_derivation.rs @@ -7,7 +7,6 @@ use crate::impl_wasm_serde_conversions; use crate::queries::utils::deserialize_required_query; use crate::sdk::WasmSdk; use dash_sdk::dpp::dashcore; -use dash_sdk::dpp::dashcore::secp256k1::Secp256k1; use dash_sdk::dpp::key_wallet::{bip32, DerivationPath, ExtendedPrivKey}; use serde::{Deserialize, Serialize}; use std::str::FromStr; @@ -109,20 +108,19 @@ fn derive_common_from_mnemonic( let derivation_path = DerivationPath::from_str(path) .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid derivation path: {}", e)))?; - let secp = Secp256k1::new(); let derived_key = master_key - .derive_priv(&secp, &derivation_path) + .derive_priv(&derivation_path) .map_err(|e| WasmSdkError::generic(format!("Failed to derive key: {}", e)))?; - let xpub = bip32::ExtendedPubKey::from_priv(&secp, &derived_key); + let xpub = bip32::ExtendedPubKey::from_priv(&derived_key); let private_key = dashcore::PrivateKey::new(derived_key.private_key, net); - let public_key = private_key.public_key(&secp); + let public_key = private_key.public_key(); let address = dashcore::Address::p2pkh(&public_key, net); Ok(CommonDerivation { path: path.to_string(), private_key_wif: private_key.to_wif(), - private_key_hex: hex::encode(private_key.inner.secret_bytes()), + private_key_hex: hex::encode(private_key.inner.to_secret_bytes()), public_key_hex: hex::encode(public_key.to_bytes()), address: address.to_string(), network: network.to_string(), diff --git a/packages/wasm-sdk/src/wallet/key_derivation.rs b/packages/wasm-sdk/src/wallet/key_derivation.rs index ad5db95a1b5..9423e329d74 100644 --- a/packages/wasm-sdk/src/wallet/key_derivation.rs +++ b/packages/wasm-sdk/src/wallet/key_derivation.rs @@ -8,7 +8,6 @@ use crate::queries::utils::{deserialize_query_with_default, deserialize_required use crate::sdk::WasmSdk; use bip39::{Language, Mnemonic}; use dash_sdk::dpp::dashcore; -use dash_sdk::dpp::dashcore::secp256k1::Secp256k1; use dash_sdk::dpp::key_wallet::bip32::{ ChildNumber, DerivationPath as BIP32DerivationPath, ExtendedPrivKey as BIP32ExtendedPrivKey, ExtendedPubKey as BIP32ExtendedPubKey, @@ -430,10 +429,7 @@ impl WasmSdk { .map_err(|e| WasmSdkError::generic(format!("Failed to create private key: {}", e)))?; // Get public key - use dash_sdk::dpp::dashcore::secp256k1::Secp256k1; - let secp = Secp256k1::new(); - - let public_key = private_key.public_key(&secp); + let public_key = private_key.public_key(); let public_key_bytes = public_key.inner.serialize(); // Get address let address = dashcore::Address::p2pkh(&public_key, net); @@ -483,7 +479,7 @@ impl WasmSdk { // Derive the key at the specified path let derived_key = master_key - .derive_priv(&dashcore::secp256k1::Secp256k1::new(), &derivation_path) + .derive_priv(&derivation_path) .map_err(|e| WasmSdkError::generic(format!("Failed to derive key: {}", e)))?; // In v0.40-dev, ExtendedPrivKey might have a different structure @@ -491,8 +487,7 @@ impl WasmSdk { let private_key = dashcore::PrivateKey::new(derived_key.private_key, net); // Get public key - let secp = dash_sdk::dpp::dashcore::secp256k1::Secp256k1::new(); - let public_key = private_key.public_key(&secp); + let public_key = private_key.public_key(); // Get address let address = dashcore::Address::p2pkh(&public_key, net); @@ -500,7 +495,7 @@ impl WasmSdk { Ok(PathDerivedKeyInfoWasm { path, private_key_wif: private_key.to_wif(), - private_key_hex: hex::encode(private_key.inner.secret_bytes()), + private_key_hex: hex::encode(private_key.inner.to_secret_bytes()), public_key: hex::encode(public_key.to_bytes()), address: address.to_string(), network, @@ -613,9 +608,8 @@ impl WasmSdk { // Build a one-step derivation path and derive let child_number: ChildNumber = ChildNumber::from(index); let path = BIP32DerivationPath::from(vec![child_number]); - let secp = Secp256k1::new(); let child_xpub = parent_xpub - .derive_pub(&secp, &path) + .derive_pub(&path) .map_err(|e| WasmSdkError::generic(format!("Failed to derive child key: {}", e)))?; Ok(child_xpub.to_string()) @@ -628,8 +622,7 @@ impl WasmSdk { let ext_prv = BIP32ExtendedPrivKey::from_str(xprv).map_err(|e| { WasmSdkError::invalid_argument(format!("Invalid extended private key: {}", e)) })?; - let secp = Secp256k1::new(); - let ext_pub = BIP32ExtendedPubKey::from_priv(&secp, &ext_prv); + let ext_pub = BIP32ExtendedPubKey::from_priv(&ext_prv); Ok(ext_pub.to_string()) } } diff --git a/packages/wasm-sdk/src/wallet/key_generation.rs b/packages/wasm-sdk/src/wallet/key_generation.rs index e1678d374fa..8830b5b13e3 100644 --- a/packages/wasm-sdk/src/wallet/key_generation.rs +++ b/packages/wasm-sdk/src/wallet/key_generation.rs @@ -6,7 +6,7 @@ use crate::error::WasmSdkError; use crate::impl_wasm_serde_conversions; use crate::sdk::WasmSdk; use dash_sdk::dpp::dashcore::hashes::{sha256, Hash}; -use dash_sdk::dpp::dashcore::secp256k1::{Secp256k1, SecretKey}; +use dash_sdk::dpp::dashcore::secp256k1::SecretKey; use dash_sdk::dpp::dashcore::{Address, Network, PrivateKey, PublicKey}; use serde::{Deserialize, Serialize}; use std::str::FromStr; @@ -67,14 +67,13 @@ impl WasmSdk { network: Network, network_label: &str, ) -> Result { - let secp = Secp256k1::new(); - let public_key = private_key.public_key(&secp); + let public_key = private_key.public_key(); let public_key_bytes = public_key.inner.serialize(); let address = Address::p2pkh(&public_key, network); Ok(KeyPair { private_key_wif: private_key.to_wif(), - private_key_hex: hex::encode(private_key.inner.secret_bytes()), + private_key_hex: hex::encode(private_key.inner.to_secret_bytes()), public_key: hex::encode(public_key_bytes), address: address.to_string(), network: network_label.to_string(), @@ -96,7 +95,7 @@ impl WasmSdk { .map_err(|e| WasmSdkError::generic(format!("Failed to create private key: {}", e)))?; // Ensure secret key is valid before building info - SecretKey::from_slice(&key_bytes) + SecretKey::from_secret_bytes(key_bytes) .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid secret key: {}", e)))?; let key_pair = Self::build_key_pair(&private_key, net, network_wasm.as_str())?; @@ -221,13 +220,12 @@ impl WasmSdk { let hash = sha256::Hash::hash(message_bytes); // Sign the hash - let secp = Secp256k1::new(); - let secret_key = SecretKey::from_slice(&private_key.inner.secret_bytes()) + let secret_key = SecretKey::from_secret_bytes(private_key.inner.to_secret_bytes()) .map_err(|e| WasmSdkError::invalid_argument(format!("Invalid secret key: {}", e)))?; let message_hash = dash_sdk::dpp::dashcore::secp256k1::Message::from_digest(hash.to_byte_array()); - let signature = secp.sign_ecdsa(&message_hash, &secret_key); + let signature = secret_key.sign_ecdsa(message_hash); Ok(hex::encode(signature.serialize_compact())) } From 7b63fc6ef4964cafd8e40113955c37b4aa276a57 Mon Sep 17 00:00:00 2001 From: infraclaw-dash Date: Tue, 29 Sep 2026 05:17:23 +0000 Subject: [PATCH 02/22] ci: bootstrap trusted publisher before feature runner adoption --- .github/workflows/runner-image-candidate.yml | 33 ++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/runner-image-candidate.yml diff --git a/.github/workflows/runner-image-candidate.yml b/.github/workflows/runner-image-candidate.yml new file mode 100644 index 00000000000..02df53334f2 --- /dev/null +++ b/.github/workflows/runner-image-candidate.yml @@ -0,0 +1,33 @@ +name: Runner image candidate + +on: + pull_request_target: + types: [opened, synchronize, reopened, ready_for_review, closed] + branches: [master, 'v*-dev', 'ci/*', 'chore/bump-rust-dashcore-secp-033'] + paths: ['.github/runner-requirements.json'] + +# This is trusted base-branch orchestration. Never check out PR code or select +# the control revision from PR data. Build/publish execute on separate hosted VMs. +permissions: + contents: read + pull-requests: read + actions: read + statuses: write + +concurrency: + group: ${{ github.event.action == 'closed' && format('runner-image-promote-{0}', github.event.pull_request.base.ref) || format('runner-image-pr-{0}', github.event.pull_request.number) }} + cancel-in-progress: ${{ github.event.action != 'closed' }} + +jobs: + image: + if: >- + (github.event.action != 'closed' && !github.event.pull_request.draft) + || (github.event.action == 'closed' && github.event.pull_request.merged) + uses: dashpay/dash-selfhosted-image/.github/workflows/platform-candidate.yml@7d901150bd3d0789d50c46f365b058f2f5f1f52d + with: + pull_request: ${{ github.event.pull_request.number }} + control_revision: 7d901150bd3d0789d50c46f365b058f2f5f1f52d + mode: ${{ github.event.action == 'closed' && 'promote' || 'candidate' }} + secrets: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} From bf382b1601f40ff7bcf98b58051b992dcec3cf53 Mon Sep 17 00:00:00 2001 From: pasta Date: Sun, 4 Oct 2026 22:32:23 -0500 Subject: [PATCH 03/22] fix(sdk)!: require Node.js 20 for the WASM packages secp256k1 0.33 draws randomness through rand 0.9 / getrandom 0.3, and key-wallet through getrandom 0.4. Their `wasm_js` backend reads only `globalThis.crypto.getRandomValues`; getrandom 0.2's `js` feature also fell back to Node's `require("crypto")`. Node.js exposes the WebCrypto global by default from v19, so on Node 18 even a deterministic call such as deriving a public key (secp256k1 rerandomizes its context afterwards) traps. Node 18 has been end-of-life since April 2025. Raise `engines.node` of wasm-sdk, wasm-dpp2 and js-evo-sdk to >=20, matching dashmate, and say why in the evo-sdk README and next to the getrandom features in rs-dpp. BREAKING CHANGE: @dashevo/wasm-sdk, @dashevo/wasm-dpp2 and @dashevo/evo-sdk require Node.js >= 20. Co-Authored-By: Claude Opus 5.5 (1M context) --- packages/js-evo-sdk/README.md | 2 +- packages/js-evo-sdk/package.json | 2 +- packages/rs-dpp/Cargo.toml | 3 +++ packages/wasm-dpp2/package.json | 2 +- packages/wasm-sdk/package.json | 2 +- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/js-evo-sdk/README.md b/packages/js-evo-sdk/README.md index 25fd8dfa1df..9b40e141920 100644 --- a/packages/js-evo-sdk/README.md +++ b/packages/js-evo-sdk/README.md @@ -32,7 +32,7 @@ Evo SDK provides a high-level, strongly-typed interface for interacting with [Da npm install @dashevo/evo-sdk ``` -The package is ESM-only (`"type": "module"`). In CommonJS projects, use dynamic `import()`. Requires Node.js >= 18.18. +The package is ESM-only (`"type": "module"`). In CommonJS projects, use dynamic `import()`. Requires Node.js >= 20 (the bundled WASM draws entropy from the global WebCrypto `crypto` object, which Node.js exposes by default from v19). ## Usage diff --git a/packages/js-evo-sdk/package.json b/packages/js-evo-sdk/package.json index e2218e4fb7a..5378177073d 100644 --- a/packages/js-evo-sdk/package.json +++ b/packages/js-evo-sdk/package.json @@ -15,7 +15,7 @@ }, "sideEffects": false, "engines": { - "node": ">=18.18" + "node": ">=20" }, "files": [ "dist/**", diff --git a/packages/rs-dpp/Cargo.toml b/packages/rs-dpp/Cargo.toml index 2e059d1d1e8..fbaaea5f073 100644 --- a/packages/rs-dpp/Cargo.toml +++ b/packages/rs-dpp/Cargo.toml @@ -93,6 +93,9 @@ log = { version = "0.4.27" } # secp256k1 0.33 pulls getrandom 0.3 (via rand 0.9) and key-wallet pulls # getrandom 0.4; on wasm32-unknown-unknown both refuse to build without their # `wasm_js` feature, so enable it here where every wasm crate picks it up. +# Unlike getrandom 0.2's `js` (which falls back to Node's `require("crypto")`), +# `wasm_js` only reads `globalThis.crypto.getRandomValues`, which Node.js +# exposes by default from v19; the JS packages therefore require Node >= 20. [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] getrandom_03 = { package = "getrandom", version = "0.3", features = ["wasm_js"] } getrandom_04 = { package = "getrandom", version = "0.4", features = ["wasm_js"] } diff --git a/packages/wasm-dpp2/package.json b/packages/wasm-dpp2/package.json index 9b730a1f055..4dd99a705be 100644 --- a/packages/wasm-dpp2/package.json +++ b/packages/wasm-dpp2/package.json @@ -25,7 +25,7 @@ ], "sideEffects": false, "engines": { - "node": ">=18.18" + "node": ">=20" }, "scripts": { "build": "./scripts/build.sh && node ./scripts/bundle.cjs", diff --git a/packages/wasm-sdk/package.json b/packages/wasm-sdk/package.json index ee6ae546636..5d0325901e8 100644 --- a/packages/wasm-sdk/package.json +++ b/packages/wasm-sdk/package.json @@ -25,7 +25,7 @@ ], "sideEffects": false, "engines": { - "node": ">=18.18" + "node": ">=20" }, "scripts": { "build": "./scripts/build.sh && node ./scripts/bundle.cjs", From 1b823095b78363179da4d067c1e7f902d84d5ee0 Mon Sep 17 00:00:00 2001 From: pasta Date: Mon, 5 Oct 2026 00:43:24 -0500 Subject: [PATCH 04/22] fix(sdk): move v5.0-dev's encrypted-for and moderation-charter code to secp256k1 0.33 Code that landed on v5.0-dev after this bump was first written (encrypted_for in rs-sdk and wasm-sdk, moderation charter requests) still used the secp256k1 0.30 / rand 0.8 API. Migrate it the same way as the rest of the bump: drop the `Secp256k1` context from `PublicKey::from_secret_key`, `SecretKey::from_slice` -> `from_secret_bytes`, `StdRng::from_entropy` -> `from_os_rng`, and `Bytes32::random_with_rng` -> `Bytes32::new(rng.random())` since platform-value's helper takes a rand 0.8 `StdRng`. Co-Authored-By: Claude Opus 5.5 (1M context) --- packages/rs-sdk/src/platform/encrypted_for.rs | 7 +++---- packages/rs-sdk/src/platform/encrypted_for/tests.rs | 4 ++-- .../src/platform/moderation_charters/requests.rs | 10 +++++----- packages/wasm-sdk/src/encrypted_for.rs | 6 ++---- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/packages/rs-sdk/src/platform/encrypted_for.rs b/packages/rs-sdk/src/platform/encrypted_for.rs index 6c778b8885b..2d8af5f5e93 100644 --- a/packages/rs-sdk/src/platform/encrypted_for.rs +++ b/packages/rs-sdk/src/platform/encrypted_for.rs @@ -28,7 +28,7 @@ use dpp::dashcore::secp256k1::rand::rngs::StdRng; use dpp::dashcore::secp256k1::rand::{RngCore, SeedableRng}; -use dpp::dashcore::secp256k1::{PublicKey, Secp256k1, SecretKey}; +use dpp::dashcore::secp256k1::{PublicKey, SecretKey}; use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; use dpp::data_contract::document_type::{ DocumentPropertyReferenceTarget, DocumentPropertyType, DocumentTypeRef, EncryptedFor, @@ -235,7 +235,7 @@ pub fn encrypt_property( fn random_iv() -> [u8; AES_CBC_IV_LENGTH] { let mut iv = [0u8; AES_CBC_IV_LENGTH]; - StdRng::from_entropy().fill_bytes(&mut iv); + StdRng::from_os_rng().fill_bytes(&mut iv); iv } @@ -425,8 +425,7 @@ fn select_declared_keys<'a>( let contract_id = document_type.data_contract_id(); let sender_requirements = key_requirements_naming(document_type, &declaration.sender_key); - let sender_public_key = - PublicKey::from_secret_key(&Secp256k1::signing_only(), sender_private_key); + let sender_public_key = PublicKey::from_secret_key(sender_private_key); let sender_public_key_bytes = sender_public_key.serialize(); let no_sender_key = |reason: String| EncryptedForError::NoSuitableKey { identity_id: sender.id(), diff --git a/packages/rs-sdk/src/platform/encrypted_for/tests.rs b/packages/rs-sdk/src/platform/encrypted_for/tests.rs index 5660550e359..8f88a83288b 100644 --- a/packages/rs-sdk/src/platform/encrypted_for/tests.rs +++ b/packages/rs-sdk/src/platform/encrypted_for/tests.rs @@ -34,8 +34,8 @@ const DASHPAY_VECTOR_HEX: &str = "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a864d1b3807cf80 b97cd32a5e36309f3bf9535c519b1b32b2f206696ec6d0e244a2e182fceaa750"; fn key_pair(scalar: u8) -> (SecretKey, PublicKey) { - let secret_key = SecretKey::from_slice(&[scalar; 32]).expect("a valid scalar"); - let public_key = PublicKey::from_secret_key(&Secp256k1::signing_only(), &secret_key); + let secret_key = SecretKey::from_secret_bytes([scalar; 32]).expect("a valid scalar"); + let public_key = PublicKey::from_secret_key(&secret_key); (secret_key, public_key) } diff --git a/packages/rs-sdk/src/platform/moderation_charters/requests.rs b/packages/rs-sdk/src/platform/moderation_charters/requests.rs index a51d8402385..9af9df68a99 100644 --- a/packages/rs-sdk/src/platform/moderation_charters/requests.rs +++ b/packages/rs-sdk/src/platform/moderation_charters/requests.rs @@ -4,7 +4,7 @@ use crate::platform::encrypted_for::encrypt_property_for; use crate::platform::{DataContract, Document, Fetch, Identity}; use crate::{Error, Sdk}; use dpp::dashcore::secp256k1::rand::rngs::StdRng; -use dpp::dashcore::secp256k1::rand::SeedableRng; +use dpp::dashcore::secp256k1::rand::{Rng, SeedableRng}; use dpp::dashcore::secp256k1::SecretKey; use dpp::data_contract::accessors::v0::DataContractV0Getters; use dpp::data_contract::document_type::methods::DocumentTypeBasicMethods; @@ -240,14 +240,14 @@ impl Sdk { } fn fresh_entropy() -> Bytes32 { - Bytes32::random_with_rng(&mut StdRng::from_entropy()) + Bytes32::new(StdRng::from_os_rng().random()) } #[cfg(test)] mod tests { use super::*; use crate::platform::encrypted_for::{decrypt_property, EncryptedPropertyEnvelope}; - use dpp::dashcore::secp256k1::{PublicKey, Secp256k1}; + use dpp::dashcore::secp256k1::PublicKey; use dpp::data_contract::document_type::property_constraints::DocumentSystemValues; use dpp::data_contract::validate_document::DataContractDocumentValidationMethodsV0; use dpp::identity::contract_bounds::ContractBounds; @@ -260,8 +260,8 @@ mod tests { use dpp::version::PlatformVersion; fn key_pair(scalar: u8) -> (SecretKey, PublicKey) { - let secret_key = SecretKey::from_slice(&[scalar; 32]).expect("a valid scalar"); - let public_key = PublicKey::from_secret_key(&Secp256k1::signing_only(), &secret_key); + let secret_key = SecretKey::from_secret_bytes([scalar; 32]).expect("a valid scalar"); + let public_key = PublicKey::from_secret_key(&secret_key); (secret_key, public_key) } diff --git a/packages/wasm-sdk/src/encrypted_for.rs b/packages/wasm-sdk/src/encrypted_for.rs index 45e6f3575cc..5e86489b3c7 100644 --- a/packages/wasm-sdk/src/encrypted_for.rs +++ b/packages/wasm-sdk/src/encrypted_for.rs @@ -5,7 +5,7 @@ use crate::error::WasmSdkError; use crate::sdk::WasmSdk; -use dash_sdk::dpp::dashcore::secp256k1::{PublicKey, Secp256k1, SecretKey}; +use dash_sdk::dpp::dashcore::secp256k1::{PublicKey, SecretKey}; use dash_sdk::dpp::data_contract::accessors::v0::DataContractV0Getters; use dash_sdk::dpp::data_contract::document_type::methods::DocumentTypeV0Methods; use dash_sdk::dpp::data_contract::document_type::DocumentTypeRef; @@ -215,9 +215,7 @@ impl WasmSdk { // A private key that is not the sender key's would write a message nobody can read let sender_public_key = secp256k1_public_key(&sender_key, "senderKey")?; - if PublicKey::from_secret_key(&Secp256k1::signing_only(), &sender_private_key) - != sender_public_key - { + if PublicKey::from_secret_key(&sender_private_key) != sender_public_key { return Err(WasmSdkError::invalid_argument( "senderPrivateKey is not the private half of senderKey", )); From efa487d983210b989bef3e14bb1eee20a33f38cc Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Tue, 6 Oct 2026 14:02:11 +0000 Subject: [PATCH 05/22] fix(sdk)!: require Node 20 for legacy WASM-DPP consumers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Declare the minimum Node version in wasm-dpp and dash and align their README requirements with the existing WASM SDK packages. Validated the production WASM-DPP loader and ECDSA signing on Node 20.20.2 without the test bootstrap's WebCrypto shim. Co-Authored-By: Codex 🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent --- packages/js-dash-sdk/README.md | 4 ++-- packages/js-dash-sdk/package.json | 3 +++ packages/wasm-dpp/README.md | 2 ++ packages/wasm-dpp/package.json | 3 +++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/js-dash-sdk/README.md b/packages/js-dash-sdk/README.md index e8d7e0d8870..c7d26350a9b 100644 --- a/packages/js-dash-sdk/README.md +++ b/packages/js-dash-sdk/README.md @@ -29,7 +29,7 @@ Dash library for JavaScript/TypeScript ecosystem (Wallet, DAPI, Primitives, BLS, | Wallet / key management | 🟢 Full SPV wallet via wallet-lib | 🟡 Key derivation utilities only (BIP44, WIF, signing) | | Module format | CommonJS + UMD bundle | ESM only | | Browser CDN | 🟢 unpkg (UMD) | 🟢 esm.sh (ESM) | -| Node.js requirement | Any | >= 18.18 | +| Node.js requirement | >= 20 | >= 20 | | TypeScript | 🟢 | 🟢 | **Warning: This SDK should only be used in production when connected to trusted nodes. Although it @@ -52,7 +52,7 @@ Dash library provides access via [DAPI](https://dashplatform.readme.io/docs/expl In order to use this library, you will need to add it to your project as a dependency. -Having [NodeJS](https://nodejs.org/) installed, just type : `npm install dash` in your terminal. +With [Node.js](https://nodejs.org/) >= 20 installed, run `npm install dash` in your terminal. ```sh npm install dash diff --git a/packages/js-dash-sdk/package.json b/packages/js-dash-sdk/package.json index 0e610d0f278..0e82751793a 100644 --- a/packages/js-dash-sdk/package.json +++ b/packages/js-dash-sdk/package.json @@ -6,6 +6,9 @@ "unpkg": "dist/dash.min.js", "browser": "dist/dash.min.js", "types": "build/index.d.ts", + "engines": { + "node": ">=20" + }, "scripts": { "start:dev": "nodemon --exec 'yarn run build && yarn run test:unit'", "start:ts": "tsc -p tsconfig.build.json --watch", diff --git a/packages/wasm-dpp/README.md b/packages/wasm-dpp/README.md index c1da995eba0..549458d9ca9 100644 --- a/packages/wasm-dpp/README.md +++ b/packages/wasm-dpp/README.md @@ -7,6 +7,8 @@ The WASM JavaScript binding of the Rust implementation of the [Dash Platform Protocol](https://dashplatform.readme.io/docs/explanation-platform-protocol) +Requires Node.js >= 20 when used in Node.js. + ### THIS IS A DEV VERSION, NOT INTENDED FOR A PRODUCTION USAGE JUST YET ## Dev environment diff --git a/packages/wasm-dpp/package.json b/packages/wasm-dpp/package.json index afad8405fa9..b57c2cd90a8 100644 --- a/packages/wasm-dpp/package.json +++ b/packages/wasm-dpp/package.json @@ -4,6 +4,9 @@ "description": "The JavaScript implementation of the Dash Platform Protocol", "main": "dist/index.js", "types": "dist/index.d.ts", + "engines": { + "node": ">=20" + }, "scripts": { "build": "yarn run build:wasm && yarn run build:js", "build:wasm": "yarn exec scripts/build-wasm.sh", From 327153679b58feb1073a379224e5cd5c069232cd Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Tue, 6 Oct 2026 14:10:22 +0000 Subject: [PATCH 06/22] chore: move Node runtime requirements to a separate PR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restore package metadata and documentation from v5.1-dev so the Node minimum can be raised directly to 22 in a separately reviewable PR. The WASM entropy backend remains part of the rust-dashcore migration. Co-Authored-By: Codex 🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent --- packages/js-dash-sdk/README.md | 4 ++-- packages/js-dash-sdk/package.json | 3 --- packages/js-evo-sdk/README.md | 2 +- packages/js-evo-sdk/package.json | 2 +- packages/wasm-dpp/README.md | 2 -- packages/wasm-dpp/package.json | 3 --- packages/wasm-dpp2/package.json | 2 +- packages/wasm-sdk/package.json | 2 +- 8 files changed, 6 insertions(+), 14 deletions(-) diff --git a/packages/js-dash-sdk/README.md b/packages/js-dash-sdk/README.md index c7d26350a9b..e8d7e0d8870 100644 --- a/packages/js-dash-sdk/README.md +++ b/packages/js-dash-sdk/README.md @@ -29,7 +29,7 @@ Dash library for JavaScript/TypeScript ecosystem (Wallet, DAPI, Primitives, BLS, | Wallet / key management | 🟢 Full SPV wallet via wallet-lib | 🟡 Key derivation utilities only (BIP44, WIF, signing) | | Module format | CommonJS + UMD bundle | ESM only | | Browser CDN | 🟢 unpkg (UMD) | 🟢 esm.sh (ESM) | -| Node.js requirement | >= 20 | >= 20 | +| Node.js requirement | Any | >= 18.18 | | TypeScript | 🟢 | 🟢 | **Warning: This SDK should only be used in production when connected to trusted nodes. Although it @@ -52,7 +52,7 @@ Dash library provides access via [DAPI](https://dashplatform.readme.io/docs/expl In order to use this library, you will need to add it to your project as a dependency. -With [Node.js](https://nodejs.org/) >= 20 installed, run `npm install dash` in your terminal. +Having [NodeJS](https://nodejs.org/) installed, just type : `npm install dash` in your terminal. ```sh npm install dash diff --git a/packages/js-dash-sdk/package.json b/packages/js-dash-sdk/package.json index 0e82751793a..0e610d0f278 100644 --- a/packages/js-dash-sdk/package.json +++ b/packages/js-dash-sdk/package.json @@ -6,9 +6,6 @@ "unpkg": "dist/dash.min.js", "browser": "dist/dash.min.js", "types": "build/index.d.ts", - "engines": { - "node": ">=20" - }, "scripts": { "start:dev": "nodemon --exec 'yarn run build && yarn run test:unit'", "start:ts": "tsc -p tsconfig.build.json --watch", diff --git a/packages/js-evo-sdk/README.md b/packages/js-evo-sdk/README.md index 9b40e141920..25fd8dfa1df 100644 --- a/packages/js-evo-sdk/README.md +++ b/packages/js-evo-sdk/README.md @@ -32,7 +32,7 @@ Evo SDK provides a high-level, strongly-typed interface for interacting with [Da npm install @dashevo/evo-sdk ``` -The package is ESM-only (`"type": "module"`). In CommonJS projects, use dynamic `import()`. Requires Node.js >= 20 (the bundled WASM draws entropy from the global WebCrypto `crypto` object, which Node.js exposes by default from v19). +The package is ESM-only (`"type": "module"`). In CommonJS projects, use dynamic `import()`. Requires Node.js >= 18.18. ## Usage diff --git a/packages/js-evo-sdk/package.json b/packages/js-evo-sdk/package.json index e4b3116b168..9c2b19985c0 100644 --- a/packages/js-evo-sdk/package.json +++ b/packages/js-evo-sdk/package.json @@ -15,7 +15,7 @@ }, "sideEffects": false, "engines": { - "node": ">=20" + "node": ">=18.18" }, "files": [ "dist/**", diff --git a/packages/wasm-dpp/README.md b/packages/wasm-dpp/README.md index 549458d9ca9..c1da995eba0 100644 --- a/packages/wasm-dpp/README.md +++ b/packages/wasm-dpp/README.md @@ -7,8 +7,6 @@ The WASM JavaScript binding of the Rust implementation of the [Dash Platform Protocol](https://dashplatform.readme.io/docs/explanation-platform-protocol) -Requires Node.js >= 20 when used in Node.js. - ### THIS IS A DEV VERSION, NOT INTENDED FOR A PRODUCTION USAGE JUST YET ## Dev environment diff --git a/packages/wasm-dpp/package.json b/packages/wasm-dpp/package.json index b57c2cd90a8..afad8405fa9 100644 --- a/packages/wasm-dpp/package.json +++ b/packages/wasm-dpp/package.json @@ -4,9 +4,6 @@ "description": "The JavaScript implementation of the Dash Platform Protocol", "main": "dist/index.js", "types": "dist/index.d.ts", - "engines": { - "node": ">=20" - }, "scripts": { "build": "yarn run build:wasm && yarn run build:js", "build:wasm": "yarn exec scripts/build-wasm.sh", diff --git a/packages/wasm-dpp2/package.json b/packages/wasm-dpp2/package.json index 375a8ee7ea2..643ede05350 100644 --- a/packages/wasm-dpp2/package.json +++ b/packages/wasm-dpp2/package.json @@ -25,7 +25,7 @@ ], "sideEffects": false, "engines": { - "node": ">=20" + "node": ">=18.18" }, "scripts": { "build": "./scripts/build.sh && node ./scripts/bundle.cjs", diff --git a/packages/wasm-sdk/package.json b/packages/wasm-sdk/package.json index 2156e2528bd..c76b5decfbb 100644 --- a/packages/wasm-sdk/package.json +++ b/packages/wasm-sdk/package.json @@ -25,7 +25,7 @@ ], "sideEffects": false, "engines": { - "node": ">=20" + "node": ">=18.18" }, "scripts": { "build": "./scripts/build.sh && node ./scripts/bundle.cjs", From d9149f1ab590df9207e69666cb36435533eb987a Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Tue, 6 Oct 2026 14:49:06 +0000 Subject: [PATCH 07/22] chore(platform): pin rust-dashcore to merged dev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use 314f1060 across all workspace dependencies and lockfile sources. This merged dev revision excludes the separate unmerged spent-claim restoration API needed by PR #5150. Co-Authored-By: Codex 🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent --- Cargo.lock | 26 +++++++++++++------------- Cargo.toml | 16 ++++++++-------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 85d90c3748c..90b2dc07644 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1785,7 +1785,7 @@ dependencies = [ [[package]] name = "dash-network" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=870af146f7656b98e746d1a84f08c725c5af68da#870af146f7656b98e746d1a84f08c725c5af68da" +source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" dependencies = [ "cbindgen 0.29.4", "grovedb-bincode", @@ -1796,7 +1796,7 @@ dependencies = [ [[package]] name = "dash-network-seeds" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=870af146f7656b98e746d1a84f08c725c5af68da#870af146f7656b98e746d1a84f08c725c5af68da" +source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" dependencies = [ "dash-network", ] @@ -1922,7 +1922,7 @@ dependencies = [ [[package]] name = "dash-spv" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=870af146f7656b98e746d1a84f08c725c5af68da#870af146f7656b98e746d1a84f08c725c5af68da" +source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" dependencies = [ "async-trait", "chrono", @@ -1976,7 +1976,7 @@ dependencies = [ [[package]] name = "dashcore" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=870af146f7656b98e746d1a84f08c725c5af68da#870af146f7656b98e746d1a84f08c725c5af68da" +source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" dependencies = [ "anyhow", "base64 0.22.1", @@ -2001,7 +2001,7 @@ dependencies = [ [[package]] name = "dashcore-crypto" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=870af146f7656b98e746d1a84f08c725c5af68da#870af146f7656b98e746d1a84f08c725c5af68da" +source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" dependencies = [ "base58ck 0.1.100", "dash-network", @@ -2021,12 +2021,12 @@ dependencies = [ [[package]] name = "dashcore-private" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=870af146f7656b98e746d1a84f08c725c5af68da#870af146f7656b98e746d1a84f08c725c5af68da" +source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" [[package]] name = "dashcore-rpc" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=870af146f7656b98e746d1a84f08c725c5af68da#870af146f7656b98e746d1a84f08c725c5af68da" +source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" dependencies = [ "dashcore-rpc-json", "hex", @@ -2039,7 +2039,7 @@ dependencies = [ [[package]] name = "dashcore-rpc-json" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=870af146f7656b98e746d1a84f08c725c5af68da#870af146f7656b98e746d1a84f08c725c5af68da" +source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" dependencies = [ "dashcore", "grovedb-bincode", @@ -2054,7 +2054,7 @@ dependencies = [ [[package]] name = "dashcore_hashes" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=870af146f7656b98e746d1a84f08c725c5af68da#870af146f7656b98e746d1a84f08c725c5af68da" +source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" dependencies = [ "dashcore-private", "grovedb-bincode", @@ -3184,7 +3184,7 @@ dependencies = [ [[package]] name = "git-state" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=870af146f7656b98e746d1a84f08c725c5af68da#870af146f7656b98e746d1a84f08c725c5af68da" +source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" [[package]] name = "glob" @@ -4463,7 +4463,7 @@ dependencies = [ [[package]] name = "key-wallet" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=870af146f7656b98e746d1a84f08c725c5af68da#870af146f7656b98e746d1a84f08c725c5af68da" +source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" dependencies = [ "aes 0.8.4", "async-trait", @@ -4492,7 +4492,7 @@ dependencies = [ [[package]] name = "key-wallet-ffi" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=870af146f7656b98e746d1a84f08c725c5af68da#870af146f7656b98e746d1a84f08c725c5af68da" +source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" dependencies = [ "cbindgen 0.29.4", "dash-network", @@ -4508,7 +4508,7 @@ dependencies = [ [[package]] name = "key-wallet-manager" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=870af146f7656b98e746d1a84f08c725c5af68da#870af146f7656b98e746d1a84f08c725c5af68da" +source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" dependencies = [ "async-trait", "dashcore", diff --git a/Cargo.toml b/Cargo.toml index 07c3c880f3c..0849257b982 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,14 +65,14 @@ grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "9791d2772 grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "9791d277207998b7789c85b0745e99d37040a12e" } grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "9791d277207998b7789c85b0745e99d37040a12e" } grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "9791d277207998b7789c85b0745e99d37040a12e" } -dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "870af146f7656b98e746d1a84f08c725c5af68da" } -dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "870af146f7656b98e746d1a84f08c725c5af68da" } -dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "870af146f7656b98e746d1a84f08c725c5af68da" } -key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "870af146f7656b98e746d1a84f08c725c5af68da" } -key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "870af146f7656b98e746d1a84f08c725c5af68da" } -key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "870af146f7656b98e746d1a84f08c725c5af68da" } -dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "870af146f7656b98e746d1a84f08c725c5af68da" } -dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "870af146f7656b98e746d1a84f08c725c5af68da" } +dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "314f10600e35311f3d875a6e8740476ab6c79c0c" } +dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "314f10600e35311f3d875a6e8740476ab6c79c0c" } +dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "314f10600e35311f3d875a6e8740476ab6c79c0c" } +key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "314f10600e35311f3d875a6e8740476ab6c79c0c" } +key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "314f10600e35311f3d875a6e8740476ab6c79c0c" } +key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "314f10600e35311f3d875a6e8740476ab6c79c0c" } +dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "314f10600e35311f3d875a6e8740476ab6c79c0c" } +dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "314f10600e35311f3d875a6e8740476ab6c79c0c" } tokio-metrics = "0.5" [patch.crates-io] From c0b4b97f8a1a8626c2f51bc8d7baeab9c65e10da Mon Sep 17 00:00:00 2001 From: lklimek <842586+lklimek@users.noreply.github.com> Date: Tue, 6 Oct 2026 19:27:46 +0200 Subject: [PATCH 08/22] chore!: require Node.js >=22 for JS packages and >=24 for dashmate (#5310) --- .devcontainer/devcontainer-build.json | 2 +- .pnp.cjs | 18 +++++++++--------- ...ode-npm-20.19.30-e3d3d7af6e-4a25e5cbcd.zip | Bin 446970 -> 0 bytes ...node-npm-22.20.5-998a48d6f3-a223a73e00.zip | Bin 0 -> 478402 bytes Dockerfile | 9 ++++++--- book/src/evo-sdk/getting-started.md | 2 +- book/src/evo-sdk/networks-and-environments.md | 2 +- book/src/evo-sdk/overview.md | 2 +- book/src/getting-started.md | 2 +- packages/bench-suite/package.json | 3 +++ packages/dapi/package.json | 3 +++ packages/dashmate/docs/installation.md | 2 +- packages/dashmate/package.json | 2 +- packages/js-dapi-client/package.json | 2 +- packages/js-dash-sdk/README.md | 4 ++-- packages/js-dash-sdk/package.json | 5 ++++- packages/js-evo-sdk/README.md | 2 +- packages/js-evo-sdk/package.json | 4 ++-- packages/platform-test-suite/package.json | 3 +++ packages/rs-dpp/Cargo.toml | 2 +- packages/swift-sdk/run_integration_tests.sh | 6 +++--- packages/wallet-lib/package.json | 3 +++ packages/wasm-dpp/README.md | 2 ++ packages/wasm-dpp/package.json | 5 ++++- packages/wasm-dpp2/package.json | 4 ++-- packages/wasm-drive-verify/package.json | 3 +++ packages/wasm-sdk/README.md | 2 +- packages/wasm-sdk/package.json | 4 ++-- qa-contract/README.md | 2 +- qa-contract/package.json | 2 +- scripts/setup-ai-agent-environment.sh | 8 ++++---- yarn.lock | 18 +++++++++--------- 32 files changed, 77 insertions(+), 51 deletions(-) delete mode 100644 .yarn/cache/@types-node-npm-20.19.30-e3d3d7af6e-4a25e5cbcd.zip create mode 100644 .yarn/cache/@types-node-npm-22.20.5-998a48d6f3-a223a73e00.zip diff --git a/.devcontainer/devcontainer-build.json b/.devcontainer/devcontainer-build.json index df88d659340..f13e5946563 100644 --- a/.devcontainer/devcontainer-build.json +++ b/.devcontainer/devcontainer-build.json @@ -59,7 +59,7 @@ }, "ghcr.io/devcontainers/features/github-cli:1": {}, "ghcr.io/devcontainers/features/node:1": { - "version": 20, + "version": 24, "installYarnUsingApt": false }, "ghcr.io/eitsupi/devcontainer-features/jq-likes:2": {}, diff --git a/.pnp.cjs b/.pnp.cjs index c1f40457d98..8d3d5332a00 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -2831,7 +2831,7 @@ const RAW_RUNTIME_STATE = ["@dashevo/wasm-sdk", "workspace:packages/wasm-sdk"],\ ["@types/chai", "npm:4.3.20"],\ ["@types/mocha", "npm:10.0.10"],\ - ["@types/node", "npm:20.19.30"],\ + ["@types/node", "npm:22.20.5"],\ ["@types/sinon", "npm:9.0.11"],\ ["@types/sinon-chai", "npm:3.2.5"],\ ["assert", "npm:2.0.0"],\ @@ -3158,7 +3158,7 @@ const RAW_RUNTIME_STATE = ["@dashevo/dpns-contract", "workspace:packages/dpns-contract"],\ ["@dashevo/wasm-dpp", "workspace:packages/wasm-dpp"],\ ["@types/bs58", "npm:4.0.4"],\ - ["@types/node", "npm:20.19.30"],\ + ["@types/node", "npm:22.20.5"],\ ["@yarnpkg/pnpify", "npm:4.0.0-rc.42"],\ ["ajv", "npm:8.18.0"],\ ["assert", "npm:2.0.0"],\ @@ -3211,7 +3211,7 @@ const RAW_RUNTIME_STATE = ["@dashevo/wasm-dpp2", "workspace:packages/wasm-dpp2"],\ ["@types/chai", "npm:4.3.20"],\ ["@types/mocha", "npm:10.0.10"],\ - ["@types/node", "npm:20.19.30"],\ + ["@types/node", "npm:22.20.5"],\ ["assert", "npm:2.0.0"],\ ["bs58", "npm:4.0.1"],\ ["buffer", "npm:6.0.3"],\ @@ -3249,7 +3249,7 @@ const RAW_RUNTIME_STATE = ["@dashevo/wasm-sdk", "workspace:packages/wasm-sdk"],\ ["@types/chai", "npm:4.3.20"],\ ["@types/mocha", "npm:10.0.10"],\ - ["@types/node", "npm:20.19.30"],\ + ["@types/node", "npm:22.20.5"],\ ["assert", "npm:2.0.0"],\ ["buffer", "npm:6.0.3"],\ ["chai", "npm:4.3.10"],\ @@ -5263,10 +5263,10 @@ const RAW_RUNTIME_STATE = ],\ "linkType": "HARD"\ }],\ - ["npm:20.19.30", {\ - "packageLocation": "./.yarn/cache/@types-node-npm-20.19.30-e3d3d7af6e-4a25e5cbcd.zip/node_modules/@types/node/",\ + ["npm:22.20.5", {\ + "packageLocation": "./.yarn/cache/@types-node-npm-22.20.5-998a48d6f3-a223a73e00.zip/node_modules/@types/node/",\ "packageDependencies": [\ - ["@types/node", "npm:20.19.30"],\ + ["@types/node", "npm:22.20.5"],\ ["undici-types", "npm:6.21.0"]\ ],\ "linkType": "HARD"\ @@ -9640,7 +9640,7 @@ const RAW_RUNTIME_STATE = ["@types/chai", "npm:4.3.20"],\ ["@types/dirty-chai", "npm:2.0.2"],\ ["@types/mocha", "npm:10.0.10"],\ - ["@types/node", "npm:20.19.30"],\ + ["@types/node", "npm:22.20.5"],\ ["@types/sinon", "npm:9.0.11"],\ ["@types/sinon-chai", "npm:3.2.5"],\ ["@yarnpkg/pnpify", "npm:4.0.0-rc.42"],\ @@ -21613,7 +21613,7 @@ const RAW_RUNTIME_STATE = ["@tsconfig/node12", "npm:1.0.9"],\ ["@tsconfig/node14", "npm:1.0.1"],\ ["@tsconfig/node16", "npm:1.0.2"],\ - ["@types/node", "npm:20.19.30"],\ + ["@types/node", "npm:22.20.5"],\ ["@types/swc__core", null],\ ["@types/swc__wasm", null],\ ["@types/typescript", null],\ diff --git a/.yarn/cache/@types-node-npm-20.19.30-e3d3d7af6e-4a25e5cbcd.zip b/.yarn/cache/@types-node-npm-20.19.30-e3d3d7af6e-4a25e5cbcd.zip deleted file mode 100644 index a2ad60a210a193841c594ad8525c53ffe56a7ad6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 446970 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

9GKusc0tyGihm$j)`Vv5*XJ7`N~wW`Iv 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
    +j-`eNwd1CzuBaE3CG<^ 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%0A;G@{tDjZIK$Jeo*Y@GulSVIY!iaMnTo z2P+42Rkx+6V|*iSVRqX3beJU^V?s_n_cxIq2|#XdTAl(noD5fYKBv8dKEfw}1mq}x zG@V`xK+njUk1Ja?i)gC#ZtL02!M?|$JRzHv(@UJ(4NIjq?zmGbgYq4TfkGbUcuXtbyD`?OKz(Vwd-bWM zcEMR-&Zca`)l7&81P%};PZvwAmAr97rad*pWi7ZkwOSy4CZ8T=g5T=KIr{eOPJHLNPK+BSSS3 zbv9)lO+wroEl*1)nVEY@;j@T)#d^uNKTFwAMjLflFjm6GF#=_c=H z2W&%idO}Os_q%_`#vdh)$p&9i;wK;J{TYjm@>a8PX5n~>C(Y6{L_O!Q1OkEJJy#n< zRNp~<^@IHh%B{sA2bN9O5fG$ywXy{p(z+yw!W5T(nURm6oTkzddq)YDQkq%F91q8C z|8nn~@c-NY&|(!WprbfEqBCz`RM)}hV}%0duDMj(3M)Jh^l4~Ixg4`!8N(IIy>1kW zPD}uIJ<^)EBp`lKJdKEbcBp)RKB_)oR&EktlPPmRpih4x`|T=;;-Y`Ha$5T+6?<0B z65-%MzN!Gi!Ko>28LoJ;dKL??C>VjyCM10um=oK6Pr*x?n3r z`#l6m2!ZvT{_uQVf$ohWeq$NN`bAXVvlaYm%SkPhcmFysQ^S(r13i4J3JSBI&+b>+7#m{+oiqv6u-?*=2H!q@94T2J^5{A2NVlu+=>ZzBUf zd8H6$oiSC=x-{3lW!!!)T|SN1SjG%#%xHWMf^xEv2pR^q6Sec_{HIJTQ%TG!Tw?iA253xbp zcIExcz8&@TydeQ^TuCRnA0g_!}Nk+*J2#-x`^m;ek*fB%f_dNAR7HM5iO(NSS! z> zdvnW~MQ#Q|wo6*rB(LL#m6FyRhuR-+ozFqH8#@CAo5yIj$2^alyP~&-FD)#?DI~Ka z;5bl)8Hn1JdSR*FvXwPpI?$aC#A-jZ_y+t2JMUoOD9xPUEyo*T%N`P=%=tj+9Q!7H zvP2h+jWGaRPA0O4ifk@wQ)CRiM}k&Rr0JEA6kMs$*B z`@~QSGaOq`358@`RCLx6I-Fd}^lO?#nR@Zd=!ztv-5W-~WXRR{muxJzFF~7!qAyXc zeUCKk3O$vx)G_ZY&!&E)K{0a9W>8p66#&;jy4KDZE{aD)v^oe{v0Ie~+N}noKLJY) z^xB7_S5BJ{cd^t;jsh|6dY3Ihr;=;kd*lG0&*$z)AhUFa)zRbqlIKw~~g zC`i`m-U5|rG>|Kfc%I)WS4iAgk@)e1y?*g(E+LKMsxV84Oog+RbEbzZELXFcJEqyP zB77L|WG-MsHy+mTuV5(#x?*n`w}W;MhI+IwcPf$rqmHo==fcrD0DbqAgv>hI0k%WT85YG@j(&zj&8 z#FmvfJ1}UN51wf-d=cV;rP>lDtR<~spGDXf+K1rS-XNk|14t%3S;llU$uQ8a(84~m zxj9%e^S>!S4Bs2^jOlYI@)%u9LUPb}6HGrzIgEu4JyJ(#>ix-hFXO-|qGtx=H#hMl z>mcTDpq2SacUlK3Rt<=rk6G}Osr6D*Y2e979qltcWd!68<76d}ezgNIuH>adxEEgs zGpqE3PTEwpxLqz-P*Y{c;uQlc~TLd=j z@}AfNiQ{p%QsesswgBKM6unAhyvqmjoR$#&!|85MK{Svj@-b-+NGP|NJILQ$Si=HI zdqH6pd)%?7J0YN#*F+ouvDTjeg1dIoHQdpyuM9_-C;X3TFMC>n@M0(dQ&p~VqKo4` zd88wB-h31d8|9wqZw847|Eb!_4}3ql!`6X|6mZd_`^X%qO%x6IvX>QRIuJJH>zKbb z0ABLG-Rv3bBJgyybZ774IC){Wrv8+5|0=F%Xgf%_(0LT!`NRfniu+gnS4*rf>p=xj zgc25AVivyW_-vZLYn=kb6c^d^J&eb1xGtFx`0#As8TUezyr=Maq!R9(S#=p4cT}Ld z;av6j?UCM#%%Z-6#Cv_aa%CGSv#*zo=5BznyFy^xv;x9oqqlO;NytEFWvksJZ=MpTfY*G z?}U6rQ-6y>hz1ePEZ^QTIddM8UcsJt3uy}DAS&rIK(|<>+zmHE!LY!y>rjGhz^LJz z%i@Q}e+UTbIj@)o{sb7G#r^PrGTx@_>fP3tGnE7!u08Scx<1I}#*b{vV8<*K>4aTJ~AuMO*! zSE@FGib(=Ke>D}G1u5gA#0qa*Is7sMFaCQ|pOB44*@<=|uQH?TT`dg^rzh^z;n_S1 zB~*IUR%G)^ZhlZv8`mVi#(eY(I=lc02d>c_zixa5V;9N@5L!T|`o0A@rAz7K}6* z0mre`I!DeUOz2lIqskO`&?cQhKVNlwW5ah+SB9!UmzyCDexJ~_Xz-n%g%MUvfZg1V zjoNPyW82|`>DHR1Ffa!eZ!l%ttNml%4^S7`J@CY14S=E=Yd5It6Qr*Tv7p3?oJ2b& zJgKd(3eXs=)6+B_9q)R{R6MUaiL#im`;a4#uWT_i+BLD{>RmI{A;sFIs`~|YJC4PB zQf-aNBzgHHvjoI`X2Tlw!J~}lzWC8JNQZ&xrs0_zOncdUMNTGI=%uB3FIMvcoW}GW zx9_{fR3(BL(tR?xTwPUOe1Dv%3 zM|)Bv|B(qE&$Oyto7w?N%b;Ec5i1kv?sJ*Ruh%IgG~ zEnCoh1Ph=Ro`M97G+%mG23$%ftG>3=DXVUWUI40U6n_1KTNW@Iy z!}J&Z0iUh$)@|U5IKCJ`f4=1Wy*9yrAGdTc$LR$o9;FmK7*pDQ>fULuZaQ~CCb^(o z2Mh@d#}*$~Ho_v@2*AMhg|1L@uw_*Fhay8TRt7E=<_u-bSQGXy0n9>7@$S)WxRVYm zSdf`$p|#EFNTDOaL`2ksA+NE}@P`trmmy(V-Ra{(qzsDs}U#)b%Tqy=l@Q38Tp!X(0p zREJzP#NgUYfT7N@ZMXE6kB;6LRiLgF1{e5@?%|wuMzv*O6;V4|C?=Cb;#D8n8RpSz zwYfy=IVtW>7Z>D{mZr#@kPcxu9lBXur;WM2N)-L^*$qbVixT&1$(SvZwT#Q#J@5|k zDqNW?dLsNrhxudysYFwraHWnVbd=?E^s5WRSe{h2ZJ9Q8;NJz2R2}fviyU9IN1x8IbF>X46w#(N08r*S{b$%yaUUTzX4UEccL9d@g=0b5x=yUdkBj2PS64 zfYyn`N>wkv($?ZxCWd<-QVZ4`@hX#BIA1E|{zEAqm$EI`2n;GOD?Qc{R9asT40NW0 zkGg<1-1Q@i&<7j9v_%Olc$NHnS!f(ffZr*@t^P05D3+vt%SAub>pCyHR?*#qEdX#&Vg_1H@!Mq47&ruI(MDN(E=HMq z0IVXg|If6~2|bi^YbHS@)yvqct0MHI_3#Myf69T+Q%SAC9SF87{t9qj#+p z2rhglcs|??Tm^R6_M%U>fAdoZ`~WA^X$S3qsp&Ax229s|&z!ad{lGfQMAhdsTYZd) zK))?EVYUVsa<~rpfo4IWg&-`+M0{|_n5?uSUGpTU#xc`Ub)P3B8}T5v%NS%cx6E!r zJ_F@ePpv&~CQ2|X0VsvS;E+bnz4cd)p?~BJg1tv%5sn?L3231c^~9SD%f|hPz#leT zQJ>@^KKPM9w23>3%0K7#p&YYhw&TZJzmTt8zlMlM2R2M&V977<8j*z$EO-1~-rw4P zxZXY<%Ig(-KF(dIyv$*udlk1al%btIm(RxSsW8uESAgbghzU??kIl!8_d`RRoP{WUu4UtAe1DL1)Jz%`#+EM&{=8iwd|?-UCNG z;_};3q`1H`MJq3kykfzRq9x)aE;9!hUf@{-Yv44>waCN9ZCT^e@d}-$(o#E?8abCi zt>ljU<|dt_#Lvow?>WVE;L>x|a0c^S{SezP}ET z%b{8VekQ2^5dzrI6w2+}Jh|sff>}4p&go@N9u+Py@nkW&WU-2eN~Bzt?eM4|-TpR0 zRd$eKu5P$N`DiyaTX^)NZKf(V?S8zWstx+INrsO$Dyqgt<;+kbAb$-t=nTI8Zn0h0 zU@#7YY~*T&nbL8bJ)k>F^kSNS)r#LdVAs3UYd-yY_RU_YneE)3Xh|bo06R3X?JBKx z;M^J4AhoeK`k0)4sa>8}-&lTkPu4&8?YTZ?BfR;gXfhsam(l<3jqBc!rJYTD>CkQr zU7ZV`G4Srq+UAI(mCrnN(rE(UA)?=JVp|VBgtMMwY(if*&O2|xVOgw|Y~!LrHjz4&27|gXsp#{{CjS3iE01K1_O?xlNuKScOw{`^vZ55u5iVDIEfxD zCH4>V!pw0DmbIfiK}>5C_lLNSUiJgYmFdnPbL0Do=~qH>%ai2QkSoN9$z%O{q<)-kc6Yok zp%TeevzCCzr|iJCxAUQPdVoAcPigjNU!R1qPfMNDaN5o8gBEmzlnsCE%=c>e4Jhir zn7oaxq@pyagjs*+o`a-Iu6KnP+dal@et*I-X$Fc@6DB3I^BXayA1Dq7jv~^)qEpzF zp8To)eVga9PQuu`KL`iyZEe;()7S0Qb-s7S*LHi$*e3g zmWlCwVLusHuEe+N$06Oh^^0_?wRhvmM@AF3q$y03y&Oz7NECW1fJg>t{TqrEd)FwZ zV}~FE2ht<@OX`5EvRmo2Zbno+@&t(}Vd&&|M$jr+>Cb)XsG4u#JQnaup))7jXRQZK znwO5$+@jRr5Y|{+#%ijSrhh&vw(WsC)RmAMBY*U$azsxP0ZcJfHe85cq8g)M?quen z`*WZcrH$2E!R9^X3uv)`~M8;id5x zIo?nWXGLeLyf>JDrV8neM$xSg{!9V}HHWm*$F~3}rjj*rsnN-Bu zQ~PLT!6j-wpi^LE`&zWmPX{r&d8PiewS4drXFiwhX=1-8y2Zqo9sk9vHdRnLM)It` z7RThQpN3>~E&qCZ)9qf;i`s%h`rSZN%}F)H#sEM>Vst{LS)Wsr+OO{<@_3XJ#N#*f z`gU~fct-dJ@$UcHaOvL%;U#!#)BqA=(lD+j6(8Mf0sgC*lkjya6|1-^c*xy17*eP_DSE>wTdy}0qZ~03}K6D|Eyzw|NT@po9nym z*Y%^%=Zd&`pFT^8qWO`{6h5~ zBOFVW3<~%KHf?s`l==i7jj*1bjMugNW;j811ClLf5Jrs~9`HsPgm#MTG2=&$dDY{~rWq$qEJYCSYH8yY1x^5sytmfzAPhx4088S(TdD2>oR)`jUo>!e>d%K&fprP?PqX>n#2MfcK$73&*=f(!L6Me@4>7m1o27ER2AI5l1= z6FiAN5=rpiJ&!2`PnnWZR~SbB*W{j~IS)qal4_(^L*D?Zz>>(QvWpj9d_``F)SbPW zuJZR)H(4>)$yE&k4Fwq#-45YD4vk51nJnKt^e8VM50s>BNm7Yv7rWnuU689F;v zQzL=FjvN=Z);1u$1jl5i&>P4tRO2)+WymPqAxKGAE^&-;U%`}==o31p_)e!Hg^z{y zfRT_&K{SQPRrJqyKp&d570`q4EI&2vZ;g?oKlW6nYul;U*p83HR(;~^O5pI`q>Xzu z5Flz;ya_REA3teb5$N+llT4WvEIC&gF`5++hlx%@J?}$Rxe6tUQMIF3o2XwU2~k%Q z0+EF9L!S&-6uCr}V4pFj`|c@al8eiQdESM+ zM}WU%tkydJ#xg~&Yp=M6iS_4q>F2BLDryp;!5$5RH@;fhhNiae%2XRjyrf`*I2l$E zQqcleld2p*8x zQ}AfS&eT+Ssh_^=``*88HHosMHprzR!t=pBEXu7So&`g{K`9W!c8_tNi{sRQ*9t&D zf*lB|w+C{@{4$a>m-(JXG?bJ%6C9Adrl7RXw;I9l$7C8g{2ZSsQ1+e`*x-0AI~Y2j zU!O>p;(m}*Loip;eMXTDwN^!U#4xm$C|m(eE!i_wHpw^-7PKFyeTHL+*4~dC9d6%h z*r8_w2}_p)no=`a%Xdwp$=h@8NyB3 z;4-b^cxPV<>jIBHZDJcLtd9}EQGsKX-M&Iq0GnfZZV{CxWe12Q=ut#lxnI z3#XGzSs5X|11;V)EJd<#83*FyS_6#GU54G> zlXNtGkCJ3;)NeBtdA#@WDC~8kf+3x_81Dx)Im;%@p#oHRkh=@?A_XUoHa7p zLF)}Z6;xo5ufLAT8|SqA3yjdT3V34+#**#b#7bH6!lOGejDf5w4809`{8vJN5*?v< z6jU1G`^{Pgq4*^;K|jpRq3V?vdKZ9CNi9NoRbjTr$Nhf#qI`Cjknww`x%@z?0hyZ8 zHDt~lai{5Q8_h7S3AZXKkGl3z#dsH7Lawg9$yiTXydv=D5&4=8k@35xHv)-lP^{sy}Z zdC_4*m{@*`}=78shoy%8R6y*?+#vE8mYE5*#`n zH&=LRR$sXmrZ{%ZxHax=o0>PAI`i+czOX`~*>ugZsx4dhTJ(0^O95h^g>KrroI|iW z`OHOQq8l|fHS_dqM}`m*R-y>+^uz%i$0baGa|mhte{b!%&f{aiS{N9=EavzxT$JEI zt+LVMTK+HKHqWA`YWG-Ec9 zzxORybQmU+uAk5fHoNBb$>V0^ZG#ofLe!HbVdJy1XCK>y&jS6|ZCh@PdUz zkcQL#e}tVwbSP1mMq}HyZQFRUZQHinfNDY_F8s3}0Ssw?yAq68-b^j6Z&j+E_%f-Eu_%{eGr807HA014;D$MGgqDZ)Dx#kEB;ka+ z!-IRuU?oc(()(i$v$vs^)uEqY8)!h<(Wz6V4E%9^!g0F`IO#M)&J7-wV-kS-dFD4%xpOM1o(UK(US|(f&B^s19!dix@Wf?zf)EREkG@b&?dVI( zU*ETbr=qzZ=T+}GFPdkF8GTsnsJ^+A*ePfg69U_)#C8XOzwdc&{Y;4jsVsDIk zYDxE*ulCG#Zz3tfjM)QakcN!J4zWL~Q`sqW{DT8Ja@oe1*-dj)!Mvl#-2Aqsq-E!e zO;Hd3;>Y&Rr%IjTu7{y#n5=0x_`3;=+5sp?hjT&|WAeVHa1>E951yecFl|)``?15T z>)f`7_r-aMAD8+L*;P9zh)emm1^(nbngauO87hP&^&Kz?;2Q=9)O*nrmu%9V+YgfH z&jTS!_0>avla+?N9@+oiStj0ypY=soiFUnt^{bsA`JxUbv{v@8$leS2b>}syCJFxp z%3_0yY-y5~@!OUS(?v?ypw|B5M$g-zTQK^2q0$+N^oCoh|79g!c2$zIk`h37E67@7 z6J*>%b<*64kh$AVv({@CK06~Fbxmh})?KaAGPqW#qr%&nOi=RDkhGDTuhOcKZ_^5> zb?ejKiaY9i-Qd1e4Vuuss&f7f>D_D2WEpDAmDk6-{Awrp)EKJkkX ztbo7W0Oate1IXHp!xZ{^Dl|FO$(J>Kxw`Y>r{l2*JQiYCB+ji&t=S`f{(HP4 zp8|G0A=^M_x@b6aMA@cfIC(TveSpKo^C8k#t}Y~K0YCBo3tUAg)^fe?_sTh)_{YVIU}%NlZo~w%oKt=x?1? z325<|plD!xjx?)xmkDsj7ZWWn7#>s3(yva*z4H>j9%6o~e(fLNUp|Dn+Jdd+w5Xy2 z0T8m3lq70QNk)?)E6HNf+Y!Qz=*pPNDjIhlgpYIv^lrV$(j3Vq| z*3~{|w>$)5k47P=JbecO?)A{u6o#7>k&MnN+j!D$Oy)0wp#`b#B^Bc68V|T%oZ-Gh zwXt{nL0^a`9xw#y!8gz9=!JbW2>>=%o>-4e&bE_*8|XBrMS3_nl5b4?XU}W+Ygl= zvvy+cAIBR1x;s>$Ct?En%1FC?cz z#mx_%T)Hs6iRSXgqvf1+Jb7cKAD;KoSpJ#^d3IS!jy z#1PDTp}<^azagT|ly5j!tR}{gYD+aCdvS@)E+WN4mYs@{7+_^FQ|Z1Sp-v)#6UV2XnI?1xm^ZV9n|CEYV5$~4NT#*-O~!n zjV++YjAo<57<#)wfKf=c5iNy0%26r?xmBZK`p={2Vl&vvM};RjI$D!vt}^97GxwDg z!v;@|i9~e@EQ#g(8#X^+YSy4&-Jcm18b;f zC1we~WSu=QfneWmWL1j%jQi>-2~{TAHh8D7q$K0lZ5efz-qtrx9L$Zi^pN(FAP20e6s$%2yPp;`hNT6+l52UxKJkZ z$g|K@#ST~m5gzwNPawX~C%D3;)ytzbTXwyEUA>W(&lzu*uU7t3N0d=?qe@DN;;@Co zdXc6!IS^6!t5z=j0}myc2KSCGa+alCSM$PlD+Rt!lQZ{2d`+s~vKV~>i8ui-?{WM? zlD*G_XOs$$F_S*Z%UnWS$A_?U(`5f%e#a|BqbyE%3=OyTR8>=e1V-@BP_$a6lD)k;0}ryu09{!Tjkx>3b1Fk zT=N#iV>dFCUNX7r1wx#r0Qls#gAE#7Mq@JnX_2-4>!blOy{M?{vYrw>}7@K_cDR^p7$1j{l4RYllvzUCY zvs!BF!*r%dPF;Y=z)&up$L*z>Nzt<^u28J5u!#?>X+%Vnp;aTCs;&x;M#g9?~erNcrr@N-_<;DlD@s(5jRe8gixX6_qSh1x zX@n=1IQer>JSHqU(3!d9cvzh7q^7&q4>`w;_xi*m7i!QutJcMK7C_FET zc(_p}rt%+fwbNs84A4S}sfqJ`>wRtSgffYx>+$(1XqFUv$h45&rc;5uNj$ z?SKOcCz0Mb{oKx!!K<3OBKKA27LF5(BNetb>e>i)F?QX{kstp5P37tN`8J0G0RZ@f z0sx@-f1>Qare{546Qf^7kK=zw+J{xAZ8yabdM+u!+SQRz7F_)YQWQvJ4}fk6A+TC` zLB!fg#8Bu{Ihdi8#4*PqL4i|{tozKV5D4^r-^CR%k$&`Wcsr-6G zZSFCOXcR$XOik&X=3)*fCgV*K$s1uflj7(gxx4<;e{4%2@O2Cw!G*w2cseWquqY1= zbE5gH+T^w(8i<&#<*O-Bf)XLgn4;oy(7kbwLZor10~=>vq$w6AxGd6l#1z^ugd^h3 zT3NU~^wd_Fe5f-Pu=lE&&Spw<9va<~BuxjGc01=m?{C zIfk6vVZHn>K>9Y1zW&tsdo;lw55^c^n89zSwe(+KuRakp#o2o#qUcn@acCIB^JWX% z`Pt?&W<^zEdFDwJ1W)Qw@{LJjxXIP9V8u+zW6X9(Fzsa9RM%~2s;fVCEq%W zHK`*44}owl_7;8R{@8uZscq%x+K~$u>9KNersFO!CGy+rU;D|d-Ay3L;aAtlgti_Q zJ0)k2{>yfr2^TF0JuURN z9?U1vNBG6xUdI@XM4=nIb6OzHL5YNY6Y4?QXsAWQojU7P=78%T(K6{@7WjjE*q3lC z?~&N)qom`l>~^=zW{G&M1^BkmKk(q)k>E|l^!>bOUm=nB}@t6 zN}VB{vvX42lV<2o9SS-dIKl&4%cS@XEKZr3in0?*!L;q)rOy4f<`l6BVRQ??Y*H|^ zfx-n|f$iG%GvoGGVf8!k>%Vj-=p;eE6GmVj?w5M}VRyXlS2)chlAzhiAFAQ7`N5q( z-1baOL9p!Kf6_zQ@MU}d&JUjH{qO(ET<2KUFe?DSrXb+|O*MBmadQ42K!%Oe=Ggq6 z+YeMyZUyqOe4Mdeg3iSANNdaOx-q+Jb#_OQa|N-85YY-rA3&b5N#T$07h4df{+*bR zrk!k!atcvE&hK^epL>v84$nibEB5bP=Eyit){#MqT(=6-E{*UlQJUx^1^k{geVDkZ z1;&vAlGk|1%jq7~D}!{Q%gsJ_x|}@{ecJQ@%|n4Vklvo#;`!q#48KUFOTzu)j%Y?z z7ccL@P6Q9-t+%#Fx)2Z_>A(AtpnnA>_Het_2OeoiLQxM(lcs;|QQui&{~EXdsVRJ2 z=!{8`rk8aLcF!)#<jwP5(NN-Kz7H3iCNklVlGhafpAO% z8_+C;OaR{#5g6teLcbTq3f*zBPoxBi^|?(>-5zQb%@`UdtMotk=MM#N1~eAf7)|GY z20_z4(?#$+FxzM!O&^VDj4&>O9<0_=J=u<|ox>Q>u21-;I5N@Z4?V|w6>N~FlN-_Zkaitn7<`_>Dr)Q>VJ z#tuP0$M$|W+6|S4Js+K*5h$V%WrbdXp1qP`W)#F9V~|rCi&#J%y`M=bZ%-i$kq7NA zv_8Po!*JgOHn6TDY2L{IPle?dPn3|Z>YYwn)66VZ3>(ON|J1|$BKOLvUhM!*mzqf< zCkaE7V6}70K&`*QGV#Fh%+?f{Y_p%rj`Pj2!OW@U0ASNq&pz?ejZWXKcV{rm&0{Pn zFotBpe9Mysnn@#~n@fJjer0Fs`S;WZ%)hp%9ezx_@pGD=z2DK9=Q-xZB5ZfTZ_Aw0 z)K(lix2ewYs!z;1r=uPct4yT#DG!w{il)~d#D47!)<>?61!7tRfD-z_JE99_6Rmve zgComMcr!1ct-7&ZvTNIzi%_o-g{)`bB2eJu0QL#ndK#lHlpUbo1H_9>oHfN`b>ETF z{SKsyqf50mTFfGibh6T(h9h&1S3BuetbJld9I+O!D6%y{jg31{1b{Hib)Ft-SFLyejv@ak*xW)I1{4@ z)Vi3WM~W{*1M|QoiQb>MS$jG-4Glx6%mXV~<|a#wIc1mxbDh%&oLUZgkMy*SNQw-( zPbiC6uhX8snmXE`s+=`I1`Ex~oHol~Ag>|PPQ?CDs|sWY3s4YrC?|lDbQ3S#$diY3 z=^KQa7rMLhu=b1i#~cR#dk_C-p8sIC&z^HSL52%@R_rg2o>}7Hpfo|>$yP3Dr7+8^ z@5~v0*y-fa=iSA5yx->(KcBYPX;^s7k5dbL9vuGk;5w2@B!+Tqv&}08B;e1G`p2-B zsMpIVgC5C4FBtWjd8>8zKklw({MBf_=iCwU92!_M~-l4Ki@Av=P86CFZx1=iCJNMlG~m^a&^DaW7| zVr!*%@H#wunl%v*2}c>>sHGIWo!66JU4{OQz=bW7yh0wY|HK1DCg?d%JLpL&xs@HJ zZiw`3M)u3@IIDN&Jzr}dVl1Vl`#RA&&FRWT@f}eOG*mc1szg7{j3l}OjtMkLrFxq_ z5kLmBytU=HF-W&2%aTh}ReERD)I>+!w0aaw$5ck;pyOWHuqQ#l7o%p#IGH%44xSo9 za3Ud69>XpYysmnnOY|rZG?|hx%rmqdeH&bXcWm`v6-Uq`Y4w|f=8xu&iY+Rr0HQG4Bk-0eO96$kWH`JU0^bFo0hN;g_+VqptMad4VCB%A zX7<*b-=c4k6anXDaRNqBMm&#H;Q$s;Xc?$3K{#4ga1}O41d$qus3b7;l-qC;3oRIkVc`1Gclto^0!!zH- z?}Q8gHS1hi+HbJEejrE_8xa^kY?&dKGO;i zaiKM1wrnx!uG-_sj|b2{Wwvj3DfocfXZQ#`&7bLB`dUUG>Ctq$C`hd|THXD{JlA(J znG+V)>;g1_Z!Z6Bfm=NoPXGm%$Z5Xxm53xd%@4c$7_BO^OR>D9G^QnOO?RIEt&QXB z4;JG8YJvRMd+l_Zjyxx^!$vF=+-CP)aN2>Va!AU1^f7BgO%_nAeAi2%U#rYOmGr89 zlFceBfPy2VjR%%Ueu+xCz%?7~L1&5WHmRTF#L^5ravc`2zs~-a?*Onf&Bi?wC>Nh!2O(<(iTus7SvkkZD=?W@E*Z_0L+SjSU`=kF0gS9 zzth`VK&eHn8i}^(kTz&;5mTV$kgz`F**?faAz+3{9BQYcJm-(F!b0kPjMIt2eo=w2lg2h zSArt(frWqcX*+?9!6@e>#c_0AcN2$MmcM?#3ZL5}7Ckfzwk)kf^7G?;o^+G2H0}6f z@G=H|{6OaHukOFz&7bt|0sFY-?%*JHz3HQn4tdxk)E3F=&^b9zL|ONE6+Y&F;3)*w z#Z9@8hFkpgy5ty*VUhysA7VdKvO)nog~&qIK@bF01nFZ+=#V9_3HYdm7t&`jdf^!; z*n|-qpj!jVS)h0Og8fZ5>(;{J8rwq3&sk)~F|t^@!OR=DHac;+r4q zMEXc!g9MSs#`*+DOzic&0TujxOE$f-#q_~<1kBAU=F-AWGbdjF3^e7~c)!ph@K+0F+0xas3jP*7pX(q7#t)uA(#$oJxMFsj0@Ov^TG{jC;Kk)=q zV2-)1VrZg7lI4vR_iQ<`}|l|g$&JW z9EzZBKzQeeBXA4-m3_(rL7gxgl!_2hn1s@l0ud2a$jI*z;z{a#WgZ)(eXEdl(~p;ntLo_?l=q+$>JW9H_wVDRMQyG zSWv_Z7``_wz7JpHL*nq8w~*F*(WO8Z%(4lAuj6FE$v!{(e#sz5?OVT1*!=o5uGqq? zEvGt$Th?Nxdn#wXwjKT; z5??cc@wI*t4XZ z6vOzbR~7?(!9^wCV+?Lkn4AfxlbN&$YmlI}0b>%Gv<%aud*%v_)PH!7MSY)z_Cf*6IwaJj%G^%%g$fsJF1fu%u>K z;h2)ftniEp&R03c>;vme()NuFHwA(sMn?i#Qj%EZ!w@sn>GyO&%@+OLhYjqGsf=~v+z^^t zW1vGlSBeEp5xsv|DyBRHy?C*q<5W8=Wxw^i!xez5 zY$7P?aVfoT`4e8#M~?1lc+(bmb|XQgoejANEn0QI7;)exqKnfT+U5ddddWP!y7`c; z;0w*B0+u4qzID7`eHO#sKc6}Klm~-%&eqhmlWRZGav@7NhLwV3S=?k`GB=+6V;gRk ziCUsBi*vYoAU~lCW|$m$lKC0uh%dOZ0S4`_zfr3Pd_MQqy_B<;#K`U#4S^<&oh7^v zC81s4(o4+Q4Xjus2_VJ$_o=Va+XtXkw*^-gfcqM~V4$KIQoA#|{zFV+>3MB}<{4#;q=xW=$;(-1l-KeaRRBnFyJKUB}LERzYC z7y-0kW7V=QJN1iqum_lDxd1+pivD|>urk#b!5S4BCZx6;8~RUmuzYr5t@EiX^4#2} ziV5}3h$=G_%sF;AEj=FFcMQU2vnf2vc4&hKzGBtk_T7lV#(F-yMKj9a((Tm9JD0qP zuG^sJicHdW4^Hr4^IjcBR_}IvH|m*S`{p!P;H_Px1BjV1c9j~|PfA*^!~pDUQKj&SZrlDxlV2-L~=D^%w^T`-0< zF3RA4|1uq3i!A9WuAl-c?LyPY2z~#7;@R)+){yU+u~_1mOMPw2VAN7o`Exw>qSl8> z^|7)9W}e?7;8X3tlKXD!M~vqwE4Id1_pid7-Y{to;sL;@c)NrFzJ9=^C8SnY4@Fgw zs66bbi+HP{=hJffI5ft~xQ6-%D}Z{>;DOJLzY1mOr*k5M-K9|yQxW7DD>Cd-m+=$T z#o(`s9z&tr5YK_FsYHq|1+sp4GpAuY?syrM7^)72@>8e>1V;#khw*lMNrp~wZzU{D zAwWj#`SNI|tr3w0u5~TwM zt9yzzN1wDQZP>>44lp8J#yT>hcR6bO$Q%o~8!STVx19bqnUrMY8gQ{DWeB8guZhfbY z+qWOGwzTC;Pv!h4^?jnkp!#wTb);HgP^*=tYqef?gtznI{{Gy2fULV#A-+r&=IyrA zT5CaUkwFk>EbFmhMdc{FVhQk(L#sSQP1>g^WpK9%c(J_CzrnV$ugL4|DzI(Qc5j-J zC==gmsYmk}37@_p%U^9hAZlzoy>K~QqaAx(3pmI*?8BOTh@owU6>H#UR5nO?(m7Y$ z{V>46^2hBZ6(Sec8k;H+iU`+Iwc81fyq`NGOMVDX(gY}H*=9nfO5m6H%--|6zo;bQ z1r;mU5uvh`0Nomea{eRfGP_;3p-VrA0iQ5Gyhc@JHREzALL zRqV76PDE%=QgP0t4beZR%gMN+bBi(?meAgw8XK7RpN4yX7Gyq*3Rm3nc+u2Z9&w&b zf~kpZBV+F>l6Hro>j;<@Bt4wZ+|$jHBiE@5*{|Bn#q-UV*GQJy`xZvFHm1GOgPLHK zUUZHZ#l04saXo`lu}spod#2g)+GnLnn^Dz)R5FTHy38yid)=lj=24O1EG>zZFSseH8Rxo>KZsJF>@at1 zY@N9K?4pNSU7%j#Gy*9j!1z});B}vRZkk{00{`@qfzP5Ob&lFyy*$QMgk!D@t~+ae ztfVCip3v(bJeKj0Yf&kL*u%?g-$|sLiPry|!=gfpt{wy8kU*~~{t<0ux3Iq>ky@o&_vV>jI z?iuzJcB{o`-FoR*?~*0fe$!aj=QxUgQv4kE04>Cr>}a2IShFq;te+(asEf#>$3 z`glpLnnpUcH?^_zb{$CP5l37Q2g2bTI_r?5aL4!~IPA`g6_&6qzzBnp*4ED}l()!9 zn}8w#yg<&vS(xYhqj&TnKS^e6joXEi1Cs>6Vg4E?UZLa-IXlz@Ez-9-&Ta$!hOj+w? zImz%1;GpM!p=S!6F2!K)4gyyy+yzZOu~Izp%%H$s0a{tE~X_gR?g2vq&c3cY$^)0RHp zkTq76M<~`^LUzAfTUV2{r|E0&+wA+enDhJWZ5FtC#a5U*q=MrIvo&RUSa;Q5*fTiz zl6yiJP%3T{Z8rWKuNa=&6~ja2%8p<|4?gg$b1T(ABuygQvV?Xu%@fayeV`W;`EIkI zlI|EHo1%+DQCi~y{Ve!>tn9Xz6j_L|5;;R;pSF@Rj`p$F$045`kf}hTUbq|Cm>%I3 zD0pzA7n4n8RPC+M-8OhL%s}69j5giFpKv|U>RS=#?F^sSs3a5MR?tj(q7GK7R_gxD+bD%pGaIWU*%D5RqI%E4c*+>apJYKs{tq*(U`R_~JFi=a}xi zc;W;5-W9Ny+UPMzp%{S}YlRUGO^Z1rzD?F1C5pFc6DQ42P&7sQd6RWYh9|%99M*FI z-49+_myai+!4T~{?l-@~w4CY#WUo9tU*5NsYqs}A`G}RPJURv%P3F2aFpz6dpf`E6 zz`BW{cbR9A*rWjakALK@yo{*0;khKjyNPBIe-#NB3TgSs)JO+|b%x9Am9U6a)EZC2 zscK(5B8TwnH}QuJlrH&Q5Pf*!E$H#pYEP;Bfa1PHb*(N>Arnc}`Q+{T+C6FY&Vj?8 zbidFL8%ds^1|#WHF$y&U5C7Gz7-cW2QjP(XR$OiQne&>fN1_}@+L`WC!K@*6YL4-q ztwPVNSFL(hlC+yK*~@c)n>~b8X%c7)8eR0>Hkz1ytR9_fpviQ;uazciZwx-T|${HHGg3C{C$h=hfU7(KFFV8rqKnzw~&YcL}~SWi!pU z?WrQvE-psEwh35n6TYivEZ!65$KQ>_{V>0xBvNN@)3b~74Ekq4 zo4E~0PdAzSy9P(1HGD>-|jYZ=_b;iW<)C>kQ-6nMd8j=G?2cMusmnwl|i=6jpJ4O_#)A?i< zjdox%!aNM55wnd1c=v9`xOAYrTkDesW`O!Nx`5t;$rhd5>_i((fOlW7ohZoUXJNIp z>u#yjPz&Gou{%OrlJ~r=QDRt$dCUqIAGJW;r|h_dxV?2p%IP^&c;XIX=@|BcO0sIW zMwc|z8CKJ3J4!C>b5@!*l~;+m7E9O&Z9X>krEC$(?P;2VSsb1cx>MPIL805Pquv)u zoF47Yae%qr_oK6K0Mph{2Et;XlI2FC5(`7jt-8zE`lJB$#Y*-3*c419bF3%c=cZT{ z{}tRy0Cz1-*XlH}!JIO0Q;PK7@-0{2tS>Fv*@ky>9LW#jB<*#oVFton19tM&n6faA z+5xxAfZV?T>OPO@a^jSNOt5KSaYrc9t0ACO)V@_nb3IWbdT1IO4#|TNJObzl^BJ!a zz}6I5$BjJU@~sU&1<(0$Jbuup*(ELJo2b%CSD=|klVMeZ4ds?CWv38kKTU&)OJmE> zNWam)&7{+wxdJx9D8QKIu?ktn00uuND6LW%B0I@^7mR=Lus_5j2&XKDARX;OO1ZIw}Hm zGl*MXAA?KkY~La{fX~?LpshmhBs&rs38y>4*a(kC6t5jb}%fecBekiK#5v%ATFB31^zgNu9 z4VG@H7}2(q&jxvp6mT1>c?-J?%d!)MBGe>Ggt)O18A;Y){mMz4Nj!0MA9`xr^ZI2^ z;*4!VS&a^}#Kx4{0i==}#iB6^cm&!;UOc!-{6>z6%vODDT`2^uFB@!vg3dze^&m z=v!orDwrZ7rovFlh^k*d$TUsW58x5fQtpkt9$fEYj363?Dr&guqO|NBhas|Q8@ z?XWO*$TvMn4>3;C5{@$!STkW~fNw_!{#C=^N^cQ76~0PIT{D7HRI0%sBzh%yI6Ej= z{~oHCX08`>SPZG2BHR0BV-PD;aQKkF%ja8a*MTItN#L4F(y%aibTj_NSOD(EJo`Z3 zQP8p>oRvEDMPJ>y2X^oM$bNv!O+&g92$S}nKJ@F7#Tt3>eHrlbM5nl$?uB;7JmO2( z>J1nF6H}iT!Y&tHNVc;GN1EAWHrKTSE@3G$ed|vCx`}LtK_hWEM@=ue{m>cYP zN}@xXG!h-Vhel1ieGz7i523R^9Sq4z@q_en6{A!g&DGV3NXI(H4L`#F?tzFatdXfC z+af?P`!$LNfY>00#%68~jugkvse|rK-KFQNYJPo#qEv;6Y1Q6XYyJ5>xrfDe?5-%t zL_-bJ;vyj1nEHGsrg51TpJU2vKHN_Pj`*L$bo*jr5qmPZ;R1u(pOSgg_Hf)$VQ0Me z9(do;sHvqz=7i>r7!S@0lzL?=aM#&QhbOh`F4tGgtI?O=#fjD>ehX^8{-t~R2M)l_ zYj$Ku=`MbHD|06-8NeKG(Foi*9anG>fgQ9GHRd?SooGBMR z3S4*ZcT72k&~{U$>dZ2y#+X_d7a(LqSKNex*LK+oCJCes1scOkd@}%x_cH&MJI`UO z|7I_lRBr*g7VqSIUc(H#MEe5~@Rf=LxISyq%nbKb!p`xrnA_ltl*)*c?npD&ZU2O~ z4YSn;bIq;F5@jo~duTI_>M*kg-qac8*Xhe7r>(JmVVUE&jzq!{I{*nZl_Vt1#8HJe zIQZwy8H7fNZVzjfz9R;R)_7F3UmdEzP&%`deYYYF)`Fqkq+wELDgG1U$Xu#H|vWHp7#D~B#Ys{Za~c-o*Ten)|vmANfEAkd3QSb zJ#;dti-9wn9Me0hJoalATzjEQ={GUzKd8l(g+n&i$>xSEFN(9Su*ktcbMD~#rOBqA_5LfOA^C%JT%|&eK7%?=vWC_k? zUvF?c)M&3LZ->_fXt_s;+uwI(bRR*o*FQ4YPiEBOYHuXw;9U0Bsz86ZJnIK&U22)u{*%Qc+q-9ZiZW04ZFkwqt{JZKPRK*OH zQCxl{*_ha%{R~pZyHR7h>T}<4`$elOCJz&rW#5w*4Ow$xCj#ntnHfoxv9)DH4I^qE zo&R^E;~R#IvdEF`OCnc?Qw@jIylnbN)@$^@tHtNBWmx$w!nPpwuav#0O!oF~;or}sSsm@XcgyLyQo`6gwLw1=4ryWTDuRy4yf|nS2#$t{GQaB2O zy_$N{e1h$ZPzmir(0Y)bbEB0c${Djt=Y)bncXjn2tdW7=2JYMi72J@0$;rl2JW}Ak z&oh;3dRDk54)bDC4Ksz+>GV(9oAWfd9FFBc_HN5@a=6l#Gb~H7csWPcFnt!(#6JC3 zpm6%RkQhw)ONaefUFX)m&sWN`1AYsQ0+0=mi2H_#_+F5w@S|iEEod?hWDAn~z@r`m ze!-JzxpGc^?)8vn!-WWFWtXZJp-^{+b(!|c#1qUYXsqi#r7dUsJ54fXJo2h$-a?(b zy*^_lga|Ar$>d*AYts6lgR&$Hm;sh{dl_P}AEh($*Q)Amoe#(;S^ z=clckWVDd@153Pu^p+zoo!gZC?(sI?#+fEjZta!?FMN%4Fef%DjHh=Iz$#Ekv=7iX z{refJ9>PVbXpA+BL-_Qym&Ax}>6xc`>cdl_luyQ2{>^R(aR{4noX!<|2?`()H;iZ$0+{(^2nq^m}Rq~&b}(2s>C6Zs|& zAosSw>?l4`(OV5S50J7iNntSI$%#|9ixuPWXABHVJ5n`$5zPD?4sqJlaigE>`HT~7 z$BT}a)Ta0u7NMnT)ZrG74Q-r3eNCMgqe@&gstnUo4bvpi_f_SW3+BPVrMR27&o`ur z2D%tDvmG|c@FR*VJ1?n|J=*N%81v_Ib1vGV!aHNQGul>Jjb8f@z6XDd2Sm4-7ShJi zI0Qk<+ym)5D~HfDueof;=BZIL;!={Bic$U-XXmgi3=;s?YumPM+qP}nwr$(CZQJ^; zZ5#Ph$tGE3kq4OVRP{{vIb4Z_?dm;J!CK@Jwhm2f44PNL4Tl~#yJ2?E^_8a!ILbG& zDNCaUP{j6+6^=pU@C%CMU{BezY9 zQM%e|vc2TT&N9iSqg%&?V=l@WR;<`5`yLl91*0xDK_cYvY1$y@MBzsbgP8x$Q8jfS zgm6OBAqLjari3fCYw*1O+a!AqfEFRtNKqqzWfLXdm?4*X%FmW&_TcD<%q(+uQ*-I zEcw+@D5!kNZbS?QCGt;Kf0a1cG80ZIY;tujy4F{K$sR;#yE$<6CKAtuVJTGJluDNh zfKv9j1P;r$aZ)<0;&!~q8hxY{_js>hWl9Ox=m0-C{ZcX|EE2zRitM zfROJ#ESu}wt_NedW1|FhL%n#?qrB2Eo4H=aOAke3ZaErvjxg2?iZGqGM|%&aB&iVF zMuaKP*$)K&wm99*6DnAXC3SGKSED&Hwf*wwI9ro`=Mi86UqvXi%PO3(Mh_-j`T62C zXI`G4TX2_IWl&RE79%aBbGo|d_n!K#Y9sEiCXFc2NwO&R`?*<8c|(|$I@V(9&+_hR?w9sel- z6qo?Dxk~&gXyR4^{o0F*l8Q>I0xbs$M9GLxk}uXBxD!993Cce`(Zp2lEd$foVxu5LX3q7l!HnsG*%OeB-eLTZ^MA<~W9Pcu~gy@;b`viH~V{H45QpuS$>f zAg6B1dl9^S;@k6t<{W^7A8S7p2h9*Oy&PQNw@_tPob2_O{sTW2aUG%VodD>Td~vzB z__oZDgHovpfRI+5>PS{Vt{ zo8yuD0_RUtnW?&2=)H&TAzCU#km<*e+M!8`3<8LyU4@yZ{7C>dLx8H zH6@Jdg6qw&ph41rIZAj`_d;&74hMzqv1-6}poaBLY~NaW5p)V5VDPxE=XKzkzhahN z#`e-9OdDdSz0Zc9{FjT%t^H?*i}Ed_T^L{2!q03yfA`FoEs}YfpsiOS@zIFh-X$x6 zREvTKW@ymWX}Co8-*OpEtqsNDQO`z+#sL4>!5dR~{IqM4!WqP?_eou(syRztnR`>t zX!dwS(&JpFV5nzD&93Ns5-5Jk?)ZJ!!Pa^?eb*t5q5J?zZn!18BU_1p(M1K9QUAT% zd{!wMdUE2~&kLD$OxzncG!p`e!)sgwU}Mt{XuA`_Si!LYE`fg2Jt<`!>2EP6!M~Z=nz)H%do*8 z=-F3MpYXUPTs7WDt9|ZU#xrYVt#-UuAPJA68E{~_1!(i~HR}5VTk(QVqHVw*72KZ_ zS8I~lVdUq}5b$IPK}{VY$8dlq19i;v@_v{T^S3|+8Y8qKyq|$V zINRj3RRZ5<`P+K&L(v_9EXkU?5-_}uv-Uw_bJhA{`rG(nD`!~miCOpj-dqvn(~>>03_P2{ zijcEZ2fecW{n6i^ryC&ts-MFy=<}@4$EenJiKqP{Lh{HHSQN6cT#mkofz}HT&%_#a zy~wzZD!d9+&p#07I$>A+K2u?beEAs<7O+}1Y6Qh0P(PG^^1?tZYEKx<+W+W|-qP^; zlsq?7h}aenl!(#5k;7>P2GJ~DKKw;eF7A~7{e2xZYVOv{H1cS;mtd?ae!XA%5+*-D z{wI=C)cS|_Tj5+l>6eW}QO7>OY)xQ+oB)3jp$z2%M>1G*9s z(K)@TIu#qI0I}jl-%l4~4n8g}1MzDc!i|y?=|Xaw$|fpwnI3&gVwfYFylTq%&FFl+ z#>}Oe66iq0ri^Ay)sj+`P{UFpM&&`JyfY%>9uqNIP_dL$G#kNQ?6E;%eT1IWwQEelSj(_qsxq7GTDn+Xe6>>D zeCC-svjxn@lSL?90S}j?9F_o$3<)f2W~noc4Qi^1nl4Jxd%O0Cb*+x=t8ICCC9_avVDb0;p>Xdz4R1eQJt zktk#+gUN9Jj)Y$i0uJK&yWy`#QT(4M#dI1o)ui1l;+O44aQLFZ@fS4*9;GGeFk#(_ z#t#znMf<9IU(9(>uVcb~I-JVklOu?j7eD0~KbgxsApGMtuoaWEWzFtI40&bHGGz>f z+(TUS`0(}-lbOvIBx#0dR8q|yc9Se0>ZPm}xlFsY#m2>!S~Z+F$inmMM3JIB^CB_p z_bE&3M4k zuJA3_JoF)5%8-p1+e8ef4X_n{1Whu~g3ENYaKZ6kFxcOxVKxX?IL(`Y6StY9myo}1 zRq(P51ZN~uGK5*rE@Y)KO1R!#t0Ee2DNGoELv$$OgES!o{Hv4c+q@u@Pof9I4dHb+ zlo^4dMvxtF2nT-9G;w~@R@6HuZgzpsI!y@HQURwYf_M8bb897Apb$~bRY;2Su zm!xKy<007%u2{z9%%8*ZYjC9FM=dBflHS;3b~{qe)6+OyMW>5y?Lycz09n~>AO>^e z_|88=)m!BKIn>S;=f00+CL7 z!nS?A$)0tpdL(Jq1gb&((xc9NdGoD{5?d#c?8wV5p9sos|1UaqS|t}SehJQac11p> z$p$IHHu@PQUqZRY-7--S*$2d~JeKGy5Xs=IO9APJ5GX^z?QLg~(+1;CBm#RrfMJ5m z6@_ZrxThFwDgA^HC!&n4^@z6SK=m$bxKCm9hVqzWmhh68&N|Cee&HxdK(o&o6MN=5 z6FJURVh64#&2BeK>nD$mAn}P7qCVT+`3Y>lp4y8^x8E<+U2Am)?9+R8HJoq93+M7{?D?8JcbdAEgLr*vbT(qBovKuGnB8&3y^QCgOcTQ;#=TO) zKq`VJm~Wv-YYWXf2n~UT(li>=8byuJdFnE4u(te%7U;N53=SAouBE8eq4Fhs?nI8` zChAx~Di}9Y%5Ksn*MLw77;wP=s)c|L3b9R(NHh}6p&RZwfl^>k<>cfhB*^aaE);CP zLS@rs+Uule>UKOw?3qJbhfiK6XEzb}Lh*2)u>6C@0vvqwZUwAs+eK(?dN%yW%L$?v z`JZ`*ZABI^&*7LEht-?)cQLTA(|tkmjD?2pWi1PB-@2seV9ZGJ4QXI^@PWqR*6d@> z)S4L6rLE{u`$EDOy`v9dEPOgtgfXI?%`KFJ4H1!)G^s&Fo{g{>+_ffn_DVJfo@B;| zclR`T4OK=ri3`_wvQ0bSzx%9!W!yT>26+X;GwAjsa^5TAS84hKCn9JIJL=3ffXm3m zo2zCwEje&0G1Hc;MEb!9ji6;ta*IbAzcYeX}Ofodi(yMa~NNZPuN zG~B=Qj>~C9|JIp-nuV6)Y~*b{uSaTVd)uN_JQaQ*l+Rz2 zUJu~t^U%J6V{`pfic<&rB=4DJl^UG&H#Oqo!B*7xGw0<;aO6#k!f3dvbuf!&{ZKL6+Yr*U|r#i4+|)Nu(o6u33cxQAGS zdFha%#*^_zY!G>FFvnxgHj_6|Vz>P1pNY#ey2zE&UCYVSQ(m-nOY)C5{JD8upXtrh z?;KxEac^pu?apcTEk2i1J>G5da?3`i-Yqqz=$<{ocM@dIy#QX|`2w^oht1n#R5Ah& zheihXQtUoIkI(aH@%X>Tb#Z-#@#M|50B@&D>>R_g@VT{Yv|rO$WrxM}Dvq)+yvR0Y z9dKaWilNqnNI>)%_>aHfrwU5Gm{an0fhVkA9imW8^_JZL9BU`)kq61gtPM_yV^aIe zPKVl+$^O^}M`owz+$#m%(NL(|2Y?1w+J-=&l0ekDCdSK%{LtrTrbeSAqqTET>>m3P;pU#17%f3Eee9J5V7**SEZcamPi)xM&Cp@hLOkNZ>Lr`KQy z($+;YW@*b-(#R~ja9Z%ZiMPW_d&hLi!!>*N_kc(IQH1xH;&05)1O@(-XK>VhDb(6X zR@of?2py%BotzErcX6H?;3b|+J$5?}G^9Y1YXpgbyTSLj- zB3xCUL_e5fde)m-t$)BiNeu3txRXnKS*6zh-ICHLm|hqDJ~j4U6;98bE7g3=B8H*m zO8*w8okEr-GqbQw8n}ff-i@`RKl=PzI!{Y&n`%^{>nGvcYB?*OUYi@+Vp;#qkXpbt z>g()2A@XEU>$1OjmfYJT9s=*~{QYCK+j*#`a2%WM9Fwm<)W>c$#POW}u?mB5TJn3X z;h!a5S)lo=;HMl>^_Qcb{-msRGqjfK$HtJwRH5-&$YrQdVSiYM?6*x<+{P$g`;P$- zkJ0+d6t#*oN)^Db^Wvw9_VY{q-7;f+WFIngH)rwK5BPr)G^nI^s1Ru=L&*Ydif9prNw|pW;I~m2@+bwk=t0+F6TD??uC%>$&3zk^n+FLNWj* zF?reY_tj%wDTInfmy{#vPR86Wn<%haS*2{Q-*)$}8+iwMH-B6(!)(~Yo{etEAPM&D zh$G{4Q8pA;)f#wEVzT>(2#*X>DdMz{y$~}#;?)>IDl+}q3j^VJelA)P2-bFezE_r&WPiE zwcz*`m*L3{}Vj~RKciOx4hweSa{TCo;b44obN(;*rt{YQ1JRV zxV;{JU-$RZ|Gby=aeDZ@d(cZCCHQBx@;^7{vrp~*Px#VHnmO@Bf;b&l3Au%TZIJM@ z{%=9SHhFW8)G>T9-gEHVuTO(`fpNnATWn*HXvp(fdwib#&oAnm1X#eS+29_n&p+)S znki&|$5%@{W81A`5o3cqvRZ3Tuft1?a7~jS>r}C1w%ng%_(#$w`*BAc`WN>@ntbuY z1el-h%wo zV~#0pb=eVPPs8qlIY>;f!TDYtGJ^ww))=!zr?ivB)4i9zx*UnzHjH=q}9$we9Cp6mdYc*m0X(Naoe)_U0ctx7098p1``t1!hy9>?Pid(O2L#J z1HPC{q?UOx{`{qT=|opi&a+LT%(G3Yrw?;9bI3X@g*v)hBNQC*{8tRL;*)nh2kL5B z_s1u{YjO-Sb4ChsrX_S?TmYWzhY^@2c=M;8QR5gL4)h=Z%M|0o!a%G*&{AX?!$b+< z@1%3(9tLepBi-?RAH4%G3Tj|qi*vVkpf&+bNE;7lnmI@=`G?dHY>S#OG9xQJLG9x6 z(WjaBTa>#TC+vOf%)Z~9u64e?As+8EQc6D(*@=%>k?I0YQ%;0*LXus#2nj<)j1X{n z@^lW0%=zaN5YUtadgbAL`d!Rl8C3Jd)WR*AJPC`adRUD3koWGF5%99{W8a zXVUZtA7j(B*rNSDchbtV5yAo^`Q@5WjJlEOc?@I@h-za{OEzr+JdDJ?{YXnoJ2WqI z(Mfi7F2c{Xt7VyUUq>ba-0M6Te&hCg_2aE?^1IJ&80xUG1GLQeM-LpCa}4Az8PNGs z-?06*zEaK8Dt;qXgkQqOQjrw^z+dVv!VDRITb@Aw2n>;!<b#iA9*lygsrGNKBrB1$Z#O$h)1k|`m*vHB3PP<`k9oQSCn+%7X(iU8JR&Nt{5DmC$Zfl1%;g1&p|Mz^j zf<$o*INPxaBT?iG@XuOX^N9_IL|NjqJCaua`iTHWoaJYd4ddw4K1M7vl@Zkn}Oh?6Be8;@nD1Ovy9 zURvBXJ(*BuSwo5XbvSr}(nIX;hH5ZapKPP{`&7?rm%X^bd4o*E0a8jCf;UO?ARnZm z%Y!*&be{m$xoZKFc5)OX)UgZ7?6^T!g4<@$qF98yN5EZeq*19i2qQalH%zpyJecDC zR)n9SnbWR@J5#GzR-{fTLDEf7$&OU~i+Di6Q4Lcso&-BZSpqWQJWA<~uoJi9;bzWr zga3?Vps`PZ4a(YZ&?gYCeryTyb%9r`*MIXo#eJ)OBHLQHoklho)Ez7v)6=*C5lIXVH z7(>g?vmcDPhxIc89z$(MjorP=jCx}VGhtpO#Zt_8u-7p#10=-YUc|RKul`@poRWMg zrUmY3bJWa}>9QOQXJ*`F@D7tqtozK(T+M;20mPfET2n-}@7&#C?pwt{FMSkJkTjr& z-lTf8jhe>D51bV4p{Mk~_Gqf#$lP3wL-UBz5aB!cIMmTB`n7?WtP||VojmB-{*{0| z@44}%)i0SXmDC(Ny8VA8UK~ckmV>3p`D3VxuSwrvL`_1kVIcM{rNKW#xa&Gsg%Eha z09?#g-msG>z}=;s8yFGTvqQHBs;#Jv5Z}yrbnS5c#yCjYlmpZeZ8`dzrtVQ&Lz04q zv>+mB=<7#6&fu=?NwaKfuSKfVPU#9@^Lf0`c5NzsE8(7bgFusiEy>>xrY@#3+NUL^ zkYbw#N01N~3JNZ8R%iFpuB9grn3cqakg9KS^=Ja zs}I=K{2mP7q802hNCXL^p3jtKC&(_??O^p)ZJ^x`Ii^PvZj`z4)dqn&;>V>n>Et`(xS3IV1N|VETVG>AK-92P`3(`#Bv5j@ zID>-J{K;q_zYHq3ZIcxSoDW2vBdT9&p({J$2@<6lo;65g@UGX=gOhFrGSy*bcel!$S(qXh#k0jrTb9Lv0#gCQbN=O)N9}gHs^* zL899&;@@6#=jmoIxEGH)$&?Zaw_%7*vve;mF#gdQr3<45K;XhxH1l;3*1}vZ4SZ6? z!*=EE+lTeTa~$1Gjh*VvRo~M9J#2X#6S8T#O0OCMNR)FEd?u!ri~uUM%wB4`i2n2q zRA+L@mY#q2AbAnA9+)mOy=jXac!S6=xRgNt3;ymGZ9D& zQ0d;@=;a?yDfM`H-=_yHpRTM}3)v0rIz1fLeGjEUk3lK^5bghdp7+n+PY$~K^AA=2 z{G|5N2a(g$(|MCRql|W@ADeFs*(ln`6}y&)|BB(^+ZaRQ1c2|qdY?8;dM4dXg^ss4 z$6Yz046*Vd2{nslugFLuWGHmWa&*%=vSIL1*(IA{L!M_4NGwO14Ab-vZ6qrvjs{gF zI|Wae@C6HPVN`CymH3N&lBo1r)g+RuDkP z6;Cs>#Ti)Q$4_-l*{CktwuXh6B0_sA3o$B&^2Epu&4*KCwO>z8(0%A3{8f>&dsb9~ zO~J`hC1bE%8-wpg7)13J=x2?#ig$n9n$%lSkhIon!mE+`eP3?+0XnJYjtGI;3jBtz z$h|B4E4G_W%&E##mZHr)%#+Xpk%vgd>#un>P1lk2#ScHYd%t|>d{{4}clH4iF|$c{ z07ns$PT(v+CK2ygz97bF>X&$tG=t=v5#R^UqmZVCwnue7 zWpm7)nxEQ^_#1Zm8|0*kBt#;`+|-|J;;84-RfZQ#fTb8WagmSSvzQ>iv>BkkgIy@Q z{48N^Gb2U@g`UUV>EVLJ<=ygAQk=m;OV3FkY(_$oDGuHEuoi@~V=dlF-bRGG_vgjd z$uuK%Ae+Ht5NmguK7$PBbb`VCfH;lwolV-ei{#SVxLVer*H8%~S!9mq9aqcFjLv4e zUD&GxovJgE)d+T6bLSw0ica8~JH+r&Zz6tm#fPe*RBsoqBsE+i888k?yGqkl;iwng zgE@%m^Q*^~{@dF;TZHpL&vI-CA zFsPiAaMf6MFi3ly?#XD)^2lkvM|18$;e{!*-hGm|={ctds7_m;j9{#`ky(x-wphuG-beMB6|XwB zQmzC9L=Q14EsZW{{hC{F=BU9{+rjvZ>B1za5~osi$w-0}50vmb_Uoy+S*k<(<$f4h_n12? zIS?uxFOhp9J4VP`2q!YGr7?0k?Id5tRh?|gjM@nQ$0JAgRpUQ^PmU|{oa9wT~MvL=5Z?1w)4aHH` z6Q0d7P}RCa^*(>;Gz zhC_HzazzDy0d~-}VG4d@zKh6JF$Gx*Y>6k>&3de$&)5}h8bM0pwGy_>)W(VBFtyXzw` z?chP=riu{Ml$M~XMuv^>$f{X0hUx}SZv`_xm*W_i`fFM|wK}c!QMKkXD{lFiugj+x zNa}on)mDM_X&|FLfT-;w9su;pr(n>#wlw3uHAgI@mGY-lO(q(B zK*yG&!%9|2QXz?bLIMd~F3kupH2Rn?5Va%>b7l`KM=_xkj%Q-RO2thIxo4_uU~GgB z#%UI>LYKo1WeI^~D1iUI*3~5d5x~JJvF?s;={Qz?>;^Jd_S8a1dNYykCIzEd(diqI zv?avAYP5euLDbGa-)lbxCO%ODP`npQu(S1p6cA~iamr}2EPAkp4FmQCQ&8yeFve&^ z-r7SYHiTCpU$KsK0_k?w1xSZ#Ssl(?pFcnZZRj7*V>)z@;be>ECr$VcPU49UXi$BKl#2s{d9#h89wl!TY<4*knCHyP76@a( zyr~j6nvv_pkI9N~5m|E-p22B+E)a%ZTPon7DUx<1IBf`#1Pw8eYlK1cA4~JZX7a}; z(1>_ujnx1pj$!r8(itBd=d+YI_k)5=C&jYFPbbJ(H2MHJJ);d<>z2UWCIE6K*xZM%4rzxPJV<%v4o|rZ`C@mmZusouXh-0P$Fv&%`HE^x%!XLN=9zkAHBb`uiN*^?O|$)i(N`1Abb$}80IlMw=AyF-`lsxKy)c}L)utLTScXnRW!+%-{xARis_1$gUm|1AltvG0d;>kot^ z@CdS^iU%4I<&6st?*@#j=GlZa@`?ZZBfxkjv;o4sp&28$0H z9LCN1IRI!@5C3^39Bk%>@aomOzcFSkY;1Va{Ti%2=}a`{PW9zx<>=`{F89m{{l(!Q z^^O$9Vy(Vy-V$xQFl)8ODK}rRX%PWP5G)ZwQzS^lASW}-qcRj-f~}|pHe7> zcQWyAvws2RRY?0k*v1l+x%2pA24y=z@b`QuKUzG{3eR z7ksks7{+i|GQOF~&jbT`MU&fv z@mu zC$~+q&Q<<8Xm>KP71rOoOkBNmlW(wGp?`vcP2M#r*#_g0UB1aN3M=2D7GVCb3C#{M z9LzsdVj<$z#zm=RLn_z2ul}7>P3hEMylrTIUYK%&5O8(c#=tu5%E3CSqBcP_W2so* zIW4k@t}zw1&xi7WK+H&sf*Y-lgkW%5+u<_dU9jfJ4gPA1Z@%r&8>M-Kr3?W)6AM_$ zkznJ|R1d7h9|wE9?X+2o;R|cXB$xRbNV20szaCTA(uW&v4QK^VYAkDv!BACe$v%G{ zjlo|=Aox_Nw+M1+ZYJ`g#6Z4xX$)S! zH5L49a`R&isXazXD5}E zrE&JY&H*95f*-_^LonR!Dj+iSSRGt=(Po#3UxC)lj&3z8M`u5ycnnv8qKcXz9m6y? z&-Crwt?xIw&HkQ%pk*iiY5=`U7vcyu4pRAvFy~VWCIp2$D2_?IZ@}4uF2` zaUCs8MoV)!jfdT$5BBe8lx!`DF(@#aQKa;`2Fjq~ZB4S(T2rw&+3m{1s=`E|o$}M` z#K}Hv3X{ccggYzixr>$$7i+Ovb6F$#!}&a_y00x{#&= zxIE|7VIAem=`C4q1=o5H=rR;YD9MxrF!eh|DqvJ|-dD0lji5QAeA}{>Z4NrPyd)Db z(SXgN)a6VktF+WLq^s3`toy*%E+lfhl14;Oe9U}uQy{*bpI1p~)bYb)dT`4hMr`+` z)^T$XVl+oBQhUgFolv&IzFiq1c;z%)+Xu*@G7p~Ge`KADWEitXs7}29V%??pLH(IS z@#mGURYm#MU{=6+Bk9R)7KH>=7NNPFOO$&^B!u(z!=o>um^uy)y4}lFdO;O(^Kkn3 zdA;JmsKo!yo=*@zD|$BgyJ` zVyY02%20Xdn~mB^=x8m~q%#;d20foB24CGA1|GpiPe}4*Q9_A79N+(4>nwjsM`l{} zVot&_&Vy8rl_H50rhuW#P{*r;p=(T0`LQOz$x8TZIxGzhud2==6lY)=JVi`0Pt+(o z%UdC-dGH>tgXfDlv21{7RG>jklD^<)w>PWAZ?UghbcKs8>oz-4%s|iU=Ltw^IJF$n ze6i$Pk*=tJhcB*Y>u2bS`+Q*?2DBb%ZlNL}!dMptI;m3=s-B~TKOeoP_*E*^8%q|= zgAis9TlGT^p9QLqd@TAz65H1BWBZ`%q)oH28Gr%pRCy+##!4>B#Ar!$8e*MIFh6AY z^g|}L#mDt(*vQvK%n>n(t!xb`eDEgGauG>btN>2>g22U%&kN&;WF1D_bk0nSEYzcX zK>P)>?jg6$%eaA<9;U#}?kztpzX z4l9!~yu1p6Hy1YiH`Y98=B|k*7aL6nWT40u7wl@rj15#_M=UrF3sqMllRR?&l9Yz8 za=FZ@8oz>L8bja8)d*eq?3`aqYh~=*OU85hi>?AO@T90k1wekyft5jhwG4F2a!{#V zHq;gey{O#p5ZP4eS(YS97b>R_I(a5Cv@MmhYY&sBg7T-UTeRkb;h#-gXfO0=D&cFy zG`Ti#4m{`7Aim!-ct%hMc1dM+r5SUoLp5kZm^QxgB;53pQ`r%_GFCV-qd`Tms*eU| zG^MLx%Ka9Pv=d~%X8K`=qAUXd#BapCUASM z3d-ija(Tt&A~?FKgA`_sQL3fH?TKw?Y|i7oe{7eA3<{d`QxXp!yE>)BA%M^l1eD~gcCcm&I!eK~y9azN_skL~u?OIq7yP@OkL zyJz4E^l6$_7n)WbdQsnZVHgu~lM-;P;IP%J%*@a8hVH?Ry+4tc|AvJ2~vCgsIm1#IGBqyk3~8s`&?7F+Nq` zZx|^l)$NQwrK~2U&t3S@>8PdKdUs`y0rRd}3CDiSpTDXZ=bNAFM6*x$Z*#@mbL-+p zp&+|7s?OQl+K@t`7Z3qhF3X|%mKTB#Wj5A5k$3$GNPY@K?g0!>JQCaX@Y%YRnDcLErOQ%P{Yo5S*_=gMc7l7G*43&LoWy1xS22XVg3uM<;t zxjW^x4vTr{#j7MG8qz2k1MhLrG0fN)TP5X-+FD9|`Blc>Df%xKmS&i#WN9QJ%Vb<3 zJH_qi-4TkH)coSzN-D?wFOLgizRnSwyGSR>GY-LWRV3?rHb(r@Doe3 zfD=zH+#;E03f+L>2L5KLhgH%*yVf*vZ~xy~o!upX&JFl-ZK}?(Z~X|Rm3EP$h;1YX zYLKw6?L1ofzd{g#%^3Q)kHt~8a6-cGiTRkDw9%GXsY<_7Q!VfYZ8EgJ#$FitTp3Km~^(^bF(0Rai#puq$$-8AILsRy^edy#`FP(d8u3%HWplJ85T05&46TZKX>}Mz!<2HM$$Q8)u{Xps?m6D zseME1rfFjZo)jZ5`CwcH<0e+*np`X;6?s;6NMrX!vUpX;TPD2tc8=Vz zM{Y{Aq2tBgx!=g;c&}67`=q>x$0p15_bGA)9M58uB_8c*wL%CkVItb8w&&LP9RtVd zW6q;}`P939ZA)n2Ink3k2cO6Pz6x8X%;F^<2Y#j(1(USg=adaxX)Ih5i3eSt>bxy zNgrvBu$vBk3mdz0hRHojR?@3nK3O3sOT7?mUr7j*)W%0=WCTn)y+hmVvCU*yJG)c# zhXkhnpiR>$#H<{MTJ)@dGiZ;3Pl>DAoAnyR$>yeWgxYH?E>#BJz>YYasKyCODUPh3 zJ=fXHDUt0wX{K$it+8(J$}9sMYCQ4oR>KtRlr?*9#Y@@#F|LWd@!aY#Q# zQDOV;n;mc3VKis5e{oux=IwU~rZrc-)LQhMUt$Qj6K-D{U=+w1S$ zhms?ioe8-LUxr(7sgy2{y2?{Ysb5}zs9u-9N+iL807Ndwa}|RV&$GNvICSlf#(oOa~!zb?ne(PEWwM| zbu0+P!vNvI9w>pxP@P*UKw*hUK&qLmDYNON3wk1wn0yc&2_Vr|$b;7|qNv7$BVBfh z4(C2XL?P^`z)1jKYF57(L~l<3Zm{|7Y*-Wx~g7QVmt?Aq8tF zlXBKUT-Nc-;cMO{`SjjQT}-|CcH!OfvUU%Wuyd~Q_-;~7{pi}FkbK$2!!#;8<8(l} zj8ByFgjv*?Dmsi?P^uR;1NB%YtZ|?v-^kklU7*b(W&9^vyBYJ;rV&!}E7gtm%@s@T z9mo{vKS{9X`z%J8ZifGTdqz*RooVI3f>Ik6eAx`>5Maib>`kHpy_jAMrdBR2dug2` z;M*T7SIYuk7hFXfx?K zj2o}2pB#U+Sz%&`8lj2K=$`VZk#4Km`Tf#1+c?Saotnl{M$Fu}&B(4Enp&9DdhG&q z+w~LFZKrP7sGM~=oG@D(S>IA6qOvog^~D3I0MW_#lbXiACCesuYC4N{=eO5w-f`6H z%~!G|CHI+tkvun8P3$d5$gxiiA5%+9RT2lgRYV$Q>P)E(LF4Q$5MI&qR`9^9zuW52 zv}6wpQ}z*E1P58v=Os^iAQ8-JzL9XHV+4<r)YUYEwE{dCR{8y_;UO)x76^=NB zYvnpHON#co(6*@=9kmEq^?@R6$GEy0!hoX)t-WtF9&T&98ik ztoHIRvjK8PQ+k8vmq$x{!{uF!q?GNS9`oAIR=gprJjFzL?Pd9)iRP^6v^QFwMO5=- zDltCTmnnN>ltMUV*}K&f%^eeN1ZG)k4#>K0z;9|u zY|C{aD0sN=AauKp4{H8sLynBWLAC^AWu|z?Z0m**gt%dKCsgD6M{oAXdJ-ljh}aK9 z6H?h@u5iUyI4yE&<4lb4(=BUI$qQNb17{sM6NKhoU`jJ|He7kBovF}c1=O=Xjz#la z7a}1e>hq+yPOThALwAO>j_^%iO@r~sZVuH?{fgep*0 zVG%^r?!*tCkvl*U}rj7~Q-3ID8 zT`QJ2wT^~Gs`g`vpM zibc4}r%6lw=8C~Orn+4Fe z3!1D>xHx~c;c-fujE4SEtDiZQqe8#b-{&oDmn8$K_ksoJpxZECQNL@%W(YD@6$r1s zunJEt+MUsD;abz$yoiloja37wnr}hygThQ*2T3hDDdq7O7q9VBl!g0=Z6dtI;#P^@ z^nH?xy;I`bbILaI=CQWpw}Lg`*=SzZJPJBqT#N}r>vOTEx6)AP69;PkJPo7z(H7*h zk7vj9J31jh%D&m>cey-cIF-VKEL?1Md$~e zd$h!bQT9c7^8WcmNQvzttQQpjZKv|4agmQi|3ZzAhnqZWN-NyOyY%(6B#hK2SxIYQ z$AOdyZJh|xT08O5AMS)f@M-IDDf3`d8u~S9lO$%4UT>+VvYHT*o{d`Y zvWI#@R5M`Pfow7xvBpKkpeCNd$5W-%jmUjukZs6tI+;C8*_`*#b#G?;bteuf)cvP# z|L5Ij{SnFjGA~7Z>vYnhU_=g4F44+X-6G&i*?$ql+xd(Gzd%Y-qoEB1U9^T zs=oZ&7eU5Q+XjV2`g>w=yG+gAhu(#2J{igv*R5u>LOmpMGF|hw=hg4}3W{LfDc=uQ zIK^MsdUvrZ#unaezN1Ptz0C*HSOq+J)h7U~q6?`~j^bj-ZjSJs6We1DRxXCj_mKmy zma2+OT4ryqAa=EKxG;gIC{ZYTcXu=$81L4-Dsxumi>c^Hg(h%^YC%KvukP=obe?>R z^ICqNn9h#_!pqdKN#rEIp4>{IXR+1;oMdsNs0!v{-FyK;?)eY9Hkw}Fg;7s5&5dvq zyy2C}`eewezIq$oiufS<(4S&J136m~z{QME6=OJEMa)gOslz06I2Ky#rk!wc$SCpQ zqoU0s>F$m7wLZy9W&_l0TbejjE_u_3sE7IFOYN?V?XrU1}4{E=6xw4+D4zF^-pg_g4 z>Y8l=x@%UvB};j%!7LEgg;Gbg+Bi~gv!|UUUDEXEG_Q+O2h$Xrs~u8|+tK{v(`R7i zR4D0Ag9_znYcYUF%Cf=kf_8L{$}pQW(&!<$&DxucDg*7o6jlFO_~wk7y44RWNfmsw z`dvGfpQHks;auyNPMtIya6B^0^LsY9glf=zdzQA4mR~Qie^C7#&dFZcu5FT6+j{>d zLw2R|vZYca+Ph)rjM>q78P&SQRj7C~{%_?phT=>Qr7;Frkk~wQ!D&M-GJ+$^;vtl@ zu_)rpIOl;l^VPZB_d_(=uO(|+YO|kz3B|cbUUysFUNrBj<*}V1L;a(tb%5=!`KSz4 zoNwOxAv!kaT@;+HQ+iN)rvX#L$~(d$b$i9r+*nKaD1LKuJpv6VMc6qIQ94PXQRJg4 z3*KpFVsc?KJO9t+ z#w;yh%aGXxui4r`4Th4$jq31w)(AS)!BypH=&{sXI=_uS8SL;0$VRE*im#FzvVzZ5 zX4#6IssXt@8dujtz<{sH2K9j6Uf#~(>E0y|`oMSe| z!>(f`u>|H`r(c!SA*d1~umNKF;cbFo2 zba_xdZQzcLw1$#pwGj+z3VDsm0D_5g@cO)id6v>LYjxKx25F1+xAYU1l^LC+>hQ#^ z>4J$WWY>K=Q6u{Y3}KEf@$>a(&NBx@{uug}j|VWO468ahpMK>NxEOe_cg$=cf64iOooq6m5Bdmt8S)8j5usG5Os$AduqLyXiKz&;`#1u#9an$`7Y(gFD zU4X&taacMTdhcXJ!M6ML4L|AoP`WA;rA8D#Z6krM^yN(wE z{$|iPr-P+eDk1UBJ59#CmC}l9Ud`!K>TLCH2fLFkG9< zu$gyEf5o0x4%ju)6sxq%(Ek=pPZh$+m_MD!{_{AIK>mmAE;aZJSYiecIX=}~dOLA) zWtm}%`kU*o-0+Vb>SGJzeM;=4(KquwMw~7Mgnd8Xf@I|4j*=6$*DkI|N$$ z>JeUTl%tVGB+4YguiSgX*4U$il9mrRP9(@GXam1uAuNLH?C}H3|T^)(HWD$ zel$W0*Q;0E(XoG~%TRVLEz;s?b8Q%+ZLpIHifj7YB)fAnfkx=l3merxu6t1y3VRC> z9S3_)=(;zhhghoj?5?qQU=+6y-_hz#&PF|Gx20P6L$zm}B{;?)39ffoFD?@jk6f8= zloEV7T_vGQ)nuXBm^p@2>fwVf$K0j7NzQu6Pz{3=bAx?0o6A(ExG|!fRvCn~?qA;dx~UGyCSN0I-?6Lm+cPxLbe_&T`i>8l#_=1ylcJb{Y!ypvDLt zsyy|G%^*<2>-)688J~XbgL2niVKPzfUqj~fNsIiXj6pgT)m)6EVen3BwZ2UdxXc`D z1&2rEGNJpVa#b`fDP}^O-e#_Hb+UzGZ*`uI+XQT0G^T-!z4oO$S3ouCE1DH+ST;bR z5=M$jH%h`Q$(Q`I7G1nGv1RP(&nF~`Ar;t4BC(NQ_8dLh6U%y7(1HMo6XU!)_nxOC z*S{OO-`^snha-f9x#3N)6p;l*DlNZNk0qIKXRpGHi)t0Tla9yrAMpQLOtX6@a_0J{ zi{B##0;2w3i)qeACT0d^?q>F`|7oZ-Y3Vs^aANq~)DW6OI4ZB+H~K*isXH{_s@}0AmI1y#jmd@GAvVV z<7~;W*cY!1Z6r5K9?w*+)vi#BhaKRo5N$rJXGCv^GSe*b()uLBx}rG@Mt81u9kRP z30m{+%ueO=gv%ox&`Akr4){lWLQ`u#S`wB6@OkWZJo@Avm`_Qf3%Jv zR*m%Z6veUCvGco{u~e#>Xsbp#t@UzVw;T-GlN5@p_-}F78>Hq#8qFxS`d0_gxK=lY zhedEWi((9|J{f1@I!!1!hxHlED`hY{y2dSAE!*NGHclZv+ZuN64ztSOH-OswNA18^ z5^Fiy0Hu{@=4w6)MT!2!wNWvS)mfRf?ba%tX2)XiNt5!C_SpTF^W$W?rbF~Wcs7S2L0EGjBpe{WNuw7rQ*LQe*CB($u9?ysS z2xswJoux{YO`w#ltDysmWx~AU-_$|Xtd)R-qwp)r!S4{c%ke9iG@)3hHf5(TFHM{! z61_+a*unRqF@ZC$;NlttZUu($mP23yTal5dWLq}N-@GtcMiB|^REyWTX7$lOA$ba z;u#y-y0I67ALuE;QPqL)yqkjP0{IufW@oISt_^H>J(KUflT%TD}7 zy}v>XnR_BUP3DB!=etEEGNFZHOBZ1%%zl(@Dwhvnw{0{nMZe ziJl|IGM;<^`$eFsRW+L3X|qdI85QsW=2X@NAE(?+rX6&665&`!q8J;pd}9E<)727s z!1#WqJMwtiy;7gp^74O~*|WnZH=R-y=9`7Z47UKt(-48WOvdDULNufdvb0BtKPP^J zq6pp_l2R#_4`T}EeY{_-;g(`HqqvgPOZ`n&pwOGmiFl>Cu0&yTbCKQhk>u(iW=y?e zs6*ae@|zd=Wxj2p-(+)AEPCZngVZcB2MF~i)%^Q0`FmkAlyeN(p`O$s&am^FcM-(X z@RZu~TD;(HyJHLnP4%(8{xY?wZO!^HX$&#?gwBXZmi`Psf6;wtN$(3#2(Oa9E@sO4plNGRsme@QfrGBSjBE-5dfh8PSk zh<{RY2nQ{-q#RZi+T(kWdqZViI}j$ zL+PVuZpS)O8T7l`^Kl{-G-h_bBjcc4-Y;HSW*EL>1ZZNzL~ToZJJu3O?=uchL0*F# zEm1+-3UIEjY1-e+YWfWw$l)^Dd9l6F#ZA~93Q4F5b#7q5eo=yaoVTg*5X{4mkDXSH zf>|3(e=eIIUO-$J4yE?z@Ot0dNS?1`EYd|!7O{7^o)bmH8*p(E2pjac*guOCw<^XE zFED&mWc3!$c>`E}_sNAHgiP)}ro+-+8yJP&(ZN_HHsjE{r3K(YLto9%#Ssqy<4ja= zBZV-qok9rpjjP35nSs$JX4ct+%y+mdfZf*w;x-DRi~LR1ZT{FN{aYZ-lW|GiRg{Bg z-zwRt%HEz8^m_3K8dWwsvT0el6^kqI`p&d_Qh7Km`cu*=$h&1N{dbazumsT=7~HYjaq;iIn1Isb zo`v*FpufJOhGAavUV^dsVuthdTg!>SXfCfcX|?-)I&zAoI_&aFL}-^NXi-}~&w9&b zsZWc}ybia`NJ(OC`_`iobU8X9=l~9*)) z{t<@-!tJMw!e5-b@Z|$?zOvvpr_+$x5m+-a51cG?N#=mVYj=A6s2#7o5e&w=+njk4QRC+4o|ucEa*XNI|0HuCdp9Ltjmn&+IYJ`|dmVy;*KfPh z&!HCbf;gVcxbD1DnX}%e!I19qUHiUObN(H@jg@-H4l`rU_K<~kB}*N)BhY7GMnHVb zUf+UVN%z$+uZZqI-@3{u-7N{L}@BQG-@Ek~#W>{v7Y=xsu%2j+Z2nNL;8*u}eKze|B z1k%l~T>3g!2HOvM9%&u=t>)qY%8Kg&lV%pW=_CR(W)VY$SkMh}&Frq{vRNc}@7LWC zsJ=NZez=uo>t}(<=T^spEU)+7jqSwUoeb#Qzy9NASM}P!~3O z_WzG6m+dd(vi~o=N>cn^SOr(F|6mog?f!*Ul<#c=Au!0nGcM=Et?tTb{g%wjEpSU* z!m)f0%HXIJ%gicSZ?W-ehm9ZqnOm_GGae^zE<=Gv0vsLU-vj9uIoJqWKCidHKBLvc z!9~(1`3_gaW~kX3VTKFJGp6)#4Uaeu39x++GCDB)QrH@80k73b>VqX(eKE-7Miwx} z05j~775c=KDKjO;B05~CVo`Fy-cj&bf?N}mb0Bz?d2-+1I#wY_W%|^jQQJDnRe>kn zNzt=HGu?Q?oOr@`lYG?z{^5lrRyhHjcR~skJ#7fKwc170W&)13A6@_~%XUndvBMqn zo24c_t$LxKi;Rs>c2J`!0SMv~s3F`O4-;h4L5b8syCT-(j3j1O;y3=<~*#vomG&8ekFUQDm9mz}+M zle|{okf!H6jv^4md@SIidCz9z2}sBKOU8PK{d$yqmJl(#;Tr`9Gw}#we!jJj`??F< zGl=N=4Q4}DiP+1B8Ti`%vI&2V3_RB(C`_t@GdP>?@<)6(xnI2~O5S4L!I#t`V$C(J z1Ptm@-U0<<9Pc(?F^=B?<%E)B<2P$5lx%)|{y21fZ#}Vl85xS2(`OArGFBfM3v!sW zp%>{mss9i5W{77tpEN$j%=vE>{d;KK<4bgvCD9~i6A@_KAf*On`6~K>*_;oaO4Z{} z{APj44^{`l@eqFD?I5Qqb4(B=>(Kg~!oBk>)I^+HcHlv~o%p)>BQVkAG3E+#WZ_J# zC?x6nMv&9xUbV!;=u4y*rA)%9^bYuUvPG(Iz7($t^24GMTAg!;OhYs}E&tkTW(^O;cUYHJUiO3E{MJ z;uL)dhz&4s<+MXJ>LOg_5ei1Y#>^HVniW*7hb};-REs$8X7{x*^jMBePLLadLwpd zY!P@h&|+6qNG`$u_VjHwmIVx?DeSym=mU?sW%%r@E_DW20=t8@rEcNLPig~%cPDLP&rGnAW{BSPNIz0U%F+$ z(2Jl?ONVjPM8=amsB6)#%-aW+x0SYkMNLj1D!M1HinOil(y0qO9xPf6&0ej4y^yM$ z)y&&FLa{Hh1~fe0UlLe2)bMtyd#J5g8&h~lC>KIs+F}V8cK~AOn#^eoGx%MNc|UJR zA=_najFr%Kuay{$is691rCu9StyS@`By{!^k_iYpWw<umbIdbt1f1nhIi!M?z30Qv!8<>N>!ujoT_-z< zX6X3L7dt`Dtscc@SCflwmX8tl!T7*Yd_mYfONIr43{&ETSF!A z1v_yR*`U>p7Z#`KtA~Als9$K*zdVp~19i*znw&b`uSYgMvE;tJyfzIwb^W*3YBIrw z#(rSQp&%*bpY2Ou6{Gsya{2$*Ioex^wHAEe+^!=U9@g^e-{M!KNf!NYo*dWA2uMvpaQ~Jsq4Ms> zq|68`0-ko*2AN8M;U=UJFMQI;Om{8;YPwOWOlRhMjnEF%$L1=7;f{#sPu?qVEQNc_ zv9mrT?H<6qZmIpQrB3l6H{q+XsJTSy0p#f!I{HTqN<=Pv11FzZhXSt?@#tQ_MPRf*v{ z;CW{?hcz3U+6^O~rA&F+@%gY#9%fQovMg%~UR~M~@s)M)z2u81>|VW{)ey+f&>MW+ zb60w$+lQZue}7ZGQF%26_3VfYMz1>bp4dD~ivgjSIeTb$sPKZBg0Dg!5kMXex8c@y zfL)3w;3;w79do0r@LU_(DVD8V5YAuP(XOADa{G>9;sUu{qDbHi?Sxz!00G+kxMRDCp6Kw7@4Ti`hoD zb%sUV$(@LVKT@Inxt4BmM%!|dx>2p9G18269v&}$Ua#6|w=Z`BB>Kfu=E&Y7>Jt$Q za#y^e1d}s_2B3emP7X`hk!?c6(THn8MEqtBNhR~>NriIOb>Q_w+^S2r&Y$fcK7rLO z)9bj%PVk3R?SU(!0T5mZw`{HiOasxRMj*(uF38cI6;E#=N2Uxfs`hL=8BQqsJ@c>c z{B(7nF%Ke2fd*nAYtsnrHpXJMOz7ye^6j#0e^!#BqD3rh{+tqi`}Y2|^c|_;pGWHc z@;Z+FK*-q2VD?xmdKmfZ5M8)T|IzU#eW8C{*8XHbzR?=MO5qlwc}olsJl zJ~K?cZqJV#LUhrzK;jx$Bj%kLqwk63tvLLI`3=G2;1@gPdsx6TrMfnE!BM zs=INX+!OcAfyg0JIdUN>!nsXk8+!rx=)z#X#n@*g_T4^OX)T6wcLbs)4&G`x#*6(f zpHzf?&JYuAR|VEuS9E?lTecPUjR<<5{HeKt$^vX?#if#9*}67vuibC=)Pa6T=?Q{I zfQTZZc71?;_jKISYzc(hAB;SVi5~WGnfPSZ;>|Z~=&=8xkfNQxFM9Q&k8^ANZ7GGl z3x>obKwSdLqB-{BXQ&fmH@fcQqvM@CsEuJMpQ4&#FdD<`9(1#@2cpf@U!T}o6}uC5 z0d4z?f%hEBG%oDGlR9PPHiF;2xL7hUZh95k&cgrwP-rT!VGLvIIRR) zV4=Z0TyY}Ky%Bl_QQv=qQ7fB3vdlC48%9*fI^%$-`C2q;G&fP7M*BVK*NNPuz<+Jv{DBV z#GeB_TAJV^K+@JjBO&@%Dj%I-v1J7P0AaOc%u3`7sx?{?3J5vI?U=P=o`lWtCstnU z)9aZt|$P|zA^L&OBi0Z9jzkLrYA^(xLmD7mKsulsvnzYSb1$@X27j=0YEOAQyCU5aqt9c>#L+n7{c-2owDEEE(5_kAQx(&|^ZTdZBQJbmU zm=%=cB}A+?o`N-iQZWvHJ9r@aze1d$Ky)xX1JS~vSoMdN(LX=ghkC{F+xmk==C4Z2r73bRZU!NVO2YAKR^3z&f-} z$`ZE#cUC1D)ee@MxMXO7kTvua2jb@q7W2Zz8eVQ=CC~wnBD1-9vh>8@|Gs-NmpN;~ zhcBUJB7~dslsEFS3Uo>ZgSqQ&=wsz8eD8)4yL=Hb=hKvDDh>-&r;4ET#I#0}$```by)E5^KJbU~>09ZwYh6XYmEz!xQZVEN z;u)*|W*o=HW`kV4pfp!Vc0?4Yb(;-duBy&!(^+kC!-wGFC0-1Y+=FZg!U5K%!l^6G znOs1|)9{>P9iiG-e$quR#8@i99&65sc*Xt8Y ze9200NHaW@8*f>>B`qFeXPYO78CvaR1_-U^lx}nth(u{LtPd9wDoyFjWYWQ5jN!1g z?#1f(PE)mHN>l2AdBkb(lbGFq(xG;U>Cc@C^f3iJ*O%%(b^geC#=l~Ron0|kGxolt#CIf z!#oAF4uRU;An4irUgg+?H`h9hGh}9nQ6_{s>Q>svO|@3ALaS<-q|LxTuQdS!DkC!T z@69d_l~Q6r@83L+wIpyE{S1vW8xC0K}72ig|g zv=VJe`LYSz_+=a=)6JlLJ8WIa z=&jgR0e{ugkYGUdn1mP7B1#tTUb3wift3#!MT)IN!4ybs4A9yxqH4nGsdAVnXTm2~ z*w=2>^MvsA#?};HeHrWL;Nr5}!*#Kf_2e-9&SCMD+3-E3^>bVq@ZbH`2v}Z0ef2{Z zuek-ZPwm@y0^`g^gCS6f94~3U|3noHLBH8Uru2 zW-j?#g$XXawjN1_4AKxB@dp>rj@lhk+4F ze)xW}iM!sT-w=6-t$=DU{+ckfKK2ZO=vYIJ5x}-ue82~orEjSIp32s#Cz7pPI>Vyi z`9oy~8o|E-zOk{PR--=hJP4F63`UDyXs(1co8*r&hPb}9kUTe_htYNt*ud24wX2#} zRc>(`S5NcV+bzEFa9(n0DqlLu`CY6x^}d<+-E@2XbbmFnzh;xXL5irDW0eP%02#tO zCr{ZRHc3Pz=yT~H$>T|UvQ6kWC8s2&le}hFcIZd7Rl?affcfwUM*>|zxr9Po8>LS> zF2yhr_dasOdUldHcRa9Dxf4{Y@yqwh|HfZp9ga}pfHHDNA~gA47tmmZRzf5pI4-cN zTP>{8hZ8XZ++(`Y?>y$!Smn-`0!-<>D8L(ym5ei+*%c50`9~q#n)x&2QVr*TcczB& zkcL1L<`J`tlt&nTRb>E4X`N1KGQa1ONoI7%1`5Y;9+R=79%{SD#%7gTW_kOs_^Q3w zY{1vT&*#<7w;R*0)aS$S@W32+u5ryPLqH*$Rrpnw2Igf2gZGQiOMBlKsBBmJiG>j^ z`1ghJB@O0Nzier%Lf@yVtR&0VDlKJXzm0W6T7a6Ic^8sn$kgW_8vQP$O@k<8bd-wv z{XVAK-{Hjb#t6#kX+mLlDrxNINQ5fWvwcY^HpEA(c7FB!7ck%ts)nN8eY2yYVX%>v zl*{J-ZuA=?OGtPvOeC;jaqdj%7hR>_N8>fHoAzs6M|m(yI_5ZeQ~S% zBe`FoB`2;ayJ_O!;ju5{9!1ebAqBQgSYoi7DVRe90iogo=+<&eQN<2Jnw)~pCJUmeS86-A?cSQ9HlxF?!d;@L_Qu6)abaR9v0&)O&op^<1 zds{I+i>}bs7_)Q?#p0-tYJV>^Auk+7VBrtardQ$^E4^-LRq~GFaYB1|Z6AWrPc+m0 z<^?Gj{}6jL7t1~g+C4NGlZ)Sx;wzmq9nwB{l4HH_+AD$Avfkfj>3A9_uMG|T`c-8{ zj$bCl1x%KP;)N%4B|Uw>WGbCLus5C**p`w3ZHg(4>=l(`EhRBO;(PVjMEYe7;-G%C z()Uh_?wO(GG_kE!a)9WZ)&{K0uSP4$hel|l3qKvKe4q$AyA?CDl6qBgp7U+*aA?kmc=<(7DYkx!7gnX4Xq@5qfCBeeAx{2MbJBbih z_1XfSD$H3C9&cHZBtr$C=FrDF_3xY$`l-j3Zfm?7bcXmDsHcLs#=v#s-X|0!0;*q6 zYt8w?4y@|d?b8mre=y!FOy`H;iPU}`;V6{7)Lc6yS+V%vH~xNq{Cr;sc;xS$A{1M= zrC^mUkJ<&>u6wj^1OsIXiG;%A#7ZE_bFiLu-(oy38lE}-Qq1=D3=A{0IAAWnD>y1T&u8sP-y?pqHQR5nTVKPvsI3n)Z7hE2C z!*Mz*h=++T`6icND#A}me2g8MMF2xK~NtlB6TBT7>C*!*ygi z_MabeD#561QOB{6hs3c33B_#4YqBpl*k|QRelTVr)IV?QDtZv{rm|9SZdB^>`Xs~5 ziqlAi+Ufaq*2*YYz`t514H=7szf;nf{;^)9p)4=8-^P=V2_(!+;itQj!TU^sy(eL1 zffV*z1hLL)s`Ke!t1QZSQXOegB*zg#TK3_N3zENf9+VhdonR40N7xF>|f$*m8R@@a9EvTsbvAYQ;|W zmoKK(yO>@xn#UfGTQy@uaPwRYHf`@BQ^YB`ibon; zrRKJa2gU6{w4>;h=--VYgEB~X`VVb)Opl&Fx;1k2o_ndtVYx%W}s>Z>S zB|TqRc!{z5)7^x(!64|_O5G>G#%7KKDM%T5-#z@YU9Q^@idkU81j`wbusXJ~|6Qjf z7|&+F(@LpW7o}=ae3H(nm)h}itPBk{r-X)jcVw0m54&TsEb$C{;4z}Yof7D z{L6l16?UH6VH1I&byFQ(jW$~Z;l94}I1NeT{Wkz!OI9LLN{uFS%7xc2_enjB)hLK& zN-)aSvh2lnZM3D7NZqnvK-n(u4dyyrDZmX-E(VRk?q@lcfP*-rZe*AhuM?yQiEiGu zg{SQgrOG--Xz9WDTeh3q!|-8Bq{PX>mqgmnn3}c+GFgI^{()xarW_}4&d#bF$aT!b ztJIt)QGxSRtZ_rtV-6}8xT&0la2`keMrUFe*ui*#bLzFnVCr=%k%&VQbAXI^i{qVi z`rlLr!8?hWp`Ktps*Y_he+h>>b;Br*9WG0Y%M@zm?vWE>A;QpTu1-y(7=Q zKzeZg8BDJ-{J$1aEW z72G#SM(ce7+)d-yp-4L&t%{J}xGQ6waL6aFOxt9WrZ^_qVpqQi@8>1q_YB+%&M(DM zvpXkpnkbqfC15?F??~wt6%oa}s70msrU3=x3-v6GuhL8YvP2&af(D&TUzN+2m{=DOty=wf}U5( zM%7M*G?BwrX)3vDtF7kQPH%P+s|@Qt^2;02A2YQ5zCnDJ29my{3x-@b2D*9-DIGwF z*uAOoapvPs_bxl^x)`;69K+9zDoIX~JP#0Zo|Yoh<+2v_Qd^am!w<#i8!vH#BP94C z025vA?1MA7NP7fsi}b*{khA@i>juR;AYjlq$y>Mttj9+HLRG(OTEzo4>3%#xnH9gf zyt?1QVyVTiE>6pl^5ERxO$kYxdPh~^*BeTg?|@+&Etqd=2PC@PSz!YMIOuBvt5hP~ z>s-4|+ZHAa%vFa9CXYl3t1R~2874&7*`sQjeH&3&+FN0++*B$(0ma`?=o5sfNQjK% zseeY!W4}l;29TtfJUyPjj1(P@$wcom@uY%bV%bDxc?wHK*Lr*lrA`53Qqni zDsMh~17&Y|QC@reuM3>N44*!FU3o@jC(=`E)G|4xfpz-(n4RD_v3LYud);`5u&CwF z7H*xYI{VGiSK4;o#nM;4e~pQyVb=W7u-FD&3LB#lVIqFg!Y*J(6*~)dNK8aJ^l4+~ z!n5i3A=0DgTiyKhWr1673kMEUIE7zF>tw`mLWXQuhF*)-J^UN>l7?W8TAr2E7#U+a=a^@N zn=GE6G=4hZ8Q0oqO-%J1PX^Yzo|_3Xro7D0`odFVJ7c0SQ4Kv*Jvl*~vS%@wYEn)a zmv@S-&s^)X<$}`1t^ob1LqBCiFQ<$S%-VsTgToVU0!D~{ten)_gIGetiPTpKi^iRt zR7oLWWqIX*h@gLI;|_MQy&f-E_VFTc_00}vVQRk9y0k!Ng~O&~&Y=o#iZWSOJkEg!8s2{c za6s-0b5ia}3^3>2+wil&3iDMZg1(znY72Dcyu%m%vK$P#sjMq&11*Q z9bQ&qisJG&3w0L}C_)ZLSa;RpRS$q5c9N zpjuY#LBgbx-gTHbo(GjErsF{}X_35Ul3M`{Ao!Kg@^NWJ1b%%w@TQ8RYWILugmL#xaM^UW zW0=#bgv6+z(F+EfZy#S%lea^ypbame)Zi_%a?P*@qW1B7!UcMviB9bR&b19u@Cf`T z!k8|Dm4n^?m2wfy@Ia~Q;xIt;L-K3ggY6;mKrW$)*B}G3SA;O1h%B_v1ly^6=ncp5 z{UfbL;FuS^3gv9f?tp(*%rP2m1x$R9H+TafRfaq7_GGLgj*@U4_b~>BVix80aTYc` zblG`(Y}~?Rijvp5EjKMWhd*E*tjHKlx}G%RpU#O`@wNZN+6^nayTXDD?Ns5iO+T5s zS10(7l$7|BK!o-0o{c?%{#cI^H{^mr4xR^^C=^>=1pb8EFx-MGfkoqHb=2Xc0p3Jn zONW7kO8KHs zx2;Rc#tc{dUfoi|r^f0VbI9J-h0_3>t#a}Qj}pD5!IX0iX;jwDl=759+5CooWgyi* z&ij%`vCt`zRV*~tbr9vPOuk2hm5Y@d`R*8NN4L6U;JtC zVn2{fmjRGe@*lgSX~|!y!s2I7J$YdB99g%@k4}JY`~nwjEsa1&u11zO>-w9=$3}~> zuImJZKdYM0l?A0s2W|y~E)m06?ir!$dsh%}A1xg%nT}^W{#{YA$^hGjFRw1iz_R-1 z+G|G9f&j1EA#0oREmnuct@&VR?qiZ#me^oFV zndjf4kD#Rj-B>cWSgh29IQ)kzw=u>Q8dOn_)9Mjxp%G0+T{P-VsQQ5y+q}M zRa@6E?{Q0t@Yj(nW?@j_;kxrqFZk?c<`%uNfLOm662E!iiV>a|H)_a8d_9 zwTkdoz{#B9p|e9NTxXr$E(e|XO7%kj0b3S9>7}IQ1-g8?{C9L-X%g%_^C7ytO}E#) zSf%@{&XS$CXbGn5($1CVRp!QOCTw^TG&tIe1#R>nk!UD?Pwv7x{Xlr?cceobBZko+ z^o6s&4lE7^tIwdfXAGwUVwc&I;n5_YQdQC0VuSc{Z&svO>v+Wda^xnyH2)g@l7RmbTr@oZIMov^bzQyf=w+5mvD)dI{+un8O~rs_ z&1S%R!rGpmZr0E#gFaOWtWZuU#Mi47W&ZUa@UzMG`0p0;MUjvo%wl~TI_Ho7P3)=F z-Ztk2CIk2~#M`_$WRAvfD{tr5cN&G0<12km69;6#)VApZ|uK%u|n+gb?;7#U3kdt^VAR^yeRaa%ba;@>|d=rGkY0{qLNFcMHe623>LRrRz zPmlQFwS}!0ug`80(`WejD`e*0Jbd)XFt;0M53&O4s}zgO@!I}rX4X=E7_cI`yLC)r z^Y=nhEP!Cvgi+PcKE}mTC3#2NW5@mFF;jr7$OLUpT<8YoX9c8KpcG=pAQ0R<%m~avg)se z{#sf~6f<3G2sx=8HA1C5RuoFLgdUah~WzsbThY_&NlV6 ztVhAc`)C{b7T${UL7E%*D8S+!6yzx`XEj(@n2ZRnP>6EDidCtpsO=lhrmfPHbiT~6 zSXNnNiEW{53SwTL(7d1?n=Lvtw(y<`E~(U)g7FVGrwb))Tb@LbLR?A^)fsRcX!_1F zedXw&6*Z8P?&^Ei%6nk^7ss%E=mUtOa~qc&Z<1L5S9jbe(MQI~}W{W+)8F!Br4Wu#xPzH9nt zB<6LD%tq*tgGSlx=j%cn#lYaA}aW#?cs(9Y~pUztY%BZ}IcR}{cmhurRk zY#Aj-kgZuBPpi8xk@imGxA9DjW4`vnzxopN8{Od!)LI=<*>d<6Y>`!!o4^MGw35{- z7N6af4MsXxX*&vF%1@e;EX)f*BgUwUauF5_t_J7e-N(;ZVKx|0pTc%VSUn+b*65eg zRwR86UvC4uGOJ=~)@?Sz`GIOtkM^52MR#0o`_MplHy_{Kd|IJnKGfcB1IHCA&3z#Y zhMPV*chk`nE^t_oUQZ*K%eZ8Y%x`NGi}-Wx+z;VMPmsV1vB>sxTX+F? z6$aerLe{{AE*BfT8S-9K)$d1F0ucHQMbEijR{6quChNoA1|Q6AhA6M>DqE}Z(pBHI z)}Vek_xHAX_*;GB4v+t!^&mk0h9!BUKeu4K-M7xiMoWKO(|KcgaHp8GqDHO)0oY?@ z(YyAE1Z%%3@r!SNDmYMW7E?WJ{CeIU;I}odiA`m&;hk3v-NTy44UCst^kIxim?~=9 z#LUtuZlxp8SzsY<)d?M^FxlY1AXl&x99ZodR>MQhrt9JrDC=X?OH!G}MFO*knmLyM z7a_El)vii(8|XzBpw8syx6W@tQb~eFy}aPQttfX}q6TGUk<_1A65#yS{{v7<0|XQR z0ssgAK(5vZtW9;C+D8BYM_&N|A^-pYZf|5|Uu|z>b!=sGFFX?xp7lJNKZ3XEoFNO~yBl8?#AmNSlzj5qP|SaP!aX!Hso2}+1bKueI8 zWk>)0t*bAf0fL8=?6cV&iv-ct-Bs1q)zx+E+`02ja3?rAkApDHhVlMyx03R50Z2iPt#}|Jbm{n?**@;^a_85cM&84N@hu% z2hlVR=6O7tkAqP*4Wf&SaWaTzNtOmtI#hiP<4^H8yNIXIAP+9%@wgWpWkEC;#CeVY zli5D}!B1fr{>qOgI6Ueq7)`TD(9&~gJ(4%^&p-1x4vzC|J{`m-?ep2}BH!QHIZI~e z^L}rTO?I%^uQ~nM>5sGi&ZoV*z1@4g-JNmL-$8gV;SPY>`DPdo#yBGx_7?uQ6@2|B zfd6DXcW?%?s|%PrjcQAeYKNX~J)*bc&-6B-aTE>W;8mJm#Dm$}3&c$>F!8?~q>}pY zPVj0JgtO^94!c3t{}o!a@*vI9?KquJ;%U@J%3V$R**GTzUrZ6LniNSo7|)0C5IzuH zfhLn^HiHM#IG>MaJ-e&$mpJ*OI!e-* zMls89`u2F*@n>w7q-Vh-o}Fj*U^~u0{t0j*7+o-e?qchg9sM~!&o2L!42N<0-G0!| zvT+=x!EeDll_5N;!sj7iJsIJ6h|lMfK8zbVIho+I3=lJzPxCmqJWs|NVK`_;zcbLI z^8}VgZgYIXbj1e1I%lyo%iVUSoB0KHies5XpOeXa5)2~1Xg-SuAA>yk0w|2~JP2Q< zFt22G6$a2rn9j%J(2uh{FTRHH#q1nVB@_NFKnRTxW-%a+GMmpX<};jhKD!zNtuJ9^ z1yA1`z6u7}7{)dL{s#&148JhX4CeVPn;atq}uA)pQ3`X;Gz{?JYmu9#wKV=`|p&z3-d;hSh8(GOKj9$S77PIskQQY0W`wjneAx-nB@S^aasYc8U@$Ei)wtATBh+CY)7AB-7d}i z%``f9@80zy6VN~t{V7h*R$N<)@H#k;rqN)A`{XRM8~$FHG6Sl_RW-H_VH-)$)<73( zE@NsQL^Bl7gLA+Hila}_crH6s4jKn6mccXwu{nW3B^Mya#{k$rSqDFiN}>h>hMg?} zF`S+W3>!ilHdRn4`bo+P4oSigo?HNm!XTFpXq6*EQD>4#KRF1(-LPWU`__-a{uGu^ z8G8-5EanYthPxGxh@~cMG_Y^wK^y@ZS)c?TR@|W=KTrl!yt#wRDnELOPkVwhCi9JV(VX!j-N&HmcCsG@FCOgCT>6 zK98q8(3od2PdjzM5;_Q7fYNC^oDZN`AJpK>VE3W5u)4U;NJ*R0+@0Z&M&NMGOJJ-? zMyeh&FK}WvKphwx$DgAKvOo2+?C!>l>}(ZFaGjy0wF30k+bvQ8q#g9YY$)3WZ58wh z_YfPlt&!$H9;4v0T8rGCleFp84hdT*JmjRGlP))`Xk`yS(J!-%AZ)>K)Ej<9hX?DR zwB3&OWw70#uReBygC~OR&PwR+2EdTCbKt1XG7S z@o0o<5^BFO0fmm8kplgB6kfVDQK@7SK+-MvxCOM@%K7h-+F*ft&=8wV^8qbxVKv-} zW8kw0WDK04r&lnAX^bKo4!C$qr)AZLZzQE)?Jy55ttry!G8r6lXW6t)VZ63axOD0} z#G^rQprIjBP}PaXTlO;^oAZc=?mpsvoo9Lk1$bk(171t+BRVWQkA{Fv=fN~SgV{Kb z*aF}l=4q@&biVWQw5;IqfR-o`RM= z*?fwJEX(Y`uz;CaApv=rOmo~`&*r$ZDySqc+UxMI&34q`g)8QYhr&ARW?>DE;!64v3s^gSt|l6PtH={2zUbPA9f+^I)$Am z1F3l)N75BG0Q)+aF|8E`N((d^XW2zB_>jkZCRMr@OW*+=u26MB4Tp4^;GEcVh{n*_ zxgO*{L8d^rpYHY^^zP}0$W{}@yie1^dBH@H|X_x1fRYhpA>dS zXJ51@hG+*{^$c(}Mh5%(PdJ!(Lm$$-P zo03G+Fk)KxI-)bM>0AI__ZqV4FF+jWz5P{8w)=E^3}6PW!&uq6gTK55arHGop%mQT z3$A^P?KH67g_f5jMsUuaPQW5#SlO%?%`m%nd+>h^ zsBh2NB|uS;>jo<_ioyqqOp&5#G>Q58OzGi4Je`VdT^3h1Q8!MJzNCY@xt^_;P2ldRhLhOl;}WVgS{wka!e`lJ0+>0x%4hK;c!7<94{@Ay zl|o?9Xvwb;@Vu6}MtP5~{MxM%T(<1s%yw`)g3GztPW(B^XTT0^fJEx?SJe(}6De$A zgr@`MQ8;|@z-U}=;1_Jyga4(O;XiH(=ZPK-uZ<8oj{@YR7jNFacypv)w0PFTKmX|c zeF6LUhog=Ii@70!E7l9OY37V^hkT!+WK6ezN;&;M%LUX0V#~`3@I$*uf6Y0nDYm&m zcG-Q1;-(89?)P?=;~X0PJ~G2&b&@YI=^5jwVnQWY$S?VvzNtQHcVAxN!qD?wa zJ{y7#6Y?e;={qQ`+R_c_4D5-u)Ld{)?=;jyY#k}sAjRQR9-YO#RxM8N_U>s!qCw*V zE>uaIJmlXA<-s;784;=((;=NzuGD2di1QifD)l-54vul84Ria!rz{!PigAtBXoRLE zKCRJ_2=v2z4s<<30}d-RS)wi3aHRmtDE^;$0y-Bgyzy0+n#|LSD8W6j&=&38c}JgH z6_5{ULO*ZGF_E+cZD8ICKK$+F%a_l49xnY>bLqDWxOA3%IQ*F}bjk>-kZsSvkUbNj zqfZXNgireb`WYNPrr8)SH|ZjDMhfNSc?RU;7Y;$`jK-uAOrq&W!Ih zo^;QcZDxEe17n&GpwD>t4D_HFR|VdVUBJ!4Yi2ljRL04-z1>}l&j1au9V0@cv3{uL z^3)wiVOsU*v>F!L;qos$Dq;3DAjDbM4#tWI$}_EH7V+dm6ItN>c=AdBmasW>4l=eh zH6B*m`rJd&0KPC)E?oK`B)nacF+AgkmSmULdWhG(o;_S13Cd0Sa8fU- zn-H8&7&1c?w$@xqT!FvmxZRXrYRC(MFz#>PhSPDrmDu7Z*gE5sF5@D6O-^4KUzNb+ z%clg-fnlx~CDn_{ob2JXNs`C<_y~?JuuYTa2kPD|@ATT#i?UCKu%5Cx$QdLA!xnjF z(KnZd;nTuY+D6GzY^tAhdN9qj-7ac9pnN=sYXH>= zlfjGvkN<-FvUZ}Lm~OMQ-6FO?9jn-td5u6?%WZ+eV=KtDi%9W9@Dwk{!LN;+0q~?e zgUxRm!#ao&B(SjD_rrZrJO<{2OHo5o|4LFw_N<()?wJl~u#ny{8$HI|y-uaFjF%-l zx18UmsEy<}U1jxZHnnpcj9S zqPxZ%aD+xZyuW8Ef(;gI%D7z40P}cLjGks!z2NC^nBYJ`n1z`#FJLEq4OE%N$fPh< zSJUD{SVjz8bIwz+%)uGbEa#lnUNNF4NYVkg zS)e7D?+GGn>7xk$yx&N6%OD%eZdjE-jIY<4vhbi6!J}jNaN<1emtXAn*d677zZHIC z*zm8fYdlio*+O9zUh6nkvd1w0;Lffkjp1PsfUCf9aI6t}XZIvH={j$K2LZR;dqN0- z0q)x^_D;x=c)tMU+5&Q2P0Ay>|LN|sU5#qhHiAC&|D8Ayi7aGv4}z|FajI$J#B{?OnAJ1p=*@;~#a*XlaMgY)04 z@Cc>a32^286YDv!F{%4t{y08veThIj*@buDV;`@=2+YYzN4L0sw2&_{WC|ftfg>Jt zZST5$@W8YA!&(}k9_~BK?Ks$V*IxiMDLjA`y98a1!E$^8bA-lE*=^1E9cqw)51a09 zv{{tILlE5eNDM^t9_)MpVj6WoU@gQne&kMY+bN?%c@`SpM;}UbDhbp+J&V!VZy-m1 zy0nGQL-e=-7cTH-z7j>JwG?_FNtyke@71hWXl59*^n=Z&&X2x5&BwfsgC#I<+yf1K_rYCOW65Yr z5`N2~=ob5P;^m|T(sDZO)o(7L*`3sefxBmRnFq((ZQg`0>42%;B>MuRih9}fY$r~) zKOBOoe30+_68Crh6D|b~2h-$Yw)0+TPdh(Agi|y=WrY*pBhVfDtbeQ>^KYm_0Nv3( zo0b#eor05K!MQ`XAWvb}-JYXSrs6W9z@5$EPzfR@mcyd$C#t=e@pJ{pmm@OdU0-Mos zRIA;5s`D6Ltb=6lCRJ^exVwejntt4)UqJxum43Zsl!*mU5ZFc)omAPqlTH&ATcG$f zLq{2eWh`C5n`*pWJ+?*KamX&ddQpS@4Xml^Ha;~$e4-~`Atq5@Mnl1LXt{$~D2ENt zI&hav0*frNOAt-Hi|2hbrI^?FNfmW>UG)@jb&MW5q_sI(Yb%UGxxE%+U)T?Ajc~x4 zACXvrSfuA^4PlqaOBC>v_G`@;Xw-r930o5m1&47DH6w93vKe6U2-*<cd@Y|o`l0l_ffdlrJ55y?wmLmTFm9-70z$^jQz zV7@kuYpK=syth~P-lAh%SafAJ`|WU;7yPU}KGkSEACFy+!Te@qBs!7hXumrMc26OY zsvrj^uylOk&UYmP=+2T~@sB*aBfH8PffYvF7GXhGmIV>WnR`6j_hG-IIJ;}O>6$|VUZGTmE_4<1N%gRRX^yHxBiy-g%D=c&6R&(COzt4|~r+&OSa?9wPi#Ifkev zs<1#@*i@N2K z=+VtO0V%W`pZJ}iO^{-t0ctXW(Is1cz4kY-VxlS>j~>=K9^JPbk3#7zZ02pGhNk4k zJpGtvmndySa$*=5KRZ6D^gvoj-ynP+pS}2eajW#@F55eJZyj)e)lOrINn<3od~8K< zLOLhsDg@Q)EDP(V$^TscvVi0=o;sTIE9(&aISk}h$^D$}HGy9Ap*eNvbx#{MDz{aj zMGHW;tc!QPFYWk%-Nrf>k9tb@BmG`CUGg-@I_l#KtJ5C7k|XJ@QvU^e%L4iNQ*-*8 zmKcM$TI;W*J;5oKzQVa)JWAn7q}-pg0i@pnFSk~(9D6w79oqk6Ot+n>(q4Ny|B=4R zM^(NbW2AF7rNjxw2Lyc8@`U^wGI1ah?^_H59B5VCcv~s*>Ie{szDX|NDi?293O*HU zQ?_8fTMDza+<{l#i!l#x`Me@vEHZ{i%K^8UX0wa_4JAvV?i;UtJ=n^{Pj8e~{|7|6 zZ3cve4Ymq7FM_3e0Ip5&3fQ{e!FeT*wKL9(!-sHNhfXTu*ePMcA*TMg1Op@)JkUF4 z49fZ(yBVW;8b}}HmMf{^z;g&wV8BMKQA%e=%2{-o$$^86@5eb9rBm@ZBHjiw*bKRT zVT!gS&msJ5=l<^B|4~wDU-6)nJEP^%cmdIGVU_WHz)OE`c_N zJzCC`es?Ze)Cy+Tfm6EkP_J2=&n2|SFc&qvHjq7XMuy|)#Q?G0!rwrm(<_fkCf$N_ zH}#}kvh|$n%!krMx%LC|yR$(lZ3}0+t)3dwJ2*HvsBdjWV`DS)ah2%_3d(UrsxLSj z4Pp@FL&5}P_4tpk_~Cjh^g6#v4}|G<3lkz`;EMmJ77wWiOaH!Aty!RiRt`rJH1Ctw zP27u?l(vm~Sp0O3E>*4BlYN67r`hPqh^b9n2tk~gUc+Ei^#V}18d zl+M|nu9VwcMaO}D3rc*HbBZy}5_I;?yGq*JLvB!6Z+J)QySIQ^kFe&ILIb+0oe(=i&x6 zb~y1G=qk04$dHg`+u6mY&J>DlDsWSi(M?ifViwfT5CAaF82SjEoBuLIhdH=fsk;j# z#3#A(f2Vv7bCtBuknI2J@;o~5L6lUorJjDbl8Jfc}JuAaPn`!=|@cmKhD zP@D=(;-q>G5*d$8LzQL&=7ahYxXKe5tt2sOc8eGY6;2_&nipkuA;%}UyP%o%yuJ>K ziFNX5Xg!1k4nxY0w|^UqvW>3!b}cC$UvVXob`etfBy?uIMWSur zMNHmJfvaZYJoIr6Uxs18397?=$aQR)GYXeA62(e`0E*HLl9Eb}?^9$sOu4ciU24*n zO0bmT2^2&>oP)l|IY_6Jn1jPSE~g0G04}E-IlsQV;hEWZ<&gkgq-7C_3j*|FxNlsmY^?xie0CyYN_TAQ17tl3L_)iZ)q#f^te z?j+ad2sMi4^yfj>k)K~#hx$tD+z$Qq>?rEd1YU|j!)V3>728~OC!t2%?7Fbn2s&`5$QnR2S=)wUCMgCAfpy-Cef9o9NB&vW2jNX=@3_ zq2bo^3%;`cJ`!m!jPTAKFt6XaL&mJg=p>Lz?h{!#=}ytD1tfKK(B@%oe)YBUE%F~v zq5Ysa?@5bl!BW$xO6l=%p0rTv)T+K!%L}nTiL?s-`q!Y$krGzbm?LExn^$Ne%b%St zVQ){Pd8azAVjB-RO`Dp`;I-`e>jc6Vt}!zneboW{S4!pKoFOAww%TuXQ> z@Me`$ssAi`W0YrG6yEV3ly`zb?XmP!`pH9f z4M~UFR5Wumj?Ty^qBo8lnHQ5vi3^)}9`(}fxeeGPHp$(hcBinyGH~CWWj2TL^72f) zYgKacNo|!U<-B@Hzyu+i@*wujoPSs}dGd|Y!6;LA;sm1E+`DG^lCX)4iz#{~5F{?v z(o70jdu_EGszwSp)L*(tDNSFd&@Imz8#kRbxj|uU5EZ!L8b2gSY~TLN)I>M=Csr|| zqa~)pY0MYT0SoU0_(MF5RZ*h~Msh(GnZc71;oDM5gb_P}xZ9ofYbh%bsvUUcf>(>; zDDhOK&~{F9wGg3h#dG5_gW~F`vmEhJk7|Dq8;p~jG7wpCq2h>?00GA&`I*bW`lc$;&u8|~wBjHm?3Jt6 zEGm8kQK^OAadMV-IZkjXgAEK#R~ESnp1wJJ6~y@0RIBP-=Y&71PLkAZbMTs2y}8#CNv{jmA<28qL$(N>9S7PeD20!*b5x7~wE;a@7j zh+wODv{TQ)dlE!jIoPVutjz?QVy;2=AP6YrYD%;VA}>7hN6TQ_dhbttxYL<3J?_ky zDN}v55Ky1C%Q$Sp$?0=Em~)kClL(26;|!U&QkZazCS|HJtCgx=LHp5gY4TF-Q~A(a z=S<-E7UnjBJHpuurM##!CHI_+q-rZ!77E-BHGBD6la>2VUGbBQ4 zW}{LS)FNEkiC5V(yNp#bYb(SSQHfj6bQ=8i&!6`mKH`5>IRW6Ux0fkTp~cPMipF^p zi=2s2RW~+V&&)Ov);oHC+uQT0u}{l;S5f8VC?j z){b_|h%)0-?-=j#%_9zgS0K+j!ZH$<9OQ=338UVt8gLh-GpN2td(Dum-o9xx?=3`i z#mMWBzU1I_w!Do|0sePy@qFmhJ@|BQS(>PJw?UnIOHim{M$Kro!9;}8g`UD}f zM-Mi^A_~@RGRuQC8KwdQx4=3YTYo%eg8w~S7oGI!A$)pR$6B|5RW*zy@w{n#Zj4VW z5c|7u?25Qui-%1bjAxWKmJtAm|B3Q~JU-&bW#e_}iIpGY^z=f3IX#8(Fbq3O#-iTY zy=JxEk9FrgPKA3?Hw4NoQs}*=-viyvZ}nq-QvITf+9y?})+*DeUAX+FEu>x7XdA#) zQ*ND`9b2!IIn6hg<7$-KuZ;3mRj#LKrmQKN^ld2Gjz8F`aw5q011=y^wG_YS3rm+u zX=ZpeG{y%o+RO&$WQxhA9uWT`gPXBq_Zpcf-vVvy(%LGyl54pQ^Zh?0ek<(E$X$ns z`A#w85d+Qva&#FbGee&WTW?4yQoXnZpyt2a-E*I{+qGBcP( zX;|bw4Hu0veI2_9P_o6kn3de`UAl7Rw_L9w^u(amhlR4h;3`r^y`5O*(sEV`1C(lB zL(;*Cs@kH*C?2EcE`~&aZPtQCo|tOxBeLmfb8bQ^vQ#L0{SC#oj)?+FVseUsYKv z6<6Dr6b(y@-rw^iLbEEXKpj?v&jJYbSn9NSO{$9&ph!^V0@;o(EeB?UO%1iB~`tcjh7u~sEELM-m{9U0;p$*(o!)oCB zb^D?=OjRou8ikAs@73@ZjHT`A`8%%2UFxahHT^G~>~4d@%(_#rHD@m#ArrQ8Jaq`E zHuX0`%-&Kby-lHMfhoG#$d?~*6&@Q8a5=o*Tk#aVc`Pq5CpVwbIult1`*oK6rRZ>f zZHvZDQe}Zjc)6KwW`gTctInFgG~FI-&>D3+R9s-ET^<9Qp7^GyTW6AQvh(ZgWaeW< z6)fG(vP3Ok=*d0`Qr<#z{{#JWgj+Y!X@WCo{QA0KTv2I zxkz);=dcIq6~T!j8OQsL$_#=FMjpRNKQ*W`s9tTi{`KOgpWYsjBi6rD{*po5(-qW| z_6LJ~4d^W>^+4~ne1%Yr7co|+n{m;$=u&+rTu1fC%Q;>=)`kAn~eXAMK8}F>pZxSbE!3gLe|We`ln~~ zi(|Ch>O+>sk6WcD-@_^Wd{%g_oleaI&wGQbC@nqkya!oruL=*qFS-20+cb8YLSb+$ zv*&x}X%3!B#qXZCAhK>2m!Ejvyh10zOV1;DcQult_^Tl4_xdL?>;LMmmhiG{h>%;d{iW{JgWc@7XiHX5L!R=0(oB`Ha+pa`Ado5^3418f0v1_ z9lO~Mw&Rkji%@d={!28dXvs0 zh{J$^xGkZ~3S>cb9$^#;_E21C5N_i743#P>OsqJ`T>~!AIVA(f-PrlW{|4E1cAEgd$V4phlVD%dE!!=u z3)Pjgz?9uy8qf?AYR^n6?0*+g{8^i;Tpan&}Td(2Dg}w#)jiHL` zYBj5NP0JEV%0Y&po5O}d>MhT_E5629WM1+u^DmrsIi{)!<9I}l=Ol9Bs!m>0H>5Nb z)!~HRLD9sOX4sWJh8kycEQc2NHh?eT7}8t_QAkTb2<`*)uxTCe5$29k(P@eoWa*D^ z>&N?zW*b%2&sBR(rx;f!h<<9bVnE*DT)vbDVsoj=)`uS8J`OK-K#g&p z^Z^J6|KE>|Bv?t)HiR(ePXf5_)DZryq!Sl@F6vi>pI8GX;b8*Mh$KQ$|EpTy9@Q60COHd=zAOG^Nr$@j15WIlGWKRCsG`;0eBASM8Q-8;L$5p9V|4|= zt<;*}t7+QK9Q>&YQ~skoUQbc(0tK#I^V-ulhh-@J!9<1;yMbU5YeyRnd? zd2OH^^}Jw+BdAO?F2q(YIY15omO-jQlK2+TW;`FV$nIawWLvPoVj7bL15D0RSexI! zeeD^feHH2~YlZml$}7`doo=0Sl0Ar!MJk~BPYt5#n_D}q*{7m!GDJ* zB=_<>k)EX>>r?>gl?|86A!6ngCPfZzDj0g6OD#>>&Sz3$WT;CGsS$vAt4|rb)%B`^ z%2g#OC+5&(I?<^~Qc?}kBZ1=e%^FgxTo?1RgpqdQa2gO{oGBJp2A8M6=_r?tnw8>N z9%`H85{6GziE>GP-lYo1(a%YI8B)Rm0y(s5Z&Te$v=>25L>&Z+oG7Kh{G2X`a9D*# zV6=Q|7}&kqwfKqh9c;|ud81QKkz-vMR^b9jdsF-h>Qb^9x?bASHU5V*p?m=J512jK zfJX?ZmJIt&D%T3|s4s%)F8FZta{F82dIHULa{RERi3{GD>sPMDibV^>Qh*)`z@|L) z>5j1LZEow*HE-)sFI6WWQcOwnc3&mgD!ZIuNO!YXGhnW^H3I=$)(lV^;6uWJYS!bw z?H1L_N~CZomDgLOV7rsx-5p($@FWJgbfNkf)|!&aX=hYIRdRRrR?r!wqqTGfJy@+X z0EHl%m&sz%n$Jet-?qX{DH9eo3mb{l$8q~arvr_FS*Gg!m zD*Ge>=ABdPd)E;e>PPim>{(lQ%ogLQh^I>7;vPAStWyoAs#Y}RYze(%ESu3BYIw{K zXl&pPa<(@hiH6puR!m#6YrM`%X4R=Z7+vp)!INE4xu3aO3RZoSn(v^-x;lvi5OUPS zP9Nso>)<>42M2~8$*H%gj}D$arwJVjKXW}o%(RZmBDNbN$ajvNJmQmTPKA#)rbykk74j+-7h#H14;Ky(Xg)l zTYe7XgC9rsDnd}EA%7nMsbzz`Zd$L*NRx1mYraeqaV+?A0AxgS>y4QYdl|Z%u zdh$N)2gh$EnJm>6&~?*KfJ%frzT%>J1I}SA$0v13qDX7)a<-_ymr`WF07uCgrck9D zGP#>D%t(By_@6QvWzq&%(QqGl{fGS6s=Dgh=*9awiFGWClYdQ^SQcR5EI{3&#`6Lx zVdYzUr=C5dQ9Wt%=s-z4f}NXw9rCUXt;8F{oJw_THo-EC8l@huZ_$aw4K64MRp!er z=F2U<$R&>7dQ@|zDakT1@Z~~+Yv^)7C^a<6Q!xvpt7nrzM2tIQN#l}hdmNbT$1Hj{ zs}8A4rS_Z>uB#d=u_c2+GY`g9Yy*ia?obJUA0uk$%io^@2Jz}vr89%+B~fv3{ys^d z=(9qj{y04#&qn4gmQAUqD`7HtJcENA3Ii97Gngs*_UnS&lqr=i{U!d6&KCHBc* zciW-Dmt)gP40JCMEkR(i#g!~dG&>tDh9Lat_{2P<&)$3E$|aPS*3j`dOV9X6)$Pf? zf92I$kP0ikdX?%MExoUEuNCffNJc=2K22L_Sj;jXw5MMV#d`X-K&9k_>O3jJ(>nlT zk*mOElYtyU(&n*=km7uECby~NO0>fIFsa((!!CUYI}(W7)PC3d$E~R1v}tA_RehGOOC=7#gO6+85!=? z6O8!;!9lsS6+op(psk1HFUl3T!yYA{l^sC@5&EKv{z++*!x|y%lL~m>`mjPO&_Lfn z+%o!mEID6x(6rUl86Pqy?@+$$Xy{BF;QSHJ0KnT>>2j%# zP{>e>KkyQS6B=GIDRmaepG=jmo1~;%;>kP8`2YZ`QYtLagpLkbNs8F)iXsvPABmAj zs&?=glUxmnM|7C6oMCDPcyEHKTygZ)jP-2W7_;A{$%RUD720CzRF!afs{&gR^N(@V zEar*U3Hxan-su^FCFpfDI0y41;Ro;drg%=}xLhh2j($k+JBTzeZjmz{m?5f9a*q1jr4sE@*v*-(SGN&rj3tflw{GQAaj*F->UY47zRNJ z%X18#lE9fMhLMxj$GaX@xqL=KPAZ^;zNynl7tXzUW{dG7ZDjJ){fp0-(^TA5enyU~ zK8SULx8qE0j5J?N-NIvhpmt7x;AE@)6ty zbh2L(Bwj$p0l4g<&ZxeDssGr6&L=jym5`r2sfM>CBrlvNWnZ0Z=gE_TA(~1r7MajG zAk=c=aw4uI-;cM7#}VXPbwcfeMevEQwPN*!jmxs_9cT#rufIW*C1iyL;e&cv#vGesmu~B-k5zUc4j^lr`A@ z6RVLw^9v-+3uah3%Y`$wW?SdJW>31x2gsf}&4IXvb{!d;c7`F| zs${BaEyBZhWq}4fReosr%`_E(uYrCSEGM=r7RQe;Wp#b*2%{e@8RX!|ddmA?Nx)by zFC}1{4X!xQqbFT^6i@2p+VWj*M7DiZtHM}pwjLE$g1cI=cpM>dX;HXNUmnRx2qd&E z*b{hL)Pwa9{0Zn86$HVbzPh4g8}z0LBw+$)*W&nPk@`e@;clcEp$qB)AeOBpHl@Fo zC2PK4P~Hl=uys|o@7rMuuXCUK1(aJaUEkJdJm|GWp&j7&wJ7h4oR-=$yClR5Ta`O zyygIN0(qI!9YG(9%fw^}{lv7+`D)l0lF9K`4JA~DV(pWHDH7H2xt#17Twb8z6)m^9 zd=|E3&nNA{069$}xr)J?N(~fU4xJHjYgIvamdE2!adJ-yU5X8of`^Pewp=guExFXT zfvX4OqXCxdlx}`hu;P@8N=9WwU{AW#ssXPPS(r)Cy+#CR^ zm$dgRCk-@hLq&Szm2tJ_vF4o0yU%B6h$b&2bl}wQ%cSsfNxL}3WmkIl^4$)MFg2{` zPBC$VKy>;@ifyKDoy(Rt8HHy?RM;pUjc`T^;v^cz>}(QG2eDNyTdqUEDwO#yaNUrM zBQ+gn7xZvJ;?^Wok>DU|WSIlOi$CMK?<(J$+ANe6z=QQ4;+b9-a?g)gN6K0L{6)0X zy7_8{EltBpf2|TQ_H~+E5h@NGPH0mA>&~y>5uekAPpjS7ZR~bb}CELa-!tM_#ZytfA=72=|VqOzmtwOIgcetJAS^+}03OYJ?B7SJ+jadU+$j95n$6Q?2?gz3N%#Cu4YSb_W zHEWzNh<2jR0c8eOtwUd^_H(Otw>nLgv_9pA&1F0xL)$p{5)W%v()V)5D~L#A%<~PC z$t75i)IsoC1cGqVzreQ^hv>YhFZf2M@RE z8PJ6sr_hZOKZI3*wiy20wv8QtEK^RS((j2v!4ixUn4z-`*6Oy-0_7nkX#ri1e1CyW z0op{h*O2Ml^PeCW#Oa5_kHa3P79ocNyB#@)I?`R%IpLsdSE(&E)9=!_!>ZsZKvJBy zO$gpA8S3L>8;f1n;&Sq2AwG3cP@N-&W>>(ZpSC)?zYQ2qvT4R?cZdjGT_)2U@!W%j zc&ity{&Lj-fK?id3eez#OC6`dA-c$B`shK`Fc;y+Qx_bLRbT+YFpM;AhA8jQpnPX> zXp3?`R4xL={3H%&MHI$$i+agd*)b@X!=sx(z4|151Q8bpcOOBp#ldbtF+pM8R@vz% z#Fq@P2fH&YfsSq0DukU=cV-hm^ues)2ZtlZiXtU{sQ1q1EWBs?<)pK%MsXk$xV^6rI4Sw9d;So#{CzDk{Mz`QBkw#ENln48OP=J&x>P5 z9m-_&R&Xm*4@_)6JL6L^NiA-Gi#Lz1KyHKqJsJsYi)1)xA+lixTt{(lauo3EdtkveH79> z+?R>(@SfT39nK5nLM5yw0nK7&W=_6c+Ptin2aXC3H;ezqh&%?G$!L0P5Yi3PSWjRU zV^Q4_=hnC5DA%Na)CZ4v&YD;4fT@RnLEC}~fme@7&`qCvmAEIKbJGv}i~f0u%H*}F zGv!}qi|k!fE74^AWF5PTDn#=4MguB0qlWF*15Ul$v$QR=(gaYzU~_Y`wb|0DKG+Cr z9gby><4|X5GAVa_?V<@qP~v_*Fzt}$UiFJVNWxm_8u~?WWn@BK;+=_*o+q7(NklA$ zC9PnV-g-w04zFHqe4Vg^8nHTsjdYQFPQ`>!|D$?7_^gsq~a97Afl2g%>iZwSBwYJEJ{5W>&KOr|R7 zAT-F4)bp01>CE)sp4P*B0mHE=T06ru=QxWXtjQZ8L%^xa&w;beCugZ3KK6?R9MWau4p z{PgyjdJg*Hme3ta3nEB@ZN@`g+jtd7AbNopX!<^JTmUA@af-8`ovnad=R&*(Oev1k zw9kY|W=@Q6pmGb}C@VLS1!{L`kcSl2Id$*W_EzVL*0JE=6j(UqVlvyUAQe!#J}v^7 zZ3o-bHDi$$sM>D>T{}Kay4L;Rx~!J|=yI_?km_66nlh7UhATDD7sKfCGBoi4{eS}K zhfW#%+tN5Dn7!6|M~{ttxb%XIg=GT1grMf);mSG`IQX(N=wWabj^sbA0NlUX^Q&iN z?pC#JD?#Ml`F`>B<8~lYY4yS*&X+4kkjKh+H|?f6~A)W=PFA<+1oEFulf9_n~8F?7Jq>yDfB=R){c0*u+J0{rH#?|_59B5u%c;eML| zD!AVcFc-IHvjDEvBBe9+>9#3%9x3OQ3?JN&re=>7GFzAOlia(N-=WZ;{+hTxkiu4{mi;#_9=+JNP@zO)a6E0rzOD{V8i6FehN)433<^Fw84@;vroX zlAqaWX5)_&jV`L5_e@yS=|R@9T*_h+14pOIh!r#@y5-kPhX7Cwt#NRqCXp_YE>LRa zSxy=T3D+?4y#2RqSgN2GkPdTG&lBC}dB_)5wgGAv+@-@qKbA;T7_~q@6ncVg0~fuH zz)9!LB>q0c2u)RB5eB%{#DAD!Ai>~n&(Ik>6T#0Gw$M}|V_%Rph3f}FR8BNP-|g*@ zpx<{ROR2y&yTH*}p0K9W&8O5GJQc?;`S)c7>MpPG!#wSZ#5-q#uUCj?*xK6dzKmad zLD4ye=5LEfJ8JB!Dw1ePMlF$(?dHIrth9nOaG#sNps<<85Q(AQH$BLcz@UTiCtgGW z2qlmt*H(0NYkzt+hGg-9kMe<#8dVuGaeG8b*v457KuQ3$CTrPS^!VI;pZ0F(pprnApcr?!^3wAyO^xIJsp<-$PK6FyY`wyeOU4jKyh+m;=KVo? zav{3%=8bh9{sx#z;^MlI@r(x3tF^XY{3=&JafOvZY(XEeYNW?f+n@85-UrRXqE$%> z5oixgqb79cB4*d2w&1&a*>+UNP8Uc{7D zw&(l0KCCESn=e06mFa|wO&pF}%J3J{aholvAC)Ev^BWhpk?yGMLQT0dDmkU8hHt%t z8fM0w-pTz#z`b|nzhj16OLDfLF4&k%&plSw^&jcK?OqQ$#UOX_fV~YbD%qBcI%`2R4Z8)9(Rg;3XL=%cWoN z1axqZ=vKYog+)L^?;UVg4riQ3PfdZ(|Ga!%@eKCCr$FxL)*`IJjNe=3<_JKfFbYM>q z`CpTZ9j#N?dAbo>RGj`($PAfAbiIA&gp%T6T47#^x_ZlDbiXVx3|gTb|RInsQpuc42ef&picmasxE^2IOClSiLrh&bQaz9 z2zF`}Yw~gAj#F|1X=5UQ=8-b^I3(vGQKu*C;RdrUg7Z$|Q|3FF(Nunye{-DC>7%&K zfEQt@g0oZ%xu|IX%td2tkU z!JkHNn%tpgIN|1^MkUJ{RIlI;WfFpJlMO=rY_36|7c_HekTKgz5b{Ieh6X8n4#Uec zVSiN{!$3F1H3p+J7%qt7Pfz8+g-0JqqgWH4>>xVmtjt$k989Q2P~KkL3I?U;Tj|Z|L;%%-_lb42hUmbT*U8Q` zHBTh5f22OR2K@|E0{-E{KtT?UvkufqEYf>&YPlb_3PoU6kF7r0rEU7KEbN?Y7_rol zQ&1k2?b`7>Ifi|Zyg|r{PeZwiXN3x4pXVqoSQY-p<3BKHjV=SQp?(v`jX&dN3->^oi)W z6p%N6^=`ml@|8(ad}$vv55|?Z!Y|xEEKxX&m)&9O(;6-pQ7;ot%v0PAPpj^XSTOP% zGBc-a?MED9S_Cy_ZH3F2hseQis&Wg;Mo&^ZLk$w^Bf&;D zsy3HliPRl2KMnA)OyzooW64x5J@#m%V!uNAP?l2BA1VV%^LRivHRUF6u=Jq3Gi*` z8PP_L{_>-)X(KOV@2nKdt%Gi54|NIgx50Q-^D^|CX(mDY_)z9`l$o}1*NE{nM7_4@ z9hw`#y>~#Ft(?`*Mrl|&i6baRpXElI;7M4juzjLU0_kxW9iS@E7;bIAs+_?c;Q-MK zbzr->Poc0(hg(agkhFmQTSesTBTu}TAw`hk_8QUMeST-H*-X0ZK+m^qky?wj?qFDj zqkg67wtx8B|0jtOwox9vCYebFJ%%UQF3TTacY5sj%$9+PH2MSizbUJ{g{9{yQXn9% zNFX4h|H};I&iUVxm7dqXl2yZxKt4#RTv|CAc+#TS*u~bUMQ&U|1K!D!i!O*H9Q6=c zAGqLH^wiC_|CWm(1f&?($aRnQjk+j`%j3Vp7uW63^EUqR*T;pri%5#p$(tRn%L|9z zVgQv8(~80Q=5ncVla^s{0G4&{2$ngX@@|z@`ID-PHtEPI+w*M5=f(ZC$3+V*=F`=I z5kv_cq<%3}T{kxZtSTj(g>FO~P zIlKfBjWI3l>YqE^d^b4`T@Rkwf&PLTccHBT9xp47PRxHB%mNto&_orTuA@TDLAmUWjDKkt&5A$Oz7D zYZ}`{o5*w`Q_R6RRts&3(=(@yqOjR$e*uC#J?FqN(lPHiTSp_Ka9oT^Kt{TtbjizO+C~`{_Q?sEaTobez}|N_wpnwhY^gh>p+=3EXyxq& z%F?N(CYBs~ww^X5We3QyKEbBKo=j)7LE+zJ6W(4_98&+V0{{70)1f#eq$KHRrMA^< z8YN1s8CX+h3N%trjEo-4f|O^Cxn~5u4a0k-Pv}ID9NuBe+zG72oPc{3Ga1-KYal|6 zkv$}#?#t_*jhq7Ur5>QuFm#~Yy{)Zc#1U-!&GBn z;FH;DoFok?QA)b$9Djow@gyBIkf@a`P><1XLCcJa3Hh=HfC$cL#e1}Pctl|v(Ef$~ z?UsVJH*%HdV-Ps|?fB#A$aeZvoi|6=8x{@D5QOaQe?$8rD4RZ(N$&hG%dld_4|aBn zS)jN8UzanVeM&~hGLH;<3RFl%O?Rj)ARywQwSS%MF+8C(b|?HlXLtg)%ngPdU1HaH*oB$5?G2xCoTqhl1Q5 zZ1En-_vosilAOs4CO#FCFvequ8pLQy*+rOe&6=?nB#<%0h5bE6)~IKAo(r8EB|i}D zHq95=u$ePVICLqV0JG#R-KZZC177W{HA_ugh14NpF8n5^AL(^f*D`+I$L+&;a(_1S z&P<&4H)HGHnpxT|ox6o8lBYM5L)Rn=5hN}A2piJlw<%mT2WA6f_=1SuT=-v?WMO30 za$ziU6mMm5Q1HD7cG`*xl(gp46Z*>fww$1Rjy>&NqKoM+4kC&hh|HBH7Kl>T7e%sT z1WuGDWU1`SZ<>ZCT|$f&NmvvNB%_P;RREA0#^{y5I()z~eZcV_>*^zY^rMo`+^i1D z4{v}CeDbd^00`tk*Im$!{?@1e8@k56A$kcqLLk@ZHFPKe?cpb<1a*%fn9G4%x+hc+yHVENU4c5{%K8&KaC3e|K+2Ho!4+n6$B@IDE1D+U|9KNHE ziDoueFIfzObVK6E3Tcpw`XHJNP2seo%cGsqA~_a74Icd}VPrV{NI=&!yqQlj4^MQjC-%AoPh1-UdjR-q~>}j0n(*V`>BDCBnmh7i{uY`!% z+E`_%*?ECUkGMMRxv+8(e0U_SW+5*XxgECc=k_git{7WAbzj?3H@D>;8v_uKlpiCM zIn`CIV}Qjj4+FIduv#7U0LR6Pot~=VkdPo!{&fvE=cc)i#UT%amE4l~>WT9_Jlnb| z;2@^YuU|59J}+YZQ@rSy_mVq39XcyZ8o1Fq9;Rd=Wb{r`#l-pdk?3A@UL7UFF~asB?iz z-^^W>uIL3gXD;lS^rWLEYzrg*3Ob>CRPRQ%nVTeHTuff`_+1bTj8fBB-6D$=PGV9G zE?{`-x*Vz|Qf_WGk-_*-Ct|65nHE{iS;t){`_Ia~8XT7L=~okZ?oDnp?CmHG+g}2H zr~Qg#U!a|7n8;_eV3^QyvfZIb%YMs40rpN6XxMZ2uD0z;v$ycJva%~dE2rN(NI6zl z2wmx*yP7@{eDwwhZX9_vMlck-_~ZRTZPEwjLGPt8h#PTN6Ry11dbcyBqxSmJVSxz2 z&PYmlwZyz935?A3szH+UMU>#1v~R!9TU+TQEkcfOF?|m8x&!gy6P3nO%l3%j@fc6G zNu8d;#o20TXot#X%oqmX)+uh9=FL_I{xT!Yi^9e*BeNoc{M@t|76Hwju(7IoMt=xS zMaR*YbOMg3;ey;>RcQWl`TUf$=m?i?{Yig{Ti_lJN|VOz3K+sw#v{Cp&tPP2^P`fj zm}X-$hz03XYJeMrZ(|=}ElmOK6rQM47t7}O*mQ~ouh=JEfKC^V;9g6QFQUtHFSYCzY?6M|2iZR$s&h4?dUEbb3EsceGo?FTj7zzW;KSO`+dI@?&8@=2h~ z_0({SLukYbo9JJ2rhIcw7WT0DLSRTg55Gg+lSZu)rX4?5q=-PaB{x{?O$vrYrGH?O zB8Yp52iNjI{_5f0h|gJ!Be$lUAfZJ^aUPjA{ifI;y6y=aqa??;<)g8xZ`Na}>_H`0 zILMd0vMzK9Ci@7)wXh{irCew?!QP*+kXs)}c?&Y!8~*oq9|Ox*9atnQ5R|)skgVP~ zwh15!M+s|4VnQ|t{Ju`@<=rgUD3%yl0}qN969u1Vd=xK*1z=k(-=}>O z85Ou8D<;u@QI#9Vd;@p2XCy}E;e%$r@gjXR_#DgwcPsnWuHb!u z<@e8o{m_EOR)&J*(5wa!yb^J1M@^!8w{m_`=YfFp7)D%8NfdRy%8Xe4o;eyhU*FqN zqxG&EizZ;JJq>CsVwm!vDci7hL;R(Wf9wN{NZD9f0~d!Bfg-D5_^8-`1w2`GOJttG zjw)fv+Ev3|z{_6hD(J~b{_GRSxhlM1E~akDKd7Q@QZd%r>?k!?d~sy8)>a?oG4)P7 z!`pBmG$k>#6Z^=CrS;*AA+%QhXJYBU~|7iKY&4ONet?MPE@ns>rjIH2^Fjq9Eu zSv3tBsD?i=Du97_%%$3%M&QbgH+?)XUN-gJ*2u?{i~H}@V_-e<-guxI%i3)|=Z_$0 zkMG;&2a*WMC$x4z($FUT*G80#(@J`3muvyi+daDQW74Q%l!aRdCr6f z@z|z=YKdoluCQX1AW1V2ZneVJA`b}jTfC(t#-y1s`pV8$RQe1c`N)qQwr)}g(Q;UA zd&IaOc52xqh3nq!w(7B#u3R!Re%Tjou)=w>1(y43B^pANIk7kY7ddE9!0DMk>(B10 zmplz;BEh{Dn-1PgD2dd2+nbffh;G6|j(letFo}Xs>ylFM-5P&oBJLXSa+M&SyTf0f zvSR3Mg_0LZ6Q(x#M#QQ2^V6K6`P)?NHTO|U^pF3$>}sl^aO5V6Vz;SO?owKFT5sCsqgsFEkiLPezM5K~c%G+nT($s^JZSlfbn) z>RCaq=3l;(?W`ssIly_g;7Z#_%qTJwL$xR0Y-T7Or!G#n-eGG^6a28}up$42gY&Cz z>r-t(zbauwqolFP11`}4Z73DiT`Jv{0>_N)B)cw<6f?oq7&%{EI2dJJgbNAL#`#D! z8ar2~scNfMU~6~Bmq3ixx2G$2<{_7#-uPQ+{t&$9m96u)FOT7y;aZ7!wk!H1Frv}= z-Dd4?zIiy^2oN-rt0F%xjWya+@cC>+9QWWnS2XvEF($qaZslm*U5_SIa6ITUjpkC0 zUq`7MhO+^3g_wVR8yi$3G!mXY?6j~^tJFdo91J+$Rg`813NqUOldudZphx z4U&b14^^~Oy#f=tkfdEGpMFSueH9z2ILoUb`27?lq-{t+91P!s6rggMF4a%$o@-;U z+(xtx32e!LaetnDYZq5Om*}1G@vew_F8@CNNm*QqBFQotd@Cv@9=9r0cEl5CYFJ8h zzvM8VyFd*?=8;FM1cm{nt}xyqCHuE{tF{iCm&_EcD`@>2d?a-x-Yf0jpYW?K=aqSt zZE$U3B~mC|L+n5W6wB=+aC=>(TEgYnN>!Evoa^jMYSRtAi%WsqQ*HIF1VSz12ejTO zd8}7^Aq`sfu5mFnd)8}dx*o-?g|YZqb0RNdf3O^ov{(zh+Qz0mlaVE4DeAcV6qtX+ zpQ?tEejUn4l2#)zIr`dRWO`Nm@n(`^uS~yPdgy7YKvEq@Q+O0ZP2sBK-=;%YS|(gL zrZh*`114bePWAy^MJ~i&K$aPd$`zHJ2ecG$^BS+}K@#zvf#bpV&@JK$Ht@h&C)->} z(x71L*eu{4)Na06it8QgXB9Fz9e$eer+MV;L`PdO=BXUO=o0H!tIZMoPgUZr_^RfZ z1whgFb`8mtoKm?vzZHM+PPnwe2_CvI);W3-XMwYEoqbgi)j9Y156OOnAmqz;mvjR8 zyUs#!f-#U3o?D-r@pha31>GyvkiJ{^oYC5z6FOmGXthb-cFWtzzkqucbeW!&pZ{+4J(EZwoc%`IoGJzC%NZFxFG$qO7aBxDCIe-Zaq0P^J#m=LkzKQr+mV zeKnbV;O_Dlw7HC3tS7C0`Ec3c_H!_Cx-zVCZ@z^4u1iY24y2(X_a(}^_o9{z;fSmh z<@KHgmN_{6@svtB6J$=p|8pC7^JsY9C#n@W=nSOopTS?mM(vPUmpR_-_oq9^>F+#g z&eme)zXXEJmSbXq3Ye)R_Y;SjCBaxOuuSdTo|4Xz)|Q%rx2aOHOTa>C19!A5|MRniEcQt-TPmCD@Tfx#Eh>Ed1+5-CC`bh%GpR{WU>{%1l@D#mQ_RbIY|s?e4o(aci^Uf%d0egetiET z^KBn^9`2(Fvm5lINn;xvxXRjCY!bBA2^z5tQI5PyI?)Un)^46@l@d|$U=RX{VbYL- z{N>}CRuv8Y9I-niOVQu#s=rawEf>+)oKQewj>e5vT!=30tR!rqs1g?3X{{8$T>M0( zfer}zHklUgHgeC}DDJ_Tfv$WgtTfKh?;WW%FyMW?8%r6fW!&!a^|~8eyC_SrLu9KU zqv4|KOgg1{Xns;v1f;7NbtPQ3vlL2k)n5;B&}POH)uPE(9qzSgT?+_&@Li>T4>4bTve zlO>dsZoua#P{At$&mqxcw#W}Y%dE^yeF7|7L?vXbxf@U;TmotK$$(lTTp$IB`TZ1L zV;UVXE}F5P2oEBcvtrp2aC$H;ib;s@`K{=W^BN+wLF_73{xlxi!DJfL`g^;)RL1@& zFK8Y`UnG#m;-3>3tLWDej2#IoAJH=wSK_>TNuvNIg9J=@ksePW%dzaGfhKH!7hn)w=LqWE5iV|e%>TTaRAffJVc8ma!o&toOt;F@9-=g7eQ?Yq4S zoQ2eVRHIEgPIPPc{uLr4niQ~?CeELMCJ9_QFBrt64B99xJba=UOq_7$-Oe1mYgz#N zt2I#l@2;sGm%!AYduw>Hm3!lic~ybyuo$elR!#AZWkfDS0ez;h%TZct>XXf!RDuA# z9-DSAk=~sYANQfo6>t181iKy}vd1sT;Nk;SbR8e&D8-s`+A{9#l8 znGsl1MBMnqLPV`9T@BwKH+hM9JhfYU@F1JNHF=FiS9ip{e9>Z;Mf}xvgPEKawT-1? z=t|pr?!xq!5h-^IUG(lpn>+3x<=p=5rI!oU4ZOi#Ll;Fc4v)8|uLT~S^BX&a1n@XG z04g%=9v4FICG5B3Q&(AgmX29(e(_CwEjOL5aC2ocUcbn2_~L^8f3l{6YHS>5Y)A@y zFR7=x6`su-#Eu-c0NZgp#gYDgLr&d|o3xI9(=D6=q;P7Wxy>WlokmqZglEL}kB~v3 z0Ke8UUAuiAEn8u?W`Ob{cao;%IqT`m%5y!E2NPbAtwe40Q>pbBuMoqbqazD17pLV# zA+0~}iKr0B@oj;C<#1j6OvV&s#MjsIv8w8!ms|KZk{4|EAA(AIo~-ta#2l(En_`cd z4*f6Tt;6F_to!WFm9~wJyiUxPjqHBs$X~YFrS0rjPhvMNxXH)Mm4EVgRswAQ88!ZW z_Qk~;jxs08tmCWkkYETUu?sn8EY8tPwbpg#HbVgyBovA(tufIaZ< zn!^>os~knVs8xe9Q~;cv*?75|bgF5_6=Sj=@lilp&D;}Wi>sH)@aSbN%yqIYWTadC zOhheWE6`3GTr5VcsyE)Ghg}JlD^c2@8d8$0gXYDuo&j}>VtmQ^GhcJV%X;X>*@uhT zD>lieMVaVkU%cs%o33wHO0b0|VSv-O??w&&TlSDee5V z%1ZcBCTY6-t>$hQE?YMCH_qsl^HOgIoiFw~X&-81qXDRq>H#_`E^QSo4W!mO=iDpg zPBJ1ok1cFYtNJoI*|D|^B;7`h&%<2v4c($I59#Y0HFUN$yI;OjSd}eB_i?ypB^okj zZx7EMciJ?~yggX}}cytAyqNxO9VO2U+$()nbBr0{r@=Lj9&Y`HT zNhf{^%Wz6dmaDMY@q4|e&IGbr@0j)^Dp(-XvRas-$Mt?a8LE-&b7Lvwj(|y!x2sWj z+VbTyRdu9f%&iI%+u@1gwa5DBzy0|xiVwFJTHBHav;pZ);!pUM){s3)drcY-m9Ae5 zA&fC$v9UmEEQMs{7E-nkFY!2+Xc;VTg9aI65nw@iy>3gXPm~ub1q9qlPjxWWli=w+ zYAUcBobPg+aUu?{!B9M1O;p}-?<}9l1T8>kwW|2hxq}UpqyU#$cE;Idx6w2)$GeWw zrsUw5uDD{y&;Hl@w2yj;_!Uf+rrlPLzC2I0!tt|l;HXUd__m4Caw^mrJZy;Lp16V7 zUIbpZ7#$vovyOu7VQK257wXqSK0AhshuD9E7Wt~JI1nYG1ZiK9Ev1#uh&*au~}@yr_Q~T^=JfUWu!e7ibBYFm8GB-DJX&E&D*l)~Ckvimq<3uB!KvIlNIpwuyMaeid(c z;big0MRce&ysCmPNZTq+L+tS~EEl*DBq+U*i*Bw8-%?1rqhd(S5v`)P8V(;c>PP*F zM~CFDi1iP#KeZIT_7h&`rUa8Mw~_~!M$LXtZSX!r^fNNi%lHT2V%%BN{xmw)2DZ%A zdRuCV@nU$JquUJ=TAH>dKim*y>U3igo1anC=(tc zdaR-z2mNU2kdU`FlPYU`*kY*cBuuH!?{wkALAjHr2E1LE``l^E8f_(0t4eERaaG^A zF4%UhwIF>;BlFdd z@33TUO~czFAX)SoLxaK7v)&A3&72hKEO^v`QoWGQ&BvQK!j3v$k@OrDI8|X%ad7YQ5KHi!43c|78(Y{{i-$ zENnhh@kj;{mM3CMaX8V&6jq=2aWh3}Qhq?OI(LxX23=F1Q)1Pv5iY8*;n6t3^sQ%s zSt}>{w&Vj+IFWG;LH2S8|2M}?jQZS<%EALrAO^C0;IU*((PQVW>q+0)zxt45M;t~4EnV0Cmd z1k`wNf^LxbuBNKNPwp~*f6`6S6r-8+(QVE(Z*QyNl97_-vP;ZVwNYlGYbR$`79F-L zPvr!cqaL%1Gf+ceX!ftxb<+@MKmk-`6}xJCS%>uuxQBX=cu&YUPP9`7?w>A~OKakw zLVL^#=t-8GapQzrX~?f98-;F#epAo|n+h4ayVfnezrU?Wqox>bPCvy6Cbc5Fdr}!w zRo)duaWp!(v{$&Ky%TwK#lcY8cgj5jNbah3-7!AiveJ&8z=sN{RG8lO_fm&BS z*}I(Y^X+Uk&WAOy6IsB|H%3JaItJBYF=mTcx=r&Zjk6BjkH^Vs(NRCqYx!DpW?!^P8Qjd-i*B05O{Y!6 zwq(EOA+&9vm!-vA@#-FAm~&R$ipp_-Gs+Hhb58b(7IZCcnmhV&S)ieQNmJ8{8I^V! z42p@sx@nj(u%oj_8Bn{G!-Adm=T@>VMT+wPlzFuHU6;BFvqr?ceEHBbJtDFT zK?J8cBJ+V(U670;6t90k_LTEHE#&$;ailp7%qN?fPp2^I0(rRhTJT>zeuqj>*7Tlu zpeYiX&{T4;7i4+WxHpx>z`|a<^$gLZ<48YeG@zUe*Hqn0* zjG`Sy4U9D63GE`j_nWKwN|94)ZN*W;GMjgunh3q5hoS?M?&PggLbcoyJ&Q2sQE*t0 zn=;=#QR$7U8J8_ulxb)4u{ZDqI40>J?Y5e{6?&DqE(JR@7>(UE@|XzKI@z?xEGp;< z`bMJ`8GI)_xCtK-P>8hp`r)iW9p#Qs30@^Pts(^j>>M}ZdLTAN?}*gp{ikL-UzW~k z>k_bqxFUG4Dp!@ySJI7v0t1^^-Nt-j5z7~Tfd3Z-5;l;E+@b*jLTvi~P$0YiqCkE& zE*pQ7Pd&b1QXAZ)xX~W#P$`u!`J$`I_|4X+C*-f`t0%z53PUr&+JLB{?&bn^{HI{C z5>rilUbb;58Dz+27yg}_v$bvy2igwI?jF@SqKT37TkBtu=5LVX{@<0!s{4-qa$e91XjkxL1(Fxx~9tmhPUw`y4f|+Nl@bflDJV zsjIk@lnPQVKxQ}uL@n7eSDpf(s1=6hJ#ZKqw0ip~ORg0)JAX+Nz~~amkTUOcWt@$e zVwH;cqez;`@h|IF;PFUe*`#mIyX2K*Pzn^IRLESs$AU9Db7l(dfM96-iXsvp1I!fbQ;gjD1#I zOH^F=00yafzCfW9zbYV z%NNhTxxD>~3^qC8l?L(!X7$+^GcW(sj+r;LM3q+(t>FX0LMLNRQzQpLr>Z`7%4cg8 zsIl)(z$oth0{kVDoK_EeLs&;JW~?oi7yocq^QM(>dlLcFDJb>Gh&0j=DUoBuC5E3w zZ!SXQ!Jm~1V4>rYpLk=_`c^x932r-RW3=M!)BoGc^IYuPk!kjW|N1a$W6|fUN`FG* z-4WU>Em1a6%EqsWhWqOGa0bDituU6=>5pK z9|SyXU=&bN`W__Usk?($)x_R-LEkV3@tT%KKvP9k79kWHpF%^73cl!2w)nRXNkypYtUYt4jJgVXBAZjz&DY@*Z5#C8z@LvH+H?%$pjB0`)fu_wv@CoDPzveUk=p*8=TiOi}8<1Q_`PN zyaUUw0=~EzDph=F6f|Ua&0ifos#MKL>DgF{E;MdVqBNz{(o(vCPa}uU?TH>cKApr7o zwoHE)UAI@6FVspCm{8_lj$-lf4#{Uu1Og67Ov|$`1`09Jx4xAmnSbIKL8Uq=C97N zi=(!$1&8j)p6LO|EXA%vl#T0!1bDXz`_=Zzhkg*ZW6 zqx+{NDr2ebQcXRe*v`<3(fSL?D+N-a+g|uONr1z?o~v2h6*zHVWkW~aa``+zG-B35 zD!oXl$@cos4{8MuDF*JWk`9l_X`__(QDQ5g*Ck?npTm67DQ{-%KJ+sItvZgVWs8C) zvKMq1^;&EpB&?okHT1t1SJ;RCk(3DW6991=RVcR0H5b&-oWJPuBqT@8M1lW?#$Xt2 zNio$9e{+Z8KKYusr0Wh$(;aT5<#=tp4C}xsR{zz4E#&Z?D0(W<`=|ItfY(Wn?qTWI zXgx`7@;#pIhlTy?a$8u~@q3gBb9KY}#eU3EYIW)w1;~OXu!fj>gQ# z_35OO;mhG#D&Hc?sux7hmYKdf-ZIhA3iw?%_2G4i=JPw;j0=`j6GODsq$Zp#8&FT^ z6tDCyUh- zgHZ^NIXc(<153eLEnUaX`K3nYL>*nQb`AyS>AK+h+Q_f0K%%u3`y7lP@jxexB|}xM zla;fn`aRLb@6BxDHi0aq*e)90fb8{>CQrO2f{P`n6P>}H#SvfFxk*qeD!2kGZ9`C5 zktUDH<6OU=Z|cT&?o0H3cPTFwjp~PdzV<^QJG2UX4>V91M)?y2j6#3@xLZH_ulHxd z4dE%nJegV>myLTTcK>>8c~FuwI0Pm%KxR`9qYD-v@d1fBomRN%B!dZ^YQhJQ?5tF- z!afmmM6Ho;n-Q}H=Zf5pKalc@k6eAoJ!2of2sF}3u$zKH8e`T1OvGxGOI0|&g4vZQWOpH*3s zP9ddhgdc)w`+7`atoZ~v2O^D`8=)?xOr;)xfaM63BuH`Rt)eer47GcCI6^R~=HEk) z6F~x{?ZjW*Y{{_PMbiCN%tWy9TmXk7?%KyebY-xuF9D>jv|+BMM7d|1Q%xIN)V%N* zR5drI4AR*G9=w6GqTWB0k(F{6=wv0gOvc>hppAxFY8Q}XJA@Y*u2l^OcVuydc#V?` zZ3unr^-v9y7CMceJwAjz$u366B(>5MjVWSM>_tSUFHIJyH)X6E%Af_~&B1QfT< z`!WIb2d)$Gmj$FX4Y%JPW%ASy}Rom`|d~NwFjJi4ax}0H473V$FZF} z>fT0@TatJDwrN7MInoTbLH^!NKFa6pOT*;JWHlbdyggc1)|e-tmLy>aloSPmG82|< zP_d6v@cLUxg=rkqP?fx~oZ7rVb`_OhT)ePfYG5S8lz$=+olRxQOn)%gz%?{|88Pqy z1(2GQMhheYm_&bD-iOEKCSG=?Fxd~R(M2??^^Q1A#%qRTsCdMt6fXp8@{fV3BlF=O zI))z~edP<0a@eJup~cuDXOc@Q7!0w;`=gj>vH@Pl@+Tx26OJ9xLu0j98LJw8#X<8D5CyDa+w zwO{0%;Z6hZ9zrLl@!q`|$(K}GF%c^XLsGP3Ar7-0_fUlFBz8=g8e(C1qQtah5}vd8l0oFM)b%c2vV&qt_!>{jdK5sIR279GZnlQ~Vka1~=ad@D zHlQz^3uMubh$Kto|is)8W_*f}S;}niG*fforyCtYLQbrB)pwZw49G z#V-TO;@>i4VBBXKHUP~N?}OVGPX!ql;$+b~SFjz8{re0=l>cAiNxyz?`je51X<5oq zKBbL2HeMIIIKa@@{g*rhr4mHTtH?+9GC^2%pM^9;{R!g~QAQLe1(3o_dMLf1 zJn(U-Exi}%xXKP-TGX{p$s`U%=mP}HMs#%gY~5EAun+SBfK5rSh`p#|_DGUVMRT5g z(`F)ac3pU~Qd?v5g)6gPVjxQ}rjpW`D9Fx^wDR$t054kzI*u$2A{(6W77oOn846zD zm;07wUb-Y84adx|jOt8OT3(N!iK&x1SnZFLPW+pLWqXQd$*?paVz6yfkoq|%l4H{8 zdtVzE7mVsKOZXqY&LK(|C5X;r+qP}nwr$(~$F^#Fu zx?d_(Y;@_rM?>KMkXhGV{25;T1oR~Pk^r0oDvBil); z(EFArX3mq`pE$IEF8gsrlKUn+w`BSM*##;TOC$N>!m^|J_~>95-o6zDWrnoPY6@ol z!5INixbxrvXRj9Nx#}Nil06Re^6ihTEa4EyWj`aoW-Cx-$6{bet@YELro)5iVUoTi z5$)dYi0D_R-{F#TJLuzB2r4g{WcR4$d36WGZtO+%#c0PWu-$W2=K=f{gTGg||EbRY z+uVL%xHML%EfqkloTDlvDHgnHEkX%#a1+pe*ymYwLYL}>;c^f@(4e9K$f!G2+6uyk zy6)$kP^ImVQ1Bo5+;ZQ=>K3tBv%FYsHz=R@#2#?CWPccHC1rZ@EBVbA10YOXkuznF>Wh^FUq?4Od;@@bj@T)5dQ|0+mBmJ;|z)sB5MqQ(edBF;?-nSi-LIP+l_ns zydQF3#~_%tyq&*Ek)1)gA&EDG84F$uevc4mS$B?Sc2>C>_J%nimdn)}+Tyn8y(wOS zd&SzQc*MnC+zEikaI>G1EX81zH19=SMDSn}O`2YM7v9q9S26K@2S>^3c|(G1TUHZ2 zMUB3F1H*T&Oa%bM(J%=;0%P#d;^yK+;HGqKW}X5a79y3Or9OJoSolh@O29qa*5Bt# zlv3vyg09a|&M6QTSCzw#MCsq>@meP^aDJ|H<+5p)DAR~B3N&m7i=~8EWy5MQhUmax z@>)Cb2d*)bpn7-qUgp1|U z!%qfdqMcaQB3coA6v)aHWN^g-IoR2&l;&2)02M(<4J;g41byKPdxU*Qoa`;Vpb*{S zdst>ZUZUt)W|!$^V<~S+cU5EfT-nn+G!KDxTUx}fj7tQQewxI|);i9AgL9ry5J-e9 zHlA)NXG$xSJg%Tug8pH`Hi^~42A%{NY?C3Rz}&TakLeM9{$w`Q zSuK+RT0b|qxA{y3`93)+>QpTc@;Fp4$xTR)vOv=+@F{Ww-W~2(R;#A8FKiQIJK7bB zDpCeJrpdvZ*C|^ID5y1y1{k`z8-pK`HXsIeLYSE^R)1Q;xwPm|X#$+-U84lrnJVgE zmedMaBGi_H<5f8=Yx~ZD`ib^*QF16eBpDj8i2ASaX~tRUGr+7ULQ}Oolq;7Va7z?N+Fn)7~PdHGmuy|q}qCj z^^eU4D6;ztLO6cUX5A%A<;al0(YdLCEuQgD=+yXUgN@kf-`#!ysL4z}D-}>W4tmEf z`VuewpfBA~O^d26>@7YBE98ZpE#Sp;Zmjmy?<){JEDWEnP&r*E7A?H(!s8Cr-gYWn zY4yegzGq@x&;3lb`5OGJ1H0JG_$yy0-R)9DWQ^6-^pdsJC7&m64xQ&$^N34cLws!H zd9H<^wbej9cs26;j+4}Cw-OL5uD7m-I@*%03g6kZhcDqy&v_QAs0)6Ho?7bA8Zh#6 zrONh#l$li75?iHVTFAFcKr|X`OG~2_uR?=e)e80kRN1$wsI6BQQfJjIq5;J&maE*j~^X3p^C%l229?>m(GsP zSUUD7AuJ~D=*lBfM-ca?rk#JGBIN6Wcm+;ZnPP<4++GLV4K$}%f6@LPs)R4cS zZI`V`U44647MywhzgNLOCuk>C;C%XL`nDWFg$_;y1EbxkUz0#*L7=RSc`mQ>Bk~*M zdV1v2*EMeW!Jy#2cYyPx*Xn_|Mate7clC_u-BR3h#*x)kPNHg4K|v*dl-n(t{j<}r zRjJkdVM^yRvw^$_Zg{4Ux&pAe7%dA*W2Xe~Fp~w&+;$3ux(mpppKc=aHgx`Q`2GOp z2T`8$+ZRhVi}M)OvNZ;kzE7yl%tRTcO`x!!lb0*~hV`pSv8JT+wk3tw(2=xS5X+ix z!jijE_Z)lsuh|szl9jqzGQH*$=S2+1%Rq)!p_2)wXs1f$loDi-y>oIpE1Fnp1i7I?@+O1BbE3wN2>JS`-!-02X z1QIrAQGm*$U2(%Ev&#%UCf?9osZ5q9#FRZ@R>G%aZ-;g*&w}3%p9s z^7x=}2v?zP!zLV!bBEwaeH=w22IWv)7x#{{jez`2d&6rEveBQQvledfudnr?ox<1Y zs@&_%6~Kw`>gMR1f5jQfQo2DvrgA&8^iU{1&)#}6V}B3$8DYn^^r3Xq0Igp;zXL&` zUFe=dJws0nYeyt9Q0u2(;0i||e>eC<>AL3OTf6x)3(o8WwE}YrKjpf)$~JP!xnC9p z+=3scDLN|3M!qT)+K4Op`Zbu%(H=V7o<5W_ompgo+^8?0I?- z7|t5n>>LTQfaB%YK=h#f9s5x>8x26g{e-t4N|SW^hD+LX(dB}o?vxJ5#vn@28r8tq zA7?+{>U$#!HUOK}er#cQEas<-#Epid7ACXXtDMg`soVJs@i3in9+NBZOPABUW)rEq_&X5vN4LxJ5B6jiJ0JMD>;3!Pnn6f358&+kx!wBOvkOIVl?+@1CI)Gf2AKHy z+T-cQA7=C~;lbg3f6e-W7v^AdS6KGAEP7R;aQ8<)>Nri^0${LtefOTyqA755G^j|Q z>bc}y!Fuy?%;MP*YuM$=n$EtTf!NZUB4BbTE}C!c;EeM=a3tmn*C;=fd&?f+3xof7 z5e8gx*WN&v>><_p@tMbtE+;+3W8uq$NHyq@CHqtpUFqhSu_sNdZa12_AZ~lGwTAOl zaHOx#8GR)h+K)|-ekY#q|E9se(o24f`L666UM(nXGU&NA1Tw-=Wk@9{;)IdI%2vdd z!+mqEZ#rT+o?WpK&s0+CG&&7lAYVEbx-G?RL+yP8ajoj^=)i#^`nSfd9L>gc9j*s3 z63CLAL|!&_D0(@Y?lEywu@rHV(V>;(R9NZYZOxYT;<$5-vWZ~Ky_$R*898fOq9+L4 zXJm5}2)>6mY{eq&1~Ec&uysvA()mZQyY&NQ7rH4KnjY*Q1Q&fECnzwf2hzmynsB_+ z>}h(Y%AUMK91xmr&je-K*%WYEqVc&f7OOL|NZeBU7@8@T4-ryngGrQWK59|rQivwc z+m|g}WN;h<(62PxerQJ9@kJ)%07+4~7UIvlK|aq~u;cr=MY z+tlh1HhRH9&cq<>Q&H-S2awK`b5(c>a0=@sRiWc=h15)05rDZv2`V6t;jhq-6sps; zn7b#V^-9Hk(W+ft8)RSs5xlnWBZNEUHo`qo(k>40yc<(VK7EO?&Qbhq?VXfDJCsivaP{!Gs-v*-Cy`h znfy`5nf0jn5^4vkGtSZ?h4D(};T`6{Tx(1^&8X{qUTLp=xxf%Cc#$^{Df;yad{#x? ziJ=!f=5T?-K>k{2{4jptQhUR?f5wb0{)iJlu7aeIEkC;7v z;lxL05hQFi1^OAwZATR6r#-ru-g@uwdf5*GUF~DL6xdIv-V^q=%!%L|$@|Q@s!?*c~@P#|y7;`ZKZ5jD7#_{gObTEl$@3zZQulPI=xFX}C ziAm-|=7#NQ%nj$bmCzL471hA@rxSQeVQ{$Qo?(C95oX}AeAH`akr9?rkIsb)=E0p? z91rB8Eyiq>P5)Td!Vun?OC)+U+0M{O%D#12ceSj*yJW9A;Qpx7+*oLY4 z%GFM8$-mkqka&9Q+-kze6gyJOfc`;rEZ38wXg^kpqyP5j8>8+hAO1* z%;z7cS^jf}lx(2Zf#Q=Ea2Fygly7PXaXn@NtEjOke5i9 z6QryaPlEr4o@KV>VG)@hDOdxn^SzaasEQDf^7PP;)FTZDW%L5%XpA69c!?VoTRLCr z31eDs;Ueeys2FvP1O%h7R;}FBd~!qhYfH@1Y=MldE}CX~D=inpg{r7zuu`TQ$#Q_c zXNHeg#RhURWz`XB)iPfIWD?62^pU zV&b7G;RMb~CE#7#IQ46YrK%jRZ+^~kd~iDS;BrKn<*5OU_ zDrsESyHfl|zb?u1@El!5rz(5;r6{3Oz{rvOiG3<{Vgk;A zv{a>JZEKsrw}(RMp|=r*%(7>jSTXL%{u-g6rzdrtxct#Vzun#t;x-j$`0*Tm%~$~y zQCd77*3LiQ>UQK zlr+QOI9y2U7ciH;vNGx@y)q!O4O(-Ay{YTRn(0bT9T8WRllMF{dA|s0LW06rby3{t z&JEwzESe3pL&&_X1J!x=1n( ze<*n$vSvfiMhQrh8Z488)2&#T+cDzuFqo2Per#wjpK~Vo1BJt4S6V^9vSS{Z+AwzX z#O2;92GRhkoT9{?ReDoDv|#zMZ)~?eH904?129)XoT`@c8(OWgF|cY;_Kn|YPn#hd zH5sSNRYS!=qwWSlPkAXCWb`bt)@qoEZ9MH6C>(-Fa?wt>an{gSt>AFLYh8#gzBh+b zKv`FKnlQ`bUtJnY>6LTjw!VCVQLZi&UCbQSUHqdx0R)LS-F9qX;uwv3wglPg9eO#( z00isxCq3L9X@bPtdR+EApaS&aNb;90_Av8R-?Xo-f*%q+OB^H`cd|u8gRzw(?F4;u zNej%aY=fu4O<&KY~a&3+kl)eVKCS*AaIMZHDu5RB2*pS!6ghZ-WZc zQ8G=#imV^^JfeScnj?9@_9+06tuTaXFH6bDXp$E|S-W=k5=W z3QrdHlgBzbIZ-G>ozh;#q`Rh6#W}7DU>Wr#W!X#fQDhG1rX5qk~A5ZyP>IpG3As z?-_@id1%TUx~wVA>9sMkHxeNoS~g;`*kz9dDjfUCcm^mJj#p}9|4HG5aZoj^o38Ee z3xJ;XdLkP3+UQ&q<>OXka?qHjU?~WaTfPt64Gf1e1Ct z5dBZ81ku_w)69wyyyyZRN4KAJg`dZVk4+X)qZKwe^N|8e|*+R6HjEcuW{ zVB>kj=nQ}5gz#`3FelYJRgIybrPj}_*hs5!O z=6Y51Sx)N$F@}rT+CW`K;Hb3C;d$i-snu}Gmiqz{k#yC!O`|{kUA0}gxRS{@%SQde zU^c5JaG3U%u-N{>$Fg#D0twSgFkFYI&}Y}4BL$VlzhaGh3BvnuVVu&FBl9p%T|TG$ zcyaWe|B3#Q+-XHO;V%|a^k1n3Xkow8n}N&l-)UY;m^Zh2iuQksCbqmiW*<=pF-eBoctmijY81!k@^C=n^6{}9qZEd z5mz@m4D-c2t#4RY$&Q#_`1;zt|Fge?h2KTdJ4+tJZpQz!r>eF^OIeY!kwm{&M&xrN z@0d|F=O)p<9P|#VTy@~AnlSgn-y@9B%qve=xN6Mun`#nFGH6MXomq;TB~-(d$uljH z>bZA?%-SBpUFGXR(*+IjORHS0?tImH|I0#=q4+~M7k6pP^7?vN8l4P9t9g~wrip@e z+5N-7+;f|!SIfdpvpwMd@M2E&-oAjFjyL^x+>M+cn8JKk72Yq^|1}zM{hCROYrqbW{4cl12QdlY^6~t3f%y0y zgJvF?itiF@+}CHr)&qf}y5Nb4nHVP~3w127fUQIpmm{aK z$ghHczYO~@- zl?i*P?WS?2zR|IEefwF=@s?X#ezJHpMLbALweF`-FB`V~egL!*-P<+v9(m`JS-a)S-eJ#O7@c@-yT%7N z%j4hDRD8#<&FFmjGz~DS1=pF8L910{E)`!PU>@U0WJH&awyh70Tw0%*}j?O!e^Zgit05=*?<2# zGXYx=r^f~r59|YEMX0iURra%T3*_<2n{8I2LszJZ;AMqnu8Ew>&C~iN^nxTkj=#*d zy?PPZ$zHr2p2JR4UZdBO@0(HJG(5-JWoPG1CJ>7^lljq%XX8veN!RCcBW{ zBS_=5qD_6NfGb9n;3JXEW1+3ATC8sCAR0bGFLTAf$Ic&R#fhcBPUABK;VV+}m)Vzms zwQ=4a4D&JGzx=Hc6R#!g=vkol01tu9$AX{011OQ0n{f(@jl1Nd`_Qce85DhJjDwoP zU{edOJ6<4nvZZGYff`zt3QaBU#rg44bC>AM)fKu$Ay&8KTzS(mSM6#L%Y?i9>Y3~} zKzswsrqg~qbf0)4j*sUK9PLCdB%1SkS ziIttnIE5-xsa^4Z#^4wdbOj-|YH7*AbPMrY~^ zYSnw_0m2WFr+MInm1ZQAP*6%W@rI|dAgSRQt7Sjczyn}3`TWYxGLTedrsPyLQ3D3a zQVbyzC(JP@iIE92`B&54Y>`@MfZkMwHl&o*twxVGwb@)W;tC85T@TP%yxg28-EJJ( zD&34_X5vqhQ1v1svvk5#$24xn@Ka}C1ngDf;gl)ntiN|Iw6TPf(Z+Jf{0Z1Aq2~lJ z2beMFQw@nZj#Ns?cu{emu1@aXQ++bN;YF!ioGcQ`Y~yuM0lY$d_ao%!;RyY4JT}`} zrI9drL5HMatQ3}EsCmzmT!=?1%Z}&V%Zk;YNY5oQ2&>W*Zs`+eaAWjsQ(wJSHSW#= zx!1_Llm&2w{02h4j${KLp4>OK0()6**gpWV1LW929#%bYs%Y3HrIT))Km{od*#esk zx(?l9w*gwtN-1Z(vfa~!iI^{6=3Y{3P|c4_V0GL(QWS26eF};5UGb_|i5B*{Uq&o4 zOG2s?gp>o4bn1%W6Um~fhxG;*9ANGU{O;Kiz0}{5l`2Hb_ z?aF<8NWA>xVlrtN3*ECDul{@L34sR=shbfmI~Wk-k!C6!~xs z(Dh?AOLVSLw8{otGL#EiOyW8#J6$TFK}%0b=gkgAC&dSybe$>EbGh{Z6>8wUgR^Fo zm*5qbxk#^RxV%PnAQZ$tSLK=DQh{t+8X(^6h-7g>C9vbfii(d#n&cd@eE4;ONoE7$ z#PpvEREOr{Om!JPzKdV%rUbN7%9^h^4;-7@bk+Od-TEWPFA*K@*Wa0ZUZ>?Qh@U;K z)S(ax>5)~bw2{6`!VNYZ|2fW+tPaG~X5Yh?8wHPwz_9Am#^P#FfP*#&zvR**2aZ8p zQi%XarNaHrl+OLsA(uFLBz#=?QJeWBn|bA}t(wEeT`C^ed;I292%%q*eXuKW+PM$sf@QNeK-jFRU&qTlnT#L;rFwl*V(D7u1P;88~g zQ4)9sg)o5Muyen8i|}K-OLD!WbSj54OzqTj^l&+ZB~qG4BFfi{Z$W)t81(b0@F8bQ zr`o75i}%3mK^XVIy?~ByW0G@r zDxhZ)=M?&_Rp8*kL3J|kLn&(-EbY^4@!{I}#TUa8Zvzk$#>#~qzbazb7cBhfuasFS zR%vx|@rj3{V-DJrX?8Pwn2R(%DiJ-Wb)M%5I(+zY_x`kK*_ zxb>z@;RKn>hCrfVu%V5&Sg|?P20BEg7;GlpklghXTq_r>#a)tKe@R%8k+5J7qx!^6 zwr-Q?O@s*K`hKV17r%llUX-52!QH03G^%=8h1}k0g z(CJlTtcV2(w8|7{i@B+#8Prm~CKXq~WA$zu2Mq8pm`)X6e6)L%Q-lzd`IAx}avt%@ z6HbmOlez+8bd4`kqsO(WUTa9}%Q!f%m@blgA<47g8oX*t9epb{q zq#OIyk|bBJeaF*+V%@pZ=rfc;YdewXAZ(l10Bb>0b3x1};-mnQP1i}a;+pj3X<~-AAbbd9@1Dbz-tvE7 zxE^MuDw4v_LqLSncR7V{rVyDbl9z!}@zrUtvJ7t{V}1#7uai-Bf~yED$u0aca?J$# z)aMVnfq|#&@hTQEWBSDB6aSr#)1a})oTU`~&Vqh*=Z#9nzRTQJL=HbI57q<}sN)&v z8cc^mM;A0HLnTBg6gJ$B=Mv-Hd=Uk;tZ@+YGKcC7q~Ao2kB%s=54FRHNRuoEpofqoBnU468*!un0f`l+f1p<~ z)WV82{UjuYNDGME#ghdWrtKiTGBepE6U*HtXq90&7Fz^u2O+t3(YZ}Ob9iRs$lf22 zaQ^aP(O^tfU9fDqan~l4Sd~1Fa|bmtsb(N;Ev$3~-(DKfAG z`5m9H2Xx@#v}B$Y(@TT?evg~mnHa$(>P|Ys4FE3x77s zglHDZe}F|-U-ZrBeF`&x8=`fZ0y+cTQ#W>HD%^J1kwU}_%4Pb$=x zgRH4$g^mJEhM$zW_mV2(3u;F6OeOtK_h_T;Y_GM9-Rbj==%dSd`C-hBwz5$?W%~EX z)Wip=`6K8XVLKaH5?^XaPAdMGr9fyIa(IHzjBl^1UpMsz#K|L#xH*l?#x?Xg`uZz9 zO3nH+bozPx{3JSqIoKaw;Z#0YHI0?dV@OX1mf1w`;2|`NuOWb&zA5y!p?vTKv^0Pg zmm`aE(XNGtkdNN-cPy2+llQ;CSGwPviR^-ACW3JN(;z_pm#>c;w-5hW7n~#m?Km-H# zO+0gVHHovw{X+lmVXgm?PXK}1Rfzu?|IZ@*Z)0a~VybUzZ{liW>P#=-;_2{T_@8(5 z?xsex<~H_5hBnUh|M$F?x6hy||9{!6OauS`&HwiadnapCCw&(SCsRWc=l_-7p6Gi2 zX9J!1x0eTWwnZfg2f7j2lvR?%5lK8Y`JtNFT}y2*1t6&)0t2K2kX+4C*;(hPf5$iR z)9Ib-p^$8HcXF$fB0&WHpYJ*^FYnLbY>&5lp!dK74P;?=I?2#ep-)~i`AfCTp(HBk zlTqVzBEiIwVycQ@Ou!TsWTa(&=)SoQ_(YgVcQtb0i?a_Je$14MBDf_U%^}llBMEfS z5mETqQE_EpGoBGaOibbVTBM)kSyW*)a0YLB>Il#JdB6Elf|nHUUus`XpzOb3qNdbw_j7vscWM8h327=rh$S^nW$b!BpUiaF~nmboS9@R#Oc%n z4Tt=drbGSHB%ok1B;^<Sq>$4AoAhN42ImSd-U;_>a#X;;Bc^R$^FT_82 z6{N92EMJJ^o{UGG1Qr8E`bOIW4u9etapBJsrmBhshR6xx$EWtSUqn==lWz2@Y(H@< z?9DIAqo<(#BJs1qvgok1vBVQz7(N92sRP@l$A%d*4$q2s4=>C{jEr-k0+}k(O!1B< zJnykEid7psG|5yGa!7%FKCGEW&+Yq~;6Vm3j0}eVLEwDC z01Fx#^!09gnf%2k7$(@;O%qZ*ST+iJFbMG;5rBwx7}nolJ`>h3nmNOyvY?|dXkKLP z>-YY!wfCL)p;EMGHP8BHS}`Y{fT`-zpiPLpqRS#MMDzO=l^XLoEt3|zMvo#;) zDn(X%C1O|+Ka!Y7wVHzIdh}BnDUKr4F=G5T1PF&Fnvh_&V&$JNaB$f5W(g-NEyZ5D zt8RD?g`akY&wlR|{Rh_*O5n?TB3U>KS!ScZ1W5ed}04nDBcpN+s)I-bnZCo`!10wWI{ zQ^f@-HY4^LiWd};l*vyX3Nq6q%A&jaWpY$hlMK^ZE?+y3Tp(crVYovNEHtj+ zN-(1cWPHb07@Pmz!NiK~LnNwGT1p0Ogd%4;@{kwhycmN%0*Z;5KL@)P8|$I1z#S(e ziE(CtRKi8?cuUIGUOmbrTj=75Bfbg1%Og?d$W$$Cz>ukAEK1-GF7?=nvzGBc{d(cu z-r0yJ!_dEX-)Le0sk1f_XQ>+57<1AB^N@R8gN&04&Ee&!^X=Uu{uKv4puReR5qF_B zeKvE~p7Ky`?y~j5DMbjOyLhGFmP35*qH2tr?3-P9d>>4{^?=5mhZa^iF(oi=cd`kN zq@+06$gbb|u4Zq^7>kzQ;(p1mf>rPVT|S7}Q+NE2q1U}^C;{?g0 zgW%qBE#7G-{J#LB)M>&BAVUBJ~qcwiq)Q-OOgFwYs>jVfJlo=|Y=L;LezTezOj zHg^txCVwY;p#x0ve+|KQz3Fk0)40TiZVUyQXGO&>ea>-f9DVV1ZsRRhs+smwwW{m+=_k2> z2ldJ%L9=X94-u$av?dq7d1GV84O8s_nI^HEO(7@oWfhgmbxlt_0YVfR-XSgk|JF^r z>Z~U|4Ka5ZLfKa8Y(@|?!Y$HK%d)UdndA(%EBC zlVgWQB3zE!z%K6soFN_gToqi$V(;G+y)wr#o+?(hX2W`8U3>3ctDgRwxX126^x-E& z^|ysx?F-mI$)Me*omB3LAy#BV_k(5q$6Lq~7LKz#fy3Po>S?<|`qf_17MWf9W1}$I z0d`{9Sf!%jpiossvk`;5NbosXaS-9!G+e`!K0hO`s}?X?v7HPF$mHf*qKql+keCR8 z&^u`sspWONHGri$F%;z<_&pP8y52fd$k6AfI{qYL>&+aAtM zzd*IyE?E&SMj!X=k#>Nf;Z2RyoF-0He~aO@850lcK7VP7hiU0lq9sVL?D#$CR5BUh zlwot}Bdz9+6j1)tqe-=mI&^|0X<6hc&PKEge^C}-EDmqVm=g{e$FMo0ofGGt2m?5P zCyVRZLs>A7U362S6q|^)z-1MIVU(TFc^nj1MduvOD__U{cz>{`VpfRPm?gh5&M1Vl z|J>;+0|)iuL=hht4yg-ml&<-rX7<1{f#le6;b#PK>T5+4KM~%{6>Bd>HRc+kPFH>1 z@p}n*dHnli}pMSDea|;Bp??)y>D}ly|6|m#)02)jO(^ZzmaTNtkK;(YQaO>J1961+r=?4dL_8h4FV_9^6Lha ztAK!=0}d)gvNvT#JSlCqKf9Sd{2gIU_s*}L=JQ*+6 z?`0dS5GtBVxR@7AjLpp91@7VWK%Pp1qTuZFV;4ko+5Bmu1wbmEz@!K@xP#-bG=yE` zT|TY$O;|hmbz!Z(7g041hf>X*%(Da!Qe&oqlsxp<7)d2F^kH>V@+RAQi2w>CX)V>k zA-C1J=09;LXGqjHR%Ly{r%|8!4?2L#uAu!X!Fz^x)2a%*4J5J_@mE|4AHgh+Mu4IN zD_To&x1d}U$&yzNgQ*?!W|A=UhxW%(pgqx!YTh#fk24xRoQ7*wwT(e3aVXbq!f*~h z4$mWl|3ql9NFN5up}sG8JQ!hRly=*j()7@*vk2`yMWIv1>t+VX0c_Y8bE$OeKIhV6 z;5C`?>Pi0zY9#SW+ll7(2@V9H(TZ04m)=|XQFPH6&eV9~4HAY?!LH}c%VV%pxwxlC z+5lZ+-vy-tAbp`cIawFq^33b=VqpkJRl9&NlftFnkS%UMcmz=@#ipfWJw#oC@f|=3wX@}1@INAr^<3(e z0Iblqy0D2}WRv=RpD8`Y!8=6ygUv@#D3&z7dNGnD5C5^rhxb^rzCAxuWR!Hf*OTcC zfB}k{n&^AUD__e#s2GvxS0PBd>}c0bvGdKYquS9zai!zI*96VKaP@qhRBk;|Yapv? zkrzrjZ?EmA!v&E$Fx>M>@DWrJqG*wsQ~JoD)Gi7qMf}Gf%ORi7As0+_NROY+TS6j) zha)IEhTV(TK^03XtZp|5rwlePWhIR!;ksST=S=7odd+C8{2APcP)uJwo*CkPff^t%_g z`cshm#o6?rqlil916aou7OA0cF z6}@597IiZvTmePnuy~?KehYoa7`c$E`AWa~&TOLaG8!LVZVJZmr;$TFqkDo$UL_$vubw>J3 zXX0^DTkqehomO9!l1s(vHWIx+l6ttrwnBf-m z43XSk#z8!hTK3s3;Va-JTKpTRsweWZOY~1n>71s1qMRRk#>t-wfp&_}LBa^M z;Ts=4E&&kBKUS3fCaD>9cNg`dR+Jd&vfb9-J;Kq{*RMaDCHRpzKiP-+Ka-_)Vi(gb1!`m7YTKvIU9o0?zQWL#DO)@G6(}3xS#6MF{Y>%MCd%X<>Qs85B-2#avSVK3 zQgZQq|Db&<0^T3ps)s!q?V1bhTqrGSx?BZu6xa?Vv88Auhy-;bZVYd5VTSdyO}Ngu zZn8aFdGc+|+2Oq*-#eZmL1dqgY37T~W@~!kb2Cq+xc>>bKck8UppcHplRj2?)N?#b zTdA<-m$8ghk>?;Ccw(Vo_oBc|ynM`0Bvru!l)>FjG{jVu5G-mhdVCXM3!r(lOHQ1i zDl;TaaEkQJ9`V?O#K>;^*A)R&t%r)@V3g z`E`p6uDtX_M5!&ES5HAVr}4kF2!x{GYsk@rpJ&GD&`Or z6}glg%!8osMIWaOhlij0^kXVT}mt^nJ?oeaL0vv|xdGI5tEluf6y zKitYb5{?jWCZJ*U83kfG`A7=f!6ih%Ju-MB2L7wI{pd}-_OLPIXADw}i)vot(Krt( zd-|H6NIocOf`rtU5?@}7mLzcTHA@2R*c;yL-~VWe?@7 zfF*L;3HgLIIj(rX0=42fNj~awfY}d#vp~2j?z}^saG;5xIu|G)BLPY_ZCXUiR}mKx zk?CSGmCMrhI+)~4pG)YQhkTtfN*>lx-n~$}r=vRm8_?YGl!}afe>M_=J4#O`+4BUe zI+3^8{P`ASUxe?@YO9pmlsq%7ZrQ_5@wId(Ye*|926ZEb+g05zwxI^Td{iZ*h=u#e3BDDU&zxZ;)S5%fXiizfuEI?X zy~%7MFH`h??-sGyD$uwI&S285Zl)5fl1xt4PCHn^M=oXq3BbHC!W?rsXKyi&i9Rq8 zTp@zaQdaVkb@3ho;EAoIedUGBaL&t6#EMZvh)ADR!lLpS>`$k?$bI~68VBnKLt ztPPK+dS^;)$Q2Sys;gG;tWS351!=wh3N%SQ7h^rU#e+*#M3e*0?`dv25tG9taj@fl z*lIap4U9H6l^|&XRG|s`nKvDlO67*u{d{H8(I(|r3d+PVKJxm*s1V^39Ho`&AY2A# z$}Lb;-Bbm$U`VHVi)Mqpf#AW^CaR!)1m>besw1YH$0db~fRg9$9*Cmb<$e?@RvOU7 zi+>@|a!u51rZNmQoP{UxRt8KUxaJ_I_DDHb*61^fmi4ZiNq~Z?m1Go z<)ayDTQa5W8%zT6&6F&frC#XasmsvaM<&BnB}L1XGB=dXl}s`9gYaXUiSjb2nM?Bm zzpb*cK6EfT`fHJ~;8?R&@6aYF39yQmZbuIoLbaPXSp{(`{b_zqlrOK>T^m` zoU{Vu17#lv^mOZ^XRs#GeF6!5?>?-3EIsKFqO}s$QX>#}guPSwnP2<5n z*q>MIVI(H&HDF%He0wkX;w!LY=!58B5m!n2uEwwmm3AdzK5SJ~?Cn9GfrflX>B<7^u%Gpg+NJ)3;iv~{Bl zNO|9!*ka^A*y!jqtd@$aS(H6l8-_0kwZ)mDck-J5qTxl>F2BZ%%dm@e2PsrN-bGu0 z>VGsc*|DyCm1f~&53?4VyT$~-W%Z@;lci3=A9+zwEg%n7JdJ>0rL2soRuw_ysrwcr z*5|uvT>SC}-@#XxhkA31)6*)`N~!`hOsNz1aB7u%v#lPO6&eS75@CWU%B*|+-DRQ= z>>B@J-iE$8cJi;o;(vcof%zmN#8@WfQ!#WCnMt7dEI^m-?zDJnYPoLV$Zw~N56IgH zhb7q3AaNmh%jMP$f(wO*9R&4RBwpX@i6VjD*X!2o;o}xk&+^Qpne?C0T`HJsxq&SI z`x*PEb||bwn+D|z`-Zmg`4+exO;FYOt&kUuIL+0ZkR;izAJ_cLsf=%1N;q!Lr)*IU zL`KGmDeY7w+HD}cIOrnhj-aF|gb>OdloBpklyB}mX8-T-mO={UcvNB;BQdo+$}!=A zF<<*kVP4tFGcCe+@*E+N%jI1CEOzLocKa|VJ#7`rv9O-+r_ zSncN4V)#hncWDyMF1v)$GVLFY;PK2X_mtW>=IZSU|MOYTB^9TnKz;S6e*EaJ9F%^_ zXsyN9oUBP(TBNJe#*bEiZ1CA!Hb`y#4|#EbcBfCG$h~}qkgT5dwh32|lb%zgb5I3T zq5c#)H39s`GigpS7V2iO!;+W$oyQ5|Y7hRpT0ENn4IAtlssE8VzLiQZqW49Yo|ndP zmRL`-+t=2bV9HpOmD>6({H00xxMdkXO3S*MsJVWagpgOL#9IQ8a|+XCz4!O8)3s4Z z5Fhzeht4VInZ^WeFc9(x28L0coMm7Nidzl2)j)ALT7=ziig3E0Vp)Y>ViP>Mbn`zC zkH^=QgT5R5+-@INAE)=zlc}IIe$hnxEpxA*m2|y-5AB#fbv7M61_uzzHg?DXm%^vn z-qR!#t_mc#C~;`m)$tbldo^;Ux3jrjl5j7&MiRhCe4aw!c}x#~_M|a;Aygrb*TP+b zo1E8(7yYMzLd9?&<|$JA+RV^96VUq1)H=>JBbEGGpPi+zUY56Tve?1blU{T7UAQ_B zZR?=_)>Uq-BfXf%zTA**O||yY1?i%-8_wZ#c>HX|Sf6I?$EeaDk_lcawJeCO>3KDE)Rd`gXyT?h2y)~cw~>^#A^QC-=219cJFI0Nhz1m7vCLDUC3Pan3b zal1K_GBscMlO^1QOATw7(YKX|P^$9vmywGHmDR?_rc!(ripa_(vSt1Bz5b(_uZ_HB zGl6q6PaNC9lE+AeSqryxIGfgYW}7$z(<@FuWp&r9i9vq|Lmv8)CmFJ|u3WuKiP*S0LBKRqDC2abyA-Xk)!M||`KMJuI1$=n0}5Wbg5)h{NvomtR0NK8!M=H`d)_#o z&jl;_3@DG|ZyCshm7h%rW)Vglb!ZU2vm(b*F{l^?R0MSa?s25D-C!lD5egvrLn=j=14egg^0%#i6u&l z14LVwOaTcEYyA)vxc+#wd8=Zaakil|ktI?s+ux2Zsp;%gSv$DDWr^PNX%tI#iiXrS z=Mg5Br~Hl{;$FA{2nviR?E6Bg&+D2Fs%Ox`UcEIq`f}c-`?bnvUx1+J`P>}QRrFnl z;$h{}FF0N2c#4~{L*C!(b=Z?sbDU?yn#N^K;iW!X5yI6R2cly@^if+e-)0ZuJz(Mq zyfR7>s`c>;k8K?IVP-?9>>U#M)0XD8O2p zDIIJYI{?m$tWVGqCD$Xygd04jRP28Ie-{vKZe)xj=BUYVjLCWt)URep{e9*X7e z)5uJ{BdvnD8z7mElU`pWw@(k#25S_!{^Qh}cx0eY4SAM+=Jm;>i0XaBTe2f*CT#|F z%WBpRZ{ByIOFASuSt2i)o_>CO-~^S_a_I3~I*r-7o#;;Na!cR#-T1>vC(s}ZWDAxL zdBNh+<~+>CX#e)h6KyZ2={X zs*C)`t-YPOrg_LK+o$Wh|5-ANPVS@Umyu^#j9wR%gf+98$qvD~J+J$f8ttmH(e7ez zW0uviCg*iBYzlwL%Jw(d>U|&gG6UthR;%>IE6FkunV440LdK2;x9T>mDRH5#>WgSw z&PgK9b9yHx+2D3|!(Zz=HDaygD8+3~5BBs@D+{Og+Lp}(44ZR!49=Ad0ZTxKbW zB)<4;cZ5_*x_Yj@t8J~1vw6XraK%G9%drQBt@?iGdv0!9PP~HKquuS+`$sHU%c4xU zc>JU-T~EkXx;!xhYh!nJ8L^JZ zpy%;1>p$cE@TD9_RO;=92Q&fPFSBWBySf!2R-tr`bKDBCz zI>p7OUJ$9WHn5fr_~WhVgwY!treshUp%TX3M5YtplHh@NLCErmZI>-Nun};&XT2_t zWkUV2IpqNIKkpSY0M4f_+N1TIYt&Xw4@BS@+OgTpQrUGUkW|6-H zsfy47g|-NkNcZhSV=CWQh{o}K3Or{~GOBNt5E-DzHjqpgI76H)s=&Q3gs z_49JYVUNfrjOQ6gp7)WNbNzb_`*)Sf`SHuew+x%xa!GlAk1?V3wTu+ib z$L(3S&^~i7)X&-IWAS>=(k$!{I5jrkf^G2X7W6V*p$2rk8{2l6``giMjqt3M_;rZ? zOFiv(@t#2OkBGUz-=YV=NdM;OVa=WSc^(D#ch$y2449rJjHd>bW~ z%1TmqW6%UCIT|1%^WWf=Rs*CCfPFOB?@-@y7ssCPb6M{0d7Ad(VO-tG*=?=do@eO9 zO%FQZ>)_%-#Pxh%zVt|vQvblZP+vtK)Fl{^83#mn9#oeffsSIC=V^7c(tO3hY>!4u zI|Ul0SK}MKY3ek!WOK9Z!1fR~+~gxqFTVFy zQjJ`QDDK)QzV#ynw)xCZPw<{q`}!B9XduPnfSCXbUq+Ib2(H@DXH0opM%VWugI88t z&S2XUC03gad*fj)V)TWQ!20CX#5?ukT*Y*7I^Mz#Snn3^vyfOwPz+H`Fb^cN?tPHG z&7%y6)4G=ywK7xqq5#fy%jofZMIQ2)rf2X+Oc`XPA4uC}b2{Ff=pB+H6tR_7HwVvU z43(7bEg(cfHQFCfx(EIoFkbF$YDU>5-oFd?Pn!-im`p~Fht)nw58_7L&U!H_?rVul zk~~5>1q`x+TYtHj(AoAhYr(C5%5)TYp%Chdi0tmbj`{qNsBp4rQJKX)C9)$Vd#rD5 zrnTx&i&0bV+h;!{33plK^Jky|bm%qK#dP-`y!Gip7e4vNXmFgN2i!U*f&&^tpTSoc zA~{a%tm2Cc1OhR+fPcNXcHhQ+Ufm$~U+`Z!{@<<;OVD5*qzd1(SclhHjlak32#t1j zcPqM_3`Iv(oYEkDZoOgrGTDV8*L%5s+?>ZX4p}*@MBt;)!(vH`9Q}UY^Rlih1@6UL zI=kq#e>Z~JQ9{p?VxMs2c$vD$04z5)1qq zi^F}xK*0nquZ3SF-1}(tqj>AekXNv%7WBZO0ny0mcusS-`yJ7E6rHnp(Ph2r<~@zu z1daqr7e4@1n?%mBbnAHt*mC1E5W^s=^Vpw_VU?whQ}StYmwa=j`CCT;a6=TbnVV`HU#J&T%P{PgGR2A=>d=`9_$o_Y36OQK9u2hI*%0_)Iy zx|@OQzBV`-f~J6izrj-=_4$W2x~-WpROgwj}1v{GX+P9IN7OUcamjtj6ZdcWBj z`1YVKy%uyiU(kQuun{z0;k4eEjcG`&bVpiT5qH4sLTNPm4iQq-I?$@560eZ8`=!UP z_(%g!+uvh6_OOt5fYtXU!7|K3a>iT{P;gcEOrf!2F>okN2B#E^Y2VPMeAs#)XdUbE zT6fFj9k?QU>i2@)zgRLFP3TKE!Z=#g@CUU=S*XMhdZW$_KwMR2k} z8taq|D!(cfqxU^j{pa&?4X7Oz@11La5w>Wbgj#=#)wQf=czA@}+EA;NjZaX?$l{#Y zJ>kbQ%H~+qnklGGU?=|bE)do+kDm}HpzhFsWFgyDU`lvC`ri=P)~-vwk`yfnE(r4~ z@$7I>Z3k~SRZ@!KPuL40!O5MfIQ8rhkomD2X^NKGP{K|6W9hzLI75S>M=O z#Pz+7q58?2pP?M3vaDqb0Q(b^sP_piHdgXQ_p-O;UFxH}p(R=Z_#g=wrT!=kOxZ`D znBf$ZKCsW8tZeD9mXf>Ul_rYKLt0=>6`D9Ga}r@mzIbr1PI+)-$G<%)zy7oTolFJZ z@}Wb^4NC~QrcsR6kgKHQ=H7s=A1jV9Jo`J2&@^C2^rn2cs9wNb_dp2wu)#p?Y|Sth{GKGNh(_ zKD3VX-9sKq?0N_kHb9>x+dX->K7z3JcOk1IECTPW@z0W~Y{oQ{{JOQG&trFm?ZPuG zvJo*d2Ku9T$8OpGaa(;ly=KhD4Bq*;t35&53 zjOG8g9}YygfZ(-^P9jv1t!Kz@c~m`}14(KM1UsmAYF(z?z6b71NKaXW9Q$-WPzESS zFB4Mst@(l&`U)2VG;|wt$i05(F2Dqb6~Zd7Sqo?%Q(*!?Ort6ov%;baW70oFEEAl` zOPeGkMYP5YXZ&p|1tn7^)r8|Sh<0@CY(>qNNJigpy?T;~<9|Mj`^=GGPL+9iK~p)w zbMD2FL7hEYd%*pzwsCelBfyIv}?R;T#7w7oAma! zWmJ^|c|CJfK$}#7&qt$_0<)}mgZTM!XVC%4F{w?q;1diLP3u)meU*EfW`6CUZjQJr-pLXBwf{_`{GIADC*{LlmF(=BKXfLDv+7FXZO#dFov($tl=(a!iqsOSl)Y(Vn|tMsWI zZkD@#mgIQybNk-o1H$w={$p6< znEItuwY$+ydilIMl9mO*vI*C9I4R}TVd}OF-R@u(5K|npL&+#Q`hXZv^N2gFt~U+k zRW$<|IB)~DEZ)H8c$l&UVYywA0M>T6HxI7l+~v^TYqR^DEytCB%;1Hp=VEUh@_ifZ zgO&zn)b=#3akU12d@|dOi@M4MTyDTmHiGdt9W@Op}Szs#Nurf ztO!D?Xc54ai~;}?UM(#;%p4&L%DL5(`AA{7 zD@9MGi1y3`=9O@FfF?ifvn~XWkU}D*ZKYxUcx}mew05giJ7pUgRk6^+28j@Q6wD1| zc*@acB2mtvH3ye_qI*!N!w6j_>fR0li6tYuHR#%5*L-b3TNWIZ!#MioRMldxU4&~- z9(;0+vB05tm6)9RoKW$dmuA=F3sJC+fABPba35867Rmm5(M|z>Y$E>($Tl78%Z*5w z^_hl&BR!Pvd)=NtlXvjL+aXW#B#J1>LT2x0Z!yVlj;BxsSbHiZX@I0Crb?`$>doe) zICf1zTMDH?UQQ*U2T?h42GIRjFYVY(dmbI{VTY%mGf(KgxuPOdApbmwWobPzETy?+ zeu#?<#oabczZ*+%))GUoy*#N#>lr4+ud|4<<=wYG%zce@u)XLm2}0&Cky)uBIGL8Y zp#~|L*aUSBLm5=UxEqQX1IF8jyemHR=s-nDgv6BvH%ilJG~*yS z5%vhW0WSG$+cxc=;l#bnHkIB(CtJ>m0dxyDm&$&zqAFMualQ)Shz2dNRuTXe!$DPA zAGKuKQW|rl>78SO6$#o@D`r_1w|2O2q7xVenF$}Kj$fa$4#$LoszzZ+M#O{n@(h%u z|M~AI8g$JhMUO$5aGsGcC`CzoF^5j#HiT)~^ha{DRdg&5EtxJkO+h9xfV#}JLQP#A z$V5%U*FiZvts;%FjO0mKf<}cbimW25Q(spdM5U6Pa=d|)vTWttAVor3_eh0HujO&V z(?G>TUGvZKC~%eTK;^zW=JuFez||RQpwBjQ!qJ|C2hyz{$zP(V5Q4+0nwt`9G&)DG8Zr32I4| z(J48KDG3#MNorbI8p=63T8eRL+F#1j<>BU4rr7laxtZ=fWK?q^meLYUV`H5A^A2jgx4{BAU0y8ExI%anV z=m@V9L+hls{JKAux6u$7YNI)j1Z{*GSPpeWn+iWflVa&t9ZLO(&)I~ri2?%IqNzOX zyDQ0_#%hAA%lL{fT3l|yLw`Qsnmq{4f>)S6`0m|FTw~6~8DFAaYS)=Zo6+f0tANLO zr_N54KG~t4gWO3!-mS@xep+Rd@vEQ~Hhjxi-Urx!g&6K^+6Dg?UQ{3e0EYkn5L?(9 zo4Ef6-RQV_MPLRPVXtn%M9$4G0Qouj0eLaAnzjBXe1p(HRn6*c+nog> zZDRvijyViM$`E1B5J_H$EK`lKqoU1WtNJed4DKrlrmoe)H3@P_2AF)NRlt>M^YfB3df7I&Ab*}dEmv47OXF8_TfXOoY{q=m>WY#2P?e#z zF;$zrfJ3@8<$(krSST7)p5#g`s!0RyE|@0ucib z`BN<0cV6YCYOL=#cPzH#e{TQL6UWB>oT8v{N_Cy`{$IP`GIf<01}dxKMDyArqBZNJE_oGWNPgLyC? z=Ev}?4Yh8_I4d?WxH_(OF@c_}FnGt@l%;y9e^$mL@&#lsgcP?#X;q~WpD&|e5M0$j zwVuQ$+xj^%;)*n{Ic{l-t*=8x=-pIq3pNryo3G>Ee^SE2Oa@QDOtq%v61|b@8KC=C z;l8h|gaii_!L@s|u#(7vylb*Ris&rS;ha2EuHceqWpBIZF7U#?dw=iH?@aE$R_`UK zD<0`@<)VZBpTX&D;%MM(=lH*MX3@%hj+yi*oe!ut%dW#irH^qXXY7fQTnp?%p zf(^Oxrxl@h+Owh|o@z>swrdIl`7I%(on+PbNnLwkan}S2%l|r4WSaw~=jALIbCpz? zXpA6BX!T%=`y0rNfF-N~0ffue9@s5KGd0IDii{BD$vf4O5!>b|AB#F~ZjofFV}Rr* zuRZ>hRNd!N2gMSD=spNVikW-Os7S6<-h8-H+opa4m*2P2%dJNQMheUddP!M(pU^Wb z5=!%WIAU{}(F3CiKy0lV_Tsvb1BX(|pII~ANo$z}!PeP=p=SY0ZHcv`i3%sNJrg8w zggdmf>t+XO8U4^}5D`&QYW}8Az`Q6}Qv!U{*i=L3G_XA1zy#2PzJs}e)9fstsgyd) zn-c#R$#+go%*k0GQEZC-T|W=pQ-lmnvk&-7+(C?OyCK3_XdUT41iPp+f3TqY*8d`! zvzUlN?IP`u2>an$8iI@5k9xM&vLL=Xea>+4p>1?$)jd+OKMb~&+|o9Sxheaj0+$fB z3cfFkeY=xmK2)UkQataO1nzRPFL2+tS8{k)U1TGQ^K+dfG-~t^>BNi^VxU;%2Ye1~ zgMJt5Kp^es=7(NsBXQ9^R-Mz}%@>Y?f8a8y!as?hCmfOMqBpHssf0srY+d9$NJKEV;IGOzKYP;gTQY7L?`1znf zv9?~lW(v7twY9gKiGrVmsn_r1pP9rJa7Jy3s_f7{qDltTqr>`udkK9Z&P;zNUjhf) zw8uj{iOH)hQl?CnG$~OmU-$fYhV`15F3oB?kY|5j&v*-jPJIdugW0zIWI&&UPDdx^ zhU*&a*^|Bv7C}A^H(}2hR=}VE*}%)H$CN9XQ^y}RJ}03*9wUTH=Sou7LDTWsD-^C< zl`7#D&5NT6#?x(#M+Z^cKCQI>i*dOX<|{@-8|;T_Kj?AbHTh)rKrXw=Rw7~YVht2q zg(PaU9U`Edm%9oe*e5S!y4S)S8gYUDp6efnXvQ`by5R6fzTwGqQY--7i8XRK(!UY5 zV+rizIkZm>S(xtiB4k2|H)7vm3+2i~xBHYlqMP=(>yAAX5iKHU*Er`@LA{Iv@=b{4 zZU3OdI~Uk4XYft%?K?A9dM2@#7EQrk=~6m*1YU()IOM#stT(Xnb(mK1p*8=+>;M?m zF8}ZT4UKcaJ@F7jOcx2%-d>zXLbx5)Z@8KjN+R5?)yr|>|+sKa$@FATS`*>QNjI7~@gH6`Y2=)D*qcNV`B|C{_L; zyGCMwmhLMqJrG}x`FI$WO*rprYvSf(j>t-_@RF9^c2edUu z^+05`hpGYX3?c-o>%#GX4~aE{jr9GV?A;e?0z_xYy>1KVWnoRBhJZ~WkB6hhjdhf% zop8x`pP`g!BD;DG!jNG>-VvLX${YlDJ~KvKF7b{T44BeHTtOw9X_6I2On#nXe+Dt9 z^&tv{sn+T(qYf>baH9Yqmyl}6+anUn=d1Bu{1b;WQjxU(4#J}=S5}4!RA9XxlHLV& zxM6M`FYsQP7rz zAY2LN|A%giC$QywSMt!q5+iCzb&EusFqNq~(6(`Z{EfDU23K>==TC@km?&u(anIQS zN(J&ISk44wyvU#^s1Fm#JajpeHDi#oHW_vohg+91x8J=V+L8Rdd-%C~xS)?0g(&NY z5?JBC-LITzFI6ytnA+ca8+aC~b=$l(W_3+hiPxE*#K3myp#(;tIx8>tLo1(DaH@COTn%uiGuMmno z)s7w{URMqd7lKZkSUx|0K#od2Tauy7pCepI9`_B1vE@I*59~#}1Wk~Qg2G1*C~Qc9 z#0wl6{n;y*;y5gN76EGTSCls@l$3w0u6a^F!OmORFfX6`5}BB2g+ia`d;zmn(tnHH zW1j4`A7m~##ZVUmIK$6m%r_dQqfEm((nYj`6#ToGgeB=}w6pFNS9%^0Seou? zJlv8UqDYGC0e#+pCPy`4C2gogr21{C=EojP31T znLe;kKi7G$VZ*+BfPH@m*dma4m0Ph{%c3-jYaq)x%AP7!;<(c0{(QU!$zaT53nb#V z*Qh2ryRPB&U_|tBekuU4u5BvdAL|4z@fKnHYf0;CoI7Xc+*+0`oU>{Evt{~kY5R=T z&2`5`YlFyr-NudeGKbT&wfWN>@XD^NaJ{xDYSl^_`HWdU@1X8Lr%yh6!tz%Ej z@@20lAT_Ji;#F;$9HjlVC4ZZ>UCuzuN9f+BaHFk(FvhHORnhm!_`0?K?o?0b-!;Pmu3HUK;rONT16MIkjUy(!M1F~ zwzC&c>!X}AsqJKfBh@!mqXW|V%??%;lPm6E4x#Yx2$B7kxw9hknAdY+(?5SbFF+fo zoaLDxo>T{&NK-Ak;@jyWX=SRZWv^1epNzal&FWGrZWH(>aV+5cV4Bj`(0XQH0T2Vs z1X+R-y5gZnb!m*=Rifq`8Bj;bN>&X^nm*?31iex`>F7T?E&JWAx_pq$stzwTI5J7P z%~+o2*GhIVQ3{#n8|XCp0%cdE!L6gJfg>@tx5kLv?eLACs$FlCghL@|y^IN(AkwE0 zu79x&3?}vmaGe05Wv4s*hj6hNYkSwQ3!Tn~83^qkc-M0MX<Q++IGmz0W*8tx&3rL2=V;$xioA{SY}1A z0qjX*O#0l8)^M<6qs>{=j#eQ+H$qQ^-GSEMm^jHV6YPScT-swQe>Qrl>74!Jt4oT0 zu}k^4aqh~GNkFJ_G{dTrIh&=De!#nO(|(RtYE5C@uVfZUhf-%?RUc#)N8zSxnxV?!BxRy$2Ftc=H#np!J1z@-2|%T0W^EO_#+{s zUVE|ftkhI!L4&y^QKUP=yDu*{vX7!Er19y3svEM}pIo=dPV2BN@s)#HIQwj6F(u^HR$ zRm@vfnDb<*U=@-P&e4482;1{s@VY@sz6mWtp(T}tcLn?>bbFpN#-OtNxZ?-Ip-K#` zS19<%+Wd9^u(}+ZaQc;Q0*KD?`j*LyYSMfY(FshRK5|d58yCOo_Wr9Iz=<0Xdr;4? z8~d&uS5Df@byn}#@X9fLGlTG4hjoWSjJ3V#6HY|QgZs}e2|ggfUtW<_^`qm$G`WoT zxTX)xZENn!8~im?SB)6BEP{{6gnIc=6?J?v;E+m}V*z#&QM9N%=Mw|vm(R=2Jz) zJ~$$Vw;H7pa2+&Bwvc*=2>g( z)H!8ti?{h0(|5OOGA8#$PdpOC{~WTEB2P~gAYhWf`krMO&!Ux-^ zqL~pt3EY1JkiSao>Oikh%I`^aK!8CQO{7c$+X>hEc3^;w7xLBf2+-rQvjDF+fn^b5uB&=Pao>cxqxF&qsXrN2I%}zi&RnVmvXeZ4F#^E zUiz0Xw#b-}zbDh2CG!poRvqN()TwlHDu7Mu8ctc4aAh{dl2*8EVfL2vY4u>obfksM z-`JXjWp9Lcj{OR`?gbK9gEKXkjI#ce53-2zyRxbVt!q6wCe}2#A)CDx;SUCd33o$_ z`b&`Ky%v`uqj}ZFB~PmGP*-Jenpxj*42(laTho5Cz`om6^LB`y z^>`-k{zZYBFK>TnMqC3S5@T0XUa?61C=KMz(#2UieOwA);CaE@EyWv#ms@>R`=}vO zZuJ_Yrs^Ce3Ulj`cmnOt6 zaw#;Q?8O7=h6G{-x-_J#>lwRs=kVUEW3htJa>ZnVY90Uv$Ep^v$NG{af4wvU3~i^O zlPea@5>9kiR7tUF;#or`&xX^>*#{6Z=_V|*XABEw-uU>{mF z>I7R8(8zh$HfhdwJrF;k^wUz% zUi4!q$sE*oO?^yg%bNCUvYU2&MLg5*fM%ldzDV$#;uQuT-X_Pl+WH$qKY)_DZBYjG z0nBYn2yb!!p4rkM_dUww+;sGAvN6D?hSq_VDGuXgXz%sy1e>z(Mw#(`RZ#bJkZqs_ zP!I#w6)U9=d<%duTa@zbPSX^LVxTK90al;AfhqMr*?V!0k;jKjbr+4-EIY+6x%|2~ z;*)Z31!P$-OeW2grbg4bBqFs?RUE2t)0*ZZD(D8+ZuGqbPVWWNBI>G4w3N~+>!W#PWa$0BmeN|_(g0e4qo_Hy9UdaY|`CHzqKRngzb0yMh&U@ zdmcu%BpC>{P6@&ylOoXdnhS!C0Kl}gRRBz)riEh0?QlrBKd*bk*_LNrILa8K_`_?ZvdN@Ub4t(OM0JUDymaBa zRSdqOLp8OlmuaQ|t0%;i#qkZ@u+JK3U*TS1W~ogrp|r1LuzxW4TRT-{Hnla^RW-vX zCCLD3XpMjT8+cR$`DOdZJv~%A{~RLX!r}?}lTlRy;_r4N0R%O`1CE~RdQ_8V9-|5f zML3jjl66IhB*dV{65b9OUtW#aekU?CKL(~S;E!Xt&l>SVR@E_tG)iIK|LUx`q0&6+ z*lX?8;zFN{+N^@aIZ!5vER73UcF-j0$SdeWpx(~SAv@>x-ISC{zh}6p&X200vDB4<;f) z?|~KvJlGHPYv_l2{aOPcY()z%79t|A;7`TqqCUt<-3K~B|9?GL_gVsgwu92EFLB(L zHwso5V$nDI5hQh}y+u|O0xl*7Ii8thHEIy*FJ~xSZvr%!6^k#Gu(9HmO|1#DQ~=44 zo1aHtXWcq@gMM325e2QHm^2web^_>} z9h+?XB2mU^Qr1$%)i;I+r>wJGOIXfV;>J=}iC2wWSA~WyLPP7Q`T0HNv)AYlLN|S8 zHzKeP=J`rV{F@)P8OHWv_YL`9yXz=iq4EpAb3xx#004^rsmW>j-+P=qA8re(89gVp zvj#6!Hp{F0Tum}zuH6-^pypm3$3>Aif5xA!pMHH9pn#Q|X#hB>Nl9;#Wwu@fGg zZ#ZEU^Tyqj6Wo^L=Es>$!K$H6rZuwU}_!OCo(PjEK}o# zEj5%`z8cewBCJYMROTiw4bpFm`Z*r+#J3_U)oQPbP14haj$k_n8X|t>jfN?rwRT%s zy+3`4XWQJ_M@>17-Jt|E8uIOEU0ad6axL1)D#%?SOw=*5dq;2Z7ej>A1#WXdy3sYL z_AMVHkC*HJuE}0v_Ikz`qAok7OEo^|@L24{rCz2oa^CEC0|o{aI(pJ_ibuB99^pS9 zgk0~-%wsZ&%183Uz@LrQmj?{N#PqVT=gTwn09joUdsP9#3PpVMz7{$idbG12)RqUp zHI6=k;RZfG$a*2XS*^w^zRu+8L>_7f4tDl<++3C?!YZ5m^8;tL0(@mN7VsL?mXW#l zm&29^PBfgx!w$YD@f~u%4kR>zMYZ5l^G$d(T5t)t;m&stc7=X&S;=!vCl7nafs*H1fr zUuE<8?c5-z^h1pLq1S!4v4DC*(Vfa^(RD#vqsE5EuC7x$KK9_r zT&t)!PIr8XrR1z}@$%Zu|LNHZ>!OZb7^ezQ|Q~^Av9L=h@UAB(BJ1-MdzQ z4}W!7!5a=bASn^XRmO}C$Ec?1x99CFSi@f8twNB9g3-4 zPrwvHL8!rY^wn7vZyO^(Cy_|_Y1lrQp-UGCQ;`(}2dg^yuhStTI}4~#yfQc8d@0mz zfXs|R%Ej4lGU(h3W^!}+>8wQJt5MN&^5L=dqKdo0-4}|JVG@3v$~gP|iW3|=Sg2m(GZQq`+vmI}&VFD5S-l&0+ z1M`3r{B2zi$jlh%YV;P(z>AS{Jv}EF1td*fCGB2btO5?bet*@)oieeiiZu0C}t?hjolt z`B)1&a=JndMgKMb0uxb2pXvKOnChIW@M(R|-K^(XfG?&vGIQmxswe_saBWyzljJ~> z_y+guVhDd@ z(8}zE&SSUNpy!G1Il~-4Bp(?h+sjT>#&qwV8}1>#pNLR_V)l3Vfd;XFT5Vnwcfhv< zjX`t1grJ{?3&u>pm2jS_;w6yF`zipIJ!UNe);-|h0pfP_Y!!E06%hVN-N8mBcL#Sb zlpj`Zn~bGzXDE={rJ^d?l02B2on1b=Dt|)D&y6dJF1|J%4x5y>GCCMF3DD35&P?Q^ zJwY%RW&dyfNbERR9g#I}q}TJJuO$hU8ns)uce{&5DSixI6!qGI6$VN2*lpt`|Az!@RBz-kESs-TT6De&` z^q=Suslicw0qm)JmMkl2LB1IOFA;{@Ja(YrR?#SO3F_V`)CB4nIm5WL`T=j$EMY6P zMVL+0NmqeC74_KOiuY5ZMd+!;OV! zomoptv%)2Qa=K(<#NMTHin|ecHN^`($43qcJ;ukHavl}sd`qV*JFl;#bA=(>eySUh zG!km~|I$L*jKrv?xSz0!kOja=_I|4FiuSapgYp!n8G+&z-^nh6lq1Lr2N?tbK8-}i z@j<3YB>f##VGjz@#Ik|D5eIl!-^F2&O;6!~j3wnW)W%>C(ClAbSf$otB|OEtbo0a2 z#YXghJu`@>UDi$!%3e{jL2Bn84j9W%doB56IHO98!*6OHG+rX&^&N8Fo5ldJWH#psuKW>`|0To4OowL{A zPa^VcY0N#M9A0h+mT?0r%H;twcWy@u+uYH)r;qyi|gGtxNVu?45*hc4OJbhhV== zqyFa7c+>eN0|6>CDHohnD>NGB)E|h362bvEXR)|s&;=(iv#t}+?pkh072!-t?Y1@* zgQ)A{=1-4L*hAP!FZNZ6JZp_* zlZX7PaX*cDNgczC9x*&aI7#cunkxJy3&mh#q7MS5v!v)jwgDCamP)5%8o8~eS&Z+@ z5jdr8E4ajCbh%hO6&^V-+krrH&_WyUR;@41owvvpwy(|jK$-)YkZxct^O)1j=p35*v(yNvB^I}McD|cqRwM}t3JhV}@v35Ju);iOU zBa67d?`JII3|?a5v8kn9c@+)bV#0HIs0B`9uEoNLV4@Z<3;g%d zbI|=>(%e{p*;OOjA;PgAw0BM-tvB9`L=u>jQ9l-z9(bRKQS~_!Uxy}+V}FE; z-SUxbTmv{_8^(J^L`aL^Wr*9RSF)lbPRyI2Wh?@CIOa#NG8P3r?h0(2t>T$qd=8i{Nqdj}9)7dQ;f;si6!J|rQ(qC}Y? zD!^vhXdTID``SXEF5R$2^b{!{1s`4{(PWw)=KZ5_D7ynG;UKPhfyMfYXa$w`!%P#U zn^XQhSZ_M5Up@_GTbS6+zLc|Y(l{zcOyN>+@oqk1(;;voq`mumA>hfXQ&=OwaA@ek zZc~IKhZb50&ttOcZI($^e6qwlw7J(~{W22H-$rAxO88Ez$3_VQ@#EvIybtju{1B3)|Aw3Xd%47w%OHz#CNtxL?{>NI4UJE8Hg$#~s(hqDGeHp>$n0PNrMywu}e9C%iCO{ChWO!$F{ui%h9vm;xeo`CBx zF`ZoKqs@sjH!N(WclMXI!Hk^5l}OpEJMrhVAa(;#Qw>*%e~&wm54hKQXp`9(&p_T4 z%s^O@-XyQcCu|R~$&($D-X82wpof9Nhlc5@f1xD+kpl_yP41Mw0Y!e{(+Gyhx{lAt|(Xn4MBz-{&0oqSa&y&yc(1EyQ zg4A#VEHWlpih>H&`1*+(+`%(GsCcA0zfCVK(rjX zAlDD(ttD|O{fXhC0r4)*MLUwO8U0Ik8rWx(MW+ZlsAlP{{Wwaayj~#ziL&jMW)s%Z zK^|76Yj6LHv2%zPEa=kgvu)e9ZQIOe+sJ3zwr$(CZQDj?t**&mgX$i|Fa{CpM%)wM z*;~`4ucwt{$hiWqC#grql9a09fcL6;1lbev>&~PU+KQMEi5g^8N{S8}i7U+zvaP)Wf@rQ(|+gVG?4#_V7fuV-k*@qezez+UzXoRYtzomh6 zU4~qrq$pk8U?dDvYV(0Ksy5_(H8lE0rzIzJ^r*~(e;^QRiCbT}8I|X24BGMq65;Rm zwG}J#Ycve!Gw$~J4Yy}pACr4$8M(^QgIwUoXkvNx#a0sqG`d~#0>E*X&HSJP*aOzI zwxd?)V-6)NhXhko&u!0kLsU=YTB*B4u1B>co$t#_&-XJ?B81QFDUJGGg9Mf$VryBZ z*6ygY9z{F%zw|uHDMm7p{#;sO;M+1(+|GwAC6wNQj?e-Rbqk$85}Py(h*CqaNTf&5 zMy?y6CK{P#RmhLRy#Yfo(cq?EWLq!}Ms&r@-v&3nx^*~?SalQ?Jn_H_esCY4p!$cP zWHzAX{ZzcuJ{0H)4UbNbpxeNcgU%fb6f9*1N@^nmInKRO`!d0*af*zQA5@tBaMC}q zl|Dfl4x)CObczdbm|KJHcbC(GJR7@zIOdnmz~tlwh~2XrmJxTOqC@NCPCMJ}^!xJM z?0#I=Yn)(ZFn&I>8-s5v&vY)5NZJ1Y0#j?&u(*VM)+@!^>Q?Sih&jtZH)oDiXqi$7 zZ1r;GhY4@DH}fsST50Vu^#5LEHjJ8|^PT;L$uX291x7N+;}Mr*VqkPfrqWQ@V-wH! zh#8l0QI;>j=w)$mvhg542*&ZUJwj*sB^LOraxQ}>#Qb@XIS!e%_2Hh7yy)B_W4qhV z`>$0wdpqxsI$7@4+ytuWvC7Zgbv%8}zJO7_f(K-t&4gz9>MvNt{J6s1QDM&Ds!YP(YbF;^+`0BDP3GNAiUl za@-H}Ad1_Ti0VRYrgyvkGgeu$Z#IjT_nvm1sed@#v>MynIQoP7aen^f5dd`P^$%SRJ}rd})=fzM zoX7f>NNZqj2L7JLH`-j=QiOGv_P-Oe^e#?aQ+GQ74J3ka$_BeRpk+ODP*-IILd-?w z>;n`PwGtAf=qSoX0K*_BtGq>t^a6?{ed9lai8*KT^SYIJz+O-le5L`qD z^!a|GhdFgOXmyu$cVAuzKAMD{?hVhsKeSDe9}RfL3Gzz7*0kSMyj77P%CPQkma?&7 zP_e+i_iQ_{0W@Inw4EUivY{*Z{AmrS3$$2LR%AVFbQG>DF#~-Tn&3lS#fbP?8@rF< z9Fa#3^q$4W zRGEUlYWC!x!BdwBs?mLZp5!Qhm>Qk^v#!p)V|e!cY(!0X+Ga`@-rT`<(Aa~AqLTd* zU!xGY2JojHqWrBcSCpYI`Y{anafX`%-n`I!wwkX%NB<*AC2=Lyi_dK}(_@6vOii17 zeOzK<7{LrR(D8mc=@n1;m3&$hsX_|kIYMay`L5Nmz36R^#8W|O85XTfha$C3AQDmvRB96iOM{~)7LMTUGX>q=F~`V6sE1=H!1aKads zEmXQBE56*kuOljOjafqbNQg$`)%r5*p{9~#kfx+cceDWRpIqI5(yB@Oh`~+!>$$c2 z6>*5@2MwXhSUS!>2FY~y$y9Zlm0gsP7V$Lr+VmKzSXM`!d7u~r8ZAF-xhC+)%y?Sp zBA&<^>abj~+dL<dc$`t|DyT6|3@jTTYnsJC^HfY1C8Z+7XipLN za6`&n1VG8bPmIH(iN~en(4j1u$14Ke8}SsAvsjvq0u&{9L(2;6-$=Uf{IVkEuk z72fyfHc*_O&-?u@^^-qF6*qqk5(F6~CCqk5h}YPw*QjyWudzV>gNIhLKAj)XLwtcm zwE6KEDR8;{m8_;mRO-nmjwR6h?!<0BU&*eRsII~W^i?P7FP+T|iRVIcGoRV1#LI%l zJc#;@t0I`>`BLcVofY^+4_bvUkGu2l=-?>);e4^9YjiOHd1f;)4&BT}eKynbzu7!; z|AImrOCp}><44xss^C?c@jHH?805_77Tyw>vtr2>>H~u9imx6bLc-WbmY}|uO(n6IC7~sw?9+2EPYb4*GC;-f;+F=y;G=-kGq#f+b7ppPiFee9_vDOB{oz}e2_FxcspG>$ z+WEXdy*~1y<`s0wXzIxDN)G1H%p}M~kn(F$J#otNW2HMwdT%^rIrHl?VaSM3V@P_x zwwa)sDy+U5Ch36VghXT(lWj}Tf>mU2Fvj3n#>(5x`6g-dO>A642EQ)qljcItMm!Vt z^$N)Y)?dOPr78Rtw$GHDcjgG|pzX4RB+g?sT?h@iT38{DjiU|-(c}K>^(0F{px;i5 z>4nTJt)gKFhtMu_wFqG8B7w)cV1qwTHZlaD{gfiDFedN18Im-6?tBzz+3QaxxJmm%=CVg#7F^PGaJH+bD^Y`Ca_!W>5jTzfT;p z(sjP0u7KUqUYCKj20@?zP^?;gzPN}eGtX_RzMFW`m}4A+nPa{LzV3paytSw6soQSL zeOLGMgzWxmW8?r$?fd{`4o|kEz%}<`Mry-}ii=bcfm@K4vEaCdT$bc?@fpF0Q%&3!EXgjg}BrwgogHNpoIE!Z{J2|0Z;;$qo{KU>tj6;zy1Z>K0kfp zR+(YIeFySoAib(`i@gVnQ$WZ_Qjy1PZxgEslJc=?w$I5L0szU}}n zr%&AGX2$+~Fh?I2v#Y60b?gvuVj_GWQMlOk1B8tkN0WiXV4I=DDDwWShsvGl^^Kn^ zFk4qXtj0L<;n>)TswOW&%%vQ|gysg>D^JGO*Pe6@FC#B%#gkX9C{5qT)`?ABz9g-8 z(giK(BO?GfD%6$8EG!ccyv;iJM|D}L8iqQZm5hn>d5(;O6`y*SAUOXZ^H^Q~uL~!~ z;ql8)WRunHd>y(LXW4S0f=$zE@_T1u)Mu1qF4TmJ6}{uI3PLo-9rr^Pc}Lksb%~E1 zt~hQsD2jDf*+u_#L-J(}ney5+Mf1!7;Hq=jN;e#d@K!|W#!i%H!DeRWysW-dn|TP5 zI9P&hA^+kwg10_5F-U=*WeDYF8SuQ;wi94$m@C_kvKluu!TE4vCKyZ-yeM-7a|n;G z_n_XXw}A^pAbKt{UDK+{K&8N)`UnbPOo0(ZSQ5uB&U6!jJNZ{dau2z&vh3GKD%-udn8 zm-OZanBGCAJ9@<4+(L^#v4C%obQm604mjFipfe_VdFjY{(l_knwYuezGO!Pg+KGoUtp-FgCEZggMp7qS%urdWvVD<(gT_ahhVYplH_yqp(Xm+=6-%LK`qXTHD{acV@RD!fFDo)!4q=2A4rWs8- zw-l8lHHzMeNS3f8PR7G|n^uh36G?hfRG}+v+S1GGM`?d0DWUm*{A`=Bq3*zQ;gH6_ z)H?e!InYqK-U8hMbu(^ShRH~u;VJR;|{~7p_z$wqbKJO}{g+cN_K2khq z>;J4HqjWFvH1Djhn`YJx6po^&NpZB3ae5f~E!uqYc3Q7XN__D5s1F(3M0^>1$fp)q zjC75IQ;&-?ik#6pP?mo0w1u0h#JjHLf{KCP?;1eKkeaz6TV zN?jwk+B*=8sY?8V|M$rISM!U*Bg&^u98}MBD=U)xCgjeMf%+1Xli|P;6@LyiGc43bMBSl~v~!8E@0dg`g+_?wBPc zJpTKTfLy~&iV>_Y@XEaeduj@;dC;5zDnHVMvD>X$B?E&Q)6D%ICW}JNil#`tR+UO! zsd7;ANj+;&$1KY2GcA#*8{gn$Z*JxX9iwnce-RY)u1LQs7U7+E&%S_j=75 zEf+A8SvA3i8bUd?Bma;-Xt2nJE=CMp&tN|z|2Ed@CYZmuG}QbH;OS3_(ViOaAaER= zpV`5GI?_`eSE;NMCmGMh{G&XZrxxltE~`-l6|D9!47an03!hjzy1HFACP3Omo)R|C zdY1w@s0q|!KI4H5MTz-%cjBZQkC_3{I4tReyM1BZcI zLfKdI^`;yQriuDE28aXKP8{NOtfvKj# z6n4sSiLIKldryXRv_#EiOZPv63NC5I-VHx$_hV}gUZh4vVqsiW_oY16D&H(irND9} zu2f7a-YJngZoYKBjR)p#6Cn_WZSZe@x`2%39!*_1m~ehC0qRem-!4{$6XjWrt|2k% z_Pu0SZHd6u(!?M=WDu4|Df4M-FlOl5;`N}$PPEubmc~Erp^M|M1^=JX9tQ(Pn z07E%0C2OP%F;TE5h^({kdJlHlM5rENA2Z^FR;)Npp?64I2~-AqVZz`k6{`|*OI~7! z^Li1;W^~F%g{s4AqSDW}|C|?Q_iVqr=m|=D5K8a+`0r^MSEtVu&5IJXQZ-n3=A}aY zFi}Mz!E{GLZ z0G6;mJu@Wi3%Z5gz`3_Sdx<}@zoOUjv8%qLM?BWHp3T-!=%LjeNj)%BmnrROxpu@{ z@#JGO3KhXikCf#l*d}~+2_NgYO=?E;Z&MGeNmsg7u zX=&`V=CxjV>SkvS+I52(yG~trvuzLd3Pw}*)HwjhRJUObCrn+XEDG3!upM(7dcR5s z8yaqG@-D^Lj8T0Z&W-De9?+2mmIcb5sxVIU9`R*PW!1JTIfhvboT8)A?o}VDW<*QP zgo1ZXLVf@*{Ae}bK4D_myF=-yOF+KU#4>gz1lNO4xhs~JdQD|#|A2aZT!$zyo{9yO zVlrd7T>^R`Gkq2ud*Ft^CiL%R#b%0bWv#ydynU{BDiO}Y6{=-d@2dMGX1&J5+a5Jo zGpA+OY8M9YZU()&aHDmv+hAc%iG*1taglxNbzyh3o-Y3K`#NtT#FM4B%2#!S_d}W{ zncpgHH=TkNS>hQF51?1EBQY_8I`=R5!2GfK+2Xs)j2c;Pj-m9n-}GU0$jkc zV^mc%n5Y8i1}i{aAe(bDCEHVbpw8|^lGyfTA09ac!74`k7OLId7C*I>JL!xk!I zM=7vK(f*=~#zO2|4?M#D5gkHq8IOZmqMY!>}kF(89ZC2Rrr3rhOz}yFlocSEP$*M(_Y-PpT@WL|MVc z_m7$R>LL7PvQ@$}bk53!50*_(r9uuyWnEvEEwwTxt0j-q4hpRn{%RR6YE?xVb&VF+ ztT5xp5Q2aPm3gu#f3$AjT@KXemStV70^n7P|88!|F2+Ty5@eW(s#&U*k5$0MYI1Rb z=C*hl+W^;C1J-2)xCnD18wDJEX6S_u%YN-gjUX#p2V1Ke`}Qc?-*7(4D_a~LF33MkRUL%6SiQD{ z<>EpSVQ)O~ac6desr1Y3ptQnsK_Gz3lI$lVU4S~p~>m`z|@=yHxysTei+wvV7!lKW`1 zsNZ9AY*{0!o97|lSLp%>rXPnBimqc9A3O!7>EQDt`pR_BBW8O#CWe30%3si-_&q zK{-vM0$VbyEKy$M($afJFd87Skf}j87WXSgZLN$#<)=78j;qw6nevjWnXdIa@{idU;_fkl)DkG$e&R z!R~2`g*fm0EpIHa+v+#ojGlR!bVcr&v`DVQddRrS`%c6keS!MQExKR)t08~d9dP?{ za!ym)c{s_*LLXN19x}kH=&(05SJO5_;W+IB9Qv;RpUL1*$@f|sZ~y=fOaK7-|1BB( zUp8lJ3tN-_WFTy7D%x$bA^bz@5(EIc0u4&$f^pb18%t1yuv8UDibh!o3u58cZ`RS-dQSWdOH62skKCM^#Mg^(R&N>sXn_$JmL+ zFtm7LScHaf2`9$|t<(4HQH!=hNF#q2S{dCHMZYUvYpP1c+B8MxvRpw&|%VZ_Uj-fAlgK57JB{IC2~oGY~0&9)sR#tsmuJvZMv8(=tWmHoLi1X zD*00&G~PQtmhDP^yueQ#hQ#_FBGHNI8}C#$m_oT6wbXsh<)kx?UedFQUj~a#WI~&nsQ(ZGz zM_+5~fQb!3yRLZG_f&Yxo{bMV88@1V=F5B$JPs`Z@)VR-mtj^xNbwJbcixqMkCmzw z+o?3D^uB5F12h{4*Cn)aFb4XCZJO?OY?`8WOMD$Z-dB9AraI8mX8!vv-3&*T}% zG9Djoc)Q}|yOIN}^_zt#U848+ViC%us2@V_UPm*f0ydN|7DUv4Awy{YQhVws?uNgf zg4PmLzTyJc9=cP-(lVpqO}S@Ie}E>(6Vl3oV2B!O%>thM65n`g(&(Xc1swLst9 zXOdm1Cvo!Y$7VwRZt&u{q<}@zlUe)(!Piy`Ww*qk)s{jrLR!9MiYD+~Ub$O?lFzpt zQsoGylswS1N5=1!+_x5e209iBv?!yB?VpT|@5XmIkq3v;^<)_{_*S!mHVE^mI7pHv zdxSAXuqH_)r;WFxcu=v}K`Dknw>91HCWB$>IRnLakI)`gJN}86WEh0-ZH#!waqNPF z@Ugpspg;psjBh6J8o5U&TL!RkC5^%3XRH@~n2+ByBIg1)k=dxgzL)dLIq9?7x-9T6 zR6A~UG@7*`WYs~u^@G%_Ns70veRU3%wX*3mv zIkk~Sr#eMmy}GaU$k?YW^bplv(4|Vxy=9-{d>8w^WPsUKTBn0SkN8MO5Ej%jj%2$s zZilY_^_^m!^U0}e(31(qtA7@K{4Ph7;OKEp_;D(^ql2;*ug7V}6<#IHI}Lw*cy{V8Rb4FdoSbsv=tK1=!{H|VtFh3s`g!*Yg@JY5>3j~&(ODzH z9Vvv#Ulc%?61SxOM5apNhh=-w4aq`!C{VzkVrhz|4iS`!4NT?AVReL?b4!OrKdI0N zc+bIPPmF~nymePjy%|uZSGYo`<%`19&gxs(vRUS6m%}~_70utgR;=;YBWh);E5l;0 z-&EXVYYOqx(OZ<^Eb957&(Yp>&SIVAIp531og=Ke=u(PoY2*)y`N#;7tN69mE~?AU zp`s2#XJ-+8IZ5yX*s zP#`e3bJ0U>7@jh(2>~8eEf#5@f>zb~RZgngJo{i3HU*}ovYY>W6?%E6-waEumY2ms z^Hf$we_<(%6fyY|FxQC>#OWLkoARNL(-~~%+NOxDNy3ZE;Z{rF({10dVsj%sHD2Ze zo{_64kL{OZeKu@HF8$pgIODj(o9G`W-0&~(|2%kaTf+8g|KZw{!2kg0{*RIsnN~if|J46k%u%(TUbrfsHj^XTrt5 zG1)~2@s5^#sT_&VS2NaK2DPjHcruP4R&9Hxpy4Do&?<47QZR0I49bo(%FekFLP%qp zpk_+T^I0W1V%Wh|bN;*fR&tO7Ur#g~@Nzgvu1Yc>$`HXa)dFW~`}=J9Hv=8Ow)ci) z$^NWc5SYM=3<;DU-|?IC9Bw%0(bG)ohhR*k&}RveXQQMm-CuGKK{O`yeGci| zfWO3)Q=9wRN}#$WeOOhO%Y=-QosL!_qM-8)`eosw2(YMz)l}=_*(Vv~W@s5^?x&y> z4JE_^r=;p8@RpVg-nU$V*2p&nQP+0!Pr6yId~jIq^7RxQ%@jPG`^;Tj)>CaSA@~2Pr7h;ou7Ovpkm*HeSaUu*X5aBl)?b-$kqY zi!JnRk{80aSwbFG5!ceD+Gs*8Yw=Q}8RXyo2Rbfz1GtX(AL}d=8UTRy|6}R@lRBj) z8@b7V(0yBr;dl|Uhrmrt#wa~mxn%(yQ?Xqn6kS?f0Z?n}&iF z7E+6AeKPh+BR&JtBWjaIVA6z-v*n_{x&Uu&lAplrUKKW1uA+&{C1A@mS1e$njv7-A>|WlKP+V?@p5b{RyBDEh+{l$~^p+F6@(O1pII5rc3e&fr5e(F&kEx&r-?t$V(e618s z_fPrnWeV3;YuhipHZHi4^Y6qAh;I!u96Sx)*X>#{Ze*&_)7}Iay?#$WoW7R#dqMQ= z&LUUP!$@+#?W$D#-zTq{>t)qpeS15%OTB-Frbf6~cyKD51ZSB&kDSM{(`nFjnjOV1 zECuhj{A|q*R{Iw)OYaJ>3k&8YEK3x`dsLSWN61!_dvOTfaK8 z#xZujuR=)ZI=PeuV=>Cvi2w(ev=_9+a0DH=HRGJT(Ogky3Uwe>Oh*$B86FkB_Z=+w zMJ2;qk)o1bLsyQ24AaBI!VH})PS^{X=gcOBk(HWw$tV>aNenUxO(}>@rH%di8J>;@ zs9)Flc<=pK{ZxM&zptx%ZyLXM8h<#Dh9JUtC-oRaYm$i&bI_L%#p_9}Mv~&_t#Xbq zqg_Yg$om{D^%}~;Gvt7Ap4lxCKe1*g<1}NLM(5F1?{T4JWgxnZErJRqtm}Z0ILw$w zPnZ~NOSz|ugr>#V#+4+>l=bT+K6f~_Uu%{M#s_lM2P29k-+Q*6DF`x+s(SB|!I;k_ zgAaIk=Nyl8REx%lhaCfwrVoEJeGP@3h89D^)K9$k);S6?GnQRUAfu2kfY{UEqzX4X z&5w zQb3^UHnW&oFcc|3jaDuaXmJ(?_>t}EmqZKeJrE}0;A%HazISW~vJ7^NwM5|rrBIOU z((oVWjk95`RusNfByaXV&xsy)5p)c)4JGo+x@#(cP;j_!B?A42ekl%ycTw_aUngPA zgw7z2Glb)bWRfOrH|2Zd7?VLWAuFPBVq1a0?2{BILo&rChm>@GN*@Z&^G`Y@Ju-?z zQDX|H^cyyY@f(p*CSUsl95FV@`b9(&B*vfWK;~g$_&qPmRqLxxAQMIox92B5*>kkY z0RQ3>_%B*OsY=w7?W~z2R(~JkiRb-3_%qbiDKwT#- zAW2X*n=YfiKxgD`j4DnYOdHTN?heA}KeZOI13b$#d~}mz%1BS>Y>YYUtmOC1lhPP# z4kaI7HU-@i;|uDvJ_4HIs8wSo0Nm|&!s$Ijs%M_XMQfsr609-G3EWPBA)HRr$Ke=< zdUA${>IM%Olgzdy)t+m|89Zdo7k`I6TrK z;SVdixY{1;tYngrcjC-WC{Q;xVc3if4V=8noIYrT}d~yuV|k(dKCPR^2NN~2I% zDiUIe?J>p|NT3NagSq9#YJ&P>V3g%xMesgf$$%75j$qr4efkH~fNTK#Wq~u^;-@3q z1zcwcQuW*I5U1=sQ|u&r9-Rc~5|qt3KENt*iy%F+#Je)}w$0!1m?7Mrd4!i!9aF;} zTxm@-aaF(xnIjJ<%G#zfNYiukRQekao>fPW@Fhx^Sr1Rr7Xh=KekGXtYbMgUmP+L< zYnNr?64d^=sJmKx3#A6^l)*%argXF^$c9BwS;v*BeLQjbu_k!Ew^eKcB(j(roB5uG zFfWC^UaX8==h^dCx_%*wcdEkLenc}Arg~GRQpNA>*3l6+rw27Wj@cXQMc=APFkNA*{?lIgXUa<_q5 zo=ZPqvs%c5S$E3AXUz8>s~MFP4W3nB8%+;J1oLP5K|JZSED3WBd-Q~E{xDJ9V)?cP zjc_Y;t>-Tn?k1bTYpxX*mZ-3GkCl?O0@bu%Wo7;H`%o-||aHg#6o|pl>=f(WQOIHuYbtk-LP`zj`7PD;Ct6C2o2_NKpg$r0YOquT__$(E z!tV|0_f9c&Jl-w#uwbnITuba}P6n-LleFD4)YSKyZz?og(Q@#F5%K;M6e9icEGpr? zBspe^IgSk7Wn2f~0QB;hK7pDk-(w$^Rr}~w9cgmp6zsf~B zun#^cC;TKe%oIXv&eNa(x=1l2fJlnDNTVfkQJn}yI>fkesHmflj6RSNPIFd^xWGfA^?v zzR&oDLY3k=S%2`JDg641nVun(~cSV;~y=i`P6CWiEmIm4=JGvr9&ksIz6i|2yrv}?c}`}CRv-U|iif0?lG-d=zo4a8XNLa5+H zgbm|2roHgDx^xHEs>Zvvpt1$g{*(M;xuDk8Yo9?>+OL&E1=)hK*Sz76Wz2_AmaEUP z?Xjqbx^RPL73%`J$#27P)I+mG=YCyIqwxSzHH2kiN;!ivwN-Ck#}c0`kimJ}63@|O z*V}?OjwK-!d!{o(Ml$EJW80oBd*&quy7|2ezP0Q0RJz*Tz>2o0!6AE{A;CZG1|H@E z&*HJm+|991k6@P}XL@($EZ4|+byVMk9KM2T?I-Z`M+1!vn?Z%>oy9`!@6cs7y% zJ{R=yqw#Rcd<8WSLcJGq3?XV40{VD5$3klPejtY7%o^c2HM_7@QQetn7h($mQ?3vx zoR|Z8;h7AHUT31t6LW5v(HcF5gU^8J6o*ND!vor-vDW?9c}cIUvy%tfO&GquS_3VF zYIqg3XkCuiiV4%|4)QwEwp&8w=4oLI+9M`Ob(oW%@$Os4H6*-4YofA6Ta|f`jb2bJ z`3-B%l3RT9+Qh?O8)HyA5locb2yzqD0&k%&F!SZZxi+TR&4yQ%FOps0%H(GR8Bkxr z%WO2~)AI}=kX>#wEBq0R52#=NBZvb}+I3x|7Qy0>26pzX~2 z!4sf)1CE=^8l{_K_?ZCd2hh#IkONG-KIARXEpnND|0a)ph(~K>QSUIjiPW9DJ>CM9 zUfu1^<|V~6mv^{B=S5E=X>POnaGOA#bLCj)bN6Y=cvJ9}D>{;Il-wN&sb@|SK1+b_~zB+_V4n} zbmcJ3q4qChuz>>KFx<>bMin%C3KAo3vYkbGPFAXd_%f#MNde{#oZRvN78HWPluGiJ zD|M&kjkcWjAI6bmL)_=~xanb7QNeLw!Lnmo6wkNt{(Uk&dY(&?db@z%VAY$yt)c>( z;)*qu(zz0VtRK4>^Xh^7rQ5o7PNds3DZ47KhjJ5!_g|NZ^{qqE=&B53GD~%lfXQ%n z(gJSdz)SEre`Cu|q5^g!7?nG|I0zlP%wDa55jN;L)i1BV>wSaJA1UqT$!+S?7QHDLo}06uo-3tNEgloV7)LUK9Z~ zF9EiF7`6;^V$@}~LY~k4S&tIx;`2vhx-pjP!(6_dW$r-rrT2|yCpQz+f}`qLCFh07 zz5;-7(c$R=9&_JqYGP*-wDgO4)9-N-LN<{=TYG4!3l*;}XRw-_4C(5v2$snT2u&>i z2>^X-CsIDV&DfrEOYpdJ=&l+WKJ+8Q!{fK;@4sSWWMu3Ke6{6B64x6XHNWnHg=w+o zlw$~d^%1eAqmNgQb`UVcy-WosK@yFQs?M*sqH8rXoYpls%v|#bM>iAb zCY~WGQ`Tn*`U9_SNcw7`_%S%syi}{)8SU@-piT8+W9Wv%gdWX!27NQ2ag?6&D2WYVfQVp z=74Z{gFz+H%8r`pZ(TPvd6^+F+3qF#X4wsP+6K3`)?ULc6sN@Ub~2&6r${LQM9Shd z?=>3orL`zrFIA(AU&pHj1%z?{RfP=T6Z{_PJ# z*!a>X^MZqNc~~i_t?fAXo7?SL&u95g&3L{OI@O*nb~V{HvC~_B zw>@UQu3;V|&rf%pS~rP0&2|<9FogPEPlDli;4oA=De)n?eGFKaRuQV1#Y#ugIp3-=|NPb*97O4Mc^Z{ zb|PNhuI4@H9V6(O?e$}(d<4PlKj`R2F1+b-c=4kv# z0!e7tq*Yr47)^Q5_PYY}6;o2xFh=Xmb#r+(*r4O8;3NX9Aoj7+qM zY)W^nl7YU>4gGr}=Q!wIh}l7sB5E@Xk~dlGn#&#ackI2u;x59wXDc(K;gb~uI#7{2 zNd+Qo12# zsZ6K15AhtdE?9QFov?U^ie(OY<>#{@A({9l7^y`A70$bg2XJoJah)ojuphu?THm`} zkMSKjUmQ90xIWX^GklG%JNi|6ZM)P5Z69`}j;g>8qks&6Fx#%gvDA6*Y^qWljCoM%)MgXhi*#p+EfYAbgG^Z#+m`#sjxC>OJVQzC@hd|r( zC#|Rd07E7Ax&^)iAatG*kk*>p*_9<=(>^}-9X~u4%o^`-=HE}_IEw?~xko{u&q?21 zProy#?M0q8w=o!k{n8@pIhc*0bai{|>wWEWnXZ~mtC@6*$IOf?1sobAt~3_zISTv@bd z0+zr=$sL8qhld=Dw0k$9{d!6$F~3Drm4__3m#9H^8HhLBmhJZzwE7O|s)PLz!})N> z)n(|X_c|?v+b%&5m~hFij)&hdU31Vc*7Zb>aPf!Trg8i6amQ_45tHIHlmoqcQB7L-Q#8OGjQ`KYlt*Bmd_TFEMzOK+*-M&%E`^_mp8 zwn&>dYNM*pwhPKZ%PNHSVLD>jAWt zYu1VC#HIIO;4)dMbuXKa%DP2&wk@@+&x*M_o^FN+NaD(SEFp&WOc-8(N0wZT9RysC%Ll;w16GyuL9*pUn zosM~~9XH3~f4@;0--#DW2oe$~H1#ahUp#QhW3n5MHdV@11`se3!UX+70iWw*DwLAN z$CQs)Z+4Dyjyk+d{Qv>u2uy0bQ8!5=NlHpfdwWBA?u2q6*rLBZDAsO69_J-UdW~}N z7~h!T4hi9(p}&wt$|c0Mkgqs---u$2Gp4#(ir$71o(^%_U7SYV7|ONE$b zh#Ur&Ql=se5(%IR7^TSrq64iE>*f9Kx*Guu5cgu4bM7bu#n&}65d1Loh6TzA7WT3; zM2Q{@@l%a^`!?E9aM5P(hIpJYKH(V;9vMvhjkO=0o!eA_P9iml?iBbE1wl}HZ(MNS zCR^6QDbp%3_M#HpHxhylMaefG8m@r~m{7O$MVw0Su*!lfW7B}*h2Qoi8sCb23T<}W zTL{??5a;;bM7J8(q(!MR9{^GS$WIj`5SEmRC{{Z;GL>TfPi1ET73J1F{Gm&_Q$VC! zx)Bhhkp__%hVGJXq(r(Dq`Ra=x+D~&L6AlX0Rai=@A3L_)%knp-tTd-Mp$cpXP3}mM)+Hr-uFXs@icOOOQFbotOtRmVZ%68pjnTqkc4)=bMa`^6E zQEzUixjI)=yzuLfv=ou~!BOyTtzXw4v&Nmz3Gqw_(B!e<^ta3^!rR|Y$cP!fIVOgH zFh-tv+1aSkL>`qZwse8wwMsmJm;XUs> zR+$NX$}T~TXZ10pZvW2Br|xTE!wxUNW|2KIM{Yx3UTkl*i(OD$E6vy+l)S7uFAW5- zDDQbDRN~wx;ZAtDWEV=!$Vzpg>CR-0s{)VqoC9yV^Okma-W#*KBKUVbQDj+*=(|8~ zHIv$HAL>Xv;l4R?oY~JxvVLS_emkf0lNw_#kNqkwF+=3gVkb5=S|~agMT1aF5hyhbGN zz^mTu?sh~$d>Q<~wy{L|SR=qUm5ysOgXuM0xIJxdc6S$}GTR;a*@eVxq^cb0F48^h zUU-JC!g8a8X~90N4ZTGE#aKhag*FEDA#bNqi5uB;GPyYF<84S>k;^f$8d_eipL%X5 z*}mXwI>mZAmuGgX=Pc~}8uta+(o7r12yvyHNZ3&e$MGxPG|ImGA-rP%56%Gz56*^uYxNrD zUFQKvleD|O+jF>_Wu|kFe3oO;w zI^fJmw?wDY=loTz4`xcVI$uR@-(;Qr;@0@CSZr&tz>6?L%bg0{I+uo87d+-$U&#>= zT8moqEKL|I1poCi5i64yN!#kX%#k7ylX>eCDyphE_GNDfU%VIITodKRY>?BnV~7@BF$ZjaPV*d8rPZ zX51-o$HG?r)IC{PChtT{6Er#;sN~RP%S?Mckjara=(eg0(l~s^ZSxkKo`6#q|2b-r z+TB1$6=qGE%8Gq2nO>4)MnxhxeRn1qp6wYu-~I9PQKmvUDuUI9PId^omq!aHiA0M( zd}~iI^KzeF<~z2E0#FXN;J9*br)O>uYH2G$ikhS~7m(F4pN%^@*ob-&KT7%j`iJL} z9`#YB7e>jLhf_uwn8=u=7kmEI#cd9+j6gz~({nV56S!0n259)^wGNYnLe7R=52O}c zBUxl616M*+RF}KASd%)8qpyGVTpeESN%=4-sd1RtoX06xfjK6@oZd|v9mF!v=rBW+ zUtTSg#32Ufhb|L)f+Jp$kme%K=|temiPW(twQfFBWavYhvB$Hr$Cd6>4)j z2Hk~>`eTyb4uqXnPBfCr0~{8%Y6ac>$i&l6Hf2PrO67+aO0cx?M@v9sB)SKSeJ7&D zO}D?Y3+mTN72WH+$S-oLDmoZXVveSF4fq)UPTki~YSEf%Fme8kRhVFDp2lNYwjReo z4aK}&TH@pZM?a1c54=Q?&<%Y==Hy4T@NVn@^O=`A3E={y9;NBix+Yp@eG#X2Byzp_ zqa^)>@lFV}*{>g$IN&~W7&E+s@;P%4StvqzIU&^E1@9V(ThSQ7I{q1LoV&g(VR@qD zO&8@weu=Cagr~TiSd$CXKI%^o-PZ=B`pK)v>{GFBJ|rTIlL#E}F^rUpS(A{k3w(ga zOJFw+AMne3Pa>AWGV~hWTjIVk_G3d?(~+!=5a*ZOB49;BW3XPl=>QhPN>EY z&CoGgeeIEb5FKa0%jU!UhdD%>>sQuiM@BQeZaGR>~(MIGdh>B~pvCk-lCg-`4z*YUmxL zJeP0tMcx60Uqz*oOlwY5LZ94QdKSIKr_Wy=qLz{zEu#t%3my{7(3~2dr$!U+vJQ1y zmRM(D=*x+eYOV(ttwr%y#mEPIYL=9_v6LMdl8$@UV51?$c1R6Juj%CZv34i6xb$HL zZ&~R*E{pEYA^c(N?JpIcHQJYJakPqGikx-GIn=SSq~=vTqE;UC?QE7SyK|WIpGRk< zJ1cK_mve&;>evUZ^Cvr0rG@gD%`B|Q(F5VhAtCtc%If6`80wf$BD3wCiHWHy>lb7% z9X_qBZmrpVz1MBBwRG!Rr0N|w1kLht64jTt0tup*D2T_+g1I8l`69NY<=~kPJA1T4 zJq%l>UaTb?waQ@(xGBtT_nCnyBUU7gNv=_rSoxT4@~4OH&55U9eB2m#Q(rOY=NElw zpuBkhlkp3N?KekXD&~9yT0M_O$4^J=D;~UJ_GxQsx~uW5QrEQ_{;~P6)~Bciyw9`~+gC4nEXbtUEdmHp!iski87U3PQ&;ch=^^4jMNX zKz@R2qrzU~4#y!`=Svo>e9Mmn3VT0_EJ}q1n3Bwsh-#nZ?{J4m_P=s)LIpF?!h;CD zNW8J@V+{+RWkyYm6Te{`(d8<}i-O-6GlmDRqpo%q0@weU`nkf5Xn{Leq>d{g&aEa? zkBIQ>MvX-7GWBFQ_zMu|DD;|6Gb6p)Yp>Q$&C^W0`%!?Ob!jny=MktSM)F?&OmcU$ zu6w}ABe?#eVsYb_AX7SGJ^0(W3T$*a)jh>~4|RFgMacR1krqHC))TDJ$_b{^;EACn z_%`trG`GU3kH$hxoY+1A4Tm2C`n4bCtR@zPu(fODn-at_yT0hg#KFF3gzsz8L-dB| zZnKO__K)34u4mr5
    (cBc@uCST5}U#0~HHM!_qNAbAD!;B^URfz=N7o#j6sWnRp zEHiBGeA%W)iXBrpl=0GfRSY4&7%Q~Yuh&hsbmmP^*E##lR>jl0ehQ}}ZB)JMRGq$5 z)%tl#sVVC-_bezq>h@VXU;g1}U2M2cf{<6sq=oLdFsR6}va>f(C`cYaivTfyZxcBDp? zVmhh>D^?!ppp?g*H!np|Oxq|_+;!U3_ptLESj<8B?q7}3UxHH#mj}FU9(Q4co|#V- ztJ7w*B?X|o+ml}5%dv83r;FU40-$7dmZB?btVIBsPp_mw)!u?6V zHyu%Nns0YEF1!#0k{-H7swwpuzimeI(kc9Ut9X<<)M&o6)R8s)>7ut{C4smR-%fP9 zvnC~|j8|#6v_h+9Jk@gu63V>5KQiIxdiqreYfSZH3WJo`9jpLT&whr$_B5lIFi|U0 z+;EiW?f#pndjifiU7CrJP1ObLCPNyiJzC!g%8(x{1}yc`YRv3p;#jn&DM;hre})=l znfdIKe3Hg>o{;e3=$Z};q(dun!g``z5~8_CsWz8gt2~l3DyMJOZzEj5b3sl>Pp1)| zWr%qbEU{7}#UTr>rQ}?5hQJ$LV01On7IMiKeoL2OCm=r-(7wYEYMcM*jy;(o(m}Cf zy|FP>!-~_M2kNG&{f%$TZsQ8yNKw@l3^&O{D$ zEY@1f!D6XqkMl=PO`ap0cG=iQpi!C?Se|gM)(%I?3f&uv+js?cX8p3_RkG-_H)y4{ z+A%de{4TA1l!NaKGd3=Zjqxg-WC~pjPsa)0cf*z3-zRMC$Tr)PpS9xC-Zk}YZ+6EX zyzk$E7FOeJ9O(IYMW?N1uoOddEc9*vNsGesymq?C7H$k}ET0?KIk|WcocwRI+{W?# zEDxvSx|V_fp@=}!BeXQ*Gedr0hJ}UN@>E*hD^9|ogqQiQ!&=?uo=;2ON%T{qiO(|w zAa?=g1#t$>0~A-zj8gVjrl3scisV*W{hU?fTZ-rn3_wLu ztP|J3yuRbD{=BZFB_`d0NGrnq-C2CyYJVG?LrUL#P)+uz|76f^*3EO|OMwhpT`aWc zkY2>`YjH78%Dz1y?)JXjM)nZIN&Z&otgf_)S)=IlSdE?HaJ};7Y}(S(8mnTyo(Iu> zT0RHp+W01jyd~7Yd++xpBoSG$tBz+dK3~MyGS;dFs=u^R8gZeIc5|!p?n!;!+Q#(~ zpJAC2*Va#9W*4loea8Ao*l%I-e#3tH(IXY%inY&`<#R>3)(rDbJvKE}!N&|Wat}S| zEzpqq(rH=Wvvn)7;R-pvu}RbAMWo~jX&%Y*ptO;+9H)y&U=H;@R8pKJ@{9~#Uw
    T1~mzF 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
    563{4x61+ku`8mCyB9b|=%aXPJR^XVs0rBhN+ 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
    n3WOPsc{Oa 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%0DqN-Ngx*BSt%lt>o;i?8?q!n|)_< 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 rj#Q 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

    LpW#b1Coc64TmCJ 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 G*bHnqIWHF1WLX$`8i8irF>;Y(&**MxPV7h|P# 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 KkQ1nXK>iO}fxZd= literal 0 HcmV?d00001 diff --git a/Dockerfile b/Dockerfile index 6984e3ad1b9..6d7b8f16e9c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -72,7 +72,7 @@ ARG DEPS_IMAGE=deps-${DEPS_IMAGE:-base} # # DEPS: INSTALL AND CACHE DEPENDENCIES # -FROM node:20-alpine${ALPINE_VERSION} AS deps-base +FROM node:22-alpine${ALPINE_VERSION} AS deps-base # # Install some dependencies @@ -828,7 +828,10 @@ RUN yarn workspaces focus --production dashmate # # STAGE: FINAL DASHMATE HELPER IMAGE # -FROM node:20-alpine${ALPINE_VERSION} AS dashmate-helper +# Node 24 is the newest usable LTS line: on Node 26 `cbor` (through +# nofilter 3.1.0) fails every decode with "Insufficient data", which breaks +# js-dapi-client, and the image ships neither Yarn nor Corepack. +FROM node:24-alpine${ALPINE_VERSION} AS dashmate-helper RUN apk add --no-cache docker-cli docker-cli-compose curl @@ -880,7 +883,7 @@ RUN yarn workspaces focus --production @dashevo/platform-test-suite # # STAGE: FINAL TEST SUITE IMAGE # -FROM node:20-alpine${ALPINE_VERSION} AS test-suite +FROM node:22-alpine${ALPINE_VERSION} AS test-suite RUN apk add --no-cache bash diff --git a/book/src/evo-sdk/getting-started.md b/book/src/evo-sdk/getting-started.md index 7dca58e5dda..7c426a9bd1f 100644 --- a/book/src/evo-sdk/getting-started.md +++ b/book/src/evo-sdk/getting-started.md @@ -13,7 +13,7 @@ full type definitions included. In CommonJS projects use a dynamic `import()`: const { EvoSDK } = await import('@dashevo/evo-sdk'); ``` -Requirements: Node.js ≥ 18.18 or any modern browser with WebAssembly support. +Requirements: Node.js ≥ 22 or any modern browser with WebAssembly support. ## Quick start diff --git a/book/src/evo-sdk/networks-and-environments.md b/book/src/evo-sdk/networks-and-environments.md index c6ff4f62673..2a78855a3c6 100644 --- a/book/src/evo-sdk/networks-and-environments.md +++ b/book/src/evo-sdk/networks-and-environments.md @@ -103,7 +103,7 @@ handles platform differences transparently. **Node.js considerations:** -- Requires Node.js ≥ 18.18 (for WebAssembly and `fetch` support) +- Requires Node.js ≥ 22 - ESM-only package — use `import`, not `require` - No additional polyfills needed diff --git a/book/src/evo-sdk/overview.md b/book/src/evo-sdk/overview.md index 646e49ae187..324bac417f0 100644 --- a/book/src/evo-sdk/overview.md +++ b/book/src/evo-sdk/overview.md @@ -3,7 +3,7 @@ The **Evo SDK** (`@dashevo/evo-sdk`) is the primary JavaScript/TypeScript SDK for building applications on Dash Platform. It provides a high-level, strongly-typed facade over the WebAssembly-based Rust SDK, working in both -Node.js (≥ 18.18) and modern browsers. +Node.js (≥ 22) and modern browsers. > **API reference**: For detailed per-method documentation with interactive > examples, see the [Evo SDK Docs](https://dashpay.github.io/evo-sdk-website/docs.html). diff --git a/book/src/getting-started.md b/book/src/getting-started.md index ff9dbc5c46c..f3aeda52ae8 100644 --- a/book/src/getting-started.md +++ b/book/src/getting-started.md @@ -5,7 +5,7 @@ Platform monorepo. ## Prerequisites -- [Node.js](https://nodejs.org/) v20+ +- [Node.js](https://nodejs.org/) v24+ - [Docker](https://docs.docker.com/get-docker/) v20.10+ - [Rust](https://www.rust-lang.org/tools/install) v1.92+, with the wasm32 target: diff --git a/packages/bench-suite/package.json b/packages/bench-suite/package.json index e0ab33d578d..d2714dc5f81 100644 --- a/packages/bench-suite/package.json +++ b/packages/bench-suite/package.json @@ -3,6 +3,9 @@ "private": true, "version": "5.0.0-beta.2", "description": "Dash Platform benchmark tool", + "engines": { + "node": ">=22" + }, "scripts": { "bench": "node ./bin/bench.js", "lint": "eslint ." diff --git a/packages/dapi/package.json b/packages/dapi/package.json index c96c0c0c4aa..092e016cd8b 100644 --- a/packages/dapi/package.json +++ b/packages/dapi/package.json @@ -3,6 +3,9 @@ "private": true, "version": "5.0.0-beta.2", "description": "A decentralized API for the Dash network", + "engines": { + "node": ">=22" + }, "scripts": { "api": "node scripts/api.js", "core-streams": "node scripts/core-streams.js", diff --git a/packages/dashmate/docs/installation.md b/packages/dashmate/docs/installation.md index c367b811cb9..9180a987069 100644 --- a/packages/dashmate/docs/installation.md +++ b/packages/dashmate/docs/installation.md @@ -47,7 +47,7 @@ To install the NPM package, it is necessary to install Node.JS first. We recomme ```bash curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash source ~/.bashrc -nvm install 20 +nvm install 24 ``` Once Node.JS has been installed, use NPM to install dashmate: diff --git a/packages/dashmate/package.json b/packages/dashmate/package.json index 65f1b458a8c..702ad9ddf23 100644 --- a/packages/dashmate/package.json +++ b/packages/dashmate/package.json @@ -48,7 +48,7 @@ } ], "engines": { - "node": ">=20" + "node": ">=24" }, "license": "MIT", "bugs": { diff --git a/packages/js-dapi-client/package.json b/packages/js-dapi-client/package.json index bdfd97643f9..c3468491baf 100644 --- a/packages/js-dapi-client/package.json +++ b/packages/js-dapi-client/package.json @@ -72,7 +72,7 @@ "webpack-cli": "^4.9.1" }, "engines": { - "node": ">=18.18" + "node": ">=22" }, "files": [ "docs", diff --git a/packages/js-dash-sdk/README.md b/packages/js-dash-sdk/README.md index e8d7e0d8870..cb065274233 100644 --- a/packages/js-dash-sdk/README.md +++ b/packages/js-dash-sdk/README.md @@ -29,7 +29,7 @@ Dash library for JavaScript/TypeScript ecosystem (Wallet, DAPI, Primitives, BLS, | Wallet / key management | 🟢 Full SPV wallet via wallet-lib | 🟡 Key derivation utilities only (BIP44, WIF, signing) | | Module format | CommonJS + UMD bundle | ESM only | | Browser CDN | 🟢 unpkg (UMD) | 🟢 esm.sh (ESM) | -| Node.js requirement | Any | >= 18.18 | +| Node.js requirement | >= 22 | >= 22 | | TypeScript | 🟢 | 🟢 | **Warning: This SDK should only be used in production when connected to trusted nodes. Although it @@ -52,7 +52,7 @@ Dash library provides access via [DAPI](https://dashplatform.readme.io/docs/expl In order to use this library, you will need to add it to your project as a dependency. -Having [NodeJS](https://nodejs.org/) installed, just type : `npm install dash` in your terminal. +With [Node.js](https://nodejs.org/) >= 22 installed, run `npm install dash` in your terminal. ```sh npm install dash diff --git a/packages/js-dash-sdk/package.json b/packages/js-dash-sdk/package.json index 0e610d0f278..bbd0e66a2ef 100644 --- a/packages/js-dash-sdk/package.json +++ b/packages/js-dash-sdk/package.json @@ -6,6 +6,9 @@ "unpkg": "dist/dash.min.js", "browser": "dist/dash.min.js", "types": "build/index.d.ts", + "engines": { + "node": ">=22" + }, "scripts": { "start:dev": "nodemon --exec 'yarn run build && yarn run test:unit'", "start:ts": "tsc -p tsconfig.build.json --watch", @@ -57,7 +60,7 @@ "@types/chai": "^4.3.11", "@types/dirty-chai": "^2.0.2", "@types/mocha": "^10.0.6", - "@types/node": "^20.10.0", + "@types/node": "^22.0.0", "@types/sinon": "^9.0.4", "@types/sinon-chai": "^3.2.4", "@yarnpkg/pnpify": "^4.0.0-rc.42", diff --git a/packages/js-evo-sdk/README.md b/packages/js-evo-sdk/README.md index 25fd8dfa1df..134a0c05fd4 100644 --- a/packages/js-evo-sdk/README.md +++ b/packages/js-evo-sdk/README.md @@ -32,7 +32,7 @@ Evo SDK provides a high-level, strongly-typed interface for interacting with [Da npm install @dashevo/evo-sdk ``` -The package is ESM-only (`"type": "module"`). In CommonJS projects, use dynamic `import()`. Requires Node.js >= 18.18. +The package is ESM-only (`"type": "module"`). In CommonJS projects, use dynamic `import()`. Requires Node.js >= 22. ## Usage diff --git a/packages/js-evo-sdk/package.json b/packages/js-evo-sdk/package.json index 9c2b19985c0..e5dc9f6f148 100644 --- a/packages/js-evo-sdk/package.json +++ b/packages/js-evo-sdk/package.json @@ -15,7 +15,7 @@ }, "sideEffects": false, "engines": { - "node": ">=18.18" + "node": ">=22" }, "files": [ "dist/**", @@ -33,7 +33,7 @@ "devDependencies": { "@types/chai": "^4.3.11", "@types/mocha": "^10.0.6", - "@types/node": "^20.10.0", + "@types/node": "^22.0.0", "@types/sinon": "^9.0.4", "@types/sinon-chai": "^3.2.4", "assert": "^2.0.0", diff --git a/packages/platform-test-suite/package.json b/packages/platform-test-suite/package.json index bf187692783..f001cac89a5 100644 --- a/packages/platform-test-suite/package.json +++ b/packages/platform-test-suite/package.json @@ -3,6 +3,9 @@ "private": true, "version": "5.0.0-beta.2", "description": "Dash Network end-to-end tests", + "engines": { + "node": ">=22" + }, "scripts": { "test": "yarn exec bin/test.sh", "lint": "eslint .", diff --git a/packages/rs-dpp/Cargo.toml b/packages/rs-dpp/Cargo.toml index 0850ef2b27a..1e74169571d 100644 --- a/packages/rs-dpp/Cargo.toml +++ b/packages/rs-dpp/Cargo.toml @@ -97,7 +97,7 @@ log = { version = "0.4.27" } # `wasm_js` feature, so enable it here where every wasm crate picks it up. # Unlike getrandom 0.2's `js` (which falls back to Node's `require("crypto")`), # `wasm_js` only reads `globalThis.crypto.getRandomValues`, which Node.js -# exposes by default from v19; the JS packages therefore require Node >= 20. +# exposes by default from v19; the JS packages require Node >= 22. [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] getrandom_03 = { package = "getrandom", version = "0.3", features = ["wasm_js"] } getrandom_04 = { package = "getrandom", version = "0.4", features = ["wasm_js"] } diff --git a/packages/swift-sdk/run_integration_tests.sh b/packages/swift-sdk/run_integration_tests.sh index 1177a642dd3..938858d275d 100755 --- a/packages/swift-sdk/run_integration_tests.sh +++ b/packages/swift-sdk/run_integration_tests.sh @@ -13,12 +13,12 @@ REPO_ROOT="$( cd "$SCRIPT_DIR/../.." && pwd )" cd "$SCRIPT_DIR" -# dashmate pins Node 20–22; bail early instead of failing several +# dashmate requires Node 24+; bail early instead of failing several # minutes into the dashmate startup with a cryptic error. node_major=$(node -p "process.versions.node.split('.')[0]" 2>/dev/null || echo 0) -if [ "$node_major" -gt 22 ] || [ "$node_major" -lt 20 ]; then +if [ "$node_major" -lt 24 ]; then echo "Node $(node --version 2>/dev/null || echo '') is not supported by dashmate." - echo "Use Node 20–22 (e.g. \`nvm use 22\`) and re-run." + echo "Use Node 24 LTS (e.g. \`nvm use 24\`) and re-run." exit 1 fi diff --git a/packages/wallet-lib/package.json b/packages/wallet-lib/package.json index c8ee66ae754..d1a3501cf54 100644 --- a/packages/wallet-lib/package.json +++ b/packages/wallet-lib/package.json @@ -5,6 +5,9 @@ "main": "src/index.js", "unpkg": "dist/wallet-lib.min.js", "types": "src/index.d.ts", + "engines": { + "node": ">=22" + }, "scripts": { "build:web": "webpack --stats-error-details", "lint": "eslint .", diff --git a/packages/wasm-dpp/README.md b/packages/wasm-dpp/README.md index c1da995eba0..774f56513cb 100644 --- a/packages/wasm-dpp/README.md +++ b/packages/wasm-dpp/README.md @@ -7,6 +7,8 @@ The WASM JavaScript binding of the Rust implementation of the [Dash Platform Protocol](https://dashplatform.readme.io/docs/explanation-platform-protocol) +Requires Node.js >= 22 when used in Node.js. + ### THIS IS A DEV VERSION, NOT INTENDED FOR A PRODUCTION USAGE JUST YET ## Dev environment diff --git a/packages/wasm-dpp/package.json b/packages/wasm-dpp/package.json index afad8405fa9..b989ba92b41 100644 --- a/packages/wasm-dpp/package.json +++ b/packages/wasm-dpp/package.json @@ -4,6 +4,9 @@ "description": "The JavaScript implementation of the Dash Platform Protocol", "main": "dist/index.js", "types": "dist/index.d.ts", + "engines": { + "node": ">=22" + }, "scripts": { "build": "yarn run build:wasm && yarn run build:js", "build:wasm": "yarn exec scripts/build-wasm.sh", @@ -46,7 +49,7 @@ "@dashevo/dashcore-lib": "~0.22.0", "@dashevo/dpns-contract": "workspace:*", "@types/bs58": "^4.0.1", - "@types/node": "^20.10.0", + "@types/node": "^22.0.0", "@yarnpkg/pnpify": "^4.0.0-rc.42", "ajv": "^8.18.0", "assert": "^2.0.0", diff --git a/packages/wasm-dpp2/package.json b/packages/wasm-dpp2/package.json index 643ede05350..b4a63215ac7 100644 --- a/packages/wasm-dpp2/package.json +++ b/packages/wasm-dpp2/package.json @@ -25,7 +25,7 @@ ], "sideEffects": false, "engines": { - "node": ">=18.18" + "node": ">=22" }, "scripts": { "build": "./scripts/build.sh && node ./scripts/bundle.cjs", @@ -42,7 +42,7 @@ "devDependencies": { "@types/chai": "^4.3.11", "@types/mocha": "^10.0.6", - "@types/node": "^20.10.0", + "@types/node": "^22.0.0", "assert": "^2.0.0", "bs58": "^4.0.1", "buffer": "^6.0.3", diff --git a/packages/wasm-drive-verify/package.json b/packages/wasm-drive-verify/package.json index f09a090c5ca..6239309a755 100644 --- a/packages/wasm-drive-verify/package.json +++ b/packages/wasm-drive-verify/package.json @@ -52,6 +52,9 @@ "dist", "pkg" ], + "engines": { + "node": ">=22" + }, "scripts": { "build": "./build.sh", "build:modules": "./scripts/build-modules.sh" diff --git a/packages/wasm-sdk/README.md b/packages/wasm-sdk/README.md index 64bf9198866..47a70756c34 100644 --- a/packages/wasm-sdk/README.md +++ b/packages/wasm-sdk/README.md @@ -209,7 +209,7 @@ Accepted values are simple levels ('off'|'error'|'warn'|'info'|'debug'|'trace') ## Environment & compatibility - ESM-only package ("type": "module"). Use dynamic import in CJS. -- Node.js: 16+ recommended (18+ preferred). +- Node.js: >= 22. - Browsers: modern engines with WebAssembly + Web Workers. --- diff --git a/packages/wasm-sdk/package.json b/packages/wasm-sdk/package.json index c76b5decfbb..6541b388d97 100644 --- a/packages/wasm-sdk/package.json +++ b/packages/wasm-sdk/package.json @@ -25,7 +25,7 @@ ], "sideEffects": false, "engines": { - "node": ">=18.18" + "node": ">=22" }, "scripts": { "build": "./scripts/build.sh && node ./scripts/bundle.cjs", @@ -43,7 +43,7 @@ "devDependencies": { "@types/chai": "^4.3.11", "@types/mocha": "^10.0.6", - "@types/node": "^20.10.0", + "@types/node": "^22.0.0", "assert": "^2.0.0", "buffer": "^6.0.3", "chai": "^4.3.10", diff --git a/qa-contract/README.md b/qa-contract/README.md index a1b4bef2057..f172577e32f 100644 --- a/qa-contract/README.md +++ b/qa-contract/README.md @@ -199,7 +199,7 @@ Leave `testCase` as OwnerOnly so the canonical catalog stays curated. ## Install & run The scripts use the recommended **`@dashevo/evo-sdk`** (js-evo-sdk) in trusted -mode (required so state-transition responses are proof-verified). Node ≥ 18.18. +mode (required so state-transition responses are proof-verified). Node ≥ 22. **Option A — standalone (published SDK):** diff --git a/qa-contract/package.json b/qa-contract/package.json index 38836429ddf..2c8618176f3 100644 --- a/qa-contract/package.json +++ b/qa-contract/package.json @@ -5,7 +5,7 @@ "type": "module", "description": "On-chain QA framework storage layer (data contract + scripts) for Dash Platform testnet.", "engines": { - "node": ">=18.18" + "node": ">=22" }, "scripts": { "register": "node src/register.mjs", diff --git a/scripts/setup-ai-agent-environment.sh b/scripts/setup-ai-agent-environment.sh index 069cf901882..83543a865e5 100755 --- a/scripts/setup-ai-agent-environment.sh +++ b/scripts/setup-ai-agent-environment.sh @@ -77,16 +77,16 @@ run_apt install -y --no-install-recommends \ xz-utils \ zip -log "Ensuring Node.js 20 LTS via NodeSource" +log "Ensuring Node.js 24 LTS via NodeSource" NODE_MAJOR=0 if command -v node >/dev/null 2>&1; then NODE_MAJOR=$(node --version | sed 's/v\([0-9]*\).*/\1/') fi -if (( NODE_MAJOR < 20 )); then +if (( NODE_MAJOR < 24 )); then if [[ -n "${SUDO}" ]]; then - curl -fsSL https://deb.nodesource.com/setup_20.x | ${SUDO} -E bash - + curl -fsSL https://deb.nodesource.com/setup_24.x | ${SUDO} -E bash - else - curl -fsSL https://deb.nodesource.com/setup_20.x | bash - + curl -fsSL https://deb.nodesource.com/setup_24.x | bash - fi run_apt install -y --no-install-recommends nodejs else diff --git a/yarn.lock b/yarn.lock index 884fa0a45c6..591f83ff3dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1785,7 +1785,7 @@ __metadata: "@dashevo/wasm-sdk": "workspace:*" "@types/chai": "npm:^4.3.11" "@types/mocha": "npm:^10.0.6" - "@types/node": "npm:^20.10.0" + "@types/node": "npm:^22.0.0" "@types/sinon": "npm:^9.0.4" "@types/sinon-chai": "npm:^3.2.4" assert: "npm:^2.0.0" @@ -2087,7 +2087,7 @@ __metadata: dependencies: "@types/chai": "npm:^4.3.11" "@types/mocha": "npm:^10.0.6" - "@types/node": "npm:^20.10.0" + "@types/node": "npm:^22.0.0" assert: "npm:^2.0.0" bs58: "npm:^4.0.1" buffer: "npm:^6.0.3" @@ -2129,7 +2129,7 @@ __metadata: "@dashevo/dashcore-lib": "npm:~0.22.0" "@dashevo/dpns-contract": "workspace:*" "@types/bs58": "npm:^4.0.1" - "@types/node": "npm:^20.10.0" + "@types/node": "npm:^22.0.0" "@yarnpkg/pnpify": "npm:^4.0.0-rc.42" ajv: "npm:^8.18.0" assert: "npm:^2.0.0" @@ -2180,7 +2180,7 @@ __metadata: dependencies: "@types/chai": "npm:^4.3.11" "@types/mocha": "npm:^10.0.6" - "@types/node": "npm:^20.10.0" + "@types/node": "npm:^22.0.0" assert: "npm:^2.0.0" buffer: "npm:^6.0.3" chai: "npm:^4.3.10" @@ -3937,12 +3937,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^20.10.0": - version: 20.19.30 - resolution: "@types/node@npm:20.19.30" +"@types/node@npm:^22.0.0": + version: 22.20.5 + resolution: "@types/node@npm:22.20.5" dependencies: undici-types: "npm:~6.21.0" - checksum: 10/4a25e5cbcdfc61b9bf45ebbbd199a6ce26efed34bd67c45c3472654a1cf988f7b58ec7c1574ffb5b83c6a772e7eaf1ca9de94fdafc3a36ae6efb852c1f0b6fb0 + checksum: 10/a223a73e00e948b751b00144523674ecc73e7a3f14f48a0f3a6fca40745ff548a8b0ff5baca090d45ae8b61481cc56c1158db4df312970baeb6fc1b81fc511a1 languageName: node linkType: hard @@ -7282,7 +7282,7 @@ __metadata: "@types/chai": "npm:^4.3.11" "@types/dirty-chai": "npm:^2.0.2" "@types/mocha": "npm:^10.0.6" - "@types/node": "npm:^20.10.0" + "@types/node": "npm:^22.0.0" "@types/sinon": "npm:^9.0.4" "@types/sinon-chai": "npm:^3.2.4" "@yarnpkg/pnpify": "npm:^4.0.0-rc.42" From 7c73e983b4c3cd9b5e2ead34bbe0360646b7b4d9 Mon Sep 17 00:00:00 2001 From: lklimek <842586+lklimek@users.noreply.github.com> Date: Wed, 7 Oct 2026 11:27:35 +0200 Subject: [PATCH 09/22] fix(platform-wallet): retain SPV client and track shutdown through cancellation (#5311) --- Cargo.lock | 1 + .../rs-platform-wallet-ffi/src/manager.rs | 8 +- packages/rs-platform-wallet-ffi/src/spv.rs | 10 +- packages/rs-platform-wallet/Cargo.toml | 1 + .../rs-platform-wallet/src/manager/mod.rs | 30 +- packages/rs-platform-wallet/src/spv/peers.rs | 46 +- .../rs-platform-wallet/src/spv/runtime.rs | 817 +++++++++++------- 7 files changed, 584 insertions(+), 329 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 90b2dc07644..e17e6ece13a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5612,6 +5612,7 @@ dependencies = [ "sha2 0.10.9", "simple-signer", "static_assertions", + "tempfile", "thiserror 1.0.69", "tokio", "tokio-util", diff --git a/packages/rs-platform-wallet-ffi/src/manager.rs b/packages/rs-platform-wallet-ffi/src/manager.rs index 20f0c421b20..731fb5c6a32 100644 --- a/packages/rs-platform-wallet-ffi/src/manager.rs +++ b/packages/rs-platform-wallet-ffi/src/manager.rs @@ -711,9 +711,11 @@ pub unsafe extern "C" fn platform_wallet_manager_get_wallet( /// Destroy a PlatformWalletManager handle. /// -/// Runs the full lifecycle shutdown (bounded: quiesce + join every -/// coordinator, SPV, the payment-hook tasks, and the event adapter) and -/// removes the handle. Always returns `Success` for a live handle. +/// Runs the full lifecycle shutdown (quiesce + join every coordinator, +/// SPV, the payment-hook tasks, and the event adapter) and removes the +/// handle. Shutdown waits are bounded except for SPV's waits for a +/// concurrent start/stop and in-flight client queries. Always returns +/// `Success` for a live handle. /// /// A non-clean shutdown — a worker that outlived its join budget — is /// logged, **not** surfaced as an error, because it is no longer a diff --git a/packages/rs-platform-wallet-ffi/src/spv.rs b/packages/rs-platform-wallet-ffi/src/spv.rs index dc722ff47b7..a40a8cb32c8 100644 --- a/packages/rs-platform-wallet-ffi/src/spv.rs +++ b/packages/rs-platform-wallet-ffi/src/spv.rs @@ -586,10 +586,9 @@ pub unsafe extern "C" fn platform_wallet_manager_spv_stop( // Under the registry guard on purpose — it mutates the manager's // runtime; see `platform_wallet_manager_spv_start`. // - // A stop that did not complete (the client stop ran over its budget, or - // the run loop survived the abort and was re-parked for a retry) is - // returned as an error: a start issued after it would find the parked - // run loop and spawn none. + // A stop that did not complete (teardown outlived the stop deadline and + // stays tracked for a retry, or it failed) is returned as an error: a + // start issued after it is refused until a later stop joins the teardown. let option = PLATFORM_WALLET_MANAGER_STORAGE.with_item(handle, |manager| { let spv = manager.spv_arc(); block_on_worker(async move { spv.stop().await }) @@ -651,6 +650,9 @@ pub unsafe extern "C" fn platform_wallet_manager_spv_rescan_filters( } /// Clear all persisted SPV storage (headers, filters, state). +/// +/// A running SPV client is stopped first and stays stopped; nothing is +/// cleared, and an error is returned, when that stop does not complete. #[no_mangle] pub unsafe extern "C" fn platform_wallet_manager_spv_clear_storage( handle: Handle, diff --git a/packages/rs-platform-wallet/Cargo.toml b/packages/rs-platform-wallet/Cargo.toml index d3da739bcba..5c6fe75d5d1 100644 --- a/packages/rs-platform-wallet/Cargo.toml +++ b/packages/rs-platform-wallet/Cargo.toml @@ -92,6 +92,7 @@ name = "shielded_chunk_timing_bench" required-features = ["shielded"] [dev-dependencies] +tempfile = "3.27.0" # In-process Signer for the manual testnet # verification example (`examples/dpns_marketplace_testnet.rs`). simple-signer = { path = "../simple-signer", features = ["state-transitions"] } diff --git a/packages/rs-platform-wallet/src/manager/mod.rs b/packages/rs-platform-wallet/src/manager/mod.rs index 51a57c32451..a442f8a48c4 100644 --- a/packages/rs-platform-wallet/src/manager/mod.rs +++ b/packages/rs-platform-wallet/src/manager/mod.rs @@ -61,7 +61,7 @@ pub enum WalletWorker { ShieldedSync, /// SPV runtime — the network event source feeding every persister- /// visible wallet event. Not a registry worker: `SpvRuntime::stop` - /// owns its (bounded, abort-escalating) join, and + /// owns its teardown task and bounds only the wait for it, and /// [`shutdown`](PlatformWalletManager::shutdown) folds the stop /// outcome into the report so a failed SPV stop can never hide /// behind a clean coordinator join. @@ -908,14 +908,18 @@ impl PlatformWalletManager

    { /// 4. The event adapter — the sink those stores feed into — drains /// LAST. /// - /// **Every phase is bounded.** SPV stop owns its own abort-escalating - /// join; the payment-hook drain is bounded by `PAYMENT_DRAIN_BUDGET`; - /// the coordinator drains run concurrently under - /// `COORDINATOR_DRAIN_BUDGET`; the registry join uses each worker's - /// join budget; the adapter join is bounded too (its live handle is - /// re-parked on timeout so a retry re-joins it). A wedged await - /// therefore surfaces as a non-clean report instead of hanging the - /// FFI's `destroy` forever. + /// **Every phase but SPV stop's lock waits is bounded.** SPV stop + /// bounds its wait for the teardown task, which it never aborts: a + /// teardown still running at the deadline is reported non-clean and + /// stays owned for a later stop to join. Before that deadline starts, + /// SPV stop waits without bound for a concurrent start or stop and + /// for client queries already in flight. The payment-hook drain is + /// bounded by `PAYMENT_DRAIN_BUDGET`; the coordinator drains run + /// concurrently under `COORDINATOR_DRAIN_BUDGET`; the registry join + /// uses each worker's join budget; the adapter join is bounded too + /// (its live handle is re-parked on timeout so a retry re-joins it). + /// Past those SPV lock waits, a wedged await therefore surfaces as a + /// non-clean report instead of hanging the FFI's `destroy` forever. /// /// Returns a [`ShutdownReport`] keyed by [`WalletWorker`] — including /// the non-registry workers [`WalletWorker::Spv`], @@ -927,10 +931,10 @@ impl PlatformWalletManager

    { /// /// [`WorkerStatus`]: dash_async::WorkerStatus pub async fn shutdown(&self) -> ShutdownReport { - // SPV first: it is the event source feeding everything below, and - // its `stop` owns a bounded, abort-escalating join of the run-loop - // task. Its outcome lands in the report — a failed stop must not - // hide behind a clean coordinator join. + // SPV first: it is the event source feeding everything below. Its + // `stop` bounds the wait for teardown but never aborts it, so a + // timeout means SPV work is still live. Its outcome lands in the + // report — a failed stop must not hide behind a clean coordinator join. let spv_status = match self.spv_manager.stop().await { Ok(()) => WorkerStatus::Ok, Err(error) => { diff --git a/packages/rs-platform-wallet/src/spv/peers.rs b/packages/rs-platform-wallet/src/spv/peers.rs index 6684f5858ee..f60ce99bcf8 100644 --- a/packages/rs-platform-wallet/src/spv/peers.rs +++ b/packages/rs-platform-wallet/src/spv/peers.rs @@ -9,7 +9,7 @@ use std::collections::HashMap; use std::net::SocketAddr; -use std::sync::Mutex; +use std::sync::{Mutex, MutexGuard, PoisonError}; use dash_spv::network::NetworkEvent; use dash_spv::EventHandler; @@ -49,28 +49,30 @@ pub(crate) struct PeerTracker { } impl PeerTracker { - /// Addresses from the latest `PeersUpdated` event. - pub(crate) fn snapshot(&self) -> Vec { + /// Lock the snapshot, recovering from poisoning: it is only cloned, + /// cleared or replaced whole, so a panicking holder cannot tear it. + fn connected(&self) -> MutexGuard<'_, Vec> { self.connected .lock() - .expect("peer tracker mutex poisoned") - .clone() + .unwrap_or_else(PoisonError::into_inner) + } + + /// Addresses from the latest `PeersUpdated` event. + pub(crate) fn snapshot(&self) -> Vec { + self.connected().clone() } /// Forget all peers. Called when the SPV client stops so a stale /// snapshot can't outlive the connections it describes. pub(crate) fn clear(&self) { - self.connected - .lock() - .expect("peer tracker mutex poisoned") - .clear(); + self.connected().clear(); } } impl EventHandler for PeerTracker { fn on_network_event(&self, event: &NetworkEvent) { if let NetworkEvent::PeersUpdated { addresses, .. } = event { - *self.connected.lock().expect("peer tracker mutex poisoned") = addresses.clone(); + *self.connected() = addresses.clone(); } } } @@ -232,4 +234,28 @@ mod tests { tracker.clear(); assert!(tracker.snapshot().is_empty()); } + + /// The tracker runs inside dash-spv's event dispatch, so a poisoned + /// mutex must not turn every later peer event into a panic there. + #[test] + fn should_keep_tracking_peers_after_the_mutex_is_poisoned() { + let tracker = std::sync::Arc::new(PeerTracker::default()); + let poisoner = std::sync::Arc::clone(&tracker); + let _ = std::thread::spawn(move || { + let _guard = poisoner.connected.lock().unwrap(); + panic!("mock panic while holding the peer tracker mutex"); + }) + .join(); + assert!(tracker.connected.is_poisoned()); + + tracker.on_network_event(&NetworkEvent::PeersUpdated { + connected_count: 1, + addresses: vec![socket([1, 1, 1, 1], 9999)], + best_height: Some(100), + }); + assert_eq!(tracker.snapshot(), vec![socket([1, 1, 1, 1], 9999)]); + + tracker.clear(); + assert!(tracker.snapshot().is_empty()); + } } diff --git a/packages/rs-platform-wallet/src/spv/runtime.rs b/packages/rs-platform-wallet/src/spv/runtime.rs index 6e8204682c8..bb58de71197 100644 --- a/packages/rs-platform-wallet/src/spv/runtime.rs +++ b/packages/rs-platform-wallet/src/spv/runtime.rs @@ -1,11 +1,11 @@ //! SPV client runtime — manages the DashSpvClient lifecycle. -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::{Arc, Mutex}; +use std::future::Future; +use std::sync::{Arc, Mutex, MutexGuard, PoisonError}; use std::time::Duration; use tokio::sync::RwLock; -use tokio::task::JoinHandle; +use tokio::task::{JoinError, JoinHandle}; use dashcore::sml::llmq_type::LLMQType; use dashcore::sml::masternode_list::MasternodeList; @@ -28,78 +28,18 @@ use crate::wallet::platform_wallet::PlatformWalletInfo; type SpvClient = DashSpvClient, PeerNetworkManager, DiskStorageManager>; -/// Graceful join budget for the SPV run loop before escalating to `abort`. +/// Maximum wait per stop call; the owned teardown continues after this deadline. const SPV_STOP_TIMEOUT: Duration = Duration::from_secs(15); -/// Budget for `DashSpvClient::stop()` itself. -/// -/// dash-spv's stop joins its internal monitors, and those monitors dispatch -/// host event callbacks **synchronously** — a callback blocked in host code -/// (an FFI persister `store`, say) makes the stop unbounded. Since -/// [`SpvRuntime::stop`] sits on the path the FFI `destroy` must return -/// through, an unbounded stop hangs teardown outright instead of surfacing -/// `ErrorShutdownIncomplete`. On timeout the partially-stopped client is -/// dropped and the stop is reported as an error, so the caller treats SPV -/// as non-clean. -const SPV_CLIENT_STOP_BUDGET: Duration = Duration::from_secs(15); - -/// Post-`abort` confirmation grace for the SPV run loop. -/// -/// An abort only lands at the task's next await point, so a task parked in -/// synchronous host-callback code cannot be interrupted at all. Without this -/// bound the post-abort `handle.await` waits forever — the same hang the -/// graceful timeout above was meant to escape. -const SPV_ABORT_GRACE: Duration = Duration::from_secs(2); - -/// How often [`SpvRuntime::wait_until_ready`] re-checks for a started client -/// with connected peers. +/// How often readiness is rechecked for a client with connected peers. const SPV_READINESS_POLL_INTERVAL: Duration = Duration::from_millis(250); -/// Join a stopped SPV runner, escalating to cancellation after `timeout`. -/// -/// Returns `None` once Tokio has confirmed the task terminated. Returns -/// `Some(handle)` when it is *still live* after the post-abort grace: the -/// caller must re-park that handle (so a teardown retry re-joins it rather -/// than silently detaching a callback-capable task) and report SPV as -/// non-clean. -#[must_use = "a returned handle is a still-live task that must be re-parked and reported non-clean"] -async fn join_spv_task(handle: JoinHandle<()>, timeout: Duration) -> Option> { - join_spv_task_within(handle, timeout, SPV_ABORT_GRACE).await -} - -/// [`join_spv_task`] with an explicit post-abort grace, so tests can drive -/// the survived-the-abort path without waiting out [`SPV_ABORT_GRACE`]. -#[must_use = "a returned handle is a still-live task that must be re-parked and reported non-clean"] -async fn join_spv_task_within( - mut handle: JoinHandle<()>, - timeout: Duration, - abort_grace: Duration, -) -> Option> { - match tokio::time::timeout(timeout, &mut handle).await { - Ok(Ok(())) => None, - Ok(Err(error)) => { - tracing::warn!(?error, "SPV background run loop join error"); - None - } - Err(_) => { - tracing::warn!("SPV stop: background run loop did not unwind in time; aborting it"); - handle.abort(); - match tokio::time::timeout(abort_grace, &mut handle).await { - Ok(Err(error)) if !error.is_cancelled() => { - tracing::warn!(?error, "SPV background run loop abort join error"); - None - } - Ok(_) => None, - Err(_) => { - tracing::warn!( - "SPV stop: background run loop survived abort for {abort_grace:?}; \ - keeping the handle for a teardown retry" - ); - Some(handle) - } - } - } - } +#[derive(Default)] +enum Shutdown { + #[default] + Idle, + Running(JoinHandle>), + Failed(String), } /// SPV client runtime — owns the `DashSpvClient` and drives sync. @@ -112,33 +52,12 @@ pub struct SpvRuntime { client: RwLock>, last_config: RwLock>, task: Mutex>>, - /// Stops currently running. A stop takes the run-loop handle out of - /// `task` before joining it, so an empty `task` does not mean the old run - /// loop has exited; `start` refuses while this is non-zero. - stops_in_progress: AtomicUsize, - /// Serializes [`stop`](Self::stop): a stop running alongside another - /// would find no client and an empty `task` while the first is still - /// joining the run loop, and report success early. - stop_serial: tokio::sync::Mutex<()>, + /// Teardown state. Held across client construction, run-loop spawning and + /// each stop or storage clear, so none of them interleave. + shutdown: tokio::sync::Mutex, peer_tracker: Arc, } -/// Counts one running [`SpvRuntime::stop`] for as long as it lives. -struct StopInProgress<'a>(&'a AtomicUsize); - -impl<'a> StopInProgress<'a> { - fn begin(count: &'a AtomicUsize) -> Self { - count.fetch_add(1, Ordering::SeqCst); - Self(count) - } -} - -impl Drop for StopInProgress<'_> { - fn drop(&mut self) { - self.0.fetch_sub(1, Ordering::SeqCst); - } -} - /// Classify a failure from the SPV acceptance-check path /// ([`SpvRuntime::broadcast_transaction_and_wait`]). /// @@ -175,21 +94,26 @@ impl SpvRuntime { client: RwLock::new(None), last_config: RwLock::new(None), task: Mutex::new(None), - stops_in_progress: AtomicUsize::new(0), - stop_serial: tokio::sync::Mutex::new(()), + shutdown: tokio::sync::Mutex::new(Shutdown::Idle), peer_tracker: Arc::new(PeerTracker::default()), } } /// Start SPV sync. pub async fn start(&self, config: ClientConfig) -> Result<(), PlatformWalletError> { + if self.client.read().await.is_some() { + return Err(PlatformWalletError::SpvAlreadyRunning); + } + // Fail fast instead of queueing behind a stop that is still joining. + self.ensure_no_live_run_loop()?; + let shutdown = self.shutdown.lock().await; { let running = self.client.read().await; if running.is_some() { return Err(PlatformWalletError::SpvAlreadyRunning); } } - self.ensure_no_live_run_loop()?; + self.ensure_joined(&shutdown)?; let network_manager = PeerNetworkManager::new(&config) .await @@ -225,36 +149,38 @@ impl SpvRuntime { Ok(()) } - /// Refuse a start while the run loop of an earlier stop may still be live. - /// - /// A stop that is still joining the run loop has already taken it out of - /// `task`; when that loop exits, `run` clears the client, which would be - /// the one started now. [`stop`](Self::stop) also re-parks a run loop that - /// survived its abort, and [`spawn_run_loop`](Self::spawn_run_loop) keeps a - /// parked loop instead of spawning one, so a client started over it would - /// never sync. A parked loop that has exited since is dropped and the - /// start goes ahead. + /// Refuse restart until all previous startup and shutdown work is joined. fn ensure_no_live_run_loop(&self) -> Result<(), PlatformWalletError> { - // Lock `task` before reading the counter. A stop counts itself before - // it takes the handle under this lock, so a start either still sees - // the handle or already sees the stop. - let mut task = self.task.lock().expect("spv task mutex poisoned"); - if self.stops_in_progress.load(Ordering::SeqCst) > 0 { + let shutdown = self.shutdown.try_lock().map_err(|_| { + PlatformWalletError::SpvError( + "an SPV start or stop is still in progress; wait for it before starting SPV" + .to_string(), + ) + })?; + self.ensure_joined(&shutdown) + } + + /// Check that nothing is left to join. Takes the locked `shutdown` state: + /// `task` only changes under that lock, so the answer cannot go stale. + fn ensure_joined(&self, shutdown: &Shutdown) -> Result<(), PlatformWalletError> { + if !matches!(shutdown, Shutdown::Idle) { return Err(PlatformWalletError::SpvError( - "an SPV stop is still in progress; wait for it before starting SPV".to_string(), + "SPV teardown is incomplete; stop SPV again before starting it".to_string(), )); } - match task.as_ref() { - Some(handle) if !handle.is_finished() => Err(PlatformWalletError::SpvError( - "the previous SPV run loop has not exited yet; stop SPV again before starting it" - .to_string(), - )), - Some(_) => { - *task = None; - Ok(()) - } - None => Ok(()), + let task = self.task_slot(); + if task.is_some() { + return Err(PlatformWalletError::SpvError( + "SPV startup has not been joined; stop SPV before restarting it".to_string(), + )); } + Ok(()) + } + + /// Lock the startup-task slot, recovering from poisoning: the slot is only + /// read, taken or replaced whole, so a panicking holder cannot tear it. + fn task_slot(&self) -> MutexGuard<'_, Option>> { + self.task.lock().unwrap_or_else(PoisonError::into_inner) } /// Check whether the SPV client has been started. @@ -359,8 +285,7 @@ impl SpvRuntime { Ok(*quorum.quorum_entry.quorum_public_key.as_ref()) } - /// Drive the sync loop of an already-[`start`]ed client until [`stop`] - /// is called + /// Start upstream's background sync while retaining its client for queries and stop. async fn run(&self) -> Result<(), PlatformWalletError> { let client_guard = self.client.read().await; let client = client_guard @@ -376,111 +301,111 @@ impl SpvRuntime { .await .map_err(|e| PlatformWalletError::SpvError(e.to_string())); - let mut client = self.client.write().await; - let _ = client.take(); - self.peer_tracker.clear(); + self.finish_startup(result).await + } + async fn finish_startup( + &self, + result: Result<(), PlatformWalletError>, + ) -> Result<(), PlatformWalletError> { + if result.is_err() { + let client = self.client.write().await.take(); + if let Some(client) = client { + client.stop().await; + } + self.peer_tracker.clear(); + } result } - /// Stop SPV sync gracefully. Unlocks the data dir safely. - /// - /// **Every phase after taking the client is bounded** — `stop` runs on the - /// path [`PlatformWalletManager::shutdown`](crate::manager::PlatformWalletManager::shutdown) - /// and therefore the FFI's `destroy` must return through, so a wedged - /// host callback has to surface as an error rather than hang teardown: - /// the client stop is capped at [`SPV_CLIENT_STOP_BUDGET`], the run-loop - /// join at [`SPV_STOP_TIMEOUT`] with an [`SPV_ABORT_GRACE`] post-abort - /// confirmation. A run loop that outlives all of that is **re-parked**, - /// not detached, so a teardown retry re-joins it — and the error return - /// keeps it out of a clean shutdown verdict. + /// Stop SPV sync, waiting up to 15 seconds for startup and teardown. /// - /// Taking the client is not bounded: it waits for the client's read - /// guards, and [`broadcast_transaction_and_wait`](Self::broadcast_transaction_and_wait) - /// holds one for its whole acceptance wait (the caller's timeout). That - /// keeps a broadcast on a live client and the data directory free once - /// `stop` returns, at the cost of a stop waiting for the broadcast. - /// - /// Idempotent. Stops run one at a time: a second call waits for the one - /// in flight, then finds no client and re-joins whatever that one - /// re-parked, so it never reports success while the old run loop is live. + /// A timeout or cancelled caller leaves teardown tracked for the next stop. + /// Restart stays blocked until a stop confirms completion. Client queries + /// already in flight finish before teardown starts; this wait is unbounded. pub async fn stop(&self) -> Result<(), PlatformWalletError> { - let _stopping = StopInProgress::begin(&self.stops_in_progress); - let _serial = self.stop_serial.lock().await; + self.stop_with(|client| async move { client.stop().await }) + .await + } - let taken = { - let mut client = self.client.write().await; - client.take() - }; + async fn stop_with(&self, stop_client: F) -> Result<(), PlatformWalletError> + where + F: FnOnce(SpvClient) -> Fut + Send + 'static, + Fut: Future + Send + 'static, + { + let mut shutdown = self.shutdown.lock().await; + self.stop_locked(&mut shutdown, stop_client).await + } - let stop_result = match taken { - Some(c) => match tokio::time::timeout(SPV_CLIENT_STOP_BUDGET, c.stop()).await { - Ok(()) => Ok(()), - Err(_) => { - // The client is dropped with the timed-out future. The - // data-dir lock may outlive this call, which is strictly - // better than never returning from `destroy`. - tracing::warn!( - "SPV client stop did not complete within {:?}; abandoning it", - SPV_CLIENT_STOP_BUDGET - ); - Err(PlatformWalletError::SpvError(format!( - "SPV client stop did not complete within {SPV_CLIENT_STOP_BUDGET:?}" - ))) + /// Run or rejoin teardown. The caller holds the `shutdown` lock. + async fn stop_locked( + &self, + shutdown: &mut Shutdown, + stop_client: F, + ) -> Result<(), PlatformWalletError> + where + F: FnOnce(SpvClient) -> Fut + Send + 'static, + Fut: Future + Send + 'static, + { + if matches!(*shutdown, Shutdown::Idle) { + let client = self.client.write().await.take(); + let startup = self.task_slot().take(); + let peers = Arc::clone(&self.peer_tracker); + // Never cancel upstream run/stop: each can own callback-capable tasks + // that cancellation would detach before their handles are restored. + *shutdown = Shutdown::Running(tokio::spawn(async move { + let result = match startup { + Some(task) => task.await, + None => Ok(()), + }; + if let Some(client) = client { + stop_client(client).await; } - }, - None => Ok(()), - }; - self.peer_tracker.clear(); - - let handle = self.task.lock().expect("spv task mutex poisoned").take(); - let join_result = match handle { - None => Ok(()), - Some(handle) => match join_spv_task(handle, SPV_STOP_TIMEOUT).await { - None => Ok(()), - Some(live) => { - // Re-park rather than drop: dropping a `JoinHandle` - // detaches the task, and this one can still reach host - // callbacks. Keeping it lets a teardown retry re-join. - *self.task.lock().expect("spv task mutex poisoned") = Some(live); - Err(PlatformWalletError::SpvError( - "SPV background run loop did not terminate after abort; \ - it is still tracked for a retry" - .to_string(), + peers.clear(); + result + })); + } + + if let Shutdown::Running(task) = shutdown { + match tokio::time::timeout(SPV_STOP_TIMEOUT, task).await { + Ok(Ok(Ok(()))) => *shutdown = Shutdown::Idle, + Ok(result) => { + *shutdown = Shutdown::Failed(format!("SPV teardown task failed: {result:?}")) + } + Err(_) => { + return Err(PlatformWalletError::SpvError( + "SPV teardown timed out; it is still tracked for a retry".to_string(), )) } - }, - }; - - // A failed client stop is the more informative diagnosis, so it wins; - // either one makes the caller's shutdown verdict non-clean. - stop_result.and(join_result) + } + } + match shutdown { + Shutdown::Failed(error) => Err(PlatformWalletError::SpvError(error.clone())), + _ => Ok(()), + } } - /// Spawn the sync loop of an already-[`start`]ed client on the current - /// tokio runtime and return immediately. + /// Start upstream background sync on the current Tokio runtime. /// - /// Call [`stop`] to stop it + /// Skipped with a warning while a start or stop is in progress, or while + /// earlier startup or teardown is still unjoined. Call [`Self::stop`] to + /// stop it. pub fn spawn_run_loop(self: &Arc) { - { - let existing = self.task.lock().expect("spv task mutex poisoned"); - if existing.is_some() { - tracing::warn!( - "spawn_in_background called while a task is already running; ignoring" - ); - return; - } + let Ok(shutdown) = self.shutdown.try_lock() else { + tracing::warn!("SPV background sync not spawned: an SPV start or stop is in progress"); + return; + }; + if let Err(error) = self.ensure_joined(&shutdown) { + tracing::warn!(%error, "SPV background sync not spawned: earlier SPV work is unjoined"); + return; } - let this = Arc::clone(self); - - let handle = tokio::spawn(async move { + let mut task = self.task_slot(); + *task = Some(tokio::spawn(async move { if let Err(e) = this.run().await { - tracing::warn!("SpvRuntime background run loop exited with error: {}", e); + tracing::warn!("SpvRuntime background startup failed: {}", e); } - }); - - *self.task.lock().expect("spv task mutex poisoned") = Some(handle); + })); } /// The peers the SPV client is currently connected to, each classified @@ -689,20 +614,22 @@ impl SpvRuntime { /// Clear all persisted SPV storage (headers, filters, state). /// - /// If the SPVClient is running it will be stopped and the - /// storage will be cleaned. If it is not running a tmp - /// Storage Manager built from the cached config will be used. + /// A running client is stopped first, as by [`Self::stop`], and stays + /// stopped: start SPV again to resync. Nothing is cleared unless that + /// stop completes. pub async fn clear_storage(&self) -> Result<(), PlatformWalletError> { - // Fast path: a live client holds the storage lock; clear through it. - { - let client_guard = self.client.read().await; - if let Some(client) = client_guard.as_ref() { - return client - .clear_storage() - .await - .map_err(|e| PlatformWalletError::SpvError(e.to_string())); - } - } + self.clear_storage_with(|client| async move { client.stop().await }) + .await + } + + async fn clear_storage_with(&self, stop_client: F) -> Result<(), PlatformWalletError> + where + F: FnOnce(SpvClient) -> Fut + Send + 'static, + Fut: Future + Send + 'static, + { + // Held to the end so no start can reopen the storage mid-clear. + let mut shutdown = self.shutdown.lock().await; + self.stop_locked(&mut shutdown, stop_client).await?; let config = self.last_config.read().await.clone().ok_or_else(|| { PlatformWalletError::SpvError( @@ -866,72 +793,6 @@ mod masternodes_by_voting_key_tests { } } -#[cfg(test)] -mod shutdown_tests { - use super::*; - use std::sync::atomic::{AtomicBool, Ordering}; - - struct DropFlag(Arc); - - impl Drop for DropFlag { - fn drop(&mut self) { - self.0.store(true, Ordering::SeqCst); - } - } - - #[tokio::test(start_paused = true)] - async fn timed_out_spv_task_is_aborted_and_joined_before_return() { - let dropped = Arc::new(AtomicBool::new(false)); - let dropped_in_task = Arc::clone(&dropped); - let (started_tx, started_rx) = tokio::sync::oneshot::channel(); - let handle = tokio::spawn(async move { - let _flag = DropFlag(dropped_in_task); - let _ = started_tx.send(()); - std::future::pending::<()>().await; - }); - started_rx.await.expect("SPV task should start"); - - assert!( - join_spv_task(handle, SPV_STOP_TIMEOUT).await.is_none(), - "an abortable task must be confirmed terminated, not returned as live" - ); - - assert!( - dropped.load(Ordering::SeqCst), - "abort must be joined so task-owned callback state is dropped" - ); - } - - /// A run loop parked in **synchronous** code cannot be interrupted by - /// `abort` — the cancellation only lands at the next await point, which - /// never comes. The post-abort confirmation must therefore be bounded - /// and hand the still-live handle back, so `stop` can re-park it (a - /// dropped `JoinHandle` detaches the task, and this one can still reach - /// host callbacks) and report SPV non-clean. - /// - /// Without the post-abort deadline this test hangs: that is exactly the - /// hang that would reach the FFI's `destroy`. - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] - async fn spv_task_surviving_abort_is_returned_for_reparking() { - let (started_tx, started_rx) = tokio::sync::oneshot::channel(); - let handle = tokio::spawn(async move { - let _ = started_tx.send(()); - // Stands in for a monitor blocked inside a synchronous host - // callback: no await point for the abort to land on. - std::thread::sleep(Duration::from_millis(500)); - }); - started_rx.await.expect("SPV task should start"); - - let live = - join_spv_task_within(handle, Duration::from_millis(10), Duration::from_millis(20)) - .await - .expect("an un-abortable task must be handed back, never silently detached"); - - // Cleanup: the blocking section does end eventually. - let _ = live.await; - } -} - impl std::fmt::Debug for SpvRuntime { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("SpvRuntime") @@ -964,9 +825,18 @@ mod tests { SpvRuntime::new(wallet_manager, Arc::new(PlatformEventManager::new(vec![]))) } - /// A stop that could not join its run loop leaves it parked. A start - /// issued while that loop is live must fail instead of starting a client - /// that `spawn_run_loop` would give no run loop. + /// Poll until `ready` holds, failing the test instead of hanging it. + async fn wait_until(what: &str, ready: impl Fn() -> bool) { + tokio::time::timeout(Duration::from_secs(5), async { + while !ready() { + tokio::task::yield_now().await; + } + }) + .await + .unwrap_or_else(|_| panic!("timed out waiting for {what}")); + } + + /// An outstanding startup must remain owned until stop joins it. #[tokio::test] async fn should_refuse_a_start_while_a_parked_run_loop_is_live() { let runtime = unstarted_runtime(); @@ -994,9 +864,7 @@ mod tests { let _ = release_tx.send(()); } - /// A stop that is joining the run loop has taken it out of `task`. A start - /// in that window must fail: when the old loop exits, `run` clears the - /// client, which would be the newly started one. + /// Startup remains owned by teardown while its original slot is empty. #[tokio::test] async fn should_refuse_a_start_while_a_stop_is_joining_the_run_loop() { let runtime = Arc::new(unstarted_runtime()); @@ -1098,10 +966,9 @@ mod tests { .expect("the second stop finds nothing left to stop"); } - /// Once the parked run loop has exited, the next start drops it and - /// goes ahead. + /// Completion alone cannot prove success: stop must check the join result. #[tokio::test] - async fn should_drop_a_parked_run_loop_that_has_exited() { + async fn should_join_a_completed_startup_before_allowing_restart() { let runtime = unstarted_runtime(); let exited = tokio::spawn(async {}); tokio::time::timeout(Duration::from_secs(5), async { @@ -1113,6 +980,8 @@ mod tests { .expect("an empty task should finish promptly"); *runtime.task.lock().expect("spv task mutex poisoned") = Some(exited); + assert!(runtime.ensure_no_live_run_loop().is_err()); + runtime.stop().await.unwrap(); assert!(runtime.ensure_no_live_run_loop().is_ok()); assert!( runtime @@ -1124,6 +993,356 @@ mod tests { ); } + #[tokio::test] + async fn should_retain_client_after_successful_startup() { + let (runtime, _storage) = offline_runtime().await; + runtime.finish_startup(Ok(())).await.unwrap(); + *runtime.task.lock().unwrap() = Some(tokio::spawn(async {})); + assert!(matches!( + runtime.start(ClientConfig::default()).await, + Err(PlatformWalletError::SpvAlreadyRunning) + )); + let retained = runtime.is_started(); + let progress = runtime.sync_progress().await; + let broadcast = runtime + .broadcast_transaction_and_wait(&dummy_tx(), None) + .await; + assert!( + matches!(broadcast, Err(BroadcastError::Rejected { reason }) if reason.contains("no connected peers")), + "broadcast must reach the retained client" + ); + runtime.stop().await.unwrap(); + assert!( + retained, + "successful startup must retain access to the background client" + ); + assert!( + progress.is_some(), + "queries must remain available after startup" + ); + } + + #[tokio::test] + async fn should_track_shutdown_after_stop_caller_is_cancelled() { + let runtime = Arc::new(unstarted_runtime()); + let (release, pending) = tokio::sync::oneshot::channel(); + *runtime.task.lock().unwrap() = Some(tokio::spawn(async move { + let _ = pending.await; + })); + let stopping = { + let runtime = Arc::clone(&runtime); + tokio::spawn(async move { runtime.stop().await }) + }; + wait_until("stop to take the startup task", || { + runtime.task.lock().unwrap().is_none() + }) + .await; + stopping.abort(); + let _ = stopping.await; + let blocked = runtime.ensure_no_live_run_loop().is_err(); + let retry = tokio::time::timeout(Duration::from_millis(10), runtime.stop()).await; + let _ = release.send(()); + runtime.stop().await.unwrap(); + assert!( + blocked, + "cancelling stop must not permit a restart over live work" + ); + assert!( + retry.is_err(), + "retry must wait for the original work to finish" + ); + } + + async fn offline_runtime() -> (Arc, tempfile::TempDir) { + let storage = tempfile::tempdir().unwrap(); + let runtime = Arc::new(unstarted_runtime()); + runtime + .start( + ClientConfig::testnet() + .with_storage_path(storage.path()) + .with_restrict_to_configured_peers(true), + ) + .await + .unwrap(); + (runtime, storage) + } + + #[tokio::test(start_paused = true)] + async fn should_keep_timed_out_upstream_stop_alive_until_retry_joins_it() { + let (runtime, storage) = offline_runtime().await; + let (release, pending) = tokio::sync::oneshot::channel(); + let stopped = Arc::new(std::sync::atomic::AtomicBool::new(false)); + let completed = Arc::clone(&stopped); + let result = runtime + .stop_with(move |client| async move { + let _ = pending.await; + client.stop().await; + completed.store(true, std::sync::atomic::Ordering::SeqCst); + }) + .await; + assert!(result.is_err(), "an incomplete upstream stop must time out"); + assert!(runtime.start(ClientConfig::default()).await.is_err()); + assert!( + runtime.stop().await.is_err(), + "retry must not report clean while upstream stop is pending" + ); + release + .send(()) + .expect("timed out stop must still own its future"); + runtime.stop().await.unwrap(); + assert!(stopped.load(std::sync::atomic::Ordering::SeqCst)); + runtime + .start( + ClientConfig::testnet() + .with_storage_path(storage.path()) + .with_restrict_to_configured_peers(true), + ) + .await + .expect("restart after confirmed teardown"); + runtime.stop().await.unwrap(); + runtime.stop().await.unwrap(); + } + + #[tokio::test] + async fn should_keep_upstream_stop_alive_when_its_caller_is_cancelled() { + let (runtime, _storage) = offline_runtime().await; + let (entered, started) = tokio::sync::oneshot::channel(); + let (release, pending) = tokio::sync::oneshot::channel(); + let stopping = { + let runtime = Arc::clone(&runtime); + tokio::spawn(async move { + runtime + .stop_with(move |client| async move { + entered.send(()).unwrap(); + let _ = pending.await; + client.stop().await; + }) + .await + }) + }; + started.await.unwrap(); + stopping.abort(); + let _ = stopping.await; + assert!(runtime.start(ClientConfig::default()).await.is_err()); + assert!( + tokio::time::timeout(Duration::from_millis(10), runtime.stop()) + .await + .is_err() + ); + release + .send(()) + .expect("cancelled caller must leave upstream stop owned"); + runtime.stop().await.unwrap(); + } + + /// Clearing stops the client, so the runtime must not keep reporting it + /// as started or holding its startup task. + #[tokio::test] + async fn should_leave_spv_stopped_and_restartable_after_clearing_a_running_client() { + let (runtime, storage) = offline_runtime().await; + runtime.finish_startup(Ok(())).await.unwrap(); + *runtime.task.lock().unwrap() = Some(tokio::spawn(async {})); + + runtime.clear_storage().await.unwrap(); + + assert!( + !runtime.is_started(), + "a cleared client is stopped and must not be reported as started" + ); + runtime + .start( + ClientConfig::testnet() + .with_storage_path(storage.path()) + .with_restrict_to_configured_peers(true), + ) + .await + .expect("restart after clearing a running client"); + runtime.stop().await.unwrap(); + } + + #[tokio::test] + async fn should_keep_teardown_tracked_when_a_clear_storage_caller_is_cancelled() { + let (runtime, storage) = offline_runtime().await; + let (entered, started) = tokio::sync::oneshot::channel(); + let (release, pending) = tokio::sync::oneshot::channel(); + let clearing = { + let runtime = Arc::clone(&runtime); + tokio::spawn(async move { + runtime + .clear_storage_with(move |client| async move { + entered.send(()).unwrap(); + let _ = pending.await; + client.stop().await; + }) + .await + }) + }; + started.await.unwrap(); + clearing.abort(); + let _ = clearing.await; + assert!(runtime.start(ClientConfig::default()).await.is_err()); + assert!( + tokio::time::timeout(Duration::from_millis(10), runtime.clear_storage()) + .await + .is_err(), + "a retried clear must wait for the teardown it left running" + ); + release + .send(()) + .expect("cancelled caller must leave upstream stop owned"); + runtime.clear_storage().await.unwrap(); + runtime + .start( + ClientConfig::testnet() + .with_storage_path(storage.path()) + .with_restrict_to_configured_peers(true), + ) + .await + .expect("restart after the retried clear joins teardown"); + runtime.stop().await.unwrap(); + } + + #[tokio::test] + async fn should_wait_for_startup_before_stopping_its_client() { + let (runtime, _storage) = offline_runtime().await; + let (release, pending) = tokio::sync::oneshot::channel(); + let startup_done = Arc::new(std::sync::atomic::AtomicBool::new(false)); + let completed = Arc::clone(&startup_done); + *runtime.task.lock().unwrap() = Some(tokio::spawn(async move { + let _ = pending.await; + completed.store(true, std::sync::atomic::Ordering::SeqCst); + })); + let stopping = { + let runtime = Arc::clone(&runtime); + tokio::spawn(async move { + runtime + .stop_with(move |client| async move { + assert!(startup_done.load(std::sync::atomic::Ordering::SeqCst)); + client.stop().await; + }) + .await + }) + }; + wait_until("stop to take the startup task", || { + runtime.task.lock().unwrap().is_none() + }) + .await; + assert!(!stopping.is_finished()); + runtime.spawn_run_loop(); + assert!( + runtime.task.lock().unwrap().is_none(), + "cannot spawn during teardown" + ); + release.send(()).unwrap(); + stopping.await.unwrap().unwrap(); + } + + /// Failed-startup cleanup runs inside the startup task, whose finished + /// handle stays owned until stop joins it. + #[tokio::test] + async fn should_clean_up_failed_startup_and_allow_restart_after_stop() { + let (runtime, storage) = offline_runtime().await; + let config = || { + ClientConfig::testnet() + .with_storage_path(storage.path()) + .with_restrict_to_configured_peers(true) + }; + let startup = { + let runtime = Arc::clone(&runtime); + tokio::spawn(async move { + let failure = PlatformWalletError::SpvError("mock startup failure".into()); + assert!(runtime.finish_startup(Err(failure)).await.is_err()); + }) + }; + *runtime.task.lock().unwrap() = Some(startup); + wait_until("the failed startup to finish its cleanup", || { + runtime + .task + .lock() + .unwrap() + .as_ref() + .is_some_and(|task| task.is_finished()) + }) + .await; + + assert!(!runtime.is_started(), "failed startup must drop its client"); + assert!( + runtime.start(config()).await.is_err(), + "an unjoined startup must block restart" + ); + runtime.stop().await.unwrap(); + runtime + .start(config()) + .await + .expect("restart after stop joins the failed startup"); + runtime.stop().await.unwrap(); + } + + #[tokio::test] + async fn should_never_report_clean_after_a_startup_task_panics() { + let runtime = unstarted_runtime(); + *runtime.task.lock().unwrap() = Some(tokio::spawn(async { panic!("mock startup panic") })); + assert!(runtime.stop().await.is_err()); + assert!( + runtime.stop().await.is_err(), + "a join failure cannot be forgotten by a retry" + ); + assert!(runtime.start(ClientConfig::default()).await.is_err()); + } + + #[tokio::test] + async fn should_preserve_ownership_when_cancelled_while_waiting_for_client_access() { + let (runtime, _storage) = offline_runtime().await; + let guard = runtime.client.read().await; + let stopping = { + let runtime = Arc::clone(&runtime); + tokio::spawn(async move { runtime.stop().await }) + }; + // Stop takes `shutdown` and then parks on the client write lock. + wait_until("stop to take the shutdown lock", || { + runtime.shutdown.try_lock().is_err() + }) + .await; + stopping.abort(); + let _ = stopping.await; + assert!( + guard.is_some(), + "a cancelled lock wait must not take the client" + ); + drop(guard); + runtime.stop().await.unwrap(); + assert!(!runtime.is_started()); + } + + #[tokio::test] + async fn should_keep_teardown_panics_non_clean_on_every_retry() { + let (runtime, _storage) = offline_runtime().await; + assert!(runtime + .stop_with(|_client| async { panic!("mock teardown panic") }) + .await + .is_err()); + assert!(runtime.stop().await.is_err()); + assert!(runtime.start(ClientConfig::default()).await.is_err()); + } + + /// A panic under the `task` mutex must not make every later lifecycle + /// call panic too: teardown has to stay reachable. + #[tokio::test] + async fn should_keep_lifecycle_usable_after_the_task_mutex_is_poisoned() { + let runtime = Arc::new(unstarted_runtime()); + let poisoner = Arc::clone(&runtime); + let _ = std::thread::spawn(move || { + let _guard = poisoner.task.lock().unwrap(); + panic!("mock panic while holding the task mutex"); + }) + .join(); + assert!(runtime.task.is_poisoned()); + + runtime.spawn_run_loop(); + runtime.stop().await.unwrap(); + assert!(runtime.ensure_no_live_run_loop().is_ok()); + } + /// A minimal valid transaction — the unstarted-client arm never /// inspects it. fn dummy_tx() -> dashcore::Transaction { From 4045c13fda2895f4e728727e40a73e04ef37ce77 Mon Sep 17 00:00:00 2001 From: lklimek <842586+lklimek@users.noreply.github.com> Date: Wed, 7 Oct 2026 17:27:50 +0200 Subject: [PATCH 10/22] refactor: share rust-dashcore BLS backend through dpp::bls (#5320) Co-authored-by: Codex --- CHANGELOG.md | 11 + Cargo.lock | 284 +------------ Cargo.toml | 4 - packages/rs-dpp/Cargo.toml | 5 +- .../generate_bls_compatibility_vectors.rs | 138 +++++++ packages/rs-dpp/src/bls/bls_signatures.rs | 236 +++++++++++ packages/rs-dpp/src/bls/mod.rs | 7 + packages/rs-dpp/src/bls/native_bls.rs | 39 +- packages/rs-dpp/src/bls/serde.rs | 281 +++++++++++++ .../rs-dpp/src/core_types/validator/mod.rs | 6 +- .../rs-dpp/src/core_types/validator/v0/mod.rs | 24 +- .../src/core_types/validator_set/mod.rs | 24 +- .../src/core_types/validator_set/v0/mod.rs | 32 +- packages/rs-dpp/src/errors/protocol_error.rs | 2 +- .../identity/identity_public_key/key_type.rs | 21 +- .../identity_public_key/v0/methods/mod.rs | 33 +- packages/rs-dpp/src/lib.rs | 8 +- .../src/serialization/dashcore/bls_pubkey.rs | 273 ------------ .../rs-dpp/src/serialization/dashcore/mod.rs | 20 +- packages/rs-dpp/src/serialization/mod.rs | 4 +- packages/rs-dpp/src/signing.rs | 34 +- packages/rs-dpp/tests/bls_compatibility.rs | 388 ++++++++++++++++++ .../fixtures/bls_compatibility/README.md | 37 ++ .../fixtures/bls_compatibility/vectors.json | 328 +++++++++++++++ packages/rs-drive-abci/src/abci/error.rs | 2 +- packages/rs-drive-abci/src/error/execution.rs | 2 +- packages/rs-drive-abci/src/error/mod.rs | 2 +- .../block_end/validator_set_update/mod.rs | 8 +- .../update_operator_identity/v0/mod.rs | 40 +- .../update_masternode_list/mod.rs | 19 +- .../update_state_masternode_list/v1/mod.rs | 4 +- .../update_quorum_info/v0/mod.rs | 2 +- .../core_chain_lock/choose_quorum/mod.rs | 6 +- .../core_chain_lock/choose_quorum/v0/mod.rs | 14 +- .../verify_chain_lock_locally/v0/mod.rs | 27 +- .../verify_recent_signature_locally/v0/mod.rs | 10 +- .../storage/store_platform_state/v1/mod.rs | 4 +- packages/rs-drive-abci/src/mimic/mod.rs | 6 +- .../rs-drive-abci/src/mimic/test_quorum.rs | 16 +- .../src/platform_types/commit/mod.rs | 6 +- .../src/platform_types/commit/v0/mod.rs | 18 +- .../v0/old_structures/mod.rs | 2 +- .../v0/fixtures/README.md | 35 ++ .../v0/fixtures/generate-quorum-storage-v0.rs | 60 +++ .../v0/fixtures/quorum-storage-v0.hex | 1 + .../v0/for_saving_v0.rs | 84 +++- .../v0/for_saving_v1.rs | 4 +- .../v0/quorum_set.rs | 7 +- .../v0/quorums.rs | 34 +- .../src/platform_types/validator/v0/mod.rs | 6 +- .../platform_types/validator_set/v0/mod.rs | 28 +- .../src/query/document_query/v1/tests.rs | 55 +-- .../system/current_quorums_info/v0/mod.rs | 6 +- .../tests/strategy_tests/execution.rs | 24 +- .../tests/strategy_tests/main.rs | 2 +- .../masternode_list_item_helpers.rs | 23 +- .../tests/strategy_tests/masternodes.rs | 22 +- .../tests/strategy_tests/query.rs | 29 +- .../test_cases/address_tests.rs | 7 +- .../rs-drive-proof-verifier/src/unproved.rs | 11 +- .../rs-drive-proof-verifier/src/verify.rs | 35 +- .../src/masternode/locator.rs | 18 +- .../src/masternode/update_service.rs | 35 +- .../system/queries/current_quorums_info.rs | 2 +- packages/simple-signer/src/signer.rs | 17 +- 65 files changed, 1941 insertions(+), 1031 deletions(-) create mode 100644 packages/rs-dpp/examples/generate_bls_compatibility_vectors.rs create mode 100644 packages/rs-dpp/src/bls/bls_signatures.rs create mode 100644 packages/rs-dpp/src/bls/serde.rs delete mode 100644 packages/rs-dpp/src/serialization/dashcore/bls_pubkey.rs create mode 100644 packages/rs-dpp/tests/bls_compatibility.rs create mode 100644 packages/rs-dpp/tests/fixtures/bls_compatibility/README.md create mode 100644 packages/rs-dpp/tests/fixtures/bls_compatibility/vectors.json create mode 100644 packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/README.md create mode 100644 packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/generate-quorum-storage-v0.rs create mode 100644 packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v0.hex diff --git a/CHANGELOG.md b/CHANGELOG.md index 95020ebedad..78acb562cdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## [Unreleased] + +### Changed + +- **dpp (Rust API migration):** BLS types now live under `dpp::bls::{PublicKey, + SecretKey, Signature, BlsError}`; the generic `dpp::bls_signatures` re-export + is removed. Use `PublicKey::to_bytes()`, `SecretKey::from_be_bytes()` returning + `Option`, and Basic `sign(message)` / `Signature::from_compressed()`. The + `serialization::dashcore::bls_pubkey` Serde adapter remains a deprecated alias. + Serialized encodings and consensus rules are unchanged. See [#5320](https://github.com/dashpay/platform/pull/5320). + ## [5.0.0-beta.2](https://github.com/dashpay/platform/compare/v5.0.0-beta.1...v5.0.0-beta.2) (2026-10-06) diff --git a/Cargo.lock b/Cargo.lock index e17e6ece13a..3393eccb4e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,7 +15,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ "crypto-common 0.1.7", - "generic-array 0.14.7", + "generic-array", ] [[package]] @@ -791,7 +791,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array 0.14.7", + "generic-array", ] [[package]] @@ -809,7 +809,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" dependencies = [ - "generic-array 0.14.7", + "generic-array", ] [[package]] @@ -844,35 +844,11 @@ dependencies = [ "subtle", ] -[[package]] -name = "blsful" -version = "3.0.0" -source = "git+https://github.com/dashpay/agora-blsful?rev=0c34a7a488a0bd1c9a9a2196e793b303ad35c900#0c34a7a488a0bd1c9a9a2196e793b303ad35c900" -dependencies = [ - "anyhow", - "blstrs_plus", - "hex", - "hkdf", - "merlin", - "pairing", - "rand 0.8.6", - "rand_chacha 0.3.1", - "rand_core 0.6.4", - "serde", - "serde_bare", - "sha2 0.10.9", - "sha3", - "subtle", - "thiserror 2.0.18", - "uint-zigzag", - "vsss-rs", - "zeroize", -] - [[package]] name = "blst" -version = "0.3.12" -source = "git+https://github.com/supranational/blst?rev=71a00877c75a482e7d44c4e6370c1a7fb82444be#71a00877c75a482e7d44c4e6370c1a7fb82444be" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20659f9bbee16cbbd2f7393e40ab6309f5a98f76a2eb57a995ec508b72387fe" dependencies = [ "cc", "glob", @@ -880,24 +856,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "blstrs_plus" -version = "0.8.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a16dd4b0d6b4538e1fa0388843acb186363082713a8fc8416d802a04d013818" -dependencies = [ - "arrayref", - "blst", - "elliptic-curve", - "ff 0.13.1", - "group 0.13.0", - "pairing", - "rand_core 0.6.4", - "serde", - "subtle", - "zeroize", -] - [[package]] name = "borsh" version = "1.6.1" @@ -1594,26 +1552,13 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "serdect", - "subtle", - "zeroize", -] - [[package]] name = "crypto-common" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ - "generic-array 0.14.7", + "generic-array", "rand_core 0.6.4", "typenum", ] @@ -2328,12 +2273,12 @@ dependencies = [ "async-trait", "base64 0.22.1", "bech32 0.11.1", - "blsful", "bs58", "byteorder", "chrono", "chrono-tz", "ciborium", + "dash-pkc", "dash-spv", "dashcore", "dashcore-rpc", @@ -2380,6 +2325,7 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tracing", + "zeroize", ] [[package]] @@ -2605,41 +2551,6 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest 0.10.7", - "ff 0.13.1", - "generic-array 0.14.7", - "group 0.13.0", - "hkdf", - "pkcs8", - "rand_core 0.6.4", - "sec1", - "subtle", - "tap", - "zeroize", -] - -[[package]] -name = "elliptic-curve-tools" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de2b6fae800f08032a6ea32995b52925b1d451bff9d445c8ab2932323277faf" -dependencies = [ - "elliptic-curve", - "heapless 0.8.0", - "hex", - "multiexp", - "serde", - "zeroize", -] - [[package]] name = "embedded-io" version = "0.4.0" @@ -3103,18 +3014,6 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", - "zeroize", -] - -[[package]] -name = "generic-array" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e55f16dcf0e9c00efbe2e655ffe45fc98e7066b52bc92f8a79e64060a79351" -dependencies = [ - "rustversion", - "serde_core", - "typenum", ] [[package]] @@ -3225,9 +3124,7 @@ checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff 0.13.1", "memuse", - "rand 0.8.6", "rand_core 0.6.4", - "rand_xorshift 0.3.0", "subtle", ] @@ -3604,15 +3501,6 @@ dependencies = [ "byteorder", ] -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - [[package]] name = "hashbrown" version = "0.12.3" @@ -3677,20 +3565,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" dependencies = [ "atomic-polyfill", - "hash32 0.2.1", + "hash32", "rustc_version", "serde", - "spin 0.9.8", - "stable_deref_trait", -] - -[[package]] -name = "heapless" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" -dependencies = [ - "hash32 0.3.1", + "spin", "stable_deref_trait", ] @@ -4166,7 +4044,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ "block-padding", - "generic-array 0.14.7", + "generic-array", ] [[package]] @@ -4451,15 +4329,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "keccak" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" -dependencies = [ - "cpufeatures 0.2.17", -] - [[package]] name = "key-wallet" version = "0.45.0" @@ -4547,7 +4416,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin 0.9.8", + "spin", ] [[package]] @@ -4785,18 +4654,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d97bbf43eb4f088f8ca469930cde17fa036207c9a5e02ccc5107c4e8b17c964" -[[package]] -name = "merlin" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" -dependencies = [ - "byteorder", - "keccak", - "rand_core 0.6.4", - "zeroize", -] - [[package]] name = "metrics" version = "0.24.6" @@ -4963,20 +4820,6 @@ dependencies = [ "pxfm", ] -[[package]] -name = "multiexp" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ec2ce93a6f06ac6cae04c1da3f2a6a24fcfc1f0eb0b4e0f3d302f0df45326cb" -dependencies = [ - "ff 0.13.1", - "group 0.13.0", - "rand_core 0.6.4", - "rustversion", - "std-shims", - "zeroize", -] - [[package]] name = "multimap" version = "0.10.1" @@ -5068,8 +4911,6 @@ checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", - "rand 0.8.6", - "serde", ] [[package]] @@ -5085,8 +4926,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", - "rand 0.8.6", - "serde", ] [[package]] @@ -5135,7 +4974,6 @@ dependencies = [ "num-bigint", "num-integer", "num-traits", - "serde", ] [[package]] @@ -5313,15 +5151,6 @@ dependencies = [ "zip32", ] -[[package]] -name = "pairing" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" -dependencies = [ - "group 0.13.0", -] - [[package]] name = "parking" version = "2.2.1" @@ -5776,7 +5605,7 @@ dependencies = [ "cobs", "embedded-io 0.4.0", "embedded-io 0.6.1", - "heapless 0.7.17", + "heapless", "serde", ] @@ -5906,7 +5735,7 @@ dependencies = [ "num-traits", "rand 0.9.4", "rand_chacha 0.9.0", - "rand_xorshift 0.4.0", + "rand_xorshift", "regex-syntax", "rusty-fork", "tempfile", @@ -6256,15 +6085,6 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" -[[package]] -name = "rand_xorshift" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" -dependencies = [ - "rand_core 0.6.4", -] - [[package]] name = "rand_xorshift" version = "0.4.0" @@ -7183,20 +7003,6 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array 0.14.7", - "pkcs8", - "subtle", - "zeroize", -] - [[package]] name = "secp256k1" version = "0.33.1" @@ -7277,15 +7083,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "serde_bare" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51c55386eed0f1ae957b091dc2ca8122f287b60c79c774cbe3d5f2b69fded660" -dependencies = [ - "serde", -] - [[package]] name = "serde_bytes" version = "0.11.19" @@ -7510,16 +7307,6 @@ dependencies = [ "digest 0.11.3", ] -[[package]] -name = "sha3" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" -dependencies = [ - "digest 0.10.7", - "keccak", -] - [[package]] name = "sharded-slab" version = "0.1.7" @@ -7681,12 +7468,6 @@ dependencies = [ "lock_api", ] -[[package]] -name = "spin" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "023a211cb3138dbc438680b32560ad89f699977624c9f8dbb95a47d5b4c07dd3" - [[package]] name = "spki" version = "0.7.3" @@ -7730,17 +7511,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "std-shims" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "227c4f8561598188d0df96dbe749824576174bba278b5b6bb2eacff1066067d0" -dependencies = [ - "hashbrown 0.16.1", - "rustversion", - "spin 0.10.1", -] - [[package]] name = "strategy-tests" version = "5.0.0-beta.2" @@ -8758,12 +8528,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "uint-zigzag" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61faa33dc26b2851a37da5390a1a4cac015887b1e97ecd77ce7b4f987431de9f" - [[package]] name = "unarray" version = "0.1.4" @@ -8949,24 +8713,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "vsss-rs" -version = "5.1.0" -source = "git+https://github.com/dashpay/vsss-rs?branch=main#668f1406bf25a4b9a95cd97c9069f7a1632897c3" -dependencies = [ - "crypto-bigint", - "elliptic-curve", - "elliptic-curve-tools", - "generic-array 1.4.3", - "hex", - "num", - "rand_core 0.6.4", - "serde", - "sha3", - "subtle", - "zeroize", -] - [[package]] name = "wait-timeout" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index 0849257b982..34adc6786a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,10 +75,6 @@ dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "314f10 dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "314f10600e35311f3d875a6e8740476ab6c79c0c" } tokio-metrics = "0.5" -[patch.crates-io] -# Supply no_std aggregate verification while retaining blstrs_plus's exact 0.3.12 pin. -blst = { git = "https://github.com/supranational/blst", rev = "71a00877c75a482e7d44c4e6370c1a7fb82444be" } - # Size-tuned profile for the iOS `rs-unified-sdk-ffi` staticlib, which # otherwise ships huge. Inherits `release` and is ONLY used by the iOS # build (`build_ios.sh --profile release`) diff --git a/packages/rs-dpp/Cargo.toml b/packages/rs-dpp/Cargo.toml index 1e74169571d..1a41858a72e 100644 --- a/packages/rs-dpp/Cargo.toml +++ b/packages/rs-dpp/Cargo.toml @@ -12,7 +12,8 @@ authors = [ ] [dependencies] -blsful = { git = "https://github.com/dashpay/agora-blsful", rev = "0c34a7a488a0bd1c9a9a2196e793b303ad35c900", optional = true } +dash-pkc = { git = "https://github.com/dashpay/base-sdk", rev = "e6402ced257c370a586ade9840ebbf545a4b7926", default-features = false, features = ["bls"], optional = true } +zeroize = { version = "1.9.0", optional = true } ed25519-dalek = { version = "=2.2.0", features = ["rand_core"], optional = true } anyhow = { version = "1.0.81" } async-trait = { version = "0.1.79" } @@ -117,7 +118,7 @@ core_key_wallet_manager = ["dep:key-wallet-manager"] core_key_wallet_serde = ["dep:key-wallet", "key-wallet/serde"] core_spv = ["dep:dash-spv"] core_rpc_client = ["dep:dashcore-rpc"] -bls-signatures = ["dashcore/bls", "dep:blsful"] +bls-signatures = ["dashcore/bls", "dep:dash-pkc", "dep:zeroize"] ed25519-dalek = ["dashcore/eddsa", "dep:ed25519-dalek"] all_features = [ "json-conversion", diff --git a/packages/rs-dpp/examples/generate_bls_compatibility_vectors.rs b/packages/rs-dpp/examples/generate_bls_compatibility_vectors.rs new file mode 100644 index 00000000000..f137030b63a --- /dev/null +++ b/packages/rs-dpp/examples/generate_bls_compatibility_vectors.rs @@ -0,0 +1,138 @@ +//! Emit deterministic compatibility fixtures from DPP's current BLS backend. +//! Run with `cargo run -p dpp --example generate_bls_compatibility_vectors`. + +use dpp::bls::{PublicKey, SecretKey, Signature}; +use dpp::core_types::validator::v0::ValidatorV0; +use dpp::core_types::validator_set::v0::ValidatorSetV0; +use dpp::dashcore::{hashes::Hash, ProTxHash, PubkeyHash, QuorumHash}; +use serde_json::json; +use std::collections::BTreeMap; + +fn main() { + let key_generation: Vec<_> = [[0u8; 32], [1; 32], [255; 32]].iter().map(|ikm| { + let key = SecretKey::from_ikm(ikm).unwrap(); + json!({"ikm": hex::encode(ikm), "secret_key": hex::encode(key.to_be_bytes()), "public_key": hex::encode(key.public_key().to_bytes())}) + }).collect(); + // Public test material: unit scalars, asymmetric bytes and the last canonical scalar. + let keys: Vec<[u8; 32]> = [ + "0000000000000000000000000000000000000000000000000000000000000001", + "0000000000000000000000000000000000000000000000000000000000000002", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000", + ] + .iter() + .map(|s| hex::decode(s).unwrap().try_into().unwrap()) + .collect(); + let mut basic = Vec::new(); + for bytes in &keys { + let key = SecretKey::from_be_bytes(bytes).unwrap(); + for message in [ + vec![], + b"Dash Platform BLS compatibility".to_vec(), + vec![0; 32], + (0..=255).collect(), + ] { + let signature = key.sign(&message).unwrap(); + basic.push(json!({ + "secret_key": hex::encode(bytes), "message": hex::encode(message), + "public_key": hex::encode(key.public_key().to_bytes()), + "legacy_public_key": hex::encode(key.public_key().to_legacy_bytes().unwrap()), + "signature": hex::encode(signature.to_bytes()) + })); + } + } + let mut secure_aggregation = Vec::new(); + for indexes in [ + vec![0], + vec![0, 1], + vec![0, 1, 2], + vec![2, 0, 1], + vec![0, 0], + ] { + let message = b"Dash Platform secure aggregation"; + let signers: Vec<_> = indexes + .iter() + .map(|&i| SecretKey::from_be_bytes(&keys[i]).unwrap()) + .collect(); + let public_keys: Vec<_> = signers.iter().map(|key| key.public_key()).collect(); + let signatures: Vec<_> = signers + .iter() + .map(|key| key.sign(message).unwrap()) + .collect(); + let point = Signature::aggregate_secure(&signatures, &public_keys).unwrap(); + secure_aggregation.push(json!({ + "secret_keys": indexes.iter().map(|&i| hex::encode(keys[i])).collect::>(), + "message": hex::encode(message), + "public_keys": public_keys.iter().map(|key| hex::encode(key.to_bytes())).collect::>(), + "signature": hex::encode(point.to_bytes()) + })); + } + let mut scalars = Vec::new(); + for (name, bytes) in [ + ("zero", [0; 32]), + ( + "order", + hex::decode("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001") + .unwrap() + .try_into() + .unwrap(), + ), + ( + "order_plus_one", + hex::decode("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000002") + .unwrap() + .try_into() + .unwrap(), + ), + ("max", [255; 32]), + ] { + let key = SecretKey::from_be_bytes(&bytes); + scalars.push(json!({ + "name": name, "input": hex::encode(bytes), + "normalized": key.as_ref().map(|key| hex::encode(key.to_be_bytes())), + "public_key": key.as_ref().map(|key| hex::encode(key.public_key().to_bytes())), + "signature": key.as_ref().map(|key| hex::encode(key.sign(b"scalar boundary").unwrap().to_bytes())) + })); + } + let mut infinity = [0; 48]; + infinity[0] = 0xc0; + let public_key = SecretKey::from_be_bytes(&keys[0]).unwrap().public_key(); + let mut storage = Vec::new(); + for (name, key) in [ + ("public_key", Some(public_key)), + ("absent", None), + ( + "infinity", + Some(PublicKey::try_from(infinity.as_slice()).unwrap()), + ), + ] { + let validator = ValidatorV0 { + pro_tx_hash: ProTxHash::from_byte_array([0x11; 32]), + public_key: key, + node_ip: "127.0.0.1".into(), + node_id: PubkeyHash::from_byte_array([0x22; 20]), + core_port: 19999, + platform_http_port: 1443, + platform_p2p_port: 26656, + is_banned: false, + }; + let config = bincode::config::standard().with_big_endian(); + let set = ValidatorSetV0 { + quorum_hash: QuorumHash::from_byte_array([0x33; 32]), + quorum_index: Some(2), + core_height: 123456, + members: BTreeMap::from([(validator.pro_tx_hash, validator.clone())]), + threshold_public_key: public_key, + }; + storage.push(json!({ + "name": name, "validator": serde_json::to_value(&validator).unwrap(), + "validator_bytes": hex::encode(bincode::encode_to_vec(&validator, config).unwrap()), + "validator_set": serde_json::to_value(&set).unwrap(), + "validator_set_bytes": hex::encode(bincode::encode_to_vec(&set, config).unwrap()) + })); + } + println!("{}", serde_json::to_string_pretty(&json!({ + "basic": basic, "secure_aggregation": secure_aggregation, "scalars": scalars, "storage": storage, + "key_generation": key_generation + })).unwrap()); +} diff --git a/packages/rs-dpp/src/bls/bls_signatures.rs b/packages/rs-dpp/src/bls/bls_signatures.rs new file mode 100644 index 00000000000..a3f9d3bb2b6 --- /dev/null +++ b/packages/rs-dpp/src/bls/bls_signatures.rs @@ -0,0 +1,236 @@ +//! Platform BLS keys and Basic signatures backed by rust-dashcore's dash-pkc primitives. + +use dash_pkc::bls::{BlsPublicKey, BlsScChia, BlsScIetf, BlsSecretKey, BlsSignature, Fr}; +use rand::{CryptoRng, Rng, RngCore}; +use std::fmt; +use zeroize::Zeroizing; + +const fn infinity() -> [u8; N] { + let mut bytes = [0; N]; + bytes[0] = 0xc0; + bytes +} + +/// Errors exposed by Platform's BLS operations. +#[derive(Debug, Clone, thiserror::Error)] +pub enum BlsError { + /// An input has an invalid length or encoding. + #[error("invalid inputs: {0}")] + InvalidInputs(String), + /// The signature does not verify. + #[error("invalid signature")] + InvalidSignature, + /// A key or signature could not be decoded. + #[error("deserialization error: {0}")] + DeserializationError(String), +} + +/// A validated G1 key, including the identity permitted by historical Platform parsing. +#[derive(Clone, Copy, Default, Eq, PartialEq)] +pub struct PublicKey(PublicKeyPoint); + +#[derive(Clone, Copy, Default, Eq, PartialEq)] +enum PublicKeyPoint { + #[default] + Infinity, + Validated(BlsPublicKey), +} + +impl PublicKey { + /// Return the canonical compressed IETF encoding. + pub fn to_bytes(&self) -> [u8; 48] { + match &self.0 { + PublicKeyPoint::Infinity => infinity(), + PublicKeyPoint::Validated(key) => key.to_bytes(), + } + } + + /// Re-encode a public key for Core's legacy wire format. + pub fn to_legacy_bytes(&self) -> Result<[u8; 48], BlsError> { + let PublicKeyPoint::Validated(key) = &self.0 else { + return Ok(infinity()); + }; + key.to_scheme::() + .map(|key| key.to_bytes()) + .map_err(|_| BlsError::InvalidInputs("Invalid byte sequence".into())) + } + + fn validated(&self) -> Result<&BlsPublicKey, BlsError> { + match &self.0 { + PublicKeyPoint::Infinity => Err(BlsError::InvalidSignature), + PublicKeyPoint::Validated(key) => Ok(key), + } + } +} + +impl From<&SecretKey> for PublicKey { + fn from(key: &SecretKey) -> Self { + key.public_key() + } +} + +impl TryFrom<&[u8]> for PublicKey { + type Error = BlsError; + + fn try_from(value: &[u8]) -> Result { + let bytes = value.try_into().map_err(|_| { + BlsError::InvalidInputs(format!("Invalid length, expected 48, got {}", value.len())) + })?; + // Persisted state and shipped validation accept canonical infinity, but verification rejects it. + if bytes == infinity() { + return Ok(Self(PublicKeyPoint::Infinity)); + } + BlsPublicKey::from_bytes(&bytes) + .map(|key| Self(PublicKeyPoint::Validated(key))) + .map_err(|_| BlsError::InvalidInputs("Invalid byte sequence".into())) + } +} + +impl fmt::Display for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&hex::encode(self.to_bytes())) + } +} + +impl fmt::Debug for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +/// A secret scalar; the backend clears its storage on drop. +#[derive(Clone, Debug)] +pub struct SecretKey(BlsSecretKey); + +impl SecretKey { + /// Parse a big-endian scalar using Platform's historical nonzero reduction rule. + pub fn from_be_bytes(bytes: &[u8; 32]) -> Option { + let scalar = Zeroizing::new(Fr::from_bendian_reduce(bytes).ok()?); + BlsSecretKey::try_from(*scalar).ok().map(Self) + } + + /// Derive a key from at least 32 bytes of keying material using Core's keygen-v3. + pub fn from_ikm(ikm: &[u8]) -> Result { + BlsSecretKey::from_ikm(ikm) + .map(Self) + .map_err(|e| BlsError::InvalidInputs(e.to_string())) + } + + /// Draw keying material with Platform's rand interface and derive a secret key. + pub fn random(mut rng: impl RngCore + CryptoRng) -> Self { + loop { + let material = Zeroizing::new(rng.gen::<[u8; 32]>()); + if let Ok(key) = Self::from_ikm(material.as_ref()) { + return key; + } + } + } + + /// Return the canonical big-endian secret scalar. + pub fn to_be_bytes(&self) -> [u8; 32] { + *self.0.to_bytes() + } + + /// Derive the corresponding public key. + pub fn public_key(&self) -> PublicKey { + PublicKey(PublicKeyPoint::Validated(self.0.public_key())) + } + + /// Sign arbitrary message bytes with the Basic IETF domain separation tag. + pub fn sign(&self, message: &[u8]) -> Result { + Ok(Signature::from_validated(self.0.sign(message))) + } +} + +/// A validated G2 Basic signature, with canonical infinity represented separately. +#[derive(Clone, Copy, Eq, PartialEq)] +pub struct Signature(Option>); + +impl fmt::Debug for Signature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Signature").field(&self.to_bytes()).finish() + } +} + +impl fmt::Display for Signature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&hex::encode(self.to_bytes())) + } +} + +impl Signature { + /// Decode a subgroup point, including the identity rejected during verification. + pub fn from_compressed(bytes: &[u8; 96]) -> Option { + if *bytes == infinity() { + return Some(Self(None)); + } + BlsSignature::from_bytes(bytes) + .ok() + .map(|signature| Self(Some(signature))) + } + + fn from_validated(signature: BlsSignature) -> Self { + // Aggregation can produce infinity; keep its historical parsing and verification policy. + Self((signature.to_bytes() != infinity()).then_some(signature)) + } + + /// Return the compressed IETF encoding without a scheme tag. + pub fn to_bytes(&self) -> [u8; 96] { + self.0 + .as_ref() + .map_or_else(infinity, BlsSignature::to_bytes) + } + + /// Verify a Basic signature over arbitrary message bytes. + pub fn verify( + &self, + public_key: &PublicKey, + message: impl AsRef<[u8]>, + ) -> Result<(), BlsError> { + let signature = self + .0 + .as_ref() + .ok_or_else(|| BlsError::InvalidInputs("signature is the identity point".into()))?; + let PublicKeyPoint::Validated(key) = &public_key.0 else { + return Err(BlsError::InvalidInputs( + "public key is the identity point".into(), + )); + }; + key.verify(message.as_ref(), signature) + .map_err(|_| BlsError::InvalidSignature) + } + + /// Aggregate signatures with deterministic public-key weights. + pub fn aggregate_secure( + signatures: &[Self], + public_keys: &[PublicKey], + ) -> Result { + let signatures: Vec<_> = signatures + .iter() + .map(|sig| sig.0.as_ref().ok_or(BlsError::InvalidSignature)) + .collect::>()?; + let keys: Vec<_> = public_keys + .iter() + .map(PublicKey::validated) + .collect::>()?; + BlsSignature::secure_aggregate(&signatures, &keys) + .map(Self::from_validated) + .map_err(|_| BlsError::InvalidSignature) + } + + /// Verify a secure aggregate over a common message. + pub fn verify_secure( + &self, + public_keys: &[PublicKey], + message: impl AsRef<[u8]>, + ) -> Result<(), BlsError> { + let keys: Vec<_> = public_keys + .iter() + .map(PublicKey::validated) + .collect::>()?; + let signature = self.0.as_ref().ok_or(BlsError::InvalidSignature)?; + signature + .secure_verify_aggregates(message.as_ref(), &keys) + .map_err(|_| BlsError::InvalidSignature) + } +} diff --git a/packages/rs-dpp/src/bls/mod.rs b/packages/rs-dpp/src/bls/mod.rs index a5501f4634d..44b95b4a2bb 100644 --- a/packages/rs-dpp/src/bls/mod.rs +++ b/packages/rs-dpp/src/bls/mod.rs @@ -1,5 +1,12 @@ #[cfg(feature = "bls-signatures")] +mod bls_signatures; +#[cfg(feature = "bls-signatures")] +pub use bls_signatures::{BlsError, PublicKey, SecretKey, Signature}; +#[cfg(feature = "bls-signatures")] pub mod native_bls; +/// Serde implementations and field adapters for BLS public keys. +#[cfg(feature = "bls-signatures")] +pub mod serde; use crate::{ProtocolError, PublicKeyValidationError}; diff --git a/packages/rs-dpp/src/bls/native_bls.rs b/packages/rs-dpp/src/bls/native_bls.rs index 3e1341bf9ab..2bcd1dd4273 100644 --- a/packages/rs-dpp/src/bls/native_bls.rs +++ b/packages/rs-dpp/src/bls/native_bls.rs @@ -1,13 +1,11 @@ -use crate::bls_signatures::{ - Bls12381G2Impl, Pairing, PublicKey, SecretKey, Signature, SignatureSchemes, -}; +use crate::bls::{PublicKey, SecretKey, Signature}; use crate::{BlsModule, ProtocolError, PublicKeyValidationError}; #[derive(Default)] pub struct NativeBlsModule; impl BlsModule for NativeBlsModule { fn validate_public_key(&self, pk: &[u8]) -> Result<(), PublicKeyValidationError> { - match PublicKey::::try_from(pk) { + match PublicKey::try_from(pk) { Ok(_) => Ok(()), Err(e) => Err(PublicKeyValidationError::new(e.to_string())), } @@ -19,22 +17,17 @@ impl BlsModule for NativeBlsModule { data: &[u8], public_key: &[u8], ) -> Result { - let public_key = PublicKey::::try_from(public_key)?; + let public_key = PublicKey::try_from(public_key)?; let signature_96_bytes = signature .try_into() .map_err(|_| ProtocolError::BlsSignatureSizeError { got: signature.len() as u32, })?; - let Some(g2_element) = - ::Signature::from_compressed(&signature_96_bytes) - .into_option() - else { + let Some(signature) = Signature::from_compressed(&signature_96_bytes) else { return Ok(false); // We should not error because the signature could be given by an invalid source }; - let signature = Signature::Basic(g2_element); - match signature.verify(&public_key, data) { Ok(_) => Ok(true), Err(_) => Ok(false), @@ -48,13 +41,11 @@ impl BlsModule for NativeBlsModule { .map_err(|_| ProtocolError::PrivateKeySizeError { got: private_key.len() as u32, })?; - let pk = SecretKey::::from_be_bytes(&fixed_len_key) - .into_option() - .ok_or(ProtocolError::InvalidBLSPrivateKeyError( - "key not valid".to_string(), - ))?; + let pk = SecretKey::from_be_bytes(&fixed_len_key).ok_or( + ProtocolError::InvalidBLSPrivateKeyError("key not valid".to_string()), + )?; let public_key = pk.public_key(); - let public_key_bytes = public_key.0.to_compressed().to_vec(); + let public_key_bytes = public_key.to_bytes().to_vec(); Ok(public_key_bytes) } @@ -65,15 +56,9 @@ impl BlsModule for NativeBlsModule { .map_err(|_| ProtocolError::PrivateKeySizeError { got: private_key.len() as u32, })?; - let pk = SecretKey::::from_be_bytes(&fixed_len_key) - .into_option() - .ok_or(ProtocolError::InvalidBLSPrivateKeyError( - "key not valid".to_string(), - ))?; - Ok(pk - .sign(SignatureSchemes::Basic, data)? - .as_raw_value() - .to_compressed() - .to_vec()) + let pk = SecretKey::from_be_bytes(&fixed_len_key).ok_or( + ProtocolError::InvalidBLSPrivateKeyError("key not valid".to_string()), + )?; + Ok(pk.sign(data)?.to_bytes().to_vec()) } } diff --git a/packages/rs-dpp/src/bls/serde.rs b/packages/rs-dpp/src/bls/serde.rs new file mode 100644 index 00000000000..205a1e71b8a --- /dev/null +++ b/packages/rs-dpp/src/bls/serde.rs @@ -0,0 +1,281 @@ +//! Serde support for Platform BLS keys in hex, byte-sequence and tagged-enum inputs. + +use crate::bls::PublicKey as BlsPublicKey; +use ::serde::de::Visitor; +use ::serde::ser::SerializeTuple; +use ::serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt; + +/// Size of a compressed BLS12-381 G1 public key. +const COMPRESSED_G1_LEN: usize = 48; + +impl Serialize for BlsPublicKey { + fn serialize(&self, serializer: S) -> Result { + if serializer.is_human_readable() { + serializer.serialize_str(&hex::encode(self.to_bytes())) + } else { + let mut tuple = serializer.serialize_tuple(COMPRESSED_G1_LEN)?; + for byte in self.to_bytes() { + tuple.serialize_element(&byte)?; + } + tuple.end() + } + } +} + +impl<'de> Deserialize<'de> for BlsPublicKey { + fn deserialize>(deserializer: D) -> Result { + if deserializer.is_human_readable() { + deserializer.deserialize_any(BlsPublicKeyVisitor) + } else { + // Bincode needs the fixed tuple length and cannot use deserialize_any. + deserializer.deserialize_tuple(COMPRESSED_G1_LEN, BlsPublicKeyVisitor) + } + } +} + +/// Serialize a public-key field with the DPP key's standard representation. +pub fn serialize(pk: &BlsPublicKey, serializer: S) -> Result { + pk.serialize(serializer) +} + +/// Decode a field from self-describing or buffered tagged-enum data. +/// +/// For formats such as bincode, use the key's `Deserialize` implementation instead. +pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result { + // Tagged enum buffers may supply either representation regardless of is_human_readable(). + deserializer.deserialize_any(BlsPublicKeyVisitor) +} + +fn from_compressed_g1_bytes(bytes: &[u8]) -> Result { + if bytes.len() != COMPRESSED_G1_LEN { + return Err(E::custom(format!( + "expected {COMPRESSED_G1_LEN} compressed-G1 bytes for public key, got {}", + bytes.len() + ))); + } + BlsPublicKey::try_from(bytes).map_err(|_| E::custom("not a valid compressed G1 point")) +} + +struct BlsPublicKeyVisitor; + +impl<'de> Visitor<'de> for BlsPublicKeyVisitor { + type Value = BlsPublicKey; + + fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!( + f, + "a {}-char hex string or {} compressed-G1 bytes", + COMPRESSED_G1_LEN * 2, + COMPRESSED_G1_LEN + ) + } + + fn visit_str(self, s: &str) -> Result { + if s.len() != COMPRESSED_G1_LEN * 2 { + return Err(E::custom(format!( + "expected {} hex chars for compressed G1 public key, got {}", + COMPRESSED_G1_LEN * 2, + s.len() + ))); + } + let mut bytes = [0u8; COMPRESSED_G1_LEN]; + for (i, slot) in bytes.iter_mut().enumerate() { + let hi = hex_nibble(s.as_bytes()[i * 2]).map_err(E::custom)?; + let lo = hex_nibble(s.as_bytes()[i * 2 + 1]).map_err(E::custom)?; + *slot = (hi << 4) | lo; + } + from_compressed_g1_bytes(&bytes) + } + + fn visit_bytes(self, v: &[u8]) -> Result { + from_compressed_g1_bytes(v) + } + + fn visit_seq>( + self, + mut seq: A, + ) -> Result { + let mut bytes = Vec::with_capacity(COMPRESSED_G1_LEN); + while let Some(b) = seq.next_element::()? { + // A valid compressed-G1 public key is exactly COMPRESSED_G1_LEN + // bytes; reject as soon as a hostile payload exceeds that rather + // than allocating/parsing an arbitrarily long sequence first. + if bytes.len() == COMPRESSED_G1_LEN { + return Err(::serde::de::Error::invalid_length(bytes.len() + 1, &self)); + } + bytes.push(b); + } + from_compressed_g1_bytes(&bytes) + } +} + +fn hex_nibble(c: u8) -> Result { + match c { + b'0'..=b'9' => Ok(c - b'0'), + b'a'..=b'f' => Ok(c - b'a' + 10), + b'A'..=b'F' => Ok(c - b'A' + 10), + _ => Err("invalid hex character in compressed G1 public key"), + } +} + +/// `Option` variant for fields like +/// `Validator::public_key`. +pub mod option { + use super::*; + + pub fn serialize( + opt: &Option, + serializer: S, + ) -> Result { + // Option delegates to the DPP public key's Serialize implementation. + opt.serialize(serializer) + } + + pub fn deserialize<'de, D: Deserializer<'de>>( + deserializer: D, + ) -> Result, D::Error> { + struct OptionVisitor; + + impl<'de> Visitor<'de> for OptionVisitor { + type Value = Option; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("Option") + } + + fn visit_none(self) -> Result { + Ok(None) + } + + fn visit_unit(self) -> Result { + Ok(None) + } + + fn visit_some>( + self, + inner: D2, + ) -> Result { + super::deserialize(inner).map(Some) + } + } + + deserializer.deserialize_option(OptionVisitor) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ::serde::{Deserialize, Serialize}; + use serde_json::json; + + // A known-valid compressed-G1 BLS public key (deterministic — the + // ValidatorSet fixture's seeded StdRng(42) threshold key). + const PK_HEX: &str = + "969c5d5873f49aa994c5f6a850924ca1840c4ad1791aaaecd90093d4a5c0c3799f2d98540f5366cfa0a33f143fd69263"; + + // Newtypes that drive the `with` module(s) through serde. + #[derive(Serialize, Deserialize)] + struct Wrap(#[serde(with = "super")] BlsPublicKey); + + #[derive(Serialize, Deserialize)] + struct OptWrap(#[serde(with = "super::option")] Option); + + #[test] + fn should_accept_the_same_json_representations_directly_and_through_the_adapter() { + let bytes = hex::decode(PK_HEX).unwrap(); + for value in [json!(PK_HEX), json!(PK_HEX.to_uppercase()), json!(bytes)] { + let direct: BlsPublicKey = serde_json::from_value(value.clone()).unwrap(); + let adapted: Wrap = serde_json::from_value(value).unwrap(); + assert_eq!(direct, adapted.0); + assert_eq!(direct.to_bytes().as_slice(), bytes); + } + } + + #[test] + fn should_reject_malformed_keys_directly_and_through_the_adapter() { + for value in [ + json!("ab"), + json!("z".repeat(96)), + json!(vec![0u8; 47]), + json!(vec![0u8; 49]), + json!(vec![0u8; 48]), + ] { + assert!(serde_json::from_value::(value.clone()).is_err()); + assert!(serde_json::from_value::(value).is_err()); + } + } + + #[test] + fn should_decode_bincode_keys_without_consuming_the_following_fields() { + let key: BlsPublicKey = serde_json::from_value(json!(PK_HEX)).unwrap(); + let record = (key, 0x21u8, Some(key)); + let config = bincode::config::standard().with_big_endian(); + let encoded = bincode::serde::encode_to_vec(record, config).unwrap(); + let mut expected = key.to_bytes().to_vec(); + expected.extend([0x21, 1]); + expected.extend(key.to_bytes()); + assert_eq!(encoded, expected); + let (decoded, consumed): ((BlsPublicKey, u8, Option), usize) = + bincode::serde::decode_from_slice(&encoded, config).unwrap(); + assert_eq!(decoded, record); + assert_eq!(consumed, encoded.len()); + assert!( + bincode::serde::decode_from_slice::(&encoded[..47], config).is_err() + ); + } + + #[test] + fn should_keep_legacy_field_adapter_paths_working() { + #[derive(Serialize, Deserialize)] + struct LegacyKey( + #[serde(with = "crate::serialization::dashcore::bls_pubkey")] BlsPublicKey, + ); + #[derive(Serialize, Deserialize)] + struct LegacyOption( + #[serde(with = "crate::serialization::dashcore::bls_pubkey::option")] + Option, + ); + + let key: LegacyKey = serde_json::from_value(json!(PK_HEX)).unwrap(); + assert_eq!(serde_json::to_value(key).unwrap(), json!(PK_HEX)); + for value in [json!(PK_HEX), json!(null)] { + let key: LegacyOption = serde_json::from_value(value.clone()).unwrap(); + assert_eq!(serde_json::to_value(key).unwrap(), value); + } + } + + #[test] + fn json_hr_round_trip_is_the_hex_string() { + // Human-readable (serde_json::Value): hex in, identical hex out (visit_str). + let pk: Wrap = serde_json::from_value(json!(PK_HEX)).expect("from hex"); + assert_eq!(serde_json::to_value(&pk).expect("to json"), json!(PK_HEX)); + } + + #[test] + fn json_borrowed_str_path_works() { + // The shared visitor accepts borrowed strings as well as owned JSON values. + let pk: Wrap = serde_json::from_str(&format!("\"{PK_HEX}\"")).expect("from_str"); + assert_eq!(serde_json::to_value(&pk).expect("to json"), json!(PK_HEX)); + } + + #[test] + fn value_non_hr_byte_seq_round_trip() { + // Non-HR `platform_value` serializes the key as a 48-byte sequence; the + // shared visit_seq path must reconstruct it. Re-serialize to JSON to + // confirm the same key. + let pk: Wrap = serde_json::from_value(json!(PK_HEX)).expect("from hex"); + let value = platform_value::to_value(&pk).expect("to value"); + let pk2: Wrap = platform_value::from_value(value).expect("from value seq"); + assert_eq!(serde_json::to_value(&pk2).expect("to json"), json!(PK_HEX)); + } + + #[test] + fn option_some_and_none_round_trip() { + let some: OptWrap = serde_json::from_value(json!(PK_HEX)).expect("some"); + assert_eq!(serde_json::to_value(&some).expect("to json"), json!(PK_HEX)); + let none: OptWrap = serde_json::from_value(json!(null)).expect("none"); + assert_eq!(serde_json::to_value(&none).expect("to json"), json!(null)); + } +} diff --git a/packages/rs-dpp/src/core_types/validator/mod.rs b/packages/rs-dpp/src/core_types/validator/mod.rs index 5cc5681b205..7215cddba58 100644 --- a/packages/rs-dpp/src/core_types/validator/mod.rs +++ b/packages/rs-dpp/src/core_types/validator/mod.rs @@ -1,4 +1,4 @@ -use crate::bls_signatures::{Bls12381G2Impl, PublicKey as BlsPublicKey}; +use crate::bls::PublicKey as BlsPublicKey; use crate::core_types::validator::v0::{ValidatorV0, ValidatorV0Getters, ValidatorV0Setters}; #[cfg(all(feature = "json-conversion", feature = "serde-conversion"))] use crate::serialization::JsonConvertible; @@ -37,7 +37,7 @@ impl ValidatorV0Getters for Validator { } } - fn public_key(&self) -> &Option> { + fn public_key(&self) -> &Option { match self { Validator::V0(v0) => v0.public_key(), } @@ -87,7 +87,7 @@ impl ValidatorV0Setters for Validator { } } - fn set_public_key(&mut self, public_key: Option>) { + fn set_public_key(&mut self, public_key: Option) { match self { Validator::V0(v0) => v0.set_public_key(public_key), } diff --git a/packages/rs-dpp/src/core_types/validator/v0/mod.rs b/packages/rs-dpp/src/core_types/validator/v0/mod.rs index dc4e9a9e3f3..eedbcb3dd1b 100644 --- a/packages/rs-dpp/src/core_types/validator/v0/mod.rs +++ b/packages/rs-dpp/src/core_types/validator/v0/mod.rs @@ -1,7 +1,7 @@ use dashcore::{ProTxHash, PubkeyHash}; use std::fmt::{Debug, Formatter}; -use crate::bls_signatures::{Bls12381G2Impl, PublicKey as BlsPublicKey}; +use crate::bls::PublicKey as BlsPublicKey; #[cfg(feature = "serde-conversion")] use serde::{Deserialize, Serialize}; @@ -21,14 +21,12 @@ pub struct ValidatorV0 { /// The proTxHash pub pro_tx_hash: ProTxHash, /// The public key share of this validator for this quorum - // `BlsPublicKey` is a dashcore type, so its serde wrapper lives in - // `serialization::dashcore::bls_pubkey` (now self-sufficient — no upstream - // dependency; accepts hex string or byte sequence through any deserializer). + // Tagged enum buffers can carry either hex strings or byte sequences. #[cfg_attr( feature = "serde-conversion", - serde(with = "crate::serialization::dashcore::bls_pubkey::option") + serde(with = "crate::bls::serde::option") )] - pub public_key: Option>, + pub public_key: Option, /// The node address pub node_ip: String, /// The node id @@ -56,7 +54,7 @@ impl Encode for ValidatorV0 { match &self.public_key { Some(public_key) => { true.encode(encoder)?; // Indicate that public_key is present - public_key.0.to_compressed().encode(encoder)?; + public_key.to_bytes().encode(encoder)?; } None => { false.encode(encoder)?; // Indicate that public_key is not present @@ -168,7 +166,7 @@ pub trait ValidatorV0Getters { /// Returns the proTxHash of the validator. fn pro_tx_hash(&self) -> &ProTxHash; /// Returns the public key share of this validator for this quorum. - fn public_key(&self) -> &Option>; + fn public_key(&self) -> &Option; /// Returns the node address of the validator. fn node_ip(&self) -> &String; /// Returns the node id of the validator. @@ -188,7 +186,7 @@ pub trait ValidatorV0Setters { /// Sets the proTxHash of the validator. fn set_pro_tx_hash(&mut self, pro_tx_hash: ProTxHash); /// Sets the public key share of this validator for this quorum. - fn set_public_key(&mut self, public_key: Option>); + fn set_public_key(&mut self, public_key: Option); /// Sets the node address of the validator. fn set_node_ip(&mut self, node_ip: String); /// Sets the node id of the validator. @@ -208,7 +206,7 @@ impl ValidatorV0Getters for ValidatorV0 { &self.pro_tx_hash } - fn public_key(&self) -> &Option> { + fn public_key(&self) -> &Option { &self.public_key } @@ -242,7 +240,7 @@ impl ValidatorV0Setters for ValidatorV0 { self.pro_tx_hash = pro_tx_hash; } - fn set_public_key(&mut self, public_key: Option>) { + fn set_public_key(&mut self, public_key: Option) { self.public_key = public_key; } @@ -274,7 +272,7 @@ impl ValidatorV0Setters for ValidatorV0 { #[cfg(test)] mod tests { use super::*; - use crate::bls_signatures::SecretKey; + use crate::bls::SecretKey; use bincode::config; use rand::prelude::StdRng; use rand::SeedableRng; @@ -284,7 +282,7 @@ mod tests { // Sample data for testing let pro_tx_hash = ProTxHash::from_slice(&[1; 32]).unwrap(); let mut rng = StdRng::seed_from_u64(0); - let public_key = Some(SecretKey::::random(&mut rng).public_key()); + let public_key = Some(SecretKey::random(&mut rng).public_key()); let node_ip = "127.0.0.1".to_string(); let node_id = PubkeyHash::from_slice(&[3; 20]).unwrap(); let core_port = 9999; diff --git a/packages/rs-dpp/src/core_types/validator_set/mod.rs b/packages/rs-dpp/src/core_types/validator_set/mod.rs index 3b5928c00b8..0b517f4ed00 100644 --- a/packages/rs-dpp/src/core_types/validator_set/mod.rs +++ b/packages/rs-dpp/src/core_types/validator_set/mod.rs @@ -1,4 +1,4 @@ -use crate::bls_signatures::{Bls12381G2Impl, PublicKey as BlsPublicKey}; +use crate::bls::PublicKey as BlsPublicKey; use crate::core_types::validator::v0::ValidatorV0; use crate::core_types::validator_set::v0::{ ValidatorSetV0, ValidatorSetV0Getters, ValidatorSetV0Setters, @@ -101,7 +101,7 @@ impl ValidatorSetV0Getters for ValidatorSet { } } - fn threshold_public_key(&self) -> &BlsPublicKey { + fn threshold_public_key(&self) -> &BlsPublicKey { match self { ValidatorSet::V0(v0) => v0.threshold_public_key(), } @@ -133,7 +133,7 @@ impl ValidatorSetV0Setters for ValidatorSet { } } - fn set_threshold_public_key(&mut self, threshold_public_key: BlsPublicKey) { + fn set_threshold_public_key(&mut self, threshold_public_key: BlsPublicKey) { match self { ValidatorSet::V0(v0) => v0.set_threshold_public_key(threshold_public_key), } @@ -148,7 +148,7 @@ impl ValidatorSetV0Setters for ValidatorSet { ))] mod json_convertible_tests { use super::*; - use crate::bls_signatures::{Bls12381G2Impl, SecretKey}; + use crate::bls::SecretKey; use crate::core_types::validator::v0::ValidatorV0; use crate::core_types::validator_set::v0::ValidatorSetV0; use dashcore::hashes::Hash; @@ -165,16 +165,12 @@ mod json_convertible_tests { /// keys ARE deterministic, but the 96-char hex / 48-byte literal is too /// unwieldy to inline as a string constant, so we interpolate the actual /// `to_value`/`to_json` of the same pubkey objects we put in the fixture. - /// (The dedicated `serialization::dashcore::bls_pubkey` unit tests + /// (The dedicated `bls::serde` unit tests /// independently cover the pubkey round-trip.) - fn build_fixture() -> ( - ValidatorSet, - BlsPublicKey, - BlsPublicKey, - ) { + fn build_fixture() -> (ValidatorSet, BlsPublicKey, BlsPublicKey) { let mut rng = StdRng::seed_from_u64(42); let pro_tx_hash = ProTxHash::from_byte_array([0x11; 32]); - let validator_pubkey = SecretKey::::random(&mut rng).public_key(); + let validator_pubkey = SecretKey::random(&mut rng).public_key(); let validator_v0 = ValidatorV0 { pro_tx_hash, public_key: Some(validator_pubkey), @@ -188,7 +184,7 @@ mod json_convertible_tests { let mut members = BTreeMap::new(); members.insert(pro_tx_hash, validator_v0); - let threshold_pubkey = SecretKey::::random(&mut rng).public_key(); + let threshold_pubkey = SecretKey::random(&mut rng).public_key(); let set = ValidatorSet::V0(ValidatorSetV0 { quorum_hash: QuorumHash::from_byte_array([0x33; 32]), quorum_index: Some(7), @@ -204,7 +200,7 @@ mod json_convertible_tests { // visible and verifiable in-place — neither path interpolates `to_json` / // `to_value` of the object under test. JSON (HR) uses the hex string // directly; the non-HR `Value` form is the *same* 48 bytes decoded into an - // `Array` of `U8` (blstrs serializes the pubkey through a `u8` tuple, which + // `Array` of `U8` (the DPP key serializes through a `u8` tuple, which // platform_value collects as `Array[U8]` — NOT a typed `Bytes` variant). const VALIDATOR_PK_HEX: &str = "85d81dd12c73cca83f7d1bf8b78fadb695e3a2bc21d53b35ff2f74eaa28c6e163c98d3d5f9bb7252b4d836e484c7cc60"; @@ -234,7 +230,7 @@ mod json_convertible_tests { // keyed by ProTxHash hex, hash fields as lowercase hex strings, // sized-int fields preserved. The inner Validator's own // `$formatVersion` tag (now applied) appears alongside its other - // snake_case fields. (`serialization::dashcore::bls_pubkey` additionally + // snake_case fields. (`bls::serde` additionally // has its own dedicated BLS round-trip tests.) assert_eq!( json, diff --git a/packages/rs-dpp/src/core_types/validator_set/v0/mod.rs b/packages/rs-dpp/src/core_types/validator_set/v0/mod.rs index 2384b186b22..b988a0a1f43 100644 --- a/packages/rs-dpp/src/core_types/validator_set/v0/mod.rs +++ b/packages/rs-dpp/src/core_types/validator_set/v0/mod.rs @@ -1,5 +1,4 @@ -use crate::bls_signatures::Bls12381G2Impl; -use crate::bls_signatures::PublicKey as BlsPublicKey; +use crate::bls::PublicKey as BlsPublicKey; use crate::core_types::validator::v0::ValidatorV0; #[cfg(feature = "core-types-serialization")] use bincode::de::BorrowDecoder; @@ -33,14 +32,9 @@ pub struct ValidatorSetV0 { /// The list of masternodes pub members: BTreeMap, /// The threshold quorum public key - // `BlsPublicKey` is a dashcore type, so its serde wrapper lives in - // `serialization::dashcore::bls_pubkey` (now self-sufficient — no upstream - // dependency; accepts hex string or byte sequence through any deserializer). - #[cfg_attr( - feature = "serde-conversion", - serde(with = "crate::serialization::dashcore::bls_pubkey") - )] - pub threshold_public_key: BlsPublicKey, + // Tagged enum buffers can carry either hex strings or byte sequences. + #[cfg_attr(feature = "serde-conversion", serde(with = "crate::bls::serde"))] + pub threshold_public_key: BlsPublicKey, } impl Display for ValidatorSetV0 { @@ -67,7 +61,7 @@ impl Display for ValidatorSetV0 { pro_tx_hash, validator.node_ip )) .join(", "), - hex::encode(self.threshold_public_key.0.to_compressed()) // Assuming BlsPublicKey is a byte array + hex::encode(self.threshold_public_key.to_bytes()) // Assuming BlsPublicKey is a byte array ) } } @@ -91,7 +85,7 @@ impl Encode for ValidatorSetV0 { // Custom encoding for BlsPublicKey if needed // Assuming BlsPublicKey can be serialized to a byte slice - let public_key_bytes = self.threshold_public_key.0.to_compressed(); + let public_key_bytes = self.threshold_public_key.to_bytes(); public_key_bytes.encode(encoder)?; Ok(()) @@ -237,7 +231,7 @@ pub trait ValidatorSetV0Getters { /// Returns the members of the validator set. fn members_owned(self) -> BTreeMap; /// Returns the threshold public key of the validator set. - fn threshold_public_key(&self) -> &BlsPublicKey; + fn threshold_public_key(&self) -> &BlsPublicKey; } /// Trait providing setter methods for `ValidatorSetV0` struct @@ -251,7 +245,7 @@ pub trait ValidatorSetV0Setters { /// Sets the members of the validator set. fn set_members(&mut self, members: BTreeMap); /// Sets the threshold public key of the validator set. - fn set_threshold_public_key(&mut self, threshold_public_key: BlsPublicKey); + fn set_threshold_public_key(&mut self, threshold_public_key: BlsPublicKey); } impl ValidatorSetV0Getters for ValidatorSetV0 { @@ -279,7 +273,7 @@ impl ValidatorSetV0Getters for ValidatorSetV0 { self.members } - fn threshold_public_key(&self) -> &BlsPublicKey { + fn threshold_public_key(&self) -> &BlsPublicKey { &self.threshold_public_key } } @@ -301,7 +295,7 @@ impl ValidatorSetV0Setters for ValidatorSetV0 { self.members = members; } - fn set_threshold_public_key(&mut self, threshold_public_key: BlsPublicKey) { + fn set_threshold_public_key(&mut self, threshold_public_key: BlsPublicKey) { self.threshold_public_key = threshold_public_key; } } @@ -309,7 +303,7 @@ impl ValidatorSetV0Setters for ValidatorSetV0 { #[cfg(test)] mod tests { use super::*; - use crate::bls_signatures::SecretKey; + use crate::bls::SecretKey; use bincode::config; use dashcore::PubkeyHash; use rand::rngs::StdRng; @@ -326,7 +320,7 @@ mod tests { // Create a sample ProTxHash and ValidatorV0 instance let pro_tx_hash = ProTxHash::from_slice(&[2; 32]).unwrap(); let mut rng = StdRng::seed_from_u64(0); - let public_key = Some(SecretKey::::random(&mut rng).public_key()); + let public_key = Some(SecretKey::random(&mut rng).public_key()); let node_ip = "192.168.1.1".to_string(); let node_id = PubkeyHash::from_slice(&[4; 20]).unwrap(); let validator = ValidatorV0 { @@ -345,7 +339,7 @@ mod tests { members.insert(pro_tx_hash, validator); // Create a sample threshold public key - let threshold_public_key = SecretKey::::random(&mut rng).public_key(); + let threshold_public_key = SecretKey::random(&mut rng).public_key(); // Create the ValidatorSetV0 instance let validator_set = ValidatorSetV0 { diff --git a/packages/rs-dpp/src/errors/protocol_error.rs b/packages/rs-dpp/src/errors/protocol_error.rs index 790b4ff887d..1cc5106db22 100644 --- a/packages/rs-dpp/src/errors/protocol_error.rs +++ b/packages/rs-dpp/src/errors/protocol_error.rs @@ -290,7 +290,7 @@ pub enum ProtocolError { /// BLS signature error #[cfg(feature = "bls-signatures")] #[error(transparent)] - BlsError(#[from] crate::bls_signatures::BlsError), + BlsError(#[from] crate::bls::BlsError), #[error("Private key wrong size: expected 32, got {got}")] PrivateKeySizeError { got: u32 }, diff --git a/packages/rs-dpp/src/identity/identity_public_key/key_type.rs b/packages/rs-dpp/src/identity/identity_public_key/key_type.rs index 78ff0224abe..260fc6b3a9d 100644 --- a/packages/rs-dpp/src/identity/identity_public_key/key_type.rs +++ b/packages/rs-dpp/src/identity/identity_public_key/key_type.rs @@ -12,7 +12,7 @@ use itertools::Itertools; use lazy_static::lazy_static; #[cfg(feature = "bls-signatures")] -use crate::bls_signatures::{self as bls_signatures, Bls12381G2Impl, BlsError}; +use crate::bls::{self, BlsError}; use crate::fee::Credits; use crate::version::PlatformVersion; use crate::ProtocolError; @@ -168,8 +168,8 @@ impl KeyType { private_key.public_key().to_bytes() } KeyType::BLS12_381 => { - let private_key = bls_signatures::SecretKey::::random(rng); - private_key.public_key().0.to_compressed().to_vec() + let private_key = bls::SecretKey::random(rng); + private_key.public_key().to_bytes().to_vec() } KeyType::ECDSA_HASH160 | KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => { (0..self.default_size()).map(|_| rng.gen::()).collect() @@ -217,18 +217,15 @@ impl KeyType { KeyType::BLS12_381 => { #[cfg(feature = "bls-signatures")] { - let private_key: Option> = - bls_signatures::SecretKey::::from_be_bytes( - private_key_bytes, - ) - .into(); + let private_key: Option = + bls::SecretKey::from_be_bytes(private_key_bytes); if private_key.is_none() { return Err(ProtocolError::BlsError(BlsError::DeserializationError( "private key bytes not a valid secret key".to_string(), ))); } let private_key = private_key.expect("expected private key"); - let public_key_bytes = private_key.public_key().0.to_compressed().to_vec(); + let public_key_bytes = private_key.public_key().to_bytes().to_vec(); Ok(public_key_bytes) } #[cfg(not(feature = "bls-signatures"))] @@ -277,9 +274,9 @@ impl KeyType { ) } KeyType::BLS12_381 => { - let private_key = crate::bls_signatures::SecretKey::::random(rng); - let public_key_bytes = private_key.public_key().0.to_compressed().to_vec(); - (public_key_bytes, private_key.0.to_be_bytes()) + let private_key = crate::bls::SecretKey::random(rng); + let public_key_bytes = private_key.public_key().to_bytes().to_vec(); + (public_key_bytes, private_key.to_be_bytes()) } KeyType::ECDSA_HASH160 => { let mut seed = [0u8; 32]; diff --git a/packages/rs-dpp/src/identity/identity_public_key/v0/methods/mod.rs b/packages/rs-dpp/src/identity/identity_public_key/v0/methods/mod.rs index cee0986d364..2f7034433b9 100644 --- a/packages/rs-dpp/src/identity/identity_public_key/v0/methods/mod.rs +++ b/packages/rs-dpp/src/identity/identity_public_key/v0/methods/mod.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "bls-signatures")] +use crate::bls; #[cfg(feature = "ed25519-dalek")] use crate::ed25519_dalek; use crate::identity::identity_public_key::methods::hash::IdentityPublicKeyHashMethodsV0; @@ -10,8 +12,6 @@ use dashcore::hashes::Hash; use dashcore::secp256k1::SecretKey; use dashcore::{Network, PublicKey as ECDSAPublicKey}; use platform_value::{BinaryData, Bytes20}; -#[cfg(feature = "bls-signatures")] -use {crate::bls_signatures, crate::bls_signatures::Bls12381G2Impl}; impl IdentityPublicKeyHashMethodsV0 for IdentityPublicKeyV0 { /// Get the original public key hash fn public_key_hash(&self) -> Result<[u8; 20], ProtocolError> { @@ -93,15 +93,14 @@ pub(in crate::identity::identity_public_key) fn validate_private_key_bytes_for_k KeyType::BLS12_381 => { #[cfg(feature = "bls-signatures")] { - let private_key: Option> = - bls_signatures::SecretKey::::from_be_bytes(private_key_bytes) - .into(); + let private_key: Option = + bls::SecretKey::from_be_bytes(private_key_bytes); if private_key.is_none() { return Ok(false); } let private_key = private_key.expect("expected private key"); - Ok(private_key.public_key().0.to_compressed() == data.as_slice()) + Ok(private_key.public_key().to_bytes() == data.as_slice()) } #[cfg(not(feature = "bls-signatures"))] return Err(ProtocolError::NotSupported( @@ -143,7 +142,7 @@ pub(in crate::identity::identity_public_key) fn validate_private_key_bytes_for_k #[cfg(test)] mod tests { use super::*; - use crate::bls_signatures::{Bls12381G2Impl, Pairing, Signature, SignatureSchemes}; + use crate::bls::Signature; use crate::identity::{Purpose, SecurityLevel}; use dashcore::Network; use dpp::version::PlatformVersion; @@ -157,10 +156,9 @@ mod tests { .random_public_and_private_key_data(&mut rng, PlatformVersion::latest()) .expect("expected to get keys"); let decoded_secret_key = - crate::bls_signatures::SecretKey::::from_be_bytes(&secret_key) - .expect("expected to get secret key"); + crate::bls::SecretKey::from_be_bytes(&secret_key).expect("expected to get secret key"); let public_key = decoded_secret_key.public_key(); - let decoded_public_key_data = public_key.0.to_compressed(); + let decoded_public_key_data = public_key.to_bytes(); assert_eq!( public_key_data.as_slice(), decoded_public_key_data.as_slice() @@ -174,18 +172,13 @@ mod tests { .random_public_and_private_key_data(&mut rng, PlatformVersion::latest()) .expect("expected to get keys"); let decoded_secret_key = - crate::bls_signatures::SecretKey::::from_be_bytes(&secret_key) - .expect("expected to get secret key"); - let signature = decoded_secret_key - .sign(SignatureSchemes::Basic, b"hello") - .expect("expected to sign"); - let compressed = signature.as_raw_value().to_compressed(); - let g2 = ::Signature::from_compressed(&compressed) - .expect("G2 projective"); - let decoded_signature = Signature::::Basic(g2); + crate::bls::SecretKey::from_be_bytes(&secret_key).expect("expected to get secret key"); + let signature = decoded_secret_key.sign(b"hello").expect("expected to sign"); + let compressed = signature.to_bytes(); + let decoded_signature = Signature::from_compressed(&compressed).expect("valid signature"); assert_eq!( compressed.as_slice(), - decoded_signature.as_raw_value().to_compressed().as_slice() + decoded_signature.to_bytes().as_slice() ) } diff --git a/packages/rs-dpp/src/lib.rs b/packages/rs-dpp/src/lib.rs index 68ca1a36084..2a7ab144989 100644 --- a/packages/rs-dpp/src/lib.rs +++ b/packages/rs-dpp/src/lib.rs @@ -48,7 +48,7 @@ pub mod validation; #[cfg(feature = "client")] pub mod dash_platform_protocol; -mod bls; +pub mod bls; #[cfg(feature = "fixtures-and-mocks")] pub mod tests; @@ -86,7 +86,9 @@ pub mod withdrawal; pub use async_trait; -pub use bls::*; +#[cfg(feature = "bls-signatures")] +pub use bls::native_bls; +pub use bls::BlsModule; pub mod prelude { @@ -141,8 +143,6 @@ pub mod prelude { pub use bincode; pub use bincode::enc::Encode; -#[cfg(feature = "bls-signatures")] -pub use blsful as bls_signatures; #[cfg(feature = "data-contracts")] pub use data_contracts; #[cfg(feature = "ed25519-dalek")] diff --git a/packages/rs-dpp/src/serialization/dashcore/bls_pubkey.rs b/packages/rs-dpp/src/serialization/dashcore/bls_pubkey.rs deleted file mode 100644 index ccc6d8f852a..00000000000 --- a/packages/rs-dpp/src/serialization/dashcore/bls_pubkey.rs +++ /dev/null @@ -1,273 +0,0 @@ -//! Local serde wrapper for `BlsPublicKey` that tolerates -//! owned-string sources (`serde_json::Value`, `platform_value::Value`, and -//! anything routed through serde's `ContentDeserializer` for tagged-enum -//! buffering). -//! -//! ## Why this exists -//! -//! Upstream `blstrs_plus` 0.8.18 (`src/serde_impl.rs:119`) implements the -//! human-readable deserialize path as: -//! -//! ```ignore -//! if d.is_human_readable() { -//! let hex_str = <&str>::deserialize(d)?; // borrowed-only -//! ... -//! } -//! ``` -//! -//! `<&str>::deserialize` only succeeds when the deserializer's visitor -//! receives `visit_borrowed_str` — which `serde_json::from_slice` / -//! `serde_json::from_str` provide, but `serde_json::from_value`, -//! `platform_value::from_value`, and `ContentDeserializer` do **not** (they -//! produce owned `String`). Round-tripping a `BlsPublicKey` through any -//! `Value` representation therefore fails with -//! `"invalid type: string ..., expected a borrowed string"`. -//! -//! This is technically a serde compatibility quirk rather than a single -//! crate's bug — but the leaf type is the only place to patch. See plan -//! §10b "Common pattern: serde's `ContentDeserializer` HR-quirk" for -//! the broader narrative. -//! -//! ## How the workaround works -//! -//! A single `deserialize_any` visitor accepts BOTH wire forms upstream emits: -//! the 96-char hex string (`visit_str`) and the 48-byte compressed-G1 form as -//! raw bytes or a `u8` sequence (`visit_bytes` / `visit_seq`). Either way it -//! hex/byte-decodes to the compressed-G1 representation, lifts it to -//! `G1Projective` via `to_curve`, and wraps into `PublicKey` -//! directly (the inner field is `pub`). -//! -//! Driving it via `deserialize_any` (rather than branching on -//! `is_human_readable()`) is what makes it robust to serde's internal-tag -//! `Content` buffer: that buffer's `is_human_readable()` does not reliably -//! match the original deserializer, so a `Value` (non-HR) pubkey could arrive -//! as a byte *sequence* while the old HR branch expected a *string* — which is -//! exactly why the `ValidatorSet` value round-trip test used to be `#[ignore]`d. -//! Accepting both shapes in one visitor sidesteps that, and also sidesteps the -//! upstream borrowed-only `<&str>::deserialize` HR path. Both serde_json and -//! platform_value are self-describing, so `deserialize_any` is safe; bincode -//! never reaches here (it goes through the separate derived `Decode`). -//! -//! Note: `BlsPublicKey` carries a public key on the G1 curve -//! (the `Bls12381G2Impl` name refers to where signatures live, not keys). -//! Compressed G1 = 48 bytes = 96 hex chars. -//! -//! ## When to remove this -//! -//! This wrapper is now self-sufficient (no behavioral dependency on an upstream -//! fix). Once upstream `blstrs_plus` accepts owned strings AND a byte-sequence -//! HR form on its own `Deserialize`, this wrapper and the `serde(with = ...)` -//! annotations on `Validator::public_key` / `ValidatorSetV0::threshold_public_key` -//! can simply be dropped. - -use crate::bls_signatures::inner_types::{G1Affine, GroupEncoding, PrimeCurveAffine}; -use crate::bls_signatures::{Bls12381G2Impl, PublicKey as BlsPublicKey}; -use serde::de::Visitor; -use serde::{Deserializer, Serialize, Serializer}; -use std::fmt; - -/// Compressed-G1 wire size for BLS12-381 (where the public key lives in -/// `Bls12381G2Impl`). -const COMPRESSED_G1_LEN: usize = 48; - -pub fn serialize( - pk: &BlsPublicKey, - serializer: S, -) -> Result { - // Upstream serialize already produces a hex string in HR and a byte tuple - // in non-HR; both are correct on the wire. Nothing to override here. - pk.serialize(serializer) -} - -pub fn deserialize<'de, D: Deserializer<'de>>( - deserializer: D, -) -> Result, D::Error> { - // One visitor that accepts BOTH wire forms upstream emits: the - // human-readable 96-char hex string, and the non-HR 48-byte compressed-G1 - // form (as `bytes` or a `u8` sequence). Driving it via `deserialize_any` - // makes it robust to serde's internal-tag `Content` buffer, whose - // `is_human_readable()` does not always match the original deserializer — - // the bug that previously forced the ValidatorSet value round-trip test to - // be `#[ignore]`d (the non-HR bytes arrived while the old code's HR branch - // expected a string → "invalid type: sequence, expected a string"). It also - // sidesteps the upstream `<&str>::deserialize` borrowed-only HR path (the - // original reason this wrapper exists). Both serde_json and platform_value - // are self-describing, so `deserialize_any` is safe; bincode never reaches - // here (it goes through the separate derived `Decode`). - deserializer.deserialize_any(BlsPublicKeyVisitor) -} - -fn from_compressed_g1_bytes( - bytes: &[u8], -) -> Result, E> { - if bytes.len() != COMPRESSED_G1_LEN { - return Err(E::custom(format!( - "expected {COMPRESSED_G1_LEN} compressed-G1 bytes for public key, got {}", - bytes.len() - ))); - } - let mut compressed = ::Repr::default(); - compressed.as_mut().copy_from_slice(bytes); - let affine = Option::::from(G1Affine::from_bytes(&compressed)) - .ok_or_else(|| E::custom("not a valid compressed G1 point"))?; - Ok(BlsPublicKey::(affine.to_curve())) -} - -struct BlsPublicKeyVisitor; - -impl<'de> Visitor<'de> for BlsPublicKeyVisitor { - type Value = BlsPublicKey; - - fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!( - f, - "a {}-char hex string or {} compressed-G1 bytes", - COMPRESSED_G1_LEN * 2, - COMPRESSED_G1_LEN - ) - } - - fn visit_str(self, s: &str) -> Result { - if s.len() != COMPRESSED_G1_LEN * 2 { - return Err(E::custom(format!( - "expected {} hex chars for compressed G1 public key, got {}", - COMPRESSED_G1_LEN * 2, - s.len() - ))); - } - let mut bytes = [0u8; COMPRESSED_G1_LEN]; - for (i, slot) in bytes.iter_mut().enumerate() { - let hi = hex_nibble(s.as_bytes()[i * 2]).map_err(E::custom)?; - let lo = hex_nibble(s.as_bytes()[i * 2 + 1]).map_err(E::custom)?; - *slot = (hi << 4) | lo; - } - from_compressed_g1_bytes(&bytes) - } - - fn visit_bytes(self, v: &[u8]) -> Result { - from_compressed_g1_bytes(v) - } - - fn visit_seq>(self, mut seq: A) -> Result { - let mut bytes = Vec::with_capacity(COMPRESSED_G1_LEN); - while let Some(b) = seq.next_element::()? { - // A valid compressed-G1 public key is exactly COMPRESSED_G1_LEN - // bytes; reject as soon as a hostile payload exceeds that rather - // than allocating/parsing an arbitrarily long sequence first. - if bytes.len() == COMPRESSED_G1_LEN { - return Err(serde::de::Error::invalid_length(bytes.len() + 1, &self)); - } - bytes.push(b); - } - from_compressed_g1_bytes(&bytes) - } -} - -fn hex_nibble(c: u8) -> Result { - match c { - b'0'..=b'9' => Ok(c - b'0'), - b'a'..=b'f' => Ok(c - b'a' + 10), - b'A'..=b'F' => Ok(c - b'A' + 10), - _ => Err("invalid hex character in compressed G1 public key"), - } -} - -/// `Option>` variant for fields like -/// `Validator::public_key`. -pub mod option { - use super::*; - - pub fn serialize( - opt: &Option>, - serializer: S, - ) -> Result { - // Option's built-in Serialize delegates to T's Serialize, which - // is the upstream BlsPublicKey impl — already correct. - opt.serialize(serializer) - } - - pub fn deserialize<'de, D: Deserializer<'de>>( - deserializer: D, - ) -> Result>, D::Error> { - struct OptionVisitor; - - impl<'de> Visitor<'de> for OptionVisitor { - type Value = Option>; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("Option>") - } - - fn visit_none(self) -> Result { - Ok(None) - } - - fn visit_unit(self) -> Result { - Ok(None) - } - - fn visit_some>( - self, - inner: D2, - ) -> Result { - super::deserialize(inner).map(Some) - } - } - - deserializer.deserialize_option(OptionVisitor) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use serde::{Deserialize, Serialize}; - use serde_json::json; - - // A known-valid compressed-G1 BLS public key (deterministic — the - // ValidatorSet fixture's seeded StdRng(42) threshold key). - const PK_HEX: &str = - "969c5d5873f49aa994c5f6a850924ca1840c4ad1791aaaecd90093d4a5c0c3799f2d98540f5366cfa0a33f143fd69263"; - - // Newtypes that drive the `with` module(s) through serde. - #[derive(Serialize, Deserialize)] - struct Wrap(#[serde(with = "super")] BlsPublicKey); - - #[derive(Serialize, Deserialize)] - struct OptWrap(#[serde(with = "super::option")] Option>); - - #[test] - fn json_hr_round_trip_is_the_hex_string() { - // Human-readable (serde_json::Value): hex in, identical hex out (visit_str). - let pk: Wrap = serde_json::from_value(json!(PK_HEX)).expect("from hex"); - assert_eq!(serde_json::to_value(&pk).expect("to json"), json!(PK_HEX)); - } - - #[test] - fn json_borrowed_str_path_works() { - // `serde_json::from_str` yields borrowed strings — the path upstream's - // `<&str>::deserialize` handled but `from_value`/Content did not. Our - // `visit_str` takes `&str`, so both work. - let pk: Wrap = serde_json::from_str(&format!("\"{PK_HEX}\"")).expect("from_str"); - assert_eq!(serde_json::to_value(&pk).expect("to json"), json!(PK_HEX)); - } - - #[test] - fn value_non_hr_byte_seq_round_trip() { - // Non-HR `platform_value` serializes the key as a 48-byte sequence; the - // `visit_seq` path (the bug the un-ignored ValidatorSet test exposed) - // must reconstruct it. Re-serialize to JSON to confirm the same key. - let pk: Wrap = serde_json::from_value(json!(PK_HEX)).expect("from hex"); - let value = platform_value::to_value(&pk).expect("to value"); - let pk2: Wrap = platform_value::from_value(value).expect("from value seq"); - assert_eq!(serde_json::to_value(&pk2).expect("to json"), json!(PK_HEX)); - } - - #[test] - fn option_some_and_none_round_trip() { - let some: OptWrap = serde_json::from_value(json!(PK_HEX)).expect("some"); - assert_eq!(serde_json::to_value(&some).expect("to json"), json!(PK_HEX)); - let none: OptWrap = serde_json::from_value(json!(null)).expect("none"); - assert_eq!(serde_json::to_value(&none).expect("to json"), json!(null)); - } -} diff --git a/packages/rs-dpp/src/serialization/dashcore/mod.rs b/packages/rs-dpp/src/serialization/dashcore/mod.rs index 14b8c22773e..c21d6baa443 100644 --- a/packages/rs-dpp/src/serialization/dashcore/mod.rs +++ b/packages/rs-dpp/src/serialization/dashcore/mod.rs @@ -1,18 +1,6 @@ -//! Serde `with` wrappers for types owned by external (dashcore) crates. -//! -//! These live here — rather than next to their dpp consumers — because the -//! type being (de)serialized is **not** a dpp type; it belongs to dashcore or -//! the BLS implementation (e.g. `blstrs_plus`). Grouping them under -//! `serialization::dashcore` keeps "serde for out-of-crate types" in one place, -//! distinct from dpp-type-local serde (which lives next to its type). +//! Compatibility re-exports for Serde field adapters. -/// Compressed-G1 BLS public key (`BlsPublicKey` from `blsful`'s -/// `blstrs_plus`). Used via `#[serde(with = "crate::serialization::dashcore::bls_pubkey")]`. -/// -/// The wrapped type comes from `crate::bls_signatures`, which is only linked when the -/// `bls-signatures` feature is on. Its only consumers — -/// the `core_types` validator/validator-set structs — are themselves gated behind -/// `core-types`, which requires `bls-signatures`, so gating here keeps the module -/// from breaking builds that omit BLS (e.g. `wasm-drive-verify`). +/// Accept hex strings or byte sequences for Platform BLS public keys. #[cfg(feature = "bls-signatures")] -pub mod bls_pubkey; +#[deprecated(note = "use dpp::bls::serde instead")] +pub use crate::bls::serde as bls_pubkey; diff --git a/packages/rs-dpp/src/serialization/mod.rs b/packages/rs-dpp/src/serialization/mod.rs index b50bf581cb3..a85bf4d5445 100644 --- a/packages/rs-dpp/src/serialization/mod.rs +++ b/packages/rs-dpp/src/serialization/mod.rs @@ -1,3 +1,5 @@ +#[cfg(any(feature = "serde-conversion", feature = "bls-signatures"))] +pub mod dashcore; #[cfg(feature = "json-conversion")] pub mod json; // Not gated behind `serde-conversion`: this fixed-size byte-array helper is @@ -5,8 +7,6 @@ pub mod json; // `block::extended_block_info::ExtendedBlockInfo`), so it must resolve in every // feature configuration. It only depends on `serde` and `base64`, both // non-optional dependencies. -#[cfg(feature = "serde-conversion")] -pub mod dashcore; pub mod serde_bytes; #[cfg(feature = "serde-conversion")] pub mod serde_bytes_var; diff --git a/packages/rs-dpp/src/signing.rs b/packages/rs-dpp/src/signing.rs index ef52bc65136..ba2256dd17b 100644 --- a/packages/rs-dpp/src/signing.rs +++ b/packages/rs-dpp/src/signing.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "bls-signatures")] +use crate::bls::{self, Signature}; use crate::identity::KeyType; use crate::serialization::PlatformMessageSignable; #[cfg(feature = "message-signature-verification")] @@ -10,11 +12,6 @@ use crate::{ #[cfg(feature = "message-signing")] use crate::{BlsModule, ProtocolError}; use dashcore::signer; -#[cfg(feature = "bls-signatures")] -use { - crate::bls_signatures::{self, Signature}, - crate::bls_signatures::{Bls12381G2Impl, Pairing}, -}; impl PlatformMessageSignable for &[u8] { #[cfg(feature = "message-signature-verification")] @@ -44,17 +41,15 @@ impl PlatformMessageSignable for &[u8] { } } KeyType::BLS12_381 => { - let public_key = - match bls_signatures::PublicKey::::try_from(public_key_data) { - Ok(public_key) => public_key, - Err(e) => { - // dbg!(format!("bls public_key could not be recovered")); - return SimpleConsensusValidationResult::new_with_error( - SignatureError::BasicBLSError(BasicBLSError::new(e.to_string())) - .into(), - ); - } - }; + let public_key = match bls::PublicKey::try_from(public_key_data) { + Ok(public_key) => public_key, + Err(e) => { + // dbg!(format!("bls public_key could not be recovered")); + return SimpleConsensusValidationResult::new_with_error( + SignatureError::BasicBLSError(BasicBLSError::new(e.to_string())).into(), + ); + } + }; let signature_bytes: [u8; 96] = match signature.try_into() { Ok(bytes) => bytes, Err(_) => { @@ -67,11 +62,7 @@ impl PlatformMessageSignable for &[u8] { ) } }; - let g2 = match ::Signature::from_compressed( - &signature_bytes, - ) - .into_option() - { + let signature = match Signature::from_compressed(&signature_bytes) { Some(g2) => g2, None => { return SimpleConsensusValidationResult::new_with_error( @@ -79,7 +70,6 @@ impl PlatformMessageSignable for &[u8] { ); } }; - let signature = Signature::::Basic(g2); if signature.verify(&public_key, signable_data).is_err() { SimpleConsensusValidationResult::new_with_error( diff --git a/packages/rs-dpp/tests/bls_compatibility.rs b/packages/rs-dpp/tests/bls_compatibility.rs new file mode 100644 index 00000000000..c14ff906f7b --- /dev/null +++ b/packages/rs-dpp/tests/bls_compatibility.rs @@ -0,0 +1,388 @@ +use dash_pkc::bls::{BlsPublicKey, BlsScChia, BlsScIetf, BlsSecretKey, BlsSignature}; +use dpp::bls::{PublicKey, SecretKey, Signature}; +use dpp::core_types::validator::v0::ValidatorV0; +use dpp::core_types::validator_set::v0::ValidatorSetV0; +use dpp::native_bls::NativeBlsModule; +use dpp::{BlsModule, ProtocolError}; +use serde::Deserialize; + +#[derive(Deserialize)] +struct Corpus { + key_generation: Vec, + basic: Vec, + secure_aggregation: Vec, + scalars: Vec, + storage: Vec, +} + +#[derive(Deserialize)] +struct KeyGeneration { + ikm: String, + secret_key: String, + public_key: String, +} + +#[derive(Deserialize)] +struct Basic { + secret_key: String, + message: String, + public_key: String, + legacy_public_key: String, + signature: String, +} + +#[derive(Deserialize)] +struct Aggregate { + secret_keys: Vec, + message: String, + public_keys: Vec, + signature: String, +} + +#[derive(Deserialize)] +struct Scalar { + name: String, + input: String, + normalized: Option, + public_key: Option, + signature: Option, +} + +#[derive(Deserialize)] +struct Storage { + name: String, + validator: serde_json::Value, + validator_bytes: String, + validator_set: serde_json::Value, + validator_set_bytes: String, +} + +fn corpus() -> Corpus { + serde_json::from_str(include_str!("fixtures/bls_compatibility/vectors.json")).unwrap() +} + +fn bytes(hex: &str) -> [u8; N] { + hex::decode(hex).unwrap().try_into().unwrap() +} + +#[test] +fn should_preserve_key_generation_from_fixed_material() { + let vectors = corpus().key_generation; + assert_eq!(vectors.len(), 3); + for v in vectors { + let ikm = bytes::<32>(&v.ikm); + let current = SecretKey::from_ikm(&ikm).unwrap(); + let candidate = BlsSecretKey::::from_ikm(&ikm).unwrap(); + assert_eq!(current.to_be_bytes(), bytes::<32>(&v.secret_key)); + assert_eq!(*candidate.to_bytes(), current.to_be_bytes()); + assert_eq!(current.public_key().to_bytes(), bytes::<48>(&v.public_key)); + assert_eq!( + candidate.public_key().to_bytes(), + bytes::<48>(&v.public_key) + ); + } +} + +#[test] +fn should_match_frozen_basic_signatures_in_both_backends() { + let vectors = corpus().basic; + assert_eq!(vectors.len(), 16); + for v in vectors { + let secret = bytes(&v.secret_key); + let message = hex::decode(v.message).unwrap(); + let public: [u8; 48] = bytes(&v.public_key); + let signature: [u8; 96] = bytes(&v.signature); + assert_eq!( + NativeBlsModule.private_key_to_public_key(&secret).unwrap(), + public + ); + assert_eq!(NativeBlsModule.sign(&message, &secret).unwrap(), signature); + assert!(NativeBlsModule + .verify_signature(&signature, &message, &public) + .unwrap()); + let candidate = BlsSecretKey::::from_bytes(&secret).unwrap(); + assert_eq!(candidate.public_key().to_bytes(), public); + assert_eq!(candidate.sign(&message).to_bytes(), signature); + let decoded = BlsSignature::::from_bytes(&signature).unwrap(); + assert!(candidate.public_key().verify(&message, &decoded).is_ok()); + assert_eq!( + PublicKey::try_from(public.as_slice()) + .unwrap() + .to_legacy_bytes() + .unwrap(), + bytes::<48>(&v.legacy_public_key) + ); + assert_eq!( + SecretKey::from_be_bytes(&secret) + .unwrap() + .public_key() + .to_legacy_bytes() + .unwrap(), + bytes::<48>(&v.legacy_public_key) + ); + assert_eq!( + candidate + .public_key() + .to_scheme::() + .unwrap() + .to_bytes(), + bytes::<48>(&v.legacy_public_key) + ); + let mut wrong_message = message.clone(); + wrong_message.push(1); + assert!(!NativeBlsModule + .verify_signature(&signature, &wrong_message, &public) + .unwrap()); + assert!(candidate + .public_key() + .verify(&wrong_message, &decoded) + .is_err()); + let mut other_secret = [0; 32]; + other_secret[31] = 3; + let other = BlsSecretKey::::from_bytes(&other_secret) + .unwrap() + .public_key(); + assert!(!NativeBlsModule + .verify_signature(&signature, &message, &other.to_bytes()) + .unwrap()); + assert!(other.verify(&message, &decoded).is_err()); + } +} + +#[test] +fn should_match_frozen_secure_aggregates_in_both_backends() { + let vectors = corpus().secure_aggregation; + assert_eq!(vectors.len(), 5); + for v in vectors { + let message = hex::decode(v.message).unwrap(); + let public_keys: Vec<_> = v + .public_keys + .iter() + .map(|key| PublicKey::try_from(bytes::<48>(key).as_slice()).unwrap()) + .collect(); + let signatures: Vec<_> = v + .secret_keys + .iter() + .map(|key| { + SecretKey::from_be_bytes(&bytes(key)) + .unwrap() + .sign(&message) + .unwrap() + }) + .collect(); + let point = Signature::aggregate_secure(&signatures, &public_keys).unwrap(); + assert_eq!(point.to_bytes(), bytes::<96>(&v.signature)); + assert!(point.verify_secure(&public_keys, &message).is_ok()); + let candidate_keys: Vec<_> = v + .secret_keys + .iter() + .map(|key| BlsSecretKey::::from_bytes(&bytes(key)).unwrap()) + .collect(); + let pks: Vec<_> = candidate_keys.iter().map(|key| key.public_key()).collect(); + let sigs: Vec<_> = candidate_keys + .iter() + .map(|key| key.sign(&message)) + .collect(); + let refs: Vec<_> = pks.iter().collect(); + let candidate = + BlsSignature::secure_aggregate(&sigs.iter().collect::>(), &refs).unwrap(); + assert_eq!(candidate.to_bytes(), bytes::<96>(&v.signature)); + assert!(candidate.secure_verify_aggregates(&message, &refs).is_ok()); + let mut wrong_message = message; + wrong_message.push(1); + assert!(point.verify_secure(&public_keys, &wrong_message).is_err()); + assert!(candidate + .secure_verify_aggregates(&wrong_message, &refs) + .is_err()); + } +} + +#[test] +fn should_preserve_historical_scalar_reduction() { + let vectors = corpus().scalars; + assert_eq!(vectors.len(), 4); + for v in vectors { + let input = bytes(&v.input); + let key = SecretKey::from_be_bytes(&input); + assert_eq!( + key.as_ref().map(|key| hex::encode(key.to_be_bytes())), + v.normalized, + "{}", + v.name + ); + // The strict upstream constructor cannot replace the historical DPP parser directly. + assert!( + BlsSecretKey::::from_bytes(&input).is_err(), + "{}", + v.name + ); + if let Some(normalized) = v.normalized { + assert_eq!( + hex::encode(NativeBlsModule.private_key_to_public_key(&input).unwrap()), + v.public_key.unwrap() + ); + assert_eq!( + hex::encode(NativeBlsModule.sign(b"scalar boundary", &input).unwrap()), + v.signature.unwrap() + ); + let reduced = dash_pkc::bls::Fr::from_bendian_reduce(&input).unwrap(); + let candidate = BlsSecretKey::::try_from(reduced).unwrap(); + assert_eq!(*candidate.to_bytes(), bytes::<32>(&normalized)); + } else { + assert!(matches!( + NativeBlsModule.sign(b"scalar boundary", &input), + Err(ProtocolError::InvalidBLSPrivateKeyError(_)) + )); + assert!(matches!( + NativeBlsModule.private_key_to_public_key(&input), + Err(ProtocolError::InvalidBLSPrivateKeyError(_)) + )); + } + } +} + +#[test] +fn should_preserve_identity_key_parsing_without_accepting_identity_signatures() { + let mut public_key = [0; 48]; + public_key[0] = 0xc0; + let mut signature = [0; 96]; + signature[0] = 0xc0; + // This is historical parsing behavior, not permission to verify an identity signature. + assert!(NativeBlsModule.validate_public_key(&public_key).is_ok()); + assert!(BlsPublicKey::::from_bytes(&public_key).is_err()); + assert!(!NativeBlsModule + .verify_signature(&signature, b"identity", &public_key) + .unwrap()); + let valid = bytes::<48>(&corpus().basic[0].public_key); + assert!(!NativeBlsModule + .verify_signature(&signature, b"identity", &valid) + .unwrap()); + + let identity_key = PublicKey::try_from(public_key.as_slice()).unwrap(); + let identity_signature = Signature::from_compressed(&signature).unwrap(); + assert_eq!(identity_key, PublicKey::default()); + assert_eq!(identity_key.to_bytes(), public_key); + assert_eq!(identity_key.to_legacy_bytes().unwrap(), public_key); + assert_eq!(identity_signature.to_bytes(), signature); + + let v = &corpus().basic[0]; + let message = hex::decode(&v.message).unwrap(); + let valid_key = PublicKey::try_from(valid.as_slice()).unwrap(); + let valid_signature = Signature::from_compressed(&bytes(&v.signature)).unwrap(); + assert!(valid_signature.verify(&identity_key, &message).is_err()); + assert!(identity_signature.verify(&valid_key, &message).is_err()); + assert!(valid_signature + .verify_secure(&[identity_key], &message) + .is_err()); + assert!(identity_signature + .verify_secure(&[valid_key], &message) + .is_err()); + assert!(Signature::aggregate_secure(&[identity_signature], &[valid_key]).is_err()); + assert!(Signature::aggregate_secure(&[valid_signature], &[identity_key]).is_err()); +} + +#[test] +fn should_preserve_invalid_input_results_and_validation_order() { + let v = &corpus().basic[0]; + let public_key = bytes::<48>(&v.public_key); + let secret = bytes::<32>(&v.secret_key); + for len in [0, 31, 33] { + assert!( + matches!(NativeBlsModule.sign(b"", &vec![1;len]), Err(ProtocolError::PrivateKeySizeError { got }) if got == len as u32) + ); + assert!( + matches!(NativeBlsModule.private_key_to_public_key(&vec![1;len]), Err(ProtocolError::PrivateKeySizeError { got }) if got == len as u32) + ); + } + for len in [0, 95, 97] { + assert!( + matches!(NativeBlsModule.verify_signature(&vec![0;len], b"", &public_key), Err(ProtocolError::BlsSignatureSizeError { got }) if got == len as u32) + ); + assert!(matches!( + NativeBlsModule.verify_signature(&vec![0; len], b"", &[0; 48]), + Err(ProtocolError::BlsError(_)) + )); + } + for public in [vec![], vec![0; 47], vec![0; 48], vec![255; 48], vec![0; 49]] { + assert!(NativeBlsModule.validate_public_key(&public).is_err()); + assert!(matches!( + NativeBlsModule.verify_signature(&[0; 96], b"", &public), + Err(ProtocolError::BlsError(_)) + )); + } + for signature in [[0; 96], [255; 96]] { + assert!(!NativeBlsModule + .verify_signature(&signature, b"", &public_key) + .unwrap()); + assert!(BlsSignature::::from_bytes(&signature).is_err()); + } + assert!(NativeBlsModule.sign(b"", &secret).is_ok()); +} + +#[test] +fn should_reject_points_outside_the_prime_order_subgroup() { + // On-curve points x=4 in G1 and x=(2,0) in G2, in compressed IETF encoding. + let mut public_key = [0; 48]; + public_key[0] = 0x80; + public_key[47] = 4; + let mut signature = [0; 96]; + signature[0] = 0xa0; + signature[95] = 2; + assert!(NativeBlsModule.validate_public_key(&public_key).is_err()); + assert!(BlsPublicKey::::from_bytes(&public_key).is_err()); + assert!(Signature::from_compressed(&signature).is_none()); + let valid = bytes::<48>(&corpus().basic[0].public_key); + assert!(!NativeBlsModule + .verify_signature(&signature, b"", &valid) + .unwrap()); + assert!(BlsSignature::::from_bytes(&signature).is_err()); +} + +#[test] +fn should_read_and_reemit_frozen_validator_storage() { + let config = bincode::config::standard().with_big_endian(); + let vectors = corpus().storage; + assert_eq!(vectors.len(), 3); + for v in vectors { + let encoded = hex::decode(v.validator_bytes).unwrap(); + let (validator, consumed): (ValidatorV0, _) = + bincode::decode_from_slice(&encoded, config).unwrap(); + assert_eq!(consumed, encoded.len(), "{}", v.name); + assert_eq!(bincode::encode_to_vec(&validator, config).unwrap(), encoded); + assert_eq!(serde_json::to_value(&validator).unwrap(), v.validator); + if let Some(key) = &validator.public_key { + // QuorumForSavingV1 uses bincode(with_serde): exactly 48 bytes, with no length prefix. + let bytes = bincode::serde::encode_to_vec(key, config).unwrap(); + assert_eq!(bytes, key.to_bytes()); + let decoded = + bincode::serde::decode_from_slice::(&bytes, config).unwrap(); + assert_eq!(decoded, (*key, 48)); + } + assert_eq!( + serde_json::from_value::(v.validator.clone()).unwrap(), + validator + ); + assert_eq!( + bincode::decode_from_slice_untrusted::(&encoded, config) + .unwrap() + .0, + validator + ); + assert_eq!( + bincode::borrow_decode_from_slice_untrusted::(&encoded, config) + .unwrap() + .0, + validator + ); + let encoded = hex::decode(v.validator_set_bytes).unwrap(); + let (set, consumed): (ValidatorSetV0, _) = + bincode::decode_from_slice(&encoded, config).unwrap(); + assert_eq!(consumed, encoded.len()); + assert_eq!(bincode::encode_to_vec(&set, config).unwrap(), encoded); + assert_eq!(serde_json::to_value(&set).unwrap(), v.validator_set); + assert_eq!( + serde_json::from_value::(v.validator_set).unwrap(), + set + ); + } +} diff --git a/packages/rs-dpp/tests/fixtures/bls_compatibility/README.md b/packages/rs-dpp/tests/fixtures/bls_compatibility/README.md new file mode 100644 index 00000000000..752f676c763 --- /dev/null +++ b/packages/rs-dpp/tests/fixtures/bls_compatibility/README.md @@ -0,0 +1,37 @@ +# BLS compatibility vectors + +These public test keys and fixtures were generated before the backend migration, +from Platform `c0b4b97f8a1a8626c2f51bc8d7baeab9c65e10da` (#5307): + +- `dashpay/agora-blsful` at `0c34a7a488a0bd1c9a9a2196e793b303ad35c900`; +- `blstrs_plus` 0.8.18; +- `blst` at `71a00877c75a482e7d44c4e6370c1a7fb82444be` (0.3.12); +- candidate: `dash-pkc` at `e6402ced257c370a586ade9840ebbf545a4b7926`, + the backend used by rust-dashcore `314f10600e35311f3d875a6e8740476ab6c79c0c`. + +`vectors.json` contains 16 Basic signatures (four canonical scalars × empty, +text, zero digest and 256-byte messages), five secure aggregates (one/two/three +signers, reordered keys and a duplicate key), three key-generation cases, four +scalar boundaries, and three validator/validator-set storage cases. Storage uses +`grovedb-bincode` 2.1.0 with `standard().with_big_endian()` and DPP's JSON schema. + +The scalar and infinity cases characterize **historical compatibility**, not +recommended validation for a new protocol. DPP accepts nonzero scalars reduced +modulo the group order and permits an identity public key to deserialize; an +identity signature still fails verification. Replacing those rules requires a +separate protocol decision. The strict dash-pkc key constructors differ here. + +The frozen expected values come from the original DPP backend listed above. +The generator calls the current DPP implementation; after a backend change, +compare its output with the frozen fixture rather than replacing the fixture. +To inspect its output, run: + +```sh +cargo run -p dpp --example generate_bls_compatibility_vectors --locked +cargo test -p dpp --test bls_compatibility --locked +``` + +Keep the frozen fixture unchanged when migrating the implementation. These tests +cover the listed cases, not an exhaustive proof of equivalence or every persisted +Platform structure. They do not test legacy signature hashing; the legacy field +is the public-key encoding of the same point. diff --git a/packages/rs-dpp/tests/fixtures/bls_compatibility/vectors.json b/packages/rs-dpp/tests/fixtures/bls_compatibility/vectors.json new file mode 100644 index 00000000000..f6c1d09c051 --- /dev/null +++ b/packages/rs-dpp/tests/fixtures/bls_compatibility/vectors.json @@ -0,0 +1,328 @@ +{ + "basic": [ + { + "secret_key": "0000000000000000000000000000000000000000000000000000000000000001", + "message": "", + "public_key": "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "legacy_public_key": "17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "signature": "a8aab303e33ed14f4a904004a92bd26ffc969c1d1e7d4b7f0c04150a73e1845a911e51a2b2d369d5cef06560c5ac9f5715c01566993d4469805df3e1f29b536481a832bf2751b6908faed6776d062d585521889232999d72b679d6e38bb5cfff" + }, + { + "secret_key": "0000000000000000000000000000000000000000000000000000000000000001", + "message": "4461736820506c6174666f726d20424c5320636f6d7061746962696c697479", + "public_key": "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "legacy_public_key": "17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "signature": "885957c480db7a90ba694ab40ea93e30e7e74c95e4ca3be0a67cda09e6f29b0d3b82d4bf9f56d6d6c2d410ed57f060b812569fac5b13555b4bb799ee33b5efb3024480e84d68962ff5db13b156d05f65d56fceec528f8bb8f1d740484d7a72e0" + }, + { + "secret_key": "0000000000000000000000000000000000000000000000000000000000000001", + "message": "0000000000000000000000000000000000000000000000000000000000000000", + "public_key": "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "legacy_public_key": "17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "signature": "b72f4d8dfacf216719bcebb8349312f6bce892587291ee672fc0c7b6d16f427002713a961d122633e2deda35e803e9c302b3fcbc9d36fbb5fdb644bd41ce296399a5ea115f0c6cd612ff7e8b08477993cf1a2412fd81bc07e7ecc7380cfd2876" + }, + { + "secret_key": "0000000000000000000000000000000000000000000000000000000000000001", + "message": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + "public_key": "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "legacy_public_key": "17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "signature": "8a527aa6d18e63de70f31c512406217224c99f5a087fd7bcea48737d40ab99bcff2ab6b20b197c1e16efaa9c3dceeb7b01be7de81f980049e69395369a96f994b18f8358f8ce52675dfd8262f69e738eab510a5bba303756955c7a7667b4b477" + }, + { + "secret_key": "0000000000000000000000000000000000000000000000000000000000000002", + "message": "", + "public_key": "a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e", + "legacy_public_key": "8572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e", + "signature": "a17a941aa5c8600ed1558ec999ed9708a164624c01160a4a1f467bbff22df6db13bf07760b59d1c59f90a3d7e518e0100d65cdf14582f3b85176dd7b76960a655479e2d0a187ef3cf334051dbe0aae7fa1a0cf26297d7a221013e55dd0c0741d" + }, + { + "secret_key": "0000000000000000000000000000000000000000000000000000000000000002", + "message": "4461736820506c6174666f726d20424c5320636f6d7061746962696c697479", + "public_key": "a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e", + "legacy_public_key": "8572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e", + "signature": "843df9b31366b4bee0b8cc9bacb9f2905b9fd87ce2df1f6a5bfd77ee0a67f3f851dbae7866aca20d0bdac74c5d1d1042089cb625364a87bafc81c4e9e582fb6d5400e2d350f6da811f35f647402597f8fc28ec4f4fcf1d9c611f4a5044b4b4ca" + }, + { + "secret_key": "0000000000000000000000000000000000000000000000000000000000000002", + "message": "0000000000000000000000000000000000000000000000000000000000000000", + "public_key": "a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e", + "legacy_public_key": "8572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e", + "signature": "8be4ae3fc1c613d49655bcedd5945cc2f4eaf38122f4dfa50f38153b0461425a743b9b3fe8d175d0a41c725bea12ba05035c1c95c85ae567c6f5cd3c6a5975b17a67683dbe2eb70897e9b018e80ac123ec6bb843a614f277851fccc548a2f695" + }, + { + "secret_key": "0000000000000000000000000000000000000000000000000000000000000002", + "message": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + "public_key": "a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e", + "legacy_public_key": "8572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e", + "signature": "ac997b406ecf999caa44ca3a0e2b42ce77bc6782fc8ac02c54e59d723e128f24f0a1f88a71b21f9890a3282a76632aa1061d736d3d502d81a2a9a1c863752a372ae94a64115d12f2ff34362861e4bb768ad533d0c087906485e795abda688f8d" + }, + { + "secret_key": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "message": "", + "public_key": "95fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe48", + "legacy_public_key": "15fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe48", + "signature": "a01aa8ac3da427ac661695badf9bb4ef2031add7eda51556349cc30b75a2a7de800d4b18a36bad836f576f30225f52a40abc60004dd0e6a834caed027bb25c674181debcbe5d072d8f8758be29887a5a99deef10c04afaf3c3cefe65363636b4" + }, + { + "secret_key": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "message": "4461736820506c6174666f726d20424c5320636f6d7061746962696c697479", + "public_key": "95fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe48", + "legacy_public_key": "15fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe48", + "signature": "8b1b6663cd08c760736bd95d4196d6db75a50b87b639228f9fffa5b7bfa0f43ca29f1f4be87b44fd395f8afe3804b85b068cde11d73f3fed9b05b9c9f296a474ac23f437c85c3f1dc973262b19b02c6875ce2bd911d3ebb3b639aaef61f6a3ae" + }, + { + "secret_key": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "message": "0000000000000000000000000000000000000000000000000000000000000000", + "public_key": "95fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe48", + "legacy_public_key": "15fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe48", + "signature": "af47ffcd8490a3d28c82c6b8c050571ed3eef9b561d9889bce097bd1d0cffbde17b6d9e7aa0e1f2415b1f8bb7e07467614c4d45fae16611b45f9e3d763ab2192ed723f3ea01b146631e68f08cbb1c20ac09ef80dbc0145d9c805e4b4b9771acc" + }, + { + "secret_key": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "message": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + "public_key": "95fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe48", + "legacy_public_key": "15fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe48", + "signature": "b7921607432c421102b45efd5c1c345b329b61ebaabd6efc99d65971a4b68e358f5f1b341b16cf56b451e7b424f53525172ea4279a3276a6d365d04e9eeb5503e1edb8ad467739d6dd7b82811324842d66088e5c211a3210acfa7cfc939441e6" + }, + { + "secret_key": "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000", + "message": "", + "public_key": "b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "legacy_public_key": "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "signature": "88aab303e33ed14f4a904004a92bd26ffc969c1d1e7d4b7f0c04150a73e1845a911e51a2b2d369d5cef06560c5ac9f5715c01566993d4469805df3e1f29b536481a832bf2751b6908faed6776d062d585521889232999d72b679d6e38bb5cfff" + }, + { + "secret_key": "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000", + "message": "4461736820506c6174666f726d20424c5320636f6d7061746962696c697479", + "public_key": "b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "legacy_public_key": "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "signature": "a85957c480db7a90ba694ab40ea93e30e7e74c95e4ca3be0a67cda09e6f29b0d3b82d4bf9f56d6d6c2d410ed57f060b812569fac5b13555b4bb799ee33b5efb3024480e84d68962ff5db13b156d05f65d56fceec528f8bb8f1d740484d7a72e0" + }, + { + "secret_key": "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000", + "message": "0000000000000000000000000000000000000000000000000000000000000000", + "public_key": "b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "legacy_public_key": "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "signature": "972f4d8dfacf216719bcebb8349312f6bce892587291ee672fc0c7b6d16f427002713a961d122633e2deda35e803e9c302b3fcbc9d36fbb5fdb644bd41ce296399a5ea115f0c6cd612ff7e8b08477993cf1a2412fd81bc07e7ecc7380cfd2876" + }, + { + "secret_key": "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000", + "message": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + "public_key": "b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "legacy_public_key": "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "signature": "aa527aa6d18e63de70f31c512406217224c99f5a087fd7bcea48737d40ab99bcff2ab6b20b197c1e16efaa9c3dceeb7b01be7de81f980049e69395369a96f994b18f8358f8ce52675dfd8262f69e738eab510a5bba303756955c7a7667b4b477" + } + ], + "secure_aggregation": [ + { + "secret_keys": [ + "0000000000000000000000000000000000000000000000000000000000000001" + ], + "message": "4461736820506c6174666f726d20736563757265206167677265676174696f6e", + "public_keys": [ + "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb" + ], + "signature": "a5b6cbc5a885310da739d03370f1ee5d71da922c1c4fbb9a340cae955f25c78fb773bada0601caaadfbeb7c8e9805a860012dae1841d8ad9266444fff8fd0c4783ba46d86d0844c099a5d87c9edf22c50d6b82b18f6ea8499e0d711238504668" + }, + { + "secret_keys": [ + "0000000000000000000000000000000000000000000000000000000000000001", + "0000000000000000000000000000000000000000000000000000000000000002" + ], + "message": "4461736820506c6174666f726d20736563757265206167677265676174696f6e", + "public_keys": [ + "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e" + ], + "signature": "8921aa203d93e45302b404bc523295cc80d7d691b4f0a8081a1580752dbf68ea0acf681c52a74520d5c42a5739e6452102c928a29684b67eee818bf211bb019c9d3a7221975c0d1aa3b278bb3c6b86032b2d25c077b167907bc9ca6927bcab94" + }, + { + "secret_keys": [ + "0000000000000000000000000000000000000000000000000000000000000001", + "0000000000000000000000000000000000000000000000000000000000000002", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" + ], + "message": "4461736820506c6174666f726d20736563757265206167677265676174696f6e", + "public_keys": [ + "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e", + "95fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe48" + ], + "signature": "8c456b1677371cd2648677f66761539fb5d9f09b8c1a3587f4b2c95f737d9e3c509e7f293aad71322a7156b7d4581e54110c58f8055863344bea1a3e51700d1e6d30605cbab292590cae730e375dc00e60d049c9ee726b315d9c81328d03bd5f" + }, + { + "secret_keys": [ + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + "0000000000000000000000000000000000000000000000000000000000000001", + "0000000000000000000000000000000000000000000000000000000000000002" + ], + "message": "4461736820506c6174666f726d20736563757265206167677265676174696f6e", + "public_keys": [ + "95fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe48", + "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e" + ], + "signature": "8c456b1677371cd2648677f66761539fb5d9f09b8c1a3587f4b2c95f737d9e3c509e7f293aad71322a7156b7d4581e54110c58f8055863344bea1a3e51700d1e6d30605cbab292590cae730e375dc00e60d049c9ee726b315d9c81328d03bd5f" + }, + { + "secret_keys": [ + "0000000000000000000000000000000000000000000000000000000000000001", + "0000000000000000000000000000000000000000000000000000000000000001" + ], + "message": "4461736820506c6174666f726d20736563757265206167677265676174696f6e", + "public_keys": [ + "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb" + ], + "signature": "a71e6905eb7fecd7fdf91a794a0140676c8e2f94aa5420a295c668d529b7e5306478a32cf9f50c230a94f9626d893e8304bb21490ee8c7471e893c632dae250c6a49a9efb8cd5c0e790e884516b41f4a0fb5c45804d1d7e56bb366f2e153973c" + } + ], + "scalars": [ + { + "name": "zero", + "input": "0000000000000000000000000000000000000000000000000000000000000000", + "normalized": null, + "public_key": null, + "signature": null + }, + { + "name": "order", + "input": "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001", + "normalized": null, + "public_key": null, + "signature": null + }, + { + "name": "order_plus_one", + "input": "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000002", + "normalized": "0000000000000000000000000000000000000000000000000000000000000001", + "public_key": "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "signature": "b4ae0ce826b35665b1419394a212f47d9583fa453fd5096a655476cff885f8ad94418c7b9980d6b91c86b23a47a3beb510f6ceb0768fbee0bac279512d9627fe72d4d48d8a3efbef8f7652dd90836104c45cc9a680c82025c68eba5785b4c882" + }, + { + "name": "max", + "input": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "normalized": "1824b159acc5056f998c4fefecbc4ff55884b7fa0003480200000001fffffffd", + "public_key": "96ea601ca88f7d3489479129b258960b4c1df37194d30803627c30c34252679a0ada1a51bc7a4006a4f0564050d31746", + "signature": "aeab9eed96af89a7f8a8d0103496b59c950368c76d0bb18e4d57df607b24f75e80f8a436871f7cf9bb6bbf3a81e0939317e5e56648c0ba4958102649229b8dbed297a9fd29e49c13e138e181ffe7f55062597c0e15cdb42a63fe48c12b4ccf0c" + } + ], + "storage": [ + { + "name": "public_key", + "validator": { + "pro_tx_hash": "1111111111111111111111111111111111111111111111111111111111111111", + "public_key": "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "node_ip": "127.0.0.1", + "node_id": "2222222222222222222222222222222222222222", + "core_port": 19999, + "platform_http_port": 1443, + "platform_p2p_port": 26656, + "is_banned": false + }, + "validator_bytes": "11111111111111111111111111111111111111111111111111111111111111110197f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb093132372e302e302e312222222222222222222222222222222222222222fb4e1ffb05a3fb682000", + "validator_set": { + "quorum_hash": "3333333333333333333333333333333333333333333333333333333333333333", + "quorum_index": 2, + "core_height": 123456, + "members": { + "1111111111111111111111111111111111111111111111111111111111111111": { + "pro_tx_hash": "1111111111111111111111111111111111111111111111111111111111111111", + "public_key": "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "node_ip": "127.0.0.1", + "node_id": "2222222222222222222222222222222222222222", + "core_port": 19999, + "platform_http_port": 1443, + "platform_p2p_port": 26656, + "is_banned": false + } + }, + "threshold_public_key": "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb" + }, + "validator_set_bytes": "33333333333333333333333333333333333333333333333333333333333333330102fc0001e2400120111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110197f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb093132372e302e302e312222222222222222222222222222222222222222fb4e1ffb05a3fb68200097f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb" + }, + { + "name": "absent", + "validator": { + "pro_tx_hash": "1111111111111111111111111111111111111111111111111111111111111111", + "public_key": null, + "node_ip": "127.0.0.1", + "node_id": "2222222222222222222222222222222222222222", + "core_port": 19999, + "platform_http_port": 1443, + "platform_p2p_port": 26656, + "is_banned": false + }, + "validator_bytes": "111111111111111111111111111111111111111111111111111111111111111100093132372e302e302e312222222222222222222222222222222222222222fb4e1ffb05a3fb682000", + "validator_set": { + "quorum_hash": "3333333333333333333333333333333333333333333333333333333333333333", + "quorum_index": 2, + "core_height": 123456, + "members": { + "1111111111111111111111111111111111111111111111111111111111111111": { + "pro_tx_hash": "1111111111111111111111111111111111111111111111111111111111111111", + "public_key": null, + "node_ip": "127.0.0.1", + "node_id": "2222222222222222222222222222222222222222", + "core_port": 19999, + "platform_http_port": 1443, + "platform_p2p_port": 26656, + "is_banned": false + } + }, + "threshold_public_key": "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb" + }, + "validator_set_bytes": "33333333333333333333333333333333333333333333333333333333333333330102fc0001e24001201111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100093132372e302e302e312222222222222222222222222222222222222222fb4e1ffb05a3fb68200097f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb" + }, + { + "name": "infinity", + "validator": { + "pro_tx_hash": "1111111111111111111111111111111111111111111111111111111111111111", + "public_key": "c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "node_ip": "127.0.0.1", + "node_id": "2222222222222222222222222222222222222222", + "core_port": 19999, + "platform_http_port": 1443, + "platform_p2p_port": 26656, + "is_banned": false + }, + "validator_bytes": "111111111111111111111111111111111111111111111111111111111111111101c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000093132372e302e302e312222222222222222222222222222222222222222fb4e1ffb05a3fb682000", + "validator_set": { + "quorum_hash": "3333333333333333333333333333333333333333333333333333333333333333", + "quorum_index": 2, + "core_height": 123456, + "members": { + "1111111111111111111111111111111111111111111111111111111111111111": { + "pro_tx_hash": "1111111111111111111111111111111111111111111111111111111111111111", + "public_key": "c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "node_ip": "127.0.0.1", + "node_id": "2222222222222222222222222222222222222222", + "core_port": 19999, + "platform_http_port": 1443, + "platform_p2p_port": 26656, + "is_banned": false + } + }, + "threshold_public_key": "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb" + }, + "validator_set_bytes": "33333333333333333333333333333333333333333333333333333333333333330102fc0001e24001201111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111101c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000093132372e302e302e312222222222222222222222222222222222222222fb4e1ffb05a3fb68200097f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb" + } + ], + "key_generation": [ + { + "ikm": "0000000000000000000000000000000000000000000000000000000000000000", + "secret_key": "4a353be3dac091a0a7e640620372f5e1e2e4401717c1e79cac6ffba8f6905604", + "public_key": "85695fcbc06cc4c4c9451f4dce21cbf8de3e5a13bf48f44cdbb18e2038ba7b8bb1632d7911ef1e2e08749bddbf165352" + }, + { + "ikm": "0101010101010101010101010101010101010101010101010101010101010101", + "secret_key": "6fc9d9a2b05fd1f0e51bc91041a03be8657081f272ec281aff731624f0d1c220", + "public_key": "aefe1789d6476f60439e1168f588ea16652dc321279f05a805fbc63933e88ae9c175d6c6ab182e54af562e1a0dce41bb" + }, + { + "ikm": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "secret_key": "4ecb8197b8a2af28bfadf88f8084662c078bed1fa6970fd89b95ed541e35b3be", + "public_key": "96a5412d094be0d2017da0b7f17fe32540ad8356e59d181aec873543cc01433cf8cb251d3e1f950adfeeca9075a7e89c" + } + ] +} diff --git a/packages/rs-drive-abci/src/abci/error.rs b/packages/rs-drive-abci/src/abci/error.rs index 306e0644956..54777e4ce3e 100644 --- a/packages/rs-drive-abci/src/abci/error.rs +++ b/packages/rs-drive-abci/src/abci/error.rs @@ -1,4 +1,4 @@ -use dpp::bls_signatures::BlsError; +use dpp::bls::BlsError; use dpp::consensus::ConsensusError; use tenderdash_abci::proto::abci::ExtendVoteExtension; use tenderdash_abci::proto::types::VoteExtension; diff --git a/packages/rs-drive-abci/src/error/execution.rs b/packages/rs-drive-abci/src/error/execution.rs index 850ddd54717..10645c55941 100644 --- a/packages/rs-drive-abci/src/error/execution.rs +++ b/packages/rs-drive-abci/src/error/execution.rs @@ -1,4 +1,4 @@ -use dpp::bls_signatures::BlsError; +use dpp::bls::BlsError; use dpp::dashcore::consensus::encode::Error as DashCoreConsensusEncodeError; use dpp::identity::TimestampMillis; use dpp::version::FeatureVersion; diff --git a/packages/rs-drive-abci/src/error/mod.rs b/packages/rs-drive-abci/src/error/mod.rs index 241d9e7b37e..8bc9d7c1b6d 100644 --- a/packages/rs-drive-abci/src/error/mod.rs +++ b/packages/rs-drive-abci/src/error/mod.rs @@ -2,7 +2,7 @@ use crate::abci::AbciError; use crate::error::execution::ExecutionError; use crate::error::serialization::SerializationError; use crate::logging; -use dpp::bls_signatures::BlsError; +use dpp::bls::BlsError; use dpp::dashcore_rpc::Error as CoreRpcError; use dpp::data_contract::errors::DataContractError; use dpp::platform_value::Error as ValueError; diff --git a/packages/rs-drive-abci/src/execution/platform_events/block_end/validator_set_update/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/block_end/validator_set_update/mod.rs index 62f1cdfab8c..c611ea2c0a1 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/block_end/validator_set_update/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/block_end/validator_set_update/mod.rs @@ -157,7 +157,7 @@ mod tests { use dpp::block::block_info::BlockInfo; use dpp::block::extended_block_info::v0::ExtendedBlockInfoV0; use dpp::block::extended_block_info::ExtendedBlockInfo; - use dpp::bls_signatures::{Bls12381G2Impl, SecretKey}; + use dpp::bls::SecretKey; use dpp::core_types::validator::v0::ValidatorV0; use dpp::core_types::validator_set::v0::ValidatorSetV0; use dpp::core_types::validator_set::ValidatorSet; @@ -185,13 +185,13 @@ mod tests { member_seeds: &[u8], rng: &mut StdRng, ) -> ValidatorSet { - let threshold_public_key = SecretKey::::random(&mut *rng).public_key(); + let threshold_public_key = SecretKey::random(&mut *rng).public_key(); let mut members = BTreeMap::new(); for &seed in member_seeds { let mut hash_bytes = [0u8; 32]; hash_bytes[31] = seed; let pro_tx_hash = ProTxHash::from_byte_array(hash_bytes); - let public_key = Some(SecretKey::::random(&mut *rng).public_key()); + let public_key = Some(SecretKey::random(&mut *rng).public_key()); let node_id = PubkeyHash::from_byte_array([seed; 20]); let validator = ValidatorV0 { pro_tx_hash, @@ -407,7 +407,7 @@ mod tests { let mut rng = StdRng::seed_from_u64(45); let qh = quorum_hash_from_seed(1); - let threshold_pk = SecretKey::::random(&mut rng).public_key(); + let threshold_pk = SecretKey::random(&mut rng).public_key(); let empty_vs = ValidatorSet::V0(ValidatorSetV0 { quorum_hash: qh, quorum_index: None, diff --git a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/update_operator_identity/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/update_operator_identity/v0/mod.rs index f059b250783..5089b6a5219 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/update_operator_identity/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_identities/update_operator_identity/v0/mod.rs @@ -391,7 +391,7 @@ mod tests { use crate::platform_types::platform_state::PlatformStateV0Methods; use crate::test::helpers::setup::{TempPlatform, TestPlatformBuilder}; use dpp::block::block_info::BlockInfo; - use dpp::bls_signatures::{Bls12381G2Impl, SecretKey as BlsPrivateKey}; + use dpp::bls::SecretKey as BlsPrivateKey; use dpp::dashcore::hashes::Hash; use dpp::dashcore::ProTxHash; use dpp::dashcore::Txid; @@ -433,11 +433,13 @@ mod tests { .expect("expected to generate a private key") .to_bytes() .to_vec(); - let private_key_operator = BlsPrivateKey::::from_be_bytes( - &private_key_operator_bytes.try_into().expect("expected the secret key to be 32 bytes"), + let private_key_operator = BlsPrivateKey::from_be_bytes( + &private_key_operator_bytes + .try_into() + .expect("expected the secret key to be 32 bytes"), ) - .expect("expected the conversion between bls signatures library and blsful to happen without failing"); - let pub_key_operator = private_key_operator.public_key().0.to_compressed().to_vec(); + .expect("Core fixture secret key must decode"); + let pub_key_operator = private_key_operator.public_key().to_bytes().to_vec(); let operator_key: IdentityPublicKey = IdentityPublicKeyV0 { id: 0, @@ -976,11 +978,13 @@ mod tests { .expect("expected to generate a private key") .to_bytes() .to_vec(); - let private_key_operator = BlsPrivateKey::::from_be_bytes( - &private_key_operator_bytes.try_into().expect("expected the secret key to be 32 bytes"), + let private_key_operator = BlsPrivateKey::from_be_bytes( + &private_key_operator_bytes + .try_into() + .expect("expected the secret key to be 32 bytes"), ) - .expect("expected the conversion between bls signatures library and blsful to happen without failing"); - let new_pub_key_operator = private_key_operator.public_key().0.to_compressed().to_vec(); + .expect("Core fixture secret key must decode"); + let new_pub_key_operator = private_key_operator.public_key().to_bytes().to_vec(); // Create an old masternode state let masternode_list_item = MasternodeListItem { @@ -1072,15 +1076,13 @@ mod tests { .expect("expected to generate a private key") .to_bytes() .to_vec(); - let private_key_operator = BlsPrivateKey::::from_be_bytes( + let private_key_operator = BlsPrivateKey::from_be_bytes( &private_key_operator_bytes .try_into() .expect("expected the secret key to be 32 bytes"), ) - .expect( - "expected the conversion between bls signatures library and blsful to happen without failing", - ); - let new_pub_key_operator = private_key_operator.public_key().0.to_compressed().to_vec(); + .expect("Core fixture secret key must decode"); + let new_pub_key_operator = private_key_operator.public_key().to_bytes().to_vec(); let new_operator_identifier = Identifier::create_operator_identifier( pro_tx_hash.as_byte_array(), @@ -1215,11 +1217,13 @@ mod tests { .expect("expected to generate a private key") .to_bytes() .to_vec(); - let private_key_operator = BlsPrivateKey::::from_be_bytes( - &private_key_operator_bytes.try_into().expect("expected the secret key to be 32 bytes"), + let private_key_operator = BlsPrivateKey::from_be_bytes( + &private_key_operator_bytes + .try_into() + .expect("expected the secret key to be 32 bytes"), ) - .expect("expected the conversion between bls signatures library and blsful to happen without failing"); - let new_pub_key_operator = private_key_operator.public_key().0.to_compressed().to_vec(); + .expect("Core fixture secret key must decode"); + let new_pub_key_operator = private_key_operator.public_key().to_bytes().to_vec(); // Create an old masternode state with original public key operator let masternode_list_item = MasternodeListItem { diff --git a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_list/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_list/mod.rs index 36279aad809..4292296c059 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_list/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_list/mod.rs @@ -92,7 +92,7 @@ mod test { use crate::platform_types::validator_set::{ValidatorSet, ValidatorSetExt}; use crate::rpc::core::MockCoreRPCLike; use crate::test::helpers::setup::{TempPlatform, TestPlatformBuilder}; - use dpp::bls_signatures::{Bls12381G2Impl, SecretKey}; + use dpp::bls::SecretKey; use dpp::core_types::validator_set::v0::ValidatorSetV0; use dpp::dashcore::hashes::Hash; use dpp::dashcore::{Network, ProTxHash, PubkeyHash, QuorumHash}; @@ -517,10 +517,8 @@ mod test { quorum_index: None, core_height: init_core_height, members: members.clone(), - threshold_public_key: SecretKey::::random( - &mut StdRng::seed_from_u64(1), - ) - .public_key(), + threshold_public_key: SecretKey::random(&mut StdRng::seed_from_u64(1)) + .public_key(), }), ); @@ -893,10 +891,7 @@ mod test { .into_iter() .map(|validator| (validator.pro_tx_hash, validator)) .collect(), - threshold_public_key: SecretKey::::random(&mut StdRng::seed_from_u64( - 1, - )) - .public_key(), + threshold_public_key: SecretKey::random(&mut StdRng::seed_from_u64(1)).public_key(), }) .to_update() .validator_updates @@ -960,10 +955,8 @@ mod test { quorum_index: None, core_height: *core_height, members, - threshold_public_key: SecretKey::::random( - &mut StdRng::seed_from_u64(1), - ) - .public_key(), + threshold_public_key: SecretKey::random(&mut StdRng::seed_from_u64(1)) + .public_key(), }), ); } diff --git a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_list/update_state_masternode_list/v1/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_list/update_state_masternode_list/v1/mod.rs index 996608f515f..af7b64bbd84 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_list/update_state_masternode_list/v1/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_list/update_state_masternode_list/v1/mod.rs @@ -301,7 +301,7 @@ mod tests { use crate::platform_types::validator_set::{ValidatorSet, ValidatorSetExt}; use crate::rpc::core::MockCoreRPCLike; use crate::test::helpers::setup::{TempPlatform, TestPlatformBuilder}; - use dpp::bls_signatures::{Bls12381G2Impl, SecretKey}; + use dpp::bls::SecretKey; use dpp::core_types::validator::v0::ValidatorV0; use dpp::core_types::validator_set::v0::ValidatorSetV0; use dpp::dashcore::hashes::Hash; @@ -652,7 +652,7 @@ mod tests { quorum_index: None, core_height: 1, members: BTreeMap::from([(pro_tx_hash, validator)]), - threshold_public_key: SecretKey::::random(&mut rng).public_key(), + threshold_public_key: SecretKey::random(&mut rng).public_key(), }), ); state.mark_saved(); diff --git a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_quorum_info/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_quorum_info/v0/mod.rs index 2b2b6d1bd29..12fa611b15d 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_quorum_info/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_quorum_info/v0/mod.rs @@ -16,7 +16,7 @@ use crate::rpc::core::CoreRPCLike; use crate::platform_types::signature_verification_quorum_set::{ SignatureVerificationQuorumSet, SignatureVerificationQuorumSetV0Methods, VerificationQuorum, }; -use dpp::bls_signatures::PublicKey as BlsPublicKey; +use dpp::bls::PublicKey as BlsPublicKey; use dpp::dashcore::QuorumHash; use tracing::Level; diff --git a/packages/rs-drive-abci/src/execution/platform_events/core_chain_lock/choose_quorum/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/core_chain_lock/choose_quorum/mod.rs index 1b0ab684cfe..bc48b50873a 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/core_chain_lock/choose_quorum/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/core_chain_lock/choose_quorum/mod.rs @@ -2,7 +2,7 @@ mod v0; use crate::error::execution::ExecutionError; use crate::error::Error; -use dpp::bls_signatures::{Bls12381G2Impl, PublicKey as BlsPublicKey}; +use dpp::bls::PublicKey as BlsPublicKey; use dpp::dashcore::QuorumHash; use dpp::dashcore_rpc::dashcore_rpc_json::QuorumType; @@ -24,10 +24,10 @@ where // TODO: use CoreQuorumSet.select_quorums instead pub fn choose_quorum<'a>( llmq_quorum_type: QuorumType, - quorums: &'a BTreeMap>, + quorums: &'a BTreeMap, request_id: &[u8; 32], platform_version: &PlatformVersion, - ) -> Result)>, Error> { + ) -> Result, Error> { match platform_version .drive_abci .methods diff --git a/packages/rs-drive-abci/src/execution/platform_events/core_chain_lock/choose_quorum/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/core_chain_lock/choose_quorum/v0/mod.rs index 945868ec823..0c3be48ad63 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/core_chain_lock/choose_quorum/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/core_chain_lock/choose_quorum/v0/mod.rs @@ -1,4 +1,4 @@ -use dpp::bls_signatures::{Bls12381G2Impl, PublicKey as BlsPublicKey}; +use dpp::bls::PublicKey as BlsPublicKey; use dpp::dashcore::hashes::{sha256d, Hash, HashEngine}; use dpp::dashcore::QuorumHash; use dpp::dashcore_rpc::dashcore_rpc_json::QuorumType; @@ -12,15 +12,11 @@ impl Platform { /// Based on DIP8 deterministically chooses a pseudorandom quorum from the list of quorums pub(super) fn choose_quorum_v0<'a>( llmq_quorum_type: QuorumType, - quorums: &'a BTreeMap>, + quorums: &'a BTreeMap, request_id: &[u8; 32], - ) -> Option<(ReversedQuorumHashBytes, &'a BlsPublicKey)> { + ) -> Option<(ReversedQuorumHashBytes, &'a BlsPublicKey)> { // Scoring system logic - let mut scores: Vec<( - ReversedQuorumHashBytes, - &BlsPublicKey, - [u8; 32], - )> = Vec::new(); + let mut scores: Vec<(ReversedQuorumHashBytes, &BlsPublicKey, [u8; 32])> = Vec::new(); for (quorum_hash, public_key) in quorums { let mut quorum_hash_bytes = quorum_hash.to_byte_array().to_vec(); @@ -95,7 +91,7 @@ impl Platform { mod tests { use crate::platform_types::platform::Platform; use crate::rpc::core::MockCoreRPCLike; - use dpp::bls_signatures::SecretKey; + use dpp::bls::SecretKey; use dpp::dashcore::hashes::Hash; use dpp::dashcore::QuorumHash; use dpp::dashcore_rpc::dashcore_rpc_json::QuorumType; diff --git a/packages/rs-drive-abci/src/execution/platform_events/core_chain_lock/verify_chain_lock_locally/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/core_chain_lock/verify_chain_lock_locally/v0/mod.rs index 000d46582a2..c924a8f3c74 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/core_chain_lock/verify_chain_lock_locally/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/core_chain_lock/verify_chain_lock_locally/v0/mod.rs @@ -1,4 +1,4 @@ -use dpp::bls_signatures::{Bls12381G2Impl, Pairing, Signature}; +use dpp::bls::Signature; use dpp::dashcore::hashes::{sha256d, Hash, HashEngine}; use dpp::dashcore::{ChainLock, QuorumSigningRequestId}; @@ -38,11 +38,7 @@ where // First verify that the signature conforms to a signature - let decoded_sig = match ::Signature::from_compressed( - chain_lock.signature.as_bytes(), - ) - .into_option() - { + let signature = match Signature::from_compressed(chain_lock.signature.as_bytes()) { Some(signature) => signature, None => { tracing::error!( @@ -51,16 +47,12 @@ where platform_state.last_committed_block_height() + 1, round ); - return Err(Error::BLSError( - dpp::bls_signatures::BlsError::DeserializationError( - "chain lock signature was not deserializable".to_string(), - ), - )); + return Err(Error::BLSError(dpp::bls::BlsError::DeserializationError( + "chain lock signature was not deserializable".to_string(), + ))); } }; - let signature = Signature::Basic(decoded_sig); - // we attempt to verify the chain lock locally let chain_lock_height = chain_lock.block_height; @@ -237,7 +229,8 @@ where mod tests { use crate::execution::platform_events::core_chain_lock::verify_chain_lock_locally::v0::CHAIN_LOCK_REQUEST_ID_PREFIX; use crate::test::helpers::setup::TestPlatformBuilder; - use dpp::bls_signatures::{Bls12381G2Impl, Pairing}; + use dpp::bls::Signature; + use dpp::dashcore::hashes::{sha256d, Hash, HashEngine}; use dpp::dashcore::{BlockHash, ChainLock, QuorumSigningRequestId}; use dpp::version::PlatformVersion; @@ -327,11 +320,7 @@ mod tests { ]; for signature in signatures { - assert!( - ::Signature::from_compressed(&signature) - .into_option() - .is_some(), - ); + assert!(Signature::from_compressed(&signature).is_some()); } } diff --git a/packages/rs-drive-abci/src/execution/platform_events/core_instant_send_lock/verify_recent_signature_locally/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/core_instant_send_lock/verify_recent_signature_locally/v0/mod.rs index 2ed186f8a02..dcf4160a1f0 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/core_instant_send_lock/verify_recent_signature_locally/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/core_instant_send_lock/verify_recent_signature_locally/v0/mod.rs @@ -1,4 +1,4 @@ -use dpp::bls_signatures::{Bls12381G2Impl, Pairing, Signature}; +use dpp::bls::Signature; use std::fmt::{Debug, Formatter}; use dpp::dashcore::hashes::{sha256d, Hash, HashEngine}; @@ -21,12 +21,8 @@ pub(super) fn verify_recent_instant_lock_signature_locally_v0( ) -> Result { // First verify that the signature conforms to a signature - let signature = match ::Signature::from_compressed( - instant_lock.signature.as_bytes(), - ) - .into_option() - { - Some(signature) => Signature::Basic(signature), + let signature = match Signature::from_compressed(instant_lock.signature.as_bytes()) { + Some(signature) => signature, None => { tracing::trace!( instant_lock = ?InstantLockDebug(instant_lock), "Invalid instant Lock {} signature format", instant_lock.txid, ); diff --git a/packages/rs-drive-abci/src/execution/storage/store_platform_state/v1/mod.rs b/packages/rs-drive-abci/src/execution/storage/store_platform_state/v1/mod.rs index c4919e1fd19..c60fcc245a9 100644 --- a/packages/rs-drive-abci/src/execution/storage/store_platform_state/v1/mod.rs +++ b/packages/rs-drive-abci/src/execution/storage/store_platform_state/v1/mod.rs @@ -180,7 +180,7 @@ mod tests { use crate::rpc::core::MockCoreRPCLike; use crate::test::helpers::setup::{TempPlatform, TestPlatformBuilder}; use dpp::bincode::config; - use dpp::bls_signatures::{Bls12381G2Impl, SecretKey}; + use dpp::bls::SecretKey; use dpp::core_types::validator::v0::ValidatorV0; use dpp::core_types::validator_set::v0::{ValidatorSetV0, ValidatorSetV0Getters}; use dpp::core_types::validator_set::ValidatorSet; @@ -259,7 +259,7 @@ mod tests { /// threshold key derived from `byte`. fn validator_set(byte: u8, core_height: u32) -> ValidatorSet { let mut rng = StdRng::seed_from_u64(byte as u64); - let threshold_public_key = SecretKey::::random(&mut rng).public_key(); + let threshold_public_key = SecretKey::random(&mut rng).public_key(); let member = ValidatorV0 { pro_tx_hash: pro_tx_hash(byte), public_key: None, diff --git a/packages/rs-drive-abci/src/mimic/mod.rs b/packages/rs-drive-abci/src/mimic/mod.rs index 9434a5572b9..a05b0f146c4 100644 --- a/packages/rs-drive-abci/src/mimic/mod.rs +++ b/packages/rs-drive-abci/src/mimic/mod.rs @@ -13,7 +13,7 @@ use crate::platform_types::withdrawal::unsigned_withdrawal_txs::v0::{ use crate::rpc::core::CoreRPCLike; use ciborium::Value as CborValue; use dpp::block::block_info::BlockInfo; -use dpp::bls_signatures::SignatureSchemes; + use dpp::consensus::ConsensusError; use dpp::dashcore::hashes::Hash; use dpp::platform_value::btreemap_extensions::BTreeValueMapHelper; @@ -538,10 +538,10 @@ impl FullAbciApplication<'_, C> { ); let block_signature = current_quorum .private_key - .sign(SignatureSchemes::Basic, digest.as_slice()) + .sign(digest.as_slice()) .expect("expected to be able to sign"); - commit_info.block_signature = block_signature.as_raw_value().to_compressed().to_vec(); + commit_info.block_signature = block_signature.to_bytes().to_vec(); } else { commit_info.block_signature = [0u8; 96].to_vec(); } diff --git a/packages/rs-drive-abci/src/mimic/test_quorum.rs b/packages/rs-drive-abci/src/mimic/test_quorum.rs index 9521f88c46e..1adebbf840f 100644 --- a/packages/rs-drive-abci/src/mimic/test_quorum.rs +++ b/packages/rs-drive-abci/src/mimic/test_quorum.rs @@ -1,8 +1,6 @@ use crate::platform_types::validator::v0::ValidatorV0; use crate::platform_types::validator_set::v0::ValidatorSetV0; -use dpp::bls_signatures::{ - Bls12381G2Impl, PublicKey as BlsPublicKey, PublicKey, SecretKey as BlsPrivateKey, SecretKey, -}; +use dpp::bls::{PublicKey as BlsPublicKey, PublicKey, SecretKey as BlsPrivateKey, SecretKey}; use dpp::dashcore::hashes::Hash; use dpp::dashcore::{ProTxHash, PubkeyHash, QuorumHash}; use dpp::dashcore_rpc::dashcore_rpc_json::{QuorumInfoResult, QuorumMember, QuorumType}; @@ -18,9 +16,9 @@ pub struct ValidatorInQuorum { /// The hash of the transaction that identifies this validator in the network. pub pro_tx_hash: ProTxHash, /// The private key for this validator's BLS signature scheme. - pub private_key: BlsPrivateKey, + pub private_key: BlsPrivateKey, /// The public key for this validator's BLS signature scheme. - pub public_key: BlsPublicKey, + pub public_key: BlsPublicKey, /// The node address pub node_ip: String, /// The node id @@ -103,9 +101,9 @@ pub struct TestQuorumInfo { /// A map of validators indexed by their `ProTxHash` identifiers. pub validator_map: BTreeMap, /// The private key used to sign messages for the quorum (for testing purposes only). - pub private_key: BlsPrivateKey, + pub private_key: BlsPrivateKey, /// The public key corresponding to the private key used for signing. - pub public_key: BlsPublicKey, + pub public_key: BlsPublicKey, } fn random_ipv4_address(rng: &mut StdRng) -> Ipv4Addr { @@ -274,7 +272,7 @@ impl From<&TestQuorumInfo> for QuorumInfoResult { pro_tx_hash: *pro_tx_hash, pub_key_operator: vec![], //doesn't matter valid: true, - pub_key_share: Some(public_key.0.to_compressed().to_vec()), + pub_key_share: Some(public_key.to_bytes().to_vec()), } }) .collect(); @@ -285,7 +283,7 @@ impl From<&TestQuorumInfo> for QuorumInfoResult { quorum_index: 0, mined_block: vec![], members, - quorum_public_key: public_key.0.to_compressed().to_vec(), + quorum_public_key: public_key.to_bytes().to_vec(), secret_key_share: None, } } diff --git a/packages/rs-drive-abci/src/platform_types/commit/mod.rs b/packages/rs-drive-abci/src/platform_types/commit/mod.rs index 153de511e26..568c5028f5a 100644 --- a/packages/rs-drive-abci/src/platform_types/commit/mod.rs +++ b/packages/rs-drive-abci/src/platform_types/commit/mod.rs @@ -3,8 +3,8 @@ use crate::error::execution::ExecutionError; use crate::error::Error; use crate::platform_types::cleaned_abci_messages::{cleaned_block_id, cleaned_commit_info}; use crate::platform_types::commit::v0::CommitV0; -use dpp::bls_signatures; -use dpp::bls_signatures::Bls12381G2Impl; +use dpp::bls; + use dpp::dashcore_rpc::dashcore_rpc_json::QuorumType; use dpp::validation::SimpleValidationResult; use dpp::version::PlatformVersion; @@ -83,7 +83,7 @@ impl Commit { pub fn verify_signature( &self, signature: &[u8; 96], - public_key: &bls_signatures::PublicKey, + public_key: &bls::PublicKey, ) -> SimpleValidationResult { match self { Commit::V0(v0) => v0.verify_signature(signature, public_key), diff --git a/packages/rs-drive-abci/src/platform_types/commit/v0/mod.rs b/packages/rs-drive-abci/src/platform_types/commit/v0/mod.rs index af9fdc918ce..7d131cc6be3 100644 --- a/packages/rs-drive-abci/src/platform_types/commit/v0/mod.rs +++ b/packages/rs-drive-abci/src/platform_types/commit/v0/mod.rs @@ -4,8 +4,8 @@ pub mod accessors; use crate::abci::AbciError; use crate::platform_types::cleaned_abci_messages::{cleaned_block_id, cleaned_commit_info}; -use dpp::bls_signatures; -use dpp::bls_signatures::{Bls12381G2Impl, BlsError, Pairing, Signature}; +use dpp::bls; +use dpp::bls::{BlsError, Signature}; use dpp::dashcore_rpc::dashcore_rpc_json::QuorumType; use dpp::validation::{SimpleValidationResult, ValidationResult}; use tenderdash_abci::proto; @@ -82,7 +82,7 @@ impl CommitV0 { pub(super) fn verify_signature( &self, signature: &[u8; 96], - public_key: &bls_signatures::PublicKey, + public_key: &bls::PublicKey, ) -> SimpleValidationResult { if signature == &[0; 96] { return ValidationResult::new_with_error(AbciError::BadRequest( @@ -91,18 +91,16 @@ impl CommitV0 { } // We could have received a fake commit, so signature validation needs to be returned if error as a simple validation result - let g2_element = match ::Signature::from_compressed(signature) - .into_option() - .ok_or(AbciError::BlsErrorOfTenderdashThresholdMechanism( + let signature = match Signature::from_compressed(signature).ok_or( + AbciError::BlsErrorOfTenderdashThresholdMechanism( BlsError::InvalidSignature, "verification of a commit signature".to_string(), - )) { + ), + ) { Ok(signature) => signature, Err(e) => return ValidationResult::new_with_error(e), }; - let signature = Signature::Basic(g2_element); - //todo: maybe cache this to lower the chance of a hashing based attack (forcing the // same calculation each time) let quorum_hash = &self.inner.quorum_hash[..] @@ -139,7 +137,7 @@ mod test { use super::CommitV0; use crate::platform_types::cleaned_abci_messages::cleaned_commit_info::v0::CleanedCommitInfo; - use dpp::bls_signatures::PublicKey; + use dpp::bls::PublicKey; use dpp::dashcore_rpc::{ dashcore::hashes::sha256, dashcore::hashes::Hash, dashcore_rpc_json::QuorumType, }; diff --git a/packages/rs-drive-abci/src/platform_types/platform_state/platform_state_for_saving/v0/old_structures/mod.rs b/packages/rs-drive-abci/src/platform_types/platform_state/platform_state_for_saving/v0/old_structures/mod.rs index a5b7a11c78d..8ad16315f56 100644 --- a/packages/rs-drive-abci/src/platform_types/platform_state/platform_state_for_saving/v0/old_structures/mod.rs +++ b/packages/rs-drive-abci/src/platform_types/platform_state/platform_state_for_saving/v0/old_structures/mod.rs @@ -1,5 +1,5 @@ #[cfg(feature = "bls-signatures")] -use dpp::bls_signatures::PublicKey; +use dpp::bls::PublicKey; use dpp::dashcore::{ProTxHash, PubkeyHash, QuorumHash}; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/README.md b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/README.md new file mode 100644 index 00000000000..858e0c7c19a --- /dev/null +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/README.md @@ -0,0 +1,35 @@ +# Historical V0 quorum storage + +`quorum-storage-v0.hex` contains a synthetic, complete +`SignatureVerificationQuorumSetForSaving::V0` encoded by Platform commit +`7c73e983b4c3cd9b5e2ead34bbe0360646b7b4d9`, before the DPP BLS backend migration. +It uses `grovedb-bincode` with `standard().with_big_endian().with_no_limit()` +and the C++ `bls-signatures` public-key Serde implementation at revision +`0842b17583888e8f46c252a4ee84cdfd58e0546f`. + +The hex file (including its final newline) has SHA-256 +`8a2c8e59633ca1b126e3c0cc358a9347667d0f54068a874b1ae1435b85af830f` +and encodes 350 bytes. + +The four public keys come from DPP's frozen BLS compatibility vectors. Current +quorums have repeated-byte hashes `11` and `22`, with indexes `None` and `Some(0)`; +previous quorums have hashes `33` and `44`, with indexes `Some(1)` and `None`. +The previous-quorum heights are 1000 (last active), 1008 (updated), and +`Some(900)` (previous change). Configuration is `Llmq400_60`, four active signers, +no rotation, and a 288-block window. + +The test decodes the production storage enum and converts it to the runtime +quorum set, exercising the historical C++-to-DPP key conversion for both lists. +Expected keys and metadata are asserted independently of the current writer. + +To reproduce, copy `generate-quorum-storage-v0.rs` from this directory to +`packages/rs-drive-abci/examples/generate_quorum_storage_v0.rs` in a separate +checkout of the commit above, then run from that checkout: + +```sh +cargo run -p drive-abci --example generate_quorum_storage_v0 --all-features --locked -- /data/tmp/quorum-storage-v0.hex +``` + +Compare the generated file with the committed fixture. Do not regenerate the +expected bytes with the migrated backend. The generator explicitly selects V0; +the ordinary runtime-to-storage conversion writes V2. diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/generate-quorum-storage-v0.rs b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/generate-quorum-storage-v0.rs new file mode 100644 index 00000000000..12d3f4574a4 --- /dev/null +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/generate-quorum-storage-v0.rs @@ -0,0 +1,60 @@ +//! Run as a drive-abci example at the pre-migration revision documented in README.md. + +use dpp::dashcore::hashes::Hash; +use dpp::dashcore::QuorumHash; +use dpp::dashcore_rpc::json::QuorumType; +use drive_abci::config::ChainLockConfig; +use drive_abci::platform_types::signature_verification_quorum_set::{ + Quorums, SignatureVerificationQuorumSetForSaving, SignatureVerificationQuorumSetV0, + SignatureVerificationQuorumSetV0Methods, VerificationQuorum, +}; + +const PUBLIC_KEYS: [&str; 4] = [ + "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e", + "95fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe48", + "b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", +]; + +fn quorums(entries: &[(u8, usize, Option)]) -> Quorums { + entries + .iter() + .map(|&(hash, key, index)| { + ( + QuorumHash::from_byte_array([hash; 32]), + VerificationQuorum { + public_key: hex::decode(PUBLIC_KEYS[key]) + .unwrap() + .as_slice() + .try_into() + .unwrap(), + index, + }, + ) + }) + .collect() +} + +fn main() { + let mut runtime = SignatureVerificationQuorumSetV0::new(&ChainLockConfig { + quorum_type: QuorumType::Llmq400_60, + quorum_size: 400, + quorum_window: 288, + quorum_active_signers: 4, + quorum_rotation: false, + }); + runtime.set_current_quorums(quorums(&[(0x11, 0, None), (0x22, 1, Some(0))])); + runtime.set_previous_past_quorums(Quorums::default(), 899, 900); + runtime.set_previous_past_quorums(quorums(&[(0x33, 2, Some(1)), (0x44, 3, None)]), 1000, 1008); + // The normal runtime conversion writes V2; explicitly select the historical V0 writer. + let stored = SignatureVerificationQuorumSetForSaving::V0(runtime.into()); + let bytes = bincode::encode_to_vec( + stored, + bincode::config::standard() + .with_big_endian() + .with_no_limit(), + ) + .unwrap(); + let destination = std::env::args().nth(1).expect("fixture output path"); + std::fs::write(destination, format!("{}\n", hex::encode(bytes))).unwrap(); +} diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v0.hex b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v0.hex new file mode 100644 index 00000000000..2e9333f44e3 --- /dev/null +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v0.hex @@ -0,0 +1 @@ +00010400fb01200211111111111111111111111111111111111111111111111111111111111111113097f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00222222222222222222222222222222222222222222222222222222222222222230a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e0100010233333333333333333333333333333333333333333333333333333333333333333095fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe480101444444444444444444444444444444444444444444444444444444444444444430b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb0384 diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/for_saving_v0.rs b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/for_saving_v0.rs index 2800dc0683b..26867dee202 100644 --- a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/for_saving_v0.rs +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/for_saving_v0.rs @@ -114,7 +114,7 @@ impl From> for Quorums { QuorumHash::from_byte_array(quorum.hash.to_buffer()), VerificationQuorum { #[cfg(feature = "bls-signatures")] - public_key: dpp::bls_signatures::PublicKey::try_from( + public_key: dpp::bls::PublicKey::try_from( quorum.public_key.to_bytes().as_slice(), ) .expect("expected to convert between BLS key libraries (from chia)"), @@ -134,12 +134,86 @@ impl From> for Vec { .map(|(hash, quorum)| QuorumForSavingV0 { hash: Bytes32::from(hash.as_byte_array()), #[cfg(feature = "bls-signatures")] - public_key: bls_signatures::PublicKey::from_bytes( - &quorum.public_key.0.to_compressed(), - ) - .expect("expected to convert between BLS key libraries (to chia)"), + public_key: bls_signatures::PublicKey::from_bytes(&quorum.public_key.to_bytes()) + .expect("expected to convert between BLS key libraries (to chia)"), index: quorum.index, }) .collect() } } + +#[cfg(all(test, feature = "bls-signatures"))] +mod tests { + use super::*; + use crate::platform_types::signature_verification_quorum_set::SignatureVerificationQuorumSet; + use dpp::dashcore_rpc::json::QuorumType; + + fn assert_quorum( + quorums: &Quorums, + hash: u8, + public_key: &str, + index: Option, + ) { + let quorum = quorums + .get(&QuorumHash::from_byte_array([hash; 32])) + .unwrap(); + assert_eq!( + quorum.public_key.to_bytes().as_slice(), + hex::decode(public_key).unwrap() + ); + assert_eq!(quorum.index, index); + } + + #[test] + fn should_restore_current_and_previous_quorums_from_frozen_v0_storage() { + let bytes = hex::decode(include_str!("fixtures/quorum-storage-v0.hex").trim()).unwrap(); + let config = bincode::config::standard() + .with_big_endian() + .with_no_limit(); + let (stored, consumed): (SignatureVerificationQuorumSetForSaving, _) = + bincode::decode_from_slice(&bytes, config).unwrap(); + assert_eq!(consumed, bytes.len()); + assert!(matches!( + stored, + SignatureVerificationQuorumSetForSaving::V0(_) + )); + assert_eq!(bincode::encode_to_vec(&stored, config).unwrap(), bytes); + + let SignatureVerificationQuorumSet::V0(restored) = stored.into(); + assert_eq!(restored.config.quorum_type, QuorumType::Llmq400_60); + assert_eq!(restored.config.active_signers, 4); + assert!(!restored.config.rotation); + assert_eq!(restored.config.window, 288); + assert_eq!(restored.current_quorums.len(), 2); + assert_quorum( + &restored.current_quorums, + 0x11, + "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + None, + ); + assert_quorum( + &restored.current_quorums, + 0x22, + "a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e", + Some(0), + ); + + let previous = restored.previous.unwrap(); + assert_eq!(previous.last_active_core_height, 1000); + assert_eq!(previous.updated_at_core_height, 1008); + assert_eq!(previous.previous_change_height, Some(900)); + assert_eq!(previous.quorums.len(), 2); + assert_quorum( + &previous.quorums, + 0x33, + "95fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe48", + Some(1), + ); + assert_quorum( + &previous.quorums, + 0x44, + "b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + None, + ); + } +} diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/for_saving_v1.rs b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/for_saving_v1.rs index f39d8f8411f..1d903b160a1 100644 --- a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/for_saving_v1.rs +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/for_saving_v1.rs @@ -7,7 +7,7 @@ use crate::platform_types::signature_verification_quorum_set::{ ThresholdBlsPublicKey, VerificationQuorum, }; use bincode::{Decode, Encode}; -use dpp::bls_signatures::Bls12381G2Impl; + use dpp::dashcore::hashes::Hash; use dpp::dashcore::QuorumHash; use dpp::platform_value::Bytes32; @@ -68,7 +68,7 @@ impl From for SignatureVerificationQu pub struct QuorumForSavingV1 { hash: Bytes32, #[bincode(with_serde)] - public_key: ThresholdBlsPublicKey, + public_key: ThresholdBlsPublicKey, index: Option, } diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/quorum_set.rs b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/quorum_set.rs index 881347bc6ae..82bbab7fc46 100644 --- a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/quorum_set.rs +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/quorum_set.rs @@ -303,16 +303,15 @@ impl From for SignatureVerificationQuorumSetV0 { mod tests { use super::*; use crate::config::ChainLockConfig; - use dpp::bls_signatures::{Bls12381G2Impl, SecretKey as BlsPrivateKey}; + use dpp::bls::SecretKey as BlsPrivateKey; use dpp::dashcore::hashes::Hash; use dpp::dashcore_rpc::json::QuorumType; - fn make_public_key(seed: u8) -> dpp::bls_signatures::PublicKey { + fn make_public_key(seed: u8) -> dpp::bls::PublicKey { let mut key_bytes = [0u8; 32]; key_bytes[0] = seed; key_bytes[31] = 1; - let sk = - BlsPrivateKey::::from_be_bytes(&key_bytes).expect("valid secret key"); + let sk = BlsPrivateKey::from_be_bytes(&key_bytes).expect("valid secret key"); sk.public_key() } diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/quorums.rs b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/quorums.rs index 2781697e1dc..e51e16cc4aa 100644 --- a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/quorums.rs +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/quorums.rs @@ -1,7 +1,7 @@ use derive_more::{Deref, DerefMut, From}; -use dpp::bls_signatures; -pub use dpp::bls_signatures::PublicKey as ThresholdBlsPublicKey; -use dpp::bls_signatures::{Bls12381G2Impl, SignatureSchemes}; +use dpp::bls; +pub use dpp::bls::PublicKey as ThresholdBlsPublicKey; + use dpp::dashcore::bls_sig_utils::BLSSignature; use dpp::dashcore::{QuorumHash, Txid}; use std::collections::BTreeMap; @@ -149,7 +149,7 @@ pub struct VerificationQuorum { /// Quorum threshold public key is used to verify /// signatures produced by corresponding quorum - pub public_key: ThresholdBlsPublicKey, + pub public_key: ThresholdBlsPublicKey, } impl Debug for VerificationQuorum { @@ -206,39 +206,33 @@ impl SigningQuorum { let message_digest = sha256d::Hash::from_engine(engine); - let private_key = - bls_signatures::SecretKey::::from_be_bytes(&self.private_key) - .into_option() - .ok_or(Error::BLSError( - dpp::bls_signatures::BlsError::DeserializationError( - "Could not deserialize private key".to_string(), - ), - ))?; + let private_key = bls::SecretKey::from_be_bytes(&self.private_key).ok_or( + Error::BLSError(dpp::bls::BlsError::DeserializationError( + "Could not deserialize private key".to_string(), + )), + )?; let signature = private_key - .sign( - SignatureSchemes::Basic, - message_digest.as_byte_array().as_slice(), - ) + .sign(message_digest.as_byte_array().as_slice()) .map_err(Error::BLSError)?; - Ok(BLSSignature::from(signature.as_raw_value().to_compressed())) + Ok(BLSSignature::from(signature.to_bytes())) } } #[cfg(test)] mod tests { use super::*; - use dpp::bls_signatures::{Bls12381G2Impl, SecretKey as BlsPrivateKey}; + use dpp::bls::SecretKey as BlsPrivateKey; use dpp::dashcore::hashes::Hash; use dpp::dashcore_rpc::json::QuorumType; /// Helper: generate a deterministic BLS public key from a seed byte. - fn make_public_key(seed: u8) -> ThresholdBlsPublicKey { + fn make_public_key(seed: u8) -> ThresholdBlsPublicKey { let mut key_bytes = [0u8; 32]; key_bytes[0] = seed; key_bytes[31] = 1; // ensure nonzero - let sk = BlsPrivateKey::::from_be_bytes(&key_bytes) + let sk = BlsPrivateKey::from_be_bytes(&key_bytes) .expect("expected a valid secret key from test bytes"); sk.public_key() } diff --git a/packages/rs-drive-abci/src/platform_types/validator/v0/mod.rs b/packages/rs-drive-abci/src/platform_types/validator/v0/mod.rs index 2fbab063bd1..810bb7013cb 100644 --- a/packages/rs-drive-abci/src/platform_types/validator/v0/mod.rs +++ b/packages/rs-drive-abci/src/platform_types/validator/v0/mod.rs @@ -1,6 +1,6 @@ use crate::platform_types::platform_state::PlatformState; use crate::platform_types::platform_state::PlatformStateV0Methods; -use dpp::bls_signatures::{Bls12381G2Impl, PublicKey as BlsPublicKey}; +use dpp::bls::PublicKey as BlsPublicKey; pub use dpp::core_types::validator::v0::*; use dpp::dashcore::hashes::Hash; use dpp::dashcore::{ProTxHash, PubkeyHash}; @@ -8,7 +8,7 @@ use dpp::dashcore_rpc::json::{DMNState, MasternodeListItem}; pub(crate) trait NewValidatorIfMasternodeInState { fn new_validator_if_masternode_in_state( pro_tx_hash: ProTxHash, - public_key: Option>, + public_key: Option, state: &PlatformState, ) -> Option; } @@ -17,7 +17,7 @@ impl NewValidatorIfMasternodeInState for ValidatorV0 { /// Makes a validator if the masternode is in the list and is valid fn new_validator_if_masternode_in_state( pro_tx_hash: ProTxHash, - public_key: Option>, + public_key: Option, state: &PlatformState, ) -> Option { let MasternodeListItem { state, .. } = state.hpmn_masternode_list().get(&pro_tx_hash)?; diff --git a/packages/rs-drive-abci/src/platform_types/validator_set/v0/mod.rs b/packages/rs-drive-abci/src/platform_types/validator_set/v0/mod.rs index 4d639f64c1a..60fe3d57a3c 100644 --- a/packages/rs-drive-abci/src/platform_types/validator_set/v0/mod.rs +++ b/packages/rs-drive-abci/src/platform_types/validator_set/v0/mod.rs @@ -6,7 +6,7 @@ use dpp::dashcore::{ProTxHash, PubkeyHash}; use crate::platform_types::platform_state::PlatformState; use crate::platform_types::validator::v0::NewValidatorIfMasternodeInState; -use dpp::bls_signatures::PublicKey as BlsPublicKey; +use dpp::bls::PublicKey as BlsPublicKey; use dpp::core_types::validator::v0::ValidatorV0; pub use dpp::core_types::validator_set::v0::*; use dpp::dashcore_rpc::json::QuorumInfoResult; @@ -58,8 +58,8 @@ impl ValidatorSetMethodsV0 for ValidatorSetV0 { return Err(Error::Execution(ExecutionError::CorruptedCachedState( format!( "updating validator set doesn't match threshold public key ours: {} theirs: {}", - hex::encode(self.threshold_public_key.0.to_compressed()), - hex::encode(rhs.threshold_public_key.0.to_compressed()) + hex::encode(self.threshold_public_key.to_bytes()), + hex::encode(rhs.threshold_public_key.to_bytes()) ), ))); } @@ -97,7 +97,7 @@ impl ValidatorSetMethodsV0 for ValidatorSetV0 { Some(Ok(abci::ValidatorUpdate { pub_key: (*public_key).map(|public_key| crypto::PublicKey { - sum: Some(Bls12381(public_key.0.to_compressed().to_vec())), + sum: Some(Bls12381(public_key.to_bytes().to_vec())), }), power: 100, pro_tx_hash: pro_tx_hash.as_byte_array().to_vec(), @@ -123,7 +123,7 @@ impl ValidatorSetMethodsV0 for ValidatorSetV0 { Some(Ok(abci::ValidatorUpdate { pub_key: (*public_key).map(|public_key| crypto::PublicKey { - sum: Some(Bls12381(public_key.0.to_compressed().to_vec())), + sum: Some(Bls12381(public_key.to_bytes().to_vec())), }), power: 100, pro_tx_hash: pro_tx_hash.to_byte_array().to_vec(), @@ -139,9 +139,7 @@ impl ValidatorSetMethodsV0 for ValidatorSetV0 { Ok(ValidatorSetUpdate { validator_updates, threshold_public_key: Some(crypto::PublicKey { - sum: Some(Bls12381( - self.threshold_public_key.0.to_compressed().to_vec(), - )), + sum: Some(Bls12381(self.threshold_public_key.to_bytes().to_vec())), }), quorum_hash: self.quorum_hash.to_byte_array().to_vec(), }) @@ -174,7 +172,7 @@ impl ValidatorSetMethodsV0 for ValidatorSetV0 { let node_address = validator_node_address(node_id, node_ip, *platform_p2p_port); Some(abci::ValidatorUpdate { pub_key: public_key.as_ref().map(|public_key| crypto::PublicKey { - sum: Some(Bls12381(public_key.0.to_compressed().to_vec())), + sum: Some(Bls12381(public_key.to_bytes().to_vec())), }), power: 100, pro_tx_hash: pro_tx_hash.to_byte_array().to_vec(), @@ -183,7 +181,7 @@ impl ValidatorSetMethodsV0 for ValidatorSetV0 { }) .collect(), threshold_public_key: Some(crypto::PublicKey { - sum: Some(Bls12381(threshold_public_key.0.to_compressed().to_vec())), + sum: Some(Bls12381(threshold_public_key.to_bytes().to_vec())), }), quorum_hash: quorum_hash.to_byte_array().to_vec(), } @@ -217,7 +215,7 @@ impl ValidatorSetMethodsV0 for ValidatorSetV0 { Some(abci::ValidatorUpdate { pub_key: public_key.map(|public_key| crypto::PublicKey { - sum: Some(Bls12381(public_key.0.to_compressed().to_vec())), + sum: Some(Bls12381(public_key.to_bytes().to_vec())), }), power: 100, pro_tx_hash: pro_tx_hash.to_byte_array().to_vec(), @@ -226,7 +224,7 @@ impl ValidatorSetMethodsV0 for ValidatorSetV0 { }) .collect(), threshold_public_key: Some(crypto::PublicKey { - sum: Some(Bls12381(threshold_public_key.0.to_compressed().to_vec())), + sum: Some(Bls12381(threshold_public_key.to_bytes().to_vec())), }), quorum_hash: quorum_hash.to_byte_array().to_vec(), } @@ -315,7 +313,7 @@ fn validator_node_address(node_id: &PubkeyHash, node_ip: &str, platform_p2p_port #[cfg(test)] mod tests { use super::*; - use dpp::bls_signatures::{Bls12381G2Impl, SecretKey}; + use dpp::bls::SecretKey; use dpp::dashcore::hashes::Hash; use dpp::dashcore::{ProTxHash, PubkeyHash, QuorumHash}; use rand::rngs::StdRng; @@ -323,7 +321,7 @@ mod tests { fn make_validator(pro_tx_hash: ProTxHash, is_banned: bool) -> ValidatorV0 { let mut rng = StdRng::seed_from_u64(1); - let public_key = Some(SecretKey::::random(&mut rng).public_key()); + let public_key = Some(SecretKey::random(&mut rng).public_key()); ValidatorV0 { pro_tx_hash, public_key, @@ -343,7 +341,7 @@ mod tests { members: BTreeMap, ) -> ValidatorSetV0 { let mut rng = StdRng::seed_from_u64(threshold_seed); - let threshold_public_key = SecretKey::::random(&mut rng).public_key(); + let threshold_public_key = SecretKey::random(&mut rng).public_key(); ValidatorSetV0 { quorum_hash: QuorumHash::from_slice(&[quorum_hash_seed; 32]).unwrap(), quorum_index: Some(1), diff --git a/packages/rs-drive-abci/src/query/document_query/v1/tests.rs b/packages/rs-drive-abci/src/query/document_query/v1/tests.rs index cf2f59b6f15..3f0ac25dda1 100644 --- a/packages/rs-drive-abci/src/query/document_query/v1/tests.rs +++ b/packages/rs-drive-abci/src/query/document_query/v1/tests.rs @@ -3933,7 +3933,7 @@ mod having_trust_boundary { use crate::rpc::core::MockCoreRPCLike; use dapi_grpc::platform::v0::{Proof, ResponseMetadata}; use dpp::block::block_info::BlockInfo; - use dpp::bls_signatures::{Bls12381G2Impl, SecretKey, SignatureSchemes}; + use dpp::bls::SecretKey; use dpp::dashcore::Network; use dpp::data_contract::accessors::v0::DataContractV0Getters; use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; @@ -4024,12 +4024,10 @@ mod having_trust_boundary { } /// A deterministic, valid BLS scalar — no RNG dependency. - pub(super) fn quorum_secret_key() -> SecretKey { + pub(super) fn quorum_secret_key() -> SecretKey { let mut bytes = [0u8; 32]; bytes[31] = 42; - SecretKey::::from_be_bytes(&bytes) - .into_option() - .expect("a small nonzero scalar is a valid secret key") + SecretKey::from_be_bytes(&bytes).expect("a small nonzero scalar is a valid secret key") } fn register_grades( @@ -4192,7 +4190,7 @@ mod having_trust_boundary { grovedb_proof: Vec, app_hash: &[u8; 32], mtd: &ResponseMetadata, - secret_key: &SecretKey, + secret_key: &SecretKey, quorum_hash: [u8; 32], ) -> Proof { let block_id_hash = [7u8; 32].to_vec(); @@ -4224,10 +4222,9 @@ mod having_trust_boundary { ) .expect("sign digest"); let signature = secret_key - .sign(SignatureSchemes::Basic, &sign_digest) + .sign(&sign_digest) .expect("signing with a valid key succeeds") - .as_raw_value() - .to_compressed() + .to_bytes() .to_vec(); Proof { grovedb_proof, @@ -4254,7 +4251,7 @@ mod having_trust_boundary { let mtd = metadata(); let proof = signed_proof(grovedb_proof, &root_hash, &mtd, &secret_key, quorum_hash); let provider = TestQuorumProvider { - pubkey: secret_key.public_key().0.to_compressed(), + pubkey: secret_key.public_key().to_bytes(), }; let query = client_side_query(&contract); @@ -4291,7 +4288,7 @@ mod having_trust_boundary { quorum_hash, ); let provider = TestQuorumProvider { - pubkey: secret_key.public_key().0.to_compressed(), + pubkey: secret_key.public_key().to_bytes(), }; let query = client_side_query(&contract); @@ -4316,7 +4313,7 @@ mod having_trust_boundary { let mtd = metadata(); let proof = signed_proof(grovedb_proof, &root_hash, &mtd, &secret_key, quorum_hash); let provider = TestQuorumProvider { - pubkey: secret_key.public_key().0.to_compressed(), + pubkey: secret_key.public_key().to_bytes(), }; let mut tampered = mtd; @@ -4347,11 +4344,9 @@ mod having_trust_boundary { let mut other_bytes = [0u8; 32]; other_bytes[31] = 43; - let other_key = SecretKey::::from_be_bytes(&other_bytes) - .into_option() - .expect("valid scalar"); + let other_key = SecretKey::from_be_bytes(&other_bytes).expect("valid scalar"); let provider = TestQuorumProvider { - pubkey: other_key.public_key().0.to_compressed(), + pubkey: other_key.public_key().to_bytes(), }; let query = client_side_query(&contract); @@ -4665,7 +4660,7 @@ mod time_range_proof_verification { QUORUM_HASH, ); let provider = TestQuorumProvider { - pubkey: secret_key.public_key().0.to_compressed(), + pubkey: secret_key.public_key().to_bytes(), }; (signed, mtd, provider) } @@ -6711,7 +6706,7 @@ mod chained_trust_boundary { let mtd = metadata(); let proof = signed_proof(grovedb_proof, &root_hash, &mtd, &secret_key, quorum_hash); let provider = TestQuorumProvider { - pubkey: secret_key.public_key().0.to_compressed(), + pubkey: secret_key.public_key().to_bytes(), }; let (verified, _mtd, _proof) = @@ -6754,14 +6749,10 @@ mod chained_trust_boundary { let other_key = { let mut bytes = [0u8; 32]; bytes[31] = 43; - dpp::bls_signatures::SecretKey::::from_be_bytes( - &bytes, - ) - .into_option() - .expect("valid scalar") + dpp::bls::SecretKey::from_be_bytes(&bytes).expect("valid scalar") }; let provider = TestQuorumProvider { - pubkey: other_key.public_key().0.to_compressed(), + pubkey: other_key.public_key().to_bytes(), }; let refused = @@ -6788,7 +6779,7 @@ mod chained_trust_boundary { let mtd = metadata(); let proof = signed_proof(grovedb_proof, &root_hash, &mtd, &secret_key, [5u8; 32]); let provider = TestQuorumProvider { - pubkey: secret_key.public_key().0.to_compressed(), + pubkey: secret_key.public_key().to_bytes(), }; let mut tampered = mtd; tampered.height += 1; @@ -7048,7 +7039,7 @@ mod composite_trust_boundary { let mtd = metadata(); let proof = signed_proof(grovedb_proof, &root_hash, &mtd, &secret_key, quorum_hash); let provider = TestQuorumProvider { - pubkey: secret_key.public_key().0.to_compressed(), + pubkey: secret_key.public_key().to_bytes(), }; let response = response_with(proof, mtd); @@ -7205,14 +7196,10 @@ mod composite_trust_boundary { let other_key = { let mut bytes = [0u8; 32]; bytes[31] = 44; - dpp::bls_signatures::SecretKey::::from_be_bytes( - &bytes, - ) - .into_option() - .expect("valid scalar") + dpp::bls::SecretKey::from_be_bytes(&bytes).expect("valid scalar") }; let provider = TestQuorumProvider { - pubkey: other_key.public_key().0.to_compressed(), + pubkey: other_key.public_key().to_bytes(), }; let refused = @@ -7240,7 +7227,7 @@ mod composite_trust_boundary { let mtd = metadata(); let proof = signed_proof(grovedb_proof, &root_hash, &mtd, &secret_key, [6u8; 32]); let provider = TestQuorumProvider { - pubkey: secret_key.public_key().0.to_compressed(), + pubkey: secret_key.public_key().to_bytes(), }; let mut tampered = mtd; tampered.height += 1; @@ -7285,7 +7272,7 @@ mod composite_trust_boundary { let mtd = metadata(); let proof = signed_proof(grovedb_proof, &root_hash, &mtd, &secret_key, [6u8; 32]); let provider = TestQuorumProvider { - pubkey: secret_key.public_key().0.to_compressed(), + pubkey: secret_key.public_key().to_bytes(), }; let refused = diff --git a/packages/rs-drive-abci/src/query/system/current_quorums_info/v0/mod.rs b/packages/rs-drive-abci/src/query/system/current_quorums_info/v0/mod.rs index ee6fa018a85..b5c684c80f1 100644 --- a/packages/rs-drive-abci/src/query/system/current_quorums_info/v0/mod.rs +++ b/packages/rs-drive-abci/src/query/system/current_quorums_info/v0/mod.rs @@ -41,11 +41,7 @@ impl Platform { quorum_hash: validator_set.quorum_hash().as_byte_array().to_vec(), core_height: validator_set.core_height(), members, - threshold_public_key: validator_set - .threshold_public_key() - .0 - .to_compressed() - .to_vec(), + threshold_public_key: validator_set.threshold_public_key().to_bytes().to_vec(), } }) .collect(); diff --git a/packages/rs-drive-abci/tests/strategy_tests/execution.rs b/packages/rs-drive-abci/tests/strategy_tests/execution.rs index 6d25d111b30..e0f3b4fac3e 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/execution.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/execution.rs @@ -19,7 +19,7 @@ use dpp::identity::accessors::IdentityGettersV0; use dpp::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0; use strategy_tests::operations::FinalizeBlockOperation::IdentityAddKeys; -use dpp::bls_signatures::{Bls12381G2Impl, SecretKey as BlsPrivateKey, SignatureSchemes}; +use dpp::bls::SecretKey as BlsPrivateKey; use dpp::dashcore::consensus::Encodable; use dpp::dashcore::hashes::{sha256d, HashEngine}; use dpp::dashcore::{ChainLock, QuorumSigningRequestId, VarInt}; @@ -733,19 +733,15 @@ pub(crate) async fn run_chain_for_strategy<'a>( let message_digest = sha256d::Hash::from_engine(engine); - let quorum_private_key = - BlsPrivateKey::::from_be_bytes(quorum_private_key) - .expect("expected to have a valid private key"); + let quorum_private_key = BlsPrivateKey::from_be_bytes(quorum_private_key) + .expect("expected to have a valid private key"); let signature = quorum_private_key - .sign( - SignatureSchemes::Basic, - message_digest.as_byte_array().as_slice(), - ) + .sign(message_digest.as_byte_array().as_slice()) .expect("expected to sign"); let chain_lock = ChainLock { block_height, block_hash: BlockHash::from_byte_array(*block_hash), - signature: signature.as_raw_value().to_compressed().into(), + signature: signature.to_bytes().into(), }; Ok(chain_lock) @@ -860,9 +856,7 @@ pub(crate) async fn start_chain_for_strategy<'a>( .map( |validator_in_quorum| tenderdash_abci::proto::abci::ValidatorUpdate { pub_key: Some(tenderdash_abci::proto::crypto::PublicKey { - sum: Some(Bls12381( - validator_in_quorum.public_key.0.to_compressed().to_vec(), - )), + sum: Some(Bls12381(validator_in_quorum.public_key.to_bytes().to_vec())), }), power: 100, pro_tx_hash: validator_in_quorum.pro_tx_hash.to_byte_array().to_vec(), @@ -872,11 +866,7 @@ pub(crate) async fn start_chain_for_strategy<'a>( .collect(), threshold_public_key: Some(tenderdash_abci::proto::crypto::PublicKey { sum: Some(Bls12381( - current_quorum_with_test_info - .public_key - .0 - .to_compressed() - .to_vec(), + current_quorum_with_test_info.public_key.to_bytes().to_vec(), )), }), quorum_hash: current_validator_quorum_hash.to_byte_array().to_vec(), diff --git a/packages/rs-drive-abci/tests/strategy_tests/main.rs b/packages/rs-drive-abci/tests/strategy_tests/main.rs index 755fb41b856..10854de9c87 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/main.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/main.rs @@ -34,7 +34,7 @@ #![allow(unused_variables)] extern crate core; -use dpp::bls_signatures::SecretKey as BlsPrivateKey; +use dpp::bls::SecretKey as BlsPrivateKey; mod addresses_with_balance; mod execution; mod failures; diff --git a/packages/rs-drive-abci/tests/strategy_tests/masternode_list_item_helpers.rs b/packages/rs-drive-abci/tests/strategy_tests/masternode_list_item_helpers.rs index 3ada8f30353..c9dbe379d11 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/masternode_list_item_helpers.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/masternode_list_item_helpers.rs @@ -1,5 +1,5 @@ use crate::BlsPrivateKey; -use dpp::bls_signatures::Bls12381G2Impl; + use dpp::dashcore_rpc::json::MasternodeListItem; use rand::prelude::IteratorRandom; use rand::rngs::StdRng; @@ -39,12 +39,13 @@ impl UpdateMasternodeListItem for MasternodeListItem { .expect("expected to generate a private key") .to_bytes() .to_vec(); - let private_key_operator = BlsPrivateKey::::from_be_bytes( - &private_key_operator_bytes.try_into().expect("expected the secret key to be 32 bytes"), + let private_key_operator = BlsPrivateKey::from_be_bytes( + &private_key_operator_bytes + .try_into() + .expect("expected the secret key to be 32 bytes"), ) - .expect("expected the conversion between bls signatures library and blsful to happen without failing"); - let pub_key_operator = - private_key_operator.public_key().0.to_compressed().to_vec(); + .expect("Core fixture secret key must decode"); + let pub_key_operator = private_key_operator.public_key().to_bytes().to_vec(); self.state.pub_key_operator = pub_key_operator; } 4 => { @@ -97,11 +98,13 @@ mod tests { .expect("expected to generate a private key") .to_bytes() .to_vec(); - let private_key_operator = BlsPrivateKey::::from_be_bytes( - &private_key_operator_bytes.try_into().expect("expected the secret key to be 32 bytes"), + let private_key_operator = BlsPrivateKey::from_be_bytes( + &private_key_operator_bytes + .try_into() + .expect("expected the secret key to be 32 bytes"), ) - .expect("expected the conversion between bls signatures library and blsful to happen without failing"); - let pub_key_operator = private_key_operator.public_key().0.to_compressed().to_vec(); + .expect("Core fixture secret key must decode"); + let pub_key_operator = private_key_operator.public_key().to_bytes().to_vec(); let masternode_list_item = MasternodeListItem { node_type: MasternodeType::Regular, pro_tx_hash, diff --git a/packages/rs-drive-abci/tests/strategy_tests/masternodes.rs b/packages/rs-drive-abci/tests/strategy_tests/masternodes.rs index 60d53f8fabc..10b786ded31 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/masternodes.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/masternodes.rs @@ -1,5 +1,5 @@ use crate::masternode_list_item_helpers::UpdateMasternodeListItem; -use dpp::bls_signatures::{Bls12381G2Impl, SecretKey as BlsPrivateKey}; +use dpp::bls::SecretKey as BlsPrivateKey; use dpp::dashcore::hashes::Hash; use dpp::dashcore::{ProTxHash, QuorumHash, Txid}; use dpp::dashcore_rpc::dashcore_rpc_json::{DMNState, MasternodeListItem, MasternodeType}; @@ -233,11 +233,13 @@ pub fn generate_test_masternodes( .expect("expected to generate a private key") .to_bytes() .to_vec(); - let private_key_operator = BlsPrivateKey::::from_be_bytes( - &private_key_operator_bytes.try_into().expect("expected the secret key to be 32 bytes"), + let private_key_operator = BlsPrivateKey::from_be_bytes( + &private_key_operator_bytes + .try_into() + .expect("expected the secret key to be 32 bytes"), ) - .expect("expected the conversion between bls signatures library and blsful to happen without failing"); - let pub_key_operator = private_key_operator.public_key().0.to_compressed().to_vec(); + .expect("Core fixture secret key must decode"); + let pub_key_operator = private_key_operator.public_key().to_bytes().to_vec(); let pro_tx_hash = ProTxHash::from_byte_array(rng.gen::<[u8; 32]>()); let masternode_list_item = MasternodeListItem { node_type: MasternodeType::Regular, @@ -375,11 +377,13 @@ pub fn generate_test_masternodes( .expect("expected to generate a private key") .to_bytes() .to_vec(); - let private_key_operator = BlsPrivateKey::::from_be_bytes( - &private_key_operator_bytes.try_into().expect("expected the secret key to be 32 bytes"), + let private_key_operator = BlsPrivateKey::from_be_bytes( + &private_key_operator_bytes + .try_into() + .expect("expected the secret key to be 32 bytes"), ) - .expect("expected the conversion between bls signatures library and blsful to happen without failing"); - let pub_key_operator = private_key_operator.public_key().0.to_compressed().to_vec(); + .expect("Core fixture secret key must decode"); + let pub_key_operator = private_key_operator.public_key().to_bytes().to_vec(); let masternode_list_item = MasternodeListItem { node_type: MasternodeType::Evo, pro_tx_hash: ProTxHash::from_byte_array(rng.gen::<[u8; 32]>()), diff --git a/packages/rs-drive-abci/tests/strategy_tests/query.rs b/packages/rs-drive-abci/tests/strategy_tests/query.rs index a7b89ac350c..224a81325e9 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/query.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/query.rs @@ -9,7 +9,7 @@ use dapi_grpc::platform::v0::{ get_identity_by_public_key_hash_request, get_identity_by_public_key_hash_response, GetIdentityByPublicKeyHashRequest, Proof, }; -use dpp::bls_signatures::{Bls12381G2Impl, BlsError, Pairing, Signature}; +use dpp::bls::{BlsError, Signature}; use dpp::dashcore_rpc::dashcore_rpc_json::QuorumType; use dpp::identity::accessors::IdentityGettersV0; use dpp::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0; @@ -74,7 +74,7 @@ pub struct ProofVerification<'a> { pub signature: &'a [u8; 96], /// Threshold key used to verify the signature - pub public_key: &'a dpp::bls_signatures::PublicKey, + pub public_key: &'a dpp::bls::PublicKey, } impl ProofVerification<'_> { @@ -111,20 +111,17 @@ impl ProofVerification<'_> { Err(e) => return SimpleValidationResult::new_with_error(e.into()), }; // We could have received a fake commit, so signature validation needs to be returned if error as a simple validation result - let signature = - match ::Signature::from_compressed(self.signature) - .into_option() - { - Some(signature) => Signature::Basic(signature), - None => { - return SimpleValidationResult::new_with_error( - AbciError::BlsErrorOfTenderdashThresholdMechanism( - BlsError::InvalidSignature, - format!("malformed signature data: {}", hex::encode(self.signature)), - ), - ); - } - }; + let signature = match Signature::from_compressed(self.signature) { + Some(signature) => signature, + None => { + return SimpleValidationResult::new_with_error( + AbciError::BlsErrorOfTenderdashThresholdMechanism( + BlsError::InvalidSignature, + format!("malformed signature data: {}", hex::encode(self.signature)), + ), + ); + } + }; tracing::trace!( digest=hex::encode(&digest), ?state_id, diff --git a/packages/rs-drive-abci/tests/strategy_tests/test_cases/address_tests.rs b/packages/rs-drive-abci/tests/strategy_tests/test_cases/address_tests.rs index e048c50ba76..ef3b61472ba 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/test_cases/address_tests.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/test_cases/address_tests.rs @@ -75,12 +75,7 @@ mod tests { .iter() .map(|(quorum_hash, quorum_info)| { let quorum_hash_bytes: [u8; 32] = *quorum_hash.as_raw_hash().as_byte_array(); - let public_key_bytes: [u8; 48] = quorum_info - .public_key - .0 - .to_compressed() - .try_into() - .expect("public key should be 48 bytes"); + let public_key_bytes: [u8; 48] = quorum_info.public_key.to_bytes(); ((quorum_hash_bytes, quorum_type), public_key_bytes) }) .collect(); diff --git a/packages/rs-drive-proof-verifier/src/unproved.rs b/packages/rs-drive-proof-verifier/src/unproved.rs index fde99cc3399..e2a7aeea126 100644 --- a/packages/rs-drive-proof-verifier/src/unproved.rs +++ b/packages/rs-drive-proof-verifier/src/unproved.rs @@ -24,7 +24,7 @@ use dapi_grpc::platform::v0::ResponseMetadata; use dapi_grpc::platform::v0::{self as platform}; use dapi_grpc::platform::v0::get_contract_moderation_action_counts_response::get_contract_moderation_action_counts_response_v0::Result as ModerationActionCountsResult; use dapi_grpc::tonic::async_trait; -use dpp::bls_signatures::PublicKey as BlsPublicKey; +use dpp::bls::PublicKey as BlsPublicKey; use dpp::core_types::validator::v0::ValidatorV0; use dpp::core_types::validator_set::v0::ValidatorSetV0; use dpp::core_types::validator_set::ValidatorSet; @@ -315,7 +315,7 @@ mod tests { use dapi_grpc::platform::v0::{ get_current_quorums_info_response, get_status_response, ResponseMetadata, }; - use dpp::bls_signatures::{Bls12381G2Impl, SecretKey}; + use dpp::bls::SecretKey; use dpp::version::PlatformVersion; /// Generate a valid BLS public key as compressed bytes (48 bytes) from a @@ -323,11 +323,8 @@ mod tests { fn generate_valid_bls_public_key_bytes(seed: u8) -> Vec { let mut secret_bytes = [0u8; 32]; secret_bytes[31] = seed.max(1); // ensure nonzero - let sk: SecretKey = - SecretKey::::from_be_bytes(&secret_bytes) - .into_option() - .expect("valid secret key"); - sk.public_key().0.to_compressed().to_vec() + let sk: SecretKey = SecretKey::from_be_bytes(&secret_bytes).expect("valid secret key"); + sk.public_key().to_bytes().to_vec() } /// Helper: build a valid GetCurrentQuorumsInfoResponse with one quorum hash, diff --git a/packages/rs-drive-proof-verifier/src/verify.rs b/packages/rs-drive-proof-verifier/src/verify.rs index 13e5188b5e8..b60472b7a6d 100644 --- a/packages/rs-drive-proof-verifier/src/verify.rs +++ b/packages/rs-drive-proof-verifier/src/verify.rs @@ -1,8 +1,8 @@ use dapi_grpc::platform::v0::{Proof, ResponseMetadata}; -use dpp::bls_signatures; +use dpp::bls; use crate::Error; -use dpp::bls_signatures::{Bls12381G2Impl, Pairing, Signature}; +use dpp::bls::Signature; use tenderdash_abci::{ proto::types::{CanonicalVote, SignedMsgType, StateId}, signatures::{Hashable, Signable}, @@ -134,8 +134,8 @@ pub(crate) fn verify_tenderdash_signature( } })?; - let pubkey = bls_signatures::PublicKey::::try_from(pubkey_bytes.as_slice()) - .map_err(|e| Error::InvalidPublicKey { + let pubkey = + bls::PublicKey::try_from(pubkey_bytes.as_slice()).map_err(|e| Error::InvalidPublicKey { error: e.to_string(), })?; @@ -164,20 +164,17 @@ pub(crate) fn verify_tenderdash_signature( pub fn verify_signature_digest( sign_digest: &[u8], signature: &[u8; 96], - public_key: &bls_signatures::PublicKey, + public_key: &bls::PublicKey, ) -> Result { if signature == &[0; 96] { return Err(Error::SignatureVerificationError { error: "empty signature".to_string(), }); } - let signature = Signature::Basic( - ::Signature::from_compressed(signature) - .into_option() - .ok_or(Error::SignatureVerificationError { - error: "Could not verify signature digest".to_string(), - })?, - ); + let signature = + Signature::from_compressed(signature).ok_or(Error::SignatureVerificationError { + error: "Could not verify signature digest".to_string(), + })?; Ok(signature.verify(public_key, sign_digest).is_ok()) } @@ -339,11 +336,13 @@ mod tests { } /// Helper: create a deterministic BLS key pair for testing. - fn test_keypair() -> ( - bls_signatures::SecretKey, - bls_signatures::PublicKey, - ) { - let sk = bls_signatures::SecretKey::::from_hash(b"test-key-seed"); + fn test_keypair() -> (bls::SecretKey, bls::PublicKey) { + let scalar = + hex::decode("659dc45452f29c17f96dda38ae1546e0e9e818423cdb8cc857f3728c518b4cb8") + .unwrap() + .try_into() + .unwrap(); + let sk = bls::SecretKey::from_be_bytes(&scalar).unwrap(); let pk = sk.public_key(); (sk, pk) } @@ -358,7 +357,7 @@ mod tests { impl ValidKeyContextProvider { fn new() -> Self { let (_, pk) = test_keypair(); - let pk_vec: Vec = (&pk).into(); + let pk_vec = pk.to_bytes().to_vec(); let mut pubkey_bytes = [0u8; 48]; pubkey_bytes.copy_from_slice(&pk_vec); Self { pubkey_bytes } diff --git a/packages/rs-platform-wallet/src/masternode/locator.rs b/packages/rs-platform-wallet/src/masternode/locator.rs index aea3f136a98..81f482c7949 100644 --- a/packages/rs-platform-wallet/src/masternode/locator.rs +++ b/packages/rs-platform-wallet/src/masternode/locator.rs @@ -38,9 +38,7 @@ use dash_sdk::platform::Fetch; use dashcore::hashes::{hash160, Hash}; use dashcore::secp256k1::{PublicKey as SecpPublicKey, SecretKey as SecpSecretKey}; use dashcore::{Network, PrivateKey}; -use dpp::bls_signatures::{ - Bls12381G2Impl, PublicKey as BlsPublicKey, SecretKey as BlsSecretKey, SerializationFormat, -}; +use dpp::bls::SecretKey as BlsSecretKey; use dpp::ed25519_dalek::SigningKey; use dpp::identifier::MasternodeIdentifiers; use dpp::identity::accessors::IdentityGettersV0; @@ -371,18 +369,10 @@ pub fn ecdsa_key_id(secret: &[u8; 32], compressed: bool) -> Option<[u8; 20]> { } /// `(basic, legacy)` 48-byte serializations of the BLS public key for -/// `secret`, or `None` when the scalar is not below the group order. +/// `secret`, or `None` when the scalar reduces to zero. pub fn bls_public_keys(secret: &[u8; 32]) -> Option<([u8; 48], [u8; 48])> { - let sk: BlsSecretKey = - Option::from(BlsSecretKey::::from_be_bytes(secret))?; - let pk = BlsPublicKey::from(&sk); - let basic: [u8; 48] = pk.to_bytes().as_slice().try_into().ok()?; - let legacy: [u8; 48] = pk - .to_bytes_with_mode(SerializationFormat::Legacy) - .as_slice() - .try_into() - .ok()?; - Some((basic, legacy)) + let pk = BlsSecretKey::from_be_bytes(secret)?.public_key(); + Some((pk.to_bytes(), pk.to_legacy_bytes().ok()?)) } /// Tenderdash node id for an ed25519 `seed`. diff --git a/packages/rs-platform-wallet/src/masternode/update_service.rs b/packages/rs-platform-wallet/src/masternode/update_service.rs index 673dcd9a3d8..a1e5be1c627 100644 --- a/packages/rs-platform-wallet/src/masternode/update_service.rs +++ b/packages/rs-platform-wallet/src/masternode/update_service.rs @@ -26,7 +26,7 @@ use dashcore::hash_types::InputsHash; use dashcore::hashes::Hash; use dashcore::platform_node_id::PlatformNodeId; use dashcore::{Address as DashAddress, Network, Transaction, Txid}; -use dpp::bls_signatures::{Bls12381G2Impl, SecretKey as BlsSecretKey, SignatureSchemes}; +use dpp::bls::SecretKey as BlsSecretKey; use key_wallet::wallet::managed_wallet_info::transaction_builder::{ BuilderError, TransactionBuilder, TransactionSigner, }; @@ -405,25 +405,13 @@ pub(crate) fn finalize_update_service_payload( let mut finalized = placeholder.clone(); finalized.inputs_hash = unsigned.hash_inputs(); - let secret = Option::>::from( - BlsSecretKey::::from_be_bytes(operator_secret), - ) - .ok_or_else(|| { + let secret = BlsSecretKey::from_be_bytes(operator_secret).ok_or_else(|| { BuilderError::SigningFailed("the operator key is not a valid BLS secret".into()) })?; let signature = secret - .sign( - SignatureSchemes::Basic, - finalized.base_payload_hash().as_byte_array(), - ) + .sign(finalized.base_payload_hash().as_byte_array()) .map_err(|e| BuilderError::SigningFailed(format!("BLS payload signing failed: {e}")))?; - let signature_bytes: [u8; 96] = signature - .to_bytes_with_mode(dpp::bls_signatures::SerializationFormat::Modern) - .as_slice() - .try_into() - .map_err(|_| { - BuilderError::SigningFailed("BLS signature did not serialize to 96 bytes".into()) - })?; + let signature_bytes = signature.to_bytes(); finalized.payload_sig = BLSSignature::from(signature_bytes); Ok(TransactionPayload::ProviderUpdateServicePayloadType( finalized, @@ -465,7 +453,7 @@ mod tests { use crate::test_support::{ funded_wallet_manager, funded_wallet_manager_with_outputs, WalletSigner, }; - use dpp::bls_signatures::{PublicKey as BlsPublicKey, Signature as BlsSignature}; + use dpp::bls::{PublicKey as BlsPublicKey, Signature as BlsSignature}; use key_wallet::account::StandardAccountType; use std::sync::{Arc, Mutex}; @@ -801,17 +789,10 @@ mod tests { // The payload signature verifies under the basic scheme against the // operator public key, over base_payload_hash — the exact convention // `verify_message_digest` checks real mainnet signatures with. - let secret = Option::>::from( - BlsSecretKey::::from_be_bytes(&OPERATOR_SECRET), - ) - .expect("valid test scalar"); + let secret = BlsSecretKey::from_be_bytes(&OPERATOR_SECRET).expect("valid test scalar"); let public_key = BlsPublicKey::from(&secret); - let signature = BlsSignature::::from_bytes_with_mode( - payload.payload_sig.as_bytes(), - SignatureSchemes::Basic, - dpp::bls_signatures::SerializationFormat::Modern, - ) - .expect("compressed signature decodes"); + let signature = BlsSignature::from_compressed(payload.payload_sig.as_bytes()) + .expect("compressed signature decodes"); signature .verify(&public_key, payload.base_payload_hash().as_byte_array()) .expect("operator BLS signature verifies over base_payload_hash"); diff --git a/packages/rs-sdk-ffi/src/system/queries/current_quorums_info.rs b/packages/rs-sdk-ffi/src/system/queries/current_quorums_info.rs index 6105efcd5bc..d5580ebce62 100644 --- a/packages/rs-sdk-ffi/src/system/queries/current_quorums_info.rs +++ b/packages/rs-sdk-ffi/src/system/queries/current_quorums_info.rs @@ -105,7 +105,7 @@ fn get_current_quorums_info(sdk_handle: *const SDKHandle) -> Result for SimpleSigner { Ok(signature.to_vec().into()) } KeyType::BLS12_381 => { - let pk = bls_signatures::SecretKey::::from_be_bytes(private_key) - .into_option() - .ok_or(ProtocolError::Generic( - "bls private key from bytes isn't correct".to_string(), - ))?; + let pk = bls::SecretKey::from_be_bytes(private_key).ok_or( + ProtocolError::Generic("bls private key from bytes isn't correct".to_string()), + )?; let signature = pk - .sign(SignatureSchemes::Basic, data) + .sign(data) .map_err(|e| ProtocolError::Generic(format!("BLS signing failed {}", e)))?; - Ok(signature.as_raw_value().to_compressed().to_vec().into()) + Ok(signature.to_bytes().to_vec().into()) } KeyType::EDDSA_25519_HASH160 => { #[allow(clippy::unnecessary_fallible_conversions)] From 8377b64f31b27ddedfe93c418518fecea055c775 Mon Sep 17 00:00:00 2001 From: "Claudius the Magnificent AI, on behalf of lklimek" <8431764+Claudius-Maginificent@users.noreply.github.com> Date: Thu, 8 Oct 2026 13:27:26 +0200 Subject: [PATCH 11/22] fix(platform-wallet)!: tell a retryable SPV stop from one that needs a restart (#5322) Co-authored-by: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Co-authored-by: Codex GPT-6 --- .../dashsdk/errors/DashSdkError.kt | 26 ++ .../dashsdk/errors/DashSdkErrorTest.kt | 22 ++ .../ERROR_CODE_REGISTRY.md | 6 +- packages/rs-platform-wallet-ffi/src/error.rs | 35 +++ packages/rs-platform-wallet-ffi/src/spv.rs | 8 +- packages/rs-platform-wallet/src/error.rs | 11 + .../rs-platform-wallet/src/spv/runtime.rs | 290 ++++++++++++++++-- .../PlatformWalletManagerSPV.swift | 6 +- .../PlatformWallet/PlatformWalletResult.swift | 20 +- .../ErrorHandlingTests.swift | 20 ++ 10 files changed, 411 insertions(+), 33 deletions(-) diff --git a/packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/errors/DashSdkError.kt b/packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/errors/DashSdkError.kt index 420ad1a6bbb..5db48d4f6d9 100644 --- a/packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/errors/DashSdkError.kt +++ b/packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/errors/DashSdkError.kt @@ -14,6 +14,10 @@ private const val PERSISTER_UNREADABLE_USER_MESSAGE = private const val PERSISTER_UNSAVED_USER_MESSAGE = "The wallet data could not be saved and may need to be restored." +// Display text for a panicked SPV teardown; the native message is the panic text. +private const val SPV_PROCESS_RESTART_REQUIRED_USER_MESSAGE = + "Sync could not be stopped cleanly. Restart the app to use it again." + /** * Public error hierarchy of the Kotlin SDK — the Android analog of the * Swift SDK's `UserFacingError`/`SDKError` split, keyed off the native @@ -615,6 +619,26 @@ sealed class DashSdkError( class ShieldedRecoveryKeysRequired(message: String, cause: Throwable? = null) : PlatformWallet(message, cause) + /** + * `ErrorShutdownIncomplete` (native code 27). A sync pass or an SPV + * teardown was still running; nothing was wiped. Retry the stop or + * clear. An SPV start refused with this needs an SPV stop first. + */ + class ShutdownIncomplete(message: String, cause: Throwable? = null) : + PlatformWallet(message, cause) { + override val isRetryable: Boolean get() = true + } + + /** + * `ErrorSpvProcessRestartRequired` (native code 59). SPV startup or + * teardown panicked. Not retryable: restart the app process. + * [message] is the panic detail; display [userMessage]. + */ + class SpvProcessRestartRequired(message: String, cause: Throwable? = null) : + PlatformWallet(message, cause) { + override val userMessage: String get() = SPV_PROCESS_RESTART_REQUIRED_USER_MESSAGE + } + /** * Any other `PlatformWalletFFIResultCode` without a dedicated type. * Carries the platform-wallet [nativeCode] (already de-offset) and @@ -737,6 +761,7 @@ sealed class DashSdkError( 24 -> PlatformWallet.AssetLockAlreadyConsumed(message, cause) // ErrorAssetLockAlreadyConsumed 25 -> PlatformWallet.AssetLockFundingMismatch(message, cause) // ErrorAssetLockFundingMismatch 26 -> PlatformWallet.TransactionBroadcastRejected(message, cause) // ErrorTransactionBroadcastRejected + 27 -> PlatformWallet.ShutdownIncomplete(message, cause) // ErrorShutdownIncomplete 29 -> PlatformWallet.AssetLockInsufficientFunds(message, cause) // ErrorAssetLockInsufficientFunds // The deferred-token trio sits at the contiguous block 34-36 because // 27-33 are claimed elsewhere: 27 ErrorShutdownIncomplete @@ -808,6 +833,7 @@ sealed class DashSdkError( 55 -> PlatformWallet.ShieldedIdentityDebitPending(message, cause) 56 -> PlatformWallet.ShieldedRecoveryCorrupted(message, cause) 57 -> PlatformWallet.ShieldedRecoveryKeysRequired(message, cause) + 59 -> PlatformWallet.SpvProcessRestartRequired(message, cause) // ErrorSpvProcessRestartRequired else -> // @Deprecated fallback — see the code-6 arm; code 31 is the // real discriminator. diff --git a/packages/kotlin-sdk/sdk/src/test/kotlin/org/dashfoundation/dashsdk/errors/DashSdkErrorTest.kt b/packages/kotlin-sdk/sdk/src/test/kotlin/org/dashfoundation/dashsdk/errors/DashSdkErrorTest.kt index 7129c55f633..3bb82014a4a 100644 --- a/packages/kotlin-sdk/sdk/src/test/kotlin/org/dashfoundation/dashsdk/errors/DashSdkErrorTest.kt +++ b/packages/kotlin-sdk/sdk/src/test/kotlin/org/dashfoundation/dashsdk/errors/DashSdkErrorTest.kt @@ -227,6 +227,28 @@ class DashSdkErrorTest { assertFalse("Generic platform-wallet errors are not retryable", mapped.isRetryable) } + @Test + fun spvTeardownCodes27And59MapTypedWithOppositeRetryability() { + val offset = DashSdkError.PLATFORM_WALLET_CODE_OFFSET + + val incomplete = + DashSdkError.fromNative(DashSDKException(offset + 27, "SPV teardown timed out")) + assertTrue(incomplete is DashSdkError.PlatformWallet.ShutdownIncomplete) + assertEquals("SPV teardown timed out", incomplete.message) + assertTrue("a tracked teardown completes on a repeated stop", incomplete.isRetryable) + + val panicDetail = "teardown task 12 panicked with message \"boom\"" + val restart = DashSdkError.fromNative(DashSDKException(offset + 59, panicDetail)) + assertTrue(restart is DashSdkError.PlatformWallet.SpvProcessRestartRequired) + assertEquals(panicDetail, restart.message) + assertFalse("no retry recovers a panicked teardown", restart.isRetryable) + assertEquals( + "the panic detail is diagnostic and must not be the displayed text", + "Sync could not be stopped cleanly. Restart the app to use it again.", + restart.userMessage, + ) + } + @Test fun platformShieldCapacityCode41MapsTyped() { val message = diff --git a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md index 3399060f53b..0b68fb196c0 100644 --- a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md +++ b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md @@ -114,13 +114,14 @@ These are shipped ABI. Do not renumber. | 98 | `NotFound` | Sentinel — `Option` returned as an error | | 99 | `ErrorUnknown` | Sentinel — unmapped/flattened errors | -**Next allocatable integer: 59** — 27–58 are all claimed (27, 29, 31, 34–42 +**Next allocatable integer: 60** — 27–59 are all claimed (27, 29, 31, 34–42 and 46 merged; 43–45 proposed by active #4313 at head `0302b188ab`; 47 and 48 proposed by active #4356 (47 renumbered from 42, 48 from 43 — see their rows below); 49–54 proposed by active #4586 (the persister operation × kind block); 55–57 proposed by #4715 for pending identity-funded shield debits and durable recovery errors; 58 proposed by #4799 for an unavailable -identity balance response; 28, 30, +identity balance response; 59 proposed by #5322 for an SPV +teardown that needs a process restart; 28, 30, 32 and 33 reserved). **28, 30, 32 and 33 are RESERVED, not free**: 28 and 30 were vacated when the reservation trio moved to 34–36; 32 and 33 lapsed when their in-repo owners @@ -172,6 +173,7 @@ Fork-era numbers remain in the collision history, which is immutable record. | 56 | `ErrorShieldedRecoveryCorrupted` | #4715 | Proposed — durable shielded recovery data is malformed or invalid; preserved for diagnosis. Rust, Swift and Kotlin preserve this typed error | | 57 | `ErrorShieldedRecoveryKeysRequired` | #4715 | Proposed — recovery needs the account and compatible keys; ciphertext damage can produce the same symptom. Rust, Swift and Kotlin preserve this typed error | | 58 | `ErrorIdentityBalanceUnavailable` | #4799 | Proposed — Platform returned no balance for a managed identity. Retrying the read is safe; this is distinct from missing wallet ownership and must not trigger registration or funding | +| 59 | `ErrorSpvProcessRestartRequired` | #5322 | Proposed — SPV startup or teardown panicked; restart the host process. Not retryable, unlike 27, which SPV start, stop and storage clear now return while an SPV teardown is still running. Typed in Rust, Swift and Kotlin (the PR also types 27 on Kotlin) | **Code 31 left this table on 2026-08-04.** `ErrorSigningKeyUnavailable` sat here as #4183's proposal until #4183 merged (`189a3abb1c`); it is now in the merged diff --git a/packages/rs-platform-wallet-ffi/src/error.rs b/packages/rs-platform-wallet-ffi/src/error.rs index cf54ef43b03..4e13906dfe0 100644 --- a/packages/rs-platform-wallet-ffi/src/error.rs +++ b/packages/rs-platform-wallet-ffi/src/error.rs @@ -214,6 +214,8 @@ pub enum PlatformWalletFFIResultCode { /// in-flight sync pass was still running when a Clear / reset / /// sync-stop needed it provably drained. The operation failed closed /// (no state was wiped) and the host should retry once sync is idle. + /// Also returned by SPV start, stop and storage clear while an SPV + /// teardown is still running: call SPV stop again. /// NOT returned by `platform_wallet_manager_destroy` — with owned /// callback contexts (`release_fn`) a straggling worker keeps its /// context alive and releases it on exit, so destroy logs a non-clean @@ -602,6 +604,11 @@ pub enum PlatformWalletFFIResultCode { /// is safe; this does not imply missing ownership or require registration. ErrorIdentityBalanceUnavailable = 58, + /// Maps `PlatformWalletError::SpvProcessRestartRequired`: SPV startup or + /// teardown panicked. Not retryable, unlike + /// [`Self::ErrorShutdownIncomplete`]: restart the host process. + ErrorSpvProcessRestartRequired = 59, + /// The named thing does not exist. /// /// Originally (and still mostly) the code for every `Option` returned as an @@ -1019,6 +1026,9 @@ impl From for PlatformWalletFFIResult { PlatformWalletError::ShutdownIncomplete(..) => { PlatformWalletFFIResultCode::ErrorShutdownIncomplete } + PlatformWalletError::SpvProcessRestartRequired(..) => { + PlatformWalletFFIResultCode::ErrorSpvProcessRestartRequired + } // A signer failure can also reach this blanket impl wrapped as // `PlatformWalletError::Sdk(dash_sdk::Error::Protocol(..))` (any // wallet operation that propagates the SDK error via `?`). The @@ -2143,6 +2153,31 @@ mod tests { ); } + /// Hosts tell "stop again" (27) from "restart the process" (59) by code + /// alone, so neither SPV teardown error may flatten to `ErrorUnknown`. + #[test] + fn spv_teardown_errors_keep_distinct_pinned_codes() { + assert_eq!( + PlatformWalletFFIResultCode::ErrorSpvProcessRestartRequired as i32, + 59 + ); + + let restart: PlatformWalletFFIResult = + PlatformWalletError::SpvProcessRestartRequired("teardown task 1 panicked".into()) + .into(); + assert_eq!( + restart.code, + PlatformWalletFFIResultCode::ErrorSpvProcessRestartRequired + ); + + let retry: PlatformWalletFFIResult = + PlatformWalletError::ShutdownIncomplete("SPV teardown timed out".into()).into(); + assert_eq!( + retry.code, + PlatformWalletFFIResultCode::ErrorShutdownIncomplete + ); + } + #[test] fn shielded_insufficient_balance_code_is_pinned_at_41() { assert_eq!( diff --git a/packages/rs-platform-wallet-ffi/src/spv.rs b/packages/rs-platform-wallet-ffi/src/spv.rs index a40a8cb32c8..33f6ad9f9dd 100644 --- a/packages/rs-platform-wallet-ffi/src/spv.rs +++ b/packages/rs-platform-wallet-ffi/src/spv.rs @@ -344,7 +344,8 @@ pub unsafe extern "C" fn platform_wallet_manager_spv_connected_peers_free( let _ = Box::from_raw(std::ptr::slice_from_raw_parts_mut(entries, count)); } -/// Whether the SPV client is currently running. +/// Whether SPV sync is running or starting. `false` once background sync has +/// failed. Never blocks, so an event callback may call it. #[no_mangle] pub unsafe extern "C" fn platform_wallet_manager_spv_is_running( handle: Handle, @@ -352,7 +353,7 @@ pub unsafe extern "C" fn platform_wallet_manager_spv_is_running( ) -> PlatformWalletFFIResult { check_ptr!(out_running); let option = - PLATFORM_WALLET_MANAGER_STORAGE.with_item(handle, |manager| manager.spv().is_started()); + PLATFORM_WALLET_MANAGER_STORAGE.with_item(handle, |manager| manager.spv().is_running()); *out_running = unwrap_option_or_return!(option); PlatformWalletFFIResult::ok() } @@ -579,6 +580,9 @@ pub unsafe extern "C" fn platform_wallet_manager_spv_start( } /// Stop the SPV client. +/// +/// `ErrorShutdownIncomplete`: teardown is still running; call stop again. +/// `ErrorSpvProcessRestartRequired`: restart the host process. #[no_mangle] pub unsafe extern "C" fn platform_wallet_manager_spv_stop( handle: Handle, diff --git a/packages/rs-platform-wallet/src/error.rs b/packages/rs-platform-wallet/src/error.rs index 66c61cfa864..8ba719674d0 100644 --- a/packages/rs-platform-wallet/src/error.rs +++ b/packages/rs-platform-wallet/src/error.rs @@ -812,6 +812,15 @@ pub enum PlatformWalletError { #[error("SPV error: {0}")] SpvError(String), + /// SPV startup or teardown panicked and may have left background work + /// running. Not retryable: restart the process. + /// FFI mirror: `PlatformWalletFFIResultCode::ErrorSpvProcessRestartRequired`. + #[error( + "SPV startup or teardown panicked and may have left background work \ + running; restart the process before using SPV again: {0}" + )] + SpvProcessRestartRequired(String), + #[error("Token operation failed: {0}")] TokenError(String), @@ -952,6 +961,8 @@ pub enum PlatformWalletError { /// fail-closed. The wedged pass may still fire persistence / event /// callbacks; the host must keep its callback context alive and must /// not commit any wipe it was about to pair with this call. + /// Also returned by SPV start, stop and storage clear while an SPV + /// teardown is still running: stop SPV again. /// FFI mirror: `PlatformWalletFFIResultCode::ErrorShutdownIncomplete`. #[error("Background sync did not quiesce: {0}")] ShutdownIncomplete(String), diff --git a/packages/rs-platform-wallet/src/spv/runtime.rs b/packages/rs-platform-wallet/src/spv/runtime.rs index bb58de71197..f7f84ff9fb3 100644 --- a/packages/rs-platform-wallet/src/spv/runtime.rs +++ b/packages/rs-platform-wallet/src/spv/runtime.rs @@ -1,7 +1,9 @@ //! SPV client runtime — manages the DashSpvClient lifecycle. use std::future::Future; +use std::pin::pin; use std::sync::{Arc, Mutex, MutexGuard, PoisonError}; +use std::task::{Context, Poll, Waker}; use std::time::Duration; use tokio::sync::RwLock; @@ -46,6 +48,19 @@ enum Shutdown { /// /// Events are dispatched through [`PlatformEventManager`] to all registered /// handlers by reference (no cloning). +/// +/// # Lifecycle +/// +/// [`start`](Self::start) works only on a stopped runtime. [`stop`](Self::stop) +/// is idempotent; once it returns `Ok`, no SPV task runs and the storage +/// directory is released. A stop that does not complete says which way: +/// - [`ShutdownIncomplete`](PlatformWalletError::ShutdownIncomplete): teardown +/// outlived the wait and continues. Stop again; start and storage clear +/// return the same error until a stop completes. +/// - [`SpvProcessRestartRequired`](PlatformWalletError::SpvProcessRestartRequired): +/// startup or teardown panicked. This runtime refuses every later start, +/// stop and storage clear, and nothing is promised about SPV in this +/// process until it restarts. pub struct SpvRuntime { event_manager: Arc, wallet_manager: Arc>>, @@ -118,9 +133,12 @@ impl SpvRuntime { let network_manager = PeerNetworkManager::new(&config) .await .map_err(|e| PlatformWalletError::SpvError(e.to_string()))?; - let storage_manager = DiskStorageManager::new(&config) + let mut storage_manager = DiskStorageManager::new(&config) .await .map_err(|e| PlatformWalletError::SpvError(e.to_string()))?; + // Upstream starts the storage writer here and again in `run`, but + // stops it only for a client that ran. + StorageManager::stop(&mut storage_manager).await; // PlatformEventManager implements `EventHandler`; the peer tracker // rides alongside it so `connected_peers` can answer from the latest @@ -163,14 +181,22 @@ impl SpvRuntime { /// Check that nothing is left to join. Takes the locked `shutdown` state: /// `task` only changes under that lock, so the answer cannot go stale. fn ensure_joined(&self, shutdown: &Shutdown) -> Result<(), PlatformWalletError> { - if !matches!(shutdown, Shutdown::Idle) { - return Err(PlatformWalletError::SpvError( - "SPV teardown is incomplete; stop SPV again before starting it".to_string(), - )); + match shutdown { + Shutdown::Idle => {} + Shutdown::Running(_) => { + return Err(PlatformWalletError::ShutdownIncomplete( + "SPV teardown is still running; stop SPV again before starting it".to_string(), + )) + } + Shutdown::Failed(error) => { + return Err(PlatformWalletError::SpvProcessRestartRequired( + error.clone(), + )) + } } let task = self.task_slot(); if task.is_some() { - return Err(PlatformWalletError::SpvError( + return Err(PlatformWalletError::ShutdownIncomplete( "SPV startup has not been joined; stop SPV before restarting it".to_string(), )); } @@ -188,6 +214,35 @@ impl SpvRuntime { self.client.try_read().map(|c| c.is_some()).unwrap_or(false) } + /// Whether SPV sync is running or starting. `false` once background sync + /// has failed, although the client stays started until [`Self::stop`]. + /// Never waits, so an event handler may call it. + pub fn is_running(&self) -> bool { + let Ok(client) = self.client.try_read() else { + return false; + }; + let Some(client) = client.as_ref() else { + return false; + }; + let starting = self + .task_slot() + .as_ref() + .is_some_and(|task| !task.is_finished()); + if starting { + return true; + } + let mut running = pin!(tokio::task::unconstrained(client.is_running())); + match running + .as_mut() + .poll(&mut Context::from_waker(Waker::noop())) + { + Poll::Ready(running) => running, + // Upstream holds this lock while it cleans up after a failed sync + // and, briefly, while it recovers from a fork. + Poll::Pending => false, + } + } + /// Whether a broadcast issued right now could reach the network: the /// client is started *and* at least one peer is connected. /// @@ -323,6 +378,10 @@ impl SpvRuntime { /// A timeout or cancelled caller leaves teardown tracked for the next stop. /// Restart stays blocked until a stop confirms completion. Client queries /// already in flight finish before teardown starts; this wait is unbounded. + /// + /// # Errors + /// + /// See the lifecycle on [`SpvRuntime`]. pub async fn stop(&self) -> Result<(), PlatformWalletError> { self.stop_with(|client| async move { client.stop().await }) .await @@ -373,14 +432,16 @@ impl SpvRuntime { *shutdown = Shutdown::Failed(format!("SPV teardown task failed: {result:?}")) } Err(_) => { - return Err(PlatformWalletError::SpvError( + return Err(PlatformWalletError::ShutdownIncomplete( "SPV teardown timed out; it is still tracked for a retry".to_string(), )) } } } match shutdown { - Shutdown::Failed(error) => Err(PlatformWalletError::SpvError(error.clone())), + Shutdown::Failed(error) => Err(PlatformWalletError::SpvProcessRestartRequired( + error.clone(), + )), _ => Ok(()), } } @@ -803,7 +864,7 @@ impl std::fmt::Debug for SpvRuntime { #[cfg(test)] mod tests { - use std::sync::Arc; + use std::sync::{Arc, Mutex}; use std::time::Duration; use dash_spv::error::{NetworkError, SpvError}; @@ -814,7 +875,7 @@ mod tests { use super::{classify_spv_send_error, SpvRuntime}; use crate::broadcaster::BroadcastError; use crate::error::PlatformWalletError; - use crate::events::PlatformEventManager; + use crate::events::{EventHandler, PlatformEventHandler, PlatformEventManager}; use crate::wallet::platform_wallet::PlatformWalletInfo; use dash_spv::ClientConfig; @@ -849,8 +910,8 @@ mod tests { let result = runtime.start(ClientConfig::default()).await; assert!( - matches!(result, Err(PlatformWalletError::SpvError(_))), - "a start over a live parked run loop must fail, got {result:?}" + is_retryable(&result), + "a start over a live parked run loop must ask for a stop, got {result:?}" ); assert!(!runtime.is_started(), "no client may be started"); assert!( @@ -1053,6 +1114,19 @@ mod tests { ); } + /// Teardown is still tracked: the host should stop again. + fn is_retryable(result: &Result<(), PlatformWalletError>) -> bool { + matches!(result, Err(PlatformWalletError::ShutdownIncomplete(_))) + } + + /// Startup or teardown panicked: only a process restart recovers. + fn needs_restart(result: &Result<(), PlatformWalletError>) -> bool { + matches!( + result, + Err(PlatformWalletError::SpvProcessRestartRequired(_)) + ) + } + async fn offline_runtime() -> (Arc, tempfile::TempDir) { let storage = tempfile::tempdir().unwrap(); let runtime = Arc::new(unstarted_runtime()); @@ -1067,6 +1141,156 @@ mod tests { (runtime, storage) } + /// Wait out two periods of upstream's five-second storage writer and + /// check that nothing re-created the removed directory. + async fn assert_no_storage_writer_survives(storage: &tempfile::TempDir) { + std::fs::remove_dir_all(storage.path()).unwrap(); + tokio::time::sleep(Duration::from_secs(11)).await; + assert!( + !storage.path().exists(), + "a storage writer outlived its client" + ); + } + + /// Upstream starts a storage writer at construction and stops it only for + /// a client whose sync loop ran. Left behind, it keeps writing into a + /// directory that was cleared or handed to the next client. + #[tokio::test(start_paused = true)] + async fn should_leave_no_storage_writer_after_clearing_a_client_that_never_ran() { + let (runtime, storage) = offline_runtime().await; + runtime.clear_storage().await.unwrap(); + assert_no_storage_writer_survives(&storage).await; + } + + #[tokio::test(start_paused = true)] + async fn should_leave_no_storage_writer_after_client_construction_fails() { + let storage = tempfile::tempdir().unwrap(); + let mut config = ClientConfig::testnet() + .with_storage_path(storage.path()) + .with_restrict_to_configured_peers(true); + config.max_peers = 0; + assert!(unstarted_runtime().start(config).await.is_err()); + assert_no_storage_writer_survives(&storage).await; + } + + /// Drive the production startup path and wait for upstream `run` to return. + async fn run_offline(runtime: &Arc) { + runtime.spawn_run_loop(); + wait_until("the startup task to finish", || { + runtime + .task + .lock() + .unwrap() + .as_ref() + .is_some_and(|task| task.is_finished()) + }) + .await; + } + + fn offline_config(storage: &tempfile::TempDir) -> ClientConfig { + ClientConfig::testnet() + .with_storage_path(storage.path()) + .with_restrict_to_configured_peers(true) + } + + /// The production startup path: `spawn_run_loop` drives upstream `run`, + /// the client stays available for queries, and stop tears down a client + /// whose sync loop is live. + #[tokio::test] + async fn should_run_the_real_startup_and_stop_a_running_client() { + let (runtime, storage) = offline_runtime().await; + assert!( + !runtime.is_running(), + "a client that never ran is not running" + ); + + run_offline(&runtime).await; + assert!(runtime.is_started(), "startup must retain the client"); + assert!(runtime.is_running()); + assert!( + runtime.sync_progress().await.is_some(), + "queries must reach the retained client" + ); + + runtime.stop().await.unwrap(); + assert!(!runtime.is_started()); + assert!(!runtime.is_running()); + runtime + .start(offline_config(&storage)) + .await + .expect("restart after stopping a running client"); + runtime.stop().await.unwrap(); + } + + /// Upstream stops itself when its sync loop fails and reports that only + /// through `on_error`, so the retained client must stop counting as + /// running. + #[tokio::test] + async fn should_not_report_running_after_upstream_background_sync_fails() { + /// Reports the error and what a host callback sees when it queries + /// the running state from inside the callback. + struct ErrorObserver { + runtime: std::sync::Weak, + report: Mutex>>, + } + + impl EventHandler for ErrorObserver { + fn on_error(&self, error: &str) { + if let Some(report) = self.report.lock().unwrap().take() { + let running = self.runtime.upgrade().is_some_and(|spv| spv.is_running()); + let _ = report.send((error.to_owned(), running)); + } + } + } + + impl PlatformEventHandler for ErrorObserver {} + + let (runtime, storage) = offline_runtime().await; + let (sender, error) = tokio::sync::oneshot::channel(); + runtime.event_manager.add_handler(Arc::new(ErrorObserver { + runtime: Arc::downgrade(&runtime), + report: Mutex::new(Some(sender)), + })); + run_offline(&runtime).await; + assert!(runtime.is_running()); + + // Dropping the empty fixture's wallet closes its event channel. The + // real upstream monitor reports the failure and stops the sync loop. + *runtime.wallet_manager.write().await = WalletManager::new(Network::Testnet); + let (error, running_in_callback) = tokio::time::timeout(Duration::from_secs(5), error) + .await + .expect("upstream must report the background failure") + .expect("the error observer must remain registered"); + assert!( + !running_in_callback, + "a callback querying the running state must get the stopped answer" + ); + assert!( + error.contains("WalletEvent monitor channel closed unexpectedly"), + "expected a wallet event monitor failure, got {error}" + ); + + assert!(runtime.is_started(), "the client stays owned until stop"); + // Upstream cleans up after the failure in a task of its own. + for _ in 0..1000 { + assert!( + !runtime.is_running(), + "a failed sync must not report running while upstream cleans up" + ); + tokio::task::yield_now().await; + } + runtime.stop().await.unwrap(); + assert!(!runtime.is_started()); + assert!(!runtime.is_running()); + runtime + .start(offline_config(&storage)) + .await + .expect("restart after releasing the stopped client"); + run_offline(&runtime).await; + assert!(runtime.is_running()); + runtime.stop().await.unwrap(); + } + #[tokio::test(start_paused = true)] async fn should_keep_timed_out_upstream_stop_alive_until_retry_joins_it() { let (runtime, storage) = offline_runtime().await; @@ -1080,10 +1304,17 @@ mod tests { completed.store(true, std::sync::atomic::Ordering::SeqCst); }) .await; - assert!(result.is_err(), "an incomplete upstream stop must time out"); - assert!(runtime.start(ClientConfig::default()).await.is_err()); assert!( - runtime.stop().await.is_err(), + is_retryable(&result), + "an incomplete upstream stop must time out as retryable, got {result:?}" + ); + assert!(is_retryable(&runtime.start(ClientConfig::default()).await)); + assert!( + is_retryable(&runtime.clear_storage().await), + "storage must not be cleared under a pending upstream stop" + ); + assert!( + is_retryable(&runtime.stop().await), "retry must not report clean while upstream stop is pending" ); release @@ -1267,8 +1498,8 @@ mod tests { assert!(!runtime.is_started(), "failed startup must drop its client"); assert!( - runtime.start(config()).await.is_err(), - "an unjoined startup must block restart" + is_retryable(&runtime.start(config()).await), + "an unjoined startup must block restart until a stop joins it" ); runtime.stop().await.unwrap(); runtime @@ -1280,14 +1511,20 @@ mod tests { #[tokio::test] async fn should_never_report_clean_after_a_startup_task_panics() { - let runtime = unstarted_runtime(); + let runtime = Arc::new(unstarted_runtime()); *runtime.task.lock().unwrap() = Some(tokio::spawn(async { panic!("mock startup panic") })); - assert!(runtime.stop().await.is_err()); + assert!(needs_restart(&runtime.stop().await)); assert!( - runtime.stop().await.is_err(), + needs_restart(&runtime.stop().await), "a join failure cannot be forgotten by a retry" ); - assert!(runtime.start(ClientConfig::default()).await.is_err()); + assert!(needs_restart(&runtime.start(ClientConfig::default()).await)); + assert!(needs_restart(&runtime.clear_storage().await)); + runtime.spawn_run_loop(); + assert!( + runtime.task.lock().unwrap().is_none(), + "no run loop may be spawned over a panicked startup" + ); } #[tokio::test] @@ -1317,12 +1554,13 @@ mod tests { #[tokio::test] async fn should_keep_teardown_panics_non_clean_on_every_retry() { let (runtime, _storage) = offline_runtime().await; - assert!(runtime + let failed = runtime .stop_with(|_client| async { panic!("mock teardown panic") }) - .await - .is_err()); - assert!(runtime.stop().await.is_err()); - assert!(runtime.start(ClientConfig::default()).await.is_err()); + .await; + assert!(needs_restart(&failed)); + assert!(needs_restart(&runtime.stop().await)); + assert!(needs_restart(&runtime.start(ClientConfig::default()).await)); + assert!(needs_restart(&runtime.clear_storage().await)); } /// A panic under the `task` mutex must not make every later lifecycle diff --git a/packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/PlatformWalletManagerSPV.swift b/packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/PlatformWalletManagerSPV.swift index 53e279799ac..c989871e7a6 100644 --- a/packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/PlatformWalletManagerSPV.swift +++ b/packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/PlatformWalletManagerSPV.swift @@ -230,7 +230,8 @@ extension PlatformWalletManager { } } - /// Whether the SPV client is currently running. + /// Whether SPV sync is running or starting. `false` once background + /// sync has failed, until SPV is stopped and started again. public func isSpvRunning() throws -> Bool { try ensureConfigured() return try Self.readIsSpvRunning(handle) @@ -315,6 +316,9 @@ extension PlatformWalletManager { /// Throws `walletOperation` while an async [`stopSpv()`] is in flight: /// that stop is already tearing the client down, and this blocking one /// would wait on the same teardown on the calling thread. + /// + /// Throws `shutdownIncomplete` when teardown is still running (stop + /// again) and `spvProcessRestartRequired` when the app must restart. public func stopSpv() throws { try ensureNoSpvStopInFlight(before: "a blocking stopSpv()") try platform_wallet_manager_spv_stop(handle).check() diff --git a/packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/PlatformWalletResult.swift b/packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/PlatformWalletResult.swift index 7fcfa941f44..b947a83272b 100644 --- a/packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/PlatformWalletResult.swift +++ b/packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/PlatformWalletResult.swift @@ -88,6 +88,8 @@ public enum PlatformWalletResultCode: Int32, Sendable { /// no state was wiped — and the caller should retry once sync is idle. /// (Not returned by `destroy`: Rust owns the callback contexts, so a /// straggling worker is memory-safe and merely logged there.) + /// Also returned by SPV start, stop and storage clear while an SPV + /// teardown is still running: stop SPV again. case errorShutdownIncomplete = 27 /// Asset-lock coin selection came up short over the *permitted* funding /// set (dashpay/platform#4073). Nothing was built or broadcast and no @@ -258,6 +260,8 @@ public enum PlatformWalletResultCode: Int32, Sendable { case errorShieldedRecoveryKeysRequired = 57 /// Platform returned no balance. Retrying the read is safe; ownership is unchanged. case errorIdentityBalanceUnavailable = 58 + /// SPV startup or teardown panicked. Not retryable: restart the app. + case errorSpvProcessRestartRequired = 59 /// The named thing does not exist. Besides the handle/lookup failures this /// has always covered, BOTH deferred-send paths report the /// wallet-was-REMOVED case here. @@ -385,6 +389,8 @@ public enum PlatformWalletResultCode: Int32, Sendable { self = .errorShieldedRecoveryKeysRequired case PLATFORM_WALLET_FFI_RESULT_CODE_ERROR_IDENTITY_BALANCE_UNAVAILABLE: self = .errorIdentityBalanceUnavailable + case PLATFORM_WALLET_FFI_RESULT_CODE_ERROR_SPV_PROCESS_RESTART_REQUIRED: + self = .errorSpvProcessRestartRequired case PLATFORM_WALLET_FFI_RESULT_CODE_NOT_FOUND: self = .notFound case PLATFORM_WALLET_FFI_RESULT_CODE_ERROR_UNKNOWN: @@ -626,8 +632,13 @@ public enum PlatformWalletError: LocalizedError { case addressNonceMismatch(String) /// A quiesce/drain barrier (Clear / reset / sync-stop) timed out with a /// sync pass still in flight. The operation failed closed — retry once - /// sync is idle. + /// sync is idle. SPV start, stop and storage clear also throw it while an + /// SPV teardown is still running: call `stopSpv()` again. case shutdownIncomplete(String) + /// SPV startup or teardown panicked. Not retryable: restart the app. + /// `errorDescription` is fixed user text; the panic detail is on + /// `failureReason`. + case spvProcessRestartRequired(String) /// The signer has no usable private key for the requested public key /// (missing / stranded scalar) — the operation itself did not fail. /// Restored from the structured signer completion code @@ -822,6 +833,8 @@ public enum PlatformWalletError: LocalizedError { return "The wallet data could not be read and may need to be restored." case .persisterStoreFatal, .persisterStoreConstraint: return "The wallet data could not be saved and may need to be restored." + case .spvProcessRestartRequired: + return "Sync could not be stopped cleanly. Restart the app to use it again." // The three value-carrying marketplace rejections compose their // description from the typed values, because their FFI message is // the machine-readable JSON detail — showing that raw would be @@ -848,7 +861,8 @@ public enum PlatformWalletError: LocalizedError { switch self { case .persisterLoadTransient(let m), .persisterLoadFatal(let m), .persisterStoreTransient(let m), .persisterStoreFatal(let m), - .persisterStoreConstraint(let m), .persisterRestore(let m): + .persisterStoreConstraint(let m), .persisterRestore(let m), + .spvProcessRestartRequired(let m): return m default: return nil @@ -920,6 +934,8 @@ public enum PlatformWalletError: LocalizedError { self = .addressNonceMismatch(detail) case .errorShutdownIncomplete: self = .shutdownIncomplete(detail) + case .errorSpvProcessRestartRequired: + self = .spvProcessRestartRequired(detail) case .errorSigningKeyUnavailable: self = .signingKeyUnavailable(detail) case .errorStaleReservationToken: diff --git a/packages/swift-sdk/SwiftTests/SwiftDashSDKTests/ErrorHandlingTests.swift b/packages/swift-sdk/SwiftTests/SwiftDashSDKTests/ErrorHandlingTests.swift index 4f0290f3ede..f18b44a9fce 100644 --- a/packages/swift-sdk/SwiftTests/SwiftDashSDKTests/ErrorHandlingTests.swift +++ b/packages/swift-sdk/SwiftTests/SwiftDashSDKTests/ErrorHandlingTests.swift @@ -439,6 +439,26 @@ final class ErrorHandlingTests: XCTestCase { XCTAssertEqual(keyMessage, detail) } + func testShouldPreserveSpvProcessRestartRequiredFromFFI() { + let code = PlatformWalletResultCode( + ffi: PLATFORM_WALLET_FFI_RESULT_CODE_ERROR_SPV_PROCESS_RESTART_REQUIRED + ) + XCTAssertEqual(code, .errorSpvProcessRestartRequired) + XCTAssertEqual(code.rawValue, 59) + let detail = "teardown task 12 panicked with message \"boom\"" + let error = PlatformWalletError(code: code, message: detail) + guard case .spvProcessRestartRequired(let message) = error else { + return XCTFail("lost typed restart-required error") + } + XCTAssertEqual(message, detail) + // The panic detail is log material; the alert text is fixed. + XCTAssertEqual(error.failureReason, detail) + XCTAssertEqual( + error.errorDescription, + "Sync could not be stopped cleanly. Restart the app to use it again." + ) + } + func testShouldPreserveShieldedIdentityDebitPendingFFIResult() { let code = PlatformWalletResultCode( ffi: PLATFORM_WALLET_FFI_RESULT_CODE_ERROR_SHIELDED_IDENTITY_DEBIT_PENDING From 9af203336c82f3334bdb34779bbdf9742d3316ba Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Thu, 8 Oct 2026 13:24:35 +0000 Subject: [PATCH 12/22] chore(deps)!: pin rust-dashcore to BLS compatibility fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pin all eight workspace dependencies and Cargo.lock to 40e7b24c from dashpay/rust-dashcore#1145. Adapt public Ed25519 and masternode snapshot APIs, raw hash extraction, and coinbase test fixtures to its newer base. Quorum scores retain identical raw digest bytes at every protocol version; the removed Into conversion and to_byte_array both return the inner array. PlatformNodeId storage migration remains outside this change. Validation: 151 targeted tests; scoped Clippy for wallet, storage, SDK and Drive ABCI; formatting, locked metadata and consistent dependency pins. BREAKING CHANGE: Remove core_key_wallet_bip_38 and core_key_wallet_bip38 feature forwards because upstream key-wallet removed BIP38 support. 🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent Co-Authored-By: Codex GPT-6 --- CHANGELOG.md | 6 + Cargo.lock | 235 +++++++++++------- Cargo.toml | 16 +- packages/rs-dpp/Cargo.toml | 1 - .../core_chain_lock/choose_quorum/v0/mod.rs | 6 +- .../v0/quorums.rs | 3 +- packages/rs-drive-abci/src/rpc/core.rs | 13 +- .../tests/strategy_tests/strategy.rs | 2 +- .../src/manager/accessors.rs | 6 +- .../src/masternode/locator.rs | 4 +- .../rs-platform-wallet/src/spv/runtime.rs | 63 ++--- .../src/wallet/provider_key_at_index.rs | 32 ++- packages/rs-sdk/Cargo.toml | 1 - 13 files changed, 203 insertions(+), 185 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78acb562cdf..3e7cdb7b2bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ### Changed +- **Rust dependencies:** pin rust-dashcore to PR #1145 (`40e7b24c`) to accept + legacy BLS public-key encodings through binary Serde. PlatformNodeId storage + compatibility is not covered by this update. +- **Breaking (Rust features):** remove DPP's `core_key_wallet_bip_38` and SDK's + `core_key_wallet_bip38` feature forwards because upstream removed BIP38 support. + - **dpp (Rust API migration):** BLS types now live under `dpp::bls::{PublicKey, SecretKey, Signature, BlsError}`; the generic `dpp::bls_signatures` re-export is removed. Use `PublicKey::to_bytes()`, `SecretKey::from_be_bytes()` returning diff --git a/Cargo.lock b/Cargo.lock index 3393eccb4e1..3e45cdd73f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -490,21 +490,21 @@ checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" [[package]] name = "base58ck" -version = "0.1.100" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec5dc7e09f7bb15f0062da7c03086d6b71a2c84e0af4fccbbc7d8c6559847816" +checksum = "df540278d807651653f9b6f21e18903d790d7769eadfbc0dc4df0a141260318f" dependencies = [ - "bitcoin_hashes 0.14.100", + "bitcoin-internals 0.6.0", + "bitcoin_hashes 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "base58ck" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df540278d807651653f9b6f21e18903d790d7769eadfbc0dc4df0a141260318f" +source = "git+https://github.com/rust-bitcoin/rust-bitcoin?rev=7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516#7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516" dependencies = [ - "bitcoin-internals 0.6.0", - "bitcoin_hashes 1.2.0", + "bitcoin-internals 0.7.0 (git+https://github.com/rust-bitcoin/rust-bitcoin?rev=7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516)", + "bitcoin_hashes 1.2.0 (git+https://github.com/rust-bitcoin/rust-bitcoin?rev=7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516)", ] [[package]] @@ -606,7 +606,7 @@ version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc" dependencies = [ - "bitcoin_hashes 0.14.100", + "bitcoin_hashes 0.14.101", "rand 0.8.6", "rand_core 0.6.4", "serde", @@ -644,17 +644,42 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" +[[package]] +name = "bitcoin-consensus-encoding" +version = "1.2.0" +source = "git+https://github.com/rust-bitcoin/rust-bitcoin?rev=7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516#7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516" +dependencies = [ + "bitcoin-internals 0.7.0 (git+https://github.com/rust-bitcoin/rust-bitcoin?rev=7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516)", + "hex-conservative 1.3.0", + "serde", +] + [[package]] name = "bitcoin-consensus-encoding" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9daa31138eb443d5751b207f3f64154e2bb09cd59960562ccc7a7112be38147f" dependencies = [ - "bitcoin-internals 0.7.0", + "bitcoin-internals 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "hex-conservative 1.3.0", "serde", ] +[[package]] +name = "bitcoin-crypto" +version = "0.3.0" +source = "git+https://github.com/rust-bitcoin/rust-bitcoin?rev=7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516#7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516" +dependencies = [ + "base58ck 0.5.0 (git+https://github.com/rust-bitcoin/rust-bitcoin?rev=7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516)", + "bitcoin-internals 0.7.0 (git+https://github.com/rust-bitcoin/rust-bitcoin?rev=7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516)", + "bitcoin-network-kind", + "bitcoin-primitives 0.103.1", + "bitcoin_hashes 1.2.0 (git+https://github.com/rust-bitcoin/rust-bitcoin?rev=7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516)", + "hex-conservative 1.3.0", + "secp256k1", + "serde", +] + [[package]] name = "bitcoin-internals" version = "0.5.0" @@ -676,11 +701,31 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8bea3a9f0cfece4564e37cb49a38cc245ca5184719e50d7d0dda3268722c4e2" +[[package]] +name = "bitcoin-internals" +version = "0.7.0" +source = "git+https://github.com/rust-bitcoin/rust-bitcoin?rev=7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516#7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516" +dependencies = [ + "serde", +] + [[package]] name = "bitcoin-io" -version = "0.1.100" +version = "0.1.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11301df0b06f22dea7bb1916403fdd88a371031e495c49b8f96931b28189e175" +checksum = "bb5de036369d1ac59d3c1819ebc4d850f89466f5401c571a285b6ed564a4cb78" +dependencies = [ + "bitcoin-consensus-encoding 1.3.0", +] + +[[package]] +name = "bitcoin-network-kind" +version = "1.0.0" +source = "git+https://github.com/rust-bitcoin/rust-bitcoin?rev=7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516#7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516" +dependencies = [ + "bitcoin-internals 0.7.0 (git+https://github.com/rust-bitcoin/rust-bitcoin?rev=7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516)", + "serde", +] [[package]] name = "bitcoin-primitives" @@ -688,10 +733,22 @@ version = "0.103.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82e12cb1814d037edbd667554c2c0e6b067db72c83ee935b645a6b5829aca908" dependencies = [ - "bitcoin-consensus-encoding", + "bitcoin-consensus-encoding 1.3.0", "bitcoin-internals 0.6.0", - "bitcoin-units", - "bitcoin_hashes 1.2.0", + "bitcoin-units 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitcoin_hashes 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hex-conservative 1.3.0", +] + +[[package]] +name = "bitcoin-primitives" +version = "0.103.1" +source = "git+https://github.com/rust-bitcoin/rust-bitcoin?rev=7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516#7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516" +dependencies = [ + "bitcoin-consensus-encoding 1.2.0", + "bitcoin-internals 0.7.0 (git+https://github.com/rust-bitcoin/rust-bitcoin?rev=7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516)", + "bitcoin-units 0.5.0 (git+https://github.com/rust-bitcoin/rust-bitcoin?rev=7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516)", + "bitcoin_hashes 1.2.0 (git+https://github.com/rust-bitcoin/rust-bitcoin?rev=7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516)", "hex-conservative 1.3.0", ] @@ -701,19 +758,30 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ae6dcb008158c2bdbe698470280439844be5f640310e4f5d6ef0a0209e066c2" dependencies = [ - "bitcoin-consensus-encoding", + "bitcoin-consensus-encoding 1.3.0", "bitcoin-internals 0.5.0", "serde", ] +[[package]] +name = "bitcoin-units" +version = "0.5.0" +source = "git+https://github.com/rust-bitcoin/rust-bitcoin?rev=7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516#7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516" +dependencies = [ + "bitcoin-consensus-encoding 1.2.0", + "bitcoin-internals 0.7.0 (git+https://github.com/rust-bitcoin/rust-bitcoin?rev=7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516)", + "serde", +] + [[package]] name = "bitcoin_hashes" -version = "0.14.100" +version = "0.14.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9901a56e133a1fc86eeb1113e2591f45f4682451ca893bff494d2f88918e3f" +checksum = "bca4c7abb40c8817d77403c880988cfd484f23ab2365726afb2f798363e2c4a2" dependencies = [ "bitcoin-io", "hex-conservative 0.2.2", + "serde", ] [[package]] @@ -722,11 +790,22 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5304e53726dbe5f93141535e102ed97b5bf4714fbecefdda8f9fb98d7fdaff0e" dependencies = [ - "bitcoin-consensus-encoding", + "bitcoin-consensus-encoding 1.3.0", "bitcoin-internals 0.6.0", "hex-conservative 1.3.0", ] +[[package]] +name = "bitcoin_hashes" +version = "1.2.0" +source = "git+https://github.com/rust-bitcoin/rust-bitcoin?rev=7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516#7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516" +dependencies = [ + "bitcoin-consensus-encoding 1.2.0", + "bitcoin-internals 0.7.0 (git+https://github.com/rust-bitcoin/rust-bitcoin?rev=7ba35c7c1dd63892bfb9c9395bbd80e2ab5df516)", + "hex-conservative 1.3.0", + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -886,7 +965,6 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ - "sha2 0.10.9", "tinyvec", ] @@ -1277,7 +1355,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -1730,7 +1808,7 @@ dependencies = [ [[package]] name = "dash-network" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" dependencies = [ "cbindgen 0.29.4", "grovedb-bincode", @@ -1741,7 +1819,7 @@ dependencies = [ [[package]] name = "dash-network-seeds" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" dependencies = [ "dash-network", ] @@ -1751,8 +1829,10 @@ name = "dash-num" version = "0.1.0-beta" source = "git+https://github.com/dashpay/base-sdk?rev=e6402ced257c370a586ade9840ebbf545a4b7926#e6402ced257c370a586ade9840ebbf545a4b7926" dependencies = [ + "bitcoin-consensus-encoding 1.3.0", "dash-types", "hex-conservative 1.3.0", + "serde", ] [[package]] @@ -1761,8 +1841,8 @@ version = "0.1.0-beta" source = "git+https://github.com/dashpay/base-sdk?rev=e6402ced257c370a586ade9840ebbf545a4b7926#e6402ced257c370a586ade9840ebbf545a4b7926" dependencies = [ "aes 0.9.3", - "base58ck 0.5.0", - "bitcoin_hashes 1.2.0", + "base58ck 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitcoin_hashes 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "blst", "cfg-if", "dash-num", @@ -1772,6 +1852,7 @@ dependencies = [ "group 0.14.0", "hex-conservative 1.3.0", "rand_core 0.10.1", + "serde", "sha2 0.11.0", "subtle", "zeroize", @@ -1867,7 +1948,7 @@ dependencies = [ [[package]] name = "dash-spv" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" dependencies = [ "async-trait", "chrono", @@ -1877,7 +1958,7 @@ dependencies = [ "dashcore_hashes", "futures", "git-state", - "hex", + "hex-conservative 1.3.0", "key-wallet", "key-wallet-manager", "rand 0.9.4", @@ -1898,8 +1979,8 @@ name = "dash-types" version = "0.1.0-beta" source = "git+https://github.com/dashpay/base-sdk?rev=e6402ced257c370a586ade9840ebbf545a4b7926#e6402ced257c370a586ade9840ebbf545a4b7926" dependencies = [ - "bitcoin-consensus-encoding", - "bitcoin-primitives", + "bitcoin-consensus-encoding 1.3.0", + "bitcoin-primitives 0.103.0", "cfg-if", "dash-types-marker", "hex-conservative 1.3.0", @@ -1921,7 +2002,7 @@ dependencies = [ [[package]] name = "dashcore" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" dependencies = [ "anyhow", "base64 0.22.1", @@ -1935,8 +2016,7 @@ dependencies = [ "dashcore_hashes", "grovedb-bincode", "grovedb-bincode-derive", - "hex", - "hex_lit", + "hex-conservative 1.3.0", "secp256k1", "serde", "thiserror 2.0.18", @@ -1946,9 +2026,10 @@ dependencies = [ [[package]] name = "dashcore-crypto" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" dependencies = [ - "base58ck 0.1.100", + "base58ck 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitcoin-crypto", "dash-network", "dash-pkc", "dash-types", @@ -1956,7 +2037,7 @@ dependencies = [ "dashcore_hashes", "grovedb-bincode", "grovedb-bincode-derive", - "hex", + "hex-conservative 1.3.0", "secp256k1", "serde", "thiserror 2.0.18", @@ -1966,15 +2047,14 @@ dependencies = [ [[package]] name = "dashcore-private" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" [[package]] name = "dashcore-rpc" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" dependencies = [ "dashcore-rpc-json", - "hex", "jsonrpc", "serde", "serde_json", @@ -1984,11 +2064,11 @@ dependencies = [ [[package]] name = "dashcore-rpc-json" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" dependencies = [ "dashcore", "grovedb-bincode", - "hex", + "hex-conservative 1.3.0", "key-wallet", "serde", "serde_json", @@ -1999,9 +2079,9 @@ dependencies = [ [[package]] name = "dashcore_hashes" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" dependencies = [ - "dashcore-private", + "bitcoin_hashes 0.14.101", "grovedb-bincode", "rs-x11-hash", "serde", @@ -2647,7 +2727,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2708,7 +2788,7 @@ checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" dependencies = [ "cfg-if", "rustix 1.1.4", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -3083,7 +3163,7 @@ dependencies = [ [[package]] name = "git-state" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" [[package]] name = "glob" @@ -3595,9 +3675,6 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -dependencies = [ - "serde", -] [[package]] name = "hex-conservative" @@ -3632,12 +3709,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1" -[[package]] -name = "hex_lit" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" - [[package]] name = "hkdf" version = "0.12.4" @@ -3825,7 +3896,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.4", + "socket2 0.5.10", "system-configuration", "tokio", "tower-service", @@ -4085,7 +4156,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4332,27 +4403,21 @@ dependencies = [ [[package]] name = "key-wallet" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" dependencies = [ - "aes 0.8.4", "async-trait", - "base58ck 0.1.100", "bip39", "bitflags 2.13.0", - "bs58", "dashcore", "dashcore-private", "dashcore_hashes", "getrandom 0.4.2", "grovedb-bincode", "grovedb-bincode-derive", - "hex", - "rand 0.9.4", - "scrypt", + "hex-conservative 1.3.0", "secp256k1", "serde", "serde_json", - "sha2 0.10.9", "tracing", "unicode-normalization", "zeroize", @@ -4361,12 +4426,12 @@ dependencies = [ [[package]] name = "key-wallet-ffi" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" dependencies = [ "cbindgen 0.29.4", "dash-network", "dashcore", - "hex", + "hex-conservative 1.3.0", "key-wallet", "key-wallet-manager", "libc", @@ -4377,7 +4442,7 @@ dependencies = [ [[package]] name = "key-wallet-manager" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=314f10600e35311f3d875a6e8740476ab6c79c0c#314f10600e35311f3d875a6e8740476ab6c79c0c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" dependencies = [ "async-trait", "dashcore", @@ -5768,7 +5833,7 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042" dependencies = [ - "heck 0.5.0", + "heck 0.4.1", "itertools 0.14.0", "log", "multimap", @@ -5939,7 +6004,7 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.2", "rustls", - "socket2 0.6.4", + "socket2 0.5.10", "thiserror 2.0.18", "tokio", "tracing", @@ -5977,9 +6042,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.4", + "socket2 0.5.10", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -6788,7 +6853,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -6801,7 +6866,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -6860,7 +6925,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -6905,15 +6970,6 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" -[[package]] -name = "salsa20" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" -dependencies = [ - "cipher 0.4.4", -] - [[package]] name = "same-file" version = "1.0.6" @@ -6980,17 +7036,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "scrypt" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" -dependencies = [ - "pbkdf2", - "salsa20", - "sha2 0.10.9", -] - [[package]] name = "sdd" version = "3.0.10" @@ -7692,7 +7737,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -9114,7 +9159,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 34adc6786a6..ff8eb9c73c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,14 +65,14 @@ grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "9791d2772 grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "9791d277207998b7789c85b0745e99d37040a12e" } grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "9791d277207998b7789c85b0745e99d37040a12e" } grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "9791d277207998b7789c85b0745e99d37040a12e" } -dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "314f10600e35311f3d875a6e8740476ab6c79c0c" } -dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "314f10600e35311f3d875a6e8740476ab6c79c0c" } -dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "314f10600e35311f3d875a6e8740476ab6c79c0c" } -key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "314f10600e35311f3d875a6e8740476ab6c79c0c" } -key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "314f10600e35311f3d875a6e8740476ab6c79c0c" } -key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "314f10600e35311f3d875a6e8740476ab6c79c0c" } -dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "314f10600e35311f3d875a6e8740476ab6c79c0c" } -dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "314f10600e35311f3d875a6e8740476ab6c79c0c" } +dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "40e7b24c46100496a279f34952b851d40aa93a5f" } +dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "40e7b24c46100496a279f34952b851d40aa93a5f" } +dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "40e7b24c46100496a279f34952b851d40aa93a5f" } +key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "40e7b24c46100496a279f34952b851d40aa93a5f" } +key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "40e7b24c46100496a279f34952b851d40aa93a5f" } +key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "40e7b24c46100496a279f34952b851d40aa93a5f" } +dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "40e7b24c46100496a279f34952b851d40aa93a5f" } +dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "40e7b24c46100496a279f34952b851d40aa93a5f" } tokio-metrics = "0.5" # Size-tuned profile for the iOS `rs-unified-sdk-ffi` staticlib, which diff --git a/packages/rs-dpp/Cargo.toml b/packages/rs-dpp/Cargo.toml index 1a41858a72e..3438a26ece9 100644 --- a/packages/rs-dpp/Cargo.toml +++ b/packages/rs-dpp/Cargo.toml @@ -113,7 +113,6 @@ core_verification = ["dashcore/message_verification"] core_quorum_validation = ["dashcore/quorum_validation"] core_key_wallet = ["dep:key-wallet"] core_key_wallet_bincode = ["dep:key-wallet", "key-wallet/bincode"] -core_key_wallet_bip_38 = ["dep:key-wallet", "key-wallet/bip38"] core_key_wallet_manager = ["dep:key-wallet-manager"] core_key_wallet_serde = ["dep:key-wallet", "key-wallet/serde"] core_spv = ["dep:dash-spv"] diff --git a/packages/rs-drive-abci/src/execution/platform_events/core_chain_lock/choose_quorum/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/core_chain_lock/choose_quorum/v0/mod.rs index 0c3be48ad63..aeebb38a229 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/core_chain_lock/choose_quorum/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/core_chain_lock/choose_quorum/v0/mod.rs @@ -37,7 +37,8 @@ impl Platform { // Finalize the hash let hash_result = sha256d::Hash::from_engine(hasher); - scores.push((quorum_hash_bytes, public_key, hash_result.into())); + // Scores use raw digest bytes, without reversal, at every protocol version. + scores.push((quorum_hash_bytes, public_key, hash_result.to_byte_array())); } if scores.is_empty() { @@ -79,7 +80,8 @@ impl Platform { // Finalize the hash let hash_result = sha256d::Hash::from_engine(hasher); - scores.push((quorum_hash_bytes, key, hash_result.into())); + // Scores use raw digest bytes, without reversal, at every protocol version. + scores.push((quorum_hash_bytes, key, hash_result.to_byte_array())); } scores.sort_by_key(|k| k.2); diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/quorums.rs b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/quorums.rs index e51e16cc4aa..539de8888bb 100644 --- a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/quorums.rs +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/quorums.rs @@ -80,7 +80,8 @@ impl Quorums { // Finalize the hash let hash_result = sha256d::Hash::from_engine(hasher); - scores.push((quorum_hash, quorum, hash_result.into())); + // Scores use raw digest bytes, without reversal, at every protocol version. + scores.push((quorum_hash, quorum, hash_result.to_byte_array())); } if scores.is_empty() { diff --git a/packages/rs-drive-abci/src/rpc/core.rs b/packages/rs-drive-abci/src/rpc/core.rs index 749936920c7..8d3346a310d 100644 --- a/packages/rs-drive-abci/src/rpc/core.rs +++ b/packages/rs-drive-abci/src/rpc/core.rs @@ -25,13 +25,8 @@ const COINBASE_TRANSACTION_TYPE: u16 = 5; /// coinbase transaction; `0` when it carries no payload or its payload predates version 3, /// before the credit pool existed, which is how Core's own unlock limit reads such a block. /// -/// Decoded with `deserialize_partial`: the pinned payload decoder reads the fields of version 3 -/// for every later version and stops after the balance, while the version 4 payload Core v24 -/// requires appends `merkleRootAssetUnlocks` after it. A strict `deserialize`, and so a whole -/// `Block` decode, refuses those unread bytes. Core has only ever appended fields to the -/// payload, and its consensus rules (`CheckCbTx`) refuse versions it does not know, so the -/// balance stays where version 3 put it unless a Core release moves it, which Platform would -/// have to follow anyway. +/// Uses `deserialize_partial` to tolerate appended payload fields after those understood +/// by the decoder. The credit-pool balance occupies the same field from version 3 onward. pub(crate) fn credit_pool_balance_from_coinbase(coinbase: &[u8]) -> Result { let (transaction, _) = deserialize_partial::(coinbase) .map_err(|e| format!("coinbase cannot be decoded: {e}"))?; @@ -487,6 +482,7 @@ mod tests { best_cl_height: Some(30), best_cl_signature: Some(BLSSignature::from([3; 96])), asset_locked_amount: Some(BALANCE_DUFFS), + merkle_root_asset_unlocks: None, }) } @@ -499,7 +495,7 @@ mod tests { } /// Core v24 blocks carry a version 4 payload, which appends `merkleRootAssetUnlocks` - /// after the balance; the pinned transaction decoder cannot read it. + /// after the balance. #[test] fn should_read_the_balance_of_a_version_4_coinbase() { let version_3 = coinbase(Some(version_3_payload())); @@ -528,6 +524,7 @@ mod tests { best_cl_height: None, best_cl_signature: None, asset_locked_amount: None, + merkle_root_asset_unlocks: None, }); assert_eq!( credit_pool_balance_from_coinbase(&coinbase(Some(version_2))), diff --git a/packages/rs-drive-abci/tests/strategy_tests/strategy.rs b/packages/rs-drive-abci/tests/strategy_tests/strategy.rs index bf491224e93..16dba17f478 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/strategy.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/strategy.rs @@ -1699,7 +1699,7 @@ impl NetworkStrategy { let (pro_tx_hash, masternode_list_item) = full_masternode_list.iter().nth(rand_index).unwrap(); - let pro_tx_hash_bytes: [u8; 32] = pro_tx_hash.to_raw_hash().into(); + let pro_tx_hash_bytes: [u8; 32] = pro_tx_hash.to_byte_array(); let voting_address = masternode_list_item.state.voting_address; let voting_identifier = Identifier::create_voter_identifier( diff --git a/packages/rs-platform-wallet/src/manager/accessors.rs b/packages/rs-platform-wallet/src/manager/accessors.rs index 4b0674685d8..c4ff3dfb6b2 100644 --- a/packages/rs-platform-wallet/src/manager/accessors.rs +++ b/packages/rs-platform-wallet/src/manager/accessors.rs @@ -1,6 +1,6 @@ //! Read-only accessors on [`PlatformWalletManager`]. -use dashcore::eddsa::EddsaPkBytes; +use dashcore::eddsa::{EddsaPkBytes, EddsaPkHash}; use std::sync::Arc; use dashcore::{OutPoint, Txid}; @@ -1289,8 +1289,8 @@ impl PlatformWalletManager

    { for entry in pool.addresses.values() { if let Some(PublicKeyType::EdDSA(pk)) = &entry.public_key { if let Ok(pk32) = <[u8; 32]>::try_from(pk.as_slice()) { - let node_id = - EddsaPkBytes::from_bytes(pk32).hash().to_canonical_bytes(); + let node_id = EddsaPkHash::from(EddsaPkBytes::from_bytes(pk32)) + .to_canonical_bytes(); platform_index.insert(node_id, entry.index); } } diff --git a/packages/rs-platform-wallet/src/masternode/locator.rs b/packages/rs-platform-wallet/src/masternode/locator.rs index 81f482c7949..25083796d78 100644 --- a/packages/rs-platform-wallet/src/masternode/locator.rs +++ b/packages/rs-platform-wallet/src/masternode/locator.rs @@ -28,7 +28,7 @@ use dashcore::base64::engine::{DecodePaddingMode, GeneralPurpose, GeneralPurposeConfig}; use dashcore::base64::{alphabet, Engine}; -use dashcore::eddsa::EddsaPkBytes; +use dashcore::eddsa::{EddsaPkBytes, EddsaPkHash}; use std::collections::{BTreeSet, HashMap}; use std::net::{IpAddr, SocketAddr}; use std::sync::Arc; @@ -378,7 +378,7 @@ pub fn bls_public_keys(secret: &[u8; 32]) -> Option<([u8; 48], [u8; 48])> { /// Tenderdash node id for an ed25519 `seed`. pub fn ed25519_node_id(seed: &[u8; 32]) -> [u8; 20] { let public = SigningKey::from_bytes(seed).verifying_key().to_bytes(); - EddsaPkBytes::from_bytes(public).hash().to_canonical_bytes() + EddsaPkHash::from(EddsaPkBytes::from_bytes(public)).to_canonical_bytes() } // --------------------------------------------------------------------------- diff --git a/packages/rs-platform-wallet/src/spv/runtime.rs b/packages/rs-platform-wallet/src/spv/runtime.rs index f7f84ff9fb3..beb8bd157ce 100644 --- a/packages/rs-platform-wallet/src/spv/runtime.rs +++ b/packages/rs-platform-wallet/src/spv/runtime.rs @@ -490,16 +490,9 @@ impl SpvRuntime { return Vec::new(); } - let engine = client.masternode_list_engine().ok(); + let list = client.latest_masternode_list().await; drop(client_guard); - - match engine { - Some(engine) => { - let engine_guard = engine.read().await; - classify_peers(&addresses, engine_guard.latest_masternode_list()) - } - None => classify_peers(&addresses, None), - } + classify_peers(&addresses, list.as_ref()) } /// Snapshot of the current deterministic masternode list (DML) keyed @@ -517,18 +510,11 @@ impl SpvRuntime { pub fn masternode_validity_snapshot_blocking( &self, ) -> Option> { - // Clone the engine `Arc` out while holding the client lock, then - // drop it before reading the engine — same ordering as - // `connected_peers`. - let engine = { + let list = { let client_guard = self.client.blocking_read(); - let client = client_guard.as_ref()?; - client.masternode_list_engine().ok()? + client_guard.as_ref()?.latest_masternode_list_blocking()? }; - let engine_guard = engine.blocking_read(); - let list = engine_guard.latest_masternode_list()?; - let mut map = std::collections::HashMap::with_capacity(list.masternodes.len()); for qualified in list.masternodes.values() { let entry = &qualified.masternode_list_entry; @@ -560,55 +546,40 @@ impl SpvRuntime { /// it must run off the async runtime (FFI blocking thread), mirroring the /// other `*_blocking` accessors. pub fn masternodes_by_voting_key_blocking(&self, voting_key_id: &PubkeyHash) -> Vec<[u8; 32]> { - // Clone the engine `Arc` out while holding the client lock, then drop - // it before reading the engine — same ordering as `connected_peers`. - let engine = { + let list = { let client_guard = self.client.blocking_read(); let Some(client) = client_guard.as_ref() else { return Vec::new(); }; - match client.masternode_list_engine().ok() { - Some(engine) => engine, - None => return Vec::new(), - } - }; - - let engine_guard = engine.blocking_read(); - let Some(list) = engine_guard.latest_masternode_list() else { - return Vec::new(); + let Some(list) = client.latest_masternode_list_blocking() else { + return Vec::new(); + }; + list }; - masternodes_by_voting_key(list, voting_key_id) + masternodes_by_voting_key(&list, voting_key_id) } /// Snapshot of the current-tip deterministic masternode list as typed /// summaries. `None` when the list isn't available (SPV client not /// running, engine not initialized, or masternode sync not complete). - /// Clones the engine `Arc` out under the client lock and reads the - /// engine without it — the two never nest, same as - /// [`Self::masternode_validity_snapshot_blocking`]. + /// Reads an owned snapshot through the client's public query API. pub async fn masternode_list_summaries(&self) -> Option> { - let engine = { + let list = { let client_guard = self.client.read().await; - let client = client_guard.as_ref()?; - client.masternode_list_engine().ok()? + client_guard.as_ref()?.latest_masternode_list().await? }; - let engine_guard = engine.read().await; - let list = engine_guard.latest_masternode_list()?; - Some(MasternodeListSummary::all_from_list(list)) + Some(MasternodeListSummary::all_from_list(&list)) } /// Blocking twin of [`Self::masternode_list_summaries`] for FFI threads /// (`blocking_read`; never call from the async runtime). pub fn masternode_list_summaries_blocking(&self) -> Option> { - let engine = { + let list = { let client_guard = self.client.blocking_read(); - let client = client_guard.as_ref()?; - client.masternode_list_engine().ok()? + client_guard.as_ref()?.latest_masternode_list_blocking()? }; - let engine_guard = engine.blocking_read(); - let list = engine_guard.latest_masternode_list()?; - Some(MasternodeListSummary::all_from_list(list)) + Some(MasternodeListSummary::all_from_list(&list)) } /// Get the current sync progress. diff --git a/packages/rs-platform-wallet/src/wallet/provider_key_at_index.rs b/packages/rs-platform-wallet/src/wallet/provider_key_at_index.rs index 254f4569a36..47c3ad04b51 100644 --- a/packages/rs-platform-wallet/src/wallet/provider_key_at_index.rs +++ b/packages/rs-platform-wallet/src/wallet/provider_key_at_index.rs @@ -74,7 +74,7 @@ //! are scrubbed when dropped. use dashcore::bls_sig_utils::BlsScheme; -use dashcore::eddsa::EddsaPkBytes; +use dashcore::eddsa::{EddsaPkBytes, EddsaPkHash, EddsaSecretKey}; use key_wallet::account::derivation::AccountDerivation; use key_wallet::account::{AccountType, BLSAccount, EdDSAAccount}; use key_wallet::bip32::{ChildNumber, ExtendedPrivKey}; @@ -156,13 +156,12 @@ pub fn derive_platform_node_public_keys( "failed to derive Ed25519 platform-node key at index {index}: {e}" )) })?; - let public_key: [u8; 32] = signing_key.public_key().to_bytes(); + let public_key: [u8; 32] = EddsaSecretKey::from(&signing_key).public_key().to_bytes(); // The 20-byte platform node id = SHA256(ed25519 pubkey)[..20] — the // Tenderdash/CometBFT convention (rust-dashcore #884) that a ProRegTx // `platform_node_id` field carries, NOT a hash160. - let node_id: [u8; 20] = EddsaPkBytes::from_bytes(public_key) - .hash() - .to_canonical_bytes(); + let node_id: [u8; 20] = + EddsaPkHash::from(EddsaPkBytes::from_bytes(public_key)).to_canonical_bytes(); out.push(ProviderPlatformNodePubKey { index, public_key, @@ -568,15 +567,16 @@ impl PlatformWallet { "failed to derive Ed25519 platform-node key at index {index}: {e}" )) })?; - let verifying_bytes: [u8; 32] = signing_key.public_key().to_bytes(); + let verifying_bytes: [u8; 32] = + EddsaSecretKey::from(&signing_key).public_key().to_bytes(); let public_key_bytes = verifying_bytes.to_vec(); // The 20-byte platform node id = SHA256(ed25519 pubkey)[..20] // — the Tenderdash/CometBFT convention (rust-dashcore #884) // the ProRegTx `platform_node_id` field carries, NOT a hash160. - let node_id: [u8; 20] = EddsaPkBytes::from_bytes(verifying_bytes) - .hash() - .to_canonical_bytes(); + let node_id: [u8; 20] = + EddsaPkHash::from(EddsaPkBytes::from_bytes(verifying_bytes)) + .to_canonical_bytes(); let private_key = include_private.then(|| Zeroizing::new(signing_key.to_bytes().to_vec())); @@ -773,9 +773,8 @@ mod tests { assert_eq!(keys.len(), 20, "requested 20 keys"); for (i, k) in keys.iter().enumerate() { assert_eq!(k.index, i as u32, "index ordering"); - let expected_node_id: [u8; 20] = EddsaPkBytes::from_bytes(k.public_key) - .hash() - .to_canonical_bytes(); + let expected_node_id: [u8; 20] = + EddsaPkHash::from(EddsaPkBytes::from_bytes(k.public_key)).to_canonical_bytes(); assert_eq!( k.node_id, expected_node_id, "node_id must be SHA256(ed25519 pubkey)[..20] at index {i}" @@ -789,7 +788,7 @@ mod tests { .expect("platform_node_key_at"); assert_eq!( k.public_key, - via_api.public_key().to_bytes(), + EddsaSecretKey::from(&via_api).public_key().to_bytes(), "snapshot pubkey must equal platform_node_key_at at {i}" ); } @@ -814,7 +813,7 @@ mod tests { // The snapshot's index-0 public key corresponds to that golden secret. assert_eq!( keys[0].public_key, - sk0.public_key().to_bytes(), + EddsaSecretKey::from(&sk0).public_key().to_bytes(), "snapshot index-0 pubkey must match the golden secret's public key" ); // Golden pubkey + Tenderdash node-id (SHA256[..20]) for mainnet @@ -1049,9 +1048,8 @@ mod tests { // The row address is the P2PKH payload of the node id, so a // scan recomputing SHA256[..20] from the pubkey maps back to // this index. - let expected_node_id = EddsaPkBytes::from_bytes(k.public_key) - .hash() - .to_canonical_bytes(); + let expected_node_id = + EddsaPkHash::from(EddsaPkBytes::from_bytes(k.public_key)).to_canonical_bytes(); assert_eq!( k.node_id, expected_node_id, "node id must be the Tenderdash SHA256[..20]" diff --git a/packages/rs-sdk/Cargo.toml b/packages/rs-sdk/Cargo.toml index f0585dbc021..062250acabe 100644 --- a/packages/rs-sdk/Cargo.toml +++ b/packages/rs-sdk/Cargo.toml @@ -157,7 +157,6 @@ core_key_wallet = ["dpp/core_key_wallet"] core_key_wallet_serde = ["dpp/core_key_wallet_serde"] core_key_wallet_bincode = ["dpp/core_key_wallet_bincode"] core_key_wallet_manager = ["dpp/core_key_wallet_manager"] -core_key_wallet_bip38 = ["dpp/core_key_wallet_bip_38"] core_spv = ["dpp/core_spv"] core_rpc_client = ["dpp/core_rpc_client"] shielded = ["dep:grovedb-commitment-tree", "dpp/shielded-client"] From 41e945377d9426c1f92c793ed44c09f706124909 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Fri, 9 Oct 2026 07:46:32 +0000 Subject: [PATCH 13/22] chore(deps): pin rust-dashcore to legacy serde restoration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pin all rust-dashcore crates to e6873e95 from fix/legacy-serde-bls-node-id. Adapt PlatformNodeId accessors while preserving canonical wallet bytes. Validation: 206 targeted tests, scoped all-feature Clippy, and rustfmt pass. 🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent Co-Authored-By: Codex GPT-6 --- CHANGELOG.md | 6 ++--- Cargo.lock | 26 +++++++++---------- Cargo.toml | 16 ++++++------ .../rs-platform-wallet/src/masternode/list.rs | 4 +-- .../src/masternode/record.rs | 4 +-- .../src/masternode/tracked.rs | 2 +- .../src/masternode/update_service.rs | 6 ++--- packages/rs-platform-wallet/src/spv/peers.rs | 2 +- 8 files changed, 32 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e7cdb7b2bf..88e2a08266b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,9 @@ ### Changed -- **Rust dependencies:** pin rust-dashcore to PR #1145 (`40e7b24c`) to accept - legacy BLS public-key encodings through binary Serde. PlatformNodeId storage - compatibility is not covered by this update. +- **Rust dependencies:** pin rust-dashcore to `fix/legacy-serde-bls-node-id` + (`e6873e95`), restoring legacy BLS public-key and canonical PlatformNodeId + serialization, and preserving the pre-v4 coinbase payload Serde layout. - **Breaking (Rust features):** remove DPP's `core_key_wallet_bip_38` and SDK's `core_key_wallet_bip38` feature forwards because upstream removed BIP38 support. diff --git a/Cargo.lock b/Cargo.lock index 3e45cdd73f7..4c39ea78998 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1808,7 +1808,7 @@ dependencies = [ [[package]] name = "dash-network" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" +source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" dependencies = [ "cbindgen 0.29.4", "grovedb-bincode", @@ -1819,7 +1819,7 @@ dependencies = [ [[package]] name = "dash-network-seeds" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" +source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" dependencies = [ "dash-network", ] @@ -1948,7 +1948,7 @@ dependencies = [ [[package]] name = "dash-spv" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" +source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" dependencies = [ "async-trait", "chrono", @@ -2002,7 +2002,7 @@ dependencies = [ [[package]] name = "dashcore" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" +source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" dependencies = [ "anyhow", "base64 0.22.1", @@ -2026,7 +2026,7 @@ dependencies = [ [[package]] name = "dashcore-crypto" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" +source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" dependencies = [ "base58ck 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitcoin-crypto", @@ -2047,12 +2047,12 @@ dependencies = [ [[package]] name = "dashcore-private" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" +source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" [[package]] name = "dashcore-rpc" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" +source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" dependencies = [ "dashcore-rpc-json", "jsonrpc", @@ -2064,7 +2064,7 @@ dependencies = [ [[package]] name = "dashcore-rpc-json" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" +source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" dependencies = [ "dashcore", "grovedb-bincode", @@ -2079,7 +2079,7 @@ dependencies = [ [[package]] name = "dashcore_hashes" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" +source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" dependencies = [ "bitcoin_hashes 0.14.101", "grovedb-bincode", @@ -3163,7 +3163,7 @@ dependencies = [ [[package]] name = "git-state" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" +source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" [[package]] name = "glob" @@ -4403,7 +4403,7 @@ dependencies = [ [[package]] name = "key-wallet" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" +source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" dependencies = [ "async-trait", "bip39", @@ -4426,7 +4426,7 @@ dependencies = [ [[package]] name = "key-wallet-ffi" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" +source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" dependencies = [ "cbindgen 0.29.4", "dash-network", @@ -4442,7 +4442,7 @@ dependencies = [ [[package]] name = "key-wallet-manager" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=40e7b24c46100496a279f34952b851d40aa93a5f#40e7b24c46100496a279f34952b851d40aa93a5f" +source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" dependencies = [ "async-trait", "dashcore", diff --git a/Cargo.toml b/Cargo.toml index ff8eb9c73c3..4e824b54a2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,14 +65,14 @@ grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "9791d2772 grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "9791d277207998b7789c85b0745e99d37040a12e" } grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "9791d277207998b7789c85b0745e99d37040a12e" } grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "9791d277207998b7789c85b0745e99d37040a12e" } -dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "40e7b24c46100496a279f34952b851d40aa93a5f" } -dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "40e7b24c46100496a279f34952b851d40aa93a5f" } -dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "40e7b24c46100496a279f34952b851d40aa93a5f" } -key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "40e7b24c46100496a279f34952b851d40aa93a5f" } -key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "40e7b24c46100496a279f34952b851d40aa93a5f" } -key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "40e7b24c46100496a279f34952b851d40aa93a5f" } -dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "40e7b24c46100496a279f34952b851d40aa93a5f" } -dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "40e7b24c46100496a279f34952b851d40aa93a5f" } +dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "e6873e95ff3386ce7a0a03df9b2baca59e960700" } +dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "e6873e95ff3386ce7a0a03df9b2baca59e960700" } +dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "e6873e95ff3386ce7a0a03df9b2baca59e960700" } +key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "e6873e95ff3386ce7a0a03df9b2baca59e960700" } +key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "e6873e95ff3386ce7a0a03df9b2baca59e960700" } +key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "e6873e95ff3386ce7a0a03df9b2baca59e960700" } +dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "e6873e95ff3386ce7a0a03df9b2baca59e960700" } +dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "e6873e95ff3386ce7a0a03df9b2baca59e960700" } tokio-metrics = "0.5" # Size-tuned profile for the iOS `rs-unified-sdk-ffi` staticlib, which diff --git a/packages/rs-platform-wallet/src/masternode/list.rs b/packages/rs-platform-wallet/src/masternode/list.rs index cd6050af8e5..0cc701b8311 100644 --- a/packages/rs-platform-wallet/src/masternode/list.rs +++ b/packages/rs-platform-wallet/src/masternode/list.rs @@ -72,7 +72,7 @@ impl MasternodeListSummary { platform_node_id, } => ( Some(*platform_http_port), - Some(platform_node_id.to_canonical_bytes()), + Some(platform_node_id.to_byte_array()), true, ), }; @@ -223,7 +223,7 @@ mod tests { is_valid: false, mn_type: EntryMasternodeType::HighPerformance { platform_http_port: 1443, - platform_node_id: PlatformNodeId::from_canonical_bytes([3u8; 20]), + platform_node_id: PlatformNodeId::from_byte_array([3u8; 20]), }, }; let list = MasternodeList::build( diff --git a/packages/rs-platform-wallet/src/masternode/record.rs b/packages/rs-platform-wallet/src/masternode/record.rs index 19b072e7b7a..71589eca833 100644 --- a/packages/rs-platform-wallet/src/masternode/record.rs +++ b/packages/rs-platform-wallet/src/masternode/record.rs @@ -349,7 +349,7 @@ where if agg.platform_node_id.is_none() || height >= agg.platform_node_height { // Evonode IDs use canonical Tenderdash bytes in the ownership index. if let Some(node_id) = p.platform_node_id { - agg.platform_node_id = Some(node_id.to_canonical_bytes()); + agg.platform_node_id = Some(node_id.to_byte_array()); agg.platform_node_height = height; } } @@ -366,7 +366,7 @@ where } if let Some(node_id) = p.platform_node_id { if agg.platform_node_id.is_none() || height >= agg.platform_node_height { - agg.platform_node_id = Some(node_id.to_canonical_bytes()); + agg.platform_node_id = Some(node_id.to_byte_array()); agg.platform_node_height = height; } } diff --git a/packages/rs-platform-wallet/src/masternode/tracked.rs b/packages/rs-platform-wallet/src/masternode/tracked.rs index a05bbb5f53c..66e179015b5 100644 --- a/packages/rs-platform-wallet/src/masternode/tracked.rs +++ b/packages/rs-platform-wallet/src/masternode/tracked.rs @@ -1092,7 +1092,7 @@ pub fn registration_from_transaction( payout_script: p.script_payout.as_bytes().to_vec(), service_address: Some(p.service_address.to_string()), is_evonode: p.masternode_type == ProviderMasternodeType::HighPerformance, - platform_node_id: p.platform_node_id.map(|id| id.to_canonical_bytes()), + platform_node_id: p.platform_node_id.map(|id| id.to_byte_array()), platform_http_port: p.platform_http_port, }) } diff --git a/packages/rs-platform-wallet/src/masternode/update_service.rs b/packages/rs-platform-wallet/src/masternode/update_service.rs index a1e5be1c627..7e6f1719d34 100644 --- a/packages/rs-platform-wallet/src/masternode/update_service.rs +++ b/packages/rs-platform-wallet/src/masternode/update_service.rs @@ -309,7 +309,7 @@ pub(crate) fn prepare_update_service_placeholder( })?; ( Some(ProviderMasternodeType::HighPerformance as u16), - Some(PlatformNodeId::from_canonical_bytes(node_id)), + Some(PlatformNodeId::from_byte_array(node_id)), Some(p2p_port), Some(http_port), ) @@ -619,9 +619,7 @@ mod tests { ); assert_eq!( payload.platform_node_id, - entry - .platform_node_id - .map(PlatformNodeId::from_canonical_bytes) + entry.platform_node_id.map(PlatformNodeId::from_byte_array) ); assert_eq!(payload.platform_p2p_port, Some(26656)); assert_eq!(payload.platform_http_port, entry.platform_http_port); diff --git a/packages/rs-platform-wallet/src/spv/peers.rs b/packages/rs-platform-wallet/src/spv/peers.rs index f60ce99bcf8..6c179b5d58b 100644 --- a/packages/rs-platform-wallet/src/spv/peers.rs +++ b/packages/rs-platform-wallet/src/spv/peers.rs @@ -189,7 +189,7 @@ mod tests { evo, EntryMasternodeType::HighPerformance { platform_http_port: 443, - platform_node_id: PlatformNodeId::from_canonical_bytes([0u8; 20]), + platform_node_id: PlatformNodeId::from_byte_array([0u8; 20]), }, ), ]); From 1dbcaaf70b62597eff03581562dd2e25fa6ec9c5 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Fri, 9 Oct 2026 08:27:31 +0000 Subject: [PATCH 14/22] fix(drive-abci): preserve quorum key storage independently of serde MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Encode quorum keys as exactly 48 compressed bytes through a storage adapter, validating points on decode. Keep current and previous quorum layouts unchanged. Add frozen quorum, saved-state and checkpoint records generated by the pre-migration writer at 7c73e983b4. Assert keys, metadata, full consumption and byte-identical re-encoding, including infinity and malformed key coverage. No disk-format change or migration is introduced. Validation: 47 targeted tests, all-target/all-feature drive-abci Clippy and fmt. 🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent Co-Authored-By: Codex GPT-6 --- CHANGELOG.md | 5 + .../v0/fixtures/README.md | 35 ++++ .../v0/fixtures/checkpoint-platform-state.hex | 1 + .../v0/fixtures/generate-quorum-storage.rs | 101 +++++++++++ .../v0/fixtures/platform-state-v1.hex | 1 + .../v0/fixtures/platform-state-v2.hex | 1 + .../v0/fixtures/quorum-storage-v1.hex | 1 + .../v0/fixtures/quorum-storage-v2.hex | 1 + .../v0/for_saving_v1.rs | 11 +- .../v0/mod.rs | 4 + .../v0/public_key_for_saving.rs | 26 +++ .../v0/storage_tests.rs | 161 ++++++++++++++++++ 12 files changed, 343 insertions(+), 5 deletions(-) create mode 100644 packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/checkpoint-platform-state.hex create mode 100644 packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/generate-quorum-storage.rs create mode 100644 packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/platform-state-v1.hex create mode 100644 packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/platform-state-v2.hex create mode 100644 packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v1.hex create mode 100644 packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v2.hex create mode 100644 packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/public_key_for_saving.rs create mode 100644 packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/storage_tests.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 88e2a08266b..450384f9afc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## [Unreleased] +### Fixed + +- **drive-abci:** preserve the fixed 48-byte quorum-key disk encoding independently + of the BLS backend, with historical saved-state and checkpoint regression fixtures. + ### Changed - **Rust dependencies:** pin rust-dashcore to `fix/legacy-serde-bls-node-id` diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/README.md b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/README.md index 858e0c7c19a..7c2d5062e5b 100644 --- a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/README.md +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/README.md @@ -33,3 +33,38 @@ cargo run -p drive-abci --example generate_quorum_storage_v0 --all-features --lo Compare the generated file with the committed fixture. Do not regenerate the expected bytes with the migrated backend. The generator explicitly selects V0; the ordinary runtime-to-storage conversion writes V2. + +## Historical V1/V2 quorum, saved-state and checkpoint records + +`generate-quorum-storage.rs` was run against the same pre-migration Platform +commit above, using `blsful` at `0c34a7a488a0bd1c9a9a2196e793b303ad35c900`. +The configuration, keys, hashes, indexes and previous-quorum heights are the same +as the V0 fixture. V1 stores previous keys with the C++ library; V2 stores both +lists with blsful. Its public keys occupy exactly 48 compressed bytes, without +a length prefix. + +The full state fixtures contain both chain-lock and instant-lock quorum sets, +protocol versions 13 (current) and 14 (next), and empty masternode/validator +collections. `platform-state-v1.hex` comes from `serialize_to_bytes`; +`checkpoint-platform-state.hex` comes from `serialize_standalone_to_bytes`, the +writer used for checkpoint `platform_state.bin`. These two files intentionally +have identical bytes. `platform-state-v2.hex` contains the per-entry storage +record; its external collections are empty. These are synthetic historical +records, not snapshots of a live database. + +Copy the generator to `packages/rs-drive-abci/examples/generate_quorum_storage.rs` +in that historical checkout and run: + +```sh +cargo run -p drive-abci --example generate_quorum_storage --all-features --locked -- /path/to/output-directory +``` + +Checksums below include the final newline in each hex file. + +| File | Decoded bytes | SHA-256 | +| --- | ---: | --- | +| `quorum-storage-v1.hex` | 348 | `33cfbd097457cce4d8d5fadc214a12beee7a03b3b2a6deecabe77e74299bfee1` | +| `quorum-storage-v2.hex` | 346 | `bc2eefbb94cd7e8f2dae643eed1d76bff3953412955bb8a96960f755b34235de` | +| `platform-state-v1.hex` | 734 | `e2b83244feff85195d45fe4092ea85d298037f53cecdddd55c0eb7a5dda27ad7` | +| `checkpoint-platform-state.hex` | 734 | `e2b83244feff85195d45fe4092ea85d298037f53cecdddd55c0eb7a5dda27ad7` | +| `platform-state-v2.hex` | 732 | `d5eb9be1fac002863a22f1cfec3bb770ee9f67dde44ed3cba4734b18135f9616` | diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/checkpoint-platform-state.hex b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/checkpoint-platform-state.hex new file mode 100644 index 00000000000..a564ec38c38 --- /dev/null +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/checkpoint-platform-state.hex @@ -0,0 +1 @@ +0100000d0e0000000000000000000000000000000000000000000000000000000000000000000002010400fb012002111111111111111111111111111111111111111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb002222222222222222222222222222222222222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e01000102333333333333333333333333333333333333333333333333333333333333333395fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe4801014444444444444444444444444444444444444444444444444444444444444444b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb038402010400fb012002111111111111111111111111111111111111111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb002222222222222222222222222222222222222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e01000102333333333333333333333333333333333333333333333333333333333333333395fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe4801014444444444444444444444444444444444444444444444444444444444444444b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb0384000000 diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/generate-quorum-storage.rs b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/generate-quorum-storage.rs new file mode 100644 index 00000000000..9cc5bc268d3 --- /dev/null +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/generate-quorum-storage.rs @@ -0,0 +1,101 @@ +//! Run as a drive-abci example at the pre-migration revision documented in README.md. + +use dpp::dashcore::hashes::Hash; +use dpp::dashcore::QuorumHash; +use dpp::dashcore_rpc::json::QuorumType; +use dpp::serialization::PlatformSerializable; +use drive_abci::config::{ChainLockConfig, PlatformConfig}; +use drive_abci::platform_types::platform_state::{ + platform_state_for_saving::{v2::PlatformStateForSavingV2, PlatformStateForSaving}, + PlatformState, +}; +use drive_abci::platform_types::signature_verification_quorum_set::{ + Quorums, SignatureVerificationQuorumSetForSaving, SignatureVerificationQuorumSetV0, + SignatureVerificationQuorumSetV0Methods, VerificationQuorum, +}; +use std::path::Path; + +const PUBLIC_KEYS: [&str; 4] = [ + "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e", + "95fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe48", + "b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", +]; + +fn quorums(entries: &[(u8, usize, Option)]) -> Quorums { + entries + .iter() + .map(|&(hash, key, index)| { + ( + QuorumHash::from_byte_array([hash; 32]), + VerificationQuorum { + public_key: hex::decode(PUBLIC_KEYS[key]) + .unwrap() + .as_slice() + .try_into() + .unwrap(), + index, + }, + ) + }) + .collect() +} + +fn main() { + let mut runtime = SignatureVerificationQuorumSetV0::new(&ChainLockConfig { + quorum_type: QuorumType::Llmq400_60, + quorum_size: 400, + quorum_window: 288, + quorum_active_signers: 4, + quorum_rotation: false, + }); + runtime.set_current_quorums(quorums(&[(0x11, 0, None), (0x22, 1, Some(0))])); + runtime.set_previous_past_quorums(Quorums::default(), 899, 900); + runtime.set_previous_past_quorums(quorums(&[(0x33, 2, Some(1)), (0x44, 3, None)]), 1000, 1008); + let output = std::env::args().nth(1).expect("fixture output directory"); + let output = Path::new(&output); + let config = bincode::config::standard() + .with_big_endian() + .with_no_limit(); + for (name, stored) in [ + ( + "quorum-storage-v1.hex", + SignatureVerificationQuorumSetForSaving::V1(runtime.clone().into()), + ), + ( + "quorum-storage-v2.hex", + SignatureVerificationQuorumSetForSaving::V2(runtime.clone().into()), + ), + ] { + write( + output, + name, + bincode::encode_to_vec(stored, config).unwrap(), + ); + } + let mut state = + PlatformState::default_with_protocol_versions(13, 14, &PlatformConfig::default()).unwrap(); + state.chain_lock_validating_quorums = runtime.clone().into(); + state.instant_lock_validating_quorums = runtime.into(); + write( + output, + "platform-state-v1.hex", + state.serialize_to_bytes().unwrap(), + ); + write( + output, + "checkpoint-platform-state.hex", + state.serialize_standalone_to_bytes().unwrap(), + ); + let stored = PlatformStateForSaving::V2(PlatformStateForSavingV2::from(&state)); + write( + output, + "platform-state-v2.hex", + bincode::encode_to_vec(stored, config).unwrap(), + ); +} + +fn write(output: &Path, name: &str, bytes: Vec) { + println!("{name}: {} bytes", bytes.len()); + std::fs::write(output.join(name), format!("{}\n", hex::encode(bytes))).unwrap(); +} diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/platform-state-v1.hex b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/platform-state-v1.hex new file mode 100644 index 00000000000..a564ec38c38 --- /dev/null +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/platform-state-v1.hex @@ -0,0 +1 @@ +0100000d0e0000000000000000000000000000000000000000000000000000000000000000000002010400fb012002111111111111111111111111111111111111111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb002222222222222222222222222222222222222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e01000102333333333333333333333333333333333333333333333333333333333333333395fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe4801014444444444444444444444444444444444444444444444444444444444444444b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb038402010400fb012002111111111111111111111111111111111111111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb002222222222222222222222222222222222222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e01000102333333333333333333333333333333333333333333333333333333333333333395fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe4801014444444444444444444444444444444444444444444444444444444444444444b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb0384000000 diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/platform-state-v2.hex b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/platform-state-v2.hex new file mode 100644 index 00000000000..4b6bdcbfde5 --- /dev/null +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/platform-state-v2.hex @@ -0,0 +1 @@ +0200000d0e0000000000000000000000000000000000000000000000000000000000000000000002010400fb012002111111111111111111111111111111111111111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb002222222222222222222222222222222222222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e01000102333333333333333333333333333333333333333333333333333333333333333395fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe4801014444444444444444444444444444444444444444444444444444444444444444b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb038402010400fb012002111111111111111111111111111111111111111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb002222222222222222222222222222222222222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e01000102333333333333333333333333333333333333333333333333333333333333333395fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe4801014444444444444444444444444444444444444444444444444444444444444444b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb038400 diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v1.hex b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v1.hex new file mode 100644 index 00000000000..80566884687 --- /dev/null +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v1.hex @@ -0,0 +1 @@ +01010400fb012002111111111111111111111111111111111111111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb002222222222222222222222222222222222222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e0100010233333333333333333333333333333333333333333333333333333333333333333095fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe480101444444444444444444444444444444444444444444444444444444444444444430b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb0384 diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v2.hex b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v2.hex new file mode 100644 index 00000000000..0c518338948 --- /dev/null +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v2.hex @@ -0,0 +1 @@ +02010400fb012002111111111111111111111111111111111111111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb002222222222222222222222222222222222222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e01000102333333333333333333333333333333333333333333333333333333333333333395fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe4801014444444444444444444444444444444444444444444444444444444444444444b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb0384 diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/for_saving_v1.rs b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/for_saving_v1.rs index 1d903b160a1..7561f7eeae2 100644 --- a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/for_saving_v1.rs +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/for_saving_v1.rs @@ -1,10 +1,11 @@ +use super::public_key_for_saving::PublicKeyForSaving; #[cfg(feature = "bls-signatures")] use crate::platform_types::signature_verification_quorum_set::v0::for_saving_v0::PreviousPastQuorumsForSavingV0; use crate::platform_types::signature_verification_quorum_set::v0::quorum_config_for_saving_v0::QuorumConfigForSavingV0; use crate::platform_types::signature_verification_quorum_set::v0::quorum_set::PreviousPastQuorumsV0; use crate::platform_types::signature_verification_quorum_set::{ Quorums, SignatureVerificationQuorumSetForSaving, SignatureVerificationQuorumSetV0, - ThresholdBlsPublicKey, VerificationQuorum, + VerificationQuorum, }; use bincode::{Decode, Encode}; @@ -67,8 +68,8 @@ impl From for SignatureVerificationQu #[derive(Debug, Clone, Encode, Decode)] pub struct QuorumForSavingV1 { hash: Bytes32, - #[bincode(with_serde)] - public_key: ThresholdBlsPublicKey, + // Preserve the shipped 48-byte key layout in every selecting protocol version. + public_key: PublicKeyForSaving, index: Option, } @@ -78,7 +79,7 @@ impl From> for Quorums { ( QuorumHash::from_byte_array(quorum.hash.to_buffer()), VerificationQuorum { - public_key: quorum.public_key, + public_key: quorum.public_key.0, index: quorum.index, }, ) @@ -91,7 +92,7 @@ impl From> for Vec { .into_iter() .map(|(hash, quorum)| QuorumForSavingV1 { hash: Bytes32::from(hash.as_byte_array()), - public_key: quorum.public_key, + public_key: PublicKeyForSaving(quorum.public_key), index: quorum.index, }) .collect() diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/mod.rs b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/mod.rs index bc4511bd4cf..920ef6cc08c 100644 --- a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/mod.rs +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/mod.rs @@ -1,6 +1,10 @@ pub mod for_saving_v0; pub mod for_saving_v1; pub mod for_saving_v2; +mod public_key_for_saving; pub mod quorum_config_for_saving_v0; pub mod quorum_set; pub mod quorums; + +#[cfg(all(test, feature = "bls-signatures"))] +mod storage_tests; diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/public_key_for_saving.rs b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/public_key_for_saving.rs new file mode 100644 index 00000000000..f35a1ecdc60 --- /dev/null +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/public_key_for_saving.rs @@ -0,0 +1,26 @@ +use crate::platform_types::signature_verification_quorum_set::ThresholdBlsPublicKey; +use bincode::de::Decoder; +use bincode::enc::Encoder; +use bincode::error::{DecodeError, EncodeError}; +use bincode::{Decode, Encode}; + +/// The disk contract is 48 compressed G1 bytes, independent of the crypto backend's Serde format. +#[derive(Debug, Clone)] +pub(super) struct PublicKeyForSaving(pub(super) ThresholdBlsPublicKey); + +impl Encode for PublicKeyForSaving { + fn encode(&self, encoder: &mut E) -> Result<(), EncodeError> { + self.0.to_bytes().encode(encoder) + } +} + +impl Decode for PublicKeyForSaving { + fn decode>(decoder: &mut D) -> Result { + let bytes = <[u8; 48]>::decode(decoder)?; + ThresholdBlsPublicKey::try_from(bytes.as_slice()) + .map(Self) + .map_err(|_| DecodeError::Other("invalid compressed quorum public key")) + } +} + +bincode::impl_borrow_decode!(PublicKeyForSaving); diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/storage_tests.rs b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/storage_tests.rs new file mode 100644 index 00000000000..107970feca9 --- /dev/null +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/storage_tests.rs @@ -0,0 +1,161 @@ +use super::for_saving_v1::QuorumForSavingV1; +use super::quorum_set::PreviousPastQuorumsV0; +use crate::platform_types::platform_state::platform_state_for_saving::PlatformStateForSaving; +use crate::platform_types::platform_state::PlatformState; +use crate::platform_types::signature_verification_quorum_set::{ + Quorums, SignatureVerificationQuorumSet, SignatureVerificationQuorumSetForSaving, + SignatureVerificationQuorumSetV0Methods, VerificationQuorum, +}; +use dpp::bls::PublicKey; +use dpp::dashcore::hashes::Hash; +use dpp::dashcore::QuorumHash; +use dpp::dashcore_rpc::json::QuorumType; +use dpp::serialization::PlatformDeserializableFromVersionedStructureTrusted; +use dpp::version::PlatformVersion; + +const KEYS: [&str; 4] = [ + "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e", + "95fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe48", + "b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", +]; + +fn config() -> bincode::config::Configuration< + bincode::config::BigEndian, + bincode::config::Varint, + bincode::config::NoLimit, +> { + bincode::config::standard() + .with_big_endian() + .with_no_limit() +} + +fn assert_quorum(quorums: &Quorums, hash: u8, key: usize, index: Option) { + let quorum = quorums + .get(&QuorumHash::from_byte_array([hash; 32])) + .unwrap(); + assert_eq!( + quorum.public_key.to_bytes().as_slice(), + hex::decode(KEYS[key]).unwrap() + ); + assert_eq!(quorum.index, index); +} + +fn assert_quorums(set: &SignatureVerificationQuorumSet) { + assert_eq!(set.config().quorum_type, QuorumType::Llmq400_60); + assert_eq!(set.config().active_signers, 4); + assert_eq!(set.config().window, 288); + assert!(!set.config().rotation); + assert_eq!(set.current_quorums().len(), 2); + assert_quorum(set.current_quorums(), 0x11, 0, None); + assert_quorum(set.current_quorums(), 0x22, 1, Some(0)); + let SignatureVerificationQuorumSet::V0(runtime) = set; + let PreviousPastQuorumsV0 { + quorums, + last_active_core_height, + updated_at_core_height, + previous_change_height, + } = runtime.previous.as_ref().unwrap(); + assert_eq!( + ( + *last_active_core_height, + *updated_at_core_height, + *previous_change_height + ), + (1000, 1008, Some(900)) + ); + assert_eq!(quorums.len(), 2); + assert_quorum(quorums, 0x33, 2, Some(1)); + assert_quorum(quorums, 0x44, 3, None); +} + +#[test] +fn should_preserve_current_and_previous_quorums_in_old_v1_and_v2_records() { + for fixture in [ + include_str!("fixtures/quorum-storage-v1.hex"), + include_str!("fixtures/quorum-storage-v2.hex"), + ] { + let bytes = hex::decode(fixture.trim()).unwrap(); + let (stored, read): (SignatureVerificationQuorumSetForSaving, _) = + bincode::decode_from_slice(&bytes, config()).unwrap(); + assert_eq!(read, bytes.len()); + assert_eq!(bincode::encode_to_vec(&stored, config()).unwrap(), bytes); + assert_quorums(&stored.into()); + } +} + +#[test] +fn should_load_old_saved_state_and_checkpoint_with_both_quorum_sets() { + for fixture in [ + include_str!("fixtures/platform-state-v1.hex"), + include_str!("fixtures/checkpoint-platform-state.hex"), + include_str!("fixtures/platform-state-v2.hex"), + ] { + let bytes = hex::decode(fixture.trim()).unwrap(); + let (stored, read): (PlatformStateForSaving, _) = + bincode::decode_from_slice(&bytes, config()).unwrap(); + assert_eq!(read, bytes.len()); + assert_eq!(bincode::encode_to_vec(&stored, config()).unwrap(), bytes); + let state = match stored { + PlatformStateForSaving::V2(record) => { + record.into_platform_state(vec![], vec![]).unwrap() + } + _ => PlatformState::versioned_deserialize_trusted(&bytes, PlatformVersion::latest()) + .unwrap(), + }; + assert_eq!(state.current_protocol_version_in_consensus, 13); + assert_eq!(state.next_epoch_protocol_version, 14); + assert_quorums(&state.chain_lock_validating_quorums); + assert_quorums(&state.instant_lock_validating_quorums); + if bytes[0] == 1 { + assert_eq!(state.serialize_standalone_to_bytes().unwrap(), bytes); + } + } +} + +fn saved_quorum(key: PublicKey) -> QuorumForSavingV1 { + let quorums = Quorums::from_iter([( + QuorumHash::from_byte_array([0x11; 32]), + VerificationQuorum { + public_key: key, + index: Some(1000), + }, + )]); + Vec::::from(quorums).pop().unwrap() +} + +#[test] +fn should_store_exactly_48_key_bytes_without_a_prefix_and_preserve_the_following_index() { + for key in [ + PublicKey::try_from(hex::decode(KEYS[0]).unwrap().as_slice()).unwrap(), + PublicKey::default(), + ] { + let stored = saved_quorum(key); + let mut expected = vec![0x11; 32]; + expected.extend_from_slice(&key.to_bytes()); + expected.extend_from_slice(&[1, 251, 3, 232]); // Some(1000), big-endian varint. + assert_eq!(bincode::encode_to_vec(&stored, config()).unwrap(), expected); + let (decoded, read): (QuorumForSavingV1, _) = + bincode::decode_from_slice(&expected, config()).unwrap(); + assert_eq!(read, expected.len()); + let quorums: Quorums = vec![decoded].into(); + let quorum = quorums + .get(&QuorumHash::from_byte_array([0x11; 32])) + .unwrap(); + assert_eq!(quorum.public_key, key); + assert_eq!(quorum.index, Some(1000)); + } +} + +#[test] +fn should_reject_truncated_and_invalid_stored_quorum_keys() { + let bytes = bincode::encode_to_vec(saved_quorum(PublicKey::default()), config()).unwrap(); + for len in 32..80 { + assert!( + bincode::decode_from_slice::(&bytes[..len], config()).is_err() + ); + } + let mut invalid = bytes; + invalid[32..80].fill(0); + assert!(bincode::decode_from_slice::(&invalid, config()).is_err()); +} From d8b911d9ca613e0e699646231224f6df5f10ce19 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Fri, 9 Oct 2026 08:34:49 +0000 Subject: [PATCH 15/22] fix(wallet-ffi): adapt platform node ID hashing to rust-dashcore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert raw Ed25519 public keys through EddsaPkHash while preserving the canonical SHA-256 prefix returned by the C ABI. Cover fixed output bytes, output bounds, and rejected inputs, and correct the helper documentation. 🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent Co-Authored-By: Codex GPT-6 --- CHANGELOG.md | 2 + .../src/provider_key_at_index.rs | 67 +++++++++++++++++-- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 450384f9afc..7e377f4d298 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ### Fixed +- **platform-wallet-ffi:** adapt platform node ID derivation to the updated + rust-dashcore API, restoring native SDK builds while preserving canonical bytes. - **drive-abci:** preserve the fixed 48-byte quorum-key disk encoding independently of the BLS backend, with historical saved-state and checkpoint regression fixtures. diff --git a/packages/rs-platform-wallet-ffi/src/provider_key_at_index.rs b/packages/rs-platform-wallet-ffi/src/provider_key_at_index.rs index 7f731e99bd7..8564a771b3d 100644 --- a/packages/rs-platform-wallet-ffi/src/provider_key_at_index.rs +++ b/packages/rs-platform-wallet-ffi/src/provider_key_at_index.rs @@ -31,7 +31,7 @@ //! too: the wallet-manager read guard is NEVER held across the Swift //! resolver callback. -use dashcore::eddsa::EddsaPkBytes; +use dashcore::eddsa::{EddsaPkBytes, EddsaPkHash}; use std::ffi::CString; use std::os::raw::c_char; @@ -77,7 +77,7 @@ pub struct ProviderKeyAtIndexFFI { /// on the empty state. pub legacy_public_key_hex: *mut c_char, /// Null-terminated lowercase hex of the 20-byte platform node id - /// (40 chars) — `hash160` of the Ed25519 public key. Null for + /// (40 chars) — `SHA256(ed25519 pubkey)[..20]` in canonical order. Null for /// operator keys (no node id) and on the empty state. pub node_id_hex: *mut c_char, /// Null-terminated lowercase hex of the raw 32-byte private scalar @@ -364,7 +364,7 @@ pub unsafe extern "C" fn platform_wallet_provider_key_at_index_free( /// Ed25519 public key. Pure helper — no wallet handle, no key material /// beyond the public key. /// -/// Wraps `EddsaPkBytes::hash` so the host +/// Converts through `EddsaPkHash` and returns canonical Tenderdash bytes so the host /// can render the node id of a persisted platform-node public key (which /// carries only the pubkey) without re-implementing the SHA-256 digest. /// @@ -386,7 +386,66 @@ pub unsafe extern "C" fn platform_wallet_platform_node_id_from_ed25519_pubkey( } let mut pk32 = [0u8; 32]; pk32.copy_from_slice(unsafe { std::slice::from_raw_parts(pubkey_ptr, 32) }); - let node_id = EddsaPkBytes::from_bytes(pk32).hash().to_canonical_bytes(); + let node_id = EddsaPkHash::from(EddsaPkBytes::from_bytes(pk32)).to_canonical_bytes(); unsafe { std::ptr::copy_nonoverlapping(node_id.as_ptr(), out_node_id_20, 20) }; true } + +#[cfg(test)] +mod tests { + use super::platform_wallet_platform_node_id_from_ed25519_pubkey; + + #[test] + fn should_return_canonical_platform_node_id_bytes() { + // RFC 8032 test 1 public key; expected SHA-256 prefix computed independently. + let public_key = + hex::decode("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a") + .expect("valid public key hex"); + let mut output = [0xa5; 22]; + assert!(unsafe { + platform_wallet_platform_node_id_from_ed25519_pubkey( + public_key.as_ptr(), + public_key.len(), + output[1..].as_mut_ptr(), + ) + }); + assert_eq!( + hex::encode(&output[1..21]), + "21fe31dfa154a261626bf854046fd2271b7bed4b" + ); + assert_eq!(output[0], 0xa5); + assert_eq!(output[21], 0xa5); + } + + #[test] + fn should_reject_invalid_node_id_inputs_without_writing() { + let public_key = [0u8; 33]; + for length in [0, 31, 33] { + let mut output = [0xa5; 20]; + assert!(!unsafe { + platform_wallet_platform_node_id_from_ed25519_pubkey( + public_key.as_ptr(), + length, + output.as_mut_ptr(), + ) + }); + assert_eq!(output, [0xa5; 20]); + } + let mut output = [0xa5; 20]; + assert!(!unsafe { + platform_wallet_platform_node_id_from_ed25519_pubkey( + std::ptr::null(), + 32, + output.as_mut_ptr(), + ) + }); + assert_eq!(output, [0xa5; 20]); + assert!(!unsafe { + platform_wallet_platform_node_id_from_ed25519_pubkey( + public_key.as_ptr(), + 32, + std::ptr::null_mut(), + ) + }); + } +} From c1c7b095c2878ebffc2e48835230123cb934e8ae Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Fri, 9 Oct 2026 09:56:15 +0000 Subject: [PATCH 16/22] fix(test-suite): wait for rotated quorum publication before retrying proofs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The quorum sidecar publishes keys every 60 seconds. CI twice received a proof signed by the next quorum before its key appeared, then exhausted SDK addresses through the initial 60-second node bans. Retry only a missing-cache-key proof read once after 65 seconds. Keep signature verification, node banning, and transition broadcasting unchanged. Cover delayed publication, expiry of address bans, permanent misses, affected-state proofs, and invalid signatures with fake-timer tests. Validation: 4 regression tests failed before the fix; all 9 verifier tests pass after it. ESLint passes with the unbuilt local evo-sdk import excluded; two pre-existing unused catch-variable warnings remain. 🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent Co-Authored-By: Codex GPT-6 --- CHANGELOG.md | 2 + .../lib/test/createPlatformProofVerifier.js | 34 ++++++- .../unit/createPlatformProofVerifier.spec.js | 93 +++++++++++++++++++ 3 files changed, 127 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e377f4d298..a79c8de1d8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ### Fixed +- **platform-test-suite:** retry proof reads while the local quorum sidecar publishes + a rotated key, with a bounded wait and full proof verification on each attempt. - **platform-wallet-ffi:** adapt platform node ID derivation to the updated rust-dashcore API, restoring native SDK builds while preserving canonical bytes. - **drive-abci:** preserve the fixed 48-byte quorum-key disk encoding independently diff --git a/packages/platform-test-suite/lib/test/createPlatformProofVerifier.js b/packages/platform-test-suite/lib/test/createPlatformProofVerifier.js index a49f0088484..b54cde9d23b 100644 --- a/packages/platform-test-suite/lib/test/createPlatformProofVerifier.js +++ b/packages/platform-test-suite/lib/test/createPlatformProofVerifier.js @@ -1,4 +1,8 @@ const DAPIAddress = require('@dashevo/dapi-client/lib/dapiAddressProvider/DAPIAddress'); +const wait = require('../wait'); + +// Allow the sidecar's 60-second refresh and the SDK's initial 60-second node bans to expire. +const QUORUM_PUBLICATION_WAIT_MS = 65000; /** * `WasmSdkError.name` for a transition family whose proof cannot bind the @@ -54,6 +58,30 @@ function toReportableError(error) { return reportable; } +/** + * Re-query and fully verify a proof once the sidecar publishes its quorum key. + * The WASM wait APIs refresh quorum caches on every call; no transition is broadcast here. + * + * @param {Function} readProof + * @returns {Promise<*>} + */ +async function waitForPublishedQuorum(readProof) { + try { + return await readProof(); + } catch (error) { + const reportable = toReportableError(error); + if (reportable.name !== 'DapiClientError' + || !reportable.message.includes( + 'context provider error: invalid quorum: Quorum not found in cache for hash:', + )) { + throw reportable; + } + + await wait(QUORUM_PUBLICATION_WAIT_MS); + return readProof(); + } +} + /** * Shared EvoSDK instance. One per process: the verifier is stateless and the * underlying WASM SDK multiplexes concurrent requests. @@ -222,7 +250,7 @@ function createPlatformProofVerifier({ ); try { - await sdk.stateTransitions.waitForResponse(stateTransition); + await waitForPublishedQuorum(() => sdk.stateTransitions.waitForResponse(stateTransition)); } catch (error) { // Balance top-ups, credit transfers and withdrawals, address funds // movements, shields and no-history token operations have no proof @@ -235,7 +263,9 @@ function createPlatformProofVerifier({ } try { - await sdk.stateTransitions.waitForAffectedState(stateTransition); + await waitForPublishedQuorum( + () => sdk.stateTransitions.waitForAffectedState(stateTransition), + ); } catch (affectedStateError) { throw toReportableError(affectedStateError); } diff --git a/packages/platform-test-suite/test/unit/createPlatformProofVerifier.spec.js b/packages/platform-test-suite/test/unit/createPlatformProofVerifier.spec.js index 5794188d887..8c327746ce5 100644 --- a/packages/platform-test-suite/test/unit/createPlatformProofVerifier.spec.js +++ b/packages/platform-test-suite/test/unit/createPlatformProofVerifier.spec.js @@ -41,6 +41,99 @@ function executionNotProvedError() { } describe('createPlatformProofVerifier', () => { + describe('quorum publication delay', () => { + let clock; + + beforeEach(() => { + clock = sinon.useFakeTimers(); + }); + + afterEach(() => { + clock.restore(); + }); + + function missingQuorumError() { + // Match the prototype getters and nested error reported by the WASM SDK in CI. + return Object.create({ + name: 'DapiClientError', + message: 'no available addresses to retry, last error: Proof verification error: ' + + 'context provider error: invalid quorum: Quorum not found in cache for hash: ' + + '0b32385bfc1e0147b7dee87de5db332105dfd603fef6070bd9c6323aa2493a46', + }); + } + + const input = { serializedStateTransition: Uint8Array.from([1, 2, 3]), network: 'local' }; + + it('should verify again after the sidecar publishes a rotated quorum', async () => { + const waitForResponse = sinon.stub().callsFake(() => { + if (waitForResponse.callCount === 1) { + return Promise.reject(missingQuorumError()); + } + if (Date.now() < 60000) { + return Promise.reject(new Error('no available addresses')); + } + return Promise.resolve(); + }); + const waitForAffectedState = sinon.stub().resolves(); + const { verifier, stateTransition } = createVerifierWith({ + waitForResponse, waitForAffectedState, + }); + const result = verifier.verifyStateTransitionResult(input).then(() => null, (error) => error); + + await clock.tickAsync(65000); + + expect(await result).to.equal(null); + expect(clock.now).to.equal(65000); + expect(waitForResponse.callCount).to.equal(2); + expect(waitForResponse).to.have.always.been.calledWithExactly(stateTransition); + expect(waitForAffectedState).to.not.have.been.called; + }); + + it('should fail after the bounded wait if the quorum never becomes available', async () => { + const waitForResponse = sinon.stub().rejects(missingQuorumError()); + const waitForAffectedState = sinon.stub().resolves(); + const { verifier } = createVerifierWith({ waitForResponse, waitForAffectedState }); + const result = verifier.verifyStateTransitionResult(input).then(() => null, (error) => error); + + await clock.tickAsync(65000); + + expect(await result).to.be.an.instanceOf(Error) + .with.property('message').that.includes('Quorum not found in cache'); + expect(waitForResponse.callCount).to.equal(2); + expect(waitForAffectedState).to.not.have.been.called; + expect(clock.countTimers()).to.equal(0); + }); + + it('should verify the affected-state proof again if its quorum is not published yet', async () => { + const waitForResponse = sinon.stub().rejects(executionNotProvedError()); + const waitForAffectedState = sinon.stub().resolves(); + waitForAffectedState.onFirstCall().rejects(missingQuorumError()); + const { verifier } = createVerifierWith({ waitForResponse, waitForAffectedState }); + const result = verifier.verifyStateTransitionResult(input).then(() => null, (error) => error); + + await clock.tickAsync(65000); + + expect(await result).to.equal(null); + expect(waitForAffectedState).to.have.been.calledTwice; + }); + + it('should stop immediately if the refreshed quorum reveals an invalid signature', async () => { + const proofError = new Error('quorum signature is invalid'); + const waitForResponse = sinon.stub().rejects(proofError); + waitForResponse.onFirstCall().rejects(missingQuorumError()); + const waitForAffectedState = sinon.stub().resolves(); + const { verifier } = createVerifierWith({ waitForResponse, waitForAffectedState }); + const result = verifier.verifyStateTransitionResult(input).then(() => null, (error) => error); + + await clock.tickAsync(65000); + + expect(await result).to.equal(proofError); + expect(waitForResponse).to.have.been.calledTwice; + expect(waitForAffectedState).to.not.have.been.called; + expect(clock.countTimers()).to.equal(0); + }); + }); + it('should require an execution proof before accepting a state transition', async () => { const waitForResponse = sinon.stub().resolves(); const waitForAffectedState = sinon.stub().resolves(); From 069a5cab6a330452157b23a6c6307b12d9fadd6e Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Fri, 9 Oct 2026 11:20:50 +0000 Subject: [PATCH 17/22] fix(dpp): require WASM-compatible blst for external consumers Enforce blst >=0.3.17,<0.4 through the BLS feature so existing consumer lockfiles cannot retain incompatible 0.3.12. Remove the temporary constraint once the pinned rust-dashcore graph supplies the minimum. Verified fresh and existing-lock external DPP resolution, rejection of 0.3.12, and WASM clippy with state-transitions and bls-signatures. Co-Authored-By: Codex GPT-6 --- CHANGELOG.md | 2 ++ Cargo.lock | 1 + packages/rs-dpp/Cargo.toml | 7 +++++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a79c8de1d8d..355dcb0c6de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ### Fixed +- **dpp:** require a WASM-compatible `blst` version for BLS signatures, including + external consumers with older dependency lockfiles. - **platform-test-suite:** retry proof reads while the local quorum sidecar publishes a rotated key, with a bounded wait and full proof verification on each attempt. - **platform-wallet-ffi:** adapt platform node ID derivation to the updated diff --git a/Cargo.lock b/Cargo.lock index 4c39ea78998..bf6e0837047 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2353,6 +2353,7 @@ dependencies = [ "async-trait", "base64 0.22.1", "bech32 0.11.1", + "blst", "bs58", "byteorder", "chrono", diff --git a/packages/rs-dpp/Cargo.toml b/packages/rs-dpp/Cargo.toml index 3438a26ece9..bf8081e9518 100644 --- a/packages/rs-dpp/Cargo.toml +++ b/packages/rs-dpp/Cargo.toml @@ -13,6 +13,9 @@ authors = [ [dependencies] dash-pkc = { git = "https://github.com/dashpay/base-sdk", rev = "e6402ced257c370a586ade9840ebbf545a4b7926", default-features = false, features = ["bls"], optional = true } +# Keep downstream lockfiles from retaining blst versions without WASM aggregate verification. +# TODO: Remove this constraint once the pinned rust-dashcore dependency graph enforces a compatible blst minimum. +blst = { version = "0.3.17", default-features = false, optional = true } zeroize = { version = "1.9.0", optional = true } ed25519-dalek = { version = "=2.2.0", features = ["rand_core"], optional = true } anyhow = { version = "1.0.81" } @@ -104,7 +107,7 @@ getrandom_03 = { package = "getrandom", version = "0.3", features = ["wasm_js"] getrandom_04 = { package = "getrandom", version = "0.4", features = ["wasm_js"] } [package.metadata.cargo-machete] -ignored = ["getrandom_03", "getrandom_04"] +ignored = ["getrandom_03", "getrandom_04", "blst"] [features] default = ["state-transitions"] @@ -117,7 +120,7 @@ core_key_wallet_manager = ["dep:key-wallet-manager"] core_key_wallet_serde = ["dep:key-wallet", "key-wallet/serde"] core_spv = ["dep:dash-spv"] core_rpc_client = ["dep:dashcore-rpc"] -bls-signatures = ["dashcore/bls", "dep:dash-pkc", "dep:zeroize"] +bls-signatures = ["dashcore/bls", "dep:dash-pkc", "dep:blst", "dep:zeroize"] ed25519-dalek = ["dashcore/eddsa", "dep:ed25519-dalek"] all_features = [ "json-conversion", From cbb694d261f245e244fa4a596068a1b8cec55a6d Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Fri, 9 Oct 2026 12:40:04 +0000 Subject: [PATCH 18/22] test(platform): freeze historical storage and signing vectors Cover populated GroveDB state and auxiliary entries, Core transaction and lock hashes, Platform preimages and signatures, and encrypted DashPay fields inside a signed document batch. Generate expectations with the pre-upgrade Platform/rust-dashcore implementations and retain generators. Consolidate six quorum fixture files into constants without changing bytes. Validation: 62 targeted tests, scoped all-target/all-feature clippy with --no-deps -D warnings, and rustfmt. No production or storage format changes. Co-Authored-By: Codex GPT-6 --- CHANGELOG.md | 6 + .../fixtures/historical_hashes/README.md | 39 ++++ .../historical_hashes/core_transactions.rs | 58 ++++++ .../generate-core-transactions.rs | 140 +++++++++++++ .../fixtures/historical_hashes/generate.rs | 166 +++++++++++++++ .../fixtures/historical_hashes/vectors.rs | 95 +++++++++ .../tests/historical_hash_compatibility.rs | 150 ++++++++++++++ .../v0/fixtures/README.md | 35 +++- .../v0/fixtures/checkpoint-platform-state.hex | 1 - .../v0/fixtures/generate-populated-storage.rs | 195 ++++++++++++++++++ .../v0/fixtures/platform-state-v1.hex | 1 - .../v0/fixtures/platform-state-v2.hex | 1 - .../v0/fixtures/quorum-storage-v0.hex | 1 - .../v0/fixtures/quorum-storage-v1.hex | 1 - .../v0/fixtures/quorum-storage-v2.hex | 1 - .../v0/for_saving_v0.rs | 2 +- .../v0/mod.rs | 3 + .../v0/storage_tests.rs | 121 ++++++++++- .../v0/storage_vectors.rs | 185 +++++++++++++++++ .../generate-historical-encryption.rs | 44 ++++ .../tests/historical_compatibility.rs | 119 +++++++++++ 21 files changed, 1350 insertions(+), 14 deletions(-) create mode 100644 packages/rs-dpp/tests/fixtures/historical_hashes/README.md create mode 100644 packages/rs-dpp/tests/fixtures/historical_hashes/core_transactions.rs create mode 100644 packages/rs-dpp/tests/fixtures/historical_hashes/generate-core-transactions.rs create mode 100644 packages/rs-dpp/tests/fixtures/historical_hashes/generate.rs create mode 100644 packages/rs-dpp/tests/fixtures/historical_hashes/vectors.rs create mode 100644 packages/rs-dpp/tests/historical_hash_compatibility.rs delete mode 100644 packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/checkpoint-platform-state.hex create mode 100644 packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/generate-populated-storage.rs delete mode 100644 packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/platform-state-v1.hex delete mode 100644 packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/platform-state-v2.hex delete mode 100644 packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v0.hex delete mode 100644 packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v1.hex delete mode 100644 packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v2.hex create mode 100644 packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/storage_vectors.rs create mode 100644 packages/rs-platform-encryption/tests/fixtures/generate-historical-encryption.rs create mode 100644 packages/rs-platform-encryption/tests/historical_compatibility.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 355dcb0c6de..75b0ec0216f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ ## [Unreleased] +### Added + +- **Rust compatibility tests:** frozen pre-upgrade vectors for populated GroveDB + state, Core transaction and lock hashes, Platform signatures and encrypted + DashPay fields, stored as constants. + ### Fixed - **dpp:** require a WASM-compatible `blst` version for BLS signatures, including diff --git a/packages/rs-dpp/tests/fixtures/historical_hashes/README.md b/packages/rs-dpp/tests/fixtures/historical_hashes/README.md new file mode 100644 index 00000000000..e0d909c43d4 --- /dev/null +++ b/packages/rs-dpp/tests/fixtures/historical_hashes/README.md @@ -0,0 +1,39 @@ +# Historical hash and signature vectors + +The constants freeze outputs of Platform +`6499c680c6bc311e8933f396423a79459e2a88bd` (the base before #5307): rust-dashcore +`40268cc0402a8933ec539f16b2d634c4e25876ad`, secp256k1 0.30.0 and blsful +`0c34a7a488a0bd1c9a9a2196e793b303ad35c900`. + +`core_transactions.rs` holds eight consensus-encoded transactions and their txids: +regular/Evo registration, registrar/service/revocation updates, and coinbase +payload versions 1, 2 and 3. They are synthetic encoding examples; their byte-pattern +BLS fields are not valid signatures. Copy `generate-core-transactions.rs` to the +old DPP examples directory as `generate_core_transactions.rs`, then run: + +```sh +cargo run -p dpp --example generate_core_transactions --locked -- /path/to/output +``` + +`vectors.rs` holds actual signing inputs, hashes and signatures: + +- a two-input address transfer, including ECDSA and Basic BLS signatures; +- InstantLock and ChainLock consensus bytes, request IDs and sign IDs; +- a synthetic DashPay contactRequest batch containing historical ciphertexts. + +For reproduction, first run +`packages/rs-platform-encryption/tests/fixtures/generate-historical-encryption.rs` +as an example in the old encryption crate. This emits `encrypted-xpub.bin` and +`encrypted-label.bin`. Copy `generate.rs` here to the old DPP examples directory +as `generate_historical_hashes.rs`, then run it with the same output directory: + +```sh +cargo run -p dpp --example generate_historical_hashes --locked -- /path/to/output +``` + +Compare the hex outputs with the constants; do not refresh expectations using +the current dependencies. Generators live below `tests/fixtures` so the current +build does not compile their historical APIs. All keys and scalars are public +test material. Ciphertext construction is covered separately by +`platform-encryption/tests/historical_compatibility.rs`; the DPP test covers its +inclusion in the signed batch, not contract validation or encryption policy. diff --git a/packages/rs-dpp/tests/fixtures/historical_hashes/core_transactions.rs b/packages/rs-dpp/tests/fixtures/historical_hashes/core_transactions.rs new file mode 100644 index 00000000000..70346044c71 --- /dev/null +++ b/packages/rs-dpp/tests/fixtures/historical_hashes/core_transactions.rs @@ -0,0 +1,58 @@ +//! Consensus bytes and txids generated with rust-dashcore +//! 40268cc0402a8933ec539f16b2d634c4e25876ad (Platform's pre-upgrade pin). +//! Synthetic public payloads: this checks hashing/encoding, not consensus validity. + +pub const TRANSACTIONS: &[(&str, &str, &str)] = &[ + ("registration-evo", concat!( + "0300010000010001d20400000000000001512a000000fd12010200010000000101010101010101010101010101010101", + "0101010101010101010101010101010300000000000000000000000000ffff7f0000014e1f0202020202020202020202", + "020202020202020202000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223242526", + "2728292a2b2c2d2e2f030303030303030303030303030303030303030364000151040404040404040404040404040404", + "040404040404040404040404040404040414131211100f0e0d0c0b0a0908070605040302012068bb0141050505050505", + "050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505", + "0505050505050505050505", + ), "a30b4ff60dd0d633f4de56c3a1d8ee6fa875c2bba59a4cf192db5e8917e4e549"), + ("registration-regular", concat!( + "0300010000010001d20400000000000001512a000000fa02000000000001010101010101010101010101010101010101", + "010101010101010101010101010300000000000000000000000000ffff7f0000014e1f02020202020202020202020202", + "02020202020202000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728", + "292a2b2c2d2e2f0303030303030303030303030303030303030303640001510404040404040404040404040404040404", + "040404040404040404040404040404410505050505050505050505050505050505050505050505050505050505050505", + "050505050505050505050505050505050505050505050505050505050505050505", + ), "d1698b27545732719b89868a83e86a310c26f97fee2e8de682de493e0e9c90dc"), + ("registrar", concat!( + "0300030000010001d20400000000000001512a000000cc02000101010101010101010101010101010101010101010101", + "0101010101010101010000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324", + "25262728292a2b2c2d2e2f03030303030303030303030303030303030303030151040404040404040404040404040404", + "040404040404040404040404040404040441050505050505050505050505050505050505050505050505050505050505", + "0505050505050505050505050505050505050505050505050505050505050505050505", + ), "63162c82b3b22c52104f886f91e1f2b932a6ac0e464ccffc5904ed4488881e25"), + ("service-evo", concat!( + "0300020000010001d20400000000000001512a000000d002000100010101010101010101010101010101010101010101", + "0101010101010101010101010000000000000000000000000000004e1f01510404040404040404040404040404040404", + "04040404040404040404040404040414131211100f0e0d0c0b0a0908070605040302012068bb01060606060606060606", + "060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606", + "060606060606060606060606060606060606060606060606060606060606060606060606060606", + ), "466792522ea10d3ff80bb54dad7287987ec9da655df69221ff9e394b46f7cab2"), + ("revocation", concat!( + "0300040000010001d20400000000000001512a000000a402000101010101010101010101010101010101010101010101", + "010101010101010101010004040404040404040404040404040404040404040404040404040404040404040606060606", + "060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606", + "06060606060606060606060606060606060606060606060606060606060606060606060606060606060606", + ), "afe38deff52be3642bf85cdd803495d40add0910c2042e055cbd6742249ba180"), + ("coinbase-v1", concat!( + "030005000001d20400000000000001512a0000002601007b000000080808080808080808080808080808080808080808", + "0808080808080808080808", + ), "aec1210e737889ac3bd1c2a8c69416fb94b844710867ea992386d17a0f7b3e6f"), + ("coinbase-v2", concat!( + "030005000001d20400000000000001512a0000004602007b000000080808080808080808080808080808080808080808", + "08080808080808080808080909090909090909090909090909090909090909090909090909090909090909", + ), "085c1e04b82ee7f1aaae1710153c27abc887a747a2a0c2a1962cee3b109bd534"), + ("coinbase-v3", concat!( + "030005000001d20400000000000001512a000000af03007b000000080808080808080808080808080808080808080808", + "080808080808080808080809090909090909090909090909090909090909090909090909090909090909096406060606", + "060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606", + "060606060606060606060606060606060606060606060606060606060606060606060606060606060606060688130000", + "00000000", + ), "b6ea50bbf7a4309887b5d13130bae1d74fe3485dcbd13392882f3a1d82979528"), +]; diff --git a/packages/rs-dpp/tests/fixtures/historical_hashes/generate-core-transactions.rs b/packages/rs-dpp/tests/fixtures/historical_hashes/generate-core-transactions.rs new file mode 100644 index 00000000000..5c69c6e0b5d --- /dev/null +++ b/packages/rs-dpp/tests/fixtures/historical_hashes/generate-core-transactions.rs @@ -0,0 +1,140 @@ +//! Run in a standalone package with dashcore at rev +//! 40268cc0402a8933ec539f16b2d634c4e25876ad (feature serde). +//! `cargo run --locked -- /path/to/output`; copy hex-encoded consensus bytes +//! and txids into core_transactions.rs. Never refresh with the upgraded pin. +//! Run against rust-dashcore 40268cc0402a8933ec539f16b2d634c4e25876ad. +use dashcore::bls_sig_utils::{BLSPublicKey, BLSSignature}; +use dashcore::hashes::Hash; +use dashcore::transaction::special_transaction::{ + provider_registration::{ProviderMasternodeType, ProviderRegistrationPayload}, + provider_update_registrar::ProviderUpdateRegistrarPayload, + provider_update_revocation::ProviderUpdateRevocationPayload, + provider_update_service::ProviderUpdateServicePayload, + TransactionPayload, +}; +use dashcore::{OutPoint, PlatformNodeId, PubkeyHash, ScriptBuf, Transaction, TxOut, Txid}; + +fn main() { + let output = std::path::PathBuf::from(std::env::args().nth(1).expect("fixture directory")); + let key = BLSPublicKey::from(std::array::from_fn(|i| i as u8)); + let node = PlatformNodeId::from_byte_array(std::array::from_fn(|i| i as u8 + 1)); + let registration = ProviderRegistrationPayload { + version: 2, + masternode_type: ProviderMasternodeType::HighPerformance, + masternode_mode: 0, + collateral_outpoint: OutPoint { + txid: Txid::from_byte_array([1; 32]), + vout: 3, + }, + service_address: "127.0.0.1:19999".parse().unwrap(), + owner_key_hash: PubkeyHash::from_byte_array([2; 20]), + operator_public_key: key, + voting_key_hash: PubkeyHash::from_byte_array([3; 20]), + operator_reward: 100, + script_payout: ScriptBuf::from(vec![0x51]), + inputs_hash: dashcore::hash_types::InputsHash::from_byte_array([4; 32]), + signature: vec![5; 65], + platform_node_id: Some(node), + platform_p2p_port: Some(26656), + platform_http_port: Some(443), + }; + let mut regular = registration.clone(); + regular.masternode_type = ProviderMasternodeType::Regular; + regular.platform_node_id = None; + regular.platform_p2p_port = None; + regular.platform_http_port = None; + let mut records = vec![ + ( + "registration-evo", + TransactionPayload::ProviderRegistrationPayloadType(registration), + ), + ( + "registration-regular", + TransactionPayload::ProviderRegistrationPayloadType(regular), + ), + ( + "registrar", + TransactionPayload::ProviderUpdateRegistrarPayloadType( + ProviderUpdateRegistrarPayload { + version: 2, + pro_tx_hash: Txid::from_byte_array([1; 32]), + provider_mode: 0, + operator_public_key: key, + voting_key_hash: PubkeyHash::from_byte_array([3; 20]), + script_payout: ScriptBuf::from(vec![0x51]), + inputs_hash: dashcore::hash_types::InputsHash::from_byte_array([4; 32]), + payload_sig: vec![5; 65], + }, + ), + ), + ( + "service-evo", + TransactionPayload::ProviderUpdateServicePayloadType(ProviderUpdateServicePayload { + version: 2, + mn_type: Some(1), + pro_tx_hash: Txid::from_byte_array([1; 32]), + ip_address: 1, + port: 19999, + script_payout: ScriptBuf::from(vec![0x51]), + inputs_hash: dashcore::hash_types::InputsHash::from_byte_array([4; 32]), + platform_node_id: Some(node), + platform_p2p_port: Some(26656), + platform_http_port: Some(443), + payload_sig: BLSSignature::from([6; 96]), + }), + ), + ( + "revocation", + TransactionPayload::ProviderUpdateRevocationPayloadType( + ProviderUpdateRevocationPayload { + version: 2, + pro_tx_hash: Txid::from_byte_array([1; 32]), + reason: 1, + inputs_hash: dashcore::hash_types::InputsHash::from_byte_array([4; 32]), + payload_sig: BLSSignature::from([6; 96]), + }, + ), + ), + ]; + for (name, version) in [("coinbase-v1", 1), ("coinbase-v2", 2), ("coinbase-v3", 3)] { + records.push(( + name, + TransactionPayload::CoinbasePayloadType( + dashcore::transaction::special_transaction::coinbase::CoinbasePayload { + version, + height: 123, + merkle_root_masternode_list: + dashcore::hash_types::MerkleRootMasternodeList::from_byte_array([8; 32]), + merkle_root_quorums: dashcore::hash_types::MerkleRootQuorums::from_byte_array( + [9; 32], + ), + best_cl_height: (version >= 3).then_some(100), + best_cl_signature: (version >= 3).then_some(BLSSignature::from([6; 96])), + asset_locked_amount: (version >= 3).then_some(5000), + }, + ), + )); + } + for (name, payload) in records { + let transaction = Transaction { + version: 3, + lock_time: 42, + input: vec![], + output: vec![TxOut { + value: 1234, + script_pubkey: ScriptBuf::from(vec![0x51]), + }], + special_transaction_payload: Some(payload), + }; + std::fs::write( + output.join(format!("{name}.consensus")), + dashcore::consensus::serialize(&transaction), + ) + .unwrap(); + std::fs::write( + output.join(format!("{name}.txid")), + transaction.txid().to_string(), + ) + .unwrap(); + } +} diff --git a/packages/rs-dpp/tests/fixtures/historical_hashes/generate.rs b/packages/rs-dpp/tests/fixtures/historical_hashes/generate.rs new file mode 100644 index 00000000000..b01ee905a41 --- /dev/null +++ b/packages/rs-dpp/tests/fixtures/historical_hashes/generate.rs @@ -0,0 +1,166 @@ +//! Run as a DPP example on Platform 6499c680c6bc311e8933f396423a79459e2a88bd. +//! `cargo run -p dpp --example generate_historical_hashes --locked -- /path/to/output` +//! The output directory must contain encrypted-xpub.bin and encrypted-label.bin +//! from platform-encryption's historical generator, run at the same revision. + +use dpp::address_funds::PlatformAddress; +use dpp::dashcore::bls_sig_utils::BLSSignature; +use dpp::dashcore::ephemerealdata::chain_lock::ChainLock; +use dpp::dashcore::hash_types::CycleHash; +use dpp::dashcore::hashes::Hash; +use dpp::dashcore::secp256k1::{PublicKey, Secp256k1, SecretKey}; +use dpp::dashcore::sml::llmq_type::LLMQType; +use dpp::dashcore::{BlockHash, InstantLock, OutPoint, QuorumHash, Txid}; +use dpp::identity::KeyType; +use dpp::native_bls::NativeBlsModule; +use dpp::platform_value::{Identifier, Value}; +use dpp::serialization::{PlatformMessageSignable, PlatformSerializable, Signable}; +use dpp::state_transition::address_funds_transfer_transition::v0::AddressFundsTransferTransitionV0; +use dpp::state_transition::batch_transition::batched_transition::document_transition::DocumentTransition; +use dpp::state_transition::batch_transition::document_base_transition::v0::DocumentBaseTransitionV0; +use dpp::state_transition::batch_transition::document_create_transition::{ + v0::DocumentCreateTransitionV0, DocumentCreateTransition, +}; +use dpp::state_transition::batch_transition::{BatchTransition, BatchTransitionV0}; +use dpp::state_transition::StateTransition; +use dpp::util::hash::hash_double; +use dpp::BlsModule; +use std::collections::BTreeMap; +use std::path::Path; + +fn main() { + let dir = std::env::args().nth(1).expect("output directory"); + let dir = Path::new(&dir); + let write = |name: &str, bytes: &[u8]| { + std::fs::write(dir.join(format!("{name}.hex")), hex::encode(bytes)).unwrap(); + }; + let transition: StateTransition = AddressFundsTransferTransitionV0 { + inputs: BTreeMap::from([ + (PlatformAddress::P2pkh([0x11; 20]), (1000, 5000)), + (PlatformAddress::P2sh([0x22; 20]), (251, 7000)), + ]), + outputs: BTreeMap::from([(PlatformAddress::P2pkh([0x33; 20]), 11000)]), + user_fee_increase: 2, + ..Default::default() + } + .into(); + let bytes = transition.signable_bytes().unwrap(); + write("transfer-signable", &bytes); + write("transfer-hash", &hash_double(&bytes)); + write( + "transfer-serialized", + &transition.serialize_to_bytes().unwrap(), + ); + let secret = [1; 32]; + let ecdsa = bytes + .as_slice() + .sign_by_private_key(&secret, KeyType::ECDSA_SECP256K1, &NativeBlsModule) + .unwrap(); + let bls = bytes + .as_slice() + .sign_by_private_key(&secret, KeyType::BLS12_381, &NativeBlsModule) + .unwrap(); + write("transfer-ecdsa-signature", &ecdsa); + write("transfer-bls-signature", &bls); + write( + "ecdsa-public-key", + &PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&secret).unwrap()) + .serialize(), + ); + write( + "bls-public-key", + &NativeBlsModule.private_key_to_public_key(&secret).unwrap(), + ); + + let encrypted_key = std::fs::read(dir.join("encrypted-xpub.bin")).unwrap(); + let encrypted_label = std::fs::read(dir.join("encrypted-label.bin")).unwrap(); + let create = DocumentCreateTransitionV0 { + base: DocumentBaseTransitionV0 { + id: Identifier::from([0x44; 32]), + identity_contract_nonce: 1, + document_type_name: "contactRequest".into(), + data_contract_id: Identifier::from([0x55; 32]), + } + .into(), + entropy: [0x66; 32], + data: BTreeMap::from([ + ("toUserId".into(), Value::Bytes(vec![0x77; 32])), + ("senderKeyIndex".into(), Value::U32(0)), + ("recipientKeyIndex".into(), Value::U32(1)), + ("accountReference".into(), Value::U32(2)), + ("encryptedPublicKey".into(), Value::Bytes(encrypted_key)), + ( + "encryptedAccountLabel".into(), + Value::Bytes(encrypted_label), + ), + ]), + ..Default::default() + }; + let batch = StateTransition::Batch(BatchTransition::V0(BatchTransitionV0 { + owner_id: Identifier::from([0x33; 32]), + transitions: vec![DocumentTransition::Create(DocumentCreateTransition::V0( + create, + ))], + ..Default::default() + })); + let signable = batch.signable_bytes().unwrap(); + write("dashpay-serialized", &batch.serialize_to_bytes().unwrap()); + write("dashpay-signable", &signable); + write("dashpay-hash", &hash_double(&signable)); + write( + "dashpay-signature", + &signable + .as_slice() + .sign_by_private_key(&secret, KeyType::ECDSA_SECP256K1, &NativeBlsModule) + .unwrap(), + ); + + let signature = BLSSignature::from(std::array::from_fn(|i| i as u8)); + let instant = InstantLock { + version: 1, + inputs: vec![OutPoint { + txid: Txid::from_byte_array(std::array::from_fn(|i| i as u8)), + vout: 1000, + }], + txid: Txid::from_byte_array([0x77; 32]), + cyclehash: CycleHash::from_byte_array([0x88; 32]), + signature, + }; + let chain = ChainLock { + block_height: 1000, + block_hash: BlockHash::from_byte_array([0x99; 32]), + signature, + }; + let quorum_type = LLMQType::from(100u8); + let quorum_hash = QuorumHash::from_byte_array([0x55; 32]); + write( + "instant-lock-consensus", + &dpp::dashcore::consensus::serialize(&instant), + ); + write( + "instant-lock-request-id", + instant.request_id().unwrap().as_byte_array(), + ); + write( + "instant-lock-sign-id", + instant + .sign_id(quorum_type, quorum_hash, None) + .unwrap() + .as_byte_array(), + ); + write( + "chain-lock-consensus", + &dpp::dashcore::consensus::serialize(&chain), + ); + write( + "chain-lock-request-id", + chain.request_id().unwrap().as_byte_array(), + ); + write( + "chain-lock-sign-id", + chain + .sign_id(quorum_type, quorum_hash, None) + .unwrap() + .as_byte_array(), + ); +} diff --git a/packages/rs-dpp/tests/fixtures/historical_hashes/vectors.rs b/packages/rs-dpp/tests/fixtures/historical_hashes/vectors.rs new file mode 100644 index 00000000000..cdb780a64eb --- /dev/null +++ b/packages/rs-dpp/tests/fixtures/historical_hashes/vectors.rs @@ -0,0 +1,95 @@ +//! Generated by generate.rs at Platform 6499c680c6bc311e8933f396423a79459e2a88bd. +//! rust-dashcore 40268cc, blsful 0c34a7a, secp256k1 0.30; all inputs are public test data. + +pub const TRANSFER_SIGNABLE: &str = concat!( + "0c0002001111111111111111111111111111111111111111fb03e8fb1388012222222222222222222222222222222222", + "222222fb00fbfb1b5801003333333333333333333333333333333333333333fb2af80002", +); + +pub const TRANSFER_HASH: &str = "8c083e16f8455d123bbce8b166ca5d6ea77bb12f5f2c362ceadea4248b06a692"; + +pub const TRANSFER_SERIALIZED: &str = concat!( + "0c0002001111111111111111111111111111111111111111fb03e8fb1388012222222222222222222222222222222222", + "222222fb00fbfb1b5801003333333333333333333333333333333333333333fb2af8000200", +); + +pub const TRANSFER_ECDSA_SIGNATURE: &str = concat!( + "1f70b658f25806e339d2dfe598de1a272fdb26d3e7372e531e5bd9cfff86018e6e16ad6cc028b7b56bcaa6cd84077163", + "fc58b134a16a67ed474c62e622b5637f36", +); + +pub const TRANSFER_BLS_SIGNATURE: &str = concat!( + "835710b6206bbd5b32e69f5d69c6254f0172779c906dcbe009bbcbba3fba48410b8eaae6645c1c5407449d1084688ac8", + "15cb01a8cb6cc2d73e222134799af6ae5ce1dbfce5de1148c9010a11bc57d970c48b58aacad1c47e4e8b237dbc0e6c89", +); + +pub const ECDSA_PUBLIC_KEY: &str = + "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"; + +pub const BLS_PUBLIC_KEY: &str = "aa1a1c26055a329817a5759d877a2795f9499b97d6056edde0eea39512f24e8bc874b4471f0501127abb1ea0d9f68ac1"; + +pub const INSTANT_LOCK_CONSENSUS: &str = concat!( + "0101000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1fe803000077777777777777777777", + "777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888", + "888888888888000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223242526272829", + "2a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f50515253545556575859", + "5a5b5c5d5e5f", +); + +pub const INSTANT_LOCK_REQUEST_ID: &str = + "b8d23d4179eff7049abd9528d5aad75a4d020c4f6e608e1a3b3b22206c8194ad"; + +pub const INSTANT_LOCK_SIGN_ID: &str = + "a44b4447fe4a448bf5dad2a372c04df444e2364f30cc2d790b1da56a126c920d"; + +pub const CHAIN_LOCK_CONSENSUS: &str = concat!( + "e80300009999999999999999999999999999999999999999999999999999999999999999000102030405060708090a0b", + "0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b", + "3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f", +); + +pub const CHAIN_LOCK_REQUEST_ID: &str = + "04021b2a9abbf4baa053efdcad0cc345710d5911f2d87b7e9f2bf316a7898c03"; + +pub const CHAIN_LOCK_SIGN_ID: &str = + "3b15938d1073723af4d090722da31ee3ed9cc744db11179c10cfd09acd151e46"; + +pub const DASHPAY_SERIALIZED: &str = concat!( + "020033333333333333333333333333333333333333333333333333333333333333330100000044444444444444444444", + "44444444444444444444444444444444444444444444010e636f6e746163745265717565737455555555555555555555", + "555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666", + "66666666666606106163636f756e745265666572656e6365040215656e637279707465644163636f756e744c6162656c", + "0a305a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a758680ffe5ebc197cf98b7ea57529729cea6da4cc569079a85c1a7e7a803", + "e06212656e637279707465645075626c69634b65790a605a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a864d1b3807cf80fd27", + "df6cac063a5128fd6119d0d40491a7788cb4e1975bc47e5070c7919e3d8c21ab26be1a763e7908b97cd32a5e36309f3b", + "f9535c519b1b32b2f206696ec6d0e244a2e182fceaa75011726563697069656e744b6579496e64657804010e73656e64", + "65724b6579496e646578040008746f5573657249640a2077777777777777777777777777777777777777777777777777", + "7777777777777700000000", +); + +pub const DASHPAY_SIGNABLE: &str = concat!( + "020033333333333333333333333333333333333333333333333333333333333333330100000044444444444444444444", + "44444444444444444444444444444444444444444444010e636f6e746163745265717565737455555555555555555555", + "555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666", + "66666666666606106163636f756e745265666572656e6365040215656e637279707465644163636f756e744c6162656c", + "0a305a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a758680ffe5ebc197cf98b7ea57529729cea6da4cc569079a85c1a7e7a803", + "e06212656e637279707465645075626c69634b65790a605a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a864d1b3807cf80fd27", + "df6cac063a5128fd6119d0d40491a7788cb4e1975bc47e5070c7919e3d8c21ab26be1a763e7908b97cd32a5e36309f3b", + "f9535c519b1b32b2f206696ec6d0e244a2e182fceaa75011726563697069656e744b6579496e64657804010e73656e64", + "65724b6579496e646578040008746f5573657249640a2077777777777777777777777777777777777777777777777777", + "777777777777770000", +); + +pub const DASHPAY_HASH: &str = "eefce9b0eb85a4ac36ffc04008e0a5dc0b659f2bb306e2d54b24579acdb07cac"; + +pub const DASHPAY_SIGNATURE: &str = concat!( + "1f8de7c406b91a6ddbcc26d04b3be9a1c4593b30935119a928c8578d1739757c8167817b3e32c14072e1c3d62c9fd124", + "b78802d53d46f38b7599c92f80dabbeac2", +); + +pub const ENCRYPTED_XPUB: &str = concat!( + "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a864d1b3807cf80fd27df6cac063a5128fd6119d0d40491a7788cb4e1975bc47e", + "5070c7919e3d8c21ab26be1a763e7908b97cd32a5e36309f3bf9535c519b1b32b2f206696ec6d0e244a2e182fceaa750", +); + +pub const ENCRYPTED_LABEL: &str = "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a758680ffe5ebc197cf98b7ea57529729cea6da4cc569079a85c1a7e7a803e062"; diff --git a/packages/rs-dpp/tests/historical_hash_compatibility.rs b/packages/rs-dpp/tests/historical_hash_compatibility.rs new file mode 100644 index 00000000000..e983be0ab01 --- /dev/null +++ b/packages/rs-dpp/tests/historical_hash_compatibility.rs @@ -0,0 +1,150 @@ +#[path = "fixtures/historical_hashes/core_transactions.rs"] +mod core_transactions; +#[path = "fixtures/historical_hashes/vectors.rs"] +mod vectors; + +use dpp::dashcore::consensus::{deserialize, serialize}; +use dpp::dashcore::ephemerealdata::chain_lock::ChainLock; +use dpp::dashcore::hashes::Hash; +use dpp::dashcore::sml::llmq_type::LLMQType; +use dpp::dashcore::{InstantLock, QuorumHash, Transaction}; +use dpp::identity::KeyType; +use dpp::native_bls::NativeBlsModule; +use dpp::platform_value::Value; +use dpp::serialization::{ + PlatformDeserializableUntrusted, PlatformMessageSignable, PlatformSerializable, Signable, +}; +use dpp::state_transition::batch_transition::batched_transition::document_transition::DocumentTransition; +use dpp::state_transition::batch_transition::document_create_transition::DocumentCreateTransition; +use dpp::state_transition::batch_transition::BatchTransition; +use dpp::state_transition::StateTransition; +use dpp::util::hash::hash_double; +use vectors::*; + +#[test] +fn should_preserve_historical_core_transaction_bytes_and_txids() { + for (name, consensus, txid) in core_transactions::TRANSACTIONS { + let bytes = hex::decode(consensus).unwrap(); + let transaction: Transaction = deserialize(&bytes).unwrap(); + assert_eq!(serialize(&transaction), bytes, "{name}"); + assert_eq!(transaction.txid().to_string(), *txid, "{name}"); + } +} + +#[test] +fn should_preserve_historical_lock_request_and_sign_ids() { + let instant_bytes = hex::decode(INSTANT_LOCK_CONSENSUS).unwrap(); + let chain_bytes = hex::decode(CHAIN_LOCK_CONSENSUS).unwrap(); + let instant: InstantLock = deserialize(&instant_bytes).unwrap(); + let chain: ChainLock = deserialize(&chain_bytes).unwrap(); + let quorum_type = LLMQType::from(100u8); + let quorum_hash = QuorumHash::from_byte_array([0x55; 32]); + assert_eq!(serialize(&instant), instant_bytes); + assert_eq!(serialize(&chain), chain_bytes); + assert_eq!( + hex::encode(instant.request_id().unwrap()), + INSTANT_LOCK_REQUEST_ID + ); + assert_eq!( + hex::encode(chain.request_id().unwrap()), + CHAIN_LOCK_REQUEST_ID + ); + assert_eq!( + hex::encode(instant.sign_id(quorum_type, quorum_hash, None).unwrap()), + INSTANT_LOCK_SIGN_ID + ); + assert_eq!( + hex::encode(chain.sign_id(quorum_type, quorum_hash, None).unwrap()), + CHAIN_LOCK_SIGN_ID + ); +} + +#[test] +fn should_preserve_historical_transfer_preimage_hash_and_signatures() { + let serialized = hex::decode(TRANSFER_SERIALIZED).unwrap(); + let transition = StateTransition::deserialize_from_bytes_untrusted_exact(&serialized).unwrap(); + assert_eq!(transition.serialize_to_bytes().unwrap(), serialized); + let bytes = transition.signable_bytes().unwrap(); + assert_eq!(hex::encode(&bytes), TRANSFER_SIGNABLE); + assert_eq!(hex::encode(hash_double(&bytes)), TRANSFER_HASH); + for (key_type, public, signature) in [ + ( + KeyType::ECDSA_SECP256K1, + ECDSA_PUBLIC_KEY, + TRANSFER_ECDSA_SIGNATURE, + ), + (KeyType::BLS12_381, BLS_PUBLIC_KEY, TRANSFER_BLS_SIGNATURE), + ] { + let public = hex::decode(public).unwrap(); + let signature = hex::decode(signature).unwrap(); + assert_eq!( + bytes + .as_slice() + .sign_by_private_key(&[1; 32], key_type, &NativeBlsModule) + .unwrap(), + signature + ); + assert!(bytes + .as_slice() + .verify_signature(key_type, &public, &signature) + .is_valid()); + let mut tampered = bytes.clone(); + tampered[0] ^= 1; + assert!(!tampered + .as_slice() + .verify_signature(key_type, &public, &signature) + .is_valid()); + } +} + +#[test] +fn should_preserve_historical_dashpay_ciphertexts_in_the_signed_batch() { + let serialized = hex::decode(DASHPAY_SERIALIZED).unwrap(); + let mut transition = + StateTransition::deserialize_from_bytes_untrusted_exact(&serialized).unwrap(); + assert_eq!(transition.serialize_to_bytes().unwrap(), serialized); + let bytes = transition.signable_bytes().unwrap(); + assert_eq!(hex::encode(&bytes), DASHPAY_SIGNABLE); + assert_eq!(hex::encode(hash_double(&bytes)), DASHPAY_HASH); + let signature = hex::decode(DASHPAY_SIGNATURE).unwrap(); + let public = hex::decode(ECDSA_PUBLIC_KEY).unwrap(); + assert_eq!( + bytes + .as_slice() + .sign_by_private_key(&[1; 32], KeyType::ECDSA_SECP256K1, &NativeBlsModule) + .unwrap(), + signature + ); + assert!(bytes + .as_slice() + .verify_signature(KeyType::ECDSA_SECP256K1, &public, &signature) + .is_valid()); + + let StateTransition::Batch(BatchTransition::V0(batch)) = &mut transition else { + panic!("expected a historical document batch"); + }; + let DocumentTransition::Create(DocumentCreateTransition::V0(create)) = + &mut batch.transitions[0] + else { + panic!("expected a historical contactRequest create"); + }; + for (name, frozen) in [ + ("encryptedPublicKey", ENCRYPTED_XPUB), + ("encryptedAccountLabel", ENCRYPTED_LABEL), + ] { + assert_eq!( + create.data.get(name), + Some(&Value::Bytes(hex::decode(frozen).unwrap())) + ); + } + let Value::Bytes(ciphertext) = create.data.get_mut("encryptedPublicKey").unwrap() else { + panic!("expected binary ciphertext"); + }; + ciphertext[16] ^= 1; + let tampered = transition.signable_bytes().unwrap(); + assert_ne!(tampered, bytes); + assert!(!tampered + .as_slice() + .verify_signature(KeyType::ECDSA_SECP256K1, &public, &signature) + .is_valid()); +} diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/README.md b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/README.md index 7c2d5062e5b..2103cd414e6 100644 --- a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/README.md +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/README.md @@ -1,4 +1,10 @@ -# Historical V0 quorum storage +# Historical quorum storage constants + +The frozen bytes live in `../storage_vectors.rs` as hex constants. The filenames +below identify generator outputs; no separate hex files are needed by the tests. +Checksums describe those original outputs, including their final newline. + +## Historical V0 quorum storage `quorum-storage-v0.hex` contains a synthetic, complete `SignatureVerificationQuorumSetForSaving::V0` encoded by Platform commit @@ -68,3 +74,30 @@ Checksums below include the final newline in each hex file. | `platform-state-v1.hex` | 734 | `e2b83244feff85195d45fe4092ea85d298037f53cecdddd55c0eb7a5dda27ad7` | | `checkpoint-platform-state.hex` | 734 | `e2b83244feff85195d45fe4092ea85d298037f53cecdddd55c0eb7a5dda27ad7` | | `platform-state-v2.hex` | 732 | `d5eb9be1fac002863a22f1cfec3bb770ee9f67dde44ed3cba4734b18135f9616` | + +## Populated GroveDB records + +`generate-populated-storage.rs` runs on Platform +`6499c680c6bc311e8933f396423a79459e2a88bd`, before #5307, with rust-dashcore +`40268cc0402a8933ec539f16b2d634c4e25876ad` and the same historical blsful revision. +Copy it to that checkout's `packages/rs-drive-abci/examples/generate_populated_storage.rs`: + +```sh +cargo run -p drive-abci --example generate_populated_storage --all-features --locked -- /path/to/output +``` + +The corresponding `storage_vectors.rs` constants add: + +- `MASTERNODE_REGULAR` (241 bytes) and `MASTERNODE_EVO` (279 bytes): actual + per-entry writers, including IPv4/IPv6, operator keys and a non-palindromic node ID. +- `VALIDATOR_SET_ENTRY` (243 bytes): a versioned set with one member and distinct + member/threshold BLS keys. +- `POPULATED_PLATFORM_STATE_V1` (1904 bytes) and `POPULATED_PLATFORM_STATE_V2` + (764 bytes): two masternodes, one validator set and both current/previous quorum lists. +- `POPULATED_CHECKPOINT_PLATFORM_STATE`: the standalone checkpoint writer's output, + verified identical to V1 and represented by a constant alias. + +`should_preserve_historical_grovedb_masternode_and_validator_entries` exercises +the production entry codecs. `should_restore_historical_populated_state_and_checkpoint` +rebuilds V2 from its external entries and checks that its standalone checkpoint +still matches the historical bytes. These are synthetic records, not live-chain snapshots. diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/checkpoint-platform-state.hex b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/checkpoint-platform-state.hex deleted file mode 100644 index a564ec38c38..00000000000 --- a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/checkpoint-platform-state.hex +++ /dev/null @@ -1 +0,0 @@ -0100000d0e0000000000000000000000000000000000000000000000000000000000000000000002010400fb012002111111111111111111111111111111111111111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb002222222222222222222222222222222222222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e01000102333333333333333333333333333333333333333333333333333333333333333395fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe4801014444444444444444444444444444444444444444444444444444444444444444b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb038402010400fb012002111111111111111111111111111111111111111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb002222222222222222222222222222222222222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e01000102333333333333333333333333333333333333333333333333333333333333333395fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe4801014444444444444444444444444444444444444444444444444444444444444444b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb0384000000 diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/generate-populated-storage.rs b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/generate-populated-storage.rs new file mode 100644 index 00000000000..9d0b54b5bb6 --- /dev/null +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/generate-populated-storage.rs @@ -0,0 +1,195 @@ +//! Run as a drive-abci example on Platform 6499c680c6bc311e8933f396423a79459e2a88bd. +//! `cargo run -p drive-abci --example generate_populated_storage --all-features --locked -- /path/to/output` + +use dpp::core_types::validator::v0::ValidatorV0; +use dpp::core_types::validator_set::{v0::ValidatorSetV0, ValidatorSet}; +use dpp::dashcore::hashes::Hash; +use dpp::dashcore::QuorumHash; +use dpp::dashcore::{ProTxHash, PubkeyHash, Txid}; +use dpp::dashcore_rpc::json::QuorumType; +use dpp::dashcore_rpc::json::{MasternodeListItem, MasternodeType}; +use dpp::serialization::PlatformSerializable; +use dpp::version::PlatformVersion; +use drive_abci::config::{ChainLockConfig, PlatformConfig}; +use drive_abci::platform_types::masternode::{ + v0::{MasternodeStateV0, MasternodeV0}, + Masternode, +}; +use drive_abci::platform_types::platform_state::platform_state_for_saving::v2::{ + serialize_masternode_entry, serialize_validator_set_entry, +}; +use drive_abci::platform_types::platform_state::{ + platform_state_for_saving::{v2::PlatformStateForSavingV2, PlatformStateForSaving}, + PlatformState, +}; +use drive_abci::platform_types::signature_verification_quorum_set::{ + Quorums, SignatureVerificationQuorumSetForSaving, SignatureVerificationQuorumSetV0, + SignatureVerificationQuorumSetV0Methods, VerificationQuorum, +}; +use std::collections::BTreeMap; +use std::path::Path; + +const PUBLIC_KEYS: [&str; 4] = [ + "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e", + "95fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe48", + "b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", +]; + +fn quorums(entries: &[(u8, usize, Option)]) -> Quorums { + entries + .iter() + .map(|&(hash, key, index)| { + ( + QuorumHash::from_byte_array([hash; 32]), + VerificationQuorum { + public_key: hex::decode(PUBLIC_KEYS[key]) + .unwrap() + .as_slice() + .try_into() + .unwrap(), + index, + }, + ) + }) + .collect() +} + +fn main() { + let mut runtime = SignatureVerificationQuorumSetV0::new(&ChainLockConfig { + quorum_type: QuorumType::Llmq400_60, + quorum_size: 400, + quorum_window: 288, + quorum_active_signers: 4, + quorum_rotation: false, + }); + runtime.set_current_quorums(quorums(&[(0x11, 0, None), (0x22, 1, Some(0))])); + runtime.set_previous_past_quorums(Quorums::default(), 899, 900); + runtime.set_previous_past_quorums(quorums(&[(0x33, 2, Some(1)), (0x44, 3, None)]), 1000, 1008); + let output = std::env::args().nth(1).expect("fixture output directory"); + let output = Path::new(&output); + let config = bincode::config::standard() + .with_big_endian() + .with_no_limit(); + for (name, stored) in [ + ( + "quorum-storage-v1.hex", + SignatureVerificationQuorumSetForSaving::V1(runtime.clone().into()), + ), + ( + "quorum-storage-v2.hex", + SignatureVerificationQuorumSetForSaving::V2(runtime.clone().into()), + ), + ] { + write( + output, + name, + bincode::encode_to_vec(stored, config).unwrap(), + ); + } + let mut state = + PlatformState::default_with_protocol_versions(13, 14, &PlatformConfig::default()).unwrap(); + state.chain_lock_validating_quorums = runtime.clone().into(); + state.instant_lock_validating_quorums = runtime.into(); + for (tag, node_type, service) in [ + (0x31, MasternodeType::Regular, "1.2.3.4:19999"), + (0x41, MasternodeType::Evo, "[2001:db8::1]:19999"), + ] { + let evo = node_type == MasternodeType::Evo; + let node: MasternodeListItem = Masternode::V0(MasternodeV0 { + node_type, + pro_tx_hash: ProTxHash::from_byte_array([tag; 32]), + collateral_hash: Txid::from_byte_array([tag + 1; 32]), + collateral_index: 1000, + collateral_address: [0x21; 20], + operator_reward: 1.5, + state: MasternodeStateV0 { + service: service.parse().unwrap(), + registered_height: 100, + pose_revived_height: Some(200), + pose_ban_height: None, + revocation_reason: 0, + owner_address: [0x22; 20], + voting_address: [0x23; 20], + payout_address: [0x24; 20], + pub_key_operator: hex::decode(PUBLIC_KEYS[0]).unwrap(), + operator_payout_address: Some([0x25; 20]), + platform_node_id: evo.then_some(std::array::from_fn(|i| i as u8 + 1)), + platform_p2p_port: evo.then_some(26656), + platform_http_port: evo.then_some(443), + }, + }) + .into(); + write( + output, + if evo { + "masternode-evo.hex" + } else { + "masternode-regular.hex" + }, + serialize_masternode_entry(&node, PlatformVersion::latest()).unwrap(), + ); + if evo { + state + .hpmn_masternode_list + .insert(node.pro_tx_hash, node.clone()); + } + state.full_masternode_list.insert(node.pro_tx_hash, node); + } + let validator = ValidatorV0 { + pro_tx_hash: ProTxHash::from_byte_array([0x41; 32]), + public_key: Some( + hex::decode(PUBLIC_KEYS[1]) + .unwrap() + .as_slice() + .try_into() + .unwrap(), + ), + node_ip: "2001:db8::1".into(), + node_id: PubkeyHash::from_byte_array(std::array::from_fn(|i| i as u8 + 1)), + core_port: 19999, + platform_http_port: 443, + platform_p2p_port: 26656, + is_banned: false, + }; + let quorum_hash = QuorumHash::from_byte_array([0x55; 32]); + let set = ValidatorSet::V0(ValidatorSetV0 { + quorum_hash, + quorum_index: Some(2), + core_height: 1000, + members: BTreeMap::from([(validator.pro_tx_hash, validator)]), + threshold_public_key: hex::decode(PUBLIC_KEYS[2]) + .unwrap() + .as_slice() + .try_into() + .unwrap(), + }); + write( + output, + "validator-set-entry.hex", + serialize_validator_set_entry(&set).unwrap(), + ); + state.validator_sets.insert(quorum_hash, set); + state.current_validator_set_quorum_hash = quorum_hash; + write( + output, + "populated-platform-state-v1.hex", + state.serialize_to_bytes().unwrap(), + ); + write( + output, + "populated-checkpoint-platform-state.hex", + state.serialize_standalone_to_bytes().unwrap(), + ); + let stored = PlatformStateForSaving::V2(PlatformStateForSavingV2::from(&state)); + write( + output, + "populated-platform-state-v2.hex", + bincode::encode_to_vec(stored, config).unwrap(), + ); +} + +fn write(output: &Path, name: &str, bytes: Vec) { + println!("{name}: {} bytes", bytes.len()); + std::fs::write(output.join(name), format!("{}\n", hex::encode(bytes))).unwrap(); +} diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/platform-state-v1.hex b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/platform-state-v1.hex deleted file mode 100644 index a564ec38c38..00000000000 --- a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/platform-state-v1.hex +++ /dev/null @@ -1 +0,0 @@ -0100000d0e0000000000000000000000000000000000000000000000000000000000000000000002010400fb012002111111111111111111111111111111111111111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb002222222222222222222222222222222222222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e01000102333333333333333333333333333333333333333333333333333333333333333395fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe4801014444444444444444444444444444444444444444444444444444444444444444b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb038402010400fb012002111111111111111111111111111111111111111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb002222222222222222222222222222222222222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e01000102333333333333333333333333333333333333333333333333333333333333333395fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe4801014444444444444444444444444444444444444444444444444444444444444444b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb0384000000 diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/platform-state-v2.hex b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/platform-state-v2.hex deleted file mode 100644 index 4b6bdcbfde5..00000000000 --- a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/platform-state-v2.hex +++ /dev/null @@ -1 +0,0 @@ -0200000d0e0000000000000000000000000000000000000000000000000000000000000000000002010400fb012002111111111111111111111111111111111111111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb002222222222222222222222222222222222222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e01000102333333333333333333333333333333333333333333333333333333333333333395fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe4801014444444444444444444444444444444444444444444444444444444444444444b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb038402010400fb012002111111111111111111111111111111111111111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb002222222222222222222222222222222222222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e01000102333333333333333333333333333333333333333333333333333333333333333395fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe4801014444444444444444444444444444444444444444444444444444444444444444b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb038400 diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v0.hex b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v0.hex deleted file mode 100644 index 2e9333f44e3..00000000000 --- a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v0.hex +++ /dev/null @@ -1 +0,0 @@ -00010400fb01200211111111111111111111111111111111111111111111111111111111111111113097f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00222222222222222222222222222222222222222222222222222222222222222230a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e0100010233333333333333333333333333333333333333333333333333333333333333333095fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe480101444444444444444444444444444444444444444444444444444444444444444430b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb0384 diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v1.hex b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v1.hex deleted file mode 100644 index 80566884687..00000000000 --- a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v1.hex +++ /dev/null @@ -1 +0,0 @@ -01010400fb012002111111111111111111111111111111111111111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb002222222222222222222222222222222222222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e0100010233333333333333333333333333333333333333333333333333333333333333333095fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe480101444444444444444444444444444444444444444444444444444444444444444430b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb0384 diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v2.hex b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v2.hex deleted file mode 100644 index 0c518338948..00000000000 --- a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/fixtures/quorum-storage-v2.hex +++ /dev/null @@ -1 +0,0 @@ -02010400fb012002111111111111111111111111111111111111111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb002222222222222222222222222222222222222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e01000102333333333333333333333333333333333333333333333333333333333333333395fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe4801014444444444444444444444444444444444444444444444444444444444444444b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb0384 diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/for_saving_v0.rs b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/for_saving_v0.rs index 26867dee202..20ed47d60cd 100644 --- a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/for_saving_v0.rs +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/for_saving_v0.rs @@ -166,7 +166,7 @@ mod tests { #[test] fn should_restore_current_and_previous_quorums_from_frozen_v0_storage() { - let bytes = hex::decode(include_str!("fixtures/quorum-storage-v0.hex").trim()).unwrap(); + let bytes = hex::decode(super::super::storage_vectors::QUORUM_STORAGE_V0.trim()).unwrap(); let config = bincode::config::standard() .with_big_endian() .with_no_limit(); diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/mod.rs b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/mod.rs index 920ef6cc08c..58a17f5ba25 100644 --- a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/mod.rs +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/mod.rs @@ -8,3 +8,6 @@ pub mod quorums; #[cfg(all(test, feature = "bls-signatures"))] mod storage_tests; + +#[cfg(all(test, feature = "bls-signatures"))] +mod storage_vectors; diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/storage_tests.rs b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/storage_tests.rs index 107970feca9..d041e873ecf 100644 --- a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/storage_tests.rs +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/storage_tests.rs @@ -1,5 +1,10 @@ use super::for_saving_v1::QuorumForSavingV1; use super::quorum_set::PreviousPastQuorumsV0; +use super::storage_vectors; +use crate::platform_types::platform_state::platform_state_for_saving::v2::{ + deserialize_masternode_entry, deserialize_validator_set_entry, serialize_masternode_entry, + serialize_validator_set_entry, +}; use crate::platform_types::platform_state::platform_state_for_saving::PlatformStateForSaving; use crate::platform_types::platform_state::PlatformState; use crate::platform_types::signature_verification_quorum_set::{ @@ -7,8 +12,9 @@ use crate::platform_types::signature_verification_quorum_set::{ SignatureVerificationQuorumSetV0Methods, VerificationQuorum, }; use dpp::bls::PublicKey; +use dpp::core_types::validator_set::ValidatorSet; use dpp::dashcore::hashes::Hash; -use dpp::dashcore::QuorumHash; +use dpp::dashcore::{ProTxHash, QuorumHash}; use dpp::dashcore_rpc::json::QuorumType; use dpp::serialization::PlatformDeserializableFromVersionedStructureTrusted; use dpp::version::PlatformVersion; @@ -72,8 +78,8 @@ fn assert_quorums(set: &SignatureVerificationQuorumSet) { #[test] fn should_preserve_current_and_previous_quorums_in_old_v1_and_v2_records() { for fixture in [ - include_str!("fixtures/quorum-storage-v1.hex"), - include_str!("fixtures/quorum-storage-v2.hex"), + super::storage_vectors::QUORUM_STORAGE_V1, + super::storage_vectors::QUORUM_STORAGE_V2, ] { let bytes = hex::decode(fixture.trim()).unwrap(); let (stored, read): (SignatureVerificationQuorumSetForSaving, _) = @@ -87,9 +93,9 @@ fn should_preserve_current_and_previous_quorums_in_old_v1_and_v2_records() { #[test] fn should_load_old_saved_state_and_checkpoint_with_both_quorum_sets() { for fixture in [ - include_str!("fixtures/platform-state-v1.hex"), - include_str!("fixtures/checkpoint-platform-state.hex"), - include_str!("fixtures/platform-state-v2.hex"), + super::storage_vectors::PLATFORM_STATE_V1, + super::storage_vectors::CHECKPOINT_PLATFORM_STATE, + super::storage_vectors::PLATFORM_STATE_V2, ] { let bytes = hex::decode(fixture.trim()).unwrap(); let (stored, read): (PlatformStateForSaving, _) = @@ -159,3 +165,106 @@ fn should_reject_truncated_and_invalid_stored_quorum_keys() { invalid[32..80].fill(0); assert!(bincode::decode_from_slice::(&invalid, config()).is_err()); } + +fn historical_masternode_entries() -> Vec<(Vec, Vec)> { + [ + (0x31, storage_vectors::MASTERNODE_REGULAR), + (0x41, storage_vectors::MASTERNODE_EVO), + ] + .into_iter() + .map(|(tag, hex)| (vec![tag; 32], hex::decode(hex).unwrap())) + .collect() +} + +#[test] +fn should_preserve_historical_grovedb_masternode_and_validator_entries() { + for (key, bytes) in historical_masternode_entries() { + let node = deserialize_masternode_entry(&bytes).unwrap(); + assert_eq!(node.pro_tx_hash.as_byte_array().as_slice(), key); + assert_eq!(node.collateral_index, 1000); + assert_eq!(node.operator_reward, 1.5); + assert_eq!(node.state.pub_key_operator, hex::decode(KEYS[0]).unwrap()); + assert_eq!(node.state.pose_revived_height, Some(200)); + assert_eq!(node.state.pose_ban_height, None); + assert_eq!( + node.state.platform_node_id, + (key[0] == 0x41).then_some(std::array::from_fn(|i| i as u8 + 1)) + ); + assert_eq!( + serialize_masternode_entry(&node, PlatformVersion::latest()).unwrap(), + bytes + ); + } + let bytes = hex::decode(storage_vectors::VALIDATOR_SET_ENTRY).unwrap(); + let set = deserialize_validator_set_entry(&bytes).unwrap(); + assert_eq!(serialize_validator_set_entry(&set).unwrap(), bytes); + let ValidatorSet::V0(set) = set; + assert_eq!(set.quorum_hash.to_byte_array(), [0x55; 32]); + assert_eq!(set.quorum_index, Some(2)); + assert_eq!(set.core_height, 1000); + assert_eq!( + set.threshold_public_key.to_bytes().as_slice(), + hex::decode(KEYS[2]).unwrap() + ); + assert_eq!(set.members.len(), 1); + let member = set + .members + .get(&ProTxHash::from_byte_array([0x41; 32])) + .unwrap(); + assert_eq!( + member.public_key.unwrap().to_bytes().as_slice(), + hex::decode(KEYS[1]).unwrap() + ); + assert_eq!( + member.node_id.to_byte_array(), + std::array::from_fn(|i| i as u8 + 1) + ); + assert_eq!(member.node_ip, "2001:db8::1"); +} + +#[test] +fn should_restore_historical_populated_state_and_checkpoint() { + for fixture in [ + storage_vectors::POPULATED_PLATFORM_STATE_V1, + storage_vectors::POPULATED_PLATFORM_STATE_V2, + ] { + let bytes = hex::decode(fixture).unwrap(); + let (stored, read): (PlatformStateForSaving, _) = + bincode::decode_from_slice(&bytes, config()).unwrap(); + assert_eq!(read, bytes.len()); + assert_eq!(bincode::encode_to_vec(&stored, config()).unwrap(), bytes); + let state = match stored { + PlatformStateForSaving::V2(record) => record + .into_platform_state( + historical_masternode_entries(), + vec![( + vec![0x55; 32], + hex::decode(storage_vectors::VALIDATOR_SET_ENTRY).unwrap(), + )], + ) + .unwrap(), + _ => PlatformState::versioned_deserialize_trusted(&bytes, PlatformVersion::latest()) + .unwrap(), + }; + assert_quorums(&state.chain_lock_validating_quorums); + assert_quorums(&state.instant_lock_validating_quorums); + assert_eq!(state.full_masternode_list.len(), 2); + assert_eq!(state.hpmn_masternode_list.len(), 1); + assert!(state + .hpmn_masternode_list + .contains_key(&ProTxHash::from_byte_array([0x41; 32]))); + assert_eq!(state.validator_sets.len(), 1); + let set = state + .validator_sets + .get(&QuorumHash::from_byte_array([0x55; 32])) + .unwrap(); + assert_eq!( + serialize_validator_set_entry(set).unwrap(), + hex::decode(storage_vectors::VALIDATOR_SET_ENTRY).unwrap() + ); + assert_eq!( + state.serialize_standalone_to_bytes().unwrap(), + hex::decode(storage_vectors::POPULATED_CHECKPOINT_PLATFORM_STATE).unwrap() + ); + } +} diff --git a/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/storage_vectors.rs b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/storage_vectors.rs new file mode 100644 index 00000000000..09fce3a883f --- /dev/null +++ b/packages/rs-drive-abci/src/platform_types/signature_verification_quorum_set/v0/storage_vectors.rs @@ -0,0 +1,185 @@ +//! Historical bytes and provenance: see fixtures/README.md. Keep these constants frozen. + +pub(super) const CHECKPOINT_PLATFORM_STATE: &str = concat!( + "0100000d0e0000000000000000000000000000000000000000000000000000000000000000000002010400fb01200211", + "1111111111111111111111111111111111111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3", + "688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb0022222222222222222222222222222222", + "22222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62a", + "e28f75bb8f1c7c42c39a8c5529bf0f4e0100010233333333333333333333333333333333333333333333333333333333", + "3333333395fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf", + "6ac5fe4801014444444444444444444444444444444444444444444444444444444444444444b7f1d3a73197d7942695", + "638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb03", + "8402010400fb012002111111111111111111111111111111111111111111111111111111111111111197f1d3a73197d7", + "942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00222222222222", + "2222222222222222222222222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128", + "012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e01000102333333333333333333333333333333333333", + "333333333333333333333333333395fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4", + "a81e2d24daafc5387dbf6ac5fe4801014444444444444444444444444444444444444444444444444444444444444444", + "b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "00fb03e8fb03f001fb0384000000", +); + +pub(super) const PLATFORM_STATE_V1: &str = concat!( + "0100000d0e0000000000000000000000000000000000000000000000000000000000000000000002010400fb01200211", + "1111111111111111111111111111111111111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3", + "688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb0022222222222222222222222222222222", + "22222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62a", + "e28f75bb8f1c7c42c39a8c5529bf0f4e0100010233333333333333333333333333333333333333333333333333333333", + "3333333395fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf", + "6ac5fe4801014444444444444444444444444444444444444444444444444444444444444444b7f1d3a73197d7942695", + "638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb03", + "8402010400fb012002111111111111111111111111111111111111111111111111111111111111111197f1d3a73197d7", + "942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00222222222222", + "2222222222222222222222222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128", + "012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e01000102333333333333333333333333333333333333", + "333333333333333333333333333395fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4", + "a81e2d24daafc5387dbf6ac5fe4801014444444444444444444444444444444444444444444444444444444444444444", + "b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "00fb03e8fb03f001fb0384000000", +); + +pub(super) const PLATFORM_STATE_V2: &str = concat!( + "0200000d0e0000000000000000000000000000000000000000000000000000000000000000000002010400fb01200211", + "1111111111111111111111111111111111111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3", + "688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb0022222222222222222222222222222222", + "22222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62a", + "e28f75bb8f1c7c42c39a8c5529bf0f4e0100010233333333333333333333333333333333333333333333333333333333", + "3333333395fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf", + "6ac5fe4801014444444444444444444444444444444444444444444444444444444444444444b7f1d3a73197d7942695", + "638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb03", + "8402010400fb012002111111111111111111111111111111111111111111111111111111111111111197f1d3a73197d7", + "942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00222222222222", + "2222222222222222222222222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128", + "012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e01000102333333333333333333333333333333333333", + "333333333333333333333333333395fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4", + "a81e2d24daafc5387dbf6ac5fe4801014444444444444444444444444444444444444444444444444444444444444444", + "b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb", + "00fb03e8fb03f001fb038400", +); + +pub(super) const QUORUM_STORAGE_V0: &str = concat!( + "00010400fb01200211111111111111111111111111111111111111111111111111111111111111113097f1d3a73197d7", + "942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00222222222222", + "222222222222222222222222222222222222222222222222222230a572cbea904d67468808c8eb50a9450c9721db3091", + "28012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e010001023333333333333333333333333333333333", + "3333333333333333333333333333333095fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea1137", + "79c4a81e2d24daafc5387dbf6ac5fe480101444444444444444444444444444444444444444444444444444444444444", + "444430b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb", + "22c6bb00fb03e8fb03f001fb0384", +); + +pub(super) const QUORUM_STORAGE_V1: &str = concat!( + "01010400fb012002111111111111111111111111111111111111111111111111111111111111111197f1d3a73197d794", + "2695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb0022222222222222", + "22222222222222222222222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db30912801", + "2543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e0100010233333333333333333333333333333333333333", + "333333333333333333333333333095fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4", + "a81e2d24daafc5387dbf6ac5fe4801014444444444444444444444444444444444444444444444444444444444444444", + "30b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6", + "bb00fb03e8fb03f001fb0384", +); + +pub(super) const QUORUM_STORAGE_V2: &str = concat!( + "02010400fb012002111111111111111111111111111111111111111111111111111111111111111197f1d3a73197d794", + "2695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb0022222222222222", + "22222222222222222222222222222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db30912801", + "2543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e0100010233333333333333333333333333333333333333", + "3333333333333333333333333395fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a8", + "1e2d24daafc5387dbf6ac5fe4801014444444444444444444444444444444444444444444444444444444444444444b7", + "f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00", + "fb03e8fb03f001fb0384", +); + +// Generated with Platform 6499c680c6bc311e8933f396423a79459e2a88bd (before #5307). + +pub(super) const MASTERNODE_REGULAR: &str = concat!( + "000020313131313131313131313131313131313131313131313131313131313131313120323232323232323232323232", + "3232323232323232323232323232323232323232fb03e821212121212121212121212121212121212121213fc0000000", + "01020304fb4e1f6401c80000222222222222222222222222222222222222222223232323232323232323232323232323", + "2323232324242424242424242424242424242424242424243097f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b9", + "05a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb0125252525252525252525252525252525252525250000", + "00", +); + +pub(super) const MASTERNODE_EVO: &str = concat!( + "000120414141414141414141414141414141414141414141414141414141414141414120424242424242424242424242", + "4242424242424242424242424242424242424242fb03e821212121212121212121212121212121212121213fc0000001", + "20010db8000000000000000000000001fb4e1f6401c80000222222222222222222222222222222222222222223232323", + "2323232323232323232323232323232324242424242424242424242424242424242424243097f1d3a73197d794269563", + "8c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb0125252525252525252525", + "25252525252525252525010102030405060708090a0b0c0d0e0f101112131401fb682001fb01bb", +); + +pub(super) const VALIDATOR_SET_ENTRY: &str = concat!( + "0055555555555555555555555555555555555555555555555555555555555555550102fb03e801204141414141414141", + "414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141", + "414141414141414101a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c", + "42c39a8c5529bf0f4e0b323030313a6462383a3a310102030405060708090a0b0c0d0e0f1011121314fb4e1ffb01bbfb", + "68200095fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6a", + "c5fe48", +); + +pub(super) const POPULATED_PLATFORM_STATE_V1: &str = concat!( + "0100000d0e55555555555555555555555555555555555555555555555555555555555555550001555555555555555555", + "555555555555555555555555555555555555555555555500555555555555555555555555555555555555555555555555", + "55555555555555550102fb03e80120414141414141414141414141414141414141414141414141414141414141414141", + "4141414141414141414141414141414141414141414141414141414141414101a572cbea904d67468808c8eb50a9450c", + "9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e0b323030313a6462383a3a3101020304", + "05060708090a0b0c0d0e0f1011121314fb4e1ffb01bbfb68200095fde78acd5f6886ddaf5d0056610167c513d09c1c0e", + "fabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe4802010400fb0120021111111111111111111111111111", + "11111111111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171b", + "ac586c55e83ff97a1aeffb3af00adb22c6bb002222222222222222222222222222222222222222222222222222222222", + "222222a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529", + "bf0f4e01000102333333333333333333333333333333333333333333333333333333333333333395fde78acd5f6886dd", + "af5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe48010144444444444444", + "44444444444444444444444444444444444444444444444444b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b9", + "05a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb038402010400fb01200211111111", + "1111111111111111111111111111111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3688c4f", + "9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb0022222222222222222222222222222222222222", + "22222222222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75", + "bb8f1c7c42c39a8c5529bf0f4e0100010233333333333333333333333333333333333333333333333333333333333333", + "3395fde78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe", + "4801014444444444444444444444444444444444444444444444444444444444444444b7f1d3a73197d7942695638c4f", + "a9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb03840231", + "313131313131313131313131313131313131313131313131313131313131310000203131313131313131313131313131", + "313131313131313131313131313131313131203232323232323232323232323232323232323232323232323232323232", + "323232fb03e821212121212121212121212121212121212121213fc000000001020304fb4e1f6401c800002222222222", + "222222222222222222222222222222232323232323232323232323232323232323232324242424242424242424242424", + "242424242424243097f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aef", + "fb3af00adb22c6bb01252525252525252525252525252525252525252500000041414141414141414141414141414141", + "414141414141414141414141414141410001204141414141414141414141414141414141414141414141414141414141", + "414141204242424242424242424242424242424242424242424242424242424242424242fb03e8212121212121212121", + "21212121212121212121213fc000000120010db8000000000000000000000001fb4e1f6401c800002222222222222222", + "222222222222222222222222232323232323232323232323232323232323232324242424242424242424242424242424", + "242424243097f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af0", + "0adb22c6bb012525252525252525252525252525252525252525010102030405060708090a0b0c0d0e0f101112131401", + "fb682001fb01bb0141414141414141414141414141414141414141414141414141414141414141410001204141414141", + "414141414141414141414141414141414141414141414141414141204242424242424242424242424242424242424242", + "424242424242424242424242fb03e821212121212121212121212121212121212121213fc000000120010db800000000", + "0000000000000001fb4e1f6401c800002222222222222222222222222222222222222222232323232323232323232323", + "232323232323232324242424242424242424242424242424242424243097f1d3a73197d7942695638c4fa9ac0fc3688c", + "4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb01252525252525252525252525252525252525", + "2525010102030405060708090a0b0c0d0e0f101112131401fb682001fb01bb00", +); + +pub(super) const POPULATED_PLATFORM_STATE_V2: &str = concat!( + "0200000d0e55555555555555555555555555555555555555555555555555555555555555550001555555555555555555", + "555555555555555555555555555555555555555555555502010400fb0120021111111111111111111111111111111111", + "11111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c", + "55e83ff97a1aeffb3af00adb22c6bb002222222222222222222222222222222222222222222222222222222222222222", + "a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c7c42c39a8c5529bf0f4e", + "01000102333333333333333333333333333333333333333333333333333333333333333395fde78acd5f6886ddaf5d00", + "56610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe48010144444444444444444444", + "44444444444444444444444444444444444444444444b7f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e", + "3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb038402010400fb01200211111111111111", + "1111111111111111111111111111111111111111111111111197f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b9", + "05a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb0022222222222222222222222222222222222222222222", + "22222222222222222222a572cbea904d67468808c8eb50a9450c9721db309128012543902d0ac358a62ae28f75bb8f1c", + "7c42c39a8c5529bf0f4e01000102333333333333333333333333333333333333333333333333333333333333333395fd", + "e78acd5f6886ddaf5d0056610167c513d09c1c0efabbc7cdcc69beea113779c4a81e2d24daafc5387dbf6ac5fe480101", + "4444444444444444444444444444444444444444444444444444444444444444b7f1d3a73197d7942695638c4fa9ac0f", + "c3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb00fb03e8fb03f001fb038400", +); + +// Both historical writers produce identical bytes. +pub(super) const POPULATED_CHECKPOINT_PLATFORM_STATE: &str = POPULATED_PLATFORM_STATE_V1; diff --git a/packages/rs-platform-encryption/tests/fixtures/generate-historical-encryption.rs b/packages/rs-platform-encryption/tests/fixtures/generate-historical-encryption.rs new file mode 100644 index 00000000000..1945346fc4e --- /dev/null +++ b/packages/rs-platform-encryption/tests/fixtures/generate-historical-encryption.rs @@ -0,0 +1,44 @@ +//! Run as a platform-encryption example on Platform 6499c680c6bc311e8933f396423a79459e2a88bd. +//! `cargo run -p platform-encryption --example generate-historical-encryption --locked -- /path/to/output` +use platform_encryption::*; +use secp256k1::{PublicKey, Secp256k1, SecretKey}; +fn main() { + let secp = Secp256k1::new(); + let secret = SecretKey::from_slice(&[0xc0; 32]).unwrap(); + let recipient = SecretKey::from_slice(&[0x0d; 32]).unwrap(); + let public = PublicKey::from_secret_key(&secp, &recipient); + let shared = derive_shared_key_ecdh(&secret, &public); + let xpub = compact_xpub_bytes( + [0x11, 0x22, 0x33, 0x44], + [0xaa; 32], + PublicKey::from_secret_key(&secp, &SecretKey::from_slice(&[7; 32]).unwrap()).serialize(), + ); + let iv = [0x5a; 16]; + let out = std::path::PathBuf::from(std::env::args().nth(1).unwrap()); + for (name, value) in [ + ("shared-key", shared.to_vec()), + ("compact-xpub", xpub.to_vec()), + ( + "encrypted-xpub", + encrypt_extended_public_key(&shared, &iv, &xpub), + ), + ( + "encrypted-label", + encrypt_account_label(&shared, &iv, "My DashPay Account"), + ), + ( + "encrypted-empty-label", + encrypt_account_label(&shared, &iv, ""), + ), + ( + "encrypted-id", + encrypt_enc_to_user_id(&shared, &[0x23; 32]).to_vec(), + ), + ( + "encrypted-private-data", + encrypt_private_data(&shared, &iv, b"historical contact information"), + ), + ] { + std::fs::write(out.join(format!("{name}.bin")), value).unwrap(); + } +} diff --git a/packages/rs-platform-encryption/tests/historical_compatibility.rs b/packages/rs-platform-encryption/tests/historical_compatibility.rs new file mode 100644 index 00000000000..abf542f7415 --- /dev/null +++ b/packages/rs-platform-encryption/tests/historical_compatibility.rs @@ -0,0 +1,119 @@ +//! Frozen results from Platform 6499c680c6bc311e8933f396423a79459e2a88bd, +//! using secp256k1 0.30.0. These ciphertexts become signed DashPay document fields. +//! Reproduce with fixtures/generate-historical-encryption.rs in that checkout. + +use platform_encryption::*; +use secp256k1::{PublicKey, SecretKey}; + +const SHARED_KEY: [u8; 32] = [ + 0x41, 0x44, 0xa9, 0xee, 0xca, 0x1b, 0x8c, 0xd1, 0x88, 0x4c, 0x7f, 0xdb, 0x14, 0xa5, 0x6a, 0x23, + 0xa5, 0xe7, 0x63, 0x95, 0x4b, 0x21, 0x96, 0x6a, 0xa5, 0xa8, 0x62, 0xea, 0x27, 0x9d, 0x8d, 0x13, +]; + +const COMPACT_XPUB: [u8; 69] = [ + 0x11, 0x22, 0x33, 0x44, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0x02, 0x98, 0x9c, 0x0b, 0x76, 0xcb, 0x56, 0x39, 0x71, 0xfd, 0xc9, 0xbe, + 0xf3, 0x1e, 0xc0, 0x6c, 0x35, 0x60, 0xf3, 0x24, 0x9d, 0x6e, 0xe9, 0xe5, 0xd8, 0x3c, 0x57, 0x62, + 0x55, 0x96, 0xe0, 0x5f, 0x6f, +]; + +const ENCRYPTED_XPUB: [u8; 96] = [ + 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, + 0x86, 0x4d, 0x1b, 0x38, 0x07, 0xcf, 0x80, 0xfd, 0x27, 0xdf, 0x6c, 0xac, 0x06, 0x3a, 0x51, 0x28, + 0xfd, 0x61, 0x19, 0xd0, 0xd4, 0x04, 0x91, 0xa7, 0x78, 0x8c, 0xb4, 0xe1, 0x97, 0x5b, 0xc4, 0x7e, + 0x50, 0x70, 0xc7, 0x91, 0x9e, 0x3d, 0x8c, 0x21, 0xab, 0x26, 0xbe, 0x1a, 0x76, 0x3e, 0x79, 0x08, + 0xb9, 0x7c, 0xd3, 0x2a, 0x5e, 0x36, 0x30, 0x9f, 0x3b, 0xf9, 0x53, 0x5c, 0x51, 0x9b, 0x1b, 0x32, + 0xb2, 0xf2, 0x06, 0x69, 0x6e, 0xc6, 0xd0, 0xe2, 0x44, 0xa2, 0xe1, 0x82, 0xfc, 0xea, 0xa7, 0x50, +]; + +const ENCRYPTED_LABEL: [u8; 48] = [ + 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, + 0x75, 0x86, 0x80, 0xff, 0xe5, 0xeb, 0xc1, 0x97, 0xcf, 0x98, 0xb7, 0xea, 0x57, 0x52, 0x97, 0x29, + 0xce, 0xa6, 0xda, 0x4c, 0xc5, 0x69, 0x07, 0x9a, 0x85, 0xc1, 0xa7, 0xe7, 0xa8, 0x03, 0xe0, 0x62, +]; + +const ENCRYPTED_EMPTY_LABEL: [u8; 48] = [ + 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, + 0x8d, 0x5f, 0x41, 0x35, 0x9c, 0x02, 0x9e, 0x2f, 0x4b, 0xf2, 0xbe, 0x4e, 0x24, 0xd1, 0x48, 0x5f, + 0x66, 0x69, 0x25, 0x05, 0x94, 0xec, 0x49, 0x42, 0x2d, 0xee, 0xd5, 0x45, 0xac, 0x75, 0xe9, 0x6d, +]; + +const ENCRYPTED_ID: [u8; 32] = [ + 0xdb, 0x69, 0x2c, 0xa2, 0x2a, 0x6b, 0x23, 0xf0, 0x18, 0xa8, 0x39, 0xba, 0xcc, 0x39, 0x22, 0x8b, + 0xdb, 0x69, 0x2c, 0xa2, 0x2a, 0x6b, 0x23, 0xf0, 0x18, 0xa8, 0x39, 0xba, 0xcc, 0x39, 0x22, 0x8b, +]; + +const ENCRYPTED_PRIVATE_DATA: [u8; 48] = [ + 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, + 0xb7, 0xac, 0x53, 0x1a, 0x78, 0xee, 0xdb, 0x9d, 0x80, 0xde, 0x82, 0x07, 0x49, 0x6d, 0xda, 0xbe, + 0xf7, 0x4a, 0xc3, 0xcb, 0x8b, 0x5e, 0xe5, 0x7c, 0x14, 0x1b, 0xe5, 0x44, 0xb0, 0x9e, 0x29, 0xe8, +]; + +#[test] +fn should_match_historical_ecdh_shared_secret() { + let sender = SecretKey::from_secret_bytes([0xc0; 32]).unwrap(); + let recipient = SecretKey::from_secret_bytes([0x0d; 32]).unwrap(); + assert_eq!( + derive_shared_key_ecdh(&sender, &PublicKey::from_secret_key(&recipient)), + SHARED_KEY + ); + assert_eq!( + derive_shared_key_ecdh(&recipient, &PublicKey::from_secret_key(&sender)), + SHARED_KEY + ); +} + +#[test] +fn should_match_historical_compact_xpub_and_ciphertext() { + let account = SecretKey::from_secret_bytes([7; 32]).unwrap(); + let xpub = compact_xpub_bytes( + [0x11, 0x22, 0x33, 0x44], + [0xaa; 32], + PublicKey::from_secret_key(&account).serialize(), + ); + assert_eq!(xpub, COMPACT_XPUB); + assert_eq!( + encrypt_extended_public_key(&SHARED_KEY, &[0x5a; 16], &xpub), + ENCRYPTED_XPUB + ); + assert_eq!( + decrypt_extended_public_key(&SHARED_KEY, &ENCRYPTED_XPUB).unwrap(), + COMPACT_XPUB + ); +} + +#[test] +fn should_match_historical_label_ciphertexts() { + for (label, expected) in [ + ("My DashPay Account", ENCRYPTED_LABEL.as_slice()), + ("", ENCRYPTED_EMPTY_LABEL.as_slice()), + ] { + assert_eq!( + encrypt_account_label(&SHARED_KEY, &[0x5a; 16], label), + expected + ); + assert_eq!(decrypt_account_label(&SHARED_KEY, expected).unwrap(), label); + } +} + +#[test] +fn should_match_historical_contact_info_ciphertexts() { + assert_eq!( + encrypt_enc_to_user_id(&SHARED_KEY, &[0x23; 32]), + ENCRYPTED_ID + ); + assert_eq!( + decrypt_enc_to_user_id(&SHARED_KEY, &ENCRYPTED_ID), + [0x23; 32] + ); + let plaintext = b"historical contact information"; + assert_eq!( + encrypt_private_data(&SHARED_KEY, &[0x5a; 16], plaintext), + ENCRYPTED_PRIVATE_DATA + ); + assert_eq!( + decrypt_private_data(&SHARED_KEY, &ENCRYPTED_PRIVATE_DATA).unwrap(), + plaintext + ); +} From f475f72a1112f2c6d84b9d7cd1bc4d6ab7540bd3 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Fri, 9 Oct 2026 14:23:45 +0000 Subject: [PATCH 19/22] chore(deps): pin rust-dashcore to PR 1149 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pin the eight workspace dependencies and all upstream lockfile packages to 8fe0a381. Adapt WASM private-key hex decoding to the removed hashes::hex API using the package's existing hex dependency. 🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent --- CHANGELOG.md | 6 +++-- Cargo.lock | 29 ++++++++++++---------- Cargo.toml | 16 ++++++------ packages/wasm-dpp2/src/core/private_key.rs | 5 ++-- 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75b0ec0216f..f611d580872 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,8 +20,10 @@ ### Changed - **Rust dependencies:** pin rust-dashcore to `fix/legacy-serde-bls-node-id` - (`e6873e95`), restoring legacy BLS public-key and canonical PlatformNodeId - serialization, and preserving the pre-v4 coinbase payload Serde layout. + ([rust-dashcore#1149](https://github.com/dashpay/rust-dashcore/pull/1149), + `8fe0a381`), restoring legacy BLS public-key and canonical PlatformNodeId + serialization, and preserving the pre-v4 coinbase payload Serde layout. Adapt + WASM private-key hex decoding to the removal of `dashcore_hashes::hex`. - **Breaking (Rust features):** remove DPP's `core_key_wallet_bip_38` and SDK's `core_key_wallet_bip38` feature forwards because upstream removed BIP38 support. diff --git a/Cargo.lock b/Cargo.lock index bf6e0837047..744c7623efd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1808,7 +1808,7 @@ dependencies = [ [[package]] name = "dash-network" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" +source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" dependencies = [ "cbindgen 0.29.4", "grovedb-bincode", @@ -1819,7 +1819,7 @@ dependencies = [ [[package]] name = "dash-network-seeds" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" +source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" dependencies = [ "dash-network", ] @@ -1948,7 +1948,7 @@ dependencies = [ [[package]] name = "dash-spv" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" +source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" dependencies = [ "async-trait", "chrono", @@ -2002,7 +2002,7 @@ dependencies = [ [[package]] name = "dashcore" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" +source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" dependencies = [ "anyhow", "base64 0.22.1", @@ -2026,7 +2026,7 @@ dependencies = [ [[package]] name = "dashcore-crypto" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" +source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" dependencies = [ "base58ck 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitcoin-crypto", @@ -2047,14 +2047,15 @@ dependencies = [ [[package]] name = "dashcore-private" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" +source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" [[package]] name = "dashcore-rpc" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" +source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" dependencies = [ "dashcore-rpc-json", + "hex-conservative 1.3.0", "jsonrpc", "serde", "serde_json", @@ -2064,7 +2065,7 @@ dependencies = [ [[package]] name = "dashcore-rpc-json" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" +source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" dependencies = [ "dashcore", "grovedb-bincode", @@ -2079,10 +2080,11 @@ dependencies = [ [[package]] name = "dashcore_hashes" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" +source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" dependencies = [ "bitcoin_hashes 0.14.101", "grovedb-bincode", + "hex-conservative 1.3.0", "rs-x11-hash", "serde", ] @@ -3164,7 +3166,7 @@ dependencies = [ [[package]] name = "git-state" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" +source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" [[package]] name = "glob" @@ -4404,7 +4406,7 @@ dependencies = [ [[package]] name = "key-wallet" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" +source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" dependencies = [ "async-trait", "bip39", @@ -4427,7 +4429,7 @@ dependencies = [ [[package]] name = "key-wallet-ffi" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" +source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" dependencies = [ "cbindgen 0.29.4", "dash-network", @@ -4443,11 +4445,12 @@ dependencies = [ [[package]] name = "key-wallet-manager" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=e6873e95ff3386ce7a0a03df9b2baca59e960700#e6873e95ff3386ce7a0a03df9b2baca59e960700" +source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" dependencies = [ "async-trait", "dashcore", "grovedb-bincode", + "hex-conservative 1.3.0", "key-wallet", "rayon", "serde", diff --git a/Cargo.toml b/Cargo.toml index 4e824b54a2f..2da73394a5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,14 +65,14 @@ grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "9791d2772 grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "9791d277207998b7789c85b0745e99d37040a12e" } grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "9791d277207998b7789c85b0745e99d37040a12e" } grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "9791d277207998b7789c85b0745e99d37040a12e" } -dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "e6873e95ff3386ce7a0a03df9b2baca59e960700" } -dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "e6873e95ff3386ce7a0a03df9b2baca59e960700" } -dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "e6873e95ff3386ce7a0a03df9b2baca59e960700" } -key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "e6873e95ff3386ce7a0a03df9b2baca59e960700" } -key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "e6873e95ff3386ce7a0a03df9b2baca59e960700" } -key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "e6873e95ff3386ce7a0a03df9b2baca59e960700" } -dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "e6873e95ff3386ce7a0a03df9b2baca59e960700" } -dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "e6873e95ff3386ce7a0a03df9b2baca59e960700" } +dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe0a38170059742ff902acc29a696b72490903c" } +dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe0a38170059742ff902acc29a696b72490903c" } +dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe0a38170059742ff902acc29a696b72490903c" } +key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe0a38170059742ff902acc29a696b72490903c" } +key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe0a38170059742ff902acc29a696b72490903c" } +key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe0a38170059742ff902acc29a696b72490903c" } +dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe0a38170059742ff902acc29a696b72490903c" } +dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe0a38170059742ff902acc29a696b72490903c" } tokio-metrics = "0.5" # Size-tuned profile for the iOS `rs-unified-sdk-ffi` staticlib, which diff --git a/packages/wasm-dpp2/src/core/private_key.rs b/packages/wasm-dpp2/src/core/private_key.rs index 41dd66fe336..fc411d06d10 100644 --- a/packages/wasm-dpp2/src/core/private_key.rs +++ b/packages/wasm-dpp2/src/core/private_key.rs @@ -8,7 +8,6 @@ use crate::impl_wasm_type_info; use crate::public_key::PublicKeyWasm; use crate::utils::try_vec_to_fixed_bytes; use dpp::dashcore::PrivateKey; -use dpp::dashcore::hashes::hex::FromHex; use wasm_bindgen::prelude::wasm_bindgen; #[wasm_bindgen(js_name = "PrivateKey")] @@ -63,8 +62,8 @@ impl PrivateKeyWasm { ) -> WasmDppResult { let network_wasm: NetworkWasm = network.try_into()?; - let bytes = Vec::from_hex(hex_key) - .map_err(|err| WasmDppError::invalid_argument(err.to_string()))?; + let bytes = + hex::decode(hex_key).map_err(|err| WasmDppError::invalid_argument(err.to_string()))?; let key_bytes: [u8; 32] = try_vec_to_fixed_bytes(bytes, "privateKey")?; From 7ee8f7b1a8a9897596511b66e3b539c69cb42399 Mon Sep 17 00:00:00 2001 From: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com> Date: Sun, 11 Oct 2026 00:31:32 -0500 Subject: [PATCH 20/22] feat(platform-wallet): return dash-spv's verification status with an SPV quorum key (#5366) Co-authored-by: Claude Opus 5.5 --- Cargo.lock | 2 + packages/rs-platform-wallet/Cargo.toml | 3 + packages/rs-platform-wallet/src/lib.rs | 2 +- packages/rs-platform-wallet/src/spv/mod.rs | 2 +- .../rs-platform-wallet/src/spv/runtime.rs | 138 +++++++++++++++++- 5 files changed, 144 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 744c7623efd..9ec3f5789d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1955,6 +1955,7 @@ dependencies = [ "clap", "dash-network-seeds", "dashcore", + "dashcore-rpc", "dashcore_hashes", "futures", "git-state", @@ -1965,6 +1966,7 @@ dependencies = [ "rayon", "serde", "serde_json", + "tempfile", "thiserror 1.0.69", "tokio", "tokio-stream", diff --git a/packages/rs-platform-wallet/Cargo.toml b/packages/rs-platform-wallet/Cargo.toml index 5c6fe75d5d1..5d62f66db0e 100644 --- a/packages/rs-platform-wallet/Cargo.toml +++ b/packages/rs-platform-wallet/Cargo.toml @@ -104,6 +104,9 @@ drive-proof-verifier = { path = "../rs-drive-proof-verifier" } # (via `dashcore/test-utils`) for building funded, signable test wallets. # Dev-only, so the production build stays on the leaner default features. key-wallet = { workspace = true, features = ["test-utils"] } +# `test-utils` exposes the SPV client's masternode list engine, so a test can +# hold a quorum at a known verification status. +dash-spv = { workspace = true, features = ["test-utils"] } rand = "0.8" # Drives the parallel decrypt benchmark in `shielded_decrypt_bench.rs`. rayon = "1.10" diff --git a/packages/rs-platform-wallet/src/lib.rs b/packages/rs-platform-wallet/src/lib.rs index 2fedc74a1a8..e800b28747e 100644 --- a/packages/rs-platform-wallet/src/lib.rs +++ b/packages/rs-platform-wallet/src/lib.rs @@ -54,7 +54,7 @@ pub use manager::platform_address_sync::{ DEFAULT_SYNC_INTERVAL_SECS, }; pub use manager::PlatformWalletManager; -pub use spv::SpvRuntime; +pub use spv::{SpvQuorumPublicKey, SpvRuntime}; pub use wallet::asset_lock::manager::AssetLockManager; pub use wallet::asset_lock::tracked::{AssetLockStatus, TrackedAssetLock}; pub use wallet::asset_lock::AssetLockFunding; diff --git a/packages/rs-platform-wallet/src/spv/mod.rs b/packages/rs-platform-wallet/src/spv/mod.rs index 984e2cc4d9d..38e1bb05dfa 100644 --- a/packages/rs-platform-wallet/src/spv/mod.rs +++ b/packages/rs-platform-wallet/src/spv/mod.rs @@ -2,7 +2,7 @@ mod peers; mod runtime; pub use peers::{SpvPeerInfo, SpvPeerNodeType}; -pub use runtime::SpvRuntime; +pub use runtime::{SpvQuorumPublicKey, SpvRuntime}; // Re-exports so the FFI layer can build sync configs and read progress // without depending on `dash-spv` or `tokio-util` directly. diff --git a/packages/rs-platform-wallet/src/spv/runtime.rs b/packages/rs-platform-wallet/src/spv/runtime.rs index beb8bd157ce..405cddfbf3e 100644 --- a/packages/rs-platform-wallet/src/spv/runtime.rs +++ b/packages/rs-platform-wallet/src/spv/runtime.rs @@ -9,6 +9,7 @@ use std::time::Duration; use tokio::sync::RwLock; use tokio::task::{JoinError, JoinHandle}; +use dashcore::sml::llmq_entry_verification::LLMQEntryVerificationStatus; use dashcore::sml::llmq_type::LLMQType; use dashcore::sml::masternode_list::MasternodeList; use dashcore::{PubkeyHash, QuorumHash, Transaction}; @@ -30,6 +31,17 @@ use crate::wallet::platform_wallet::PlatformWalletInfo; type SpvClient = DashSpvClient, PeerNetworkManager, DiskStorageManager>; +/// A quorum public key from the SPV masternode state, see +/// [`SpvRuntime::get_quorum_public_key_with_status`]. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SpvQuorumPublicKey { + /// The quorum's BLS public key. + pub public_key: [u8; 48], + /// dash-spv's verification status for the quorum. Never `Invalid`: the + /// lookup skips invalid entries. + pub status: LLMQEntryVerificationStatus, +} + /// Maximum wait per stop call; the owned teardown continues after this deadline. const SPV_STOP_TIMEOUT: Duration = Duration::from_secs(15); @@ -318,12 +330,33 @@ impl SpvRuntime { } /// Look up a quorum public key via the SPV masternode state. + /// + /// The key is returned whatever dash-spv's verification status for the + /// quorum, `Invalid` excepted. A caller that must know the status uses + /// [`Self::get_quorum_public_key_with_status`]. pub async fn get_quorum_public_key( &self, quorum_type: u32, quorum_hash: [u8; 32], height: u32, ) -> Result<[u8; 48], PlatformWalletError> { + self.get_quorum_public_key_with_status(quorum_type, quorum_hash, height) + .await + .map(|quorum| quorum.public_key) + } + + /// Look up a quorum public key via the SPV masternode state, with + /// dash-spv's verification status for the quorum. + /// + /// Only [`LLMQEntryVerificationStatus::Verified`] means dash-spv checked the + /// quorum's commitment against the members it derives for the quorum. Any + /// other status is a key dash-spv holds but has not verified. + pub async fn get_quorum_public_key_with_status( + &self, + quorum_type: u32, + quorum_hash: [u8; 32], + height: u32, + ) -> Result { let client_guard = self.client.read().await; let client = client_guard.as_ref().ok_or(PlatformWalletError::SpvError( "SPV Client not started".to_string(), @@ -337,7 +370,10 @@ impl SpvRuntime { .await .map_err(|e| PlatformWalletError::SpvError(e.to_string()))?; - Ok(*quorum.quorum_entry.quorum_public_key.as_ref()) + Ok(SpvQuorumPublicKey { + public_key: *quorum.quorum_entry.quorum_public_key.as_ref(), + status: quorum.verified, + }) } /// Start upstream's background sync while retaining its client for queries and stop. @@ -1144,6 +1180,106 @@ mod tests { assert_no_storage_writer_survives(&storage).await; } + /// The status-carrying lookup returns the key with dash-spv's status for + /// the quorum, and the key-only lookup the same key, whatever the status. + /// Neither returns a quorum dash-spv found `Invalid`. + #[tokio::test] + async fn should_return_the_quorum_status_with_its_key() { + use std::collections::BTreeMap; + + use dashcore::bls_sig_utils::{BLSPublicKey, BLSSignature}; + use dashcore::hash_types::QuorumVVecHash; + use dashcore::hashes::Hash; + use dashcore::sml::llmq_entry_verification::{ + LLMQEntryVerificationSkipStatus, LLMQEntryVerificationStatus, + }; + use dashcore::sml::llmq_type::LLMQType; + use dashcore::sml::masternode_list::MasternodeList; + use dashcore::sml::quorum_entry::qualified_quorum_entry::QualifiedQuorumEntry; + use dashcore::sml::quorum_validation_error::QuorumValidationError; + use dashcore::transaction::special_transaction::quorum_commitment::QuorumEntry; + use dashcore::{BlockHash, QuorumHash}; + + let (runtime, _storage) = offline_runtime().await; + let engine = { + let client = runtime.client.read().await; + dash_spv::test_utils::masternode_list_engine(client.as_ref().unwrap()).unwrap() + }; + + // Platform names the quorum by its hash in the reverse byte order. + let platform_hash: [u8; 32] = std::array::from_fn(|i| i as u8); + let quorum_hash = QuorumHash::from_byte_array(std::array::from_fn(|i| 31 - i as u8)); + let height = 1_000; + let hold = |status: LLMQEntryVerificationStatus| { + let mut quorum: QualifiedQuorumEntry = QuorumEntry { + version: 2, + llmq_type: LLMQType::Llmqtype25_67, + quorum_hash, + quorum_index: None, + signers: vec![true; 4], + valid_members: vec![true; 4], + quorum_public_key: BLSPublicKey::from([9; 48]), + quorum_vvec_hash: QuorumVVecHash::all_zeros(), + threshold_sig: BLSSignature::from([1; 96]), + all_commitment_aggregated_signature: BLSSignature::from([1; 96]), + } + .into(); + quorum.verified = status; + let quorums = BTreeMap::from([( + LLMQType::Llmqtype25_67, + BTreeMap::from([(quorum_hash, Arc::new(quorum))]), + )]); + MasternodeList::build( + BTreeMap::new(), + quorums, + BlockHash::from_byte_array([3; 32]), + height, + ) + .build() + }; + + for status in [ + LLMQEntryVerificationStatus::Unknown, + LLMQEntryVerificationStatus::Skipped(LLMQEntryVerificationSkipStatus::MissedList(992)), + LLMQEntryVerificationStatus::Verified, + ] { + engine + .write() + .await + .masternode_lists + .insert(height, hold(status.clone())); + let quorum = runtime + .get_quorum_public_key_with_status(6, platform_hash, height) + .await + .unwrap(); + assert_eq!(quorum.public_key, [9; 48]); + assert_eq!(quorum.status, status); + assert_eq!( + runtime + .get_quorum_public_key(6, platform_hash, height) + .await + .unwrap(), + [9; 48], + "the key-only lookup ignores the status" + ); + } + + engine.write().await.masternode_lists.insert( + height, + hold(LLMQEntryVerificationStatus::Invalid( + QuorumValidationError::InvalidQuorumPublicKey, + )), + ); + assert!(runtime + .get_quorum_public_key_with_status(6, platform_hash, height) + .await + .is_err()); + assert!(runtime + .get_quorum_public_key(6, platform_hash, height) + .await + .is_err()); + } + /// Drive the production startup path and wait for upstream `run` to return. async fn run_offline(runtime: &Arc) { runtime.spawn_run_loop(); From 9183842643292ff9a4bfcc1b96953f7ceaf30491 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Mon, 12 Oct 2026 11:34:35 +0000 Subject: [PATCH 21/22] build!: pin rust-dashcore to dev d036d844 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the eight rust-dashcore workspace pins from the head of rust-dashcore#1149 (now merged) to `dev` at d036d844, and the `dash-pkc` base-sdk pin to 33fcc59e so the lockfile keeps one base-sdk revision. Adapt to what changed upstream in between: - `signer::CompactSignature` is gone. Address-witness verification and the Core-signer paths use `sign_message::MessageSignature`. It accepts the same 65-byte signatures (header 27..=34, same recovery-id mapping) and `MessageSignature::new(sig, true).serialize()` yields the bytes `to_compact_signature(true)` did; only the parse error text differs. - `dashcore_rpc::Error::Secp256k1` is gone; drop its DAPI mapping. - ProRegTx / ProUpServTx carry `MasternodeNetInfo`. The wallet reads the primary routable address and keeps the `ip:port` rendering; version-3 platform ports and payout shares are not surfaced yet (TODO). - `WalletInfoInterface::truncate_above` is delegated to the Core wallet. - `WalletEvent::ChainTruncated` updates the displayed balance. It is not mirrored to the persister: the changeset cannot lower a height, sweep without a winner, or demote a confirmed record. Marked TODO(#5380). Document the removed BIP38 C exports in the changelog. 🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PSi8cX229kW2fmLxKW6ir4 --- CHANGELOG.md | 22 +++++-- Cargo.lock | 50 ++++++++++------ Cargo.toml | 16 ++--- packages/rs-dapi/src/error.rs | 1 - packages/rs-dpp/Cargo.toml | 2 +- .../src/address_funds/platform_address.rs | 20 +++---- packages/rs-dpp/src/state_transition/mod.rs | 7 ++- .../rs-platform-wallet-ffi/src/persistence.rs | 8 ++- .../src/changeset/core_bridge.rs | 50 +++++++++++++++- .../src/masternode/record.rs | 60 +++++++++++++------ .../src/masternode/tracked.rs | 5 +- .../src/masternode/update_service.rs | 43 ++++++------- .../src/wallet/core/balance_handler.rs | 5 ++ .../src/wallet/core/spend_observer.rs | 26 +++++++- .../identity/network/payment_handler.rs | 20 +++++++ .../src/wallet/masternode_withdrawal.rs | 7 ++- .../src/wallet/platform_wallet_traits.rs | 6 +- .../rs-unified-sdk-jni/src/persistence.rs | 8 ++- 18 files changed, 254 insertions(+), 102 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53aa0374d08..48c446ba746 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,13 +17,25 @@ ### Changed -- **Rust dependencies:** pin rust-dashcore to `fix/legacy-serde-bls-node-id` - ([rust-dashcore#1149](https://github.com/dashpay/rust-dashcore/pull/1149), - `8fe0a381`), restoring legacy BLS public-key and canonical PlatformNodeId - serialization, and preserving the pre-v4 coinbase payload Serde layout. Adapt - WASM private-key hex decoding to the removal of `dashcore_hashes::hex`. +- **Rust dependencies:** pin rust-dashcore to `dev` at `d036d844`, which includes + [rust-dashcore#1149](https://github.com/dashpay/rust-dashcore/pull/1149): + legacy BLS public-key and canonical PlatformNodeId serialization are restored + and the pre-v4 coinbase payload Serde layout is preserved. Adapt WASM + private-key hex decoding to the removal of `dashcore_hashes::hex`, compact + ECDSA signatures to `dashcore::sign_message::MessageSignature` (the accepted + signatures and the encoded bytes are unchanged), and masternode payload + handling to the version-3 ProRegTx / ProUpServTx types. +- **platform-wallet:** a chain reorganization reported by key-wallet + (`WalletEvent::ChainTruncated`) updates the in-memory balance but is not yet + mirrored to persisted wallet state + ([#5380](https://github.com/dashpay/platform/issues/5380)). Version-3 + provider payloads are read through their pre-v3 fields only. - **Breaking (Rust features):** remove DPP's `core_key_wallet_bip_38` and SDK's `core_key_wallet_bip38` feature forwards because upstream removed BIP38 support. +- **Breaking (C API):** the unified FFI no longer exports + `bip38_encrypt_private_key` and `bip38_decrypt_private_key`. Both were + unsupported-operation stubs that upstream removed; direct C and Swift callers + must drop their calls. - **dpp (Rust API migration):** BLS types now live under `dpp::bls::{PublicKey, SecretKey, Signature, BlsError}`; the generic `dpp::bls_signatures` re-export diff --git a/Cargo.lock b/Cargo.lock index 9ec3f5789d1..bf2e877f183 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -81,6 +81,18 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "ambassador" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3d4da7fddbc6567cbd7b5c30364f91313701b1857ab5b925eb3acf06162eed" +dependencies = [ + "itertools 0.10.5", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "android_log-sys" version = "0.3.2" @@ -1808,7 +1820,7 @@ dependencies = [ [[package]] name = "dash-network" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=d036d844b1daf5d840b777babe6f82b5df5a9ae7#d036d844b1daf5d840b777babe6f82b5df5a9ae7" dependencies = [ "cbindgen 0.29.4", "grovedb-bincode", @@ -1819,7 +1831,7 @@ dependencies = [ [[package]] name = "dash-network-seeds" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=d036d844b1daf5d840b777babe6f82b5df5a9ae7#d036d844b1daf5d840b777babe6f82b5df5a9ae7" dependencies = [ "dash-network", ] @@ -1827,10 +1839,13 @@ dependencies = [ [[package]] name = "dash-num" version = "0.1.0-beta" -source = "git+https://github.com/dashpay/base-sdk?rev=e6402ced257c370a586ade9840ebbf545a4b7926#e6402ced257c370a586ade9840ebbf545a4b7926" +source = "git+https://github.com/dashpay/base-sdk?rev=33fcc59e1868c803f9608e0e78ee8bddf3abcee7#33fcc59e1868c803f9608e0e78ee8bddf3abcee7" dependencies = [ + "ambassador", "bitcoin-consensus-encoding 1.3.0", + "bitcoin-internals 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "dash-types", + "delegate", "hex-conservative 1.3.0", "serde", ] @@ -1838,7 +1853,7 @@ dependencies = [ [[package]] name = "dash-pkc" version = "0.1.0-beta" -source = "git+https://github.com/dashpay/base-sdk?rev=e6402ced257c370a586ade9840ebbf545a4b7926#e6402ced257c370a586ade9840ebbf545a4b7926" +source = "git+https://github.com/dashpay/base-sdk?rev=33fcc59e1868c803f9608e0e78ee8bddf3abcee7#33fcc59e1868c803f9608e0e78ee8bddf3abcee7" dependencies = [ "aes 0.9.3", "base58ck 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1948,7 +1963,7 @@ dependencies = [ [[package]] name = "dash-spv" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=d036d844b1daf5d840b777babe6f82b5df5a9ae7#d036d844b1daf5d840b777babe6f82b5df5a9ae7" dependencies = [ "async-trait", "chrono", @@ -1979,7 +1994,7 @@ dependencies = [ [[package]] name = "dash-types" version = "0.1.0-beta" -source = "git+https://github.com/dashpay/base-sdk?rev=e6402ced257c370a586ade9840ebbf545a4b7926#e6402ced257c370a586ade9840ebbf545a4b7926" +source = "git+https://github.com/dashpay/base-sdk?rev=33fcc59e1868c803f9608e0e78ee8bddf3abcee7#33fcc59e1868c803f9608e0e78ee8bddf3abcee7" dependencies = [ "bitcoin-consensus-encoding 1.3.0", "bitcoin-primitives 0.103.0", @@ -1994,7 +2009,7 @@ dependencies = [ [[package]] name = "dash-types-marker" version = "0.1.0-beta" -source = "git+https://github.com/dashpay/base-sdk?rev=e6402ced257c370a586ade9840ebbf545a4b7926#e6402ced257c370a586ade9840ebbf545a4b7926" +source = "git+https://github.com/dashpay/base-sdk?rev=33fcc59e1868c803f9608e0e78ee8bddf3abcee7#33fcc59e1868c803f9608e0e78ee8bddf3abcee7" dependencies = [ "quote", "syn 2.0.117", @@ -2004,7 +2019,7 @@ dependencies = [ [[package]] name = "dashcore" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=d036d844b1daf5d840b777babe6f82b5df5a9ae7#d036d844b1daf5d840b777babe6f82b5df5a9ae7" dependencies = [ "anyhow", "base64 0.22.1", @@ -2012,6 +2027,7 @@ dependencies = [ "bitvec", "blake3", "dash-network", + "dash-num", "dash-types", "dashcore-crypto", "dashcore-private", @@ -2028,7 +2044,7 @@ dependencies = [ [[package]] name = "dashcore-crypto" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=d036d844b1daf5d840b777babe6f82b5df5a9ae7#d036d844b1daf5d840b777babe6f82b5df5a9ae7" dependencies = [ "base58ck 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitcoin-crypto", @@ -2049,12 +2065,12 @@ dependencies = [ [[package]] name = "dashcore-private" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=d036d844b1daf5d840b777babe6f82b5df5a9ae7#d036d844b1daf5d840b777babe6f82b5df5a9ae7" [[package]] name = "dashcore-rpc" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=d036d844b1daf5d840b777babe6f82b5df5a9ae7#d036d844b1daf5d840b777babe6f82b5df5a9ae7" dependencies = [ "dashcore-rpc-json", "hex-conservative 1.3.0", @@ -2067,7 +2083,7 @@ dependencies = [ [[package]] name = "dashcore-rpc-json" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=d036d844b1daf5d840b777babe6f82b5df5a9ae7#d036d844b1daf5d840b777babe6f82b5df5a9ae7" dependencies = [ "dashcore", "grovedb-bincode", @@ -2082,7 +2098,7 @@ dependencies = [ [[package]] name = "dashcore_hashes" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=d036d844b1daf5d840b777babe6f82b5df5a9ae7#d036d844b1daf5d840b777babe6f82b5df5a9ae7" dependencies = [ "bitcoin_hashes 0.14.101", "grovedb-bincode", @@ -3168,7 +3184,7 @@ dependencies = [ [[package]] name = "git-state" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=d036d844b1daf5d840b777babe6f82b5df5a9ae7#d036d844b1daf5d840b777babe6f82b5df5a9ae7" [[package]] name = "glob" @@ -4408,7 +4424,7 @@ dependencies = [ [[package]] name = "key-wallet" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=d036d844b1daf5d840b777babe6f82b5df5a9ae7#d036d844b1daf5d840b777babe6f82b5df5a9ae7" dependencies = [ "async-trait", "bip39", @@ -4431,7 +4447,7 @@ dependencies = [ [[package]] name = "key-wallet-ffi" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=d036d844b1daf5d840b777babe6f82b5df5a9ae7#d036d844b1daf5d840b777babe6f82b5df5a9ae7" dependencies = [ "cbindgen 0.29.4", "dash-network", @@ -4447,7 +4463,7 @@ dependencies = [ [[package]] name = "key-wallet-manager" version = "0.45.0" -source = "git+https://github.com/dashpay/rust-dashcore?rev=8fe0a38170059742ff902acc29a696b72490903c#8fe0a38170059742ff902acc29a696b72490903c" +source = "git+https://github.com/dashpay/rust-dashcore?rev=d036d844b1daf5d840b777babe6f82b5df5a9ae7#d036d844b1daf5d840b777babe6f82b5df5a9ae7" dependencies = [ "async-trait", "dashcore", diff --git a/Cargo.toml b/Cargo.toml index 2da73394a5b..8f87824a229 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,14 +65,14 @@ grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "9791d2772 grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "9791d277207998b7789c85b0745e99d37040a12e" } grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "9791d277207998b7789c85b0745e99d37040a12e" } grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "9791d277207998b7789c85b0745e99d37040a12e" } -dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe0a38170059742ff902acc29a696b72490903c" } -dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe0a38170059742ff902acc29a696b72490903c" } -dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe0a38170059742ff902acc29a696b72490903c" } -key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe0a38170059742ff902acc29a696b72490903c" } -key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe0a38170059742ff902acc29a696b72490903c" } -key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe0a38170059742ff902acc29a696b72490903c" } -dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe0a38170059742ff902acc29a696b72490903c" } -dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "8fe0a38170059742ff902acc29a696b72490903c" } +dashcore = { git = "https://github.com/dashpay/rust-dashcore", rev = "d036d844b1daf5d840b777babe6f82b5df5a9ae7" } +dash-network-seeds = { git = "https://github.com/dashpay/rust-dashcore", rev = "d036d844b1daf5d840b777babe6f82b5df5a9ae7" } +dash-spv = { git = "https://github.com/dashpay/rust-dashcore", rev = "d036d844b1daf5d840b777babe6f82b5df5a9ae7" } +key-wallet = { git = "https://github.com/dashpay/rust-dashcore", rev = "d036d844b1daf5d840b777babe6f82b5df5a9ae7" } +key-wallet-ffi = { git = "https://github.com/dashpay/rust-dashcore", rev = "d036d844b1daf5d840b777babe6f82b5df5a9ae7" } +key-wallet-manager = { git = "https://github.com/dashpay/rust-dashcore", rev = "d036d844b1daf5d840b777babe6f82b5df5a9ae7" } +dash-network = { git = "https://github.com/dashpay/rust-dashcore", rev = "d036d844b1daf5d840b777babe6f82b5df5a9ae7" } +dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore", rev = "d036d844b1daf5d840b777babe6f82b5df5a9ae7" } tokio-metrics = "0.5" # Size-tuned profile for the iOS `rs-unified-sdk-ffi` staticlib, which diff --git a/packages/rs-dapi/src/error.rs b/packages/rs-dapi/src/error.rs index 94112649f58..047ce36ba03 100644 --- a/packages/rs-dapi/src/error.rs +++ b/packages/rs-dapi/src/error.rs @@ -839,7 +839,6 @@ impl From for DapiError { dashcore_rpc::Error::Json(e) => DapiError::InvalidData(e.to_string()), dashcore_rpc::Error::Io(e) => DapiError::Io(e), dashcore_rpc::Error::InvalidAmount(e) => DapiError::InvalidData(e.to_string()), - dashcore_rpc::Error::Secp256k1(e) => DapiError::InvalidData(e.to_string()), dashcore_rpc::Error::InvalidCookieFile => { DapiError::Unavailable("invalid cookie file".to_string()) } diff --git a/packages/rs-dpp/Cargo.toml b/packages/rs-dpp/Cargo.toml index aab137445dc..d9d074919af 100644 --- a/packages/rs-dpp/Cargo.toml +++ b/packages/rs-dpp/Cargo.toml @@ -12,7 +12,7 @@ authors = [ ] [dependencies] -dash-pkc = { git = "https://github.com/dashpay/base-sdk", rev = "e6402ced257c370a586ade9840ebbf545a4b7926", default-features = false, features = ["bls"], optional = true } +dash-pkc = { git = "https://github.com/dashpay/base-sdk", rev = "33fcc59e1868c803f9608e0e78ee8bddf3abcee7", default-features = false, features = ["bls"], optional = true } # Keep downstream lockfiles from retaining blst versions without WASM aggregate verification. # TODO: Remove this constraint once the pinned rust-dashcore dependency graph enforces a compatible blst minimum. blst = { version = "0.3.17", default-features = false, optional = true } diff --git a/packages/rs-dpp/src/address_funds/platform_address.rs b/packages/rs-dpp/src/address_funds/platform_address.rs index bedf9343b5c..3b98a513002 100644 --- a/packages/rs-dpp/src/address_funds/platform_address.rs +++ b/packages/rs-dpp/src/address_funds/platform_address.rs @@ -11,9 +11,8 @@ use bincode::{Decode, DecodeUntrusted, Encode}; use dashcore::address::Payload; use dashcore::blockdata::script::ScriptBuf; use dashcore::hashes::{sha256d, Hash}; -use dashcore::secp256k1::ecdsa::RecoverableSignature; use dashcore::secp256k1::Message; -use dashcore::signer::CompactSignature; +use dashcore::sign_message::MessageSignature; use dashcore::{Address, Network, PrivateKey, PubkeyHash, PublicKey, ScriptHash}; use platform_serialization_derive::{ PlatformDeserializeTrusted, PlatformDeserializeUntrusted, PlatformSerialize, @@ -557,15 +556,14 @@ impl PlatformAddress { while sig_idx < valid_signatures.len() && pubkey_idx < pubkeys.len() { signature_verifications += 1; - let sig = RecoverableSignature::from_compact_signature( - valid_signatures[sig_idx].as_slice(), - ) - .map_err(|e| { - ProtocolError::AddressWitnessError(format!( - "Invalid signature format: {}", - e - )) - })?; + let sig = MessageSignature::from_slice(valid_signatures[sig_idx].as_slice()) + .map(|message_signature| message_signature.signature) + .map_err(|e| { + ProtocolError::AddressWitnessError(format!( + "Invalid signature format: {}", + e + )) + })?; let pub_key = PublicKey::from_slice(&pubkeys[pubkey_idx]).map_err(|e| { ProtocolError::AddressWitnessError(format!("Invalid public key: {}", e)) diff --git a/packages/rs-dpp/src/state_transition/mod.rs b/packages/rs-dpp/src/state_transition/mod.rs index bcc1338f0ac..fcdef43b5ef 100644 --- a/packages/rs-dpp/src/state_transition/mod.rs +++ b/packages/rs-dpp/src/state_transition/mod.rs @@ -2190,7 +2190,7 @@ impl StateTransition { /// returned public key (libsecp256k1 normalises both signing paths to /// low-s form so the 64-byte `r||s` payload is bit-identical). /// 4. Serialise as a 65-byte compact recoverable signature with the - /// `compressed` prefix convention used by `CompactSignature` — i.e. + /// `compressed` prefix convention used by `MessageSignature` — i.e. /// `[recovery_id + 27 + 4, r (32) || s (32)]`. /// /// # Errors @@ -2213,7 +2213,8 @@ impl StateTransition { ) -> Result<(), ProtocolError> { use dashcore::secp256k1::ecdsa::{RecoverableSignature, RecoveryId}; use dashcore::secp256k1::Message; - use dashcore::signer::{double_sha, CompactSignature}; + use dashcore::sign_message::MessageSignature; + use dashcore::signer::double_sha; let data = self.signable_bytes()?; // Pre-image transform matches `dashcore::signer::sign`: double-SHA256 @@ -2271,7 +2272,7 @@ impl StateTransition { // always passes `true` regardless of the underlying key encoding. The // signer's `sign_ecdsa` returns the compressed `secp256k1::PublicKey`, // so this is consistent. - let compact_65 = recoverable.to_compact_signature(true); + let compact_65 = MessageSignature::new(recoverable, true).serialize(); if !self.set_signature(compact_65.to_vec().into()) { return Err(ProtocolError::InvalidVerificationWrongNumberOfElements { diff --git a/packages/rs-platform-wallet-ffi/src/persistence.rs b/packages/rs-platform-wallet-ffi/src/persistence.rs index 9334cde2789..cb97ca1c04a 100644 --- a/packages/rs-platform-wallet-ffi/src/persistence.rs +++ b/packages/rs-platform-wallet-ffi/src/persistence.rs @@ -9398,8 +9398,12 @@ mod tests { version: 1, mn_type: None, pro_tx_hash: dashcore::Txid::from_byte_array([7u8; 32]), - ip_address: 42, - port: 19999, + service_address: dashcore::sml::masternode_list_entry::MasternodeNetInfo::Legacy( + std::net::SocketAddr::new( + std::net::Ipv6Addr::from(42u128.to_le_bytes()).into(), + 19999, + ), + ), script_payout: ScriptBuf::new(), inputs_hash: [3u8; 32].into(), platform_node_id: None, diff --git a/packages/rs-platform-wallet/src/changeset/core_bridge.rs b/packages/rs-platform-wallet/src/changeset/core_bridge.rs index 7d19ffe7372..2a253194917 100644 --- a/packages/rs-platform-wallet/src/changeset/core_bridge.rs +++ b/packages/rs-platform-wallet/src/changeset/core_bridge.rs @@ -1347,6 +1347,28 @@ async fn build_core_changeset( ..CoreChangeSet::default() } } + WalletEvent::ChainTruncated { + height, + txids, + unconfirmed_txids, + restored_outpoints, + .. + } => { + // TODO(#5380): mirror the truncation to the persister. The wallet + // has already dropped these records in memory, but `CoreChangeSet` + // cannot express it: the heights are monotonic-max on merge, a + // `SweepBatch` needs a winning transaction, and nothing demotes a + // confirmed record to unconfirmed. Until it can, the stored rows + // and the stored sync height stay as they were before the fork. + tracing::warn!( + height, + removed = txids.len(), + unconfirmed = unconfirmed_txids.len(), + restored = restored_outpoints.len(), + "Chain truncation is not mirrored to the persister; stored wallet state keeps the replaced branch" + ); + CoreChangeSet::default() + } WalletEvent::SyncHeightAdvanced { height, .. } => CoreChangeSet { synced_height: Some(*height), ..CoreChangeSet::default() @@ -1474,9 +1496,11 @@ fn sent_payment_evidence(event: &WalletEvent) -> Vec<(String, SentPaymentEvidenc WalletEvent::BlockProcessed { inserted, updated, .. } => finality(inserted.iter().chain(updated.iter())), - WalletEvent::SyncHeightAdvanced { .. } | WalletEvent::ChainLockProcessed { .. } => { - Vec::new() - } + // TODO(#5380): a payment whose confirmation a truncation undid keeps + // the verdict it already has. + WalletEvent::SyncHeightAdvanced { .. } + | WalletEvent::ChainLockProcessed { .. } + | WalletEvent::ChainTruncated { .. } => Vec::new(), } } @@ -2261,6 +2285,26 @@ mod swept_transaction_projection_tests { } } + /// Pins the gap tracked in #5380: a truncation reaches the bridge but + /// produces no persistable change, least of all a sweep (it has no + /// winning transaction) or a height (heights only move up on merge). + #[tokio::test] + async fn should_persist_nothing_for_a_chain_truncation() { + let event = WalletEvent::ChainTruncated { + wallet_id: WALLET_ID, + height: WINNER_HEIGHT, + txids: vec![txid(1)], + unconfirmed_txids: vec![txid(2)], + restored_outpoints: Vec::new(), + balance: WalletCoreBalance::default(), + account_balances: BTreeMap::new(), + }; + + let cs = build_core_changeset(&test_manager(), &event).await; + + assert!(cs.is_empty(), "a truncation is not mirrored yet"); + } + #[tokio::test] async fn sweep_names_the_dead_transactions_and_nothing_else() { let cs = build_core_changeset(&test_manager(), &swept(vec![txid(1), txid(2)])).await; diff --git a/packages/rs-platform-wallet/src/masternode/record.rs b/packages/rs-platform-wallet/src/masternode/record.rs index 71589eca833..b1a8919c074 100644 --- a/packages/rs-platform-wallet/src/masternode/record.rs +++ b/packages/rs-platform-wallet/src/masternode/record.rs @@ -22,16 +22,18 @@ pub(crate) fn provider_hash_to_20(bytes: &[u8]) -> [u8; 20] { out } -/// Rebuild an `"ip:port"` string from a ProUpServTx-style little-endian -/// IPv6-mapped `u128` address + `port`, collapsing IPv4-mapped addresses -/// to V4 so a normal masternode renders as `"1.2.3.4:port"`. -pub fn provider_ip_port(ip_address: u128, port: u16) -> String { - let v6 = std::net::Ipv6Addr::from(ip_address.to_le_bytes()); - let ip = v6 - .to_ipv4_mapped() - .map(std::net::IpAddr::V4) - .unwrap_or(std::net::IpAddr::V6(v6)); - format!("{}:{}", ip, port) +/// Render a ProUpServTx service address as `"ip:port"`, collapsing an +/// IPv4-mapped IPv6 address to V4 so a normal masternode renders as +/// `"1.2.3.4:port"`. +pub fn provider_ip_port(address: std::net::SocketAddr) -> String { + let ip = match address.ip() { + std::net::IpAddr::V6(v6) => v6 + .to_ipv4_mapped() + .map(std::net::IpAddr::V4) + .unwrap_or(std::net::IpAddr::V6(v6)), + v4 => v4, + }; + format!("{}:{}", ip, address.port()) } /// Provider (masternode) special-transaction payload fields lifted for @@ -56,12 +58,20 @@ pub struct ProviderPayloadFields { /// (ProUpServTx) payload fields from a transaction for display. Returns /// all-`None` for any other transaction. Pure; the only allocation is /// the returned service-address `String`. +/// +/// TODO(protx-v3-wallet-fields): a version-3 payload is read through its +/// pre-v3 fields only. Its service address is the primary routable IPv4/IPv6 +/// entry (`None` when it advertises none); the platform ports carried in the +/// extended address map and the DIP-0026 payout shares are not surfaced. pub fn provider_payload_fields(tx: &dashcore::Transaction) -> ProviderPayloadFields { use dashcore::transaction::TransactionPayload; match &tx.special_transaction_payload { Some(TransactionPayload::ProviderRegistrationPayloadType(p)) => ProviderPayloadFields { - service_address: Some(p.service_address.to_string()), + service_address: p + .service_address + .primary_service_address() + .map(|address| address.to_string()), pro_tx_hash: None, collateral: Some(( provider_hash_to_32(p.collateral_outpoint.txid.as_ref()), @@ -71,7 +81,10 @@ pub fn provider_payload_fields(tx: &dashcore::Transaction) -> ProviderPayloadFie voting_key_hash: Some(provider_hash_to_20(p.voting_key_hash.as_ref())), }, Some(TransactionPayload::ProviderUpdateServicePayloadType(p)) => ProviderPayloadFields { - service_address: Some(provider_ip_port(p.ip_address, p.port)), + service_address: p + .service_address + .primary_service_address() + .map(provider_ip_port), pro_tx_hash: Some(provider_hash_to_32(p.pro_tx_hash.as_ref())), ..Default::default() }, @@ -333,7 +346,10 @@ where // Registration seeds the service address and voting key; // treat both as updates observed at this height. if agg.service_address.is_none() || height >= agg.service_height { - agg.service_address = Some(p.service_address.to_string()); + agg.service_address = p + .service_address + .primary_service_address() + .map(|address| address.to_string()); agg.platform_http_port = p.platform_http_port; agg.service_height = height; } @@ -360,7 +376,10 @@ where } Some(TransactionPayload::ProviderUpdateServicePayloadType(p)) => { if agg.service_address.is_none() || height >= agg.service_height { - agg.service_address = Some(provider_ip_port(p.ip_address, p.port)); + agg.service_address = p + .service_address + .primary_service_address() + .map(provider_ip_port); agg.platform_http_port = p.platform_http_port; agg.service_height = height; } @@ -436,6 +455,7 @@ where #[cfg(test)] mod tests { use super::*; + use dashcore::sml::masternode_list_entry::MasternodeNetInfo; /// ProRegTx provider payload is lifted from the DIP-3 special-tx /// body for the UI. Fixture is the testnet @@ -757,8 +777,10 @@ mod tests { version: 1, mn_type: None, pro_tx_hash, - ip_address: 42, - port, + service_address: MasternodeNetInfo::Legacy(std::net::SocketAddr::new( + std::net::Ipv6Addr::from(42u128.to_le_bytes()).into(), + port, + )), script_payout: dashcore::ScriptBuf::new(), inputs_hash: [inputs; 32].into(), platform_node_id: None, @@ -831,8 +853,10 @@ mod tests { version: 2, mn_type: Some(1), // HighPerformance (evonode) pro_tx_hash, - ip_address: 42, - port: 19999, + service_address: MasternodeNetInfo::Legacy(std::net::SocketAddr::new( + std::net::Ipv6Addr::from(42u128.to_le_bytes()).into(), + 19999, + )), script_payout: dashcore::ScriptBuf::new(), inputs_hash: [7u8; 32].into(), platform_node_id: None, diff --git a/packages/rs-platform-wallet/src/masternode/tracked.rs b/packages/rs-platform-wallet/src/masternode/tracked.rs index 66e179015b5..7302a3df1d5 100644 --- a/packages/rs-platform-wallet/src/masternode/tracked.rs +++ b/packages/rs-platform-wallet/src/masternode/tracked.rs @@ -1090,7 +1090,10 @@ pub fn registration_from_transaction( voting_key_hash, operator_public_key: *operator, payout_script: p.script_payout.as_bytes().to_vec(), - service_address: Some(p.service_address.to_string()), + service_address: p + .service_address + .primary_service_address() + .map(|address| address.to_string()), is_evonode: p.masternode_type == ProviderMasternodeType::HighPerformance, platform_node_id: p.platform_node_id.map(|id| id.to_byte_array()), platform_http_port: p.platform_http_port, diff --git a/packages/rs-platform-wallet/src/masternode/update_service.rs b/packages/rs-platform-wallet/src/masternode/update_service.rs index 7e6f1719d34..397ad8abf05 100644 --- a/packages/rs-platform-wallet/src/masternode/update_service.rs +++ b/packages/rs-platform-wallet/src/masternode/update_service.rs @@ -30,7 +30,6 @@ use dpp::bls::SecretKey as BlsSecretKey; use key_wallet::wallet::managed_wallet_info::transaction_builder::{ BuilderError, TransactionBuilder, TransactionSigner, }; -use std::net::{IpAddr, SocketAddr}; use zeroize::Zeroizing; use super::list::MasternodeListSummary; @@ -284,8 +283,6 @@ pub(crate) fn prepare_update_service_placeholder( .to_string(), ) })?; - let (ip_address, port) = service_payload_fields(service); - // The platform triplet is serialized only when mn_type is HighPerformance, // so the evonode/regular split must be explicit here — a missing mn_type // would silently drop the platform fields on the wire. @@ -330,8 +327,7 @@ pub(crate) fn prepare_update_service_placeholder( Ok(ProviderUpdateServicePayload::new( mn_type, Txid::from_byte_array(entry.pro_tx_hash), - ip_address, - port, + service, script_payout, InputsHash::all_zeros(), platform_node_id, @@ -341,17 +337,6 @@ pub(crate) fn prepare_update_service_placeholder( )) } -/// A service socket address as the payload encodes it: the IPv6 (or -/// IPv4-mapped-IPv6) octets as a little-endian `u128`, and the port in host -/// order (the payload serializer byte-swaps it on the wire). -pub(crate) fn service_payload_fields(service: SocketAddr) -> (u128, u16) { - let octets = match service.ip() { - IpAddr::V4(v4) => v4.to_ipv6_mapped().octets(), - IpAddr::V6(v6) => v6.octets(), - }; - (u128::from_le_bytes(octets), service.port()) -} - /// The funding builder for a ProUpServTx: the placeholder payload, the /// finalizer that commits it to the selected inputs and signs it, and one /// zero-value data output. @@ -453,6 +438,7 @@ mod tests { use crate::test_support::{ funded_wallet_manager, funded_wallet_manager_with_outputs, WalletSigner, }; + use dashcore::sml::masternode_list_entry::MasternodeNetInfo; use dpp::bls::{PublicKey as BlsPublicKey, Signature as BlsSignature}; use key_wallet::account::StandardAccountType; use std::sync::{Arc, Mutex}; @@ -518,16 +504,17 @@ mod tests { prepare_update_service_placeholder(&entry, None, ScriptBuf::new()).expect("placeholder") } - /// The IPv4-mapped little-endian encoding, pinned against the known - /// testnet ProUpServTx vector in dashcore's own payload tests - /// (52.36.64.148:19999). + /// The placeholder re-asserts the list entry's address as a pre-v3 + /// (legacy) entry; its wire encoding is dashcore's. #[test] - fn service_fields_match_the_known_testnet_vector() { - let service: SocketAddr = "52.36.64.148:19999".parse().expect("socket address"); - let (ip_address, port) = service_payload_fields(service); - let expected: [u8; 16] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 52, 36, 64, 148]; - assert_eq!(ip_address.to_le_bytes(), expected); - assert_eq!(port, 19999); + fn should_carry_the_entry_address_as_a_legacy_entry() { + let entry = operator_entry(0x44, false); + let payload = prepare_update_service_placeholder(&entry, None, ScriptBuf::new()) + .expect("placeholder"); + assert_eq!( + payload.service_address, + MasternodeNetInfo::Legacy(entry.service_address.expect("entry address")), + ); } #[test] @@ -663,7 +650,9 @@ mod tests { txid: Txid::all_zeros(), vout: 0, }, - service_address: "10.0.0.1:9999".parse().expect("socket address"), + service_address: MasternodeNetInfo::Legacy( + "10.0.0.1:9999".parse().expect("socket address"), + ), owner_key_hash: PubkeyHash::from_byte_array([1; 20]), operator_public_key: BLSPublicKey::from([2; 48]), voting_key_hash: PubkeyHash::from_byte_array([3; 20]), @@ -674,6 +663,8 @@ mod tests { platform_node_id: None, platform_p2p_port: None, platform_http_port: None, + payouts: None, + shared_collateral: None, }, )), } diff --git a/packages/rs-platform-wallet/src/wallet/core/balance_handler.rs b/packages/rs-platform-wallet/src/wallet/core/balance_handler.rs index 68b0b5d555f..b6bda3d9645 100644 --- a/packages/rs-platform-wallet/src/wallet/core/balance_handler.rs +++ b/packages/rs-platform-wallet/src/wallet/core/balance_handler.rs @@ -70,6 +70,11 @@ impl EventHandler for BalanceUpdateHandler { // balance-bearing event happened to arrive. | WalletEvent::TransactionsSwept { wallet_id, balance, .. + } + // A truncation drops what a replaced branch recorded, so it can + // lower or raise the balance; its snapshot is post-truncation. + | WalletEvent::ChainTruncated { + wallet_id, balance, .. } => (wallet_id, balance), // No balance on SyncHeightAdvanced — checkpoint advance only. WalletEvent::SyncHeightAdvanced { .. } => return, diff --git a/packages/rs-platform-wallet/src/wallet/core/spend_observer.rs b/packages/rs-platform-wallet/src/wallet/core/spend_observer.rs index 8f239a22fe5..db509798de1 100644 --- a/packages/rs-platform-wallet/src/wallet/core/spend_observer.rs +++ b/packages/rs-platform-wallet/src/wallet/core/spend_observer.rs @@ -137,7 +137,8 @@ fn observing_wallet(event: &WalletEvent) -> Option<&WalletId> { WalletEvent::TransactionInstantLocked { .. } | WalletEvent::ChainLockProcessed { .. } | WalletEvent::SyncHeightAdvanced { .. } - | WalletEvent::TransactionsSwept { .. } => None, + | WalletEvent::TransactionsSwept { .. } + | WalletEvent::ChainTruncated { .. } => None, } } @@ -183,6 +184,11 @@ pub(crate) fn observed_spends(event: &WalletEvent) -> Vec { // the spend from any event, which is a gap this handler cannot close // without the loser's inputs travelling on the event. WalletEvent::TransactionsSwept { .. } => Vec::new(), + // A truncation reports coins that became unspent again + // (`restored_outpoints`), the opposite of a spend. + // TODO(#5380): a fence already retired by a spend the truncation + // undid is not re-armed. + WalletEvent::ChainTruncated { .. } => Vec::new(), } } @@ -360,4 +366,22 @@ mod tests { "a bare watermark advance must not even resolve a wallet to act on" ); } + + /// A truncation hands coins back; the outpoints it names became unspent, + /// so reading them as spends would retire fences for coins that are free. + #[test] + fn should_report_no_spend_for_a_chain_truncation() { + let event = WalletEvent::ChainTruncated { + wallet_id: WALLET_ID, + height: 900_000, + txids: vec![Txid::from_byte_array([0x31; 32])], + unconfirmed_txids: Vec::new(), + restored_outpoints: vec![outpoint(1), outpoint(2)], + balance: WalletCoreBalance::default(), + account_balances: std::collections::BTreeMap::new(), + }; + + assert!(observed_spends(&event).is_empty()); + assert!(observing_wallet(&event).is_none()); + } } diff --git a/packages/rs-platform-wallet/src/wallet/identity/network/payment_handler.rs b/packages/rs-platform-wallet/src/wallet/identity/network/payment_handler.rs index a921326330e..fae2098da34 100644 --- a/packages/rs-platform-wallet/src/wallet/identity/network/payment_handler.rs +++ b/packages/rs-platform-wallet/src/wallet/identity/network/payment_handler.rs @@ -269,8 +269,10 @@ fn dashpay_payment_records(event: &WalletEvent) -> Vec<&TransactionRecord> { // by the wallet-event adapter instead, on the lossless channel and // on the same store round as the rows that justify the verdict // (see the module docs). + // `ChainTruncated` carries txids of dropped records, not records. WalletEvent::TransactionInstantLocked { .. } | WalletEvent::TransactionsSwept { .. } + | WalletEvent::ChainTruncated { .. } | WalletEvent::SyncHeightAdvanced { .. } | WalletEvent::ChainLockProcessed { .. } => Vec::new(), } @@ -296,6 +298,7 @@ fn drives_payment_hooks(event: &WalletEvent) -> bool { // adapter's own store round. WalletEvent::TransactionInstantLocked { .. } | WalletEvent::TransactionsSwept { .. } + | WalletEvent::ChainTruncated { .. } | WalletEvent::SyncHeightAdvanced { .. } | WalletEvent::ChainLockProcessed { .. } => false, } @@ -487,6 +490,23 @@ mod tests { assert!(!drives_payment_hooks(&event)); } + /// `ChainTruncated` names dropped transactions by txid; it carries no + /// record an incoming payment could be recovered from. + #[test] + fn should_not_drive_payment_hooks_for_a_chain_truncation() { + let event = WalletEvent::ChainTruncated { + wallet_id: [0u8; 32], + height: 42, + txids: vec![dashcore::Txid::from([0x31; 32])], + unconfirmed_txids: vec![dashcore::Txid::from([0x32; 32])], + restored_outpoints: Vec::new(), + balance: WalletCoreBalance::default(), + account_balances: std::collections::BTreeMap::new(), + }; + assert!(dashpay_payment_records(&event).is_empty()); + assert!(!drives_payment_hooks(&event)); + } + /// A `BlockProcessed` that changed no records (syncing past an empty /// block) has no payment work, so it must not spawn a hook task. Pins /// the spawn-skip that keeps initial sync from taking the wallet-manager diff --git a/packages/rs-platform-wallet/src/wallet/masternode_withdrawal.rs b/packages/rs-platform-wallet/src/wallet/masternode_withdrawal.rs index 85e99301f1a..94f16c80ffa 100644 --- a/packages/rs-platform-wallet/src/wallet/masternode_withdrawal.rs +++ b/packages/rs-platform-wallet/src/wallet/masternode_withdrawal.rs @@ -31,7 +31,7 @@ use async_trait::async_trait; use dashcore::hashes::{hash160, sha256d, Hash}; use dashcore::secp256k1::ecdsa::{RecoverableSignature, RecoveryId}; use dashcore::secp256k1::Message; -use dashcore::signer::CompactSignature; +use dashcore::sign_message::MessageSignature; use dashcore::{Address as DashAddress, AddressType, Network, ScriptBuf}; use dpp::address_funds::AddressWitness; use dpp::identity::accessors::IdentityGettersV0; @@ -716,7 +716,10 @@ where ) })?; - Ok(recoverable.to_compact_signature(true).to_vec().into()) + Ok(MessageSignature::new(recoverable, true) + .serialize() + .to_vec() + .into()) } async fn sign_create_witness( diff --git a/packages/rs-platform-wallet/src/wallet/platform_wallet_traits.rs b/packages/rs-platform-wallet/src/wallet/platform_wallet_traits.rs index 653465ca35e..d5fdd12b64e 100644 --- a/packages/rs-platform-wallet/src/wallet/platform_wallet_traits.rs +++ b/packages/rs-platform-wallet/src/wallet/platform_wallet_traits.rs @@ -19,7 +19,7 @@ use key_wallet::transaction_checking::TransactionContext; use key_wallet::transaction_checking::WalletTransactionChecker; use key_wallet::wallet::managed_wallet_info::managed_account_operations::ManagedAccountOperations; use key_wallet::wallet::managed_wallet_info::wallet_info_interface::{ - ApplyChainLockOutcome, WalletInfoInterface, + ApplyChainLockOutcome, Truncation, WalletInfoInterface, }; use key_wallet::wallet::managed_wallet_info::TransactionRecord; use key_wallet::{Network, Utxo, Wallet, WalletCoreBalance}; @@ -161,6 +161,10 @@ impl WalletInfoInterface for PlatformWalletInfo { self.core_wallet.update_synced_height(current_height); } + fn truncate_above(&mut self, height: CoreBlockHeight) -> Result { + self.core_wallet.truncate_above(height) + } + fn matured_coinbase_records( &self, old_height: CoreBlockHeight, diff --git a/packages/rs-unified-sdk-jni/src/persistence.rs b/packages/rs-unified-sdk-jni/src/persistence.rs index fea73af90dc..95d71c125b3 100644 --- a/packages/rs-unified-sdk-jni/src/persistence.rs +++ b/packages/rs-unified-sdk-jni/src/persistence.rs @@ -4671,8 +4671,12 @@ mod tests { version: 1, mn_type: None, pro_tx_hash: Txid::from_byte_array([7u8; 32]), - ip_address: 42, - port: 19_999, + service_address: dashcore::sml::masternode_list_entry::MasternodeNetInfo::Legacy( + std::net::SocketAddr::new( + std::net::Ipv6Addr::from(42u128.to_le_bytes()).into(), + 19_999, + ), + ), script_payout: ScriptBuf::new(), inputs_hash: [3u8; 32].into(), platform_node_id: None, From 5d8438f695f33fc1f406be72777f2add7296df2c Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Mon, 12 Oct 2026 13:04:04 +0000 Subject: [PATCH 22/22] docs(platform-wallet): mark the unvalidated fork rewind in dash-spv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dash-spv rewinds its storage and the wallets to a fork a peer announces before the competing branch is validated beyond its own header; only the best ChainLock bounds it. The fix belongs upstream and is tracked in dashpay/rust-dashcore#1103, so leave a TODO where the client is built. 🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PSi8cX229kW2fmLxKW6ir4 --- packages/rs-platform-wallet/src/spv/runtime.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/rs-platform-wallet/src/spv/runtime.rs b/packages/rs-platform-wallet/src/spv/runtime.rs index 405cddfbf3e..73d4b7902cc 100644 --- a/packages/rs-platform-wallet/src/spv/runtime.rs +++ b/packages/rs-platform-wallet/src/spv/runtime.rs @@ -162,6 +162,12 @@ impl SpvRuntime { let retained_config = config.clone(); + // TODO(dashpay/rust-dashcore#1103): dash-spv follows a fork as soon as a + // peer sends a header whose parent is a stored block below the tip, and + // rewinds its storage and the wallets to that block before the competing + // branch is checked against the expected difficulty or chain work. Only + // the best ChainLock bounds the rewind. Pin the upstream fix when it + // lands: https://github.com/dashpay/rust-dashcore/issues/1103 let spv_client = DashSpvClient::new( config, network_manager,

pjqYYXQoLV-cf@WKhI@ba9vse3GcVP?h>N(*W`&}IrLOx^%{6>1nk(>Sa zd2yHfVs&d5aCbf;bxn(9&srTAJ)u-FoJYMzg+c8Y$*5dI{`bT9{53-#NA#mwBhMy2 ztkmrNFWN&M9D@P20{nKvRD1fIg1-a;{{Y0U<}>|D*AZ87HXa1)Evg@OK&U?Gu|x3V z24~Fjh$J<57(6*akUq<1*ADkBWWT;x$_9-(jerbSy8E;P=%8&z%1k}2ODK%W-}4$W zC2OT9bU|*Win{D?oR>jIH&j$*KoO?4n{Wrpt-XSC2f1xwtxg+Y+IkXVACT&9( z_=&BWbJUpLfxy+R)T7bVDDrZ$pm9d|kqi)T{h^GbFfRGFFl=9F0E3Tj1$|`03<<_B zfWyqw8Vb6JgD~F~kVdT<(5MCM#IUrTN%jdC=l2PFsjzc2WzF`Fei1zx&+|=W?~>%8 z`KBy_)jU2-*5OlwpuVF?yiL^#KQ1S6z@+$(?<$WxT>$xkP z0t!q2tw6axo$}$_aR92Z1uX!)Yy&n=5LChW21?BP_O0y&-gie5h{6ETHyY0eo<j>dPIrY?Aq{K7$8&2keUZa`UR3CKR$S z%8~19e{#w-jqTk?_2Rpa&lR}H`tUJuv4T0blo|mXNt3G$?srHysac7p=nP{K#hp|Z z+J_4IbOV)O(P)u6N{!&Brc12(W!k%l-BXmu_ehzVqzo->FD$tE>tG1k0Z4tX(tV1X z_*MSK8DtL`)|4OgcQX~Ghw4tg;!sx}&bJOlg|Mg*U0svX7_5&QGH@dvaZPwdq4L{2 zN52%|ep>-^&Hh>az4w3#KBKmdvHcUwI>Z{HCMYXHriV6%jhqLG_^i+gh(HNYc=4Qy z@|G2XVE2(z+5kLsfK4zWA`Zz^@7aq?HWfw1EbU~p-EY#0zta(U(~4U5 zODZ=f#HR7^tchb>J}=iVeu_1{2@kW6Yr`14c%i^q$!cT zFiV#-7@&sDI;U~sTO55aBX5JS`8q63He@a9VFTiZKRU(i@X4p7M${yMOmxrWpu0q_ zMCey_MV-WK)b7B=PYukjXbsYbnA3*r={gEqiEiS@qu1P`bRM9yV5ep^$z?DXQ`~k( zI&m&C;J$GF0l+^XCt*{fAcCo=0kvsZmM5>iw7wDQl5)Acp|Ks-k%*-Om<0d}Qhh?_ zaf09k*Ibtr4J72D3*0d1s2-goUAAL+?RSi7R_%@;no`~;lx($T73@JChHC0g=BfDb zc`XFrW*$fWJXN5fp&rIwgYO1tRQzi&SH99Ng#bRO*^QyCLFHwsG$vM#;fUGBzu$t= zb5BQuCoG{QYZTBd>qATRAhRijI_08dwdlhw^#w=nh+hw*vz5qr zhtwj6cg{gO#l?QnQq(Arqq)nl>Tql>=A4?F9m^$bw+Hlq`Qg=vkQPdEh>fQC2^~=r zz4ZwrPKn}}bi^KYcrRLV;HeH9wt=ZZLrCRqSK>(ZpZoXJD0v&)MZ4Gem>ZBj7~IqQ zP=|33Zk?J{oEO`aeBd({Lt>+b)jyaS$-2{84&8H|BHebMS$@-WiaTFL6tX`)6z(&Hp_9{pk`nuNK%_f7z zQs1qv3%UX9BA8Vg#hugW#=P7Jyb z!%gxx=GKY{+zuavH@sP1p9``HHSE;nh|S0(WM74Av@|qXVz>;tEs|P^mAh}A;993>%n61Rt zsN!pEq1mlo&1*_I&-&G$sdM^|A+%{ABZ8*1Toca08~<@!Reg}FAdZ0V+b5hH&7D%{ zYt{X5k7FFurgTW(3Niw*4lLW$YF4RDpu~Y$iXLIN^)((!*OzOJLUSgjT%S<^v__6cq_=b}UVqZ%ha7#?1uU`{`4TuR(q(7I5%j8>|)M?}QVl z!qbl37)!G&O!)ZP#}*@31HN z#8-N=f&a4{IlimS`*y>Y(SL&aQ@AVg6lba(0riBLg)b=jWZR7YLV)A+V-rIW!1WsZ zvMv|l0pD`1Q++R_?h(^s+ZPU@)zxR4*nyX~Vj3-b&R!Ol|B<4h09}bCr%<{Ww^01# zFtbOEK2Bw2XRcl_a2xn@wJ`HLT5A(0+eYnmz$%8WmM(eJ`HEHPsm{K0qc%)^{h2N$ z!|>K zw?1U6rgatZlvILH9yo8+xL>3kV;u_OgK&~O0WDwOgCz{2!w<5iqFSu`!db4>dzG{az zk|h~dURg(v0vX(NGfQ}Zoju;UO5tX7D+F zu1>-XePt>%PK-YM7$|4d_AB6DeiQxa1zX)8SuIg1--`Pkr&OG*5u1p=NP3&PP2tq1 z%5I{w^`XcUU52asHRp_>6Xd$bH=;?SdtL(AY;h0JA`~pD20(Uu*zU!fgFBChE;g** z$a|1#bo=5AGM;grHh~%$o?lbha68G*KB+iZNN38XqS0yaO=ZI5qGqK<&DipCSeq&r%;o|%R79_&C_6@vur)!=x_u@ZBEYk8|zew)*j! z%uBL*o(p4%a=%821p3SQAR=^6 z8PMZACL(Dn&K^iaP>8hPvdUHwz*bi6W!2dD*EZXxm_5}rGedUp017Djw*A=EJU9VY}`j6CyjCXZUonGgf zuvmB#Z(<6yNM*{YGokErP@}jl*p+%fqgZeG=egUV;LrRR&o0luCQxTbL=k2`@C6z7 zTsxT9S)k?u>K{b=2Xhs5srFr{j_!{c9)hQz5dF}5a_=Z%8QAJgCwk=Wxp&A$9+`vH zPzyv0(QT&;47O2Pg7OWNS6S1tn^wv5DoKiQecCE%3Q609892_LF|NpkTs1uGfRf2xK*~4 zY_c_&E-4hggaMF}V<-znKyu zDA@<2@0`GuK|URmw6$R3O#GNene@&P9z?+ZpgM>3MJ{QkETFbnDVYgO^%RJ&2lnzv z&Cx(K9CSx8)G+70dR>vmShAz@ z7E>yWj7m_Q&Pa1x=^zip(p?+p5=?{Su`6-cagM8cy~67*GzwY8tA5D~C1?MwJ_KZQ zzJt7i6;{2adXjwmkQ5i}>9rsSy9;Vk37Q1b?J6M-m^=62w?x-nDzsr(=5JLaQna?J zO7&}rW^cEizQ;tEs@x1T^aAHV7e;flYWmM*rrtn%&?9Y%xOQoLsD#)7xMC_hFz*tqs`v%7Rby z^;R`8D+C~>iLVf@sK3BWC2CjhF)+LTky?e$&E)Y*39Z7EC<&K^cG*FJjP#Xg!Vi z;uE?9RRDD%3_EG#i%cj(VQI0sw}8fB~fcUp3I# z%hA>0zjeGGD;=D%R>pU}zTb5(TpG&ph>-)Mobeps>*izN^n+5*cjF>$#ptecs4bx8 zJfAOJ;n-bOB`bhYD6iSBxZ>+dHMOc0-fI7!uYX}gJ7;32Z$17rTUPK6;6=SRZXY#H z6p!FT-)c+HCJB841mGbtX^$ z28}LTK16OT5ueYWUc%P8AP-_1U#^F)_1A60Lt-2?pS24=Y2s&m%ot8vx~m*9TO1sD zxr}s!6kfw_KKjO9B~W^Cy$k^TjF@3JokELUPx{^iZiqmN+d=`H)abD#XwnSBh@a-T zgV`jWMTo3fHT8b_o`nITg8LMF?hS|rpcgs{FlKkgPz3Uy8dwP0x4sRIdv5OcJ3I@;|}od#vih5t_n6RYisQ6 zRB>u7HjR8ty53yzMi97ba1EfH_3Yx5+EVw{Q=ESQ&WFxfH|o>q3p~-T8@qDn3=OeC_|Ut7 z!qW)p^|>GP{p-UJ+2aX1HqS=z*Wps4&vVR(a|;OX!=g51H{0u>&dP~gqya#;Mes1X|J`1o?m0tk#|S+ z-w2=m3H-vF&=5n2M0Nw)XKeepPPh(LjkOB*@9&+?JxXT?vlI~xBGb``Z^dP_L zf(d@+!Ep%?hCIzM-GOX$$bvgC^huCj`E28jxqN)*2|V`7zW?Bi5$Og53IU*Rwp>4k z7@=-mfmsGAV)b751Pe2$#{M-(Q4o%Br+*?y$HIKjRV^Rw3FN^MTQ+ZiR0q5wc|a~~ z+-|48Jy|gmc-I;oF>jIPW1IJn-6!B6wrs-!XWIwYK8mf<*?PDi0a3|4n|_{T@jT6c zYy4UCufTiaCa}L%^w0!qheIX{q&^IQp}Cy__S6@=^atds)LTc|sva3`;-gA^ zJ7`nT*9M!F1Bk3j{dh`|rjI^AdAi;3V$6ICnv0CIA*3D#CTD!0QxybhwyvKg+R9O9(_C`kVy#R z4;c^1cV-(s;Ba3D_u6(0w@v{n;JBqUIHy9>RvOE@OuuawU9P|4U$7xJZjb$3vC`a= zP~S&D#F=)&@(odRDcxxUa>I?SGomof?7c3(2wycyl;7^EOj3Y0G=wXL^|L9})MI>G zNYEQ0)2Q?vU=YDX0x7#SiM8p23y}w8w@iW3^=a>ssoMcTMJSOQj<~_M`z;~zxc+Lf z4@juLgkQrRPN^@=46*JHX%sQF&1Naujj%l}Jn=@76DPJvgB1Vd{HWDk!bc3i-E7V( zdBSBZoer@>^vJ@#m!+lRZVDCgsAJ7aRZbV8_Zm-te|P##~sXDL$H(L z*A$GtW}59AB1+{ZrXYO#h{aWjn^F6?>hgfzeO#VwNPX>{-d^4wMy$3Zq@gFJnw_U(!`yv+Uf z21k!?`VO>1ZU6?|ej%aCRlaG>+=NJIJx^O`>2FG zT`!yWGwR;zqjG%I!}F5yYR!%MP!E4_;jcW) zTL6NJAv0RnZw$l>2hX8g@bJD|6?v+@I$!KxZm(#o;I!$SnrSeI2)Sh0gGmO&lGiXF zyBRTq#l-Eyc&&-$4J&T7hYI+z02||d2?`1E3h^_BtJZyac|G5+?%=P1>fmsMnn3X< zg*5;N)`Cb6w(ElZ-$;o)YA7ta$hsVI)7>o-4-<>(?2#W55oHz-5rM2_7A**4Wk38r zE{z}0B3HM~{E672J&?8j5D*t(rqN=NBY(|d9IF(_3q5%W3qOwtaiE>}z|LplS)I#{ zbS1y`C^i^UX!>KrATrt|gX z`&hg=c?@r(KQ9gL_cu)lWdtXHY2qc6{Rts_7R*IA$B=MMRvmCtl31^?Ozhxsf|_he z_y-V`lO{mHIROyUox(dSL8PquhFzz$#svyRZC{^uzRzi#m59TXQ>bKm&}&Zj`sS9l z&k7(D0ku%3o=jnDU@@d8-@5FfI+QFT%A~K(K<$K}liBaKNi5tTpa1h6c*sGPfd7Sw zWqMc(dIZweP7V_0a#Mw6mdg1Vai2TTpL65rG?Z4>$(mGzU2NB8;$^ zL!zALRRt8p8J#c+-Y3!wrG5Brd|-Zj;B4S$WhQWP@Mk(`C@Bj5BUhfp@?X1P8Tu0# zI|WPXq0*fP`x5z*3w(6Z0J%-e z%}dlph}908#dMyo3QDG5UEvUv4_w z{`v|TYD>lk-TLwJBbiMWbaCb#K_R$WiGSezx_F4Q{#Hu3c)E;8WL_FV>UumVV7Z?o z1j*w`S;#R#91n|FM}wD_Er_gEhnl77{?iJ>N?$S#4hKE7?6I5Q3xO2FFw9JJ0iIuU z-5!%9#{n!A0jWEOMX_hGXDUq+zCB3fTger|vnaIuF~S|YKVoxlqX0m6yr*>;~nKJKMOPMu@@ z133Grl0`G{JCdk7s39WqM8_&n6RNb<5oB8Jj&aj2kctfM?+*CfVb?*Y@di2Lc0Nb9 zoB7BaBS9<198g6-^$m3nBs|mConxe~K`{6P#Tucp13o6po=G-7SMH|eq7OtOxrD8! z;Yd4~2px6VPyt4fC_})kD_b8B>DSXK9K=j{0p8qVzb(u`G{QVfI??d#tC?=;8@bQv zE=w&8R2f!5VH5{=i`<6P4k6=@xM4zSyq}ZAUI7k=p^a;{z%-S<)aBhbXcw9-nm(?J z$?fTPW^{FXWo;pi7}vg4(I(QGr)@|Dl4_&GfC!V;pW+^lTi-2v#x-`v#^9%uVQaH$ z;2W}JakOBj_&aKx(Srh)_{Jl#)jCgK>4NHy!Cyx{1Gm1icUXWsNK$66X6J5kPbA$S zR_#phU@)&xFn*DY;Wc#w++19bd6Z@l$wy+bH5fuFE<{ab_}c#033SvqLs~h|bL(*+ zIGhAj{SihGMh%iijqZ&T00{41cmMa<R8qh<6BuNY{g77gfmI2(q zA)f(Dv+%8UrIVV^hXn2cEI!9gq@eX}+M&1E;M#%xt<~R0^ABS`|JXFhmYoBC6UC&2 zuW|MA{HtBjRd25gFk1GsyEVH$;D0Lw_F)E#`-JFZQ2Ja#X+e7y*DpcyrQZNRXuc&R z3a;0@*Z;Px#nh#g;M-f=#g(p~$$spPf69qNyxrFROL1;l^El-?(c42BcZ;g}9p>qLF~PIw-`~N`8@BEcp;22HgZ`ltOMY z38)OG-vmKMy zTL>D@^mFloqG9)@8gX}{{;q)CwE96^TFru0@IK=k$cQS-Rsa~@op`PlDma3E()Cc@ z16h{yaV2oUi(@Z7+>S-RSG_l*R5~n2z$52Wavk|ZmpQ`Y4o_1u6BX`f-F1GNm~G>Z zdoFhz3E=JhWU}>fUGIGJa4v9UWCUuwM!A-R(}5SKzaAWvGR112@j@GyR_{4ya67f! zMQ*Q3HbX5U(yMfcIZvTCgH*uT_@!@eNrKMN1Z=;|fpsJk3hmXB|IKom!^o6kIz8@p zAx+%Zg%z~_3~Z=F+#e_gS26)n}BL z`j^8MgDLTEx^}%i;)-}K&Jg&12*jGiJ0KCNMwWl-!w`$#ICnOgTP6l#H`AFjWI;mb z`>6vhC~>;F5erd|E|){-a}0N|NqMT;f$zqueh2vH=wTYp5jAwSje~u2`rNw-&z=oC zdloxx<$-W(Yi-LP-?o}hC*b|_%-iH*eev16{qFNkyuF1zu|bO`mIKlG#2B*pm~5za zAK_i_m21v8ntp{7>*jU>?l%zjqjNzn@@CS4EW8v0eoKFIs=^A(?aj=*XLN>ozoHx? z7e z8_Cg>Gb|=PP^5Lc@U?Ju#5~YfVJ2LnBPQh`lo(P+^?~6zfDSF+GHf5fk6>?Vm^YPC zBnz~eJ=eNLxze5*8wzil|K$}+pEkXP>tz4nHE3@i&y|ldjBA83h#MgS$fU&^`fiEb z2&Mvo4<9k;1QJCnst)|%TL8}G#EA@njZmkCXLNaKDG^Ks2Zt$ABC<6bNS(L_Qr(5& zo@NS97`ihdj6%hti7&=~pc%W$4+AzO{g5pKt-zp*1_%og@i0Z=k6h8GZ5;x&7ulI8ZNH5r zbd$viynGDY68&X4m>sPfC$Pksd~aZmV@>-+k>0pFD~;be}BQ(}*olH@73|61LG`RK zJ`5Lc($h=)z!C6)c8{3pCU+aVre*)$9ska|=Ivn7U~w0prVZa-t!?#^*V~P=$K8F#-R&rKpZ=6M zdcAKtw{>aZ%S%)58G&Q)K8woz2iFvmm%M&@=-jHif>3A*z)0d(ggpspS znF^9?rV%(w1B<0_t%F9SQJ)JX&RL$cDVD$6PbHV#=A~QDk7IL+pg>U+gU?y~ILlR1 zE*pM#+(+6C3GY2ZJj52(QcOc>i{Kb5JyO1!20~p&vYtbg(SkGy5L%}YME8{*v5_Qg|DL?PIUpI9t>9xt;&M9 zqY=-p?zH5M>&%Tq0aUmkUP!=2e2?u5LY~sf7~LW6CMhYAUsG)*@%n~!y6Cg zQU?wD1JS@OUwjJp-N--a#dn&*()Eoca468MA-pQB>6WVfsPR0~yi3V*bCsIxhm8ive{h0P|#D$v=(Gr2IZ5oPl$pKTjN|%_&A1{NJOY~7qcHYu%j4DLSLC2(=SK}e?u^TSQem0W@_G5EFND z+y;8pu5O~R;pS>4Enm_POg-rt)VRN5f05H21Fda)4b9^5d29V7(_DXzvM*xG&0ep{ zQ=%z}CV}FYy-L!i4{~L(sI=NNBaLzfV_q!w`M*hV=wk{Yg|{Pz6|tM48ez|$*Khl_ zs`;rmmR`z1fq$8eHlSn=_V5qKr>=|3Xpp!u_P5hT^qYoop$EK4v|KLR_^9=^Jx;Ud zW1dkQ6pIB3t3QvhiA0BCiOZsHQW+0~bL@{EhLtOFtK*H>^2?v-EG5N08Fp#h00p5k z?yc(n)>^FCm3$(jvqh5np#C1puxSb zw#F2I!B3EzA$O68u$D^;%*7@XzqSq`4dtzKVYBI;fGoJ1ifG9I%0wbPB=Pz4s%!S> zrAquJ0&5l+Y#{`q&aqnM1wMST6lmqT5>Z8wsph;txWioSLSfh8&kWh6;7^OydLWzK z#kWGUYG^1{FmRu+U(Y%8u@Rdyo-BWfy>ID)xZJB#N(**M2n!YrFX+h4yd6kOp(|U7 zLyi`~+(nU*@{2V{t=P0s2roIb{6$Lrg#0Ck|D|4|Bc7f~m<0NC>2C4-pQl$~w*Ay# z+%ISLS0)JLav9pvpe|8wJ9$muKYs;dA}&1?Lorh&XTg%BKoR&Sfyr9mZ@%Wjk-wHC zg3iDk@RNm;jlZFrbSemv(F-j4zlx>#wqd!2R3&a@T^1-ShC`9A=}mbY z1HtM9FEiBFWQ<=vfny{NxQJ-bLWV=sgU9}njDOP@ppbGt?NsZG=+jQRr8!qSsnzuK zAe0q{86h(?&s5er)Ujsk-Mh@6oLsC})HlqXV2lUtaD#Izz`EvPo^G+t&F@?+34WOm zVgLDz1=&AY5^N-{pp~o?2V-9@RxSa#i+QU}#Q$C>ZmuNbj*>sfr^j3pMGE2qT;Iti5-@)EOSRtW==$vn_?o&}Xz$ zfGlf5T@s?8<^=Ok5|X>(U72~&z3Auy4CCmu>r(@|{S%vhwvn8!465g)`jhzoWZ z38;@CW8VfbM3TfT%Sq3dr4!WJiI|b@ zf(?yjM12*lcs$6{A@l>f&?v^j*w9A0rwe<+LL8_$;h_9C!!L%xGUuLdyxp^$MjfeG!B#g*b zt1Ks&;%mf_P-e_?q^(~}j>046C6Jh6?s3qf+ z-;kK&MO8W^rh%LN+SR?lKqrtQzptvhQ=2=Edr}mc7Du|yGBJ{b?Y7fvfT54w7%!td z$LeFo#ci#a(!7gA@EHGEtsPi@52y(km%TGaLroebgcCuqqc})diJ>j9aCeH9Llf^3 zXT{9Vk!ti`xN`ptL^jjz7K1fyC@+oGGw$ya2UT9ci+4A%P5O{JowoJxsh(DO$L22)|JEj>Ea zhuw1bIOKU*UYddm%U~O)U3q${qeDCQKL@mCZ7`|*P?-uk9j20smec$BD3U*Od9Pv0 zD=IIJqLsc!sIn8aOQR;e88R$1Wl2Q9i$9WV>~Q`$M5^!esRrIl8~81)5;bCWoy9e>G|1L@p!%Y6X<@Y( zJj&6qati+@{w;cI)Lt=gs|LTj>Gx@}P-=d6h7;R}ah_vpbHGtQjP5VBm+!PMN>$_^ zk)_QSGE<|DP=e$91Tv-R{^2Mwm)f0xO5Ems-M9ACkFe?RbJu)md$D2sBD^B9}W66a+6-NI!r; zPYyL^!P!+AG3ns!QjprpMN81yixsFiN!kM9{;JV^f#{H;z?bJoT|O}o1I!C4(=%-Z z63Iinec&z7bPLQsoHhr*ONK;H-tLzvOdVzv@5+%(Y|09o-d+-a|2V~`(FxW863#(c zlO%PCO=BK~f_EZ}Fs-U8)+{2A3Wc~EOh9?seo={uo<97kLQ{E*l@WHDoCZl^HL~GJ z;vt4#oY`S)!;SSutv#w!4Ju>Z$Q4sxk5yaUXUP9euFN5%q5F%cS#uf8uG{wvErpP2zU3`m`j0ims9{O@b;> zXyyoom+Mf-Qex9d@xDT+iMoj^MRHhdx|S-HO0xEi>WTI;$p_SN(KZs;E|@yu$eqODLCp)uWu^Vnll%|BN?*Uocp30cl^!xPUepk!Nz(k`^)nJolvXDPt5XT*cfUV-y)?3?K@id3;vRxQ zFSsezBe{Gemv>JtmqOoWFZiV25`}_armvlN4+xT*63V+(q>0O75oIxAS*$*}EmMxm zqD*s>soAB>NBQZIApCU*LQ>$duYy}_fW0LnE&OWJ-`CRzErh){hI9y6Z}c!PzjR?Y!4ldoxdp>1+~p0`f6AV z1C!fZ&bRqim0}#X*G~a!)I%=MQ{Xtoq(p*6q5_&MW#y`Z24OByC(YQ1;BqA_RnITY z61l2Y^4*Drv63mXv)0JUQ+Ca;>Db0^Yy0P}vXx!3Ei-#(#_DbP#&Ji%r^Z#`(dEPj zSW_m;@XP|S8G}93BnkRSs=q0HCFHDRjD6sGQ|xmjRy68{!B0SPFo= zkyfH)3$xx@Em7*3>joePlI31W%pG866Qtpf%+{nJA@|XZ&ptpq@mVHB$e}GHjVUss zf6TuXDVNW#%$Qm_20+Lc=-@;KVA&C@fq@!BYZ*#FFvnF)rf+)4Avo1+zM91>Sf-VC z$HM1*+7ZgUm7A`%fdgmS;|}Qs7>J9O^;R%chftg#LK0PXPKWr|NG3QaOu5T>@)B8Fh zn0Fn<_*YGHk~sR9==7NA^cPNal4$NobxKm{WhUO3)80wyjk>-|O}>$%&Ypgw7o43g zkqz@gDL8W2xAzS6B8fP1)Y(&UW`g;Fq~j>GS}Y-F-s4(4DMya_;*JDBBRS_Um6hW? z(6k&ce_jq4EU}yo@b@O}6#|@sBsQAjfjJ=#wM3)69lWZ?w42AYo5!@9$F!TjaM}&C zH+5L|ns#G|2wykxCM8MF1L%Du-=rkbc>uka1e}zdKiAoR-6WjJlJIN`y7xq!Oer{> zilYVKbTUpWd_c)Kv!vn}w0e$YoL1-#lvLk45^}!N#VKXK;}jHAQ*z{4q?2+?D@Bg9 z9DS%}Vop-mx#QFv$8_;*?P*1$GAlxzoe&dR&9a@Te$#8F0#EfWKGe`tjK%yZZ$Rmk zFj8a4Ndcws^GA_y0yRN5$1C%#eg_9ZtJV!t&29pZfn9QD0 zG-6suZ5)I0VT>qHDANnHsfbkH`+r`(6nQi-ff|(?WS0`{ur(|)$;N1iO$v_{YwCzJ zwZJwLalY8?x>eKd&=JIHw48Cp(`Glwh8Js!QW6%B5#K%ukuR0N2qUIf15yaL+RvUh zx1LwGnrrLTXVoxlK5eX5*IQdqwi{ZNY;LbTf4QWCdT`bT-kMB4s0^aNI~9JUiL=qfTp2f3=1ksn8#7JE(VktxEn2km zaINm_E7j=Qq)Oalq5rxIWrbx)WS*PJ|&->B4XqeFlI@c zktcPxi8Bg8mYF)^EiHG(n>BeRGe0IP?PZ4UJ@=Ra<1Hrz##?$0jDsD`oduI-VXizF z7X#!>nDqM@=>g^{=Fb$!lM5p-k5B8ql49IZkB~hIvobW614>~M@o#D5tEOx&6;D9+NB4UWpXx{Db2)iIx5LiiK1T;6H%o5$7boz_!PF8)yQf!m7ByQ07 zhBc;Cdnwst44!&2xNOE%YJG-(ZPQ;6fkIz7UPF`1;9>|Ks8=Z<+;2#M#(aKCzprCz zc6X0Uj|Z30lLf&y-0r(*2?Kes*B{5gbfx7CHD^@UM41)cEKzb-5q`?N*cVY}+*Xz) z3~WT%rKAGt@CY(t#CZg&C$$F=`?wFlON9eb4L4JP9|^M#SeV)NXbb^GQo2DCtVLgh zpt0j^RoCGz%vr+5G#p{>hv+O?GW=lVt^v~?T9G!Nh86YS)AhAn6_rknE|q~=Ov`)s zVRw%X&FH;?;kqF=Ix%yFtvFdknqlWgM2B6dm=bPAYq|g5!DijEe9|9UZQE0!TG%7B zvX%(dqR2zaH9`fT6>^C|iDYUe8UevFFdhy;J_T%5;k2Z2$S71gKVc zyKH8nP;+ReUGQIUd*rGyEV4m7DA@v^FGGLk5b;YO=qcf_w2V2gCL zNrCoHhxnMljx-`IcI4bcdFdw}nPa89adRGkZp_T~ObrZ&ePN1ai)}%gpBTrjNb8;A zm+-M*ITc==FuoSh>B|`iVDjpV>3*$jtv&aeI71t=Zc?MZ4ynVNu~->RYOmN>XI!=~ zr^tFRQ-LY|ROQ7c&NY$gWM+s{wpGY!4Q-pYYvjBIKG3qw&mtL%#gW`1o#x(*G@3R+=-~22miFAQQVizHzd@_ zZbmv^rG>GF@@Et)Apb;UB8UZ~R+?T;MwV|55+n6QL1ApQ(M4d?O7EQ57$PF|d6(_i z)%ogjO-OH0xbP=$fs`Xc(_bK#sJ^2t9)lWLPBZr|)(KLzi?VC~Ig*?3z+8FFsSFM|s!m|WR~i3_ z=g6Q% zonjeeZ(+j8kzU08#v|+{%p6##z!YvL=}=6mon~S=CpMW1(&YRmnY^SDon#i1td@v6 z7)%$o%-kgw1MwyJse=DwxI~S<^SU;-;)EQhBo~mG)I@)RJ4JN)cyp*vv?(yeXhlYl{f2wD0xD0nZA2_Y`h4~6gMirt-U z`0Xp&Uj-y@w*yRmE)SsM$uy|aIw~IEe~Rij%H|!Hwo*r6EJioX7W?j^3<8K`_~Bm~ zt)Vfzjn`0=WZW6Y(s{eSwz<7LoVGd-EE}npgUxwBTmm}Jg2!%|-3;i)Jx>O6ODTIP zvD{Lq;8?m;A8!gxEz5}Hz6iNuCyx~cEo?3*U?k$Dt86NlcrlmQvFY7Q%-`9H|uO#Ff*d3%V(0+!d^ zv5+?B*-`4+pDuO!Pp88Er%OYg6bej}S{}%|fFKd)MOgAi_#T-6oc2$F)kw^-0=djE z@5jC=*ZpYW{AMhiLUx?b#>p?c zO*Rr2GK@AXN8Yu~Mwo1*bw*a2k){k0t0PEuJhBWWP)WKIQE|*^CYh9yWvX66V73i2 z{H2MS`Ry{iv6Cz_$!mx;V^S6lSH+l_O)3~lBlsX5sw{9=$t~qGSa&PuD@?pcNk1(| zKLByxdzAMrdEX>ob4dKj*AqBMKgC5LiGr@CDN1^dEDudKO53MLBl@|8J+dHL#8 zL0&5P_bA7oN=p{+K+2Mg=~fYka5kkEpKd4MpKhm{t;Q()nV#KJQn_p|9uU;YVv5$!XDa1m zs)A-MEn7o_YZJBM^1cf@9pG3b(qI4>WC5Rt@{$+@1ElzerjKsOq&ZMER#iQ3K@*@n zim*k4Rn1vZS^sP(UX03taH^NJ>^)4BXfWOu1V2HHD0a-lA2 zl%iioZR9+J${f@Mm|9*YaP|gd0hkJ