From ae62b6ce452d3ba7cc6f5f281430528fdff8f56c Mon Sep 17 00:00:00 2001 From: Mykhailo Kremniov Date: Mon, 1 Jun 2026 13:19:21 +0300 Subject: [PATCH 01/15] 1.3.1 preparation: update rand and rustls-webpki; updade changelogs, specifying release date for 1.3.0; pacify `cargo vet`. --- CHANGELOG.md | 2 +- Cargo.lock | 52 +++++++++++++++--------------- api-server/CHANGELOG.md | 2 +- deny.toml | 13 ++++++-- supply-chain/config.toml | 12 ------- supply-chain/imports.lock | 65 ++++++++++++++++++++++++++++++++++---- wasm-wrappers/CHANGELOG.md | 2 +- 7 files changed, 99 insertions(+), 49 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 812159561..8a4b6f0b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/ - `wallet-create`/`wallet-recover`/`wallet-open` support the `ledger` subcommand, in addition to the existing `software` and `trezor`, which specifies the type of the wallet to operate on. -## [1.3.0] +## [1.3.0] - 2026-04-09 ### Added - Node RPC: new methods added - `chainstate_tokens_info`, `chainstate_orders_info_by_currencies`. diff --git a/Cargo.lock b/Cargo.lock index 176f6969b..3eb14660e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -289,7 +289,7 @@ dependencies = [ "logging", "pos-accounting", "proptest", - "rand 0.8.5", + "rand 0.8.6", "randomness", "serialization", "test-utils", @@ -444,7 +444,7 @@ dependencies = [ "enumflags2", "futures-channel", "futures-util", - "rand 0.9.2", + "rand 0.9.4", "raw-window-handle", "serde", "serde_repr", @@ -1493,7 +1493,7 @@ dependencies = [ "logging", "orders-accounting", "pos-accounting", - "rand 0.8.5", + "rand 0.8.6", "randomness", "rstest", "serialization", @@ -2978,7 +2978,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "byteorder", - "rand 0.8.5", + "rand 0.8.6", "rustc-hex", "static_assertions", ] @@ -3324,7 +3324,7 @@ version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ea1015b5a70616b688dc230cfe50c8af89d972cb132d5a622814d29773b10b9" dependencies = [ - "rand 0.8.5", + "rand 0.8.6", "rand_core 0.6.4", ] @@ -3641,7 +3641,7 @@ dependencies = [ "hickory-proto", "once_cell", "radix_trie", - "rand 0.8.5", + "rand 0.8.6", "thiserror 1.0.69", "tokio", "tracing", @@ -3663,7 +3663,7 @@ dependencies = [ "idna", "ipnet", "once_cell", - "rand 0.8.5", + "rand 0.8.6", "thiserror 1.0.69", "tinyvec", "tokio", @@ -4455,7 +4455,7 @@ dependencies = [ "jsonrpsee-types", "parking_lot 0.12.5", "pin-project", - "rand 0.9.2", + "rand 0.9.4", "rustc-hash 2.1.1", "serde", "serde_json", @@ -6374,7 +6374,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared 0.11.3", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -6572,7 +6572,7 @@ dependencies = [ "hmac", "md-5", "memchr", - "rand 0.9.2", + "rand 0.9.4", "sha2", "stringprep", ] @@ -6730,7 +6730,7 @@ dependencies = [ "bitflags 2.10.0", "lazy_static", "num-traits", - "rand 0.8.5", + "rand 0.8.6", "rand_chacha 0.3.1", "rand_xorshift 0.3.0", "regex-syntax", @@ -6893,9 +6893,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -6904,9 +6904,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -7000,7 +7000,7 @@ dependencies = [ name = "randomness" version = "1.3.0" dependencies = [ - "rand 0.8.5", + "rand 0.8.6", "static_assertions", ] @@ -7424,7 +7424,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3a8fb4672e840a587a66fc577a5491375df51ddb88f2a2c2a792598c326fe14" dependencies = [ "quote", - "rand 0.8.5", + "rand 0.8.6", "syn 2.0.114", ] @@ -7472,7 +7472,7 @@ dependencies = [ "borsh", "bytes", "num-traits", - "rand 0.8.5", + "rand 0.8.6", "rkyv", "serde", "serde_json", @@ -7596,9 +7596,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.10" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "ring", "rustls-pki-types", @@ -7779,7 +7779,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ "bitcoin_hashes", - "rand 0.8.5", + "rand 0.8.6", "secp256k1-sys", ] @@ -8056,7 +8056,7 @@ dependencies = [ "arraytools", "hex-literal", "parity-scale-codec", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -8332,7 +8332,7 @@ dependencies = [ "futures-util", "log", "pin-project", - "rand 0.8.5", + "rand 0.8.6", "snow", "thiserror 1.0.69", "tokio", @@ -8402,7 +8402,7 @@ dependencies = [ "http", "httparse", "log", - "rand 0.8.5", + "rand 0.8.6", "sha1", ] @@ -9043,7 +9043,7 @@ dependencies = [ "pin-project-lite", "postgres-protocol", "postgres-types", - "rand 0.9.2", + "rand 0.9.4", "socket2 0.6.2", "tokio", "tokio-util", @@ -11492,7 +11492,7 @@ dependencies = [ "hex", "nix", "ordered-stream", - "rand 0.8.5", + "rand 0.8.6", "serde", "serde_repr", "sha1", diff --git a/api-server/CHANGELOG.md b/api-server/CHANGELOG.md index f6f8d643c..d2ee456c4 100644 --- a/api-server/CHANGELOG.md +++ b/api-server/CHANGELOG.md @@ -6,7 +6,7 @@ The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/ ## [Unreleased] -## [1.3.0] +## [1.3.0] - 2026-04-09 ### Added - New endpoint was added: `/v2/transaction/{id}/output/{idx}`. diff --git a/deny.toml b/deny.toml index ba1cd4033..0744d0b92 100644 --- a/deny.toml +++ b/deny.toml @@ -44,6 +44,15 @@ db-path = "~/.cargo/advisory-dbs" db-urls = ["https://github.com/RustSec/advisory-db"] yanked = "warn" ignore = [ - "RUSTSEC-2024-0436", # "paste" is no longer maintained - "RUSTSEC-2025-0141", # "bincode" is no longer maintained + # "paste" is no longer maintained. + "RUSTSEC-2024-0436", + + # "bincode" is no longer maintained. + "RUSTSEC-2025-0141", + + # `rand` is unsound. Note that this has been patched in versions 0.10.1, 0.9.3 and 0.8.6, but we still + # have one place where `rand` 0.7.x is used - it's a dependency of `probabilistic-collections` v0.7.0. + # But we don't use any code from `probabilistic-collections` that actually creates an RNG, so our code + # is still sound. + "RUSTSEC-2026-0097" ] diff --git a/supply-chain/config.toml b/supply-chain/config.toml index 8fd20e4ba..2b6a91536 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -464,10 +464,6 @@ criteria = "safe-to-deploy" version = "5.4.1" criteria = "safe-to-deploy" -[[exemptions.expect-test]] -version = "1.5.1" -criteria = "safe-to-run" - [[exemptions.fallible-streaming-iterator]] version = "0.1.9" criteria = "safe-to-deploy" @@ -640,10 +636,6 @@ criteria = "safe-to-deploy" version = "0.2.3" criteria = "safe-to-deploy" -[[exemptions.instant]] -version = "0.1.13" -criteria = "safe-to-deploy" - [[exemptions.ipnet]] version = "2.11.0" criteria = "safe-to-deploy" @@ -1408,10 +1400,6 @@ criteria = "safe-to-deploy" version = "1.1.0" criteria = "safe-to-deploy" -[[exemptions.unarray]] -version = "0.1.4" -criteria = "safe-to-deploy" - [[exemptions.unicode-bidi-mirroring]] version = "0.2.0" criteria = "safe-to-deploy" diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index 878a330d4..330d757c0 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -1430,8 +1430,8 @@ user-login = "cpu" user-name = "Daniel McCarney" [[publisher.rustls-webpki]] -version = "0.103.10" -when = "2026-03-20" +version = "0.103.13" +when = "2026-04-21" user-id = 2751 user-login = "ctz" user-name = "Joe Birr-Pixton" @@ -2933,6 +2933,18 @@ crate is broadly used throughout the ecosystem and does not contain anything suspicious. """ +[[audits.bytecode-alliance.audits.instant]] +who = "Chris Fallin " +criteria = "safe-to-deploy" +version = "0.1.12" +notes = "Small crate wrapping platform primitives for time that uses `unsafe` only for FFI." + +[[audits.bytecode-alliance.audits.instant]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.1.12 -> 0.1.13" +notes = "Nothing major changed in this update" + [[audits.bytecode-alliance.audits.leb128fmt]] who = "Alex Crichton " criteria = "safe-to-deploy" @@ -3074,6 +3086,12 @@ who = "Chris Fallin " criteria = "safe-to-deploy" delta = "0.3.29 -> 0.3.32" +[[audits.bytecode-alliance.audits.rand]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +delta = "0.9.2 -> 0.9.4" +notes = "Minor bugfix release" + [[audits.bytecode-alliance.audits.sha1]] who = "Andrew Brown " criteria = "safe-to-deploy" @@ -3147,6 +3165,15 @@ criteria = "safe-to-deploy" version = "0.2.4" notes = "Implements a concurrency primitive with atomics, and is not obviously incorrect" +[[audits.bytecode-alliance.audits.unarray]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "0.1.4" +notes = """ +Crate is sound, albeit leaky, and not actively malicious. Probably not the best +crate to use in practice but it's suitable for testing dependencies. +""" + [[audits.bytecode-alliance.audits.vcpkg]] who = "Pat Hickey " criteria = "safe-to-deploy" @@ -4407,7 +4434,7 @@ who = "Nicolas Silva " criteria = "safe-to-deploy" user-id = 1281 # Nicolas Silva (nical) start = "2020-11-12" -end = "2025-06-01" +end = "2027-04-23" notes = "I am the author of this crate." aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" @@ -4425,7 +4452,7 @@ who = "Manish Goregaokar " criteria = "safe-to-deploy" user-id = 1139 # Manish Goregaokar (Manishearth) start = "2019-11-06" -end = "2026-02-01" +end = "2027-04-23" notes = "All code written or reviewed by Manish" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" @@ -4434,7 +4461,7 @@ who = "Manish Goregaokar " criteria = "safe-to-deploy" user-id = 1139 # Manish Goregaokar (Manishearth) start = "2019-05-15" -end = "2026-02-01" +end = "2027-04-23" notes = "All code written or reviewed by Manish" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" @@ -4452,7 +4479,7 @@ who = "Manish Goregaokar " criteria = "safe-to-deploy" user-id = 1139 # Manish Goregaokar (Manishearth) start = "2019-07-25" -end = "2026-02-01" +end = "2027-04-23" notes = "All code written or reviewed by Manish" aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" @@ -4637,6 +4664,22 @@ version = "1.0.10" notes = "dtolnay crate that will generate diffs for testing purposes. No IO or unsafe code." aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" +[[audits.mozilla.audits.expect-test]] +who = "Ben Dean-Kawamura " +criteria = "safe-to-run" +version = "1.4.1" +notes = """ +Expectation testing/management library. This will read/write the Rust test files, but that's +expected. It should only change string literals and any changes will be visible in code review. +""" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + +[[audits.mozilla.audits.expect-test]] +who = "Mark Hammond " +criteria = "safe-to-run" +delta = "1.4.1 -> 1.5.1" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + [[audits.mozilla.audits.fnv]] who = "Bobby Holley " criteria = "safe-to-deploy" @@ -5112,6 +5155,16 @@ yet, but it's all valid. Otherwise it's a pretty simple crate. """ aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" +[[audits.mozilla.audits.rand]] +who = "Henrik Skupin " +criteria = "safe-to-deploy" +delta = "0.8.5 -> 0.8.6" +notes = """ +Fixes RUSTSEC-2026-0097 by removing `log` dependency. Removes `simd_support` +feature. No new dependencies or unsafe code. +""" +aggregated-from = "https://hg.mozilla.org/mozilla-central/raw-file/tip/supply-chain/audits.toml" + [[audits.mozilla.audits.raw-window-handle]] who = "Jim Blandy " criteria = "safe-to-deploy" diff --git a/wasm-wrappers/CHANGELOG.md b/wasm-wrappers/CHANGELOG.md index 50ae5140f..a6df92611 100644 --- a/wasm-wrappers/CHANGELOG.md +++ b/wasm-wrappers/CHANGELOG.md @@ -6,7 +6,7 @@ The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/ ## [Unreleased] -## [1.3.0] +## [1.3.0] - 2026-04-09 ### Added - New functions: From f290cd1fc3fb531c08875a38cbd9ba84461ebe3e Mon Sep 17 00:00:00 2001 From: Mykhailo Kremniov Date: Wed, 6 May 2026 19:11:50 +0300 Subject: [PATCH 02/15] Appease clippy 1.95 --- .../src/storage/impls/in_memory/mod.rs | 2 +- blockprod/src/detail/tests.rs | 2 +- chainstate/db-dumper/src/dumper_lib/tests/mod.rs | 2 +- .../best_chain_candidates_tests.rs | 2 +- chainstate/src/detail/chainstateref/mod.rs | 5 +---- common/src/chain/config/regtest_options.rs | 7 ++++--- node-gui/src/widgets/esc_handler.rs | 16 +++++++--------- p2p/src/peer_manager/tests/connections.rs | 2 +- p2p/src/sync/peer/block_manager.rs | 2 +- p2p/src/tests/helpers/test_node.rs | 2 +- subsystem/tests/basic.rs | 2 +- utils/build_utils/src/lib.rs | 5 +++-- utils/networking/src/resolvable_name.rs | 2 +- utxo/src/cache.rs | 2 +- wallet/src/account/mod.rs | 3 +-- wallet/src/account/output_cache/mod.rs | 2 +- .../wallet-controller/src/synced_controller.rs | 2 +- 17 files changed, 28 insertions(+), 32 deletions(-) diff --git a/api-server/api-server-common/src/storage/impls/in_memory/mod.rs b/api-server/api-server-common/src/storage/impls/in_memory/mod.rs index 6c2c9f6cd..f90f80b3d 100644 --- a/api-server/api-server-common/src/storage/impls/in_memory/mod.rs +++ b/api-server/api-server-common/src/storage/impls/in_memory/mod.rs @@ -190,7 +190,7 @@ impl ApiServerInMemoryStorage { .address_transactions_table .get(address) .map_or_else(Vec::new, |transactions| { - transactions.iter().flat_map(|(_, txs)| txs.iter()).cloned().collect() + transactions.values().flat_map(|txs| txs.iter()).cloned().collect() })) } diff --git a/blockprod/src/detail/tests.rs b/blockprod/src/detail/tests.rs index d9ae6294f..0812bd07a 100644 --- a/blockprod/src/detail/tests.rs +++ b/blockprod/src/detail/tests.rs @@ -1365,7 +1365,7 @@ mod produce_block { let origin = LocalTxOrigin::Mempool; let options = TxOptions::default_for(origin.into()); - for tx in std::iter::once(main_tx).chain(dependent_txs.into_iter()) { + for tx in std::iter::once(main_tx).chain(dependent_txs) { mp.add_transaction_local(tx, origin, options.clone()).unwrap(); } } diff --git a/chainstate/db-dumper/src/dumper_lib/tests/mod.rs b/chainstate/db-dumper/src/dumper_lib/tests/mod.rs index 95f274324..9c98481bc 100644 --- a/chainstate/db-dumper/src/dumper_lib/tests/mod.rs +++ b/chainstate/db-dumper/src/dumper_lib/tests/mod.rs @@ -266,7 +266,7 @@ fn dump_blocks_random( } infos.shuffle(&mut rng); - infos.sort_by(|b1, b2| b1.input_info.height.cmp(&b2.input_info.height)); + infos.sort_by_key(|b| b.input_info.height); } infos diff --git a/chainstate/src/detail/block_invalidation/best_chain_candidates_tests.rs b/chainstate/src/detail/block_invalidation/best_chain_candidates_tests.rs index e3d6d8653..9cca7989d 100644 --- a/chainstate/src/detail/block_invalidation/best_chain_candidates_tests.rs +++ b/chainstate/src/detail/block_invalidation/best_chain_candidates_tests.rs @@ -367,7 +367,7 @@ mod test_framework { } }) .collect(); - ids_heights.sort_by(|(_, height1), (_, height2)| height1.cmp(height2)); + ids_heights.sort_by_key(|(_, height)| *height); Ok(ids_heights.iter().map(|(id, _)| *id).collect()) } diff --git a/chainstate/src/detail/chainstateref/mod.rs b/chainstate/src/detail/chainstateref/mod.rs index ed5d0c2d1..6fbbb680c 100644 --- a/chainstate/src/detail/chainstateref/mod.rs +++ b/chainstate/src/detail/chainstateref/mod.rs @@ -1002,10 +1002,7 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat start_from: BlockHeight, ) -> Result>, PropertyQueryError> { let block_tree_map = self.db_tx.get_block_tree_by_height(start_from)?; - let result = block_tree_map - .into_iter() - .flat_map(|(_height, ids_per_height)| ids_per_height) - .collect(); + let result = block_tree_map.into_values().flatten().collect(); Ok(result) } diff --git a/common/src/chain/config/regtest_options.rs b/common/src/chain/config/regtest_options.rs index 0a57d8f38..13f128ce8 100644 --- a/common/src/chain/config/regtest_options.rs +++ b/common/src/chain/config/regtest_options.rs @@ -168,9 +168,10 @@ pub fn regtest_chain_config_builder(options: &ChainConfigOptions) -> Result event::Status { let event_captured = match &event { - Event::Keyboard(keyboard::Event::KeyPressed { key, .. }) => { - if *key == keyboard::Key::Named(keyboard::key::Named::Escape) { - if let Some(msg) = &self.msg_to_emit { - shell.publish(msg.clone()); - } - - true - } else { - false + Event::Keyboard(keyboard::Event::KeyPressed { key, .. }) + if *key == keyboard::Key::Named(keyboard::key::Named::Escape) => + { + if let Some(msg) = &self.msg_to_emit { + shell.publish(msg.clone()); } + + true } _ => false, }; diff --git a/p2p/src/peer_manager/tests/connections.rs b/p2p/src/peer_manager/tests/connections.rs index fbc3fd127..ca7459868 100644 --- a/p2p/src/peer_manager/tests/connections.rs +++ b/p2p/src/peer_manager/tests/connections.rs @@ -1909,7 +1909,7 @@ async fn feeler_connections_test_impl(seed: Seed) { 10, &mut rng, ); - let mut addresses = BTreeSet::from_iter(addresses.into_iter()); + let mut addresses = BTreeSet::from_iter(addresses); for addr in &addresses { peer_mgr.peerdb.peer_discovered(*addr); } diff --git a/p2p/src/sync/peer/block_manager.rs b/p2p/src/sync/peer/block_manager.rs index f96fe57cd..9fa63130a 100644 --- a/p2p/src/sync/peer/block_manager.rs +++ b/p2p/src/sync/peer/block_manager.rs @@ -580,7 +580,7 @@ where // two versions of incoming.requested_blocks, one for the most recent request and // another one for the previous request(s), so that it can distinguish previously // requested blocks that were "cancelled" in-flight from unsolicited ones. - self.outgoing.blocks_queue.extend(block_ids.into_iter()); + self.outgoing.blocks_queue.extend(block_ids); Ok(()) } diff --git a/p2p/src/tests/helpers/test_node.rs b/p2p/src/tests/helpers/test_node.rs index f625abeeb..47642b65b 100644 --- a/p2p/src/tests/helpers/test_node.rs +++ b/p2p/src/tests/helpers/test_node.rs @@ -393,7 +393,7 @@ where .peers() .values() .map(|ctx| ctx.peer_address) - .chain(peer_mgr.pending_outbound_conn_addrs().into_iter()) + .chain(peer_mgr.pending_outbound_conn_addrs()) .collect() }) .await diff --git a/subsystem/tests/basic.rs b/subsystem/tests/basic.rs index fbd6a64f8..ff5ae4492 100644 --- a/subsystem/tests/basic.rs +++ b/subsystem/tests/basic.rs @@ -60,7 +60,7 @@ fn separate_call_and_result() { assert_eq!(responses.len(), expected.len()); // Gather and verify results - for (response, expected) in responses.into_iter().zip(expected.into_iter()) { + for (response, expected) in responses.into_iter().zip(expected) { assert_eq!(response.await.unwrap(), expected); } diff --git a/utils/build_utils/src/lib.rs b/utils/build_utils/src/lib.rs index b35b93dbf..afe3048cc 100644 --- a/utils/build_utils/src/lib.rs +++ b/utils/build_utils/src/lib.rs @@ -23,8 +23,9 @@ pub fn emit_git_env_vars() { let git_head_hash = Command::new("git") .args(["rev-parse", "HEAD"]) .output() - .map(|out| String::from_utf8_lossy(&out.stdout).trim().to_string()) - .unwrap_or("".to_string()); + .map_or("".to_string(), |out| { + String::from_utf8_lossy(&out.stdout).trim().to_string() + }); // Sanity check assert!(git_head_hash.is_ascii()); diff --git a/utils/networking/src/resolvable_name.rs b/utils/networking/src/resolvable_name.rs index c325fc65c..150f0a8fc 100644 --- a/utils/networking/src/resolvable_name.rs +++ b/utils/networking/src/resolvable_name.rs @@ -62,7 +62,7 @@ pub async fn resolve_all_take_first( let mut result = Vec::with_capacity(resolvables.size_hint().0); for resolvable in resolvables { - result.extend(resolvable.resolve().await?.next().into_iter()); + result.extend(resolvable.resolve().await?.next()); } Ok(result) diff --git a/utxo/src/cache.rs b/utxo/src/cache.rs index 68238e7f3..f2a10e798 100644 --- a/utxo/src/cache.rs +++ b/utxo/src/cache.rs @@ -268,7 +268,7 @@ impl UtxosCache

{ let block_undo = reward_undo.ok_or(Error::MissingBlockRewardUndo(*block_id))?; inputs .iter() - .zip(block_undo.into_inner().into_iter()) + .zip(block_undo.into_inner()) .filter_map(|(tx_in, utxo)| match tx_in { TxInput::Utxo(outpoint) => Some((outpoint, utxo)), TxInput::Account(..) diff --git a/wallet/src/account/mod.rs b/wallet/src/account/mod.rs index 8415440b0..e67c9323d 100644 --- a/wallet/src/account/mod.rs +++ b/wallet/src/account/mod.rs @@ -2072,8 +2072,7 @@ impl Account { median_time, utxo_states, with_locked, - ) - .into_iter(), + ), |(_, tx_output)| tx_output, |total: &mut Amount, _, amount| -> WalletResult<()> { *total = (*total + amount).ok_or(WalletError::OutputAmountOverflow)?; diff --git a/wallet/src/account/output_cache/mod.rs b/wallet/src/account/output_cache/mod.rs index 651fcc32b..e673d55e1 100644 --- a/wallet/src/account/output_cache/mod.rs +++ b/wallet/src/account/output_cache/mod.rs @@ -1598,7 +1598,7 @@ impl OutputCache { } if let Some(descendants) = self.unconfirmed_descendants.remove(&outpoint_source_id) { - to_update.extend(descendants.into_iter()) + to_update.extend(descendants) } } diff --git a/wallet/wallet-controller/src/synced_controller.rs b/wallet/wallet-controller/src/synced_controller.rs index 56dbc5543..99f13728f 100644 --- a/wallet/wallet-controller/src/synced_controller.rs +++ b/wallet/wallet-controller/src/synced_controller.rs @@ -1461,7 +1461,7 @@ where fetch_token_infos_into( &self.rpc_client, - &token1_id.into_iter().chain(token2_id.into_iter()).collect(), + &token1_id.into_iter().chain(token2_id).collect(), &mut tx_info.tokens_additional_info, ) .await?; From 7a5f03b00fe642847be29fc59eeee33f1b9848a0 Mon Sep 17 00:00:00 2001 From: Mykhailo Kremniov Date: Mon, 1 Jun 2026 13:30:43 +0300 Subject: [PATCH 03/15] Appease clippy 1.96 --- mempool/src/pool/tx_pool/tests/utils.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mempool/src/pool/tx_pool/tests/utils.rs b/mempool/src/pool/tx_pool/tests/utils.rs index e6da78353..901eee05c 100644 --- a/mempool/src/pool/tx_pool/tests/utils.rs +++ b/mempool/src/pool/tx_pool/tests/utils.rs @@ -409,10 +409,9 @@ pub async fn tx_spend_input( fee: impl Into>, flags: u128, ) -> anyhow::Result { - let fee = fee.into().map_or_else( - || get_relay_fee_from_tx_size(estimate_tx_size(1, 2)).into(), - std::convert::identity, - ); + let fee = fee + .into() + .unwrap_or_else(|| get_relay_fee_from_tx_size(estimate_tx_size(1, 2)).into()); tx_spend_several_inputs(tx_pool, &[input], &[witness], fee, flags).await } From 346bc50d1baee4134468954b812d976556fd2b3a Mon Sep 17 00:00:00 2001 From: Mykhailo Kremniov Date: Tue, 2 Jun 2026 12:08:32 +0300 Subject: [PATCH 04/15] Bump version to 1.3.1 --- .github/workflows/build.yml | 4 +- Cargo.lock | 166 +++++++++--------- Cargo.toml | 4 +- .../docker/example-mainnet-dns-server/.env | 2 +- build-tools/docker/example-mainnet/.env | 2 +- node-daemon/docs/RPC.md | 2 +- node-daemon/docs/RPC_DEV.md | 2 +- wallet/TREZOR_SUPPORT.md | 2 +- wallet/wallet-rpc-daemon/docs/RPC.md | 2 +- 9 files changed, 93 insertions(+), 93 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 376e4246e..8dc409300 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -293,7 +293,7 @@ jobs: # Build Ledger-specific tests and archive them run_tests_on_ledger_preparation: - if: false # Temporarily disabled for the v1.3.0 release + if: false # Temporarily disabled for the v1.3.x releases runs-on: ubuntu-latest steps: - name: Checkout the core repository @@ -339,7 +339,7 @@ jobs: # Run Ledger-specific tests on an emulator run_tests_on_ledger: - if: false # Temporarily disabled for the v1.3.0 release + if: false # Temporarily disabled for the v1.3.x releases needs: run_tests_on_ledger_preparation runs-on: ubuntu-latest strategy: diff --git a/Cargo.lock b/Cargo.lock index 3eb14660e..3782a86ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,7 +20,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" [[package]] name = "accounting" -version = "1.3.0" +version = "1.3.1" dependencies = [ "common", "parity-scale-codec", @@ -227,7 +227,7 @@ checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" [[package]] name = "api-blockchain-scanner-daemon" -version = "1.3.0" +version = "1.3.1" dependencies = [ "api-blockchain-scanner-lib", "api-server-common", @@ -246,7 +246,7 @@ dependencies = [ [[package]] name = "api-blockchain-scanner-lib" -version = "1.3.0" +version = "1.3.1" dependencies = [ "api-server-common", "async-trait", @@ -276,7 +276,7 @@ dependencies = [ [[package]] name = "api-server-backend-test-suite" -version = "1.3.0" +version = "1.3.1" dependencies = [ "api-server-common", "async-trait", @@ -299,7 +299,7 @@ dependencies = [ [[package]] name = "api-server-common" -version = "1.3.0" +version = "1.3.1" dependencies = [ "async-trait", "bb8-postgres", @@ -325,7 +325,7 @@ dependencies = [ [[package]] name = "api-server-stack-test-suite" -version = "1.3.0" +version = "1.3.1" dependencies = [ "api-blockchain-scanner-lib", "api-server-common", @@ -356,7 +356,7 @@ dependencies = [ [[package]] name = "api-web-server" -version = "1.3.0" +version = "1.3.1" dependencies = [ "api-server-common", "async-trait", @@ -992,7 +992,7 @@ dependencies = [ [[package]] name = "blockprod" -version = "1.3.0" +version = "1.3.1" dependencies = [ "async-trait", "chainstate", @@ -1122,7 +1122,7 @@ dependencies = [ [[package]] name = "build_utils" -version = "1.3.0" +version = "1.3.1" dependencies = [ "substrate-build-script-utils", ] @@ -1330,7 +1330,7 @@ dependencies = [ [[package]] name = "chainstate" -version = "1.3.0" +version = "1.3.1" dependencies = [ "accounting", "async-trait", @@ -1376,7 +1376,7 @@ dependencies = [ [[package]] name = "chainstate-db-dumper" -version = "1.3.0" +version = "1.3.1" dependencies = [ "anyhow", "chainstate", @@ -1401,7 +1401,7 @@ dependencies = [ [[package]] name = "chainstate-launcher" -version = "1.3.0" +version = "1.3.1" dependencies = [ "chainstate", "chainstate-storage", @@ -1417,7 +1417,7 @@ dependencies = [ [[package]] name = "chainstate-storage" -version = "1.3.0" +version = "1.3.1" dependencies = [ "accounting", "chainstate-types", @@ -1444,7 +1444,7 @@ dependencies = [ [[package]] name = "chainstate-test-framework" -version = "1.3.0" +version = "1.3.1" dependencies = [ "chainstate", "chainstate-storage", @@ -1474,7 +1474,7 @@ dependencies = [ [[package]] name = "chainstate-test-suite" -version = "1.3.0" +version = "1.3.1" dependencies = [ "accounting", "chainstate", @@ -1508,7 +1508,7 @@ dependencies = [ [[package]] name = "chainstate-types" -version = "1.3.0" +version = "1.3.1" dependencies = [ "common", "crypto", @@ -1716,7 +1716,7 @@ dependencies = [ [[package]] name = "common" -version = "1.3.0" +version = "1.3.1" dependencies = [ "anyhow", "bech32 0.11.1", @@ -1776,7 +1776,7 @@ dependencies = [ [[package]] name = "consensus" -version = "1.3.0" +version = "1.3.1" dependencies = [ "chainstate-types", "common", @@ -1857,7 +1857,7 @@ dependencies = [ [[package]] name = "constraints-value-accumulator" -version = "1.3.0" +version = "1.3.1" dependencies = [ "accounting", "common", @@ -2108,7 +2108,7 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto" -version = "1.3.0" +version = "1.3.1" dependencies = [ "argon2", "bip39", @@ -2584,7 +2584,7 @@ checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" [[package]] name = "dns-server" -version = "1.3.0" +version = "1.3.1" dependencies = [ "anyhow", "async-trait", @@ -4788,7 +4788,7 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "log_error" -version = "1.3.0" +version = "1.3.1" dependencies = [ "derive_more 1.0.0", "logging", @@ -4801,7 +4801,7 @@ dependencies = [ [[package]] name = "logging" -version = "1.3.0" +version = "1.3.1" dependencies = [ "console-subscriber", "log", @@ -4947,7 +4947,7 @@ dependencies = [ [[package]] name = "mempool" -version = "1.3.0" +version = "1.3.1" dependencies = [ "accounting", "anyhow", @@ -4991,7 +4991,7 @@ dependencies = [ [[package]] name = "mempool-types" -version = "1.3.0" +version = "1.3.1" dependencies = [ "p2p-types", "rpc-description", @@ -5071,7 +5071,7 @@ dependencies = [ [[package]] name = "mintlayer-core" -version = "1.3.0" +version = "1.3.1" dependencies = [ "chainstate", "chainstate-storage", @@ -5109,7 +5109,7 @@ dependencies = [ [[package]] name = "mintlayer-test" -version = "1.3.0" +version = "1.3.1" dependencies = [ "clap", "libtest-mimic", @@ -5125,7 +5125,7 @@ dependencies = [ [[package]] name = "mintscript" -version = "1.3.0" +version = "1.3.1" dependencies = [ "common", "crypto", @@ -5187,7 +5187,7 @@ dependencies = [ [[package]] name = "mocks" -version = "1.3.0" +version = "1.3.1" dependencies = [ "async-trait", "chainstate", @@ -5284,7 +5284,7 @@ dependencies = [ [[package]] name = "networking" -version = "1.3.0" +version = "1.3.1" dependencies = [ "async-trait", "bytes", @@ -5331,7 +5331,7 @@ dependencies = [ [[package]] name = "node-comm" -version = "1.3.0" +version = "1.3.1" dependencies = [ "anyhow", "async-trait", @@ -5363,7 +5363,7 @@ dependencies = [ [[package]] name = "node-daemon" -version = "1.3.0" +version = "1.3.1" dependencies = [ "anyhow", "assert_cmd", @@ -5377,7 +5377,7 @@ dependencies = [ [[package]] name = "node-gui" -version = "1.3.0" +version = "1.3.1" dependencies = [ "anyhow", "chainstate", @@ -5410,7 +5410,7 @@ dependencies = [ [[package]] name = "node-gui-backend" -version = "1.3.0" +version = "1.3.1" dependencies = [ "anyhow", "chainstate", @@ -5445,7 +5445,7 @@ dependencies = [ [[package]] name = "node-lib" -version = "1.3.0" +version = "1.3.1" dependencies = [ "anyhow", "blockprod", @@ -6063,7 +6063,7 @@ dependencies = [ [[package]] name = "orders-accounting" -version = "1.3.0" +version = "1.3.1" dependencies = [ "accounting", "common", @@ -6114,7 +6114,7 @@ dependencies = [ [[package]] name = "p2p" -version = "1.3.0" +version = "1.3.1" dependencies = [ "async-trait", "bytes", @@ -6171,7 +6171,7 @@ dependencies = [ [[package]] name = "p2p-backend-test-suite" -version = "1.3.0" +version = "1.3.1" dependencies = [ "chainstate", "common", @@ -6191,7 +6191,7 @@ dependencies = [ [[package]] name = "p2p-test-utils" -version = "1.3.0" +version = "1.3.1" dependencies = [ "chainstate", "chainstate-storage", @@ -6211,7 +6211,7 @@ dependencies = [ [[package]] name = "p2p-types" -version = "1.3.0" +version = "1.3.1" dependencies = [ "common", "parity-scale-codec", @@ -6543,7 +6543,7 @@ dependencies = [ [[package]] name = "pos-accounting" -version = "1.3.0" +version = "1.3.1" dependencies = [ "accounting", "common", @@ -6998,7 +6998,7 @@ dependencies = [ [[package]] name = "randomness" -version = "1.3.0" +version = "1.3.1" dependencies = [ "rand 0.8.6", "static_assertions", @@ -7329,7 +7329,7 @@ checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" [[package]] name = "rpc" -version = "1.3.0" +version = "1.3.1" dependencies = [ "anyhow", "async-trait", @@ -7359,7 +7359,7 @@ dependencies = [ [[package]] name = "rpc-description" -version = "1.3.0" +version = "1.3.1" dependencies = [ "rpc-description-macro", "serde_json", @@ -7367,7 +7367,7 @@ dependencies = [ [[package]] name = "rpc-description-macro" -version = "1.3.0" +version = "1.3.1" dependencies = [ "proc-macro2", "quote", @@ -7376,7 +7376,7 @@ dependencies = [ [[package]] name = "rpc-types" -version = "1.3.0" +version = "1.3.1" dependencies = [ "hex", "rpc-description", @@ -7730,7 +7730,7 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "script" -version = "1.3.0" +version = "1.3.1" dependencies = [ "crypto", "flate2", @@ -8038,7 +8038,7 @@ dependencies = [ [[package]] name = "serialization" -version = "1.3.0" +version = "1.3.1" dependencies = [ "hex", "rpc-description", @@ -8051,7 +8051,7 @@ dependencies = [ [[package]] name = "serialization-core" -version = "1.3.0" +version = "1.3.1" dependencies = [ "arraytools", "hex-literal", @@ -8061,7 +8061,7 @@ dependencies = [ [[package]] name = "serialization-tagged" -version = "1.3.0" +version = "1.3.1" dependencies = [ "parity-scale-codec", "proptest", @@ -8073,7 +8073,7 @@ dependencies = [ [[package]] name = "serialization-tagged-derive" -version = "1.3.0" +version = "1.3.1" dependencies = [ "itertools 0.14.0", "proc-macro2", @@ -8429,7 +8429,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "storage" -version = "1.3.0" +version = "1.3.1" dependencies = [ "common", "rstest", @@ -8442,7 +8442,7 @@ dependencies = [ [[package]] name = "storage-backend-test-suite" -version = "1.3.0" +version = "1.3.1" dependencies = [ "libtest-mimic", "logging", @@ -8457,7 +8457,7 @@ dependencies = [ [[package]] name = "storage-core" -version = "1.3.0" +version = "1.3.1" dependencies = [ "common", "itertools 0.14.0", @@ -8470,7 +8470,7 @@ dependencies = [ [[package]] name = "storage-failing" -version = "1.3.0" +version = "1.3.1" dependencies = [ "enumflags2", "storage", @@ -8482,7 +8482,7 @@ dependencies = [ [[package]] name = "storage-inmemory" -version = "1.3.0" +version = "1.3.1" dependencies = [ "storage-backend-test-suite", "storage-core", @@ -8491,7 +8491,7 @@ dependencies = [ [[package]] name = "storage-lmdb" -version = "1.3.0" +version = "1.3.1" dependencies = [ "lmdb-mintlayer", "logging", @@ -8505,7 +8505,7 @@ dependencies = [ [[package]] name = "storage-sqlite" -version = "1.3.0" +version = "1.3.1" dependencies = [ "hex", "logging", @@ -8608,7 +8608,7 @@ checksum = "b285e7d183a32732fdc119f3d81b7915790191fad602b7c709ef247073c77a2e" [[package]] name = "subsystem" -version = "1.3.0" +version = "1.3.1" dependencies = [ "async-trait", "cfg-if", @@ -8764,7 +8764,7 @@ checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" [[package]] name = "test-rpc-functions" -version = "1.3.0" +version = "1.3.1" dependencies = [ "async-trait", "chainstate", @@ -8786,7 +8786,7 @@ dependencies = [ [[package]] name = "test-utils" -version = "1.3.0" +version = "1.3.1" dependencies = [ "common", "crypto", @@ -8968,7 +8968,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokens-accounting" -version = "1.3.0" +version = "1.3.1" dependencies = [ "accounting", "chainstate-types", @@ -9427,7 +9427,7 @@ checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" [[package]] name = "tx-verifier" -version = "1.3.0" +version = "1.3.1" dependencies = [ "accounting", "chainstate-storage", @@ -9456,14 +9456,14 @@ dependencies = [ [[package]] name = "typename" -version = "1.3.0" +version = "1.3.1" dependencies = [ "typename-derive", ] [[package]] name = "typename-derive" -version = "1.3.0" +version = "1.3.1" dependencies = [ "itertools 0.14.0", "quote", @@ -9623,7 +9623,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "utils" -version = "1.3.0" +version = "1.3.1" dependencies = [ "anyhow", "clap", @@ -9655,7 +9655,7 @@ dependencies = [ [[package]] name = "utils-networking" -version = "1.3.0" +version = "1.3.1" dependencies = [ "addr", "itertools 0.14.0", @@ -9670,7 +9670,7 @@ dependencies = [ [[package]] name = "utxo" -version = "1.3.0" +version = "1.3.1" dependencies = [ "chainstate-types", "common", @@ -9744,7 +9744,7 @@ dependencies = [ [[package]] name = "wallet" -version = "1.3.0" +version = "1.3.1" dependencies = [ "anyhow", "async-trait", @@ -9797,7 +9797,7 @@ dependencies = [ [[package]] name = "wallet-address-generator" -version = "1.3.0" +version = "1.3.1" dependencies = [ "clap", "common", @@ -9812,7 +9812,7 @@ dependencies = [ [[package]] name = "wallet-address-generator-lib" -version = "1.3.0" +version = "1.3.1" dependencies = [ "build_utils", "clap", @@ -9827,7 +9827,7 @@ dependencies = [ [[package]] name = "wallet-cli" -version = "1.3.0" +version = "1.3.1" dependencies = [ "clap", "tokio", @@ -9837,7 +9837,7 @@ dependencies = [ [[package]] name = "wallet-cli-commands" -version = "1.3.0" +version = "1.3.1" dependencies = [ "async-trait", "bigdecimal", @@ -9894,7 +9894,7 @@ dependencies = [ [[package]] name = "wallet-cli-lib" -version = "1.3.0" +version = "1.3.1" dependencies = [ "async-trait", "blockprod", @@ -9944,7 +9944,7 @@ dependencies = [ [[package]] name = "wallet-controller" -version = "1.3.0" +version = "1.3.1" dependencies = [ "anyhow", "async-trait", @@ -9985,7 +9985,7 @@ dependencies = [ [[package]] name = "wallet-rpc-client" -version = "1.3.0" +version = "1.3.1" dependencies = [ "async-trait", "base64 0.22.1", @@ -10017,7 +10017,7 @@ dependencies = [ [[package]] name = "wallet-rpc-daemon" -version = "1.3.0" +version = "1.3.1" dependencies = [ "clap", "common", @@ -10033,7 +10033,7 @@ dependencies = [ [[package]] name = "wallet-rpc-lib" -version = "1.3.0" +version = "1.3.1" dependencies = [ "anyhow", "async-trait", @@ -10076,7 +10076,7 @@ dependencies = [ [[package]] name = "wallet-storage" -version = "1.3.0" +version = "1.3.1" dependencies = [ "bip39", "common", @@ -10095,7 +10095,7 @@ dependencies = [ [[package]] name = "wallet-test-node" -version = "1.3.0" +version = "1.3.1" dependencies = [ "blockprod", "chainstate", @@ -10116,7 +10116,7 @@ dependencies = [ [[package]] name = "wallet-types" -version = "1.3.0" +version = "1.3.1" dependencies = [ "bip39", "common", @@ -10259,7 +10259,7 @@ dependencies = [ [[package]] name = "wasm-doc-gen" -version = "1.3.0" +version = "1.3.1" dependencies = [ "anyhow", "clap", @@ -10305,7 +10305,7 @@ dependencies = [ [[package]] name = "wasm-wrappers" -version = "1.3.0" +version = "1.3.1" dependencies = [ "bip39", "common", diff --git a/Cargo.toml b/Cargo.toml index 5606e2d89..13a944924 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ homepage = "https://mintlayer.org" repository = "https://github.com/mintlayer/mintlayer-core" readme = "README.md" license = "MIT" -version = "1.3.0" +version = "1.3.1" edition = "2021" [workspace] @@ -128,7 +128,7 @@ utxo = { path = "utxo" } [workspace.package] edition = "2021" rust-version = "1.88" -version = "1.3.0" +version = "1.3.1" license = "MIT" [workspace.dependencies] diff --git a/build-tools/docker/example-mainnet-dns-server/.env b/build-tools/docker/example-mainnet-dns-server/.env index e7a3627cd..e3a6d62f9 100644 --- a/build-tools/docker/example-mainnet-dns-server/.env +++ b/build-tools/docker/example-mainnet-dns-server/.env @@ -4,7 +4,7 @@ COMPOSE_PROJECT_NAME=mintlayer-mainnet-dns-server # Dockerhub username, from which the docker images will be pulled. ML_DOCKERHUB_USERNAME=mintlayer -# The image tag to use, e.g. "v1.3.0" or "latest". +# The image tag to use, e.g. "v1.3.1" or "latest". ML_DOCKER_IMAGE_TAG=latest # The user and group ids that will be used to run the software. diff --git a/build-tools/docker/example-mainnet/.env b/build-tools/docker/example-mainnet/.env index 1f1e5c73a..871b088c3 100644 --- a/build-tools/docker/example-mainnet/.env +++ b/build-tools/docker/example-mainnet/.env @@ -4,7 +4,7 @@ COMPOSE_PROJECT_NAME=mintlayer-mainnet # Dockerhub username, from which the docker images will be pulled. ML_DOCKERHUB_USERNAME=mintlayer -# The image tag to use, e.g. "v1.3.0" or "latest". +# The image tag to use, e.g. "v1.3.1" or "latest". ML_DOCKER_IMAGE_TAG=latest # The user and group ids that will be used to run the software. diff --git a/node-daemon/docs/RPC.md b/node-daemon/docs/RPC.md index 70e9dc8ef..a4d196ac3 100644 --- a/node-daemon/docs/RPC.md +++ b/node-daemon/docs/RPC.md @@ -1,6 +1,6 @@ # RPC documentation for Mintlayer node -Version `1.3.0`. +Version `1.3.1`. ## Module `node` diff --git a/node-daemon/docs/RPC_DEV.md b/node-daemon/docs/RPC_DEV.md index 639fb8c2a..16e665041 100644 --- a/node-daemon/docs/RPC_DEV.md +++ b/node-daemon/docs/RPC_DEV.md @@ -1,6 +1,6 @@ # RPC documentation for Mintlayer node developer functions -Version `1.3.0`. +Version `1.3.1`. These functions are used for testing and only enabled in regtest. diff --git a/wallet/TREZOR_SUPPORT.md b/wallet/TREZOR_SUPPORT.md index 127877601..cda397ace 100644 --- a/wallet/TREZOR_SUPPORT.md +++ b/wallet/TREZOR_SUPPORT.md @@ -42,7 +42,7 @@ The Mintlayer firmware version determines the compatibility between the firmware | Mintlayer Core version | Required Mintlayer firmware version | | --- | --- | -| 1.1.0, 1.2.x, 1.3.0 | 1.x.x | +| 1.1.0, 1.2.x, 1.3.x | 1.x.x | Note: if you've built Core wallets directly from `master` instead of using a specific release, you'll probably won't be able to use a specific release for the firmware either. diff --git a/wallet/wallet-rpc-daemon/docs/RPC.md b/wallet/wallet-rpc-daemon/docs/RPC.md index eda72de8f..db2285692 100644 --- a/wallet/wallet-rpc-daemon/docs/RPC.md +++ b/wallet/wallet-rpc-daemon/docs/RPC.md @@ -1,6 +1,6 @@ # RPC documentation for Mintlayer node wallet -Version `1.3.0`. +Version `1.3.1`. ## Module `WalletRpc` From 902c07f23f3c3ab6a0ebb361e1bb686ec2583aff Mon Sep 17 00:00:00 2001 From: Mykhailo Kremniov Date: Tue, 2 Jun 2026 11:49:25 +0300 Subject: [PATCH 05/15] Update checkpoints --- .../chain/config/checkpoints_data/mainnet.rs | 77 +++++++++++++++++++ .../chain/config/checkpoints_data/testnet.rs | 76 ++++++++++++++++++ 2 files changed, 153 insertions(+) diff --git a/common/src/chain/config/checkpoints_data/mainnet.rs b/common/src/chain/config/checkpoints_data/mainnet.rs index e4ff0b555..f7aa2d300 100644 --- a/common/src/chain/config/checkpoints_data/mainnet.rs +++ b/common/src/chain/config/checkpoints_data/mainnet.rs @@ -1031,4 +1031,81 @@ pub const CHECKPOINTS_DATA: &[(u64, &str)] = &[ (571000, "E1FE292E481108C0E175C360FEA4855E6E14379E41E3380FF62BB9CEC165FE1B"), (571500, "680FF51717E055C75BC6327EBEE87AF168B9453775805601C3C9A9DF52F9F082"), (572000, "08F370D7E17B2D62901D1C5E5E71B6134AB0A8518FBAFC7026BFFCDADE7AE038"), + (572500, "9FF71B9096AABEB1E1A68C64AFB191AD18F78F4F4A874922F85B41BD30F146F6"), + (573000, "154F8C9744BB90199234180B45AF1267A6439887BD18691A5A27753C5978E325"), + (573500, "97F9550868D0EB93A184DED8F88390E53EECCEEAA64653070579689163975F5E"), + (574000, "FA830BA235F51A9DF194D7CD4D0B89225CCC2E911B1D3C5C469F19BC6591B020"), + (574500, "54B6F3F339257E8D46EC619C8906EB435B3E8662B351421E5EE548E571643230"), + (575000, "5DC351494BF552B4F043CD91BEF4492042CE774E4679F5E7644F7E96C01406DD"), + (575500, "82BBD7DFD7C7990D091C68E9FED49884FED09E59743A41A0F5AA092B689EAB95"), + (576000, "888E895CF50D46139DC2296601DEF25BB8B82728F97E466E4D14D8E5282B84DE"), + (576500, "938599E15B2361307115322F8872F1CABCB37AB74EB51A196E9F9549F93F38B2"), + (577000, "8FB79FFFE8BE597050479F255D7896756A5F04E49FE4511EB2442677C5F303F6"), + (577500, "905528C2F12C23250348CE95FB318FD5A82573D6D9B3B1D74F34D3498B446B1B"), + (578000, "B92F84AF82489387BEB7C06AAF4DFEF2E73301ED02E9DCA1ED97D20E684B89E9"), + (578500, "D9100010DF7B61FD64DBC95604123CF989232B7BC7C0886DA021F5FD8D50C8AD"), + (579000, "44295AFD81231BE3D195BCD0D9B85EAFF34B12384752600FE9CE45EF2321A33A"), + (579500, "0C95513C2FE846C4A26A0E1D84A54F9CB2C8FA03D2665FD705640B90ECC162F9"), + (580000, "FF2ACB29B02A994119B57B5DA7F61410DA848FDCAA936E444D7BDEEBC5A98BD1"), + (580500, "CF0DB4325A7CB866282AD011C2506D258E11B5E905054D6FA3D4BC96B731DC87"), + (581000, "13154D1F904501EEA092C46886A9D24BC212BB48B5C19997F1E476B34676BC69"), + (581500, "3154CB99BD507B68908D30545573CFBE8227F2D70DAADB63EC17BAA57D0A25B2"), + (582000, "9C50E359087118288AE1602A14AAA0ADFE39EF72A2D1606C3782B622D4164F95"), + (582500, "E5C65276CE9019D308A3B8058562DF238B9875A7474DB489598705F501CE475E"), + (583000, "B88BD6E98219A4D3CBBB63471CF6146589BE986E8AB5EC5D0F055D03F27DFB6D"), + (583500, "662E95DF3D838F2C17E99E3FFD5CF0773BF3AC3618B25A682E394044BCD3F742"), + (584000, "A545E888816623E98E9AEEFC1BABB67BAE07008A438BE777DD77BA95832D1491"), + (584500, "1E727BB89E44A5D6E4772E43B07FCDF89721B0C16992C49816A55B1A72C2EB6D"), + (585000, "BCBF1FC7C967ACF6B00A7F21CAF0EFF2080BE937B8F0AE8D3AF9C87955122CC5"), + (585500, "088E1C3205E95CF95F117054560FD2640280F2CDE9D71933EC2370439DEEA9BF"), + (586000, "6813913B325E62E99270F2BE9DCADF2BEE6AA74DD1CEF5966CCA6435EE717507"), + (586500, "DF8AA27DB196F74D8A86A952DDD75789F00BC119B0ED2030E3857C5E5240089E"), + (587000, "770A40FCE1FBF89CCB43A76D2411C1B1A817106D5F8CB89768AE6E0583D84E9D"), + (587500, "1054B6E45F1D0C882E688A4C5091E2E2E39CC8F66B3A96279E9597A9475917AD"), + (588000, "15E11732DD23EBE78CC5FD703004E7DCCC96253401CEB0697063D822D77499EB"), + (588500, "B78C0930380B6B4033CF211B80AB0189DB528C9E231BE6EC30D3CA41AFDB7061"), + (589000, "043DF995514330EB269C52E6760CF4E53804699DCE1E541B720B6934D665E5FD"), + (589500, "EE82505F86230110A06C15655AE5346FDA103155DA7AA8AB4BE5CBC2CEDEA60A"), + (590000, "ADC8835919DE04689FCF3FE25FA5B8C8A3D04528D55A5B46E02D19001907C29D"), + (590500, "265D271C0D5A474F980C80D2C05A68C184FCE407D26346DBCA4A819CC01D1D57"), + (591000, "232B8307D70C45CDEE81AB0965F8B0BCFD45C3A124B8E425E57972998DA9CE14"), + (591500, "ECD2FD7A903396FF4ECC9C9DF4EBA158C8DE5CB5C6872FF9CE40473E0A9AC687"), + (592000, "8352DB0AA07F0B090A906393633E517A5EC03C90585A38E47E8F39E3610AD3D2"), + (592500, "0A325174E9741832A7FFA2E5311830989FCE581A99E8A4C88E5A182A567A41CB"), + (593000, "F53FE957CBD3C4B9CD49426F95DCAC0484B0EF6498518DCD99B5D3CB93DF3E2A"), + (593500, "EC1A95B439A3DBA96BBB6E60BEAA4E906D259B5055B5D9CAFFA8918033F67A1C"), + (594000, "74263832D93E5BA1306FEDE16B644F0E40161F674EFC383311DAF3563E583415"), + (594500, "3F3B59DEB89D0046B31F1B08979422DA41186B0138488D1B02BA031C95C0D03C"), + (595000, "39235B251ADB908D1190A025F2BA7840D222B6816643C9DC4C61F1B6E1386C2E"), + (595500, "318F6D52C41A5C3A8399D66E181587919BF99A4AB16F49602E363B2239A916B5"), + (596000, "BC574068D1682FBAA4085B8E68B581E4598F9F13565B7ABB064CEFD1B56895AC"), + (596500, "23885CA1F69C2C0DE5C0BC47C1025E1E933252E73C89334D051FE39F4DE6F086"), + (597000, "026F3F7572AE0D2FC92756CDC3CFEEB0F3757F25C0D51090F6A279088726B797"), + (597500, "23B3A6942644500B8896B32231F7B47A62496305717D58420130DFDD8539D901"), + (598000, "DAC6C47F0A2D5013973125B1C23B69DE4C85C2B25DFCB9CD8C2C9A3369033EB7"), + (598500, "D7BEB0BBFC817CBA264A4AFC2216BE171AADF929DC2A876BB58F5988D8185690"), + (599000, "857C3045927B5FBB8A2DA00F37A212D0CEFA8A4E7EDCF5041DDC37300E40DC77"), + (599500, "1855AC787B9372A53A00B3ECC45C8C63EDC2D5C292F623D1A64FE1A86CE58E22"), + (600000, "F573BFF2067EC8A5E1501178BFA0FF817417BDF4344B0E4A9A75B9681878CEB3"), + (600500, "0064604064C7FD0C381F45427D2E45D7FAE4C938D9F4D7AC527ACA0CF14345BF"), + (601000, "09077F902A869B160D4628CF140D1D127B8258817710E898553EFD00E57D2FC4"), + (601500, "5D21ABBCDEF75F0A0A2F0B141F22A55D501A3376F04A763A28C3710940F7946E"), + (602000, "E9E3F90235FB9222DCDE99BB562A29DCEEE3FBA23898323912DEC6AC04D0365D"), + (602500, "F746D40BB3D35AB372E67590099C64FA4E427C97BFC1636D20D53F4F5B4ADCB3"), + (603000, "2B08F7B90A65CBFBB375FD15917A8BFA040423D5B4F5055C44887357D47E13B7"), + (603500, "CBA56847AB4478C310C3A4445CE3FD2D7CBADEDDE0048E2E899B59952058B45B"), + (604000, "C62FA7D291C42246D1540C32007A8C164E7112029A213C7416A54B2B38FCBC57"), + (604500, "41EC6FB625B26E072E3337EF423382F841A629B39119EC8E9F78723BF72D10A5"), + (605000, "97380622B5818ADF1024105516EBB01F9931B777F7011CBB713E0767C6CFACD9"), + (605500, "67005D1D46C18808C569C43CA64A10F0C8F40AF90BFBA1A138A4257034AC4DD4"), + (606000, "DF56F9AA7A0327D789320083FCEF96939BAF32D03097803FA61DCF65EE347B15"), + (606500, "EBD9B06C1A0AE56C1A02D6AA4CE3473E67D2DE0DE2E054413606F5C7D7D8FCC5"), + (607000, "4F5A869919347634677D5BCE216A30CFDE5CB1CD09CCDBB43903AAFFC24F0BE2"), + (607500, "75F3962FA2538B89EFF29AAF80C00026FE2D183C41CB4A7D0E5752AC13B0FCD9"), + (608000, "4CAA0695EB1396B748291A12E1E2BE93F1AAB418BCB27A33B89518EF327C9089"), + (608500, "2D9B82A2D269894A4C98E3B7F2918B6E1D37C14794A85719EA661BAE9C342563"), + (609000, "6BEFA2885B689CA8D41866090EF95D9E65804AA7E5702146462A262A3132BED3"), + (609500, "713EEDDB8D27D547A69E6E056B282206CDC85914D514DA1958693C58E593010B"), + (610000, "EEC7EA398230E662121E3527BC552ECC6F707E4A292CDE654C76F1A2C6B97F79"), + (610500, "B5657FEAFA70591DE7096BF1C3CFF6134631AD612CDFD8C18818BC6AB86C98F5"), ]; diff --git a/common/src/chain/config/checkpoints_data/testnet.rs b/common/src/chain/config/checkpoints_data/testnet.rs index 35bc4a3e4..b70354e27 100644 --- a/common/src/chain/config/checkpoints_data/testnet.rs +++ b/common/src/chain/config/checkpoints_data/testnet.rs @@ -1289,4 +1289,80 @@ pub const CHECKPOINTS_DATA: &[(u64, &str)] = &[ (699000, "1BE1CA072B2917B5701076D255736E2273310BA3EE2A352A4B01ED6469039BF3"), (699500, "1D9DCFB70F0E49B9D701B5002000AD34150C20BDE776761D064A82D75473D23E"), (700000, "7C64C4C6B4004BA5C0807D1DF4AC631926A4B12B33FED40FDA285428824C8A02"), + (700500, "B0C8589D75E30ADC8F0CEC6FE882C5CB70D2F749E35D5EBF748D433964A318D8"), + (701000, "2972DB8142E0566E2F50C24232DE46428B60D50D2C4B654923FCAA159F7E2203"), + (701500, "E4F96B3270EF59628114375B1BF727ABA4D15684D8877B564160F4D2168EAC27"), + (702000, "07093CE01C05F74B34687D0CE9D7A02BCA564DEBE5083E2283831A0D9ED542BF"), + (702500, "55899518D812DD4D5DC54D7E9FA8048E1CE82FF23CB88641FB5DCDD9B91717D0"), + (703000, "124A0F31E0F91459B2AB4F320AB4C6AFFB83C984FC7D7F2A4A6FB65DFE89A8AA"), + (703500, "003586ABE3993BA06B9F9315B213A4138CDBDFB02CBACB97C5EE1E8FB617C20D"), + (704000, "FBC2ABA8D2B4F0A2EDFC87F40448C19559293F15FE7E3029DF4FB06AD7ABE118"), + (704500, "F2225A757D225D2A5ED1D13025E05705116CADEE7D0A20430F0DD5F8BEF4F131"), + (705000, "CA8BC73D5FB1C47113629002F8DD456DD227DA436B6B4544444E89AA4C671867"), + (705500, "F23E9F56A6F2579D945DAFAD6AF3A725157284491F34C44F90E9B6732D901518"), + (706000, "CD86359DE4B0B1CE7D8BE4FAADBD8C3092A18067EF700624AFC8B1B44DF60F95"), + (706500, "3262DFB6B26047B5DD1742619D6DD0673A52A2167E86A835CE569B03A228BBD5"), + (707000, "D5E27A87531E29763E3F7D711BE6AFFEDAE60C83BFFD855A137860A4658EC134"), + (707500, "93014E32C099EAA9D15F6ECA007C06DA2E02C5127468FA09EBAD6C59B85ED366"), + (708000, "148E55C9A9D0FAD47143C67B19CFB96128AA71CBFB795970A1F1D4836925D283"), + (708500, "72AF91E1A4958B4C6A949EF6A6CFF3879F99D5A8D4C42AE84CA55A6FDDB2C351"), + (709000, "8C617F301F52F6492F53C2BC71021B88B1536B29E01ED5E67510E055BBB13CB4"), + (709500, "A6DFFEE8F80922466565498A945267B1FED451EF33BE8CD5BC027AF1F1B5C59E"), + (710000, "C3083C6B254EB676EA6CDB100145B670ED89337906F86B9227BAE0DED6B19CCF"), + (710500, "697D6D97D46C07D51B53CD74B142FE6AEB9599915F4CB732D690764272737DA0"), + (711000, "8B618FC8298746F07D7F68A4679ECA51EDF50B8202EA50D482583A0910BA6AB9"), + (711500, "6696345D7CFFF9E073401DD985B67756FE87A6973DB609A0585BCF2F0E730EBD"), + (712000, "0B52B9FDD0E977183E2079C26411D3368372E20D43F1B1EF2975A9ED7F8CDF36"), + (712500, "DA724AA8874096DB15AE88E6C4FF69A6DBA50FBC7A362853438D1D91D3B02072"), + (713000, "00898F03F56262E6CB4DAE2E329F671313E14522B52FC4F83D10EF39DF8623DB"), + (713500, "38E06BAC679A35B1D7563DF569F8D467249ED2CB2699B1C6271181CD83424565"), + (714000, "53DAF384FCEF436EEA747355F78557CF0F6392021B9445E503EA1D93F0FE5D35"), + (714500, "FC5D388FEEA864FC38759F4CED291C9532C5A1ACBC9866FD18B5C3B26C121EDF"), + (715000, "6D00A8FE67A7BB0444BD7C25C31DCD618BDA8BB3F70F6CA950CAB5FA77B05729"), + (715500, "8FBE0F867714CF4CC91669C2BAAE3923397F5C0153E0FB88E759A362EDBE7679"), + (716000, "07AECB3AEB119487CECF7E6D27869786CFA57BEA1745A30A1700D43502E5F10A"), + (716500, "2A71878118FA95C6E11EBEF01A09343B84C24F08C574E38C9ED2350C39881E1B"), + (717000, "EB50CB5A4CA0F4F9BF47A44AF4BC3D7A4000581C47B74F1CF4AADC08359B22BF"), + (717500, "B635B1C21AE76CF503D00F0B454E12CDC413BE974AEEC4B366C17901321FCE4D"), + (718000, "7F9CDB5C0C0A50FC413C63F60F89F3167E8DA6280625386574DD69CFF08C635F"), + (718500, "C56291025A0BECAA559B370195541F01347D3189FEE62B006A17EDFD695128ED"), + (719000, "CBF9E262BB1C010F0E4C91E2E4D164D7F1A4FAE45FB24443F7D831804AAD46E8"), + (719500, "83C8E37ED3A071197E9FA6FDF5558421C567F4DE22CBB575AF181224516AF59C"), + (720000, "9B3713893037F5F259AF9B142FFEA5B04A17FFF8EA0293A7CFFFB5ADAECFD119"), + (720500, "277BB9066F0961C4BB37918CDB7E17D3ED11440521843297E3293D744A25C1EB"), + (721000, "4683630294E33E956EA9731FD3B418FAF8828A6AB02D3F7F7B803804A84E36C5"), + (721500, "BE2C7779050CD9ABD28EA6EF603CAF0F56CAD9024762EB964B0EE33162196F94"), + (722000, "98D577BE72031B36C2E4A57ECF68585DF8C7259F0D2B392B1C74E2A8438434F5"), + (722500, "818E3C2F9A8782EBCB93905E1A577D1BA44ED31C1728F9F39797D02047C1F6E5"), + (723000, "4CC30956A313CBF26E2784F2B3C7B245EA76F2DC7B6A54B32EF0EDB3D54A669C"), + (723500, "12AA7E2DFEA164FF3F30BB4E7078C9670738E8B9B8092B194F39DC4D4EDC6D62"), + (724000, "397006774D0A2054FB75C2F3988C3440235749FF8625B85B4F008EC8BABB16D4"), + (724500, "454559EAEC445CCA2A585A9763DC938D66ECE93818372E78F19E98EA89011530"), + (725000, "A48168E5A3D46D63E8B900E28E4B6F556D4EF0BD431E2C59926293C27E8C297B"), + (725500, "D2A267354419FF462214779E60B74342CF2FC485AEA6DE14F39CD9F9BA80CBC4"), + (726000, "B403317107F0D9E02F89E1F34A8DC96328FA917FB9329077AB84388A6D19BBA5"), + (726500, "3B2D32D9AB8B44F067C9D61C547884E5CAD0A3BB6113390C179976960EE0F3D4"), + (727000, "23AD6E1A6CFFB967CBF64A9D7654E8DAE09AF4CA18710A5D5BAD369C49656F2C"), + (727500, "A6C3C31BE9708D80F3894924BE2303DA7E43A929AA533879F8C02BF5769981E5"), + (728000, "6DC9464D27B2D81B8826412520B131F3D50D0F2C3777CDA20C531F7DD8D7A559"), + (728500, "3ED0BE60943942DE55AB455BF786BC74573424F5B6499564119AD5CA86BFF843"), + (729000, "8188A961C0D999A219429AA38DC937865154F4FFA6F52CF42C94EE710D1D5AFD"), + (729500, "E06AC361B6749A024F91D777030FE2CB9650C1574E5DE2565DFA73DB6C836E93"), + (730000, "6F4B3620AE3AC97DB9DFA2DD57A540DD0DB78A14DB66FE290137B328347AC69A"), + (730500, "3DB6A56E2AFCD453FC721290ABFD3B63D50E36C8505F2E1EDF6847852DFE915A"), + (731000, "532305C427CDF24CB8E429EDABEF3B0299256935419909BB5F8D23ED9B9B1854"), + (731500, "18ABC13E9122EE13AEF49C8330F6CD41A5E9F29E171572D5C25D30C3C4455E83"), + (732000, "DF0DFB4A5D069FA9FE0B2E45185E29A5E8F54EAF210664E1DAFF7C534359A90A"), + (732500, "1F7814610C3B71DC81582133A9C4A9014FA518EA0A0696E2DC92C5FE465EBFB3"), + (733000, "EAF8BFC36FC4FCE5448654DCF808FC4F971DC57945A15C5277518B8E91650EB3"), + (733500, "B6143131629322D1E030692F6ED24B022AAC9AA493C254E13D2C44D9303AFF34"), + (734000, "3CF89CBEAF917C5171F266BA4FF8C50C18507CF911A79A6B0E3340958B637409"), + (734500, "50DD1C4AA1D1DC2146978E53011A33BABE8115A1C0D407E18A19DADBCC31AD9A"), + (735000, "EB3BFC1D1D8E3EB8BC15D53B808AF78FF159DCF4D3810E9811608CAEDA34A2DB"), + (735500, "4951BE78DE943D1A8744E95FCEEB2B0EBAA31323A1C526DEFE8E094CA7A45AFD"), + (736000, "651A0BCB3904BA3383361E31E27E09FFEFD4B679F3C2FE995DCB0F1C43CA1755"), + (736500, "A68B45E302F3811FAA71C77F4A465949A1E600E84C7BABF07B538E72A261F72F"), + (737000, "AED233AFB23BCF49DA9AD6AD27A8F77C25F366BF60A37E6803474C154B424785"), + (737500, "6B5E866C4198463DFB9BB7FC2BCEC755369800429A275847912B2EE3BC1E18AE"), + (738000, "02C9E23C5E990419CD591703A1DB54C28F31E4DB5EDBA1ED88C7CE9DBF4D1142"), ]; From 67f019e6ca79685e1cdc52618a1566a65d8be941 Mon Sep 17 00:00:00 2001 From: Mykhailo Kremniov Date: Fri, 15 May 2026 14:28:11 +0300 Subject: [PATCH 06/15] Bump min rust version to 1.91 (wasm-pack v0.15 needs it) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 13a944924..4cdc69bad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -127,7 +127,7 @@ utxo = { path = "utxo" } [workspace.package] edition = "2021" -rust-version = "1.88" +rust-version = "1.91" version = "1.3.1" license = "MIT" From 363b52b490a184de49df1d66c96edc7c5015ac4c Mon Sep 17 00:00:00 2001 From: Mykhailo Kremniov Date: Mon, 1 Jun 2026 17:03:39 +0300 Subject: [PATCH 07/15] Always use input commitments v1 in cold wallet mode --- wallet/src/signer/ledger_signer/mod.rs | 13 +- wallet/src/signer/ledger_signer/tests/mod.rs | 24 +- wallet/src/signer/mod.rs | 7 +- wallet/src/signer/software_signer/mod.rs | 58 ++++- wallet/src/signer/software_signer/tests.rs | 35 ++- .../tests/generic_fixed_signature_tests.rs | 25 +- wallet/src/signer/tests/generic_tests.rs | 23 +- wallet/src/signer/tests/mod.rs | 12 +- wallet/src/signer/trezor_signer/mod.rs | 13 +- wallet/src/signer/trezor_signer/tests.rs | 34 ++- wallet/src/wallet/mod.rs | 3 +- wallet/src/wallet/test_helpers.rs | 75 ++++-- wallet/src/wallet/tests.rs | 238 +++++++++++++++++- wallet/types/src/wallet_type.rs | 21 ++ 14 files changed, 485 insertions(+), 96 deletions(-) diff --git a/wallet/src/signer/ledger_signer/mod.rs b/wallet/src/signer/ledger_signer/mod.rs index 1ec3d2ceb..dca77fc26 100644 --- a/wallet/src/signer/ledger_signer/mod.rs +++ b/wallet/src/signer/ledger_signer/mod.rs @@ -1229,8 +1229,17 @@ impl SignerProvider for LedgerSignerProvider { type S = LedgerSigner; type K = AccountKeyChainImplHardware; - fn provide(&mut self, chain_config: Arc, _account_index: U31) -> Self::S { - LedgerSigner::new(chain_config, self.client.clone(), self.clone()) + fn provide( + &mut self, + chain_config: Arc, + _account_index: U31, + _db_tx: &impl WalletStorageReadLocked, + ) -> WalletResult { + Ok(LedgerSigner::new( + chain_config, + self.client.clone(), + self.clone(), + )) } async fn make_new_account( diff --git a/wallet/src/signer/ledger_signer/tests/mod.rs b/wallet/src/signer/ledger_signer/tests/mod.rs index c59a7017f..8f1d5f560 100644 --- a/wallet/src/signer/ledger_signer/tests/mod.rs +++ b/wallet/src/signer/ledger_signer/tests/mod.rs @@ -316,13 +316,10 @@ async fn test_sign_transaction_intent(#[case] seed: Seed) { #[rstest] #[trace] #[serial_test::serial] -#[case(Seed::from_entropy(), SighashInputCommitmentVersion::V1)] +#[case(Seed::from_entropy())] #[tokio::test(flavor = "multi_thread", worker_threads = 1)] -async fn test_sign_transaction( - #[case] seed: Seed, - #[case] input_commitments_version: SighashInputCommitmentVersion, -) { - log::debug!("test_sign_transaction, seed = {seed:?}, input_commitments_version = {input_commitments_version:?}"); +async fn test_sign_transaction(#[case] seed: Seed) { + log::debug!("test_sign_transaction, seed = {seed:?}"); let (auto_confirmer_handle, control_msg_tx, make_ledger_signer) = setup(false).await; @@ -330,7 +327,8 @@ async fn test_sign_transaction( test_sign_transaction_generic( &mut rng, - input_commitments_version, + false, + SighashInputCommitmentVersion::V1, make_ledger_signer, no_another_signer(), false, @@ -397,13 +395,10 @@ async fn test_sign_transaction_intent_sig_consistency(#[case] seed: Seed) { #[rstest] #[trace] #[serial_test::serial] -#[case(Seed::from_entropy(), SighashInputCommitmentVersion::V1)] +#[case(Seed::from_entropy())] #[tokio::test(flavor = "multi_thread", worker_threads = 1)] -async fn test_sign_transaction_sig_consistency( - #[case] seed: Seed, - #[case] input_commitments_version: SighashInputCommitmentVersion, -) { - log::debug!("test_sign_transaction_sig_consistency, seed = {seed:?}, input_commitments_version = {input_commitments_version:?}"); +async fn test_sign_transaction_sig_consistency(#[case] seed: Seed) { + log::debug!("test_sign_transaction_sig_consistency, seed = {seed:?}"); let (auto_confirmer_handle, control_msg_tx, make_ledger_signer) = setup(true).await; @@ -411,7 +406,8 @@ async fn test_sign_transaction_sig_consistency( test_sign_transaction_generic( &mut rng, - input_commitments_version, + false, + SighashInputCommitmentVersion::V1, make_ledger_signer, Some(make_deterministic_software_signer), false, diff --git a/wallet/src/signer/mod.rs b/wallet/src/signer/mod.rs index 80a6dbaab..25b598a9d 100644 --- a/wallet/src/signer/mod.rs +++ b/wallet/src/signer/mod.rs @@ -182,7 +182,12 @@ pub trait SignerProvider: Send { type S: Signer + Send; type K: AccountKeyChains + Sync + Send; - fn provide(&mut self, chain_config: Arc, account_index: U31) -> Self::S; + fn provide( + &mut self, + chain_config: Arc, + account_index: U31, + db_tx: &impl WalletStorageReadLocked, + ) -> WalletResult; async fn make_new_account( &mut self, diff --git a/wallet/src/signer/software_signer/mod.rs b/wallet/src/signer/software_signer/mod.rs index b163a34aa..fb015448f 100644 --- a/wallet/src/signer/software_signer/mod.rs +++ b/wallet/src/signer/software_signer/mod.rs @@ -39,7 +39,8 @@ use common::{ }, DestinationSigError, }, - ChainConfig, Destination, SignedTransactionIntent, Transaction, TxOutput, + ChainConfig, Destination, SighashInputCommitmentVersion, SignedTransactionIntent, + Transaction, TxOutput, }, primitives::BlockHeight, }; @@ -49,7 +50,7 @@ use crypto::key::{ PredefinedSigAuxDataProvider, PrivateKey, SigAuxDataProvider, }; use randomness::make_true_rng; -use utils::ensure; +use utils::{debug_panic_or_log, ensure}; use wallet_storage::{ WalletStorageReadLocked, WalletStorageReadUnlocked, WalletStorageWriteUnlocked, }; @@ -58,7 +59,7 @@ use wallet_types::{ partially_signed_transaction::{PartiallySignedTransaction, TokensAdditionalInfo}, seed_phrase::StoreSeedPhrase, signature_status::SignatureStatus, - wallet_type::WalletType, + wallet_type::SoftwareWalletType, AccountId, }; @@ -76,11 +77,16 @@ use super::{utils::is_htlc_utxo, Signer, SignerError, SignerProvider, SignerResu pub struct SoftwareSigner { chain_config: Arc, account_index: U31, + wallet_type: SoftwareWalletType, sig_aux_data_provider: Mutex>, } impl SoftwareSigner { - pub fn new(chain_config: Arc, account_index: U31) -> Self { + pub fn new( + chain_config: Arc, + account_index: U31, + wallet_type: SoftwareWalletType, + ) -> Self { let use_deterministic_signer = *chain_config.chain_type() == ChainType::Regtest && cfg!(feature = "use-deterministic-signatures-in-software-signer-for-regtest"); @@ -88,12 +94,14 @@ impl SoftwareSigner { Self::new_with_sig_aux_data_provider( chain_config, account_index, + wallet_type, Box::new(PredefinedSigAuxDataProvider), ) } else { Self::new_with_sig_aux_data_provider( chain_config, account_index, + wallet_type, Box::new(make_true_rng()), ) } @@ -102,11 +110,13 @@ impl SoftwareSigner { pub fn new_with_sig_aux_data_provider( chain_config: Arc, account_index: U31, + wallet_type: SoftwareWalletType, sig_aux_data_provider: Box, ) -> Self { Self { chain_config, account_index, + wallet_type, sig_aux_data_provider: Mutex::new(sig_aux_data_provider), } } @@ -291,8 +301,19 @@ impl Signer for SoftwareSigner { Vec, Vec, )> { - let input_commitments = - ptx.make_sighash_input_commitments_at_height(&self.chain_config, block_height)?; + let input_commitments = match self.wallet_type { + SoftwareWalletType::Hot => { + ptx.make_sighash_input_commitments_at_height(&self.chain_config, block_height)? + } + SoftwareWalletType::Cold => { + // Wallet in the cold mode is not aware of the actual chain height, so block_height + // will always be zero here. Since at the moment of writing this the fork has already + // happened both on testnet and mainnet, we can unconditionally assume input commitments v1. + // TODO: remove the support of input commitments v0 in the wallet, always assume v1. + // Same for orders v0/v1. + ptx.make_sighash_input_commitments(SighashInputCommitmentVersion::V1)? + } + }; let (witnesses, prev_statuses, new_statuses) = ptx .witnesses() @@ -478,9 +499,10 @@ impl SoftwareSignerProvider { ) -> WalletResult { let this_wallet_type = db_tx.get_wallet_type()?; ensure!( - this_wallet_type == WalletType::Hot || this_wallet_type == WalletType::Cold, + this_wallet_type.to_software_wallet_type().is_some(), WalletError::HardwareWalletOpenedAsSoftwareWallet(this_wallet_type) ); + let master_key_chain = MasterKeyChain::new_from_existing_database(chain_config, db_tx)?; Ok(Self { master_key_chain }) } @@ -491,8 +513,26 @@ impl SignerProvider for SoftwareSignerProvider { type S = SoftwareSigner; type K = AccountKeyChainImplSoftware; - fn provide(&mut self, chain_config: Arc, account_index: U31) -> Self::S { - SoftwareSigner::new(chain_config, account_index) + fn provide( + &mut self, + chain_config: Arc, + account_index: U31, + db_tx: &impl WalletStorageReadLocked, + ) -> WalletResult { + let wallet_type = db_tx.get_wallet_type()?; + let software_wallet_type = + wallet_type.to_software_wallet_type().unwrap_or_else(|| { + debug_panic_or_log!( + "Db tx related to a hardware wallet ({wallet_type:?}) was passed to SoftwareSignerProvider::provide" + ); + SoftwareWalletType::Hot + }); + + Ok(SoftwareSigner::new( + chain_config, + account_index, + software_wallet_type, + )) } async fn make_new_account( diff --git a/wallet/src/signer/software_signer/tests.rs b/wallet/src/signer/software_signer/tests.rs index bf4a8acb2..17dbb9f36 100644 --- a/wallet/src/signer/software_signer/tests.rs +++ b/wallet/src/signer/software_signer/tests.rs @@ -27,7 +27,8 @@ use crate::signer::tests::{ test_sign_message_generic, test_sign_transaction_generic, test_sign_transaction_intent_generic, MessageToSign, }, - make_deterministic_software_signer, make_software_signer, no_another_signer, + make_deterministic_software_signer, make_software_signer, make_software_signer_for_cold_wallet, + no_another_signer, }; #[rstest] @@ -58,19 +59,21 @@ async fn test_sign_transaction_intent(#[case] seed: Seed) { #[rstest] #[trace] -#[case(Seed::from_entropy(), SighashInputCommitmentVersion::V0)] +#[case(Seed::from_entropy(), true, SighashInputCommitmentVersion::V0)] #[trace] -#[case(Seed::from_entropy(), SighashInputCommitmentVersion::V1)] +#[case(Seed::from_entropy(), false, SighashInputCommitmentVersion::V1)] #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn test_sign_transaction( #[case] seed: Seed, - #[case] input_commitments_version: SighashInputCommitmentVersion, + #[case] before_fork: bool, + #[case] expected_input_commitments_version: SighashInputCommitmentVersion, ) { let mut rng = make_seedable_rng(seed); test_sign_transaction_generic( &mut rng, - input_commitments_version, + before_fork, + expected_input_commitments_version, make_software_signer, no_another_signer(), true, @@ -78,6 +81,28 @@ async fn test_sign_transaction( .await; } +// Cold wallet should assume v1 commitments regardless of what it thinks the current height is. +#[rstest] +#[case(Seed::from_entropy())] +#[trace] +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn test_sign_transaction_cold_wallet( + #[case] seed: Seed, + #[values(false, true)] before_fork: bool, +) { + let mut rng = make_seedable_rng(seed); + + test_sign_transaction_generic( + &mut rng, + before_fork, + SighashInputCommitmentVersion::V1, + make_software_signer_for_cold_wallet, + no_another_signer(), + true, + ) + .await; +} + #[rstest] #[trace] #[case(Seed::from_entropy())] diff --git a/wallet/src/signer/tests/generic_fixed_signature_tests.rs b/wallet/src/signer/tests/generic_fixed_signature_tests.rs index 8a1dda233..a0d8e200f 100644 --- a/wallet/src/signer/tests/generic_fixed_signature_tests.rs +++ b/wallet/src/signer/tests/generic_fixed_signature_tests.rs @@ -387,9 +387,8 @@ pub async fn test_fixed_signatures_generic( .unwrap(); assert!(ptx.all_signatures_available()); - let input_commitments = ptx - .make_sighash_input_commitments_at_height(&chain_config, tx_block_height) - .unwrap(); + let expected_input_commitments = + ptx.make_sighash_input_commitments(SighashInputCommitmentVersion::V0).unwrap(); let all_utxos = utxos .iter() .map(Some) @@ -402,7 +401,7 @@ pub async fn test_fixed_signatures_generic( &chain_config, dest, &ptx, - &input_commitments, + &expected_input_commitments, i, all_utxos[i].cloned(), ) @@ -911,8 +910,8 @@ pub async fn test_fixed_signatures_generic2( ); let ptx = req.into_partially_signed_tx(ptx_additional_info).unwrap(); - let input_commitments = ptx - .make_sighash_input_commitments_at_height(&chain_config, tx_block_height) + let expected_input_commitments = ptx + .make_sighash_input_commitments(input_commitments_version) .unwrap() .into_iter() .map(|comm| comm.deep_clone()) @@ -952,7 +951,7 @@ pub async fn test_fixed_signatures_generic2( &chain_config, dest, &ptx, - &input_commitments, + &expected_input_commitments, i, all_utxos[i].cloned(), ) @@ -1536,8 +1535,8 @@ pub async fn test_fixed_signatures_generic_no_legacy( ); let ptx = req.into_partially_signed_tx(ptx_additional_info).unwrap(); - let input_commitments = ptx - .make_sighash_input_commitments_at_height(&chain_config, tx_block_height) + let expected_input_commitments = ptx + .make_sighash_input_commitments(SighashInputCommitmentVersion::V1) .unwrap() .into_iter() .map(|comm| comm.deep_clone()) @@ -1577,7 +1576,7 @@ pub async fn test_fixed_signatures_generic_no_legacy( &chain_config, dest, &ptx, - &input_commitments, + &expected_input_commitments, i, all_utxos[i].cloned(), ) @@ -1901,8 +1900,8 @@ pub async fn test_fixed_signatures_generic_htlc_refunding( ); let ptx = req.into_partially_signed_tx(ptx_additional_info).unwrap(); - let input_commitments = ptx - .make_sighash_input_commitments_at_height(&chain_config, tx_block_height) + let expected_input_commitments = ptx + .make_sighash_input_commitments(input_commitments_version) .unwrap() .into_iter() .map(|comm| comm.deep_clone()) @@ -1944,7 +1943,7 @@ pub async fn test_fixed_signatures_generic_htlc_refunding( &chain_config, dest, &ptx, - &input_commitments, + &expected_input_commitments, i, all_utxos[i].cloned(), ) diff --git a/wallet/src/signer/tests/generic_tests.rs b/wallet/src/signer/tests/generic_tests.rs index 0f21c305f..9af9f5640 100644 --- a/wallet/src/signer/tests/generic_tests.rs +++ b/wallet/src/signer/tests/generic_tests.rs @@ -319,9 +319,15 @@ pub async fn test_sign_transaction_intent_generic( assert_eq!(err, SignerError::DestinationNotFromThisWallet); } +// Note: unlike some other tests (in particular, the "fixed signature" ones) that only accept +// input_commitments_version, which determines both the expected commitments and the height +// at which the signatures will be produced, this test accepts both the expected version +// and the flag `before_fork`, which determines the height. This is used in the software signer +// tests to check that in the cold mode v1 commitments are used regardless of the current height. pub async fn test_sign_transaction_generic( rng: &mut (impl Rng + CryptoRng), - input_commitments_version: SighashInputCommitmentVersion, + before_fork: bool, + expected_input_commitments_version: SighashInputCommitmentVersion, make_signer: MkS1, make_another_signer: Option, include_orders_v0: bool, @@ -333,9 +339,10 @@ pub async fn test_sign_transaction_generic( { let (sighash_input_commitment_version_fork_height, tx_block_height) = { let fork_height = rng.gen_range(100..100_000); - let tx_block_height = match input_commitments_version { - SighashInputCommitmentVersion::V0 => rng.gen_range(1..fork_height), - SighashInputCommitmentVersion::V1 => rng.gen_range(fork_height..fork_height * 2), + let tx_block_height = if before_fork { + rng.gen_range(1..fork_height) + } else { + rng.gen_range(fork_height..fork_height * 2) }; ( BlockHeight::new(fork_height), @@ -888,8 +895,8 @@ pub async fn test_sign_transaction_generic( assert_eq!(ptx, another_ptx); } - let input_commitments = ptx - .make_sighash_input_commitments_at_height(&chain_config, tx_block_height) + let expected_input_commitments = ptx + .make_sighash_input_commitments(expected_input_commitments_version) .unwrap() .into_iter() .map(|comm| comm.deep_clone()) @@ -908,7 +915,7 @@ pub async fn test_sign_transaction_generic( &chain_config, dest, &ptx, - &input_commitments, + &expected_input_commitments, i, all_utxos[i].cloned(), ) @@ -983,7 +990,7 @@ pub async fn test_sign_transaction_generic( &chain_config, dest, &ptx, - &input_commitments, + &expected_input_commitments, i, all_utxos[i].cloned(), ) diff --git a/wallet/src/signer/tests/mod.rs b/wallet/src/signer/tests/mod.rs index f7614cb55..caa63620c 100644 --- a/wallet/src/signer/tests/mod.rs +++ b/wallet/src/signer/tests/mod.rs @@ -21,7 +21,7 @@ use std::sync::Arc; use common::chain::ChainConfig; use crypto::key::{hdkd::u31::U31, PredefinedSigAuxDataProvider}; use wallet_storage::StoreTxRwUnlocked; -use wallet_types::seed_phrase::StoreSeedPhrase; +use wallet_types::{seed_phrase::StoreSeedPhrase, wallet_type::SoftwareWalletType}; use crate::{ key_chain::{AccountKeyChainImplSoftware, MasterKeyChain, LOOKAHEAD_SIZE}, @@ -58,7 +58,14 @@ fn account_from_mnemonic( } pub fn make_software_signer(chain_config: Arc, account_index: U31) -> SoftwareSigner { - SoftwareSigner::new(chain_config, account_index) + SoftwareSigner::new(chain_config, account_index, SoftwareWalletType::Hot) +} + +pub fn make_software_signer_for_cold_wallet( + chain_config: Arc, + account_index: U31, +) -> SoftwareSigner { + SoftwareSigner::new(chain_config, account_index, SoftwareWalletType::Cold) } // Return a SoftwareSigner that will produce Trezor-like signatures. @@ -69,6 +76,7 @@ pub fn make_deterministic_software_signer( SoftwareSigner::new_with_sig_aux_data_provider( chain_config, account_index, + SoftwareWalletType::Hot, Box::new(PredefinedSigAuxDataProvider), ) } diff --git a/wallet/src/signer/trezor_signer/mod.rs b/wallet/src/signer/trezor_signer/mod.rs index eb6b0644d..f8c85a5b7 100644 --- a/wallet/src/signer/trezor_signer/mod.rs +++ b/wallet/src/signer/trezor_signer/mod.rs @@ -1781,8 +1781,17 @@ impl SignerProvider for TrezorSignerProvider { type S = TrezorSigner; type K = AccountKeyChainImplHardware; - fn provide(&mut self, chain_config: Arc, _account_index: U31) -> Self::S { - TrezorSigner::new(chain_config, self.client.clone(), self.session_id.clone()) + fn provide( + &mut self, + chain_config: Arc, + _account_index: U31, + _db_tx: &impl WalletStorageReadLocked, + ) -> WalletResult { + Ok(TrezorSigner::new( + chain_config, + self.client.clone(), + self.session_id.clone(), + )) } async fn make_new_account( diff --git a/wallet/src/signer/trezor_signer/tests.rs b/wallet/src/signer/trezor_signer/tests.rs index 6ccdaab64..13cf526d8 100644 --- a/wallet/src/signer/trezor_signer/tests.rs +++ b/wallet/src/signer/trezor_signer/tests.rs @@ -112,16 +112,22 @@ async fn test_sign_transaction_intent(#[case] seed: Seed) { #[rstest] #[trace] #[serial] -#[case(Seed::from_entropy(), SighashInputCommitmentVersion::V0)] +#[case(Seed::from_entropy(), true, SighashInputCommitmentVersion::V0)] #[trace] #[serial] -#[case(Seed::from_entropy(), SighashInputCommitmentVersion::V1)] +#[case(Seed::from_entropy(), false, SighashInputCommitmentVersion::V1)] #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn test_sign_transaction( #[case] seed: Seed, - #[case] input_commitments_version: SighashInputCommitmentVersion, + #[case] before_fork: bool, + #[case] expected_input_commitments_version: SighashInputCommitmentVersion, ) { - log::debug!("test_sign_transaction, seed = {seed:?}, input_commitments_version = {input_commitments_version:?}"); + log::debug!( + "test_sign_transaction, seed = {:?}, before_fork = {}, expected_input_commitments_version = {:?}", + seed, + before_fork, + expected_input_commitments_version + ); let _join_guard = maybe_spawn_auto_confirmer(); @@ -129,7 +135,8 @@ async fn test_sign_transaction( test_sign_transaction_generic( &mut rng, - input_commitments_version, + before_fork, + expected_input_commitments_version, make_trezor_signer, no_another_signer(), true, @@ -258,16 +265,22 @@ async fn test_sign_transaction_intent_sig_consistency(#[case] seed: Seed) { #[rstest] #[trace] #[serial] -#[case(Seed::from_entropy(), SighashInputCommitmentVersion::V0)] +#[case(Seed::from_entropy(), true, SighashInputCommitmentVersion::V0)] #[trace] #[serial] -#[case(Seed::from_entropy(), SighashInputCommitmentVersion::V1)] +#[case(Seed::from_entropy(), false, SighashInputCommitmentVersion::V1)] #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn test_sign_transaction_sig_consistency( #[case] seed: Seed, - #[case] input_commitments_version: SighashInputCommitmentVersion, + #[case] before_fork: bool, + #[case] expected_input_commitments_version: SighashInputCommitmentVersion, ) { - log::debug!("test_sign_transaction_sig_consistency, seed = {seed:?}, input_commitments_version = {input_commitments_version:?}"); + log::debug!( + "test_sign_transaction_sig_consistency, seed = {:?}, before_fork = {}, expected_input_commitments_version = {:?}", + seed, + before_fork, + expected_input_commitments_version + ); let _join_guard = maybe_spawn_auto_confirmer(); @@ -275,7 +288,8 @@ async fn test_sign_transaction_sig_consistency( test_sign_transaction_generic( &mut rng, - input_commitments_version, + before_fork, + expected_input_commitments_version, make_deterministic_trezor_signer, Some(make_deterministic_software_signer), true, diff --git a/wallet/src/wallet/mod.rs b/wallet/src/wallet/mod.rs index b99edde20..6b768ba20 100644 --- a/wallet/src/wallet/mod.rs +++ b/wallet/src/wallet/mod.rs @@ -1190,7 +1190,8 @@ where let account = Self::get_account_mut(&mut self.accounts, account_index)?; let mut db_tx = self.db.transaction_rw_unlocked(None)?; let result = create_request(account, &mut db_tx); - let signer = self.signer_provider.provide(self.chain_config.clone(), account_index); + let signer = + self.signer_provider.provide(self.chain_config.clone(), account_index, &db_tx)?; let config = self.chain_config.clone(); let result = sign_request(result, account.key_chain(), &mut db_tx, config, signer).await; diff --git a/wallet/src/wallet/test_helpers.rs b/wallet/src/wallet/test_helpers.rs index dc3b90cf5..1aa7776af 100644 --- a/wallet/src/wallet/test_helpers.rs +++ b/wallet/src/wallet/test_helpers.rs @@ -25,7 +25,10 @@ use common::{ primitives::BlockHeight, }; use wallet_storage::{DefaultBackend, Store}; -use wallet_types::{seed_phrase::StoreSeedPhrase, wallet_type::WalletType}; +use wallet_types::{ + seed_phrase::StoreSeedPhrase, + wallet_type::{WalletControllerMode, WalletType}, +}; use crate::{ signer::{software_signer::SoftwareSignerProvider, SignerProvider}, @@ -38,27 +41,15 @@ pub async fn create_wallet_with_mnemonic( chain_config: Arc, mnemonic: &str, ) -> DefaultWallet { - let db = create_wallet_in_memory().unwrap(); - let genesis_block_id = chain_config.genesis_block_id(); - Wallet::create_new_wallet( - chain_config.clone(), - db, - (BlockHeight::new(0), genesis_block_id), - WalletType::Hot, - async |db_tx| { - Ok(SoftwareSignerProvider::new_from_mnemonic( - chain_config, - db_tx, - mnemonic, - None, - StoreSeedPhrase::DoNotStore, - )?) - }, - ) - .await - .unwrap() - .wallet() - .unwrap() + create_wallet_with_type_and_mnemonic(chain_config, WalletType::Hot, mnemonic).await +} + +pub async fn create_wallet_with_type_and_mnemonic( + chain_config: Arc, + wallet_type: WalletType, + mnemonic: &str, +) -> DefaultWallet { + create_wallet_generic(chain_config, wallet_type, mnemonic, None).await } pub fn create_named_in_memory_backend(db_name: &str) -> DefaultBackend { @@ -74,13 +65,26 @@ pub async fn create_wallet_with_mnemonic_and_named_db( mnemonic: &str, db_name: &str, ) -> DefaultWallet { - let db = create_named_in_memory_store(db_name); + create_wallet_generic(chain_config, WalletType::Hot, mnemonic, Some(db_name)).await +} + +pub async fn create_wallet_generic( + chain_config: Arc, + wallet_type: WalletType, + mnemonic: &str, + db_name: Option<&str>, +) -> DefaultWallet { + let db = if let Some(db_name) = db_name { + create_named_in_memory_store(db_name) + } else { + create_wallet_in_memory().unwrap() + }; let genesis_block_id = chain_config.genesis_block_id(); Wallet::create_new_wallet( chain_config.clone(), db, (BlockHeight::new(0), genesis_block_id), - WalletType::Hot, + wallet_type, async |db_tx| { SoftwareSignerProvider::new_from_mnemonic( chain_config, @@ -98,6 +102,29 @@ pub async fn create_wallet_with_mnemonic_and_named_db( .unwrap() } +pub async fn load_wallet( + chain_config: Arc, + db_name: &str, + controller_mode: WalletControllerMode, + force_change_wallet_type: bool, +) -> DefaultWallet { + let db = create_named_in_memory_store(db_name); + + Wallet::load_wallet( + Arc::clone(&chain_config), + db, + None, + |_| Ok(()), + controller_mode, + force_change_wallet_type, + async |db_tx| SoftwareSignerProvider::load_from_database(chain_config, &db_tx), + ) + .await + .unwrap() + .wallet() + .unwrap() +} + pub async fn scan_wallet(wallet: &mut Wallet, height: BlockHeight, blocks: Vec) where B: storage::BackendWithSendableTransactions + 'static, diff --git a/wallet/src/wallet/tests.rs b/wallet/src/wallet/tests.rs index 1ef8e95ab..35dd9e8b5 100644 --- a/wallet/src/wallet/tests.rs +++ b/wallet/src/wallet/tests.rs @@ -24,6 +24,7 @@ use rstest::rstest; use common::{ address::pubkeyhash::PublicKeyHash, chain::{ + self, block::{consensus_data::PoSData, timestamp::BlockTimestamp, BlockReward, ConsensusData}, config::{create_mainnet, create_regtest, create_unit_test_config, Builder, ChainType}, output_value::{OutputValue, RpcOutputValue}, @@ -32,7 +33,7 @@ use common::{ timelock::OutputTimeLock, tokens::{RPCIsTokenFrozen, TokenData, TokenIssuanceV0, TokenIssuanceV1}, AccountNonce, AccountSpending, ChainstateUpgradeBuilder, Currency, Destination, Genesis, - OutPointSourceId, TxInput, + NetUpgrades, OutPointSourceId, SighashInputCommitmentVersion, TxInput, }, primitives::{per_thousand::PerThousand, Idable, H256}, }; @@ -69,8 +70,9 @@ use crate::{ send_request::{make_address_output, make_create_delegation_output}, signer::software_signer::SoftwareSignerProvider, wallet::test_helpers::{ - create_named_in_memory_backend, create_named_in_memory_store, create_wallet_with_mnemonic, - create_wallet_with_mnemonic_and_named_db, scan_wallet, + create_named_in_memory_backend, create_named_in_memory_store, create_wallet_generic, + create_wallet_with_mnemonic, create_wallet_with_mnemonic_and_named_db, + create_wallet_with_type_and_mnemonic, load_wallet, scan_wallet, }, wallet_events::WalletEventsNoOp, DefaultWallet, @@ -78,6 +80,11 @@ use crate::{ use super::*; +#[ctor::ctor] +fn init() { + logging::init_logging(); +} + // TODO: Many of these tests require randomization... const MNEMONIC: &str = @@ -5493,7 +5500,12 @@ async fn sign_decommission_pool_request_cold_wallet(#[case] seed: Seed) { // create cold wallet that is not synced and only contains decommission key let another_mnemonic = "legal winner thank year wave sausage worth useful legal winner thank yellow"; - let mut cold_wallet = create_wallet_with_mnemonic(chain_config.clone(), another_mnemonic).await; + let mut cold_wallet = create_wallet_with_type_and_mnemonic( + chain_config.clone(), + *[WalletType::Hot, WalletType::Cold].choose(&mut rng).unwrap(), + another_mnemonic, + ) + .await; let decommission_key = cold_wallet.get_new_address(DEFAULT_ACCOUNT_INDEX).unwrap().1; let coin_balance = get_coin_balance(&hot_wallet); @@ -5596,6 +5608,217 @@ async fn sign_decommission_pool_request_cold_wallet(#[case] seed: Seed) { assert_eq!(coin_balance, pool_amount,); } +// Check that signing a pool decommission tx from a cold wallet produces signatures using +// input commitments v1. +#[rstest] +#[case(Seed::from_entropy())] +#[trace] +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn sign_decommission_pool_request_in_cold_wallet_expect_input_commitments_v1( + #[case] seed: Seed, + #[values(false, true)] reload_as_cold: bool, +) { + let mut rng = make_seedable_rng(seed); + + let input_commitments_v1_fork_height = BlockHeight::new(4); + let chain_config = chain::config::Builder::new(ChainType::Regtest) + .chainstate_upgrades({ + NetUpgrades::initialize(vec![ + ( + BlockHeight::zero(), + ChainstateUpgradeBuilder::latest() + .sighash_input_commitment_version(SighashInputCommitmentVersion::V0) + .build(), + ), + ( + input_commitments_v1_fork_height, + ChainstateUpgradeBuilder::latest() + .sighash_input_commitment_version(SighashInputCommitmentVersion::V1) + .build(), + ), + ]) + .unwrap() + }) + .build(); + + let chain_config = Arc::new(chain_config); + + let mut hot_wallet = create_wallet(Arc::clone(&chain_config)).await; + + // Create a cold wallet. If reload_as_cold is true, first create a hot wallet and force-reload + // it as cold. Otherwise just create a cold wallet. + let another_mnemonic = + "legal winner thank year wave sausage worth useful legal winner thank yellow"; + let mut cold_wallet = if reload_as_cold { + let db_name = random_ascii_alphanumeric_string(&mut rng, 10..20); + let _tmp_wallet = create_wallet_generic( + Arc::clone(&chain_config), + WalletType::Hot, + another_mnemonic, + Some(&db_name), + ) + .await; + + load_wallet( + Arc::clone(&chain_config), + &db_name, + WalletControllerMode::Cold, + true, + ) + .await + } else { + create_wallet_generic( + Arc::clone(&chain_config), + WalletType::Cold, + another_mnemonic, + None, + ) + .await + }; + let decommission_key = + cold_wallet.get_new_address(DEFAULT_ACCOUNT_INDEX).unwrap().1.into_object(); + + let coin_balance = get_coin_balance(&hot_wallet); + assert_eq!(coin_balance, Amount::ZERO); + + // Generate a new block which sends reward to the wallet + let block1_amount = Amount::from_atoms(rng.gen_range(NETWORK_FEE + 100..NETWORK_FEE + 10000)); + let (_, _block1) = create_block(&chain_config, &mut hot_wallet, vec![], block1_amount, 0).await; + + let pool_ids = hot_wallet.get_pools(DEFAULT_ACCOUNT_INDEX, WalletPoolsFilter::All).unwrap(); + assert!(pool_ids.is_empty()); + + let coin_balance = get_coin_balance(&hot_wallet); + assert_eq!(coin_balance, block1_amount); + + let pool_amount = block1_amount; + + let res = hot_wallet.create_next_account(Some("name".into())).await.unwrap(); + assert_eq!(res, (U31::from_u32(1).unwrap(), Some("name".into()))); + + let pool_creation_tx = hot_wallet + .create_stake_pool( + DEFAULT_ACCOUNT_INDEX, + FeeRate::from_amount_per_kb(Amount::ZERO), + FeeRate::from_amount_per_kb(Amount::ZERO), + StakePoolCreationArguments { + amount: pool_amount, + margin_ratio_per_thousand: PerThousand::new_from_rng(&mut rng), + cost_per_block: Amount::ZERO, + decommission_key: decommission_key.clone(), + staker_key: None, + vrf_public_key: None, + }, + ) + .await + .unwrap() + .tx; + let pool_creation_tx_id = pool_creation_tx.transaction().get_id(); + let (_, _block2) = create_block( + &chain_config, + &mut hot_wallet, + vec![pool_creation_tx], + Amount::ZERO, + 1, + ) + .await; + + let pool_ids = hot_wallet.get_pools(DEFAULT_ACCOUNT_INDEX, WalletPoolsFilter::All).unwrap(); + assert_eq!(pool_ids.len(), 1); + let pool_id = pool_ids.first().unwrap().0; + + let pos_data = hot_wallet.get_pos_gen_block_data(DEFAULT_ACCOUNT_INDEX, pool_id).unwrap(); + let staker_key = Destination::PublicKey(pos_data.stake_public_key()); + + // Create a block using the pool that will be decommissioned, so that the utxo consumed + // during decommissioning is ProduceBlockFromStake (because it's one of the utxos for which + // input commitments v0 and v1 are different). + let block3 = Block::new( + vec![], + chain_config.genesis_block_id(), + chain_config.genesis_block().timestamp(), + ConsensusData::PoS(Box::new(PoSData::new( + vec![TxInput::Utxo(UtxoOutPoint::new( + OutPointSourceId::Transaction(pool_creation_tx_id), + 0, + ))], + vec![], + pool_id, + pos_data.vrf_private_key().produce_vrf_data(VRFTranscript::new(&[])), + common::primitives::Compact(0), + ))), + BlockReward::new(vec![TxOutput::ProduceBlockFromStake(staker_key, pool_id)]), + ) + .unwrap(); + + scan_wallet(&mut hot_wallet, BlockHeight::new(2), vec![block3]).await; + + let pool_decommission_ptx = hot_wallet + .decommission_stake_pool_request( + DEFAULT_ACCOUNT_INDEX, + pool_id, + pool_amount, + None, + FeeRate::from_amount_per_kb(Amount::from_atoms(0)), + ) + .await + .unwrap(); + + // Sanity check: the consumed utxo is ProduceBlockFromStake. + assert_eq!(pool_decommission_ptx.input_utxos().len(), 1); + let pool_decommission_utxo = pool_decommission_ptx.input_utxos()[0].clone().unwrap(); + assert_matches!( + &pool_decommission_utxo, + TxOutput::ProduceBlockFromStake(_, _) + ); + + let expected_input_commitments = pool_decommission_ptx + .make_sighash_input_commitments_at_height(&chain_config, input_commitments_v1_fork_height) + .unwrap(); + let v0_input_commitments = pool_decommission_ptx + .make_sighash_input_commitments_at_height(&chain_config, BlockHeight::zero()) + .unwrap(); + // Sanity check + assert_ne!(expected_input_commitments, v0_input_commitments); + + // Sign the tx with cold wallet + let pool_decommission_ptx_after_signing = cold_wallet + .sign_raw_transaction( + DEFAULT_ACCOUNT_INDEX, + pool_decommission_ptx.clone(), + &TokensAdditionalInfo::new(), + ) + .await + .unwrap() + .0; + assert!(pool_decommission_ptx_after_signing.all_signatures_available()); + + let pool_decommission_signed_tx = pool_decommission_ptx_after_signing.into_signed_tx().unwrap(); + + // Verify the signature using v1 input commitments. + tx_verifier::input_check::signature_only_check::verify_tx_signature( + &chain_config, + &decommission_key, + &pool_decommission_signed_tx, + &expected_input_commitments, + 0, + Some(pool_decommission_utxo), + ) + .unwrap(); + + let (_, _block4) = create_block( + &chain_config, + &mut hot_wallet, + vec![pool_decommission_signed_tx], + Amount::ZERO, + 2, + ) + .await; + + let coin_balance = get_coin_balance(&hot_wallet); + assert_eq!(coin_balance, pool_amount); +} + #[rstest] #[trace] #[case(Seed::from_entropy())] @@ -5702,7 +5925,12 @@ async fn sign_send_request_cold_wallet(#[case] seed: Seed) { // create cold wallet that is not synced let another_mnemonic = "legal winner thank year wave sausage worth useful legal winner thank yellow"; - let mut cold_wallet = create_wallet_with_mnemonic(chain_config.clone(), another_mnemonic).await; + let mut cold_wallet = create_wallet_with_type_and_mnemonic( + chain_config.clone(), + *[WalletType::Hot, WalletType::Cold].choose(&mut rng).unwrap(), + another_mnemonic, + ) + .await; let cold_wallet_address = cold_wallet.get_new_address(DEFAULT_ACCOUNT_INDEX).unwrap().1; let coin_balance = get_coin_balance(&hot_wallet); diff --git a/wallet/types/src/wallet_type.rs b/wallet/types/src/wallet_type.rs index d46fda23b..5203b2b1b 100644 --- a/wallet/types/src/wallet_type.rs +++ b/wallet/types/src/wallet_type.rs @@ -30,6 +30,27 @@ pub enum WalletType { Ledger, } +impl WalletType { + pub fn to_software_wallet_type(self) -> Option { + match self { + WalletType::Cold => Some(SoftwareWalletType::Cold), + WalletType::Hot => Some(SoftwareWalletType::Hot), + #[cfg(feature = "trezor")] + WalletType::Trezor => None, + #[cfg(feature = "ledger")] + WalletType::Ledger => None, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum SoftwareWalletType { + Cold, + Hot, +} + +// Note: this is conceptually different from SoftwareWalletType, because here "Hot" includes +// hardware wallets as well. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum WalletControllerMode { Cold, From e61a4e5463cc2b6f5969d61937cc330187b9e5e5 Mon Sep 17 00:00:00 2001 From: Mykhailo Kremniov Date: Mon, 1 Jun 2026 18:42:40 +0300 Subject: [PATCH 08/15] Fix wallet in cold mode spamming error log messages about mempool being not available. --- wallet/wallet-controller/src/lib.rs | 98 +++++++++++-------- .../src/tests/compose_transaction_tests.rs | 6 +- wallet/wallet-node-client/src/node_traits.rs | 1 + .../src/rpc_client/cold_wallet_client.rs | 2 +- 4 files changed, 65 insertions(+), 42 deletions(-) diff --git a/wallet/wallet-controller/src/lib.rs b/wallet/wallet-controller/src/lib.rs index 1fb599d7d..96d6dd50b 100644 --- a/wallet/wallet-controller/src/lib.rs +++ b/wallet/wallet-controller/src/lib.rs @@ -227,6 +227,7 @@ pub struct Controller { rpc_client: T, wallet: RuntimeWallet, + wallet_mode: WalletControllerMode, staking_started: BTreeSet, @@ -256,23 +257,20 @@ where wallet: RuntimeWallet, wallet_events: W, ) -> Result> { - let mempool_events = rpc_client - .mempool_subscribe_to_events() - .await - .map_err(ControllerError::NodeCallError)?; - let mut controller = Self { - chain_config, - rpc_client, - wallet, - staking_started: BTreeSet::new(), - wallet_events, - mempool_events, - finished_initial_sync: SetFlag::new(), + let mut controller = + Self::new_unsynced(chain_config, rpc_client, wallet, wallet_events).await?; + + // In the cold mode, try_sync_once is a no-op, so it doesn't matter whether we call it. + // We omit the call to avoid printing the "Syncing the wallet" log line, which looks + // confusing in the cold mode. + match controller.wallet_mode { + WalletControllerMode::Cold => {} + WalletControllerMode::Hot => { + log::info!("Syncing the wallet..."); + controller.try_sync_once().await?; + } }; - log::info!("Syncing the wallet..."); - controller.try_sync_once().await?; - Ok(controller) } @@ -282,14 +280,18 @@ where wallet: RuntimeWallet, wallet_events: W, ) -> Result> { + let wallet_mode = rpc_client.is_cold_wallet_node().await; + let mempool_events = rpc_client .mempool_subscribe_to_events() .await .map_err(ControllerError::NodeCallError)?; + Ok(Self { chain_config, rpc_client, wallet, + wallet_mode, staking_started: BTreeSet::new(), wallet_events, mempool_events, @@ -1457,26 +1459,33 @@ where } } - // after the first successful sync to the tip fetch all mempool transactions - if !self.finished_initial_sync.test() { - let txs = self.rpc_client.mempool_get_transactions().await; - - match txs { - Ok(txs) => { - if let Err(err) = - self.wallet.add_mempool_transactions(&txs, &self.wallet_events) - { - log::error!("Error adding mempool transactions: {err}"); - } else { - self.finished_initial_sync.set(); + match self.wallet_mode { + WalletControllerMode::Hot => { + // after the first successful sync to the tip fetch all mempool transactions + if !self.finished_initial_sync.test() { + let txs = self.rpc_client.mempool_get_transactions().await; + + match txs { + Ok(txs) => { + if let Err(err) = + self.wallet.add_mempool_transactions(&txs, &self.wallet_events) + { + log::error!("Error adding mempool transactions: {err}"); + } else { + self.finished_initial_sync.set(); + } + } + Err(err) => { + log::error!( + "Failed to fetch all transactions from the mempool: {err}" + ); + tokio::time::sleep(ERROR_DELAY).await; + continue; + } } } - Err(err) => { - log::error!("Failed to fetch all transactions from the mempool: {err}"); - tokio::time::sleep(ERROR_DELAY).await; - continue; - } } + WalletControllerMode::Cold => {} } let mut delay = Box::pin(tokio::time::sleep(NORMAL_DELAY)); @@ -1491,10 +1500,9 @@ where let event = match maybe_event { Some(e) => e, None => { - log::error!("Mempool notifications channel is closed."); + log::error!("Mempool notifications channel is closed"); tokio::time::sleep(ERROR_DELAY).await; - match self.rpc_client .mempool_subscribe_to_events() .await { @@ -1502,7 +1510,7 @@ where self.mempool_events = events; } Err(err) => { - log::error!("Subscribing to mempool notifications failed with: {err}"); + log::error!("Re-subscribing to mempool notifications failed: {err}"); } } break @@ -1519,14 +1527,14 @@ where Ok(Some(transaction)) => { let txs = [transaction]; if let Err(err) = self.wallet.add_mempool_transactions(&txs, &self.wallet_events) { - log::error!("Tx {} failed to be added in the wallet because of an error: {err}", tx_id); + log::error!("Error adding mempool transaction {tx_id:x} to the wallet: {err}"); } } Ok(None) => { - log::warn!("Tx {} announced by mempool, but not found when fetched", tx_id); + log::warn!("Transaction {tx_id:x} announced by mempool, but not found when fetched"); } Err(err) => { - log::error!("Error while fetching a transaction from mempool {err}"); + log::error!("Error fetching transaction {tx_id:x} from mempool: {err}"); } } } @@ -1534,7 +1542,17 @@ where } } } - self.rebroadcast_txs(&mut rebroadcast_txs_timer).await; + + // Note: normally a wallet in the cold mode will not have any transactions to broadcast. However, if it was + // force-converted from a hot wallet, it may have such transactions, in which case `rebroadcast_txs` will + // repeatedly print the warning "Rebroadcasting ... failed: Method is not available in cold wallet mode". + // So we avoid calling `rebroadcast_txs` in the cold mode. + match self.wallet_mode { + WalletControllerMode::Cold => {} + WalletControllerMode::Hot => { + self.rebroadcast_txs(&mut rebroadcast_txs_timer).await; + } + } } } @@ -1551,7 +1569,7 @@ where let tx_id = tx.transaction().get_id(); let res = self.rpc_client.submit_transaction(tx, Default::default()).await; if let Err(e) = res { - log::warn!("Rebroadcasting for tx {tx_id} failed: {e}"); + log::warn!("Rebroadcasting tx {tx_id:x} failed: {e}"); } } } diff --git a/wallet/wallet-controller/src/tests/compose_transaction_tests.rs b/wallet/wallet-controller/src/tests/compose_transaction_tests.rs index 4a5626b6d..e8b18d152 100644 --- a/wallet/wallet-controller/src/tests/compose_transaction_tests.rs +++ b/wallet/wallet-controller/src/tests/compose_transaction_tests.rs @@ -45,7 +45,9 @@ use wallet::{ account::TransactionToSign, wallet::test_helpers::create_wallet_with_mnemonic, wallet_events::WalletEventsNoOp, }; -use wallet_types::partially_signed_transaction::PtxAdditionalInfo; +use wallet_types::{ + partially_signed_transaction::PtxAdditionalInfo, wallet_type::WalletControllerMode, +}; use crate::{ helpers::get_referenced_token_ids_from_partially_signed_transaction, @@ -171,6 +173,8 @@ async fn general_test(#[case] seed: Seed, #[case] use_htlc_secret: bool) { is_initial_block_download: false, }; + node_mock.expect_is_cold_wallet_node().returning(|| WalletControllerMode::Hot); + node_mock .expect_get_utxo() .returning(move |outpoint| Ok(Some(utxos_to_return.get(&outpoint).unwrap().clone()))); diff --git a/wallet/wallet-node-client/src/node_traits.rs b/wallet/wallet-node-client/src/node_traits.rs index 87c1093e1..9c1566806 100644 --- a/wallet/wallet-node-client/src/node_traits.rs +++ b/wallet/wallet-node-client/src/node_traits.rs @@ -45,6 +45,7 @@ pub trait NodeInterface { // Note: not requiring the `Error` trait here so that `anyhow::Error` can be used. type Error: std::fmt::Debug + std::fmt::Display + Send + Sync + 'static; + // TODO: rename this, e.g. to wallet_mode. async fn is_cold_wallet_node(&self) -> WalletControllerMode; async fn chainstate_info(&self) -> Result; diff --git a/wallet/wallet-node-client/src/rpc_client/cold_wallet_client.rs b/wallet/wallet-node-client/src/rpc_client/cold_wallet_client.rs index 743bef161..ac2617e9a 100644 --- a/wallet/wallet-node-client/src/rpc_client/cold_wallet_client.rs +++ b/wallet/wallet-node-client/src/rpc_client/cold_wallet_client.rs @@ -291,7 +291,7 @@ impl NodeInterface for ColdWalletClient { } async fn mempool_subscribe_to_events(&self) -> Result { - Ok(Box::new(futures::stream::empty())) + Ok(Box::new(futures::stream::pending())) } async fn mempool_get_transaction( From 5b012dfe1f2c5d7d385152860f11a8611b68a0de Mon Sep 17 00:00:00 2001 From: Mykhailo Kremniov Date: Mon, 1 Jun 2026 19:11:22 +0300 Subject: [PATCH 09/15] Update changelog for 1.3.1 --- CHANGELOG.md | 9 +++++++++ api-server/CHANGELOG.md | 4 ++++ wasm-wrappers/CHANGELOG.md | 4 ++++ 3 files changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a4b6f0b4..9daa017ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,15 @@ The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/ - `wallet-create`/`wallet-recover`/`wallet-open` support the `ledger` subcommand, in addition to the existing `software` and `trezor`, which specifies the type of the wallet to operate on. +## [1.3.1] + +### Fixed + - Wallet: + - Fixed an issue where the wallet in cold mode would always use input commitments v0, thus producing signatures + that may no longer be valid at the current height. + - Fixed an issue where the wallet in cold mode would still try to access the mempool, which would cause `wallet-cli` + to print lots of error messages like "Method is not available in cold wallet mode". + ## [1.3.0] - 2026-04-09 ### Added diff --git a/api-server/CHANGELOG.md b/api-server/CHANGELOG.md index d2ee456c4..1fdf123d6 100644 --- a/api-server/CHANGELOG.md +++ b/api-server/CHANGELOG.md @@ -6,6 +6,10 @@ The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/ ## [Unreleased] +## [1.3.1] + +No changes + ## [1.3.0] - 2026-04-09 ### Added diff --git a/wasm-wrappers/CHANGELOG.md b/wasm-wrappers/CHANGELOG.md index a6df92611..f23b00bf7 100644 --- a/wasm-wrappers/CHANGELOG.md +++ b/wasm-wrappers/CHANGELOG.md @@ -6,6 +6,10 @@ The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/ ## [Unreleased] +## [1.3.1] + +No changes + ## [1.3.0] - 2026-04-09 ### Added From 5751a8dfad3658c26ec157f3cfeb323282d1e488 Mon Sep 17 00:00:00 2001 From: Mykhailo Kremniov Date: Thu, 23 Apr 2026 19:53:42 +0300 Subject: [PATCH 10/15] Run CI jobs on push to release branches too. Fix a typo --- .github/workflows/build.yml | 1 + .github/workflows/code_checks.yml | 1 + .github/workflows/loom.yml | 1 + .github/workflows/wasm.yml | 1 + common/src/chain/partially_signed_transaction/mod.rs | 2 +- 5 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8dc409300..38018a8fe 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - release-* pull_request: branches: - "**" # target all branches diff --git a/.github/workflows/code_checks.yml b/.github/workflows/code_checks.yml index 767b51b13..7537f79d7 100644 --- a/.github/workflows/code_checks.yml +++ b/.github/workflows/code_checks.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - release-* pull_request: branches: - "**" # target all branches diff --git a/.github/workflows/loom.yml b/.github/workflows/loom.yml index 3c816e957..98d506315 100644 --- a/.github/workflows/loom.yml +++ b/.github/workflows/loom.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - release-* pull_request: branches: - "**" # target all branches diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index f695983dd..3c7a73cec 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - release-* pull_request: branches: - "**" # target all branches diff --git a/common/src/chain/partially_signed_transaction/mod.rs b/common/src/chain/partially_signed_transaction/mod.rs index adc1846e3..f0ab18759 100644 --- a/common/src/chain/partially_signed_transaction/mod.rs +++ b/common/src/chain/partially_signed_transaction/mod.rs @@ -64,7 +64,7 @@ pub enum PartiallySignedTransactionConsistencyCheck { /// Note: currently PartiallySignedTransaction's consistency checks require that the additional info /// is present even if the inputs that need it are already signed. /// -/// Thought PartiallySignedTransaction is not part of the blockchain, it is still part of +/// Though PartiallySignedTransaction is not part of the blockchain, it is still part of /// the core's public interface: /// 1) It is returned and consumed by the wallet CLI and RPC (in its encoded form). /// 2) Through the wallet RPC, it is used by the bridge, whose e2m master agent puts From 146775222d39cfcc5c1c1ff55aa733a41262a5b8 Mon Sep 17 00:00:00 2001 From: Boris Oncev Date: Thu, 23 Apr 2026 14:49:29 +0700 Subject: [PATCH 11/15] fix handling of conflicting order transactions in the wallet --- wallet/src/account/output_cache/mod.rs | 76 +++++- wallet/src/account/output_cache/tests.rs | 327 +++++++++++++++++++++++ 2 files changed, 402 insertions(+), 1 deletion(-) diff --git a/wallet/src/account/output_cache/mod.rs b/wallet/src/account/output_cache/mod.rs index e673d55e1..354942703 100644 --- a/wallet/src/account/output_cache/mod.rs +++ b/wallet/src/account/output_cache/mod.rs @@ -783,9 +783,11 @@ impl OutputCache { confirmed_tx: &Transaction, block_id: Id, ) -> WalletResult, WalletTx)>> { + // TODO: maybe make it an enum struct ConflictCheck { frozen_token_id: Option, confirmed_account_nonce: Option<(AccountType, AccountNonce)>, + conflicting_order_command: Option<(OrderAccountCommandTag, OrderId)>, } let conflict_checks = confirmed_tx @@ -804,6 +806,7 @@ impl OutputCache { outpoint.account().into(), outpoint.nonce(), )), + conflicting_order_command: None, }), TxInput::AccountCommand(nonce, cmd) => match cmd { AccountCommand::MintTokens(_, _) @@ -816,13 +819,34 @@ impl OutputCache { | AccountCommand::FillOrder(_, _, _) => Some(ConflictCheck { frozen_token_id: None, confirmed_account_nonce: Some((cmd.into(), *nonce)), + conflicting_order_command: None, }), | AccountCommand::FreezeToken(token_id, _) => Some(ConflictCheck { frozen_token_id: Some(*token_id), confirmed_account_nonce: Some((cmd.into(), *nonce)), + conflicting_order_command: None, }), }, - TxInput::OrderAccountCommand(_) => None, + TxInput::OrderAccountCommand(cmd) => { + let order_id = match cmd { + OrderAccountCommand::FillOrder(order_id, _) + | OrderAccountCommand::FreezeOrder(order_id) + | OrderAccountCommand::ConcludeOrder(order_id) => *order_id, + }; + let cmd_tag: OrderAccountCommandTag = cmd.into(); + match cmd_tag { + // ConcludeOrder and FreezeOrder are exclusive: only one tx can win. + // Any unconfirmed tx with the same command for the same order conflicts. + OrderAccountCommandTag::ConcludeOrder + | OrderAccountCommandTag::FreezeOrder => Some(ConflictCheck { + frozen_token_id: None, + confirmed_account_nonce: None, + conflicting_order_command: Some((cmd_tag, order_id)), + }), + // Multiple fills for the same order can coexist. + OrderAccountCommandTag::FillOrder => None, + } + } } }) .collect::>(); @@ -857,6 +881,21 @@ impl OutputCache { continue; } } + + if let Some((confirmed_cmd_tag, confirmed_order_id)) = + conflict_check.conflicting_order_command + { + if confirmed_tx.get_id() != tx.get_transaction().get_id() + && uses_conflicting_order_command( + unconfirmed_tx, + confirmed_cmd_tag, + confirmed_order_id, + ) + { + conflicting_txs.insert(tx.get_transaction().get_id()); + continue; + } + } } WalletTx::Block(_) => { utils::debug_panic_or_log!("Cannot be block reward"); @@ -2062,6 +2101,41 @@ fn uses_conflicting_nonce( }) } +fn uses_conflicting_order_command( + unconfirmed_tx: &WalletTx, + cmd_tag: OrderAccountCommandTag, + order_id: OrderId, +) -> bool { + unconfirmed_tx.inputs().iter().any(|input| match input { + TxInput::OrderAccountCommand(cmd) => { + let unconfirmed_order_id = match cmd { + OrderAccountCommand::FillOrder(id, _) + | OrderAccountCommand::FreezeOrder(id) + | OrderAccountCommand::ConcludeOrder(id) => *id, + }; + // It is only a conflict if it is the same order id + if unconfirmed_order_id != order_id { + return false; + } + + let unconfirmed_cmd_tag: OrderAccountCommandTag = cmd.into(); + match cmd_tag { + // Confirmed fill orders do not conflict with anything + OrderAccountCommandTag::FillOrder => false, + // Confirmed conclude order conflict with any other unconfirmed operation on the + // order + OrderAccountCommandTag::ConcludeOrder => true, + // Confirmed Freeze order conflicts with any unconfirmed Fill or Freeze order + OrderAccountCommandTag::FreezeOrder => match unconfirmed_cmd_tag { + OrderAccountCommandTag::FillOrder | OrderAccountCommandTag::FreezeOrder => true, + OrderAccountCommandTag::ConcludeOrder => false, + }, + } + } + TxInput::Utxo(_) | TxInput::Account(_) | TxInput::AccountCommand(_, _) => false, + }) +} + #[derive(thiserror::Error, Debug, Eq, PartialEq)] pub enum OutputCacheInconsistencyError { #[error("Transaction from {0:?} is confirmed and among unconfirmed descendants")] diff --git a/wallet/src/account/output_cache/tests.rs b/wallet/src/account/output_cache/tests.rs index 5cfcbfef6..1aae154c6 100644 --- a/wallet/src/account/output_cache/tests.rs +++ b/wallet/src/account/output_cache/tests.rs @@ -1267,6 +1267,333 @@ fn orders_state_update(#[case] seed: Seed) { ); } +// Regression test: when a confirmed V1 ConcludeOrder arrives and an unconfirmed ConcludeOrder, +// FreezeOrder or FillOrder for the same order is already in the cache, +// the unconfirmed one must be marked Conflicted and the confirmed one must be accepted without error. +// Previously, update_conflicting_txs returned None for OrderAccountCommand, so the unconfirmed +// conclude was never rolled back, causing add_tx to fail with OrderAlreadyConcluded. +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn update_conflicting_txs_order_v1_conclude(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); + let chain_config = create_unit_test_config(); + let mut output_cache = OutputCache::empty(); + + // Create the order (confirmed). + let parent_tx_id = Id::::random_using(&mut rng); + let creation_tx = TransactionBuilder::new() + .add_input( + TxInput::from_utxo(parent_tx_id.into(), 0), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::CreateOrder(Box::new(OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::Coin(Amount::from_atoms(1000)), + OutputValue::Coin(Amount::from_atoms(2000)), + )))) + .build(); + let order_id = make_order_id(creation_tx.inputs()).unwrap(); + let creation_tx_id = creation_tx.transaction().get_id(); + + output_cache + .add_tx( + &chain_config, + BlockHeight::new(1), + creation_tx_id.into(), + WalletTx::Tx(TxData::new( + creation_tx, + TxState::Confirmed(BlockHeight::new(1), BlockTimestamp::from_int_seconds(1), 0), + )), + ) + .unwrap(); + + // Add an unconfirmed fill order tx + let unconfirmed_fill_tx = TransactionBuilder::new() + .add_input( + TxInput::OrderAccountCommand(OrderAccountCommand::FillOrder( + order_id, + Amount::from_atoms(rng.gen_range(100..200)), + )), + InputWitness::NoSignature(None), + ) + .build(); + let unconfirmed_fill_tx_id = unconfirmed_fill_tx.transaction().get_id(); + + output_cache + .add_tx( + &chain_config, + BlockHeight::new(2), + unconfirmed_fill_tx_id.into(), + WalletTx::Tx(TxData::new( + unconfirmed_fill_tx.clone(), + TxState::InMempool(1), + )), + ) + .unwrap(); + + // Add an unconfirmed freeze tx — sets is_frozen = true. + let unconfirmed_freeze_tx = TransactionBuilder::new() + .add_input( + TxInput::OrderAccountCommand(OrderAccountCommand::FreezeOrder(order_id)), + InputWitness::NoSignature(None), + ) + .build(); + let unconfirmed_freeze_tx_id = unconfirmed_freeze_tx.transaction().get_id(); + + output_cache + .add_tx( + &chain_config, + BlockHeight::new(2), + unconfirmed_freeze_tx_id.into(), + WalletTx::Tx(TxData::new( + unconfirmed_freeze_tx.clone(), + TxState::InMempool(1), + )), + ) + .unwrap(); + + assert!(output_cache.orders[&order_id].is_frozen); + + // Add an unconfirmed conclude tx — sets is_concluded = true. + let unconfirmed_conclude_tx = TransactionBuilder::new() + .add_input( + TxInput::OrderAccountCommand(OrderAccountCommand::ConcludeOrder(order_id)), + InputWitness::NoSignature(None), + ) + .build(); + let unconfirmed_conclude_tx_id = unconfirmed_conclude_tx.transaction().get_id(); + + output_cache + .add_tx( + &chain_config, + BlockHeight::new(2), + unconfirmed_conclude_tx_id.into(), + WalletTx::Tx(TxData::new( + unconfirmed_conclude_tx.clone(), + TxState::InMempool(2), + )), + ) + .unwrap(); + + assert!(output_cache.orders[&order_id].is_concluded); + + // A *different* confirmed conclude tx for the same order arrives. + // update_conflicting_txs must mark the unconfirmed one as Conflicted. + let confirmed_conclude_tx = TransactionBuilder::new() + .add_input( + TxInput::OrderAccountCommand(OrderAccountCommand::ConcludeOrder(order_id)), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::Transfer( + OutputValue::Coin(Amount::from_atoms(1)), + Destination::AnyoneCanSpend, + )) + .build(); + let confirmed_conclude_tx_id = confirmed_conclude_tx.transaction().get_id(); + assert_ne!(confirmed_conclude_tx_id, unconfirmed_conclude_tx_id); + + let block_id = Id::::random_using(&mut rng); + let mut conflicted = output_cache + .update_conflicting_txs(&chain_config, confirmed_conclude_tx.transaction(), block_id) + .unwrap(); + + // sort conflicted by tx id + conflicted.sort_by_key(|(tx_id, _)| *tx_id); + + let mut expected_conflicted = vec![ + ( + unconfirmed_fill_tx_id, + WalletTx::Tx(TxData::new( + unconfirmed_fill_tx, + TxState::Conflicted(block_id), + )), + ), + ( + unconfirmed_freeze_tx_id, + WalletTx::Tx(TxData::new( + unconfirmed_freeze_tx, + TxState::Conflicted(block_id), + )), + ), + ( + unconfirmed_conclude_tx_id, + WalletTx::Tx(TxData::new( + unconfirmed_conclude_tx, + TxState::Conflicted(block_id), + )), + ), + ]; + expected_conflicted.sort_by_key(|(tx_id, _)| *tx_id); + + assert_eq!(conflicted, expected_conflicted); + + // is_concluded and is_frozen must be rolled back after the conflict. + assert!(!output_cache.orders[&order_id].is_concluded); + assert!(!output_cache.orders[&order_id].is_frozen); + + // The confirmed conclude tx must now be addable without error. + output_cache + .add_tx( + &chain_config, + BlockHeight::new(2), + confirmed_conclude_tx_id.into(), + WalletTx::Tx(TxData::new( + confirmed_conclude_tx, + TxState::Confirmed(BlockHeight::new(2), BlockTimestamp::from_int_seconds(2), 0), + )), + ) + .unwrap(); + + assert!(output_cache.orders[&order_id].is_concluded); +} + +// Regression test: same as above but for FreezeOrder. +#[rstest] +#[trace] +#[case(Seed::from_entropy())] +fn update_conflicting_txs_order_v1_freeze(#[case] seed: Seed) { + let mut rng = make_seedable_rng(seed); + let chain_config = create_unit_test_config(); + let mut output_cache = OutputCache::empty(); + + // Create the order (confirmed). + let parent_tx_id = Id::::random_using(&mut rng); + let creation_tx = TransactionBuilder::new() + .add_input( + TxInput::from_utxo(parent_tx_id.into(), 0), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::CreateOrder(Box::new(OrderData::new( + Destination::AnyoneCanSpend, + OutputValue::Coin(Amount::from_atoms(1000)), + OutputValue::Coin(Amount::from_atoms(2000)), + )))) + .build(); + let order_id = make_order_id(creation_tx.inputs()).unwrap(); + let creation_tx_id = creation_tx.transaction().get_id(); + + output_cache + .add_tx( + &chain_config, + BlockHeight::new(1), + creation_tx_id.into(), + WalletTx::Tx(TxData::new( + creation_tx, + TxState::Confirmed(BlockHeight::new(1), BlockTimestamp::from_int_seconds(1), 0), + )), + ) + .unwrap(); + + // Add an unconfirmed fill order tx + let unconfirmed_fill_tx = TransactionBuilder::new() + .add_input( + TxInput::OrderAccountCommand(OrderAccountCommand::FillOrder( + order_id, + Amount::from_atoms(rng.gen_range(100..200)), + )), + InputWitness::NoSignature(None), + ) + .build(); + let unconfirmed_fill_tx_id = unconfirmed_fill_tx.transaction().get_id(); + + output_cache + .add_tx( + &chain_config, + BlockHeight::new(2), + unconfirmed_fill_tx_id.into(), + WalletTx::Tx(TxData::new( + unconfirmed_fill_tx.clone(), + TxState::InMempool(0), + )), + ) + .unwrap(); + + // Add an unconfirmed freeze tx — sets is_frozen = true. + let unconfirmed_freeze_tx = TransactionBuilder::new() + .add_input( + TxInput::OrderAccountCommand(OrderAccountCommand::FreezeOrder(order_id)), + InputWitness::NoSignature(None), + ) + .build(); + let unconfirmed_freeze_tx_id = unconfirmed_freeze_tx.transaction().get_id(); + + output_cache + .add_tx( + &chain_config, + BlockHeight::new(2), + unconfirmed_freeze_tx_id.into(), + WalletTx::Tx(TxData::new( + unconfirmed_freeze_tx.clone(), + TxState::InMempool(1), + )), + ) + .unwrap(); + + assert!(output_cache.orders[&order_id].is_frozen); + + // A *different* confirmed freeze tx for the same order arrives. + // update_conflicting_txs must mark the unconfirmed one as Conflicted. + let confirmed_freeze_tx = TransactionBuilder::new() + .add_input( + TxInput::OrderAccountCommand(OrderAccountCommand::FreezeOrder(order_id)), + InputWitness::NoSignature(None), + ) + .add_output(TxOutput::Transfer( + OutputValue::Coin(Amount::from_atoms(1)), + Destination::AnyoneCanSpend, + )) + .build(); + let confirmed_freeze_tx_id = confirmed_freeze_tx.transaction().get_id(); + assert_ne!(confirmed_freeze_tx_id, unconfirmed_freeze_tx_id); + + let block_id = Id::::random_using(&mut rng); + let mut conflicted = output_cache + .update_conflicting_txs(&chain_config, confirmed_freeze_tx.transaction(), block_id) + .unwrap(); + + let mut expected_conflicted = vec![ + ( + unconfirmed_fill_tx_id, + WalletTx::Tx(TxData::new( + unconfirmed_fill_tx, + TxState::Conflicted(block_id), + )), + ), + ( + unconfirmed_freeze_tx_id, + WalletTx::Tx(TxData::new( + unconfirmed_freeze_tx, + TxState::Conflicted(block_id), + )), + ), + ]; + + // Sort by tx id + conflicted.sort_by_key(|(tx_id, _)| *tx_id); + expected_conflicted.sort_by_key(|(tx_id, _)| *tx_id); + + assert_eq!(conflicted, expected_conflicted); + + // is_frozen must be rolled back after the conflict. + assert!(!output_cache.orders[&order_id].is_frozen); + + // The confirmed freeze tx must now be addable without error. + output_cache + .add_tx( + &chain_config, + BlockHeight::new(2), + confirmed_freeze_tx_id.into(), + WalletTx::Tx(TxData::new( + confirmed_freeze_tx, + TxState::Confirmed(BlockHeight::new(2), BlockTimestamp::from_int_seconds(2), 0), + )), + ) + .unwrap(); + + assert!(output_cache.orders[&order_id].is_frozen); +} + fn add_random_transfer_tx( output_cache: &mut OutputCache, chain_config: &ChainConfig, From 984659b34c8895373d91841e0025d0efb13549c2 Mon Sep 17 00:00:00 2001 From: Boris Oncev Date: Thu, 23 Apr 2026 19:19:30 +0700 Subject: [PATCH 12/15] fix comments --- CHANGELOG.md | 4 ++++ wallet/src/account/output_cache/mod.rs | 8 ++++---- wallet/src/account/output_cache/tests.rs | 7 ++++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9daa017ae..37639f657 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,10 @@ The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/ - `wallet-create`/`wallet-recover`/`wallet-open` support the `ledger` subcommand, in addition to the existing `software` and `trezor`, which specifies the type of the wallet to operate on. +### Fixed + - Wallet: + - Fixed handling of confirmed and unconfirmed conflicting order transactions in the wallet. + ## [1.3.1] ### Fixed diff --git a/wallet/src/account/output_cache/mod.rs b/wallet/src/account/output_cache/mod.rs index 354942703..bff921209 100644 --- a/wallet/src/account/output_cache/mod.rs +++ b/wallet/src/account/output_cache/mod.rs @@ -2103,8 +2103,8 @@ fn uses_conflicting_nonce( fn uses_conflicting_order_command( unconfirmed_tx: &WalletTx, - cmd_tag: OrderAccountCommandTag, - order_id: OrderId, + confirmed_cmd_tag: OrderAccountCommandTag, + confirmed_order_id: OrderId, ) -> bool { unconfirmed_tx.inputs().iter().any(|input| match input { TxInput::OrderAccountCommand(cmd) => { @@ -2114,12 +2114,12 @@ fn uses_conflicting_order_command( | OrderAccountCommand::ConcludeOrder(id) => *id, }; // It is only a conflict if it is the same order id - if unconfirmed_order_id != order_id { + if unconfirmed_order_id != confirmed_order_id { return false; } let unconfirmed_cmd_tag: OrderAccountCommandTag = cmd.into(); - match cmd_tag { + match confirmed_cmd_tag { // Confirmed fill orders do not conflict with anything OrderAccountCommandTag::FillOrder => false, // Confirmed conclude order conflict with any other unconfirmed operation on the diff --git a/wallet/src/account/output_cache/tests.rs b/wallet/src/account/output_cache/tests.rs index 1aae154c6..b174509e1 100644 --- a/wallet/src/account/output_cache/tests.rs +++ b/wallet/src/account/output_cache/tests.rs @@ -1379,7 +1379,7 @@ fn update_conflicting_txs_order_v1_conclude(#[case] seed: Seed) { assert!(output_cache.orders[&order_id].is_concluded); // A *different* confirmed conclude tx for the same order arrives. - // update_conflicting_txs must mark the unconfirmed one as Conflicted. + // update_conflicting_txs must mark the unconfirmed ones as Conflicted. let confirmed_conclude_tx = TransactionBuilder::new() .add_input( TxInput::OrderAccountCommand(OrderAccountCommand::ConcludeOrder(order_id)), @@ -1446,6 +1446,7 @@ fn update_conflicting_txs_order_v1_conclude(#[case] seed: Seed) { .unwrap(); assert!(output_cache.orders[&order_id].is_concluded); + assert!(!output_cache.orders[&order_id].is_frozen); } // Regression test: same as above but for FreezeOrder. @@ -1531,6 +1532,8 @@ fn update_conflicting_txs_order_v1_freeze(#[case] seed: Seed) { .unwrap(); assert!(output_cache.orders[&order_id].is_frozen); + // concluded is still false + assert!(!output_cache.orders[&order_id].is_concluded); // A *different* confirmed freeze tx for the same order arrives. // update_conflicting_txs must mark the unconfirmed one as Conflicted. @@ -1577,6 +1580,7 @@ fn update_conflicting_txs_order_v1_freeze(#[case] seed: Seed) { // is_frozen must be rolled back after the conflict. assert!(!output_cache.orders[&order_id].is_frozen); + assert!(!output_cache.orders[&order_id].is_concluded); // The confirmed freeze tx must now be addable without error. output_cache @@ -1592,6 +1596,7 @@ fn update_conflicting_txs_order_v1_freeze(#[case] seed: Seed) { .unwrap(); assert!(output_cache.orders[&order_id].is_frozen); + assert!(!output_cache.orders[&order_id].is_concluded); } fn add_random_transfer_tx( From 8f15314449fdae9a5c015aa985b680062f9db921 Mon Sep 17 00:00:00 2001 From: Mykhailo Kremniov Date: Tue, 2 Jun 2026 13:20:07 +0300 Subject: [PATCH 13/15] Update the changelog, moving the fix for #2043 to [1.3.1] and re-wording it --- CHANGELOG.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37639f657..b39c4b64e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,14 +22,12 @@ The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/ - `wallet-create`/`wallet-recover`/`wallet-open` support the `ledger` subcommand, in addition to the existing `software` and `trezor`, which specifies the type of the wallet to operate on. -### Fixed - - Wallet: - - Fixed handling of confirmed and unconfirmed conflicting order transactions in the wallet. - ## [1.3.1] ### Fixed - Wallet: + - Fixed wallet handling of V1 order command conflicts, allowing confirmed ConcludeOrder and FreezeOrder transactions + to replace conflicting unconfirmed order transactions correctly. - Fixed an issue where the wallet in cold mode would always use input commitments v0, thus producing signatures that may no longer be valid at the current height. - Fixed an issue where the wallet in cold mode would still try to access the mempool, which would cause `wallet-cli` From f1a8152f99e5573d37a0cb4fb16b3f51047fb9f9 Mon Sep 17 00:00:00 2001 From: Mykhailo Kremniov Date: Thu, 4 Jun 2026 12:24:30 +0300 Subject: [PATCH 14/15] Build fix and cleanup after merge --- wallet/src/account/output_cache/mod.rs | 15 --------------- wallet/src/signer/software_signer/mod.rs | 3 ++- wallet/src/signer/trezor_signer/tests.rs | 2 -- wallet/src/wallet/tests.rs | 11 +++++------ 4 files changed, 7 insertions(+), 24 deletions(-) diff --git a/wallet/src/account/output_cache/mod.rs b/wallet/src/account/output_cache/mod.rs index d1688236c..bc4bd4437 100644 --- a/wallet/src/account/output_cache/mod.rs +++ b/wallet/src/account/output_cache/mod.rs @@ -892,21 +892,6 @@ impl OutputCache { conflicting_txs.insert(tx.get_transaction().get_id()); continue; } - - if let Some((confirmed_cmd_tag, confirmed_order_id)) = - conflict_check.conflicting_order_command - { - if confirmed_tx.get_id() != tx.get_transaction().get_id() - && uses_conflicting_order_command( - unconfirmed_tx, - confirmed_cmd_tag, - confirmed_order_id, - ) - { - conflicting_txs.insert(tx.get_transaction().get_id()); - continue; - } - } } WalletTx::Block(_) => { utils::debug_panic_or_log!("Cannot be block reward"); diff --git a/wallet/src/signer/software_signer/mod.rs b/wallet/src/signer/software_signer/mod.rs index eff1f42e1..164df1cf7 100644 --- a/wallet/src/signer/software_signer/mod.rs +++ b/wallet/src/signer/software_signer/mod.rs @@ -20,7 +20,8 @@ use itertools::Itertools; use common::{ chain::{ - ChainConfig, Destination, SignedTransactionIntent, Transaction, TxOutput, + ChainConfig, Destination, SighashInputCommitmentVersion, SignedTransactionIntent, + Transaction, TxOutput, config::ChainType, htlc::HtlcSecret, signature::{ diff --git a/wallet/src/signer/trezor_signer/tests.rs b/wallet/src/signer/trezor_signer/tests.rs index 7b85f116b..ad0c763e5 100644 --- a/wallet/src/signer/trezor_signer/tests.rs +++ b/wallet/src/signer/trezor_signer/tests.rs @@ -111,7 +111,6 @@ async fn test_sign_transaction_intent(#[case] seed: Seed) { #[rstest] #[case(Seed::from_entropy(), true, SighashInputCommitmentVersion::V0)] - #[case(Seed::from_entropy(), false, SighashInputCommitmentVersion::V1)] #[trace] #[serial] @@ -263,7 +262,6 @@ async fn test_sign_transaction_intent_sig_consistency(#[case] seed: Seed) { #[rstest] #[case(Seed::from_entropy(), true, SighashInputCommitmentVersion::V0)] - #[case(Seed::from_entropy(), false, SighashInputCommitmentVersion::V1)] #[trace] #[serial] diff --git a/wallet/src/wallet/tests.rs b/wallet/src/wallet/tests.rs index 28aeb76c9..c8d4b453b 100644 --- a/wallet/src/wallet/tests.rs +++ b/wallet/src/wallet/tests.rs @@ -24,9 +24,8 @@ use rstest::rstest; use common::{ address::pubkeyhash::PublicKeyHash, chain::{ - self, - AccountNonce, AccountSpending, ChainstateUpgradeBuilder, Currency, Destination, Genesis, - OutPointSourceId, TxInput, + self, AccountNonce, AccountSpending, ChainstateUpgradeBuilder, Currency, Destination, + Genesis, NetUpgrades, OutPointSourceId, SighashInputCommitmentVersion, TxInput, block::{BlockReward, ConsensusData, consensus_data::PoSData, timestamp::BlockTimestamp}, config::{Builder, ChainType, create_mainnet, create_regtest, create_unit_test_config}, output_value::{OutputValue, RpcOutputValue}, @@ -34,7 +33,6 @@ use common::{ stakelock::StakePoolData, timelock::OutputTimeLock, tokens::{RPCIsTokenFrozen, TokenData, TokenIssuanceV0, TokenIssuanceV1}, - NetUpgrades, SighashInputCommitmentVersion }, primitives::{H256, Idable, per_thousand::PerThousand}, }; @@ -45,7 +43,7 @@ use crypto::{ }, vrf::transcript::no_rng::VRFTranscript, }; -use randomness::{CryptoRng, Rng, RngExt as _, SliceRandom}; +use randomness::{CryptoRng, IndexedRandom as _, Rng, RngExt as _, SliceRandom as _}; use serialization::{Encode, extras::non_empty_vec::DataOrNoVec, hex::HexEncode}; use storage::raw::DbMapId; use test_utils::{ @@ -5711,7 +5709,8 @@ async fn sign_decommission_pool_request_in_cold_wallet_expect_input_commitments_ assert_eq!(coin_balance, Amount::ZERO); // Generate a new block which sends reward to the wallet - let block1_amount = Amount::from_atoms(rng.gen_range(NETWORK_FEE + 100..NETWORK_FEE + 10000)); + let block1_amount = + Amount::from_atoms(rng.random_range(NETWORK_FEE + 100..NETWORK_FEE + 10000)); let (_, _block1) = create_block(&chain_config, &mut hot_wallet, vec![], block1_amount, 0).await; let pool_ids = hot_wallet.get_pools(DEFAULT_ACCOUNT_INDEX, WalletPoolsFilter::All).unwrap(); From 125b333d80cfb8fd971799b9acccaed1cb4748b5 Mon Sep 17 00:00:00 2001 From: Mykhailo Kremniov Date: Thu, 4 Jun 2026 13:20:20 +0300 Subject: [PATCH 15/15] Update changelogs - specify release date for 1.3.1 --- CHANGELOG.md | 2 +- api-server/CHANGELOG.md | 2 +- wasm-wrappers/CHANGELOG.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8e8609f5..91b6bb766 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,7 +43,7 @@ The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/ of a cluster that exceeds the limits. The default cluster limits are: max 64 transactions, max 100'000 bytes in total. -## [1.3.1] +## [1.3.1] - 2026-06-03 ### Fixed - Wallet: diff --git a/api-server/CHANGELOG.md b/api-server/CHANGELOG.md index 1fdf123d6..5491aea74 100644 --- a/api-server/CHANGELOG.md +++ b/api-server/CHANGELOG.md @@ -6,7 +6,7 @@ The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/ ## [Unreleased] -## [1.3.1] +## [1.3.1] - 2026-06-03 No changes diff --git a/wasm-wrappers/CHANGELOG.md b/wasm-wrappers/CHANGELOG.md index f23b00bf7..0cd012fc8 100644 --- a/wasm-wrappers/CHANGELOG.md +++ b/wasm-wrappers/CHANGELOG.md @@ -6,7 +6,7 @@ The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/ ## [Unreleased] -## [1.3.1] +## [1.3.1] - 2026-06-03 No changes