From 2a4564097bd70905ed9fa21b92cdf074a72f878e Mon Sep 17 00:00:00 2001 From: Leonardo Lima Date: Wed, 22 May 2024 18:34:30 -0300 Subject: [PATCH 1/2] deps(bdk): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0` deps(chain): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0` fix(chain): use `minimal_non_dust()` instead of `dust_value()` fix(chain): use `compute_txid()` instead of `txid` deps(testenv): bump `electrsd` to `0.28.0` deps(electrum): bump `electrum-client` to `0.20.0` fix(electrum): use `compute_txid()` instead of `txid` deps(esplora): bump `esplora-client` to `0.8.0` deps(bitcoind_rpc): bump `bitcoin` to `0.32.0`, `bitcoincore-rpc` to `0.19.0` fix(bitcoind_rpc): use `compute_txid()` instead of `txid` fix(nursery/tmp_plan): use proper `sighash` errors, and fix the expected `Signature` fields fix(sqlite): use `compute_txid()` instead of `txid` deps(hwi): bump `hwi` to `0.9.0` deps(wallet): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0` fix(wallet): use `compute_txid()` and `minimal_non_dust()` - update to use `compute_txid()` instead of deprecated `txid()` - update to use `minimal_non_dust()` instead of `dust_value()` - remove unused `bitcoin::hex::FromHex`. fix(wallet): uses `.into` conversion on `Network` for `NetworkKind` - uses `.into()` when appropriate, otherwise use the explicit `NetworkKind`, and it's `.is_mainnet()` method. fix(wallet): add P2wpkh, Taproot, InputsIndex errors to `SignerError` fix(wallet): fields on taproot, and ecdsa `Signature` structure fix(wallet/wallet): convert `Weight` to `usize` for now - converts the `bitcoin-units::Weight` type to `usize` with help of `to_wu()` method. - it should be updated/refactored in the future to handle the `Weight` type throughout the code instead of current `usize`, only converting it for now. - allows the usage of deprecated `is_provably_unspendable()`, needs further discussion if suggested `is_op_return` is suitable. - update the expect field to `signature`, as it was renamed from `sig`. fix(wallet/wallet): use `is_op_return` instead of `is_provably_unspendable` fix(wallet/wallet): use `relative::Locktime` instead of `Sequence` fix(wallet/descriptor): use `ParsePublicKeyError` fix(wallet/descriptor): use `.into()` to convert from `AbsLockTime` and `RelLockTime` to `absolute::LockTime` and `relative::LockTime` fix(wallet/wallet): use `Message::from_digest()` instead of relying on deprecated `ThirtyTwoByteHash` trait. fix(wallet/descriptor+wallet): expect `Threshold` type, and handle it internally fix(wallet/wallet): remove `0x` prefix from expected `TxId` display fix(examples): use `compute_txid()` instead of `txid` fix(ci): remove usage of `bitcoin/no-std` feature - remove comment: `# The `no-std` feature it's implied when the `std` feature is disabled.` --- .github/workflows/cont_integration.yml | 10 +- crates/bitcoind_rpc/Cargo.toml | 5 +- crates/bitcoind_rpc/tests/test_emitter.rs | 16 +- crates/chain/Cargo.toml | 5 +- crates/chain/src/descriptor_ext.rs | 2 +- crates/chain/src/indexed_tx_graph.rs | 6 +- crates/chain/src/keychain/txout_index.rs | 2 +- crates/chain/src/spk_txout_index.rs | 2 +- crates/chain/src/tx_data_traits.rs | 6 +- crates/chain/src/tx_graph.rs | 23 +-- crates/chain/tests/common/tx_template.rs | 6 +- crates/chain/tests/test_indexed_tx_graph.rs | 65 +++++--- crates/chain/tests/test_spk_txout_index.rs | 2 +- crates/chain/tests/test_tx_graph.rs | 151 +++++++++++------- crates/electrum/Cargo.toml | 4 +- crates/electrum/src/bdk_electrum_client.rs | 2 +- crates/esplora/Cargo.toml | 9 +- crates/hwi/Cargo.toml | 2 +- crates/sqlite/src/store.rs | 18 +-- crates/testenv/Cargo.toml | 2 +- crates/wallet/Cargo.toml | 4 +- crates/wallet/src/descriptor/dsl.rs | 21 ++- crates/wallet/src/descriptor/error.rs | 6 +- crates/wallet/src/descriptor/mod.rs | 12 +- crates/wallet/src/descriptor/policy.rs | 121 ++++++++++---- crates/wallet/src/descriptor/template.rs | 4 +- crates/wallet/src/keys/mod.rs | 24 ++- crates/wallet/src/wallet/coin_selection.rs | 2 +- crates/wallet/src/wallet/export.rs | 2 +- crates/wallet/src/wallet/mod.rs | 17 +- crates/wallet/src/wallet/signer.rs | 60 ++++--- crates/wallet/src/wallet/tx_builder.rs | 8 +- crates/wallet/src/wallet/utils.rs | 6 +- crates/wallet/tests/common.rs | 4 +- crates/wallet/tests/wallet.rs | 109 ++++++++----- example-crates/example_cli/src/lib.rs | 2 +- example-crates/wallet_electrum/src/main.rs | 2 +- .../wallet_esplora_async/src/main.rs | 2 +- .../wallet_esplora_blocking/src/main.rs | 2 +- nursery/tmp_plan/src/plan_impls.rs | 8 +- nursery/tmp_plan/src/requirements.rs | 28 ++-- 41 files changed, 480 insertions(+), 302 deletions(-) diff --git a/.github/workflows/cont_integration.yml b/.github/workflows/cont_integration.yml index 0b853b8de..510af1dbf 100644 --- a/.github/workflows/cont_integration.yml +++ b/.github/workflows/cont_integration.yml @@ -58,15 +58,15 @@ jobs: - name: Check bdk_chain working-directory: ./crates/chain # TODO "--target thumbv6m-none-eabi" should work but currently does not - run: cargo check --no-default-features --features bitcoin/no-std,miniscript/no-std,hashbrown + run: cargo check --no-default-features --features miniscript/no-std,hashbrown - name: Check bdk wallet working-directory: ./crates/wallet # TODO "--target thumbv6m-none-eabi" should work but currently does not - run: cargo check --no-default-features --features bitcoin/no-std,miniscript/no-std,bdk_chain/hashbrown + run: cargo check --no-default-features --features miniscript/no-std,bdk_chain/hashbrown - name: Check esplora working-directory: ./crates/esplora # TODO "--target thumbv6m-none-eabi" should work but currently does not - run: cargo check --no-default-features --features bitcoin/no-std,miniscript/no-std,bdk_chain/hashbrown + run: cargo check --no-default-features --features miniscript/no-std,bdk_chain/hashbrown check-wasm: name: Check WASM @@ -92,10 +92,10 @@ jobs: uses: Swatinem/rust-cache@v2.2.1 - name: Check bdk wallet working-directory: ./crates/wallet - run: cargo check --target wasm32-unknown-unknown --no-default-features --features bitcoin/no-std,miniscript/no-std,bdk_chain/hashbrown,dev-getrandom-wasm + run: cargo check --target wasm32-unknown-unknown --no-default-features --features miniscript/no-std,bdk_chain/hashbrown,dev-getrandom-wasm - name: Check esplora working-directory: ./crates/esplora - run: cargo check --target wasm32-unknown-unknown --no-default-features --features bitcoin/no-std,miniscript/no-std,bdk_chain/hashbrown,async + run: cargo check --target wasm32-unknown-unknown --no-default-features --features miniscript/no-std,bdk_chain/hashbrown,async fmt: name: Rust fmt diff --git a/crates/bitcoind_rpc/Cargo.toml b/crates/bitcoind_rpc/Cargo.toml index ac2abb97c..1652f1f83 100644 --- a/crates/bitcoind_rpc/Cargo.toml +++ b/crates/bitcoind_rpc/Cargo.toml @@ -13,9 +13,8 @@ readme = "README.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -# For no-std, remember to enable the bitcoin/no-std feature -bitcoin = { version = "0.31", default-features = false } -bitcoincore-rpc = { version = "0.18" } +bitcoin = { version = "0.32.0", default-features = false } +bitcoincore-rpc = { version = "0.19.0" } bdk_chain = { path = "../chain", version = "0.15", default-features = false } [dev-dependencies] diff --git a/crates/bitcoind_rpc/tests/test_emitter.rs b/crates/bitcoind_rpc/tests/test_emitter.rs index 5e0622f27..7eefbc049 100644 --- a/crates/bitcoind_rpc/tests/test_emitter.rs +++ b/crates/bitcoind_rpc/tests/test_emitter.rs @@ -197,7 +197,7 @@ fn test_into_tx_graph() -> anyhow::Result<()> { .graph .txs .iter() - .map(|tx| tx.txid()) + .map(|tx| tx.compute_txid()) .collect::>(), exp_txids, "changeset should have the 3 mempool transactions", @@ -440,7 +440,7 @@ fn mempool_avoids_re_emission() -> anyhow::Result<()> { let emitted_txids = emitter .mempool()? .into_iter() - .map(|(tx, _)| tx.txid()) + .map(|(tx, _)| tx.compute_txid()) .collect::>(); assert_eq!( emitted_txids, exp_txids, @@ -509,7 +509,7 @@ fn mempool_re_emits_if_tx_introduction_height_not_reached() -> anyhow::Result<() emitter .mempool()? .into_iter() - .map(|(tx, _)| tx.txid()) + .map(|(tx, _)| tx.compute_txid()) .collect::>(), tx_introductions.iter().map(|&(_, txid)| txid).collect(), "first mempool emission should include all txs", @@ -518,7 +518,7 @@ fn mempool_re_emits_if_tx_introduction_height_not_reached() -> anyhow::Result<() emitter .mempool()? .into_iter() - .map(|(tx, _)| tx.txid()) + .map(|(tx, _)| tx.compute_txid()) .collect::>(), tx_introductions.iter().map(|&(_, txid)| txid).collect(), "second mempool emission should still include all txs", @@ -538,7 +538,7 @@ fn mempool_re_emits_if_tx_introduction_height_not_reached() -> anyhow::Result<() let emitted_txids = emitter .mempool()? .into_iter() - .map(|(tx, _)| tx.txid()) + .map(|(tx, _)| tx.compute_txid()) .collect::>(); assert_eq!( emitted_txids, exp_txids, @@ -596,7 +596,7 @@ fn mempool_during_reorg() -> anyhow::Result<()> { emitter .mempool()? .into_iter() - .map(|(tx, _)| tx.txid()) + .map(|(tx, _)| tx.compute_txid()) .collect::>(), env.rpc_client() .get_raw_mempool()? @@ -633,7 +633,7 @@ fn mempool_during_reorg() -> anyhow::Result<()> { let mempool = emitter .mempool()? .into_iter() - .map(|(tx, _)| tx.txid()) + .map(|(tx, _)| tx.compute_txid()) .collect::>(); let exp_mempool = tx_introductions .iter() @@ -648,7 +648,7 @@ fn mempool_during_reorg() -> anyhow::Result<()> { let mempool = emitter .mempool()? .into_iter() - .map(|(tx, _)| tx.txid()) + .map(|(tx, _)| tx.compute_txid()) .collect::>(); let exp_mempool = tx_introductions .iter() diff --git a/crates/chain/Cargo.toml b/crates/chain/Cargo.toml index 57113218b..8b4a3c328 100644 --- a/crates/chain/Cargo.toml +++ b/crates/chain/Cargo.toml @@ -13,13 +13,12 @@ readme = "README.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -# For no-std, remember to enable the bitcoin/no-std feature -bitcoin = { version = "0.31.0", default-features = false } +bitcoin = { version = "0.32.0", default-features = false } serde_crate = { package = "serde", version = "1", optional = true, features = ["derive", "rc"] } # Use hashbrown as a feature flag to have HashSet and HashMap from it. hashbrown = { version = "0.9.1", optional = true, features = ["serde"] } -miniscript = { version = "11.0.0", optional = true, default-features = false } +miniscript = { version = "12.0.0", optional = true, default-features = false } [dev-dependencies] rand = "0.8" diff --git a/crates/chain/src/descriptor_ext.rs b/crates/chain/src/descriptor_ext.rs index af7175525..e42d291bb 100644 --- a/crates/chain/src/descriptor_ext.rs +++ b/crates/chain/src/descriptor_ext.rs @@ -31,7 +31,7 @@ impl DescriptorExt for Descriptor { self.at_derivation_index(0) .expect("descriptor can't have hardened derivation") .script_pubkey() - .dust_value() + .minimal_non_dust() .to_sat() } diff --git a/crates/chain/src/indexed_tx_graph.rs b/crates/chain/src/indexed_tx_graph.rs index ff6686332..3848ba64b 100644 --- a/crates/chain/src/indexed_tx_graph.rs +++ b/crates/chain/src/indexed_tx_graph.rs @@ -143,7 +143,7 @@ where let mut graph = tx_graph::ChangeSet::default(); for (tx, anchors) in txs { if self.index.is_tx_relevant(tx) { - let txid = tx.txid(); + let txid = tx.compute_txid(); graph.append(self.graph.insert_tx(tx.clone())); for anchor in anchors { graph.append(self.graph.insert_anchor(txid, anchor)); @@ -234,7 +234,7 @@ where for (tx_pos, tx) in block.txdata.iter().enumerate() { changeset.indexer.append(self.index.index_tx(tx)); if self.index.is_tx_relevant(tx) { - let txid = tx.txid(); + let txid = tx.compute_txid(); let anchor = A::from_block_position(block, block_id, tx_pos); changeset.graph.append(self.graph.insert_tx(tx.clone())); changeset @@ -261,7 +261,7 @@ where let mut graph = tx_graph::ChangeSet::default(); for (tx_pos, tx) in block.txdata.iter().enumerate() { let anchor = A::from_block_position(&block, block_id, tx_pos); - graph.append(self.graph.insert_anchor(tx.txid(), anchor)); + graph.append(self.graph.insert_anchor(tx.compute_txid(), anchor)); graph.append(self.graph.insert_tx(tx.clone())); } let indexer = self.index_tx_graph_changeset(&graph); diff --git a/crates/chain/src/keychain/txout_index.rs b/crates/chain/src/keychain/txout_index.rs index 994553b3f..18690a7a2 100644 --- a/crates/chain/src/keychain/txout_index.rs +++ b/crates/chain/src/keychain/txout_index.rs @@ -251,7 +251,7 @@ impl Indexer for KeychainTxOutIndex { fn index_tx(&mut self, tx: &bitcoin::Transaction) -> Self::ChangeSet { let mut changeset = super::ChangeSet::::default(); for (op, txout) in tx.output.iter().enumerate() { - changeset.append(self.index_txout(OutPoint::new(tx.txid(), op as u32), txout)); + changeset.append(self.index_txout(OutPoint::new(tx.compute_txid(), op as u32), txout)); } changeset } diff --git a/crates/chain/src/spk_txout_index.rs b/crates/chain/src/spk_txout_index.rs index 3ee423772..a724ccdbc 100644 --- a/crates/chain/src/spk_txout_index.rs +++ b/crates/chain/src/spk_txout_index.rs @@ -86,7 +86,7 @@ impl SpkTxOutIndex { /// 2. When getting new data from the chain, you usually scan it before incorporating it into your chain state. pub fn scan(&mut self, tx: &Transaction) -> BTreeSet { let mut scanned_indices = BTreeSet::new(); - let txid = tx.txid(); + let txid = tx.compute_txid(); for (i, txout) in tx.output.iter().enumerate() { let op = OutPoint::new(txid, i as u32); if let Some(spk_i) = self.scan_txout(op, txout) { diff --git a/crates/chain/src/tx_data_traits.rs b/crates/chain/src/tx_data_traits.rs index 8fa17ff90..b3ab3515e 100644 --- a/crates/chain/src/tx_data_traits.rs +++ b/crates/chain/src/tx_data_traits.rs @@ -43,7 +43,7 @@ use alloc::vec::Vec; /// let mut graph_a = TxGraph::::default(); /// let _ = graph_a.insert_tx(tx.clone()); /// graph_a.insert_anchor( -/// tx.txid(), +/// tx.compute_txid(), /// BlockId { /// height: 1, /// hash: Hash::hash("first".as_bytes()), @@ -58,7 +58,7 @@ use alloc::vec::Vec; /// let mut graph_b = TxGraph::::default(); /// let _ = graph_b.insert_tx(tx.clone()); /// graph_b.insert_anchor( -/// tx.txid(), +/// tx.compute_txid(), /// ConfirmationHeightAnchor { /// anchor_block: BlockId { /// height: 2, @@ -76,7 +76,7 @@ use alloc::vec::Vec; /// let mut graph_c = TxGraph::::default(); /// let _ = graph_c.insert_tx(tx.clone()); /// graph_c.insert_anchor( -/// tx.txid(), +/// tx.compute_txid(), /// ConfirmationTimeHeightAnchor { /// anchor_block: BlockId { /// height: 2, diff --git a/crates/chain/src/tx_graph.rs b/crates/chain/src/tx_graph.rs index eb119e3c9..13eefd66b 100644 --- a/crates/chain/src/tx_graph.rs +++ b/crates/chain/src/tx_graph.rs @@ -445,7 +445,7 @@ impl TxGraph { &'g self, tx: &'g Transaction, ) -> impl Iterator + '_ { - let txid = tx.txid(); + let txid = tx.compute_txid(); tx.input .iter() .enumerate() @@ -516,9 +516,10 @@ impl TxGraph { pub fn insert_tx>>(&mut self, tx: T) -> ChangeSet { let tx = tx.into(); let mut update = Self::default(); - update - .txs - .insert(tx.txid(), (TxNodeInternal::Whole(tx), BTreeSet::new(), 0)); + update.txs.insert( + tx.compute_txid(), + (TxNodeInternal::Whole(tx), BTreeSet::new(), 0), + ); self.apply_update(update) } @@ -533,7 +534,7 @@ impl TxGraph { ) -> ChangeSet { let mut changeset = ChangeSet::::default(); for (tx, seen_at) in txs { - changeset.append(self.insert_seen_at(tx.txid(), seen_at)); + changeset.append(self.insert_seen_at(tx.compute_txid(), seen_at)); changeset.append(self.insert_tx(tx)); } changeset @@ -642,7 +643,7 @@ impl TxGraph { pub fn apply_changeset(&mut self, changeset: ChangeSet) { for wrapped_tx in changeset.txs { let tx = wrapped_tx.as_ref(); - let txid = tx.txid(); + let txid = tx.compute_txid(); tx.input .iter() @@ -660,7 +661,7 @@ impl TxGraph { } Some((TxNodeInternal::Whole(tx), _, _)) => { debug_assert_eq!( - tx.as_ref().txid(), + tx.as_ref().compute_txid(), txid, "tx should produce txid that is same as key" ); @@ -825,7 +826,7 @@ impl TxGraph { // resulting array will also include `tx` let unconfirmed_ancestor_txs = TxAncestors::new_include_root(self, tx.clone(), |_, ancestor_tx: Arc| { - let tx_node = self.get_tx_node(ancestor_tx.as_ref().txid())?; + let tx_node = self.get_tx_node(ancestor_tx.as_ref().compute_txid())?; // We're filtering the ancestors to keep only the unconfirmed ones (= no anchors in // the best chain) for block in tx_node.anchors { @@ -843,7 +844,7 @@ impl TxGraph { // and our unconf descendants' last seen. let unconfirmed_descendants_txs = TxDescendants::new_include_root( self, - tx.as_ref().txid(), + tx.as_ref().compute_txid(), |_, descendant_txid: Txid| { let tx_node = self.get_tx_node(descendant_txid)?; // We're filtering the ancestors to keep only the unconfirmed ones (= no anchors in @@ -884,7 +885,7 @@ impl TxGraph { return Ok(None); } if conflicting_tx.last_seen_unconfirmed == *last_seen - && conflicting_tx.as_ref().txid() > tx.as_ref().txid() + && conflicting_tx.as_ref().compute_txid() > tx.as_ref().compute_txid() { // Conflicting tx has priority if txid of conflicting tx > txid of original tx return Ok(None); @@ -1255,7 +1256,7 @@ impl ChangeSet { tx.output .iter() .enumerate() - .map(move |(vout, txout)| (OutPoint::new(tx.txid(), vout as _), txout)) + .map(move |(vout, txout)| (OutPoint::new(tx.compute_txid(), vout as _), txout)) }) .chain(self.txouts.iter().map(|(op, txout)| (*op, txout))) } diff --git a/crates/chain/tests/common/tx_template.rs b/crates/chain/tests/common/tx_template.rs index 42bc0f79f..13bce01ba 100644 --- a/crates/chain/tests/common/tx_template.rs +++ b/crates/chain/tests/common/tx_template.rs @@ -125,14 +125,14 @@ pub fn init_graph<'a, A: Anchor + Clone + 'a>( .collect(), }; - tx_ids.insert(tx_tmp.tx_name, tx.txid()); + tx_ids.insert(tx_tmp.tx_name, tx.compute_txid()); spk_index.scan(&tx); let _ = graph.insert_tx(tx.clone()); for anchor in tx_tmp.anchors.iter() { - let _ = graph.insert_anchor(tx.txid(), anchor.clone()); + let _ = graph.insert_anchor(tx.compute_txid(), anchor.clone()); } if let Some(seen_at) = tx_tmp.last_seen { - let _ = graph.insert_seen_at(tx.txid(), seen_at); + let _ = graph.insert_seen_at(tx.compute_txid(), seen_at); } } (graph, spk_index, tx_ids) diff --git a/crates/chain/tests/test_indexed_tx_graph.rs b/crates/chain/tests/test_indexed_tx_graph.rs index 328163379..43085cc21 100644 --- a/crates/chain/tests/test_indexed_tx_graph.rs +++ b/crates/chain/tests/test_indexed_tx_graph.rs @@ -52,7 +52,7 @@ fn insert_relevant_txs() { let tx_b = Transaction { input: vec![TxIn { - previous_output: OutPoint::new(tx_a.txid(), 0), + previous_output: OutPoint::new(tx_a.compute_txid(), 0), ..Default::default() }], ..common::new_tx(1) @@ -60,7 +60,7 @@ fn insert_relevant_txs() { let tx_c = Transaction { input: vec![TxIn { - previous_output: OutPoint::new(tx_a.txid(), 1), + previous_output: OutPoint::new(tx_a.compute_txid(), 1), ..Default::default() }], ..common::new_tx(2) @@ -196,7 +196,7 @@ fn test_list_owned_txouts() { // tx3 spends tx2 and gives a change back in trusted keychain. Confirmed at Block 2. let tx3 = Transaction { input: vec![TxIn { - previous_output: OutPoint::new(tx2.txid(), 0), + previous_output: OutPoint::new(tx2.compute_txid(), 0), ..Default::default() }], output: vec![TxOut { @@ -340,16 +340,22 @@ fn test_list_owned_txouts() { balance, ) = fetch(0, &graph); - assert_eq!(confirmed_txouts_txid, [tx1.txid()].into()); + assert_eq!(confirmed_txouts_txid, [tx1.compute_txid()].into()); assert_eq!( unconfirmed_txouts_txid, - [tx2.txid(), tx3.txid(), tx4.txid(), tx5.txid()].into() + [ + tx2.compute_txid(), + tx3.compute_txid(), + tx4.compute_txid(), + tx5.compute_txid() + ] + .into() ); - assert_eq!(confirmed_utxos_txid, [tx1.txid()].into()); + assert_eq!(confirmed_utxos_txid, [tx1.compute_txid()].into()); assert_eq!( unconfirmed_utxos_txid, - [tx3.txid(), tx4.txid(), tx5.txid()].into() + [tx3.compute_txid(), tx4.compute_txid(), tx5.compute_txid()].into() ); assert_eq!( @@ -374,17 +380,20 @@ fn test_list_owned_txouts() { ) = fetch(1, &graph); // tx2 gets into confirmed txout set - assert_eq!(confirmed_txouts_txid, [tx1.txid(), tx2.txid()].into()); + assert_eq!( + confirmed_txouts_txid, + [tx1.compute_txid(), tx2.compute_txid()].into() + ); assert_eq!( unconfirmed_txouts_txid, - [tx3.txid(), tx4.txid(), tx5.txid()].into() + [tx3.compute_txid(), tx4.compute_txid(), tx5.compute_txid()].into() ); // tx2 doesn't get into confirmed utxos set - assert_eq!(confirmed_utxos_txid, [tx1.txid()].into()); + assert_eq!(confirmed_utxos_txid, [tx1.compute_txid()].into()); assert_eq!( unconfirmed_utxos_txid, - [tx3.txid(), tx4.txid(), tx5.txid()].into() + [tx3.compute_txid(), tx4.compute_txid(), tx5.compute_txid()].into() ); assert_eq!( @@ -411,13 +420,22 @@ fn test_list_owned_txouts() { // tx3 now gets into the confirmed txout set assert_eq!( confirmed_txouts_txid, - [tx1.txid(), tx2.txid(), tx3.txid()].into() + [tx1.compute_txid(), tx2.compute_txid(), tx3.compute_txid()].into() + ); + assert_eq!( + unconfirmed_txouts_txid, + [tx4.compute_txid(), tx5.compute_txid()].into() ); - assert_eq!(unconfirmed_txouts_txid, [tx4.txid(), tx5.txid()].into()); // tx3 also gets into confirmed utxo set - assert_eq!(confirmed_utxos_txid, [tx1.txid(), tx3.txid()].into()); - assert_eq!(unconfirmed_utxos_txid, [tx4.txid(), tx5.txid()].into()); + assert_eq!( + confirmed_utxos_txid, + [tx1.compute_txid(), tx3.compute_txid()].into() + ); + assert_eq!( + unconfirmed_utxos_txid, + [tx4.compute_txid(), tx5.compute_txid()].into() + ); assert_eq!( balance, @@ -442,12 +460,21 @@ fn test_list_owned_txouts() { assert_eq!( confirmed_txouts_txid, - [tx1.txid(), tx2.txid(), tx3.txid()].into() + [tx1.compute_txid(), tx2.compute_txid(), tx3.compute_txid()].into() + ); + assert_eq!( + unconfirmed_txouts_txid, + [tx4.compute_txid(), tx5.compute_txid()].into() ); - assert_eq!(unconfirmed_txouts_txid, [tx4.txid(), tx5.txid()].into()); - assert_eq!(confirmed_utxos_txid, [tx1.txid(), tx3.txid()].into()); - assert_eq!(unconfirmed_utxos_txid, [tx4.txid(), tx5.txid()].into()); + assert_eq!( + confirmed_utxos_txid, + [tx1.compute_txid(), tx3.compute_txid()].into() + ); + assert_eq!( + unconfirmed_utxos_txid, + [tx4.compute_txid(), tx5.compute_txid()].into() + ); // Coinbase is still immature assert_eq!( diff --git a/crates/chain/tests/test_spk_txout_index.rs b/crates/chain/tests/test_spk_txout_index.rs index 2317b0a7d..8be252703 100644 --- a/crates/chain/tests/test_spk_txout_index.rs +++ b/crates/chain/tests/test_spk_txout_index.rs @@ -47,7 +47,7 @@ fn spk_txout_sent_and_received() { lock_time: absolute::LockTime::ZERO, input: vec![TxIn { previous_output: OutPoint { - txid: tx1.txid(), + txid: tx1.compute_txid(), vout: 0, }, ..Default::default() diff --git a/crates/chain/tests/test_tx_graph.rs b/crates/chain/tests/test_tx_graph.rs index 637758862..21b955d21 100644 --- a/crates/chain/tests/test_tx_graph.rs +++ b/crates/chain/tests/test_tx_graph.rs @@ -130,11 +130,11 @@ fn insert_txouts() { // Mark it as confirmed. assert_eq!( - graph.insert_anchor(update_txs.txid(), conf_anchor), + graph.insert_anchor(update_txs.compute_txid(), conf_anchor), ChangeSet { txs: [].into(), txouts: [].into(), - anchors: [(conf_anchor, update_txs.txid())].into(), + anchors: [(conf_anchor, update_txs.compute_txid())].into(), last_seen: [].into() } ); @@ -149,7 +149,11 @@ fn insert_txouts() { ChangeSet { txs: [Arc::new(update_txs.clone())].into(), txouts: update_ops.clone().into(), - anchors: [(conf_anchor, update_txs.txid()), (unconf_anchor, h!("tx2"))].into(), + anchors: [ + (conf_anchor, update_txs.compute_txid()), + (unconf_anchor, h!("tx2")) + ] + .into(), last_seen: [(h!("tx2"), 1000000)].into() } ); @@ -183,7 +187,9 @@ fn insert_txouts() { ); assert_eq!( - graph.tx_outputs(update_txs.txid()).expect("should exists"), + graph + .tx_outputs(update_txs.compute_txid()) + .expect("should exists"), [( 0u32, &TxOut { @@ -200,7 +206,11 @@ fn insert_txouts() { ChangeSet { txs: [Arc::new(update_txs.clone())].into(), txouts: update_ops.into_iter().chain(original_ops).collect(), - anchors: [(conf_anchor, update_txs.txid()), (unconf_anchor, h!("tx2"))].into(), + anchors: [ + (conf_anchor, update_txs.compute_txid()), + (unconf_anchor, h!("tx2")) + ] + .into(), last_seen: [(h!("tx2"), 1000000)].into() } ); @@ -235,7 +245,7 @@ fn insert_tx_graph_keeps_track_of_spend() { }; let op = OutPoint { - txid: tx1.txid(), + txid: tx1.compute_txid(), vout: 0, }; @@ -261,7 +271,7 @@ fn insert_tx_graph_keeps_track_of_spend() { assert_eq!( graph1.outspends(op), - &iter::once(tx2.txid()).collect::>() + &iter::once(tx2.compute_txid()).collect::>() ); assert_eq!(graph2.outspends(op), graph1.outspends(op)); } @@ -281,7 +291,9 @@ fn insert_tx_can_retrieve_full_tx_from_graph() { let mut graph = TxGraph::<()>::default(); let _ = graph.insert_tx(tx.clone()); assert_eq!( - graph.get_tx(tx.txid()).map(|tx| tx.as_ref().clone()), + graph + .get_tx(tx.compute_txid()) + .map(|tx| tx.as_ref().clone()), Some(tx) ); } @@ -301,7 +313,7 @@ fn insert_tx_displaces_txouts() { let changeset = tx_graph.insert_txout( OutPoint { - txid: tx.txid(), + txid: tx.compute_txid(), vout: 0, }, TxOut { @@ -314,7 +326,7 @@ fn insert_tx_displaces_txouts() { let _ = tx_graph.insert_txout( OutPoint { - txid: tx.txid(), + txid: tx.compute_txid(), vout: 0, }, TxOut { @@ -328,7 +340,7 @@ fn insert_tx_displaces_txouts() { assert_eq!( tx_graph .get_txout(OutPoint { - txid: tx.txid(), + txid: tx.compute_txid(), vout: 0 }) .unwrap() @@ -337,7 +349,7 @@ fn insert_tx_displaces_txouts() { ); assert_eq!( tx_graph.get_txout(OutPoint { - txid: tx.txid(), + txid: tx.compute_txid(), vout: 1 }), None @@ -361,7 +373,7 @@ fn insert_txout_does_not_displace_tx() { let _ = tx_graph.insert_txout( OutPoint { - txid: tx.txid(), + txid: tx.compute_txid(), vout: 0, }, TxOut { @@ -372,7 +384,7 @@ fn insert_txout_does_not_displace_tx() { let _ = tx_graph.insert_txout( OutPoint { - txid: tx.txid(), + txid: tx.compute_txid(), vout: 0, }, TxOut { @@ -384,7 +396,7 @@ fn insert_txout_does_not_displace_tx() { assert_eq!( tx_graph .get_txout(OutPoint { - txid: tx.txid(), + txid: tx.compute_txid(), vout: 0 }) .unwrap() @@ -393,7 +405,7 @@ fn insert_txout_does_not_displace_tx() { ); assert_eq!( tx_graph.get_txout(OutPoint { - txid: tx.txid(), + txid: tx.compute_txid(), vout: 1 }), None @@ -443,14 +455,14 @@ fn test_calculate_fee() { input: vec![ TxIn { previous_output: OutPoint { - txid: intx1.txid(), + txid: intx1.compute_txid(), vout: 0, }, ..Default::default() }, TxIn { previous_output: OutPoint { - txid: intx2.txid(), + txid: intx2.compute_txid(), vout: 0, }, ..Default::default() @@ -543,7 +555,7 @@ fn test_walk_ancestors() { // tx_b0 spends tx_a0 let tx_b0 = Transaction { input: vec![TxIn { - previous_output: OutPoint::new(tx_a0.txid(), 0), + previous_output: OutPoint::new(tx_a0.compute_txid(), 0), ..TxIn::default() }], output: vec![TxOut::NULL, TxOut::NULL], @@ -553,7 +565,7 @@ fn test_walk_ancestors() { // tx_b1 spends tx_a0 let tx_b1 = Transaction { input: vec![TxIn { - previous_output: OutPoint::new(tx_a0.txid(), 1), + previous_output: OutPoint::new(tx_a0.compute_txid(), 1), ..TxIn::default() }], output: vec![TxOut::NULL], @@ -572,7 +584,7 @@ fn test_walk_ancestors() { // tx_c0 spends tx_b0 let tx_c0 = Transaction { input: vec![TxIn { - previous_output: OutPoint::new(tx_b0.txid(), 0), + previous_output: OutPoint::new(tx_b0.compute_txid(), 0), ..TxIn::default() }], output: vec![TxOut::NULL], @@ -582,7 +594,7 @@ fn test_walk_ancestors() { // tx_c1 spends tx_b0 let tx_c1 = Transaction { input: vec![TxIn { - previous_output: OutPoint::new(tx_b0.txid(), 1), + previous_output: OutPoint::new(tx_b0.compute_txid(), 1), ..TxIn::default() }], output: vec![TxOut::NULL], @@ -593,11 +605,11 @@ fn test_walk_ancestors() { let tx_c2 = Transaction { input: vec![ TxIn { - previous_output: OutPoint::new(tx_b1.txid(), 0), + previous_output: OutPoint::new(tx_b1.compute_txid(), 0), ..TxIn::default() }, TxIn { - previous_output: OutPoint::new(tx_b2.txid(), 0), + previous_output: OutPoint::new(tx_b2.compute_txid(), 0), ..TxIn::default() }, ], @@ -617,7 +629,7 @@ fn test_walk_ancestors() { // tx_d0 spends tx_c1 let tx_d0 = Transaction { input: vec![TxIn { - previous_output: OutPoint::new(tx_c1.txid(), 0), + previous_output: OutPoint::new(tx_c1.compute_txid(), 0), ..TxIn::default() }], output: vec![TxOut::NULL], @@ -628,11 +640,11 @@ fn test_walk_ancestors() { let tx_d1 = Transaction { input: vec![ TxIn { - previous_output: OutPoint::new(tx_c2.txid(), 0), + previous_output: OutPoint::new(tx_c2.compute_txid(), 0), ..TxIn::default() }, TxIn { - previous_output: OutPoint::new(tx_c3.txid(), 0), + previous_output: OutPoint::new(tx_c3.compute_txid(), 0), ..TxIn::default() }, ], @@ -643,7 +655,7 @@ fn test_walk_ancestors() { // tx_e0 spends tx_d1 let tx_e0 = Transaction { input: vec![TxIn { - previous_output: OutPoint::new(tx_d1.txid(), 0), + previous_output: OutPoint::new(tx_d1.compute_txid(), 0), ..TxIn::default() }], output: vec![TxOut::NULL], @@ -665,7 +677,7 @@ fn test_walk_ancestors() { ]); [&tx_a0, &tx_b1].iter().for_each(|&tx| { - let changeset = graph.insert_anchor(tx.txid(), tip.block_id()); + let changeset = graph.insert_anchor(tx.compute_txid(), tip.block_id()); assert!(!changeset.is_empty()); }); @@ -682,7 +694,7 @@ fn test_walk_ancestors() { // Only traverse unconfirmed ancestors of tx_e0 this time graph .walk_ancestors(tx_e0.clone(), |depth, tx| { - let tx_node = graph.get_tx_node(tx.txid())?; + let tx_node = graph.get_tx_node(tx.compute_txid())?; for block in tx_node.anchors { match local_chain.is_block_in_chain(block.anchor_block(), tip.block_id()) { Ok(Some(true)) => return None, @@ -746,15 +758,15 @@ fn test_conflicting_descendants() { // tx_b spends tx_a let tx_b = Transaction { input: vec![TxIn { - previous_output: OutPoint::new(tx_a.txid(), 0), + previous_output: OutPoint::new(tx_a.compute_txid(), 0), ..TxIn::default() }], output: vec![TxOut::NULL], ..common::new_tx(2) }; - let txid_a = tx_a.txid(); - let txid_b = tx_b.txid(); + let txid_a = tx_a.compute_txid(); + let txid_b = tx_b.compute_txid(); let mut graph = TxGraph::<()>::default(); let _ = graph.insert_tx(tx_a); @@ -778,7 +790,7 @@ fn test_descendants_no_repeat() { let txs_b = (0..3) .map(|vout| Transaction { input: vec![TxIn { - previous_output: OutPoint::new(tx_a.txid(), vout), + previous_output: OutPoint::new(tx_a.compute_txid(), vout), ..TxIn::default() }], output: vec![TxOut::NULL], @@ -789,7 +801,7 @@ fn test_descendants_no_repeat() { let txs_c = (0..2) .map(|vout| Transaction { input: vec![TxIn { - previous_output: OutPoint::new(txs_b[vout as usize].txid(), vout), + previous_output: OutPoint::new(txs_b[vout as usize].compute_txid(), vout), ..TxIn::default() }], output: vec![TxOut::NULL], @@ -800,11 +812,11 @@ fn test_descendants_no_repeat() { let tx_d = Transaction { input: vec![ TxIn { - previous_output: OutPoint::new(txs_c[0].txid(), 0), + previous_output: OutPoint::new(txs_c[0].compute_txid(), 0), ..TxIn::default() }, TxIn { - previous_output: OutPoint::new(txs_c[1].txid(), 0), + previous_output: OutPoint::new(txs_c[1].compute_txid(), 0), ..TxIn::default() }, ], @@ -814,7 +826,7 @@ fn test_descendants_no_repeat() { let tx_e = Transaction { input: vec![TxIn { - previous_output: OutPoint::new(tx_d.txid(), 0), + previous_output: OutPoint::new(tx_d.compute_txid(), 0), ..TxIn::default() }], output: vec![TxOut::NULL], @@ -848,11 +860,11 @@ fn test_descendants_no_repeat() { .chain(core::iter::once(&tx_e)) { let _ = graph.insert_tx(tx.clone()); - expected_txids.push(tx.txid()); + expected_txids.push(tx.compute_txid()); } let descendants = graph - .walk_descendants(tx_a.txid(), |_, txid| Some(txid)) + .walk_descendants(tx_a.compute_txid(), |_, txid| Some(txid)) .collect::>(); assert_eq!(descendants, expected_txids); @@ -888,7 +900,7 @@ fn test_chain_spends() { // The first confirmed transaction spends vout: 0. And is confirmed at block 98. let tx_1 = Transaction { input: vec![TxIn { - previous_output: OutPoint::new(tx_0.txid(), 0), + previous_output: OutPoint::new(tx_0.compute_txid(), 0), ..TxIn::default() }], output: vec![ @@ -907,7 +919,7 @@ fn test_chain_spends() { // The second transactions spends vout:1, and is unconfirmed. let tx_2 = Transaction { input: vec![TxIn { - previous_output: OutPoint::new(tx_0.txid(), 1), + previous_output: OutPoint::new(tx_0.compute_txid(), 1), ..TxIn::default() }], output: vec![ @@ -931,7 +943,7 @@ fn test_chain_spends() { for (ht, tx) in [(95, &tx_0), (98, &tx_1)] { let _ = graph.insert_anchor( - tx.txid(), + tx.compute_txid(), ConfirmationHeightAnchor { anchor_block: tip.block_id(), confirmation_height: ht, @@ -941,19 +953,23 @@ fn test_chain_spends() { // Assert that confirmed spends are returned correctly. assert_eq!( - graph.get_chain_spend(&local_chain, tip.block_id(), OutPoint::new(tx_0.txid(), 0)), + graph.get_chain_spend( + &local_chain, + tip.block_id(), + OutPoint::new(tx_0.compute_txid(), 0) + ), Some(( ChainPosition::Confirmed(&ConfirmationHeightAnchor { anchor_block: tip.block_id(), confirmation_height: 98 }), - tx_1.txid(), + tx_1.compute_txid(), )), ); // Check if chain position is returned correctly. assert_eq!( - graph.get_chain_position(&local_chain, tip.block_id(), tx_0.txid()), + graph.get_chain_position(&local_chain, tip.block_id(), tx_0.compute_txid()), // Some(ObservedAs::Confirmed(&local_chain.get_block(95).expect("block expected"))), Some(ChainPosition::Confirmed(&ConfirmationHeightAnchor { anchor_block: tip.block_id(), @@ -963,25 +979,33 @@ fn test_chain_spends() { // Even if unconfirmed tx has a last_seen of 0, it can still be part of a chain spend. assert_eq!( - graph.get_chain_spend(&local_chain, tip.block_id(), OutPoint::new(tx_0.txid(), 1)), - Some((ChainPosition::Unconfirmed(0), tx_2.txid())), + graph.get_chain_spend( + &local_chain, + tip.block_id(), + OutPoint::new(tx_0.compute_txid(), 1) + ), + Some((ChainPosition::Unconfirmed(0), tx_2.compute_txid())), ); // Mark the unconfirmed as seen and check correct ObservedAs status is returned. - let _ = graph.insert_seen_at(tx_2.txid(), 1234567); + let _ = graph.insert_seen_at(tx_2.compute_txid(), 1234567); // Check chain spend returned correctly. assert_eq!( graph - .get_chain_spend(&local_chain, tip.block_id(), OutPoint::new(tx_0.txid(), 1)) + .get_chain_spend( + &local_chain, + tip.block_id(), + OutPoint::new(tx_0.compute_txid(), 1) + ) .unwrap(), - (ChainPosition::Unconfirmed(1234567), tx_2.txid()) + (ChainPosition::Unconfirmed(1234567), tx_2.compute_txid()) ); // A conflicting transaction that conflicts with tx_1. let tx_1_conflict = Transaction { input: vec![TxIn { - previous_output: OutPoint::new(tx_0.txid(), 0), + previous_output: OutPoint::new(tx_0.compute_txid(), 0), ..Default::default() }], ..common::new_tx(0) @@ -990,13 +1014,13 @@ fn test_chain_spends() { // Because this tx conflicts with an already confirmed transaction, chain position should return none. assert!(graph - .get_chain_position(&local_chain, tip.block_id(), tx_1_conflict.txid()) + .get_chain_position(&local_chain, tip.block_id(), tx_1_conflict.compute_txid()) .is_none()); // Another conflicting tx that conflicts with tx_2. let tx_2_conflict = Transaction { input: vec![TxIn { - previous_output: OutPoint::new(tx_0.txid(), 1), + previous_output: OutPoint::new(tx_0.compute_txid(), 1), ..Default::default() }], ..common::new_tx(0) @@ -1004,12 +1028,12 @@ fn test_chain_spends() { // Insert in graph and mark it as seen. let _ = graph.insert_tx(tx_2_conflict.clone()); - let _ = graph.insert_seen_at(tx_2_conflict.txid(), 1234568); + let _ = graph.insert_seen_at(tx_2_conflict.compute_txid(), 1234568); // This should return a valid observation with correct last seen. assert_eq!( graph - .get_chain_position(&local_chain, tip.block_id(), tx_2_conflict.txid()) + .get_chain_position(&local_chain, tip.block_id(), tx_2_conflict.compute_txid()) .expect("position expected"), ChainPosition::Unconfirmed(1234568) ); @@ -1017,14 +1041,21 @@ fn test_chain_spends() { // Chain_spend now catches the new transaction as the spend. assert_eq!( graph - .get_chain_spend(&local_chain, tip.block_id(), OutPoint::new(tx_0.txid(), 1)) + .get_chain_spend( + &local_chain, + tip.block_id(), + OutPoint::new(tx_0.compute_txid(), 1) + ) .expect("expect observation"), - (ChainPosition::Unconfirmed(1234568), tx_2_conflict.txid()) + ( + ChainPosition::Unconfirmed(1234568), + tx_2_conflict.compute_txid() + ) ); // Chain position of the `tx_2` is now none, as it is older than `tx_2_conflict` assert!(graph - .get_chain_position(&local_chain, tip.block_id(), tx_2.txid()) + .get_chain_position(&local_chain, tip.block_id(), tx_2.compute_txid()) .is_none()); } @@ -1065,7 +1096,7 @@ fn test_changeset_last_seen_append() { fn update_last_seen_unconfirmed() { let mut graph = TxGraph::<()>::default(); let tx = new_tx(0); - let txid = tx.txid(); + let txid = tx.compute_txid(); // insert a new tx // initially we have a last_seen of 0, and no anchors diff --git a/crates/electrum/Cargo.toml b/crates/electrum/Cargo.toml index 6ee822ec0..a7b72847b 100644 --- a/crates/electrum/Cargo.toml +++ b/crates/electrum/Cargo.toml @@ -13,8 +13,8 @@ readme = "README.md" [dependencies] bdk_chain = { path = "../chain", version = "0.15.0" } -electrum-client = { version = "0.19" } +electrum-client = { version = "0.20" } #rustls = { version = "=0.21.1", optional = true, features = ["dangerous_configuration"] } [dev-dependencies] -bdk_testenv = { path = "../testenv", default-features = false } \ No newline at end of file +bdk_testenv = { path = "../testenv", default-features = false } diff --git a/crates/electrum/src/bdk_electrum_client.rs b/crates/electrum/src/bdk_electrum_client.rs index 5f7d59b22..32be25886 100644 --- a/crates/electrum/src/bdk_electrum_client.rs +++ b/crates/electrum/src/bdk_electrum_client.rs @@ -310,7 +310,7 @@ impl BdkElectrumClient { Some(txout) => txout, None => continue, }; - debug_assert_eq!(op_tx.txid(), op_txid); + debug_assert_eq!(op_tx.compute_txid(), op_txid); // attempt to find the following transactions (alongside their chain positions), and // add to our sparsechain `update`: diff --git a/crates/esplora/Cargo.toml b/crates/esplora/Cargo.toml index 471e3c763..69339e891 100644 --- a/crates/esplora/Cargo.toml +++ b/crates/esplora/Cargo.toml @@ -13,13 +13,12 @@ readme = "README.md" [dependencies] bdk_chain = { path = "../chain", version = "0.15.0", default-features = false } -esplora-client = { version = "0.7.0", default-features = false } +esplora-client = { version = "0.8.0", default-features = false } async-trait = { version = "0.1.66", optional = true } futures = { version = "0.3.26", optional = true } -# use these dependencies if you need to enable their /no-std features -bitcoin = { version = "0.31.0", optional = true, default-features = false } -miniscript = { version = "11.0.0", optional = true, default-features = false } +bitcoin = { version = "0.32.0", optional = true, default-features = false } +miniscript = { version = "12.0.0", optional = true, default-features = false } [dev-dependencies] bdk_testenv = { path = "../testenv", default-features = false } @@ -27,7 +26,7 @@ tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] } [features] default = ["std", "async-https", "blocking-https-rustls"] -std = ["bdk_chain/std"] +std = ["bdk_chain/std", "miniscript?/std"] async = ["async-trait", "futures", "esplora-client/async"] async-https = ["async", "esplora-client/async-https"] async-https-rustls = ["async", "esplora-client/async-https-rustls"] diff --git a/crates/hwi/Cargo.toml b/crates/hwi/Cargo.toml index 6aad8e54d..d73646320 100644 --- a/crates/hwi/Cargo.toml +++ b/crates/hwi/Cargo.toml @@ -10,4 +10,4 @@ readme = "README.md" [dependencies] bdk_wallet = { path = "../wallet", version = "1.0.0-alpha.12" } -hwi = { version = "0.8.0", features = [ "miniscript"] } +hwi = { version = "0.9.0", features = [ "miniscript"] } diff --git a/crates/sqlite/src/store.rs b/crates/sqlite/src/store.rs index beeb9e0aa..fe4572dbd 100644 --- a/crates/sqlite/src/store.rs +++ b/crates/sqlite/src/store.rs @@ -303,7 +303,7 @@ impl Store { let insert_tx_stmt = &mut db_transaction .prepare_cached("INSERT INTO tx (txid, whole_tx) VALUES (:txid, :whole_tx) ON CONFLICT (txid) DO UPDATE SET whole_tx = :whole_tx WHERE txid = :txid") .expect("insert or update tx whole_tx statement"); - let txid = tx.txid().to_string(); + let txid = tx.compute_txid().to_string(); let whole_tx = serialize(&tx); insert_tx_stmt .execute(named_params! {":txid": txid, ":whole_tx": whole_tx }) @@ -685,9 +685,9 @@ mod test { let tx2_hex = Vec::::from_hex("01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0432e7494d010e062f503253482fffffffff0100f2052a010000002321038a7f6ef1c8ca0c588aa53fa860128077c9e6c11e6830f4d7ee4e763a56b7718fac00000000").unwrap(); let tx2: Arc = Arc::new(deserialize(tx2_hex.as_slice()).unwrap()); - let outpoint0_0 = OutPoint::new(tx0.txid(), 0); + let outpoint0_0 = OutPoint::new(tx0.compute_txid(), 0); let txout0_0 = tx0.output.first().unwrap().clone(); - let outpoint1_0 = OutPoint::new(tx1.txid(), 0); + let outpoint1_0 = OutPoint::new(tx1.compute_txid(), 0); let txout1_0 = tx1.output.first().unwrap().clone(); let anchor1 = anchor_fn(1, 1296667328, block_hash_1); @@ -696,11 +696,11 @@ mod test { let tx_graph_changeset = tx_graph::ChangeSet:: { txs: [tx0.clone(), tx1.clone()].into(), txouts: [(outpoint0_0, txout0_0), (outpoint1_0, txout1_0)].into(), - anchors: [(anchor1, tx0.txid()), (anchor1, tx1.txid())].into(), + anchors: [(anchor1, tx0.compute_txid()), (anchor1, tx1.compute_txid())].into(), last_seen: [ - (tx0.txid(), 1598918400), - (tx1.txid(), 1598919121), - (tx2.txid(), 1608919121), + (tx0.compute_txid(), 1598918400), + (tx1.compute_txid(), 1598919121), + (tx2.compute_txid(), 1608919121), ] .into(), }; @@ -730,7 +730,7 @@ mod test { txs: [tx2.clone()].into(), txouts: BTreeMap::default(), anchors: BTreeSet::default(), - last_seen: [(tx2.txid(), 1708919121)].into(), + last_seen: [(tx2.compute_txid(), 1708919121)].into(), }; let graph_changeset2: indexed_tx_graph::ChangeSet> = @@ -749,7 +749,7 @@ mod test { let tx_graph_changeset3 = tx_graph::ChangeSet:: { txs: BTreeSet::default(), txouts: BTreeMap::default(), - anchors: [(anchor2, tx0.txid()), (anchor2, tx1.txid())].into(), + anchors: [(anchor2, tx0.compute_txid()), (anchor2, tx1.compute_txid())].into(), last_seen: BTreeMap::default(), }; diff --git a/crates/testenv/Cargo.toml b/crates/testenv/Cargo.toml index 7f46d9cf2..384d899df 100644 --- a/crates/testenv/Cargo.toml +++ b/crates/testenv/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" [dependencies] bdk_chain = { path = "../chain", version = "0.15", default-features = false } -electrsd = { version= "0.27.1", features = ["bitcoind_25_0", "esplora_a33e97e1", "legacy"] } +electrsd = { version = "0.28.0", features = ["bitcoind_25_0", "esplora_a33e97e1", "legacy"] } [features] default = ["std"] diff --git a/crates/wallet/Cargo.toml b/crates/wallet/Cargo.toml index 2a129e959..8b72af47c 100644 --- a/crates/wallet/Cargo.toml +++ b/crates/wallet/Cargo.toml @@ -15,8 +15,8 @@ rust-version = "1.63" [dependencies] anyhow = { version = "1", default-features = false } rand = "^0.8" -miniscript = { version = "11.0.0", features = ["serde"], default-features = false } -bitcoin = { version = "0.31.0", features = ["serde", "base64", "rand-std"], default-features = false } +miniscript = { version = "12.0.0", features = ["serde"], default-features = false } +bitcoin = { version = "0.32.0", features = ["serde", "base64", "rand-std"], default-features = false } serde = { version = "^1.0", features = ["derive"] } serde_json = { version = "^1.0" } bdk_chain = { path = "../chain", version = "0.15.0", features = ["miniscript", "serde"], default-features = false } diff --git a/crates/wallet/src/descriptor/dsl.rs b/crates/wallet/src/descriptor/dsl.rs index 0d7e7c8e9..e97597176 100644 --- a/crates/wallet/src/descriptor/dsl.rs +++ b/crates/wallet/src/descriptor/dsl.rs @@ -703,10 +703,10 @@ macro_rules! fragment { $crate::keys::make_pkh($key, &secp) }); ( after ( $value:expr ) ) => ({ - $crate::impl_leaf_opcode_value!(After, $crate::miniscript::AbsLockTime::from_consensus($value)) + $crate::impl_leaf_opcode_value!(After, $crate::miniscript::AbsLockTime::from_consensus($value).expect("valid `AbsLockTime`")) }); ( older ( $value:expr ) ) => ({ - $crate::impl_leaf_opcode_value!(Older, $crate::bitcoin::Sequence($value)) // TODO!! + $crate::impl_leaf_opcode_value!(Older, $crate::miniscript::RelLockTime::from_consensus($value).expect("valid `RelLockTime`")) // TODO!! }); ( sha256 ( $hash:expr ) ) => ({ $crate::impl_leaf_opcode_value!(Sha256, $hash) @@ -757,7 +757,8 @@ macro_rules! fragment { (keys_acc, net_acc) }); - $crate::impl_leaf_opcode_value_two!(Thresh, $thresh, items) + let thresh = $crate::miniscript::Threshold::new($thresh, items).expect("valid threshold and pks collection"); + $crate::impl_leaf_opcode_value!(Thresh, thresh) .map(|(minisc, _, _)| (minisc, key_maps, valid_networks)) }); ( thresh ( $thresh:expr, $( $inner:tt )* ) ) => ({ @@ -769,7 +770,12 @@ macro_rules! fragment { ( multi_vec ( $thresh:expr, $keys:expr ) ) => ({ let secp = $crate::bitcoin::secp256k1::Secp256k1::new(); - $crate::keys::make_multi($thresh, $crate::miniscript::Terminal::Multi, $keys, &secp) + let fun = |k, pks| { + let thresh = $crate::miniscript::Threshold::new(k, pks).expect("valid threshold and pks collection"); + $crate::miniscript::Terminal::Multi(thresh) + }; + + $crate::keys::make_multi($thresh, fun, $keys, &secp) }); ( multi ( $thresh:expr $(, $key:expr )+ ) ) => ({ $crate::group_multi_keys!( $( $key ),* ) @@ -778,7 +784,12 @@ macro_rules! fragment { ( multi_a_vec ( $thresh:expr, $keys:expr ) ) => ({ let secp = $crate::bitcoin::secp256k1::Secp256k1::new(); - $crate::keys::make_multi($thresh, $crate::miniscript::Terminal::MultiA, $keys, &secp) + let fun = |k, pks| { + let thresh = $crate::miniscript::Threshold::new(k, pks).expect("valid threshold and pks collection"); + $crate::miniscript::Terminal::MultiA(thresh) + }; + + $crate::keys::make_multi($thresh, fun, $keys, &secp) }); ( multi_a ( $thresh:expr $(, $key:expr )+ ) ) => ({ $crate::group_multi_keys!( $( $key ),* ) diff --git a/crates/wallet/src/descriptor/error.rs b/crates/wallet/src/descriptor/error.rs index 4d15e3602..48d0091dd 100644 --- a/crates/wallet/src/descriptor/error.rs +++ b/crates/wallet/src/descriptor/error.rs @@ -37,7 +37,7 @@ pub enum Error { /// Error during base58 decoding Base58(bitcoin::base58::Error), /// Key-related error - Pk(bitcoin::key::Error), + Pk(bitcoin::key::ParsePublicKeyError), /// Miniscript error Miniscript(miniscript::Error), /// Hex decoding error @@ -103,8 +103,8 @@ impl From for Error { } } -impl From for Error { - fn from(err: bitcoin::key::Error) -> Self { +impl From for Error { + fn from(err: bitcoin::key::ParsePublicKeyError) -> Self { Error::Pk(err) } } diff --git a/crates/wallet/src/descriptor/mod.rs b/crates/wallet/src/descriptor/mod.rs index 4b1135feb..0d3948104 100644 --- a/crates/wallet/src/descriptor/mod.rs +++ b/crates/wallet/src/descriptor/mod.rs @@ -229,7 +229,7 @@ impl IntoWalletDescriptor for DescriptorTemplateOut { let pk = match pk { DescriptorPublicKey::XPub(ref xpub) => { let mut xpub = xpub.clone(); - xpub.xkey.network = self.network; + xpub.xkey.network = self.network.into(); DescriptorPublicKey::XPub(xpub) } @@ -264,11 +264,11 @@ impl IntoWalletDescriptor for DescriptorTemplateOut { .map(|(mut k, mut v)| { match (&mut k, &mut v) { (DescriptorPublicKey::XPub(xpub), DescriptorSecretKey::XPrv(xprv)) => { - xpub.xkey.network = network; - xprv.xkey.network = network; + xpub.xkey.network = network.into(); + xprv.xkey.network = network.into(); } (_, DescriptorSecretKey::Single(key)) => { - key.key.network = network; + key.key.network = network.into(); } _ => {} } @@ -606,8 +606,8 @@ mod test { use assert_matches::assert_matches; use bitcoin::hex::FromHex; use bitcoin::secp256k1::Secp256k1; - use bitcoin::ScriptBuf; use bitcoin::{bip32, Psbt}; + use bitcoin::{NetworkKind, ScriptBuf}; use super::*; use crate::psbt::PsbtUtils; @@ -743,7 +743,7 @@ mod test { .unwrap(); let mut xprv_testnet = xprv; - xprv_testnet.network = Network::Testnet; + xprv_testnet.network = NetworkKind::Test; let xpub_testnet = bip32::Xpub::from_priv(&secp, &xprv_testnet); let desc_pubkey = DescriptorPublicKey::XPub(DescriptorXKey { diff --git a/crates/wallet/src/descriptor/policy.rs b/crates/wallet/src/descriptor/policy.rs index bf8a661ea..efb7ac7ca 100644 --- a/crates/wallet/src/descriptor/policy.rs +++ b/crates/wallet/src/descriptor/policy.rs @@ -40,6 +40,7 @@ use crate::collections::{BTreeMap, HashSet, VecDeque}; use alloc::string::String; use alloc::vec::Vec; use core::cmp::max; +use miniscript::miniscript::limits::{MAX_PUBKEYS_IN_CHECKSIGADD, MAX_PUBKEYS_PER_MULTISIG}; use core::fmt; @@ -48,12 +49,12 @@ use serde::{Serialize, Serializer}; use bitcoin::bip32::Fingerprint; use bitcoin::hashes::{hash160, ripemd160, sha256}; -use bitcoin::{absolute, key::XOnlyPublicKey, PublicKey, Sequence}; +use bitcoin::{absolute, key::XOnlyPublicKey, relative, PublicKey, Sequence}; use miniscript::descriptor::{ DescriptorPublicKey, ShInner, SinglePub, SinglePubKey, SortedMultiVec, WshInner, }; -use miniscript::hash256; +use miniscript::{hash256, Threshold}; use miniscript::{ Descriptor, Miniscript, Satisfier, ScriptContext, SigType, Terminal, ToPublicKey, }; @@ -137,7 +138,7 @@ pub enum SatisfiableItem { /// Relative timelock locktime RelativeTimelock { /// The timelock value - value: Sequence, + value: relative::LockTime, }, /// Multi-signature public keys with threshold count Multisig { @@ -586,30 +587,25 @@ impl Policy { Ok(Some(policy)) } - fn make_multisig( - keys: &[DescriptorPublicKey], + fn make_multi( + threshold: &Threshold, signers: &SignersContainer, build_sat: BuildSatisfaction, - threshold: usize, sorted: bool, secp: &SecpCtx, ) -> Result, PolicyError> { - if threshold == 0 { - return Ok(None); - } - - let parsed_keys = keys.iter().map(|k| PkOrF::from_key(k, secp)).collect(); + let parsed_keys = threshold.iter().map(|k| PkOrF::from_key(k, secp)).collect(); let mut contribution = Satisfaction::Partial { - n: keys.len(), - m: threshold, + n: threshold.n(), + m: threshold.k(), items: vec![], conditions: Default::default(), sorted: Some(sorted), }; let mut satisfaction = contribution.clone(); - for (index, key) in keys.iter().enumerate() { + for (index, key) in threshold.iter().enumerate() { if signers.find(signer_id(key, secp)).is_some() { contribution.add( &Satisfaction::Complete { @@ -635,7 +631,7 @@ impl Policy { let mut policy: Policy = SatisfiableItem::Multisig { keys: parsed_keys, - threshold, + threshold: threshold.k(), } .into(); policy.contribution = contribution; @@ -644,6 +640,57 @@ impl Policy { Ok(Some(policy)) } + fn make_multi_a( + threshold: &Threshold, + signers: &SignersContainer, + build_sat: BuildSatisfaction, + sorted: bool, + secp: &SecpCtx, + ) -> Result, PolicyError> { + let parsed_keys = threshold.iter().map(|k| PkOrF::from_key(k, secp)).collect(); + + let mut contribution = Satisfaction::Partial { + n: threshold.n(), + m: threshold.k(), + items: vec![], + conditions: Default::default(), + sorted: Some(sorted), + }; + let mut satisfaction = contribution.clone(); + + for (index, key) in threshold.iter().enumerate() { + if signers.find(signer_id(key, secp)).is_some() { + contribution.add( + &Satisfaction::Complete { + condition: Default::default(), + }, + index, + )?; + } + if let Some(psbt) = build_sat.psbt() { + if Ctx::find_signature(psbt, key, secp) { + satisfaction.add( + &Satisfaction::Complete { + condition: Default::default(), + }, + index, + )?; + } + } + } + satisfaction.finalize(); + contribution.finalize(); + + let mut policy: Policy = SatisfiableItem::Multisig { + keys: parsed_keys, + threshold: threshold.k(), + } + .into(); + policy.contribution = contribution; + policy.satisfaction = satisfaction; + Ok(Some(policy)) + } + /// Return whether or not a specific path in the policy tree is required to unambiguously /// create a transaction /// @@ -725,7 +772,7 @@ impl Policy { timelock: Some(*value), }), SatisfiableItem::RelativeTimelock { value } => Ok(Condition { - csv: Some(*value), + csv: Some((*value).into()), timelock: None, }), _ => Ok(Condition::default()), @@ -952,11 +999,14 @@ impl ExtractPolicy for Miniscript { - let mut policy: Policy = SatisfiableItem::RelativeTimelock { value: *value }.into(); + let mut policy: Policy = SatisfiableItem::RelativeTimelock { + value: (*value).into(), + } + .into(); policy.contribution = Satisfaction::Complete { condition: Condition { timelock: None, - csv: Some(*value), + csv: Some((*value).into()), }, }; if let BuildSatisfaction::PsbtTimelocks { @@ -966,9 +1016,11 @@ impl ExtractPolicy for Miniscript::check_older(&older, *value); - let inputs_sat = psbt_inputs_sat(psbt) - .all(|sat| Satisfier::::check_older(&sat, *value)); + let older_sat = + Satisfier::::check_older(&older, (*value).into()); + let inputs_sat = psbt_inputs_sat(psbt).all(|sat| { + Satisfier::::check_older(&sat, (*value).into()) + }); if older_sat && inputs_sat { policy.satisfaction = policy.contribution.clone(); } @@ -986,8 +1038,11 @@ impl ExtractPolicy for Miniscript { Some(SatisfiableItem::Hash160Preimage { hash: *hash }.into()) } - Terminal::Multi(k, pks) | Terminal::MultiA(k, pks) => { - Policy::make_multisig::(pks, signers, build_sat, *k, false, secp)? + Terminal::Multi(threshold) => { + Policy::make_multi::(threshold, signers, build_sat, false, secp)? + } + Terminal::MultiA(threshold) => { + Policy::make_multi_a::(threshold, signers, build_sat, false, secp)? } // Identities Terminal::Alt(inner) @@ -1016,8 +1071,9 @@ impl ExtractPolicy for Miniscript { - let mut threshold = *k; + Terminal::Thresh(threshold) => { + let mut k = threshold.k(); + let nodes = threshold.data(); let mapped: Vec<_> = nodes .iter() .map(|n| n.extract_policy(signers, build_sat, secp)) @@ -1027,13 +1083,13 @@ impl ExtractPolicy for Miniscript return Ok(None), Some(x) => x, }; } - Policy::make_thresh(mapped, threshold)? + Policy::make_thresh(mapped, k)? } // Unsupported @@ -1087,13 +1143,10 @@ impl ExtractPolicy for Descriptor { build_sat: BuildSatisfaction, secp: &SecpCtx, ) -> Result, Error> { - Ok(Policy::make_multisig::( - keys.pks.as_ref(), - signers, - build_sat, - keys.k, - true, - secp, + let threshold = Threshold::new(keys.k(), keys.pks().to_vec()) + .expect("valid threshold and pks collection"); + Ok(Policy::make_multi::( + &threshold, signers, build_sat, true, secp, )?) } diff --git a/crates/wallet/src/descriptor/template.rs b/crates/wallet/src/descriptor/template.rs index 61d6165b9..9eb375169 100644 --- a/crates/wallet/src/descriptor/template.rs +++ b/crates/wallet/src/descriptor/template.rs @@ -583,7 +583,7 @@ mod test { use bitcoin::bip32::ChildNumber::{self, Hardened}; let xprvkey = bitcoin::bip32::Xpriv::from_str("xprv9s21ZrQH143K2fpbqApQL69a4oKdGVnVN52R82Ft7d1pSqgKmajF62acJo3aMszZb6qQ22QsVECSFxvf9uyxFUvFYQMq3QbtwtRSMjLAhMf").unwrap(); - assert_eq!(Network::Bitcoin, xprvkey.network); + assert!(xprvkey.network.is_mainnet()); let xdesc = Bip44(xprvkey, KeychainKind::Internal) .build(Network::Bitcoin) .unwrap(); @@ -597,7 +597,7 @@ mod test { } let tprvkey = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap(); - assert_eq!(Network::Testnet, tprvkey.network); + assert!(!tprvkey.network.is_mainnet()); let tdesc = Bip44(tprvkey, KeychainKind::Internal) .build(Network::Testnet) .unwrap(); diff --git a/crates/wallet/src/keys/mod.rs b/crates/wallet/src/keys/mod.rs index 5f6b54cd1..108270348 100644 --- a/crates/wallet/src/keys/mod.rs +++ b/crates/wallet/src/keys/mod.rs @@ -336,7 +336,7 @@ impl ExtendedKey { pub fn into_xprv(self, network: Network) -> Option { match self { ExtendedKey::Private((mut xprv, _)) => { - xprv.network = network; + xprv.network = network.into(); Some(xprv) } ExtendedKey::Public(_) => None, @@ -355,7 +355,7 @@ impl ExtendedKey { ExtendedKey::Public((xpub, _)) => xpub, }; - xpub.network = network; + xpub.network = network.into(); xpub } } @@ -402,7 +402,7 @@ impl From for ExtendedKey { /// impl DerivableKey for MyCustomKeyType { /// fn into_extended_key(self) -> Result, KeyError> { /// let xprv = bip32::Xpriv { -/// network: self.network, +/// network: self.network.into(), /// depth: 0, /// parent_fingerprint: bip32::Fingerprint::default(), /// private_key: self.key_data.inner, @@ -434,7 +434,7 @@ impl From for ExtendedKey { /// impl DerivableKey for MyCustomKeyType { /// fn into_extended_key(self) -> Result, KeyError> { /// let xprv = bip32::Xpriv { -/// network: bitcoin::Network::Bitcoin, // pick an arbitrary network here +/// network: bitcoin::Network::Bitcoin.into(), // pick an arbitrary network here /// depth: 0, /// parent_fingerprint: bip32::Fingerprint::default(), /// private_key: self.key_data.inner, @@ -717,7 +717,7 @@ impl GeneratableKey for PrivateKey { let inner = secp256k1::SecretKey::from_slice(&entropy)?; let private_key = PrivateKey { compressed: options.compressed, - network: Network::Bitcoin, + network: Network::Bitcoin.into(), inner, }; @@ -847,9 +847,7 @@ impl IntoDescriptorKey for DescriptorPublicKey { fn into_descriptor_key(self) -> Result, KeyError> { let networks = match self { DescriptorPublicKey::Single(_) => any_network(), - DescriptorPublicKey::XPub(DescriptorXKey { xkey, .. }) - if xkey.network == Network::Bitcoin => - { + DescriptorPublicKey::XPub(DescriptorXKey { xkey, .. }) if xkey.network.is_mainnet() => { mainnet_network() } _ => test_networks(), @@ -882,12 +880,8 @@ impl IntoDescriptorKey for XOnlyPublicKey { impl IntoDescriptorKey for DescriptorSecretKey { fn into_descriptor_key(self) -> Result, KeyError> { let networks = match &self { - DescriptorSecretKey::Single(sk) if sk.key.network == Network::Bitcoin => { - mainnet_network() - } - DescriptorSecretKey::XPrv(DescriptorXKey { xkey, .. }) - if xkey.network == Network::Bitcoin => - { + DescriptorSecretKey::Single(sk) if sk.key.network.is_mainnet() => mainnet_network(), + DescriptorSecretKey::XPrv(DescriptorXKey { xkey, .. }) if xkey.network.is_mainnet() => { mainnet_network() } _ => test_networks(), @@ -1003,6 +997,6 @@ pub mod test { .unwrap(); let xprv = xkey.into_xprv(Network::Testnet).unwrap(); - assert_eq!(xprv.network, Network::Testnet); + assert_eq!(xprv.network, Network::Testnet.into()); } } diff --git a/crates/wallet/src/wallet/coin_selection.rs b/crates/wallet/src/wallet/coin_selection.rs index 5df64796a..7bffb91d9 100644 --- a/crates/wallet/src/wallet/coin_selection.rs +++ b/crates/wallet/src/wallet/coin_selection.rs @@ -316,7 +316,7 @@ pub fn decide_change(remaining_amount: u64, fee_rate: FeeRate, drain_script: &Sc let drain_val = remaining_amount.saturating_sub(change_fee); if drain_val.is_dust(drain_script) { - let dust_threshold = drain_script.dust_value().to_sat(); + let dust_threshold = drain_script.minimal_non_dust().to_sat(); Excess::NoChange { dust_threshold, change_fee, diff --git a/crates/wallet/src/wallet/export.rs b/crates/wallet/src/wallet/export.rs index 3697f91dd..41f463a46 100644 --- a/crates/wallet/src/wallet/export.rs +++ b/crates/wallet/src/wallet/export.rs @@ -164,7 +164,7 @@ impl FullyNodedExport { fn check_ms( terminal: &Terminal, ) -> Result<(), &'static str> { - if let Terminal::Multi(_, _) = terminal { + if let Terminal::Multi(_) = terminal { Ok(()) } else { Err("The descriptor contains operators not supported by Bitcoin Core") diff --git a/crates/wallet/src/wallet/mod.rs b/crates/wallet/src/wallet/mod.rs index 0d661727d..64d9cfa9c 100644 --- a/crates/wallet/src/wallet/mod.rs +++ b/crates/wallet/src/wallet/mod.rs @@ -1177,7 +1177,7 @@ impl Wallet { }; let mut changeset = ChangeSet::default(); - let txid = tx.txid(); + let txid = tx.compute_txid(); changeset.append(self.indexed_graph.insert_tx(tx).into()); if let Some(anchor) = anchor { changeset.append(self.indexed_graph.insert_anchor(txid, anchor).into()); @@ -1478,10 +1478,7 @@ impl Wallet { let recipients = params.recipients.iter().map(|(r, v)| (r, *v)); for (index, (script_pubkey, value)) in recipients.enumerate() { - if !params.allow_dust - && value.is_dust(script_pubkey) - && !script_pubkey.is_provably_unspendable() - { + if !params.allow_dust && value.is_dust(script_pubkey) && !script_pubkey.is_op_return() { return Err(CreateTxError::OutputBelowDustLimit(index)); } @@ -1635,7 +1632,7 @@ impl Wallet { /// let tx = psbt.clone().extract_tx().expect("tx"); /// // broadcast tx but it's taking too long to confirm so we want to bump the fee /// let mut psbt = { - /// let mut builder = wallet.build_fee_bump(tx.txid())?; + /// let mut builder = wallet.build_fee_bump(tx.compute_txid())?; /// builder /// .fee_rate(FeeRate::from_sat_per_vb(5).expect("valid feerate")); /// builder.finish()? @@ -1673,7 +1670,9 @@ impl Wallet { .iter() .any(|txin| txin.sequence.to_consensus_u32() <= 0xFFFFFFFD) { - return Err(BuildFeeBumpError::IrreplaceableTransaction(tx.txid())); + return Err(BuildFeeBumpError::IrreplaceableTransaction( + tx.compute_txid(), + )); } let fee = self @@ -1704,7 +1703,8 @@ impl Wallet { let satisfaction_weight = self .get_descriptor_for_keychain(keychain) .max_weight_to_satisfy() - .unwrap(); + .unwrap() + .to_wu() as usize; WeightedUtxo { utxo: Utxo::Local(LocalOutput { outpoint: txin.previous_output, @@ -2037,6 +2037,7 @@ impl Wallet { self.get_descriptor_for_keychain(keychain) .max_weight_to_satisfy() .unwrap() + .to_wu() as usize }) }) .collect() diff --git a/crates/wallet/src/wallet/signer.rs b/crates/wallet/src/wallet/signer.rs index 280e7066f..c8c523ab0 100644 --- a/crates/wallet/src/wallet/signer.rs +++ b/crates/wallet/src/wallet/signer.rs @@ -91,7 +91,7 @@ use bitcoin::bip32::{ChildNumber, DerivationPath, Fingerprint, Xpriv}; use bitcoin::hashes::hash160; use bitcoin::secp256k1::Message; use bitcoin::sighash::{EcdsaSighashType, TapSighash, TapSighashType}; -use bitcoin::{ecdsa, psbt, sighash, taproot}; +use bitcoin::{ecdsa, psbt, sighash, taproot, transaction}; use bitcoin::{key::TapTweak, key::XOnlyPublicKey, secp256k1}; use bitcoin::{PrivateKey, Psbt, PublicKey}; @@ -159,8 +159,12 @@ pub enum SignerError { NonStandardSighash, /// Invalid SIGHASH for the signing context in use InvalidSighash, - /// Error while computing the hash to sign - SighashError(sighash::Error), + /// Error while computing the hash to sign a P2WPKH input. + SighashP2wpkh(sighash::P2wpkhError), + /// Error while computing the hash to sign a Taproot input. + SighashTaproot(sighash::TaprootError), + /// Error while computing the hash, out of bounds access on the transaction inputs. + TxInputsIndexError(transaction::InputsIndexError), /// Miniscript PSBT error MiniscriptPsbt(MiniscriptPsbtError), /// To be used only by external libraries implementing [`InputSigner`] or @@ -169,9 +173,21 @@ pub enum SignerError { External(String), } -impl From for SignerError { - fn from(e: sighash::Error) -> Self { - SignerError::SighashError(e) +impl From for SignerError { + fn from(v: transaction::InputsIndexError) -> Self { + Self::TxInputsIndexError(v) + } +} + +impl From for SignerError { + fn from(e: sighash::P2wpkhError) -> Self { + Self::SighashP2wpkh(e) + } +} + +impl From for SignerError { + fn from(e: sighash::TaprootError) -> Self { + Self::SighashTaproot(e) } } @@ -189,7 +205,9 @@ impl fmt::Display for SignerError { Self::MissingHdKeypath => write!(f, "Missing fingerprint and derivation path"), Self::NonStandardSighash => write!(f, "The psbt contains a non standard sighash"), Self::InvalidSighash => write!(f, "Invalid SIGHASH for the signing context in use"), - Self::SighashError(err) => write!(f, "Error while computing the hash to sign: {}", err), + Self::SighashP2wpkh(err) => write!(f, "Error while computing the hash to sign a P2WPKH input: {}", err), + Self::SighashTaproot(err) => write!(f, "Error while computing the hash to sign a Taproot input: {}", err), + Self::TxInputsIndexError(err) => write!(f, "Error while computing the hash, out of bounds access on the transaction inputs: {}", err), Self::MiniscriptPsbt(err) => write!(f, "Miniscript PSBT error: {}", err), Self::External(err) => write!(f, "{}", err), } @@ -549,21 +567,24 @@ fn sign_psbt_ecdsa( secret_key: &secp256k1::SecretKey, pubkey: PublicKey, psbt_input: &mut psbt::Input, - hash: impl bitcoin::hashes::Hash + bitcoin::secp256k1::ThirtyTwoByteHash, - hash_ty: EcdsaSighashType, + hash: impl bitcoin::hashes::Hash, + sighash_type: EcdsaSighashType, secp: &SecpCtx, allow_grinding: bool, ) { - let msg = &Message::from(hash); - let sig = if allow_grinding { + let msg = &Message::from_digest(hash.to_byte_array()); + let signature = if allow_grinding { secp.sign_ecdsa_low_r(msg, secret_key) } else { secp.sign_ecdsa(msg, secret_key) }; - secp.verify_ecdsa(msg, &sig, &pubkey.inner) + secp.verify_ecdsa(msg, &signature, &pubkey.inner) .expect("invalid or corrupted ecdsa signature"); - let final_signature = ecdsa::Signature { sig, hash_ty }; + let final_signature = ecdsa::Signature { + signature, + sighash_type, + }; psbt_input.partial_sigs.insert(pubkey, final_signature); } @@ -574,7 +595,7 @@ fn sign_psbt_schnorr( leaf_hash: Option, psbt_input: &mut psbt::Input, hash: TapSighash, - hash_ty: TapSighashType, + sighash_type: TapSighashType, secp: &SecpCtx, ) { let keypair = secp256k1::Keypair::from_seckey_slice(secp, secret_key.as_ref()).unwrap(); @@ -586,11 +607,14 @@ fn sign_psbt_schnorr( }; let msg = &Message::from(hash); - let sig = secp.sign_schnorr(msg, &keypair); - secp.verify_schnorr(&sig, msg, &XOnlyPublicKey::from_keypair(&keypair).0) + let signature = secp.sign_schnorr(msg, &keypair); + secp.verify_schnorr(&signature, msg, &XOnlyPublicKey::from_keypair(&keypair).0) .expect("invalid or corrupted schnorr signature"); - let final_signature = taproot::Signature { sig, hash_ty }; + let final_signature = taproot::Signature { + signature, + sighash_type, + }; if let Some(lh) = leaf_hash { psbt_input @@ -933,7 +957,7 @@ impl ComputeSighash for Segwitv0 { // Always try first with the non-witness utxo let utxo = if let Some(prev_tx) = &psbt_input.non_witness_utxo { // Check the provided prev-tx - if prev_tx.txid() != tx_input.previous_output.txid { + if prev_tx.compute_txid() != tx_input.previous_output.txid { return Err(SignerError::InvalidNonWitnessUtxo); } diff --git a/crates/wallet/src/wallet/tx_builder.rs b/crates/wallet/src/wallet/tx_builder.rs index fbe5081f7..7b02248d9 100644 --- a/crates/wallet/src/wallet/tx_builder.rs +++ b/crates/wallet/src/wallet/tx_builder.rs @@ -295,7 +295,9 @@ impl<'a, Cs> TxBuilder<'a, Cs> { for utxo in utxos { let descriptor = wallet.get_descriptor_for_keychain(utxo.keychain); - let satisfaction_weight = descriptor.max_weight_to_satisfy().unwrap(); + + let satisfaction_weight = + descriptor.max_weight_to_satisfy().unwrap().to_wu() as usize; self.params.utxos.push(WeightedUtxo { satisfaction_weight, utxo: Utxo::Local(utxo), @@ -385,9 +387,9 @@ impl<'a, Cs> TxBuilder<'a, Cs> { if psbt_input.witness_utxo.is_none() { match psbt_input.non_witness_utxo.as_ref() { Some(tx) => { - if tx.txid() != outpoint.txid { + if tx.compute_txid() != outpoint.txid { return Err(AddForeignUtxoError::InvalidTxid { - input_txid: tx.txid(), + input_txid: tx.compute_txid(), foreign_utxo: outpoint, }); } diff --git a/crates/wallet/src/wallet/utils.rs b/crates/wallet/src/wallet/utils.rs index 208a88dfe..402361134 100644 --- a/crates/wallet/src/wallet/utils.rs +++ b/crates/wallet/src/wallet/utils.rs @@ -10,7 +10,7 @@ // licenses. use bitcoin::secp256k1::{All, Secp256k1}; -use bitcoin::{absolute, Script, Sequence}; +use bitcoin::{absolute, relative, Script, Sequence}; use miniscript::{MiniscriptKey, Satisfier, ToPublicKey}; @@ -26,7 +26,7 @@ pub trait IsDust { impl IsDust for u64 { fn is_dust(&self, script: &Script) -> bool { - *self < script.dust_value().to_sat() + *self < script.minimal_non_dust().to_sat() } } @@ -95,7 +95,7 @@ impl Older { } impl Satisfier for Older { - fn check_older(&self, n: Sequence) -> bool { + fn check_older(&self, n: relative::LockTime) -> bool { if let Some(current_height) = self.current_height { // TODO: test >= / > current_height diff --git a/crates/wallet/tests/common.rs b/crates/wallet/tests/common.rs index d77147f4d..0acdc5ea1 100644 --- a/crates/wallet/tests/common.rs +++ b/crates/wallet/tests/common.rs @@ -46,7 +46,7 @@ pub fn get_funded_wallet_with_change(descriptor: &str, change: &str) -> (Wallet, lock_time: bitcoin::absolute::LockTime::ZERO, input: vec![TxIn { previous_output: OutPoint { - txid: tx0.txid(), + txid: tx0.compute_txid(), vout: 0, }, script_sig: Default::default(), @@ -96,7 +96,7 @@ pub fn get_funded_wallet_with_change(descriptor: &str, change: &str) -> (Wallet, ) .unwrap(); - (wallet, tx1.txid()) + (wallet, tx1.compute_txid()) } /// Return a fake wallet that appears to be funded for testing. diff --git a/crates/wallet/tests/wallet.rs b/crates/wallet/tests/wallet.rs index dff699a6f..17dbabc85 100644 --- a/crates/wallet/tests/wallet.rs +++ b/crates/wallet/tests/wallet.rs @@ -45,7 +45,7 @@ fn receive_output(wallet: &mut Wallet, value: u64, height: ConfirmationTime) -> wallet.insert_tx(tx.clone(), height).unwrap(); OutPoint { - txid: tx.txid(), + txid: tx.compute_txid(), vout: 0, } } @@ -1151,7 +1151,7 @@ fn test_create_tx_add_utxo() { builder .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000)) .add_utxo(OutPoint { - txid: small_output_tx.txid(), + txid: small_output_tx.compute_txid(), vout: 0, }) .unwrap(); @@ -1202,7 +1202,7 @@ fn test_create_tx_manually_selected_insufficient() { builder .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000)) .add_utxo(OutPoint { - txid: small_output_tx.txid(), + txid: small_output_tx.compute_txid(), vout: 0, }) .unwrap() @@ -1306,8 +1306,6 @@ fn test_create_tx_policy_path_ignored_subtree_with_csv() { #[test] fn test_create_tx_global_xpubs_with_origin() { use bitcoin::bip32; - use bitcoin::hex::FromHex; - let (mut wallet, _) = get_funded_wallet("wpkh([73756c7f/48'/0'/0'/2']tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/0/*)"); let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); @@ -1348,7 +1346,11 @@ fn test_add_foreign_utxo() { builder .add_recipient(addr.script_pubkey(), Amount::from_sat(60_000)) .only_witness_utxo() - .add_foreign_utxo(utxo.outpoint, psbt_input, foreign_utxo_satisfaction) + .add_foreign_utxo( + utxo.outpoint, + psbt_input, + foreign_utxo_satisfaction.to_wu() as usize, + ) .unwrap(); let mut psbt = builder.finish().unwrap(); wallet1.insert_txout(utxo.outpoint, utxo.txout); @@ -1399,7 +1401,7 @@ fn test_add_foreign_utxo() { #[test] #[should_panic( - expected = "MissingTxOut([OutPoint { txid: 0x21d7fb1bceda00ab4069fc52d06baa13470803e9050edd16f5736e5d8c4925fd, vout: 0 }])" + expected = "MissingTxOut([OutPoint { txid: 21d7fb1bceda00ab4069fc52d06baa13470803e9050edd16f5736e5d8c4925fd, vout: 0 }])" )] fn test_calculate_fee_with_missing_foreign_utxo() { let (mut wallet1, _) = get_funded_wallet_wpkh(); @@ -1424,7 +1426,11 @@ fn test_calculate_fee_with_missing_foreign_utxo() { builder .add_recipient(addr.script_pubkey(), Amount::from_sat(60_000)) .only_witness_utxo() - .add_foreign_utxo(utxo.outpoint, psbt_input, foreign_utxo_satisfaction) + .add_foreign_utxo( + utxo.outpoint, + psbt_input, + foreign_utxo_satisfaction.to_wu() as usize, + ) .unwrap(); let psbt = builder.finish().unwrap(); let tx = psbt.extract_tx().expect("failed to extract tx"); @@ -1441,8 +1447,11 @@ fn test_add_foreign_utxo_invalid_psbt_input() { .unwrap(); let mut builder = wallet.build_tx(); - let result = - builder.add_foreign_utxo(outpoint, psbt::Input::default(), foreign_utxo_satisfaction); + let result = builder.add_foreign_utxo( + outpoint, + psbt::Input::default(), + foreign_utxo_satisfaction.to_wu() as usize, + ); assert!(matches!(result, Err(AddForeignUtxoError::MissingUtxo))); } @@ -1470,7 +1479,7 @@ fn test_add_foreign_utxo_where_outpoint_doesnt_match_psbt_input() { non_witness_utxo: Some(tx1.as_ref().clone()), ..Default::default() }, - satisfaction_weight + satisfaction_weight.to_wu() as usize ) .is_err(), "should fail when outpoint doesn't match psbt_input" @@ -1483,7 +1492,7 @@ fn test_add_foreign_utxo_where_outpoint_doesnt_match_psbt_input() { non_witness_utxo: Some(tx2.as_ref().clone()), ..Default::default() }, - satisfaction_weight + satisfaction_weight.to_wu() as usize ) .is_ok(), "should be ok when outpoint does match psbt_input" @@ -1515,7 +1524,11 @@ fn test_add_foreign_utxo_only_witness_utxo() { ..Default::default() }; builder - .add_foreign_utxo(utxo2.outpoint, psbt_input, satisfaction_weight) + .add_foreign_utxo( + utxo2.outpoint, + psbt_input, + satisfaction_weight.to_wu() as usize, + ) .unwrap(); assert!( builder.finish().is_err(), @@ -1531,7 +1544,11 @@ fn test_add_foreign_utxo_only_witness_utxo() { }; builder .only_witness_utxo() - .add_foreign_utxo(utxo2.outpoint, psbt_input, satisfaction_weight) + .add_foreign_utxo( + utxo2.outpoint, + psbt_input, + satisfaction_weight.to_wu() as usize, + ) .unwrap(); assert!( builder.finish().is_ok(), @@ -1547,7 +1564,11 @@ fn test_add_foreign_utxo_only_witness_utxo() { ..Default::default() }; builder - .add_foreign_utxo(utxo2.outpoint, psbt_input, satisfaction_weight) + .add_foreign_utxo( + utxo2.outpoint, + psbt_input, + satisfaction_weight.to_wu() as usize, + ) .unwrap(); assert!( builder.finish().is_ok(), @@ -1583,8 +1604,6 @@ fn test_create_tx_global_xpubs_origin_missing() { #[test] fn test_create_tx_global_xpubs_master_without_origin() { use bitcoin::bip32; - use bitcoin::hex::FromHex; - let (mut wallet, _) = get_funded_wallet("wpkh(tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL/0/*)"); let addr = wallet.next_unused_address(KeychainKind::External).unwrap(); let mut builder = wallet.build_tx(); @@ -1613,7 +1632,7 @@ fn test_bump_fee_irreplaceable_tx() { let psbt = builder.finish().unwrap(); let tx = psbt.extract_tx().expect("failed to extract tx"); - let txid = tx.txid(); + let txid = tx.compute_txid(); wallet .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) .unwrap(); @@ -1630,7 +1649,7 @@ fn test_bump_fee_confirmed_tx() { let psbt = builder.finish().unwrap(); let tx = psbt.extract_tx().expect("failed to extract tx"); - let txid = tx.txid(); + let txid = tx.compute_txid(); wallet .insert_tx( @@ -1657,7 +1676,7 @@ fn test_bump_fee_low_fee_rate() { let feerate = psbt.fee_rate().unwrap(); let tx = psbt.extract_tx().expect("failed to extract tx"); - let txid = tx.txid(); + let txid = tx.compute_txid(); wallet .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) @@ -1690,7 +1709,7 @@ fn test_bump_fee_low_abs() { let psbt = builder.finish().unwrap(); let tx = psbt.extract_tx().expect("failed to extract tx"); - let txid = tx.txid(); + let txid = tx.compute_txid(); wallet .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) @@ -1713,7 +1732,7 @@ fn test_bump_fee_zero_abs() { let psbt = builder.finish().unwrap(); let tx = psbt.extract_tx().expect("failed to extract tx"); - let txid = tx.txid(); + let txid = tx.compute_txid(); wallet .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) .unwrap(); @@ -1739,7 +1758,7 @@ fn test_bump_fee_reduce_change() { let original_fee = check_fee!(wallet, psbt); let tx = psbt.extract_tx().expect("failed to extract tx"); - let txid = tx.txid(); + let txid = tx.compute_txid(); wallet .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) .unwrap(); @@ -1837,7 +1856,7 @@ fn test_bump_fee_reduce_single_recipient() { let tx = psbt.clone().extract_tx().expect("failed to extract tx"); let original_sent_received = wallet.sent_and_received(&tx); let original_fee = check_fee!(wallet, psbt); - let txid = tx.txid(); + let txid = tx.compute_txid(); wallet .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) .unwrap(); @@ -1885,7 +1904,7 @@ fn test_bump_fee_absolute_reduce_single_recipient() { let original_fee = check_fee!(wallet, psbt); let tx = psbt.extract_tx().expect("failed to extract tx"); let original_sent_received = wallet.sent_and_received(&tx); - let txid = tx.txid(); + let txid = tx.compute_txid(); wallet .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) .unwrap(); @@ -1949,7 +1968,7 @@ fn test_bump_fee_drain_wallet() { builder .drain_to(addr.script_pubkey()) .add_utxo(OutPoint { - txid: tx.txid(), + txid: tx.compute_txid(), vout: 0, }) .unwrap() @@ -1959,7 +1978,7 @@ fn test_bump_fee_drain_wallet() { let tx = psbt.extract_tx().expect("failed to extract tx"); let original_sent_received = wallet.sent_and_received(&tx); - let txid = tx.txid(); + let txid = tx.compute_txid(); wallet .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) .unwrap(); @@ -2011,7 +2030,7 @@ fn test_bump_fee_remove_output_manually_selected_only() { ) .unwrap(); let outpoint = OutPoint { - txid: init_tx.txid(), + txid: init_tx.compute_txid(), vout: 0, }; let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX") @@ -2027,7 +2046,7 @@ fn test_bump_fee_remove_output_manually_selected_only() { let psbt = builder.finish().unwrap(); let tx = psbt.extract_tx().expect("failed to extract tx"); let original_sent_received = wallet.sent_and_received(&tx); - let txid = tx.txid(); + let txid = tx.compute_txid(); wallet .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) .unwrap(); @@ -2074,7 +2093,7 @@ fn test_bump_fee_add_input() { let psbt = builder.finish().unwrap(); let tx = psbt.extract_tx().expect("failed to extract tx"); let original_details = wallet.sent_and_received(&tx); - let txid = tx.txid(); + let txid = tx.compute_txid(); wallet .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) .unwrap(); @@ -2131,7 +2150,7 @@ fn test_bump_fee_absolute_add_input() { let psbt = builder.finish().unwrap(); let tx = psbt.extract_tx().expect("failed to extract tx"); let original_sent_received = wallet.sent_and_received(&tx); - let txid = tx.txid(); + let txid = tx.compute_txid(); wallet .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) .unwrap(); @@ -2197,7 +2216,7 @@ fn test_bump_fee_no_change_add_input_and_change() { let original_fee = check_fee!(wallet, psbt); let tx = psbt.extract_tx().expect("failed to extract tx"); - let txid = tx.txid(); + let txid = tx.compute_txid(); wallet .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) .unwrap(); @@ -2267,7 +2286,7 @@ fn test_bump_fee_add_input_change_dust() { let original_tx_weight = tx.weight(); assert_eq!(tx.input.len(), 1); assert_eq!(tx.output.len(), 2); - let txid = tx.txid(); + let txid = tx.compute_txid(); wallet .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) .unwrap(); @@ -2335,7 +2354,7 @@ fn test_bump_fee_force_add_input() { let psbt = builder.finish().unwrap(); let mut tx = psbt.extract_tx().expect("failed to extract tx"); let original_sent_received = wallet.sent_and_received(&tx); - let txid = tx.txid(); + let txid = tx.compute_txid(); for txin in &mut tx.input { txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature } @@ -2401,7 +2420,7 @@ fn test_bump_fee_absolute_force_add_input() { let psbt = builder.finish().unwrap(); let mut tx = psbt.extract_tx().expect("failed to extract tx"); let original_sent_received = wallet.sent_and_received(&tx); - let txid = tx.txid(); + let txid = tx.compute_txid(); // skip saving the new utxos, we know they can't be used anyways for txin in &mut tx.input { txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature @@ -2481,7 +2500,7 @@ fn test_bump_fee_unconfirmed_inputs_only() { ConfirmationTime::Unconfirmed { last_seen: 0 }, ); let mut tx = psbt.extract_tx().expect("failed to extract tx"); - let txid = tx.txid(); + let txid = tx.compute_txid(); for txin in &mut tx.input { txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature } @@ -2514,7 +2533,7 @@ fn test_bump_fee_unconfirmed_input() { .enable_rbf(); let psbt = builder.finish().unwrap(); let mut tx = psbt.extract_tx().expect("failed to extract tx"); - let txid = tx.txid(); + let txid = tx.compute_txid(); for txin in &mut tx.input { txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature } @@ -3386,7 +3405,11 @@ fn test_taproot_foreign_utxo() { let mut builder = wallet1.build_tx(); builder .add_recipient(addr.script_pubkey(), Amount::from_sat(60_000)) - .add_foreign_utxo(utxo.outpoint, psbt_input, foreign_utxo_satisfaction) + .add_foreign_utxo( + utxo.outpoint, + psbt_input, + foreign_utxo_satisfaction.to_wu() as usize, + ) .unwrap(); let psbt = builder.finish().unwrap(); let sent_received = @@ -3890,7 +3913,10 @@ fn test_fee_rate_sign_no_grinding_high_r() { .unwrap(); // We only have one key in the partial_sigs map, this is a trick to retrieve it let key = psbt.inputs[0].partial_sigs.keys().next().unwrap(); - sig_len = psbt.inputs[0].partial_sigs[key].sig.serialize_der().len(); + sig_len = psbt.inputs[0].partial_sigs[key] + .signature + .serialize_der() + .len(); } // Actually finalizing the transaction... wallet @@ -3936,7 +3962,10 @@ fn test_fee_rate_sign_grinding_low_r() { .unwrap(); let key = psbt.inputs[0].partial_sigs.keys().next().unwrap(); - let sig_len = psbt.inputs[0].partial_sigs[key].sig.serialize_der().len(); + let sig_len = psbt.inputs[0].partial_sigs[key] + .signature + .serialize_der() + .len(); assert_eq!(sig_len, 70); assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), fee_rate); } diff --git a/example-crates/example_cli/src/lib.rs b/example-crates/example_cli/src/lib.rs index 319c23805..256c4bdf6 100644 --- a/example-crates/example_cli/src/lib.rs +++ b/example-crates/example_cli/src/lib.rs @@ -643,7 +643,7 @@ where match (broadcast)(chain_specific, &transaction) { Ok(_) => { - println!("Broadcasted Tx : {}", transaction.txid()); + println!("Broadcasted Tx : {}", transaction.compute_txid()); let keychain_changeset = graph.lock().unwrap().insert_tx(transaction); diff --git a/example-crates/wallet_electrum/src/main.rs b/example-crates/wallet_electrum/src/main.rs index e6c01c20b..d8dbc458f 100644 --- a/example-crates/wallet_electrum/src/main.rs +++ b/example-crates/wallet_electrum/src/main.rs @@ -94,7 +94,7 @@ fn main() -> Result<(), anyhow::Error> { let tx = psbt.extract_tx()?; client.transaction_broadcast(&tx)?; - println!("Tx broadcasted! Txid: {}", tx.txid()); + println!("Tx broadcasted! Txid: {}", tx.compute_txid()); Ok(()) } diff --git a/example-crates/wallet_esplora_async/src/main.rs b/example-crates/wallet_esplora_async/src/main.rs index f46779b3c..6510c5660 100644 --- a/example-crates/wallet_esplora_async/src/main.rs +++ b/example-crates/wallet_esplora_async/src/main.rs @@ -103,7 +103,7 @@ async fn main() -> Result<(), anyhow::Error> { let tx = psbt.extract_tx()?; client.broadcast(&tx).await?; - println!("Tx broadcasted! Txid: {}", tx.txid()); + println!("Tx broadcasted! Txid: {}", tx.compute_txid()); Ok(()) } diff --git a/example-crates/wallet_esplora_blocking/src/main.rs b/example-crates/wallet_esplora_blocking/src/main.rs index 92caca2c9..5ade6804e 100644 --- a/example-crates/wallet_esplora_blocking/src/main.rs +++ b/example-crates/wallet_esplora_blocking/src/main.rs @@ -80,7 +80,7 @@ fn main() -> Result<(), anyhow::Error> { let tx = psbt.extract_tx()?; client.broadcast(&tx)?; - println!("Tx broadcasted! Txid: {}", tx.txid()); + println!("Tx broadcasted! Txid: {}", tx.compute_txid()); Ok(()) } diff --git a/nursery/tmp_plan/src/plan_impls.rs b/nursery/tmp_plan/src/plan_impls.rs index 6b4da3c90..8bbcb5ee9 100644 --- a/nursery/tmp_plan/src/plan_impls.rs +++ b/nursery/tmp_plan/src/plan_impls.rs @@ -240,7 +240,7 @@ fn plan_steps( if max_sequence.is_height_locked() == older.is_height_locked() { if max_sequence.to_consensus_u32() >= older.to_consensus_u32() { Some(TermPlan { - min_sequence: Some(*older), + min_sequence: Some((*older).into()), ..Default::default() }) } else { @@ -318,8 +318,8 @@ fn plan_steps( (lplan, rplan) => lplan.or(rplan), } } - Terminal::Thresh(_, _) => todo!(), - Terminal::Multi(_, _) => todo!(), - Terminal::MultiA(_, _) => todo!(), + Terminal::Thresh(_) => todo!(), + Terminal::Multi(_) => todo!(), + Terminal::MultiA(_) => todo!(), } } diff --git a/nursery/tmp_plan/src/requirements.rs b/nursery/tmp_plan/src/requirements.rs index 520290c0e..0d0097dbf 100644 --- a/nursery/tmp_plan/src/requirements.rs +++ b/nursery/tmp_plan/src/requirements.rs @@ -87,20 +87,28 @@ pub enum RequiredSignatures { #[derive(Clone, Debug)] pub enum SigningError { - SigHashError(sighash::Error), + SigHashP2wpkh(sighash::P2wpkhError), + SigHashTaproot(sighash::TaprootError), DerivationError(bip32::Error), } -impl From for SigningError { - fn from(e: sighash::Error) -> Self { - Self::SigHashError(e) +impl From for SigningError { + fn from(v: sighash::TaprootError) -> Self { + Self::SigHashTaproot(v) + } +} + +impl From for SigningError { + fn from(v: sighash::P2wpkhError) -> Self { + Self::SigHashP2wpkh(v) } } impl core::fmt::Display for SigningError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - SigningError::SigHashError(e) => e.fmt(f), + SigningError::SigHashP2wpkh(e) => e.fmt(f), + SigningError::SigHashTaproot(e) => e.fmt(f), SigningError::DerivationError(e) => e.fmt(f), } } @@ -170,8 +178,8 @@ impl RequiredSignatures { let sig = secp.sign_schnorr_no_aux_rand(&msg, &keypair); let bitcoin_sig = taproot::Signature { - sig, - hash_ty: schnorr_sighashty, + signature: sig, + sighash_type: schnorr_sighashty, }; auth_data @@ -210,10 +218,10 @@ impl RequiredSignatures { }; let keypair = Keypair::from_secret_key(&secp, &secret_key.clone()); let msg = Message::from_digest(sighash.to_byte_array()); - let sig = secp.sign_schnorr_no_aux_rand(&msg, &keypair); + let signature = secp.sign_schnorr_no_aux_rand(&msg, &keypair); let bitcoin_sig = taproot::Signature { - sig, - hash_ty: sighash_type, + signature, + sighash_type, }; auth_data From 11200810d048abb8deb3c7961ca93c68011d41fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BF=97=E5=AE=87?= Date: Thu, 13 Jun 2024 11:47:01 +0800 Subject: [PATCH 2/2] chore(wallet): rm dup code Methods `make_multi` and `make_multi_a` were essentially the same thing except for a different const generic param on `Threshold`. So we rm `make_multi_a` and put a const generic param on `make_multi`. --- crates/wallet/src/descriptor/policy.rs | 71 ++++---------------------- 1 file changed, 9 insertions(+), 62 deletions(-) diff --git a/crates/wallet/src/descriptor/policy.rs b/crates/wallet/src/descriptor/policy.rs index efb7ac7ca..076b9c3c1 100644 --- a/crates/wallet/src/descriptor/policy.rs +++ b/crates/wallet/src/descriptor/policy.rs @@ -587,61 +587,8 @@ impl Policy { Ok(Some(policy)) } - fn make_multi( - threshold: &Threshold, - signers: &SignersContainer, - build_sat: BuildSatisfaction, - sorted: bool, - secp: &SecpCtx, - ) -> Result, PolicyError> { - let parsed_keys = threshold.iter().map(|k| PkOrF::from_key(k, secp)).collect(); - - let mut contribution = Satisfaction::Partial { - n: threshold.n(), - m: threshold.k(), - items: vec![], - conditions: Default::default(), - sorted: Some(sorted), - }; - let mut satisfaction = contribution.clone(); - - for (index, key) in threshold.iter().enumerate() { - if signers.find(signer_id(key, secp)).is_some() { - contribution.add( - &Satisfaction::Complete { - condition: Default::default(), - }, - index, - )?; - } - - if let Some(psbt) = build_sat.psbt() { - if Ctx::find_signature(psbt, key, secp) { - satisfaction.add( - &Satisfaction::Complete { - condition: Default::default(), - }, - index, - )?; - } - } - } - satisfaction.finalize(); - contribution.finalize(); - - let mut policy: Policy = SatisfiableItem::Multisig { - keys: parsed_keys, - threshold: threshold.k(), - } - .into(); - policy.contribution = contribution; - policy.satisfaction = satisfaction; - - Ok(Some(policy)) - } - - fn make_multi_a( - threshold: &Threshold, + fn make_multi( + threshold: &Threshold, signers: &SignersContainer, build_sat: BuildSatisfaction, sorted: bool, @@ -1038,12 +985,12 @@ impl ExtractPolicy for Miniscript { Some(SatisfiableItem::Hash160Preimage { hash: *hash }.into()) } - Terminal::Multi(threshold) => { - Policy::make_multi::(threshold, signers, build_sat, false, secp)? - } - Terminal::MultiA(threshold) => { - Policy::make_multi_a::(threshold, signers, build_sat, false, secp)? - } + Terminal::Multi(threshold) => Policy::make_multi::( + threshold, signers, build_sat, false, secp, + )?, + Terminal::MultiA(threshold) => Policy::make_multi::( + threshold, signers, build_sat, false, secp, + )?, // Identities Terminal::Alt(inner) | Terminal::Swap(inner) @@ -1145,7 +1092,7 @@ impl ExtractPolicy for Descriptor { ) -> Result, Error> { let threshold = Threshold::new(keys.k(), keys.pks().to_vec()) .expect("valid threshold and pks collection"); - Ok(Policy::make_multi::( + Ok(Policy::make_multi::( &threshold, signers, build_sat, true, secp, )?) }