From 20ee8b1e7b77cbf65942bc4f083afea95f2d1506 Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Wed, 14 Aug 2024 09:32:42 +0200 Subject: [PATCH] Upgrade rust-miniscript / rust-bitcoin to latest version --- Cargo.lock | 91 ++++++++++++++++++++----- Cargo.toml | 2 +- src/commands/mod.rs | 60 ++++++++-------- src/database/sqlite/mod.rs | 125 +++++++++++++++++++--------------- src/database/sqlite/schema.rs | 2 +- src/database/sqlite/utils.rs | 2 +- src/descriptors/analysis.rs | 122 ++++++++++++++++++++------------- src/descriptors/mod.rs | 18 +++-- src/signer.rs | 34 ++++----- src/spend.rs | 2 +- src/testutils.rs | 8 ++- 11 files changed, 287 insertions(+), 179 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 335a18d52..73b4b2e63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,6 +35,12 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "544a7f66f3407c6ed1285525418393891e0f31c2078a2d46aefb44ecef09b1b3" +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "backtrace" version = "0.3.68" @@ -50,6 +56,16 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base58ck" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" +dependencies = [ + "bitcoin-internals 0.3.0", + "bitcoin_hashes 0.14.0", +] + [[package]] name = "base64" version = "0.13.1" @@ -70,9 +86,9 @@ checksum = "3c084bf76f0f67546fc814ffa82044144be1bb4618183a15016c162f8b087ad4" [[package]] name = "bech32" -version = "0.10.0-beta" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98f7eed2b2781a6f0b5c903471d48e15f56fb4e1165df8a9a2337fd1a59d45ea" +checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" [[package]] name = "bip39" @@ -87,15 +103,18 @@ dependencies = [ [[package]] name = "bitcoin" -version = "0.31.0" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5973a027b341b462105675962214dfe3c938ad9afd395d84b28602608bdcec7b" +checksum = "ea507acc1cd80fc084ace38544bbcf7ced7c2aa65b653b102de0ce718df668f6" dependencies = [ + "base58ck", "base64 0.21.5", "bech32", - "bitcoin-internals", - "bitcoin_hashes 0.13.0", - "hex-conservative", + "bitcoin-internals 0.3.0", + "bitcoin-io", + "bitcoin-units", + "bitcoin_hashes 0.14.0", + "hex-conservative 0.2.1", "hex_lit", "secp256k1", "serde", @@ -106,7 +125,29 @@ name = "bitcoin-internals" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" + +[[package]] +name = "bitcoin-internals" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" +dependencies = [ + "serde", +] + +[[package]] +name = "bitcoin-io" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "340e09e8399c7bd8912f495af6aa58bea0c9214773417ffaa8f6460f93aaee56" + +[[package]] +name = "bitcoin-units" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" dependencies = [ + "bitcoin-internals 0.3.0", "serde", ] @@ -122,8 +163,18 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" dependencies = [ - "bitcoin-internals", - "hex-conservative", + "bitcoin-internals 0.2.0", + "hex-conservative 0.1.1", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +dependencies = [ + "bitcoin-io", + "hex-conservative 0.2.1", "serde", ] @@ -238,6 +289,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2" +[[package]] +name = "hex-conservative" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +dependencies = [ + "arrayvec", +] + [[package]] name = "hex_lit" version = "0.1.1" @@ -325,13 +385,12 @@ checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "miniscript" -version = "11.0.0" +version = "12.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86a23dd3ad145a980e231185d114399f25a0a307d2cd918010ddda6334323df9" +checksum = "add2d4aee30e4291ce5cffa3a322e441ff4d4bc57b38c8d9bf0e94faa50ab626" dependencies = [ "bech32", "bitcoin", - "bitcoin-internals", "serde", ] @@ -466,9 +525,9 @@ checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "secp256k1" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acea373acb8c21ecb5a23741452acd2593ed44ee3d343e72baaa143bc89d0d5" +checksum = "0e0cc0f1cf93f4969faf3ea1c7d8a9faed25918d96affa959720823dfe86d4f3" dependencies = [ "bitcoin_hashes 0.13.0", "secp256k1-sys", @@ -477,9 +536,9 @@ dependencies = [ [[package]] name = "secp256k1-sys" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dd97a086ec737e30053fd5c46f097465d25bb81dd3608825f65298c4c98be83" +checksum = "1433bd67156263443f14d603720b082dd3121779323fce20cba2aa07b874bc1b" dependencies = [ "cc", ] diff --git a/Cargo.toml b/Cargo.toml index b3ce9f770..5456c5ce0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ nonblocking_shutdown = [] [dependencies] # For managing transactions (it re-exports the bitcoin crate) -miniscript = { version = "11.0", features = ["serde", "compiler", "base64"] } +miniscript = { version = "12.0", features = ["serde", "compiler", "base64"] } # Coin selection algorithms for spend transaction creation. bdk_coin_select = "0.3" diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 6e2b20636..d868840ae 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -45,7 +45,7 @@ pub enum CommandError { UnknownOutpoint(bitcoin::OutPoint), AlreadySpent(bitcoin::OutPoint), ImmatureCoinbase(bitcoin::OutPoint), - Address(bitcoin::address::Error), + Address(bitcoin::address::ParseError), SpendCreation(SpendCreationError), InsufficientFunds( /* in value */ bitcoin::Amount, @@ -81,7 +81,7 @@ impl fmt::Display for CommandError { op ), Self::UnknownOutpoint(op) => write!(f, "Unknown outpoint '{}'.", op), - Self::Address(e) => write!(f, "Address error: {}", e), + Self::Address(e) => write!(f, "Address parsing: {}", e), Self::SpendCreation(e) => write!(f, "Creating spend: {}", e), Self::InsufficientFunds(in_val, out_val, feerate) => { if let Some(out_val) = out_val { @@ -598,7 +598,7 @@ impl DaemonControl { // If the transaction already exists in DB, merge the signatures for each input on a best // effort basis. // We work on the newly provided PSBT, in case its content was updated. - let txid = tx.txid(); + let txid = tx.compute_txid(); if let Some(db_psbt) = db_conn.spend_tx(&txid) { let db_tx = db_psbt.unsigned_tx; for i in 0..db_tx.input.len() { @@ -679,7 +679,7 @@ impl DaemonControl { .into_iter() .filter_map(|(psbt, updated_at)| { if let Some(set) = &txids_set { - if !set.contains(&psbt.unsigned_tx.txid()) { + if !set.contains(&psbt.unsigned_tx.compute_txid()) { return None; } } @@ -1414,7 +1414,7 @@ mod tests { input: vec![], output: vec![], }; - let dummy_op = bitcoin::OutPoint::new(dummy_tx.txid(), 0); + let dummy_op = bitcoin::OutPoint::new(dummy_tx.compute_txid(), 0); let ms = DummyLiana::new(DummyBitcoind::new(), DummyDatabase::new()); let control = &ms.control(); let mut db_conn = control.db().lock().unwrap().connection(); @@ -1482,7 +1482,7 @@ mod tests { assert!(warnings.is_empty()); assert_eq!( tx.output[0].script_pubkey, - dummy_addr.payload().script_pubkey() + dummy_addr.assume_checked_ref().script_pubkey() ); assert_eq!(tx.output[0].value.to_sat(), dummy_value); @@ -1532,13 +1532,13 @@ mod tests { // If we ask to create an output for an address from another network, it will fail. let invalid_addr = - bitcoin::Address::new(bitcoin::Network::Testnet, dummy_addr.payload().clone()); + bitcoin::Address::from_str("tb1qfufcrdyarcg5eph608c6l8vktrc9re6agu4se2").unwrap(); let invalid_destinations: HashMap, u64> = [(invalid_addr, dummy_value)].iter().cloned().collect(); assert!(matches!( control.create_spend(&invalid_destinations, &[dummy_op], 1, None), Err(CommandError::Address( - address::Error::NetworkValidation { .. } + address::ParseError::NetworkValidation { .. } )) )); @@ -1559,7 +1559,7 @@ mod tests { assert_eq!(tx.output.len(), 1); assert_eq!( tx.output[0].script_pubkey, - dummy_addr.payload().script_pubkey() + dummy_addr.assume_checked_ref().script_pubkey() ); assert_eq!(tx.output[0].value.to_sat(), 95_000); // change = 100_000 - 95_000 - /* fee without change */ 127 - /* extra fee for change output */ 43 = 4830 @@ -1775,7 +1775,7 @@ mod tests { assert_eq!(tx_auto.output.len(), 2); assert_eq!( tx_auto.output[0].script_pubkey, - dummy_addr.payload().script_pubkey() + dummy_addr.assume_checked_ref().script_pubkey() ); assert_eq!(tx_auto.output[0].value, Amount::from_sat(80_000)); @@ -2015,7 +2015,7 @@ mod tests { } else { panic!("expect successful spend creation") }; - let txid_a = psbt_a.unsigned_tx.txid(); + let txid_a = psbt_a.unsigned_tx.compute_txid(); let psbt_b = if let CreateSpendResult::Success { psbt, .. } = control .create_spend(&destinations_b, &[dummy_op_b], 10, None) .unwrap() @@ -2024,7 +2024,7 @@ mod tests { } else { panic!("expect successful spend creation") }; - let txid_b = psbt_b.unsigned_tx.txid(); + let txid_b = psbt_b.unsigned_tx.compute_txid(); let psbt_c = if let CreateSpendResult::Success { psbt, .. } = control .create_spend(&destinations_c, &[dummy_op_a, dummy_op_b], 100, None) .unwrap() @@ -2033,7 +2033,7 @@ mod tests { } else { panic!("expect successful spend creation") }; - let txid_c = psbt_c.unsigned_tx.txid(); + let txid_c = psbt_c.unsigned_tx.compute_txid(); // We can store and query them all control.update_spend(psbt_a.clone()).unwrap(); @@ -2101,7 +2101,7 @@ mod tests { inputs: vec![], outputs: vec![], }; - let dummy_txid_a = dummy_psbt_a.unsigned_tx.txid(); + let dummy_txid_a = dummy_psbt_a.unsigned_tx.compute_txid(); dummy_bitcoind.txs.insert(dummy_txid_a, (dummy_tx_a, None)); let ms = DummyLiana::new(dummy_bitcoind, DummyDatabase::new()); let control = &ms.control(); @@ -2207,7 +2207,7 @@ mod tests { input: vec![TxIn { witness: Witness::new(), previous_output: OutPoint { - txid: deposit1.txid(), + txid: deposit1.compute_txid(), vout: 0, }, script_sig: ScriptBuf::new(), @@ -2232,21 +2232,21 @@ mod tests { is_change: false, is_immature: false, outpoint: OutPoint { - txid: deposit1.txid(), + txid: deposit1.compute_txid(), vout: 0, }, block_info: Some(BlockInfo { height: 1, time: 1 }), spend_block: Some(BlockInfo { height: 3, time: 3 }), derivation_index: ChildNumber::from(0), amount: bitcoin::Amount::from_sat(100_000_000), - spend_txid: Some(spend_tx.txid()), + spend_txid: Some(spend_tx.compute_txid()), }, // Deposit 2 Coin { is_change: false, is_immature: false, outpoint: OutPoint { - txid: deposit2.txid(), + txid: deposit2.compute_txid(), vout: 0, }, block_info: Some(BlockInfo { height: 2, time: 2 }), @@ -2259,7 +2259,7 @@ mod tests { Coin { is_change: true, is_immature: false, - outpoint: OutPoint::new(spend_tx.txid(), 1), + outpoint: OutPoint::new(spend_tx.compute_txid(), 1), block_info: Some(BlockInfo { height: 3, time: 3 }), spend_block: None, derivation_index: ChildNumber::from(2), @@ -2271,7 +2271,7 @@ mod tests { is_change: false, is_immature: false, outpoint: OutPoint { - txid: deposit3.txid(), + txid: deposit3.compute_txid(), vout: 0, }, block_info: Some(BlockInfo { height: 4, time: 4 }), @@ -2284,7 +2284,7 @@ mod tests { let mut txs_map = HashMap::new(); txs_map.insert( - deposit1.txid(), + deposit1.compute_txid(), ( deposit1.clone(), Some(Block { @@ -2298,7 +2298,7 @@ mod tests { ), ); txs_map.insert( - deposit2.txid(), + deposit2.compute_txid(), ( deposit2.clone(), Some(Block { @@ -2312,7 +2312,7 @@ mod tests { ), ); txs_map.insert( - spend_tx.txid(), + spend_tx.compute_txid(), ( spend_tx.clone(), Some(Block { @@ -2326,7 +2326,7 @@ mod tests { ), ); txs_map.insert( - deposit3.txid(), + deposit3.compute_txid(), ( deposit3.clone(), Some(Block { @@ -2435,7 +2435,7 @@ mod tests { let mut txs_map = HashMap::new(); txs_map.insert( - tx1.txid(), + tx1.compute_txid(), ( tx1.clone(), Some(Block { @@ -2449,7 +2449,7 @@ mod tests { ), ); txs_map.insert( - tx2.txid(), + tx2.compute_txid(), ( tx2.clone(), Some(Block { @@ -2463,7 +2463,7 @@ mod tests { ), ); txs_map.insert( - tx3.txid(), + tx3.compute_txid(), ( tx3.clone(), Some(Block { @@ -2503,12 +2503,14 @@ mod tests { } } - let transactions = control.list_transactions(&[tx1.txid()]).transactions; + let transactions = control + .list_transactions(&[tx1.compute_txid()]) + .transactions; assert_eq!(transactions.len(), 1); assert_eq!(transactions[0].tx, tx1); let transactions = control - .list_transactions(&[tx1.txid(), tx2.txid(), tx3.txid()]) + .list_transactions(&[tx1.compute_txid(), tx2.compute_txid(), tx3.compute_txid()]) .transactions; assert_eq!(transactions.len(), 3); diff --git a/src/database/sqlite/mod.rs b/src/database/sqlite/mod.rs index fb3b2e1b1..0a6c5d85c 100644 --- a/src/database/sqlite/mod.rs +++ b/src/database/sqlite/mod.rs @@ -597,7 +597,7 @@ impl SqliteConn { /// Insert a new Spend transaction or replace an existing one. pub fn store_spend(&mut self, psbt: &Psbt) { - let txid = &psbt.unsigned_tx.txid()[..].to_vec(); + let txid = &psbt.unsigned_tx.compute_txid()[..].to_vec(); db_exec(&mut self.conn, |db_tx| { db_tx.execute( @@ -716,7 +716,7 @@ impl SqliteConn { pub fn new_txs(&mut self, txs: &[bitcoin::Transaction]) { db_exec(&mut self.conn, |db_tx| { for tx in txs { - let txid = &tx.txid()[..].to_vec(); + let txid = &tx.compute_txid()[..].to_vec(); let tx_ser = bitcoin::consensus::serialize(tx); db_tx.execute( "INSERT INTO transactions (txid, tx) VALUES (?1, ?2) \ @@ -765,7 +765,7 @@ impl SqliteConn { w_txs.len(), w_txs .iter() - .map(|t| t.transaction.txid()) + .map(|t| t.transaction.compute_txid()) .collect::>() .len(), "database must not contain inconsistent block info for the same txid" @@ -1233,7 +1233,7 @@ CREATE TABLE labels ( conn.new_txs(&txs); // Add one unconfirmed coin. - let outpoint_a = bitcoin::OutPoint::new(txs.first().unwrap().txid(), 1); + let outpoint_a = bitcoin::OutPoint::new(txs.first().unwrap().compute_txid(), 1); let coin_a = Coin { outpoint: outpoint_a, is_immature: false, @@ -1279,7 +1279,7 @@ CREATE TABLE labels ( .is_empty()); // Add a second coin. - let outpoint_b = bitcoin::OutPoint::new(txs.get(1).unwrap().txid(), 12); + let outpoint_b = bitcoin::OutPoint::new(txs.get(1).unwrap().compute_txid(), 12); let coin_b = Coin { outpoint: outpoint_b, is_immature: false, @@ -1357,7 +1357,7 @@ CREATE TABLE labels ( && c[1].outpoint == coin_b.outpoint)); // Now if we spend one, it'll be marked as such. - conn.spend_coins(&[(coin_a.outpoint, txs.get(2).unwrap().txid())]); + conn.spend_coins(&[(coin_a.outpoint, txs.get(2).unwrap().compute_txid())]); assert!([ conn.coins(&[CoinStatus::Spending], &[]), conn.coins(&[CoinStatus::Spending], &[outpoint_a]), @@ -1380,7 +1380,7 @@ CREATE TABLE labels ( // Now we confirm the spend. conn.confirm_spend(&[( coin_a.outpoint, - txs.get(2).unwrap().txid(), + txs.get(2).unwrap().compute_txid(), 128_097, 3_000_000, )]); @@ -1410,7 +1410,7 @@ CREATE TABLE labels ( && c[1].outpoint == coin_b.outpoint)); // Add a third and fourth coin. - let outpoint_c = bitcoin::OutPoint::new(txs.get(3).unwrap().txid(), 42); + let outpoint_c = bitcoin::OutPoint::new(txs.get(3).unwrap().compute_txid(), 42); let coin_c = Coin { outpoint: outpoint_c, is_immature: false, @@ -1421,7 +1421,7 @@ CREATE TABLE labels ( spend_txid: None, spend_block: None, }; - let outpoint_d = bitcoin::OutPoint::new(txs.get(4).unwrap().txid(), 43); + let outpoint_d = bitcoin::OutPoint::new(txs.get(4).unwrap().compute_txid(), 43); let coin_d = Coin { outpoint: outpoint_d, is_immature: false, @@ -1462,7 +1462,7 @@ CREATE TABLE labels ( && coin[1].outpoint == coin_c.outpoint)); // Now spend second coin, even though it is still unconfirmed. - conn.spend_coins(&[(coin_b.outpoint, txs.get(5).unwrap().txid())]); + conn.spend_coins(&[(coin_b.outpoint, txs.get(5).unwrap().compute_txid())]); // The coin shows as spending. assert!([ conn.coins(&[CoinStatus::Spending], &[]), @@ -1535,7 +1535,7 @@ CREATE TABLE labels ( // Add one, we'll get it. let coin_a = Coin { - outpoint: bitcoin::OutPoint::new(txs.first().unwrap().txid(), 1), + outpoint: bitcoin::OutPoint::new(txs.first().unwrap().compute_txid(), 1), is_immature: false, block_info: None, amount: bitcoin::Amount::from_sat(98765), @@ -1577,7 +1577,7 @@ CREATE TABLE labels ( // Add a second one (this one is change), we'll get both. let coin_b = Coin { - outpoint: bitcoin::OutPoint::new(txs.get(1).unwrap().txid(), 12), + outpoint: bitcoin::OutPoint::new(txs.get(1).unwrap().compute_txid(), 12), is_immature: false, block_info: None, amount: bitcoin::Amount::from_sat(1111), @@ -1629,7 +1629,7 @@ CREATE TABLE labels ( assert!(coins[1].block_info.is_none()); // Now if we spend one, it'll be marked as such. - conn.spend_coins(&[(coin_a.outpoint, txs.get(2).unwrap().txid())]); + conn.spend_coins(&[(coin_a.outpoint, txs.get(2).unwrap().compute_txid())]); let coin = conn .coins(&[], &[coin_a.outpoint]) .into_iter() @@ -1647,7 +1647,7 @@ CREATE TABLE labels ( assert!(coin.spend_txid.is_none()); // Spend it back. We will see it as 'spending' - conn.spend_coins(&[(coin_a.outpoint, txs.get(2).unwrap().txid())]); + conn.spend_coins(&[(coin_a.outpoint, txs.get(2).unwrap().compute_txid())]); let outpoints: HashSet = conn .list_spending_coins() .into_iter() @@ -1668,7 +1668,12 @@ CREATE TABLE labels ( // Now if we confirm the spend. let height = 128_097; let time = 3_000_000; - conn.confirm_spend(&[(coin_a.outpoint, txs.get(2).unwrap().txid(), height, time)]); + conn.confirm_spend(&[( + coin_a.outpoint, + txs.get(2).unwrap().compute_txid(), + height, + time, + )]); // the coin is not in a spending state. let outpoints: HashSet = conn .list_spending_coins() @@ -1700,7 +1705,7 @@ CREATE TABLE labels ( // Add an immature coin. As all coins it's first registered as unconfirmed (even though // it's not). let coin_imma = Coin { - outpoint: bitcoin::OutPoint::new(txs.get(3).unwrap().txid(), 42), + outpoint: bitcoin::OutPoint::new(txs.get(3).unwrap().compute_txid(), 42), is_immature: true, block_info: None, amount: bitcoin::Amount::from_sat(424242), @@ -1871,7 +1876,7 @@ CREATE TABLE labels ( // TODO: immature deposits let coins = [ Coin { - outpoint: bitcoin::OutPoint::new(txs.first().unwrap().txid(), 1), + outpoint: bitcoin::OutPoint::new(txs.first().unwrap().compute_txid(), 1), is_immature: false, block_info: None, amount: bitcoin::Amount::from_sat(98765), @@ -1881,7 +1886,7 @@ CREATE TABLE labels ( spend_block: None, }, Coin { - outpoint: bitcoin::OutPoint::new(txs.get(1).unwrap().txid(), 2), + outpoint: bitcoin::OutPoint::new(txs.get(1).unwrap().compute_txid(), 2), is_immature: false, block_info: Some(BlockInfo { height: 101_095, @@ -1894,7 +1899,7 @@ CREATE TABLE labels ( spend_block: None, }, Coin { - outpoint: bitcoin::OutPoint::new(txs.get(2).unwrap().txid(), 3), + outpoint: bitcoin::OutPoint::new(txs.get(2).unwrap().compute_txid(), 3), is_immature: false, block_info: Some(BlockInfo { height: 101_099, @@ -1903,14 +1908,14 @@ CREATE TABLE labels ( amount: bitcoin::Amount::from_sat(98765), derivation_index: bip32::ChildNumber::from_normal_idx(1000).unwrap(), is_change: false, - spend_txid: Some(txs.get(3).unwrap().txid()), + spend_txid: Some(txs.get(3).unwrap().compute_txid()), spend_block: Some(BlockInfo { height: 101_199, time: 1_231_678, }), }, Coin { - outpoint: bitcoin::OutPoint::new(txs.get(4).unwrap().txid(), 4), + outpoint: bitcoin::OutPoint::new(txs.get(4).unwrap().compute_txid(), 4), is_immature: false, block_info: Some(BlockInfo { height: 101_100, @@ -1923,7 +1928,7 @@ CREATE TABLE labels ( spend_block: None, }, Coin { - outpoint: bitcoin::OutPoint::new(txs.get(5).unwrap().txid(), 5), + outpoint: bitcoin::OutPoint::new(txs.get(5).unwrap().compute_txid(), 5), is_immature: false, block_info: Some(BlockInfo { height: 101_102, @@ -1932,7 +1937,7 @@ CREATE TABLE labels ( amount: bitcoin::Amount::from_sat(98765), derivation_index: bip32::ChildNumber::from_normal_idx(100000).unwrap(), is_change: false, - spend_txid: Some(txs.get(6).unwrap().txid()), + spend_txid: Some(txs.get(6).unwrap().compute_txid()), spend_block: Some(BlockInfo { height: 101_105, time: 1_201_678, @@ -2074,7 +2079,7 @@ CREATE TABLE labels ( let coins = [ Coin { - outpoint: bitcoin::OutPoint::new(txs.first().unwrap().txid(), 1), + outpoint: bitcoin::OutPoint::new(txs.first().unwrap().compute_txid(), 1), is_immature: false, block_info: None, amount: bitcoin::Amount::from_sat(98765), @@ -2084,7 +2089,7 @@ CREATE TABLE labels ( spend_block: None, }, Coin { - outpoint: bitcoin::OutPoint::new(txs.get(1).unwrap().txid(), 2), + outpoint: bitcoin::OutPoint::new(txs.get(1).unwrap().compute_txid(), 2), is_immature: false, block_info: Some(BlockInfo { height: 101_095, @@ -2097,7 +2102,7 @@ CREATE TABLE labels ( spend_block: None, }, Coin { - outpoint: bitcoin::OutPoint::new(txs.get(2).unwrap().txid(), 3), + outpoint: bitcoin::OutPoint::new(txs.get(2).unwrap().compute_txid(), 3), is_immature: false, block_info: Some(BlockInfo { height: 101_099, @@ -2106,14 +2111,14 @@ CREATE TABLE labels ( amount: bitcoin::Amount::from_sat(98765), derivation_index: bip32::ChildNumber::from_normal_idx(1000).unwrap(), is_change: false, - spend_txid: Some(txs.get(3).unwrap().txid()), + spend_txid: Some(txs.get(3).unwrap().compute_txid()), spend_block: Some(BlockInfo { height: 101_199, time: 1_123_000, }), }, Coin { - outpoint: bitcoin::OutPoint::new(txs.get(4).unwrap().txid(), 4), + outpoint: bitcoin::OutPoint::new(txs.get(4).unwrap().compute_txid(), 4), is_immature: true, block_info: Some(BlockInfo { height: 101_100, @@ -2126,7 +2131,7 @@ CREATE TABLE labels ( spend_block: None, }, Coin { - outpoint: bitcoin::OutPoint::new(txs.get(5).unwrap().txid(), 5), + outpoint: bitcoin::OutPoint::new(txs.get(5).unwrap().compute_txid(), 5), is_immature: false, block_info: Some(BlockInfo { height: 101_102, @@ -2135,7 +2140,7 @@ CREATE TABLE labels ( amount: bitcoin::Amount::from_sat(98765), derivation_index: bip32::ChildNumber::from_normal_idx(100000).unwrap(), is_change: false, - spend_txid: Some(txs.get(6).unwrap().txid()), + spend_txid: Some(txs.get(6).unwrap().compute_txid()), spend_block: Some(BlockInfo { height: 101_105, time: 1_126_000, @@ -2162,12 +2167,12 @@ CREATE TABLE labels ( let db_txids = conn.db_list_txids(1_123_000, 1_127_000, 10); // Ordered by desc block time. - let expected_txids = [6, 5, 4, 3].map(|i| txs.get(i).unwrap().txid()); + let expected_txids = [6, 5, 4, 3].map(|i| txs.get(i).unwrap().compute_txid()); assert_eq!(&db_txids[..], &expected_txids,); let db_txids = conn.db_list_txids(1_123_000, 1_127_000, 2); // Ordered by desc block time. - let expected_txids = [6, 5].map(|i| txs.get(i).unwrap().txid()); + let expected_txids = [6, 5].map(|i| txs.get(i).unwrap().compute_txid()); assert_eq!(&db_txids[..], &expected_txids,); } @@ -2193,7 +2198,7 @@ CREATE TABLE labels ( let mut db_txids = conn.db_list_saved_txids(); db_txids.sort(); - let mut expected_txids: Vec<_> = txs.iter().map(|tx| tx.txid()).collect(); + let mut expected_txids: Vec<_> = txs.iter().map(|tx| tx.compute_txid()).collect(); expected_txids.sort(); assert_eq!(&db_txids[..], &expected_txids,); } @@ -2243,7 +2248,7 @@ CREATE TABLE labels ( .enumerate() .map(|(i, tx)| Coin { outpoint: bitcoin::OutPoint { - txid: tx.txid(), + txid: tx.compute_txid(), vout: i as u32, }, is_immature: (i % 10) == 0, @@ -2259,7 +2264,7 @@ CREATE TABLE labels ( None }, spend_txid: if i % 20 == 0 { - Some(spend_txs[i / 20].0.txid()) + Some(spend_txs[i / 20].0.compute_txid()) } else { None }, @@ -2311,8 +2316,10 @@ CREATE TABLE labels ( conn.confirm_spend(&confirmed_spent_coins); // For easy lookup, map each tx to its txid. - let bitcoin_txs: HashMap<_, _> = - bitcoin_txs.into_iter().map(|tx| (tx.txid(), tx)).collect(); + let bitcoin_txs: HashMap<_, _> = bitcoin_txs + .into_iter() + .map(|tx| (tx.compute_txid(), tx)) + .collect(); let block_info_from_coins: HashSet<_> = coins .iter() @@ -2368,14 +2375,22 @@ CREATE TABLE labels ( assert_eq!(txids.len(), indices.len()); let mut db_txs = conn.list_wallet_transactions(&txids); - db_txs.sort_by(|a, b| a.transaction.txid().cmp(&b.transaction.txid())); + db_txs.sort_by(|a, b| { + a.transaction + .compute_txid() + .cmp(&b.transaction.compute_txid()) + }); let mut expected_txs: Vec<_> = txids .iter() .collect::>() // remove duplicates .into_iter() .map(|txid| wallet_txs_from_coins.get(txid).unwrap().clone()) .collect(); - expected_txs.sort_by(|a, b| a.transaction.txid().cmp(&b.transaction.txid())); + expected_txs.sort_by(|a, b| { + a.transaction + .compute_txid() + .cmp(&b.transaction.compute_txid()) + }); assert_eq!(&db_txs[..], &expected_txs[..],); } } @@ -2414,7 +2429,7 @@ CREATE TABLE labels ( // The helper that was used to store Spend transaction in previous versions of the software // when there was no associated timestamp. fn store_spend_old(conn: &mut rusqlite::Connection, psbt: &Psbt) { - let txid = &psbt.unsigned_tx.txid()[..].to_vec(); + let txid = &psbt.unsigned_tx.compute_txid()[..].to_vec(); db_exec(conn, |db_tx| { db_tx.execute( @@ -2466,14 +2481,14 @@ CREATE TABLE labels ( let mut conn = rusqlite::Connection::open(&db_path).unwrap(); store_coin_old( &mut conn, - &bitcoin::OutPoint::new(bitcoin_txs.first().unwrap().txid(), 5), + &bitcoin::OutPoint::new(bitcoin_txs.first().unwrap().compute_txid(), 5), bitcoin::Amount::from_sat(14_000), 24.into(), true, ); store_coin_old( &mut conn, - &bitcoin::OutPoint::new(bitcoin_txs.get(1).unwrap().txid(), 2), + &bitcoin::OutPoint::new(bitcoin_txs.get(1).unwrap().compute_txid(), 2), bitcoin::Amount::from_sat(392_093_123), 24_567.into(), false, @@ -2522,7 +2537,7 @@ CREATE TABLE labels ( }; conn.new_txs(&[tx.clone()]); conn.new_unspent_coins(&[Coin { - outpoint: bitcoin::OutPoint::new(tx.txid(), 1), + outpoint: bitcoin::OutPoint::new(tx.compute_txid(), 1), is_immature: true, block_info: None, amount: bitcoin::Amount::from_sat(98765), @@ -2590,7 +2605,7 @@ CREATE TABLE labels ( // NULL in the DB by the `new_unspent_coins` method, but are set to `None` // here anyway. let coin_a = Coin { - outpoint: bitcoin::OutPoint::new(bitcoin_txs.first().unwrap().txid(), 1), + outpoint: bitcoin::OutPoint::new(bitcoin_txs.first().unwrap().compute_txid(), 1), is_immature: false, amount: bitcoin::Amount::from_sat(1231001), derivation_index: bip32::ChildNumber::from_normal_idx(101).unwrap(), @@ -2600,7 +2615,7 @@ CREATE TABLE labels ( spend_block: None, }; let coin_b = Coin { - outpoint: bitcoin::OutPoint::new(bitcoin_txs.get(1).unwrap().txid(), 19234), + outpoint: bitcoin::OutPoint::new(bitcoin_txs.get(1).unwrap().compute_txid(), 19234), is_immature: false, amount: bitcoin::Amount::from_sat(23145), derivation_index: bip32::ChildNumber::from_normal_idx(10).unwrap(), @@ -2610,7 +2625,7 @@ CREATE TABLE labels ( spend_block: None, }; let coin_c = Coin { - outpoint: bitcoin::OutPoint::new(bitcoin_txs.get(2).unwrap().txid(), 932), + outpoint: bitcoin::OutPoint::new(bitcoin_txs.get(2).unwrap().compute_txid(), 932), is_immature: false, amount: bitcoin::Amount::from_sat(354764), derivation_index: bip32::ChildNumber::from_normal_idx(3401).unwrap(), @@ -2620,7 +2635,7 @@ CREATE TABLE labels ( spend_block: None, }; let coin_d = Coin { - outpoint: bitcoin::OutPoint::new(bitcoin_txs.get(3).unwrap().txid(), 1456), + outpoint: bitcoin::OutPoint::new(bitcoin_txs.get(3).unwrap().compute_txid(), 1456), is_immature: false, amount: bitcoin::Amount::from_sat(23200), derivation_index: bip32::ChildNumber::from_normal_idx(4793235).unwrap(), @@ -2630,7 +2645,7 @@ CREATE TABLE labels ( spend_block: None, }; let coin_e = Coin { - outpoint: bitcoin::OutPoint::new(bitcoin_txs.get(4).unwrap().txid(), 4633), + outpoint: bitcoin::OutPoint::new(bitcoin_txs.get(4).unwrap().compute_txid(), 4633), is_immature: false, amount: bitcoin::Amount::from_sat(675000), derivation_index: bip32::ChildNumber::from_normal_idx(3).unwrap(), @@ -2640,7 +2655,7 @@ CREATE TABLE labels ( spend_block: None, }; let coin_imma_a = Coin { - outpoint: bitcoin::OutPoint::new(bitcoin_txs.get(5).unwrap().txid(), 5), + outpoint: bitcoin::OutPoint::new(bitcoin_txs.get(5).unwrap().compute_txid(), 5), is_immature: true, amount: bitcoin::Amount::from_sat(4564347), derivation_index: bip32::ChildNumber::from_normal_idx(453).unwrap(), @@ -2650,7 +2665,7 @@ CREATE TABLE labels ( spend_block: None, }; let coin_imma_b = Coin { - outpoint: bitcoin::OutPoint::new(bitcoin_txs.get(6).unwrap().txid(), 19234), + outpoint: bitcoin::OutPoint::new(bitcoin_txs.get(6).unwrap().compute_txid(), 19234), is_immature: true, amount: bitcoin::Amount::from_sat(731453), derivation_index: bip32::ChildNumber::from_normal_idx(98).unwrap(), @@ -2683,13 +2698,13 @@ CREATE TABLE labels ( (coin_imma_a.outpoint, 176001, 1755001004), ]); conn.spend_coins(&[ - (coin_a.outpoint, bitcoin_txs.get(7).unwrap().txid()), + (coin_a.outpoint, bitcoin_txs.get(7).unwrap().compute_txid()), (coin_b.outpoint, coin_d.outpoint.txid), (coin_d.outpoint, coin_e.outpoint.txid), ]); conn.confirm_spend(&[( coin_a.outpoint, - bitcoin_txs.get(7).unwrap().txid(), + bitcoin_txs.get(7).unwrap().compute_txid(), 245500, 1755003000, )]); @@ -2779,7 +2794,7 @@ CREATE TABLE labels ( .enumerate() .map(|(i, tx)| Coin { outpoint: bitcoin::OutPoint { - txid: tx.txid(), + txid: tx.compute_txid(), vout: i as u32, }, is_immature: (i % 10) == 0, @@ -2795,7 +2810,7 @@ CREATE TABLE labels ( None }, spend_txid: if i % 20 == 0 { - Some(spend_txs[i / 20].0.txid()) + Some(spend_txs[i / 20].0.compute_txid()) } else { None }, @@ -2870,7 +2885,7 @@ CREATE TABLE labels ( let db = SqliteDb::new(db_path.clone(), None, &secp).unwrap(); let mut conn = db.connection().unwrap(); - let txids: Vec<_> = bitcoin_txs.iter().map(|tx| tx.txid()).collect(); + let txids: Vec<_> = bitcoin_txs.iter().map(|tx| tx.compute_txid()).collect(); let bitcoin_txs_in_db: HashSet<_> = conn .list_wallet_transactions(&txids) .into_iter() diff --git a/src/database/sqlite/schema.rs b/src/database/sqlite/schema.rs index 456e45cb3..dfd418b32 100644 --- a/src/database/sqlite/schema.rs +++ b/src/database/sqlite/schema.rs @@ -296,7 +296,7 @@ impl TryFrom<&rusqlite::Row<'_>> for DbSpendTransaction { let txid: Vec = row.get(2)?; let txid: bitcoin::Txid = encode::deserialize(&txid).expect("We only store valid txids"); - assert_eq!(txid, psbt.unsigned_tx.txid()); + assert_eq!(txid, psbt.unsigned_tx.compute_txid()); let updated_at = row.get(3)?; diff --git a/src/database/sqlite/utils.rs b/src/database/sqlite/utils.rs index c3426cc10..4ac3b498e 100644 --- a/src/database/sqlite/utils.rs +++ b/src/database/sqlite/utils.rs @@ -245,7 +245,7 @@ fn migrate_v4_to_v5( )?; for bitcoin_tx in bitcoin_txs { - let txid = &bitcoin_tx.txid()[..].to_vec(); + let txid = &bitcoin_tx.compute_txid()[..].to_vec(); let bitcoin_tx_ser = bitcoin::consensus::serialize(bitcoin_tx); db_tx.execute( "INSERT INTO transactions (txid, tx) VALUES (?1, ?2);", diff --git a/src/descriptors/analysis.rs b/src/descriptors/analysis.rs index 8ea2a3589..c6d093a54 100644 --- a/src/descriptors/analysis.rs +++ b/src/descriptors/analysis.rs @@ -2,11 +2,11 @@ use miniscript::{ bitcoin::{ self, bip32, hashes::{sha256, Hash}, - secp256k1, Sequence, + secp256k1, }, descriptor, policy::{Concrete as ConcretePolicy, Liftable, Semantic as SemanticPolicy}, - ScriptContext, + RelLockTime, ScriptContext, Threshold, }; use std::{ @@ -14,6 +14,7 @@ use std::{ convert::TryFrom, error, fmt, str::FromStr, + sync, }; #[derive(Debug)] @@ -71,9 +72,10 @@ impl error::Error for LianaPolicyError {} fn is_single_key_or_multisig(policy: &SemanticPolicy) -> bool { match policy { SemanticPolicy::Key(..) => true, - SemanticPolicy::Threshold(_, subs) => { - subs.iter().all(|sub| matches!(sub, SemanticPolicy::Key(_))) - } + SemanticPolicy::Thresh(thresh) => thresh + .data() + .iter() + .all(|sub| matches!(sub.as_ref(), SemanticPolicy::Key(_))), _ => false, } } @@ -185,11 +187,14 @@ impl PathInfo { ) -> Result { match policy { SemanticPolicy::Key(key) => Ok(PathInfo::Single(key)), - SemanticPolicy::Threshold(k, subs) => { - let keys: Result<_, LianaPolicyError> = subs + SemanticPolicy::Thresh(thresh) if thresh.k() > 0 && thresh.n() >= thresh.k() => { + // FIXME + let k = thresh.k(); + let keys: Result<_, LianaPolicyError> = thresh + .into_data() .into_iter() - .map(|sub| match sub { - SemanticPolicy::Key(key) => Ok(key), + .map(|sub| match sub.as_ref() { + SemanticPolicy::Key(key) => Ok(key.clone()), _ => Err(LianaPolicyError::IncompatibleDesc), }) .collect(); @@ -199,6 +204,8 @@ impl PathInfo { } } + // FIXME: Rust-miniscript's introduction of Arc-wrapped sub-policies in Thresh made this code + // introduce allocations everywhere. /// Get the information about the recovery spending path. /// Returns None if the policy does not describe the recovery spending path of a Liana /// descriptor (that is, a set of keys after a timelock). @@ -210,7 +217,7 @@ impl PathInfo { // special case n == len(keys) (i.e. it's an N-of-N multisig), it is normalized as // `thresh(n+1, older(x), key1, key2, ...)`. let (k, subs) = match policy { - SemanticPolicy::Threshold(k, subs) => (k, subs), + SemanticPolicy::Thresh(thresh) => (thresh.k(), thresh.into_data()), _ => return Err(LianaPolicyError::IncompatibleDesc), }; if k == 2 && subs.len() == 2 { @@ -218,29 +225,29 @@ impl PathInfo { // of the same form as a primary path. let tl_value = subs .iter() - .find_map(|s| match s { - SemanticPolicy::Older(val) => Some(csv_check(val.0)), + .find_map(|s| match s.as_ref() { + SemanticPolicy::Older(val) => Some(csv_check(val.to_consensus_u32())), _ => None, }) .ok_or(LianaPolicyError::IncompatibleDesc)??; let keys_sub = subs .into_iter() - .find(is_single_key_or_multisig) + .find(|sub| is_single_key_or_multisig(sub.as_ref())) .ok_or(LianaPolicyError::IncompatibleDesc)?; - PathInfo::from_primary_path(keys_sub).map(|info| (tl_value, info)) + PathInfo::from_primary_path(keys_sub.as_ref().clone()).map(|info| (tl_value, info)) } else if k == subs.len() && subs.len() > 2 { // The N-of-N case. All subs but the threshold must be keys (if one had been thresh() // of keys it would have been normalized). let mut tl_value = None; let mut keys = Vec::with_capacity(subs.len()); for sub in subs { - match sub { - SemanticPolicy::Key(key) => keys.push(key), + match sub.as_ref() { + SemanticPolicy::Key(key) => keys.push(key.clone()), SemanticPolicy::Older(val) => { if tl_value.is_some() { return Err(LianaPolicyError::IncompatibleDesc); } - tl_value = Some(csv_check(val.0)?); + tl_value = Some(csv_check(val.to_consensus_u32())?); } _ => return Err(LianaPolicyError::IncompatibleDesc), } @@ -348,16 +355,21 @@ impl PathInfo { } /// Get a Miniscript Policy for this path. - pub fn into_ms_policy(self) -> ConcretePolicy { - match self { + pub fn into_ms_policy( + self, + ) -> Result, LianaPolicyError> { + Ok(match self { PathInfo::Single(key) => ConcretePolicy::Key(key), - PathInfo::Multi(thresh, keys) => ConcretePolicy::Threshold( - thresh, - keys.into_iter() - .map(|key| ConcretePolicy::Key(key).into()) - .collect(), + PathInfo::Multi(thresh, keys) => ConcretePolicy::Thresh( + Threshold::new( + thresh, + keys.into_iter() + .map(|key| sync::Arc::new(ConcretePolicy::Key(key).into())) + .collect(), + ) + .map_err(|e| LianaPolicyError::InvalidPolicy(miniscript::Error::Threshold(e)))?, ), - } + }) } } @@ -570,13 +582,10 @@ impl LianaPolicy { if *desc_int_xpub == unspend_int_xpub { tree_policy } else { - SemanticPolicy::Threshold( - 1, - vec![ - SemanticPolicy::Key(desc.internal_key().clone()), - tree_policy, - ], - ) + SemanticPolicy::Thresh(Threshold::or( + sync::Arc::new(SemanticPolicy::Key(desc.internal_key().clone())), + sync::Arc::new(tree_policy), + )) } } else { // A Liana descriptor must contain a timelocked path. @@ -593,15 +602,23 @@ impl LianaPolicy { // primary path with at least one key, and at least one timelocked recovery path with at // least one key. let subs = match policy { - SemanticPolicy::Threshold(1, subs) => Some(subs), - _ => None, - } - .ok_or(LianaPolicyError::IncompatibleDesc)?; + SemanticPolicy::Thresh(thresh) if thresh.is_or() && thresh.n() > 1 => { + thresh.into_data() + } + _ => return Err(LianaPolicyError::IncompatibleDesc), + }; // Fetch all spending paths' semantic policies. The primary path is identified as the only // one that isn't timelocked. let (mut primary_path, mut recovery_paths) = (None::, BTreeMap::new()); for sub in subs { + // Rust-Miniscript now forces the policy in thresholds to be wrapped into an Arc. Since + // we lift the policy from the descriptor right above, there is necessarily a single + // reference per Arc, so it's safe to unwrap. This avoids having to clone every single + // sub below. + let sub = + sync::Arc::try_unwrap(sub).expect("Only a single reference, created right above."); + // This is a (multi)key check. It must be the primary path. if is_single_key_or_multisig(&sub) { // We only support a single primary path. But it may be that the primary path is a @@ -610,7 +627,7 @@ impl LianaPolicy { // thresh(2, older(42), pk(C)))`. if let Some(prim_path) = primary_path { if let SemanticPolicy::Key(key) = sub { - primary_path = Some(prim_path.with_added_key(key)); + primary_path = Some(prim_path.with_added_key(key.clone())); } else { return Err(LianaPolicyError::IncompatibleDesc); } @@ -645,7 +662,10 @@ impl LianaPolicy { &self.recovery_paths } - fn into_policy(self) -> miniscript::policy::Concrete { + fn into_policy( + self, + ) -> Result, LianaPolicyError> + { let LianaPolicy { primary_path, recovery_paths, @@ -653,19 +673,23 @@ impl LianaPolicy { } = self; // Start with the primary spending path. We'll then or() all the recovery paths to it. - let primary_keys = primary_path.into_ms_policy(); + let primary_keys = primary_path.into_ms_policy()?; // Incrementally create the top-level policy using all recovery paths. assert!(!recovery_paths.is_empty()); - recovery_paths - .into_iter() - .fold(primary_keys, |tl_policy, (timelock, path_info)| { - let timelock = ConcretePolicy::Older(Sequence::from_height(timelock)); - let keys = path_info.into_ms_policy(); + Ok(recovery_paths.into_iter().try_fold( + primary_keys, + |tl_policy, (timelock, path_info)| { + let timelock = ConcretePolicy::Older(RelLockTime::from_height(timelock)); + let keys = path_info.into_ms_policy()?; let recovery_branch = ConcretePolicy::And(vec![keys.into(), timelock.into()]); // We assume the larger the timelock the less likely a branch would be used. - ConcretePolicy::Or(vec![(99, tl_policy.into()), (1, recovery_branch.into())]) - }) + Ok(ConcretePolicy::Or(vec![ + (99, tl_policy.into()), + (1, recovery_branch.into()), + ])) + }, + )?) } fn into_multipath_descriptor_fallible( @@ -689,12 +713,12 @@ impl LianaPolicy { depth: 0, parent_fingerprint: [0; 4].into(), child_number: 0.into(), - network: bitcoin::Network::Regtest, + network: bitcoin::Network::Regtest.into(), }, derivation_path: vec![].into(), wildcard: descriptor::Wildcard::None, }); - let policy = self.into_policy(); + let policy = self.into_policy()?; let desc = policy .clone() .compile_tr(Some(dummy_internal_key.clone())) @@ -719,7 +743,7 @@ impl LianaPolicy { } } else { let ms = self - .into_policy() + .into_policy()? .compile::() .map_err(|e| LianaPolicyError::InvalidPolicy(e.into()))?; miniscript::Segwitv0::check_local_validity(&ms).expect("Miniscript must be sane"); diff --git a/src/descriptors/mod.rs b/src/descriptors/mod.rs index 7ff6ac30d..83907a3bc 100644 --- a/src/descriptors/mod.rs +++ b/src/descriptors/mod.rs @@ -199,7 +199,7 @@ impl LianaDescriptor { pub fn all_xpubs_net_is(&self, expected_net: bitcoin::Network) -> bool { self.multi_desc.for_each_key(|xpub| { if let descriptor::DescriptorPublicKey::MultiXPub(xpub) = xpub { - xpub.xkey.network == expected_net + xpub.xkey.network == expected_net.into() } else { false } @@ -286,10 +286,14 @@ impl LianaDescriptor { // emtpy witness). But this method is used to account between a completely "nude" // transaction (and therefore no Segwit marker nor empty witness in inputs) and a // satisfied transaction. - self.multi_desc + (self + .multi_desc .max_weight_to_satisfy() .expect("Always satisfiable") - + 1 + .to_wu() + + 1) + .try_into() + .expect("Sat weight must fit in usize.") } } @@ -773,7 +777,7 @@ mod tests { [(26352, recovery_keys.clone())].iter().cloned().collect(), ) .unwrap(); - assert_eq!(LianaDescriptor::new(policy).to_string(), "wsh(or_d(multi(3,[aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/0/<0;1>/*,[aabb0012/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/0/<0;1>/*,[aabb0013/48'/0'/0'/2']xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/0/<0;1>/*),and_v(v:thresh(2,pkh([aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/1/<0;1>/*),a:pkh([aabb0012/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/1/<0;1>/*),a:pkh([aabb0013/48'/0'/0'/2']xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/1/<0;1>/*)),older(26352))))#prj7nktq"); + assert_eq!(LianaDescriptor::new(policy).to_string(), "wsh(or_i(and_v(v:thresh(2,pkh([aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/1/<0;1>/*),a:pkh([aabb0012/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/1/<0;1>/*),a:pkh([aabb0013/48'/0'/0'/2']xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/1/<0;1>/*)),older(26352)),and_v(v:and_v(v:pk([aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/0/<0;1>/*),pk([aabb0012/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/0/<0;1>/*)),pk([aabb0013/48'/0'/0'/2']xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/0/<0;1>/*))))#c7nf353n"); // Same under Taproot. let policy = LianaPolicy::new( @@ -781,7 +785,7 @@ mod tests { [(26352, recovery_keys)].iter().cloned().collect(), ) .unwrap(); - assert_eq!(LianaDescriptor::new(policy.clone()).to_string(), "tr(xpub661MyMwAqRbcFERisZuMzFcfg3Ur3dKB17kb8iEG89ZJYMHTWqKQGRdLjTXC6Byr8kjKo6JabFfRCm3ETM4woq7DxUXuUxxRFHfog4Peh41/<0;1>/*,{and_v(v:multi_a(2,[aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/1/<0;1>/*,[aabb0012/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/1/<0;1>/*,[aabb0013/48'/0'/0'/2']xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/1/<0;1>/*),older(26352)),multi_a(3,[aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/0/<0;1>/*,[aabb0012/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/0/<0;1>/*,[aabb0013/48'/0'/0'/2']xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/0/<0;1>/*)})#tugn7xtx"); + assert_eq!(LianaDescriptor::new(policy.clone()).to_string(), "tr(xpub661MyMwAqRbcFERisZuMzFcfg3Ur3dKB17kb8iEG89ZJYMHTWqKQGRdLjTXC6Byr8kjKo6JabFfRCm3ETM4woq7DxUXuUxxRFHfog4Peh41/<0;1>/*,{and_v(v:multi_a(2,[aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/1/<0;1>/*,[aabb0012/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/1/<0;1>/*,[aabb0013/48'/0'/0'/2']xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/1/<0;1>/*),older(26352)),and_v(v:and_v(v:pk([aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/0/<0;1>/*),pk([aabb0012/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/0/<0;1>/*)),pk([aabb0013/48'/0'/0'/2']xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/0/<0;1>/*))})#eey6zfhr"); // Another derivation step before the wildcard is taken into account. // desc_b is the very same descriptor as desc_a, except the very first xpub's derivation @@ -819,7 +823,7 @@ mod tests { [(26352, recovery_keys.clone())].iter().cloned().collect(), ) .unwrap(); - assert_eq!(LianaDescriptor::new(policy).to_string(), "wsh(or_d(multi(3,[aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/<0;1>/*,[aabb0012/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/<0;1>/*,[aabb0013/48'/0'/0'/2']xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/<0;1>/*),and_v(v:thresh(2,pkh([aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/<2;3>/*),a:pkh([aabb0012/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/<2;3>/*),a:pkh([aabb0013/48'/0'/0'/2']xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/<2;3>/*)),older(26352))))#d2h994td"); + assert_eq!(LianaDescriptor::new(policy).to_string(), "wsh(or_i(and_v(v:thresh(2,pkh([aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/<2;3>/*),a:pkh([aabb0012/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/<2;3>/*),a:pkh([aabb0013/48'/0'/0'/2']xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/<2;3>/*)),older(26352)),and_v(v:and_v(v:pk([aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/<0;1>/*),pk([aabb0012/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/<0;1>/*)),pk([aabb0013/48'/0'/0'/2']xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/<0;1>/*))))#tjdnx6vm"); // Same under Taproot. let policy = LianaPolicy::new( @@ -827,7 +831,7 @@ mod tests { [(26352, recovery_keys)].iter().cloned().collect(), ) .unwrap(); - assert_eq!(LianaDescriptor::new(policy.clone()).to_string(), "tr(xpub661MyMwAqRbcFERisZuMzFcfg3Ur3dKB17kb8iEG89ZJYMHTWqKQGRdLjTXC6Byr8kjKo6JabFfRCm3ETM4woq7DxUXuUxxRFHfog4Peh41/<0;1>/*,{and_v(v:multi_a(2,[aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/<2;3>/*,[aabb0012/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/<2;3>/*,[aabb0013/48'/0'/0'/2']xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/<2;3>/*),older(26352)),multi_a(3,[aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/<0;1>/*,[aabb0012/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/<0;1>/*,[aabb0013/48'/0'/0'/2']xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/<0;1>/*)})#ayju5dfr"); + assert_eq!(LianaDescriptor::new(policy.clone()).to_string(), "tr(xpub661MyMwAqRbcFERisZuMzFcfg3Ur3dKB17kb8iEG89ZJYMHTWqKQGRdLjTXC6Byr8kjKo6JabFfRCm3ETM4woq7DxUXuUxxRFHfog4Peh41/<0;1>/*,{and_v(v:multi_a(2,[aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/<2;3>/*,[aabb0012/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/<2;3>/*,[aabb0013/48'/0'/0'/2']xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/<2;3>/*),older(26352)),and_v(v:and_v(v:pk([aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/<0;1>/*),pk([aabb0012/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/<0;1>/*)),pk([aabb0013/48'/0'/0'/2']xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/<0;1>/*))})#d06ehu7c"); // We prevent footguns with timelocks by requiring a u16. Note how the following wouldn't // compile: diff --git a/src/signer.rs b/src/signer.rs index 5b53343fa..2dda8a787 100644 --- a/src/signer.rs +++ b/src/signer.rs @@ -249,9 +249,9 @@ impl HotSigner { .as_ref() .ok_or(SignerError::IncompletePsbt)? .value; - let sig_type = sighash::EcdsaSighashType::All; + let sighash_type = sighash::EcdsaSighashType::All; let sighash = sighash_cache - .p2wsh_signature_hash(input_index, witscript, value, sig_type) + .p2wsh_signature_hash(input_index, witscript, value, sighash_type) .map_err(|_| SignerError::InsanePsbt)?; let sighash = secp256k1::Message::from_digest_slice(sighash.as_byte_array()) .expect("Sighash is always 32 bytes."); @@ -266,12 +266,12 @@ impl HotSigner { if pubkey.inner != *curr_pubkey { return Err(SignerError::InsanePsbt); } - let sig = secp.sign_ecdsa_low_r(&sighash, &privkey.inner); + let signature = secp.sign_ecdsa_low_r(&sighash, &privkey.inner); psbt_in.partial_sigs.insert( pubkey, ecdsa::Signature { - sig, - hash_ty: sig_type, + signature, + sighash_type, }, ); } @@ -289,7 +289,7 @@ impl HotSigner { psbt_in: &mut PsbtIn, input_index: usize, ) -> Result<(), SignerError> { - let sig_type = sighash::TapSighashType::Default; + let sighash_type = sighash::TapSighashType::Default; let prevouts = sighash::Prevouts::All(prevouts); // If the details of the internal key are filled, provide a keypath signature. @@ -305,14 +305,14 @@ impl HotSigner { } let keypair = keypair.tap_tweak(secp, psbt_in.tap_merkle_root).to_inner(); let sighash = sighash_cache - .taproot_key_spend_signature_hash(input_index, &prevouts, sig_type) + .taproot_key_spend_signature_hash(input_index, &prevouts, sighash_type) .map_err(|_| SignerError::InsanePsbt)?; let sighash = secp256k1::Message::from_digest_slice(sighash.as_byte_array()) .expect("Sighash is always 32 bytes."); - let sig = secp.sign_schnorr_no_aux_rand(&sighash, &keypair); + let signature = secp.sign_schnorr_no_aux_rand(&sighash, &keypair); let sig = bitcoin::taproot::Signature { - sig, - hash_ty: sig_type, + signature, + sighash_type, }; psbt_in.tap_key_sig = Some(sig); } @@ -334,15 +334,15 @@ impl HotSigner { input_index, &prevouts, *leaf_hash, - sig_type, + sighash_type, ) .map_err(|_| SignerError::InsanePsbt)?; let sighash = secp256k1::Message::from_digest_slice(sighash.as_byte_array()) .expect("Sighash is always 32 bytes."); - let sig = secp.sign_schnorr_no_aux_rand(&sighash, &keypair); + let signature = secp.sign_schnorr_no_aux_rand(&sighash, &keypair); let sig = bitcoin::taproot::Signature { - sig, - hash_ty: sig_type, + signature, + sighash_type, }; psbt_in.tap_script_sigs.insert((*pubkey, *leaf_hash), sig); } @@ -400,7 +400,7 @@ impl HotSigner { /// BIP32 encoding of those keys (xpubs, tpubs, ..) but does not affect any data (whether it is /// the keys or the mnemonics). pub fn set_network(&mut self, network: bitcoin::Network) { - self.master_xpriv.network = network; + self.master_xpriv.network = network.into(); } } @@ -565,7 +565,7 @@ mod tests { "bc1qvklensptw5lk7d470ds60pcpsr0psdpgyvwepv", ) .unwrap() - .payload() + .assume_checked() .script_pubkey(), }], }, @@ -825,7 +825,7 @@ mod tests { "bc1qvklensptw5lk7d470ds60pcpsr0psdpgyvwepv", ) .unwrap() - .payload() + .assume_checked() .script_pubkey(), }], }, diff --git a/src/spend.rs b/src/spend.rs index 9465b775e..f15538180 100644 --- a/src/spend.rs +++ b/src/spend.rs @@ -157,7 +157,7 @@ fn sanity_check_psbt( // Check for dust outputs for txo in psbt.unsigned_tx.output.iter() { - if txo.value < txo.script_pubkey.dust_value() { + if txo.value < txo.script_pubkey.minimal_non_dust() { return Err(SpendCreationError::SanityCheckFailure(psbt.clone())); } } diff --git a/src/testutils.rs b/src/testutils.rs index 3669b5fc0..601fb036a 100644 --- a/src/testutils.rs +++ b/src/testutils.rs @@ -355,7 +355,7 @@ impl DatabaseConnection for DummyDatabase { } fn store_spend(&mut self, psbt: &Psbt) { - let txid = psbt.unsigned_tx.txid(); + let txid = psbt.unsigned_tx.compute_txid(); self.db .write() .unwrap() @@ -446,7 +446,11 @@ impl DatabaseConnection for DummyDatabase { fn new_txs(&mut self, txs: &[bitcoin::Transaction]) { for tx in txs { - self.db.write().unwrap().txs.insert(tx.txid(), tx.clone()); + self.db + .write() + .unwrap() + .txs + .insert(tx.compute_txid(), tx.clone()); } }