diff --git a/Cargo.lock b/Cargo.lock index 8a0037abd..4237332a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -267,14 +267,13 @@ dependencies = [ [[package]] name = "async-hwi" -version = "0.0.25" +version = "0.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2bf351445dda3f867da2effb866be894cc88f7648a67b93d1f462fde026a6d" +checksum = "3b67f67913279ff8009b46fde618b87d46f7c4306c7e93342fed09c6bb5dea09" dependencies = [ "async-trait", "bitbox-api", - "bitcoin 0.31.2", - "bitcoin 0.32.4", + "bitcoin", "coldcard", "futures", "hidapi", @@ -409,6 +408,31 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "aws-lc-rs" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c2b7ddaa2c56a367ad27a094ad8ef4faacf8a617c2575acb2ba88949df999ca" +dependencies = [ + "aws-lc-sys", + "paste", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71b2ddd3ada61a305e1d8bb6c005d1eaa7d14d903681edfc400406d523a9b491" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", + "paste", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -481,11 +505,11 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bdk_chain" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c601c4dc7e6c3efa538a0afbb43b964cefab9a9b5e8f352fa0ca38145448a5e7" +checksum = "163b064557cee078e8ee5dd2c88944204506f7b2b1524f78e8fcba38c346da7b" dependencies = [ - "bitcoin 0.31.2", + "bitcoin", "miniscript", ] @@ -497,9 +521,9 @@ checksum = "3c084bf76f0f67546fc814ffa82044144be1bb4618183a15016c162f8b087ad4" [[package]] name = "bdk_electrum" -version = "0.14.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28906275aeb1f71dc32045670f06c8a26fb17cc62151a99f7425d258f4bda589" +checksum = "01aa7f890313a33b27bcdcbb8efe98450710fce5af51e7ab013ef6d179252ffa" dependencies = [ "bdk_chain", "electrum-client", @@ -507,15 +531,32 @@ dependencies = [ [[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 = "bech32" -version = "0.11.0" +name = "bindgen" +version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "log", + "prettyplease 0.2.25", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.87", + "which", +] [[package]] name = "bip39" @@ -557,7 +598,7 @@ checksum = "8349407999d3653dbbd3f75182742a6c8ad73f424ef83fcf5b87ead0c8ce2528" dependencies = [ "async-trait", "base32", - "bitcoin 0.32.4", + "bitcoin", "byteorder", "chrono", "getrandom", @@ -576,23 +617,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "bitcoin" -version = "0.31.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c85783c2fe40083ea54a33aa2f0ba58831d90fcd190f5bdc47e74e84d2a96ae" -dependencies = [ - "base64 0.21.7", - "bech32 0.10.0-beta", - "bitcoin-internals 0.2.0", - "bitcoin_hashes 0.13.0", - "core2", - "hex-conservative 0.1.2", - "hex_lit", - "secp256k1 0.28.2", - "serde", -] - [[package]] name = "bitcoin" version = "0.32.4" @@ -601,14 +625,14 @@ checksum = "788902099d47c8682efe6a7afb01c8d58b9794ba66c06affd81c3d6b560743eb" dependencies = [ "base58ck", "base64 0.21.7", - "bech32 0.11.0", + "bech32", "bitcoin-internals 0.3.0", "bitcoin-io", "bitcoin-units", "bitcoin_hashes 0.14.0", "hex-conservative 0.2.1", "hex_lit", - "secp256k1 0.29.1", + "secp256k1", "serde", ] @@ -617,9 +641,6 @@ name = "bitcoin-internals" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" -dependencies = [ - "serde", -] [[package]] name = "bitcoin-internals" @@ -668,9 +689,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" dependencies = [ "bitcoin-internals 0.2.0", - "core2", "hex-conservative 0.1.2", - "serde", ] [[package]] @@ -895,6 +914,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -962,6 +990,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading 0.8.5", +] + [[package]] name = "clipboard-win" version = "5.4.0" @@ -1001,6 +1040,15 @@ dependencies = [ "x11rb", ] +[[package]] +name = "cmake" +version = "0.1.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c682c223677e0e5b6b7f63a64b9351844c3f1b1678a68b7ee617e30fb082620e" +dependencies = [ + "cc", +] + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -1182,15 +1230,6 @@ dependencies = [ "libc", ] -[[package]] -name = "core2" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239fa3ae9b63c2dc74bd3fa852d4792b8b305ae64eeede946265b6af62f1fff3" -dependencies = [ - "memchr", -] - [[package]] name = "cosmic-text" version = "0.10.0" @@ -1507,6 +1546,12 @@ dependencies = [ "linux-raw-sys 0.6.5", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "ecdsa" version = "0.16.9" @@ -1529,15 +1574,15 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "electrum-client" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89008f106be6f303695522f2f4c1f28b40c3e8367ed8b3bb227f1f882cb52cc2" +checksum = "8c7b1f8783238bb18e6e137875b0a66f3dffe6c7ea84066e05d033cf180b150f" dependencies = [ - "bitcoin 0.31.2", + "bitcoin", "byteorder", "libc", "log", - "rustls", + "rustls 0.23.21", "serde", "serde_json", "webpki-roots", @@ -1860,6 +1905,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures" version = "0.3.31" @@ -2048,6 +2099,12 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + [[package]] name = "glow" version = "0.13.1" @@ -2269,9 +2326,6 @@ name = "hex-conservative" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" -dependencies = [ - "core2", -] [[package]] name = "hex-conservative" @@ -2392,7 +2446,7 @@ dependencies = [ "futures-util", "http", "hyper", - "rustls", + "rustls 0.21.12", "tokio", "tokio-rustls", ] @@ -2985,6 +3039,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "lebe" version = "0.5.2" @@ -3030,12 +3090,12 @@ dependencies = [ [[package]] name = "ledger_bitcoin_client" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8606a9c7375fb139e68fc1ca7cf9c6709566eeca448ff33e37632d8a4302eefe" +checksum = "38bb192e14da7725505c49791fde863bafee2b28dede2f37e05559fe72c29416" dependencies = [ "async-trait", - "bitcoin 0.31.2", + "bitcoin", "miniscript", ] @@ -3059,7 +3119,7 @@ dependencies = [ "arbitrary", "liana", "libfuzzer-sys", - "secp256k1 0.28.2", + "secp256k1", ] [[package]] @@ -3100,7 +3160,7 @@ dependencies = [ name = "liana-ui" version = "0.1.0" dependencies = [ - "bitcoin 0.31.2", + "bitcoin", "chrono", "iced", ] @@ -3384,15 +3444,20 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniscript" -version = "11.2.0" +version = "12.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3127e10529a57a8f7fa9b1332c4c2f72baadaca6777798f910dff3c922620b14" +checksum = "5bd3c9608217b0d6fa9c9c8ddd875b85ab72bd4311cfc8db35e1b5a08fc11f4d" dependencies = [ - "bech32 0.10.0-beta", - "bitcoin 0.31.2", - "bitcoin-internals 0.2.0", + "bech32", + "bitcoin", "serde", ] @@ -3572,6 +3637,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -4142,6 +4217,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn 2.0.87", +] + [[package]] name = "proc-macro-crate" version = "3.2.0" @@ -4212,7 +4297,7 @@ dependencies = [ "log", "multimap", "petgraph", - "prettyplease", + "prettyplease 0.1.25", "prost 0.11.9", "prost-types", "regex", @@ -4477,7 +4562,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls", + "rustls 0.21.12", "rustls-pemfile", "serde", "serde_json", @@ -4649,10 +4734,25 @@ checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", - "rustls-webpki", + "rustls-webpki 0.101.7", "sct", ] +[[package]] +name = "rustls" +version = "0.23.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "rustls-pki-types", + "rustls-webpki 0.102.8", + "subtle", + "zeroize", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -4662,6 +4762,12 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pki-types" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -4672,6 +4778,18 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustybuzz" version = "0.10.0" @@ -4769,17 +4887,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "secp256k1" -version = "0.28.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" -dependencies = [ - "bitcoin_hashes 0.13.0", - "secp256k1-sys 0.9.2", - "serde", -] - [[package]] name = "secp256k1" version = "0.29.1" @@ -4787,19 +4894,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ "bitcoin_hashes 0.14.0", - "secp256k1-sys 0.10.1", + "secp256k1-sys", "serde", ] -[[package]] -name = "secp256k1-sys" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" -dependencies = [ - "cc", -] - [[package]] name = "secp256k1-sys" version = "0.10.1" @@ -5466,7 +5564,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls", + "rustls 0.21.12", "tokio", ] diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 487a55d62..ea0b17f1b 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -10,7 +10,7 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.4" arbitrary = { version = "1", features = ["derive"] } -secp256k1 = { version = "0.28", features = ["global-context-less-secure"] } +secp256k1 = { version = "0.29", features = ["global-context-less-secure"] } [dependencies.liana] diff --git a/fuzz/fuzz_targets/descriptors.rs b/fuzz/fuzz_targets/descriptors.rs index 7278de5e8..924523f3f 100644 --- a/fuzz/fuzz_targets/descriptors.rs +++ b/fuzz/fuzz_targets/descriptors.rs @@ -35,7 +35,7 @@ impl PathConfig { .expect("Valid pubkey: NUMS from BIP341"); let dummy_fg = [0, 0, path_index, 0].into(); let xpub = bip32::Xpub { - network: Network::Bitcoin, + network: Network::Bitcoin.into(), depth: 0, parent_fingerprint: dummy_fg, child_number: 0.into(), diff --git a/liana-gui/Cargo.toml b/liana-gui/Cargo.toml index cf9e6c928..f1fa10a82 100644 --- a/liana-gui/Cargo.toml +++ b/liana-gui/Cargo.toml @@ -15,7 +15,7 @@ path = "src/main.rs" [dependencies] async-trait = "0.1" -async-hwi = { version = "0.0.25" } +async-hwi = { version = "0.0.26" } liana = { path = "../liana" } lianad = { path = "../lianad", default-features = false, features = ["nonblocking_shutdown"] } liana-ui = { path = "../liana-ui" } diff --git a/liana-gui/src/app/state/psbt.rs b/liana-gui/src/app/state/psbt.rs index ed0907fd3..c2c4b23be 100644 --- a/liana-gui/src/app/state/psbt.rs +++ b/liana-gui/src/app/state/psbt.rs @@ -325,7 +325,7 @@ impl Action for BroadcastAction { return Command::perform( async move { daemon - .broadcast_spend_tx(&psbt.unsigned_tx.txid()) + .broadcast_spend_tx(&psbt.unsigned_tx.compute_txid()) .await .map_err(|e| e.into()) }, @@ -378,7 +378,7 @@ impl Action for DeleteAction { return Command::perform( async move { daemon - .delete_spend_tx(&psbt.unsigned_tx.txid()) + .delete_spend_tx(&psbt.unsigned_tx.compute_txid()) .await .map_err(|e| e.into()) }, @@ -705,7 +705,8 @@ impl Action for UpdateAction { Message::View(view::Message::ImportSpend(view::ImportSpendMessage::PsbtEdited(s))) => { self.updated.value = s; if let Ok(psbt) = Psbt::from_str(&self.updated.value) { - self.updated.valid = tx.psbt.unsigned_tx.txid() == psbt.unsigned_tx.txid(); + self.updated.valid = + tx.psbt.unsigned_tx.compute_txid() == psbt.unsigned_tx.compute_txid(); } else { self.updated.valid = false; } diff --git a/liana-gui/src/app/state/psbts.rs b/liana-gui/src/app/state/psbts.rs index c12a401d3..0c5f59a63 100644 --- a/liana-gui/src/app/state/psbts.rs +++ b/liana-gui/src/app/state/psbts.rs @@ -88,7 +88,8 @@ impl State for PsbtsPanel { self.spend_txs = txs; if let Some(tx) = &self.selected_tx { if let Some(tx) = self.spend_txs.iter().find(|spend_tx| { - spend_tx.psbt.unsigned_tx.txid() == tx.tx.psbt.unsigned_tx.txid() + spend_tx.psbt.unsigned_tx.compute_txid() + == tx.tx.psbt.unsigned_tx.compute_txid() }) { let tx = psbt::PsbtState::new(self.wallet.clone(), tx.clone(), true); let cmd = tx.load(daemon); diff --git a/liana-gui/src/app/state/spend/step.rs b/liana-gui/src/app/state/spend/step.rs index 79a2937eb..98f78021a 100644 --- a/liana-gui/src/app/state/spend/step.rs +++ b/liana-gui/src/app/state/spend/step.rs @@ -597,7 +597,7 @@ impl Step for DefineSpend { !recipient.label.value.is_empty() && Address::from_str(&recipient.address.value) .unwrap() - .payload() + .assume_checked() .matches_script_pubkey(&output.script_pubkey) && output.value.to_sat() == recipient.amount().unwrap() }) @@ -605,7 +605,7 @@ impl Step for DefineSpend { { draft.labels.insert( OutPoint { - txid: psbt.unsigned_tx.txid(), + txid: psbt.unsigned_tx.compute_txid(), vout: i as u32, } .to_string(), @@ -671,7 +671,7 @@ impl Recipient { } if let Ok(address) = Address::from_str(&self.address.value) { - if amount <= address.payload().script_pubkey().dust_value() { + if amount <= address.assume_checked().script_pubkey().minimal_non_dust() { return Err(Error::Unexpected( "Amount must be superior to script dust value".to_string(), )); @@ -761,14 +761,16 @@ impl Step for SaveSpend { if tx.is_batch() { if let Some(label) = &draft.batch_label { - tx.labels - .insert(tx.psbt.unsigned_tx.txid().to_string(), label.clone()); + tx.labels.insert( + tx.psbt.unsigned_tx.compute_txid().to_string(), + label.clone(), + ); } } else if let Some(recipient) = draft.recipients.first() { if !recipient.label.value.is_empty() { let label = recipient.label.value.clone(); tx.labels - .insert(tx.psbt.unsigned_tx.txid().to_string(), label); + .insert(tx.psbt.unsigned_tx.compute_txid().to_string(), label); } } diff --git a/liana-gui/src/app/state/transactions.rs b/liana-gui/src/app/state/transactions.rs index 5aa619222..2abc4f4e0 100644 --- a/liana-gui/src/app/state/transactions.rs +++ b/liana-gui/src/app/state/transactions.rs @@ -160,8 +160,11 @@ impl State for TransactionsPanel { self.selected_tx = self.txs.get(i).cloned(); // Clear modal if it's for a different tx. if let TransactionsModal::CreateRbf(modal) = &self.modal { - if Some(modal.tx.tx.txid()) - != self.selected_tx.as_ref().map(|selected| selected.tx.txid()) + if Some(modal.tx.tx.compute_txid()) + != self + .selected_tx + .as_ref() + .map(|selected| selected.tx.compute_txid()) { self.modal = TransactionsModal::None; } @@ -177,7 +180,7 @@ impl State for TransactionsPanel { let outpoints: Vec<_> = (0..tx.tx.output.len()) .map(|vout| { OutPoint::new( - tx.tx.txid(), + tx.tx.compute_txid(), vout.try_into() .expect("number of transaction outputs must fit in u32"), ) @@ -458,7 +461,7 @@ async fn rbf( is_cancel: bool, feerate_vb: Option, ) -> Result { - let previous_txid = previous_tx.tx.txid(); + let previous_txid = previous_tx.tx.compute_txid(); let psbt = match daemon .rbf_psbt(&previous_txid, is_cancel, feerate_vb) .await? @@ -474,7 +477,7 @@ async fn rbf( if !is_cancel { let mut labels = HashMap::>::new(); - let new_txid = psbt.unsigned_tx.txid(); + let new_txid = psbt.unsigned_tx.compute_txid(); for item in previous_tx.labelled() { if let Some(label) = previous_tx.labels.get(&item.to_string()) { match item { @@ -506,5 +509,5 @@ async fn rbf( } daemon.update_spend_tx(&psbt).await?; - Ok(psbt.unsigned_tx.txid()) + Ok(psbt.unsigned_tx.compute_txid()) } diff --git a/liana-gui/src/app/view/home.rs b/liana-gui/src/app/view/home.rs index 6b0234f95..55f48aa06 100644 --- a/liana-gui/src/app/view/home.rs +++ b/liana-gui/src/app/view/home.rs @@ -237,9 +237,9 @@ pub fn payment_view<'a>( labels_editing: &'a HashMap>, warning: Option<&'a Error>, ) -> Element<'a, Message> { - let txid = tx.tx.txid().to_string(); + let txid = tx.tx.compute_txid().to_string(); let outpoint = bitcoin::OutPoint { - txid: tx.tx.txid(), + txid: tx.tx.compute_txid(), vout: output_index as u32, } .to_string(); @@ -330,10 +330,14 @@ pub fn payment_view<'a>( .push( Row::new() .align_items(Alignment::Center) - .push(Container::new(text(format!("{}", tx.tx.txid())).small())) + .push(Container::new( + text(format!("{}", tx.tx.compute_txid())).small(), + )) .push( Button::new(icon::clipboard_icon()) - .on_press(Message::Clipboard(tx.tx.txid().to_string())) + .on_press(Message::Clipboard( + tx.tx.compute_txid().to_string(), + )) .style(theme::Button::TransparentBorder), ) .width(Length::Shrink), @@ -342,8 +346,9 @@ pub fn payment_view<'a>( .spacing(5), )) .push( - button::secondary(None, "See transaction details") - .on_press(Message::Menu(Menu::TransactionPreSelected(tx.tx.txid()))), + button::secondary(None, "See transaction details").on_press(Message::Menu( + Menu::TransactionPreSelected(tx.tx.compute_txid()), + )), ) .spacing(20), ) diff --git a/liana-gui/src/app/view/psbt.rs b/liana-gui/src/app/view/psbt.rs index 7b7471cf3..d860aa4c0 100644 --- a/liana-gui/src/app/view/psbt.rs +++ b/liana-gui/src/app/view/psbt.rs @@ -268,7 +268,7 @@ pub fn spend_header<'a>( tx: &'a SpendTx, labels_editing: &'a HashMap>, ) -> Element<'a, Message> { - let txid = tx.psbt.unsigned_tx.txid().to_string(); + let txid = tx.psbt.unsigned_tx.compute_txid().to_string(); Column::new() .spacing(20) .push(if let Some(outpoint) = tx.is_single_payment() { @@ -357,13 +357,13 @@ pub fn spend_overview_view<'a>( Row::new() .push(p1_bold("Tx ID").width(Length::Fill)) .push( - p2_regular(tx.psbt.unsigned_tx.txid().to_string()) + p2_regular(tx.psbt.unsigned_tx.compute_txid().to_string()) .style(color::GREY_3), ) .push( Button::new(icon::clipboard_icon().style(color::GREY_3)) .on_press(Message::Clipboard( - tx.psbt.unsigned_tx.txid().to_string(), + tx.psbt.unsigned_tx.compute_txid().to_string(), )) .style(theme::Button::TransparentBorder), ) @@ -744,7 +744,7 @@ pub fn outputs_view<'a>( |col: Column<'a, Message>, (i, output)| { col.spacing(10).push(payment_view( i, - tx.txid(), + tx.compute_txid(), output, network, labels, diff --git a/liana-gui/src/app/view/psbts.rs b/liana-gui/src/app/view/psbts.rs index 9ec08f235..9a6444a71 100644 --- a/liana-gui/src/app/view/psbts.rs +++ b/liana-gui/src/app/view/psbts.rs @@ -125,7 +125,7 @@ fn spend_tx_list_view(i: usize, tx: &SpendTx) -> Element<'_, Message> { }) .push_maybe( tx.labels - .get(&tx.psbt.unsigned_tx.txid().to_string()) + .get(&tx.psbt.unsigned_tx.compute_txid().to_string()) .map(p1_regular), ) .spacing(10) diff --git a/liana-gui/src/app/view/transactions.rs b/liana-gui/src/app/view/transactions.rs index 071753b52..f87c4fc5c 100644 --- a/liana-gui/src/app/view/transactions.rs +++ b/liana-gui/src/app/view/transactions.rs @@ -108,7 +108,9 @@ fn tx_list_view(i: usize, tx: &HistoryTransaction) -> Element<'_, Message> { .push_maybe(if let Some(outpoint) = tx.is_single_payment() { tx.labels.get(&outpoint.to_string()).map(p1_regular) } else { - tx.labels.get(&tx.tx.txid().to_string()).map(p1_regular) + tx.labels + .get(&tx.tx.compute_txid().to_string()) + .map(p1_regular) }) .push_maybe(tx.time.map(|t| { Container::new( @@ -297,7 +299,7 @@ pub fn tx_view<'a>( labels_editing: &'a HashMap>, warning: Option<&'a Error>, ) -> Element<'a, Message> { - let txid = tx.tx.txid().to_string(); + let txid = tx.tx.compute_txid().to_string(); dashboard( &Menu::Transactions, cache, diff --git a/liana-gui/src/daemon/mod.rs b/liana-gui/src/daemon/mod.rs index 073a3a7ad..3f0211fc7 100644 --- a/liana-gui/src/daemon/mod.rs +++ b/liana-gui/src/daemon/mod.rs @@ -141,7 +141,7 @@ pub trait Daemon: Debug { // TODO: Use filters in `list_spend_txs` command. let mut txs = self.list_spend_txs().await?.spend_txs; if let Some(txids) = txids { - txs.retain(|tx| txids.contains(&tx.psbt.unsigned_tx.txid())); + txs.retain(|tx| txids.contains(&tx.psbt.unsigned_tx.compute_txid())); } let outpoints: Vec<_> = txs .iter() @@ -201,7 +201,7 @@ pub trait Daemon: Debug { (0..tx.tx.output.len()) .map(|vout| { OutPoint::new( - tx.tx.txid(), + tx.tx.compute_txid(), vout.try_into() .expect("number of transaction outputs must fit in u32"), ) @@ -220,7 +220,7 @@ pub trait Daemon: Debug { let mut tx_coins = Vec::new(); let mut change_indexes = Vec::new(); for coin in &coins { - if coin.outpoint.txid == tx.tx.txid() { + if coin.outpoint.txid == tx.tx.compute_txid() { change_indexes.push(coin.outpoint.vout as usize) } else if tx .tx @@ -298,7 +298,7 @@ pub trait Daemon: Debug { let mut tx_coins = Vec::new(); let mut change_indexes = Vec::new(); for coin in &coins { - if coin.outpoint.txid == tx.tx.txid() { + if coin.outpoint.txid == tx.tx.compute_txid() { change_indexes.push(coin.outpoint.vout as usize) } else if tx .tx diff --git a/liana-gui/src/daemon/model.rs b/liana-gui/src/daemon/model.rs index 34af3d057..c4657ba90 100644 --- a/liana-gui/src/daemon/model.rs +++ b/liana-gui/src/daemon/model.rs @@ -92,7 +92,7 @@ impl SpendTx { let mut coins_map = HashMap::::with_capacity(coins.len()); for coin in coins { if let Some(info) = coin.spend_info { - if info.txid == psbt.unsigned_tx.txid() { + if info.txid == psbt.unsigned_tx.compute_txid() { if info.height.is_some() { status = SpendStatus::Spent } else { @@ -158,7 +158,7 @@ impl SpendTx { .filter_map(|(i, _)| { if !change_indexes.contains(&i) { Some(OutPoint { - txid: psbt.unsigned_tx.txid(), + txid: psbt.unsigned_tx.compute_txid(), vout: i as u32, }) } else { @@ -247,7 +247,7 @@ impl Labelled for SpendTx { } fn labelled(&self) -> Vec { let mut items = Vec::new(); - let txid = self.psbt.unsigned_tx.txid(); + let txid = self.psbt.unsigned_tx.compute_txid(); items.push(LabelItem::Txid(txid)); for coin in self.coins.values() { items.push(LabelItem::Address(coin.address.clone())); @@ -307,7 +307,7 @@ impl HistoryTransaction { let kind = if coins.is_empty() { if change_indexes.len() == 1 { TransactionKind::IncomingSinglePayment(OutPoint { - txid: tx.txid(), + txid: tx.compute_txid(), vout: change_indexes[0] as u32, }) } else { @@ -315,7 +315,7 @@ impl HistoryTransaction { change_indexes .iter() .map(|i| OutPoint { - txid: tx.txid(), + txid: tx.compute_txid(), vout: *i as u32, }) .collect(), @@ -331,7 +331,7 @@ impl HistoryTransaction { .filter_map(|(i, _)| { if !change_indexes.contains(&i) { Some(OutPoint { - txid: tx.txid(), + txid: tx.compute_txid(), vout: i as u32, }) } else { @@ -356,7 +356,7 @@ impl HistoryTransaction { Self { labels: HashMap::new(), kind, - txid: tx.txid(), + txid: tx.compute_txid(), tx, coins: coins_map, change_indexes, @@ -481,7 +481,7 @@ pub fn payments_from_tx(history_tx: HistoryTransaction) -> Vec { return array; } let outpoint = OutPoint { - txid: history_tx.tx.txid(), + txid: history_tx.tx.compute_txid(), vout: output_index as u32, }; let label = history_tx.labels.get(&outpoint.to_string()).cloned(); @@ -528,7 +528,7 @@ impl Labelled for HistoryTransaction { } fn labelled(&self) -> Vec { let mut items = Vec::new(); - let txid = self.tx.txid(); + let txid = self.tx.compute_txid(); items.push(LabelItem::Txid(txid)); for coin in self.coins.values() { items.push(LabelItem::Address(coin.address.clone())); diff --git a/liana-gui/src/installer/step/descriptor/editor/key.rs b/liana-gui/src/installer/step/descriptor/editor/key.rs index d0af90cbb..c8916b53b 100644 --- a/liana-gui/src/installer/step/descriptor/editor/key.rs +++ b/liana-gui/src/installer/step/descriptor/editor/key.rs @@ -56,16 +56,16 @@ pub fn check_key_network(key: &DescriptorPublicKey, network: Network) -> bool { match key { DescriptorPublicKey::XPub(key) => { if network == Network::Bitcoin { - key.xkey.network == Network::Bitcoin + key.xkey.network == Network::Bitcoin.into() } else { - key.xkey.network == Network::Testnet + key.xkey.network == Network::Testnet.into() } } DescriptorPublicKey::MultiXPub(key) => { if network == Network::Bitcoin { - key.xkey.network == Network::Bitcoin + key.xkey.network == Network::Bitcoin.into() } else { - key.xkey.network == Network::Testnet + key.xkey.network == Network::Testnet.into() } } _ => true, @@ -210,9 +210,9 @@ impl super::DescriptorEditModal for EditXpubModal { let fingerprint = self.hot_signer.lock().unwrap().fingerprint(); let derivation_path = default_derivation_path(self.network); let key_str = format!( - "[{}{}]{}", + "[{}/{}]{}", fingerprint, - derivation_path.to_string().trim_start_matches('m'), + derivation_path.to_string().trim_start_matches("m/"), self.hot_signer .lock() .unwrap() @@ -274,9 +274,9 @@ impl super::DescriptorEditModal for EditXpubModal { self.form_xpub.valid = false; } else if let Some((fingerprint, _)) = key.origin { self.form_xpub.valid = if self.network == Network::Bitcoin { - key.xkey.network == Network::Bitcoin + key.xkey.network == Network::Bitcoin.into() } else { - key.xkey.network == Network::Testnet + key.xkey.network == Network::Testnet.into() }; if self.form_xpub.valid { self.chosen_signer = Some(Key { @@ -398,6 +398,8 @@ impl super::DescriptorEditModal for EditXpubModal { } pub fn default_derivation_path(network: Network) -> DerivationPath { + // Note that "m" is ignored when parsing string and could be removed: + // https://github.com/rust-bitcoin/rust-bitcoin/pull/2677 DerivationPath::from_str({ if network == Network::Bitcoin { "m/48'/0'/0'/2'" @@ -427,3 +429,28 @@ pub async fn get_extended_pubkey( xkey, })) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_default_derivation_path() { + assert_eq!( + default_derivation_path(Network::Bitcoin).to_string(), + "48'/0'/0'/2'" + ); + assert_eq!( + default_derivation_path(Network::Testnet).to_string(), + "48'/1'/0'/2'" + ); + assert_eq!( + default_derivation_path(Network::Signet).to_string(), + "48'/1'/0'/2'" + ); + assert_eq!( + default_derivation_path(Network::Regtest).to_string(), + "48'/1'/0'/2'" + ); + } +} diff --git a/liana-gui/src/installer/step/share_xpubs.rs b/liana-gui/src/installer/step/share_xpubs.rs index a219a71e1..8f4c61262 100644 --- a/liana-gui/src/installer/step/share_xpubs.rs +++ b/liana-gui/src/installer/step/share_xpubs.rs @@ -54,9 +54,9 @@ impl SignerXpubs { let derivation_path = default_derivation_path(network); // We keep only one for the moment. self.xpubs = vec![format!( - "[{}{}]{}", + "[{}/{}]{}", signer.fingerprint(), - derivation_path.to_string().trim_start_matches('m'), + derivation_path.to_string().trim_start_matches("m/"), signer.get_extended_pubkey(&derivation_path) )]; } diff --git a/liana-gui/src/lianalite/client/backend/mod.rs b/liana-gui/src/lianalite/client/backend/mod.rs index 54b9e36b0..de40f317e 100644 --- a/liana-gui/src/lianalite/client/backend/mod.rs +++ b/liana-gui/src/lianalite/client/backend/mod.rs @@ -1140,7 +1140,7 @@ fn history_tx_from_api(value: api::Transaction, network: Network) -> HistoryTran } } let mut changes_indexes = Vec::new(); - let txid = value.raw.txid().to_string(); + let txid = value.raw.compute_txid().to_string(); for (index, output) in value.outputs.iter().enumerate() { labels.insert(format!("{}:{}", txid, index), output.label.clone()); if let Some(address) = &output.address { @@ -1196,7 +1196,7 @@ fn spend_tx_from_api( } } let mut changes_indexes = Vec::new(); - let txid = value.raw.unsigned_tx.txid().to_string(); + let txid = value.raw.unsigned_tx.compute_txid().to_string(); for (index, output) in value.outputs.iter().enumerate() { labels.insert(format!("{}:{}", txid, index), output.label.clone()); if let Some(address) = &output.address { diff --git a/liana-ui/Cargo.toml b/liana-ui/Cargo.toml index 6b09d974a..458ac0318 100644 --- a/liana-ui/Cargo.toml +++ b/liana-ui/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" [dependencies] iced = { version = "0.12.1", default-features = false, features = ["svg", "image", "lazy", "qr_code", "advanced", "webgl"] } -bitcoin = "0.31" +bitcoin = "0.32" chrono = "0.4" diff --git a/liana/Cargo.toml b/liana/Cargo.toml index e0529dea8..e25063f54 100644 --- a/liana/Cargo.toml +++ b/liana/Cargo.toml @@ -10,7 +10,7 @@ description = "Liana development kit" [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/liana/src/descriptors/analysis.rs b/liana/src/descriptors/analysis.rs index 884761102..e724a3469 100644 --- a/liana/src/descriptors/analysis.rs +++ b/liana/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,13 @@ 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() => { + 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(); @@ -210,7 +214,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 +222,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 +352,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))) + .collect(), + ) + .map_err(|e| LianaPolicyError::InvalidPolicy(miniscript::Error::Threshold(e)))?, ), - } + }) } } @@ -586,13 +595,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. @@ -609,15 +615,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 @@ -626,7 +640,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); } @@ -669,7 +683,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, @@ -677,18 +694,21 @@ 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(); + .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()), + ])) }) } @@ -713,12 +733,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())) @@ -743,7 +763,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/liana/src/descriptors/mod.rs b/liana/src/descriptors/mod.rs index e628d92a2..070d954b2 100644 --- a/liana/src/descriptors/mod.rs +++ b/liana/src/descriptors/mod.rs @@ -203,7 +203,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 } @@ -299,10 +299,14 @@ impl LianaDescriptor { // empty 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.") } } @@ -810,7 +814,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( @@ -818,7 +822,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 @@ -856,7 +860,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( @@ -864,7 +868,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/liana/src/signer.rs b/liana/src/signer.rs index a542b116e..bbf1b2ce5 100644 --- a/liana/src/signer.rs +++ b/liana/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(); } } @@ -582,7 +582,7 @@ mod tests { "bc1qvklensptw5lk7d470ds60pcpsr0psdpgyvwepv", ) .unwrap() - .payload() + .assume_checked() .script_pubkey(), }], }, @@ -842,7 +842,7 @@ mod tests { "bc1qvklensptw5lk7d470ds60pcpsr0psdpgyvwepv", ) .unwrap() - .payload() + .assume_checked() .script_pubkey(), }], }, diff --git a/liana/src/spend.rs b/liana/src/spend.rs index 2db855aca..3f9cb2244 100644 --- a/liana/src/spend.rs +++ b/liana/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/lianad/Cargo.toml b/lianad/Cargo.toml index 4aefe8f13..02e32d408 100644 --- a/lianad/Cargo.toml +++ b/lianad/Cargo.toml @@ -23,11 +23,10 @@ nonblocking_shutdown = [] [dependencies] liana = { path = "../liana" } # 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"] } -# For Electrum backend. This is the latest version with the same bitcoin version as -# the miniscript dependency. -bdk_electrum = { version = "0.14" } +# For Electrum backend. +bdk_electrum = { version = "0.15" } # Don't reinvent the wheel dirs = "5.0" diff --git a/lianad/src/bitcoin/electrum/client.rs b/lianad/src/bitcoin/electrum/client.rs index eec542f1f..bd1a3dd54 100644 --- a/lianad/src/bitcoin/electrum/client.rs +++ b/lianad/src/bitcoin/electrum/client.rs @@ -8,7 +8,7 @@ use bdk_electrum::{ BlockId, ChainPosition, ConfirmationHeightAnchor, TxGraph, }, electrum_client::{self, Config, ElectrumApi}, - ElectrumExt, + BdkElectrumClient, }; use super::utils::{ @@ -50,7 +50,7 @@ impl std::fmt::Display for Error { } } -pub struct Client(electrum_client::Client); +pub struct Client(BdkElectrumClient); impl Client { /// Create a new client and perform sanity checks. @@ -70,11 +70,13 @@ impl Client { let client = bdk_electrum::electrum_client::Client::from_config(&electrum_config.addr, config) .map_err(Error::Server)?; - Ok(Self(client)) + let bdk_electrum_client = BdkElectrumClient::new(client); + Ok(Self(bdk_electrum_client)) } pub fn chain_tip(&self) -> Result { self.0 + .inner .block_headers_subscribe() .map_err(Error::Server) .map(|notif| BlockChainTip { @@ -84,7 +86,7 @@ impl Client { } fn genesis_block_header(&self) -> Result { - self.0.block_header(0).map_err(Error::Server) + self.0.inner.block_header(0).map_err(Error::Server) } pub fn genesis_block_timestamp(&self) -> Result { @@ -105,11 +107,17 @@ impl Client { pub fn tip_time(&self) -> Result { let tip_height = self.chain_tip()?.height; self.0 + .inner .block_header(height_usize_from_i32(tip_height)) .map_err(Error::Server) .map(|bh| bh.time) } + /// Returns a reference to the wrapped `BdkElectrumClient`. + pub fn bdk_electrum_client(&self) -> &BdkElectrumClient { + &self.0 + } + fn sync_with_confirmation_height_anchor( &self, request: SyncRequest, @@ -207,9 +215,9 @@ impl Client { // As they are descendants, we can assume they are all unconfirmed. while !desc_ops.is_empty() { log::debug!("Syncing descendant outpoints: {:?}", desc_ops); - let request = SyncRequest::from_chain_tip(local_chain.tip()) - .cache_graph_txs(&graph) - .chain_outpoints(desc_ops.clone()); + self.0.populate_tx_cache(&graph); + let request = + SyncRequest::from_chain_tip(local_chain.tip()).chain_outpoints(desc_ops.clone()); // Fetch prev txouts to ensure we have all required txs in the graph to calculate fees. // An unconfirmed descendant may have a confirmed parent that we wouldn't have in our graph. let sync_result = self.sync_with_confirmation_height_anchor(request, true)?; @@ -256,9 +264,9 @@ impl Client { .collect(); while !anc_txids.is_empty() { log::debug!("Syncing ancestor txids: {:?}", anc_txids); - let request = SyncRequest::from_chain_tip(local_chain.tip()) - .cache_graph_txs(&graph) - .chain_txids(anc_txids.clone()); + self.0.populate_tx_cache(&graph); + let request = + SyncRequest::from_chain_tip(local_chain.tip()).chain_txids(anc_txids.clone()); // We expect to have prev txouts for all unconfirmed ancestors in our graph so no need to fetch them here. // Note we keep iterating through ancestors until we find one that is confirmed and only need to calculate // fees for unconfirmed transactions. @@ -315,7 +323,9 @@ impl Client { let mut anc_fees = base_fee; // Ancestor size includes that of `txid`. let mut anc_size = base_size; - for desc_txid in graph.walk_descendants(tx.txid(), |_, desc_txid| Some(desc_txid)) { + for desc_txid in + graph.walk_descendants(tx.compute_txid(), |_, desc_txid| Some(desc_txid)) + { log::debug!("Getting fee for desc txid '{}'.", desc_txid); let desc_tx = graph .get_tx(desc_txid) @@ -326,11 +336,14 @@ impl Client { desc_fees += fee; } for anc_tx in graph.walk_ancestors(tx, |_, anc_tx| Some(anc_tx)) { - log::debug!("Getting fee and size for anc txid '{}'.", anc_tx.txid()); + log::debug!( + "Getting fee and size for anc txid '{}'.", + anc_tx.compute_txid() + ); if let Some(ChainPosition::Unconfirmed(_)) = graph.get_chain_position( &local_chain, local_chain.tip().block_id(), - anc_tx.txid(), + anc_tx.compute_txid(), ) { let fee = graph .calculate_fee(&anc_tx) @@ -338,14 +351,17 @@ impl Client { anc_fees += fee; anc_size += anc_tx.vsize(); } else { - log::debug!("Ancestor txid '{}' is not unconfirmed.", anc_tx.txid()); + log::debug!( + "Ancestor txid '{}' is not unconfirmed.", + anc_tx.compute_txid() + ); continue; } } let fees = MempoolEntryFees { - base: bitcoin::Amount::from_sat(base_fee), - ancestor: bitcoin::Amount::from_sat(anc_fees), - descendant: bitcoin::Amount::from_sat(desc_fees), + base: base_fee, + ancestor: anc_fees, + descendant: desc_fees, }; let entry = MempoolEntry { vsize: base_size.try_into().expect("tx size must fit into u64"), diff --git a/lianad/src/bitcoin/electrum/mod.rs b/lianad/src/bitcoin/electrum/mod.rs index 9d5749193..96b9b77ac 100644 --- a/lianad/src/bitcoin/electrum/mod.rs +++ b/lianad/src/bitcoin/electrum/mod.rs @@ -137,10 +137,13 @@ impl Electrum { const FETCH_PREV_TXOUTS: bool = false; const STOP_GAP: usize = 200; + // TODO: See if this caching can be done in a more optimal way, e.g. only new txs after syncing. + self.client + .bdk_electrum_client() + .populate_tx_cache(self.bdk_wallet.graph()); let (chain_update, mut graph_update, keychain_update) = if !self.is_rescanning() { log::debug!("Performing sync."); - let mut request = SyncRequest::from_chain_tip(local_chain_tip.clone()) - .cache_graph_txs(self.bdk_wallet.graph()); + let mut request = SyncRequest::from_chain_tip(local_chain_tip.clone()); let all_spks: Vec<_> = self .bdk_wallet @@ -162,8 +165,7 @@ impl Electrum { } else { log::info!("Performing full scan."); // Either local_chain has height 0 or we want to trigger a full scan. - let mut request = FullScanRequest::from_chain_tip(local_chain_tip.clone()) - .cache_graph_txs(self.bdk_wallet.graph()); + let mut request = FullScanRequest::from_chain_tip(local_chain_tip.clone()); for (k, spks) in self.bdk_wallet.index().all_unbounded_spk_iters() { request = request.set_spks_for_keychain(k, spks); @@ -228,7 +230,7 @@ impl Electrum { // so that conflicts can be properly handled. We use `sync_count` instead of current time // in seconds to ensure strictly increasing values between poller iterations. for tx in &graph_update.initial_changeset().txs { - let txid = tx.txid(); + let txid = tx.compute_txid(); if let Some(ChainPosition::Unconfirmed(_)) = graph_update.get_chain_position( self.local_chain(), self.local_chain().tip().block_id(), diff --git a/lianad/src/bitcoin/electrum/utils.rs b/lianad/src/bitcoin/electrum/utils.rs index 2da51983b..3c1c9ee10 100644 --- a/lianad/src/bitcoin/electrum/utils.rs +++ b/lianad/src/bitcoin/electrum/utils.rs @@ -46,7 +46,7 @@ pub fn block_info_from_anchor(anchor: ConfirmationTimeHeightAnchor) -> BlockInfo /// Get the transaction's outpoints. pub fn outpoints_from_tx(tx: &bitcoin::Transaction) -> Vec { - let txid = tx.txid(); + let txid = tx.compute_txid(); (0..tx.output.len()) .map(|i| { bitcoin::OutPoint::new(txid, i.try_into().expect("num tx outputs must fit in u32")) diff --git a/lianad/src/bitcoin/electrum/wallet.rs b/lianad/src/bitcoin/electrum/wallet.rs index 013a05641..91c23f74e 100644 --- a/lianad/src/bitcoin/electrum/wallet.rs +++ b/lianad/src/bitcoin/electrum/wallet.rs @@ -206,8 +206,11 @@ impl BdkWallet { let tx_graph = self.graph.graph(); let txo_index = &self.graph.index; let tip_id = self.local_chain.tip().block_id(); - let wallet_txos = - tx_graph.filter_chain_txouts(&self.local_chain, tip_id, txo_index.outpoints()); + let wallet_txos = tx_graph.filter_chain_txouts( + &self.local_chain, + tip_id, + txo_index.outpoints().iter().copied(), + ); let mut wallet_coins = HashMap::new(); // Go through all the wallet txos and create a coin for each. for ((k, i), full_txo) in wallet_txos { diff --git a/lianad/src/commands/mod.rs b/lianad/src/commands/mod.rs index d779323a8..4f4c6de14 100644 --- a/lianad/src/commands/mod.rs +++ b/lianad/src/commands/mod.rs @@ -48,7 +48,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, @@ -630,7 +630,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() { @@ -711,7 +711,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; } } @@ -1453,7 +1453,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(); @@ -1522,7 +1522,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); @@ -1572,13 +1572,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::error::ParseError::NetworkValidation { .. } )) )); @@ -1599,7 +1599,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 @@ -1818,7 +1818,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().script_pubkey() ); assert_eq!(tx_auto.output[0].value, Amount::from_sat(80_000)); @@ -2053,7 +2053,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() @@ -2062,7 +2062,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() @@ -2071,7 +2071,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(); @@ -2139,7 +2139,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(); @@ -2246,7 +2246,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(), @@ -2271,14 +2271,14 @@ 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()), is_from_self: false, }, // Deposit 2 @@ -2286,7 +2286,7 @@ mod tests { 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 }), @@ -2300,7 +2300,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), @@ -2313,7 +2313,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 }), @@ -2327,7 +2327,7 @@ mod tests { let mut txs_map = HashMap::new(); txs_map.insert( - deposit1.txid(), + deposit1.compute_txid(), ( deposit1.clone(), Some(Block { @@ -2341,7 +2341,7 @@ mod tests { ), ); txs_map.insert( - deposit2.txid(), + deposit2.compute_txid(), ( deposit2.clone(), Some(Block { @@ -2355,7 +2355,7 @@ mod tests { ), ); txs_map.insert( - spend_tx.txid(), + spend_tx.compute_txid(), ( spend_tx.clone(), Some(Block { @@ -2369,7 +2369,7 @@ mod tests { ), ); txs_map.insert( - deposit3.txid(), + deposit3.compute_txid(), ( deposit3.clone(), Some(Block { @@ -2478,7 +2478,7 @@ mod tests { let mut txs_map = HashMap::new(); txs_map.insert( - tx1.txid(), + tx1.compute_txid(), ( tx1.clone(), Some(Block { @@ -2492,7 +2492,7 @@ mod tests { ), ); txs_map.insert( - tx2.txid(), + tx2.compute_txid(), ( tx2.clone(), Some(Block { @@ -2506,7 +2506,7 @@ mod tests { ), ); txs_map.insert( - tx3.txid(), + tx3.compute_txid(), ( tx3.clone(), Some(Block { @@ -2547,12 +2547,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/lianad/src/database/sqlite/mod.rs b/lianad/src/database/sqlite/mod.rs index 93e49255d..067c3b711 100644 --- a/lianad/src/database/sqlite/mod.rs +++ b/lianad/src/database/sqlite/mod.rs @@ -611,7 +611,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( @@ -752,7 +752,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, num_inputs, num_outputs, is_coinbase) \ @@ -893,7 +893,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" @@ -1430,7 +1430,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, @@ -1477,7 +1477,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, @@ -1558,7 +1558,7 @@ CREATE TABLE labels ( .all(|c| [coin_a.outpoint, coin_b.outpoint].contains(&c.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]), @@ -1581,7 +1581,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, )]); @@ -1612,7 +1612,7 @@ CREATE TABLE labels ( .all(|c| [coin_a.outpoint, coin_b.outpoint].contains(&c.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, @@ -1624,7 +1624,7 @@ CREATE TABLE labels ( spend_block: None, is_from_self: false, }; - 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, @@ -1668,7 +1668,7 @@ CREATE TABLE labels ( .all(|c| [coin_b.outpoint, coin_c.outpoint].contains(&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], &[]), @@ -1741,7 +1741,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), @@ -1784,7 +1784,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), @@ -1837,7 +1837,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() @@ -1855,7 +1855,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() @@ -1876,7 +1876,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() @@ -1908,7 +1913,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), @@ -2080,7 +2085,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), @@ -2091,7 +2096,7 @@ CREATE TABLE labels ( is_from_self: false, }, 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, @@ -2105,7 +2110,7 @@ CREATE TABLE labels ( is_from_self: false, }, 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, @@ -2114,7 +2119,7 @@ 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, @@ -2122,7 +2127,7 @@ CREATE TABLE labels ( is_from_self: false, }, 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, @@ -2136,7 +2141,7 @@ CREATE TABLE labels ( is_from_self: false, }, 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, @@ -2145,7 +2150,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, @@ -2288,7 +2293,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), @@ -2299,7 +2304,7 @@ CREATE TABLE labels ( is_from_self: false, }, 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, @@ -2313,7 +2318,7 @@ CREATE TABLE labels ( is_from_self: false, }, 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, @@ -2322,7 +2327,7 @@ 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, @@ -2330,7 +2335,7 @@ CREATE TABLE labels ( is_from_self: false, }, 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, @@ -2344,7 +2349,7 @@ CREATE TABLE labels ( is_from_self: false, }, 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, @@ -2353,7 +2358,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, @@ -2381,12 +2386,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,); } @@ -2412,7 +2417,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), @@ -2423,7 +2428,7 @@ CREATE TABLE labels ( is_from_self: false, }, 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, @@ -2437,7 +2442,7 @@ CREATE TABLE labels ( is_from_self: false, }, 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, @@ -2446,7 +2451,7 @@ 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, @@ -2454,7 +2459,7 @@ CREATE TABLE labels ( is_from_self: false, }, 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, @@ -2468,7 +2473,7 @@ CREATE TABLE labels ( is_from_self: false, }, 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, @@ -2477,7 +2482,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, @@ -2529,7 +2534,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,); } @@ -2579,7 +2584,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, @@ -2595,7 +2600,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 }, @@ -2648,8 +2653,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() @@ -2705,14 +2712,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[..],); } } @@ -2742,7 +2757,7 @@ CREATE TABLE labels ( let tx_a = dummy_tx(1, 0); let tx_b = dummy_tx(1, 1); let coin_tx_a: Coin = Coin { - outpoint: bitcoin::OutPoint::new(tx_a.txid(), 0), + outpoint: bitcoin::OutPoint::new(tx_a.compute_txid(), 0), is_immature: false, amount: bitcoin::Amount::from_sat(1_000_000), derivation_index: bip32::ChildNumber::from_normal_idx(0).unwrap(), @@ -2753,7 +2768,7 @@ CREATE TABLE labels ( is_from_self: false, }; let coin_tx_b: Coin = Coin { - outpoint: bitcoin::OutPoint::new(tx_b.txid(), 0), + outpoint: bitcoin::OutPoint::new(tx_b.compute_txid(), 0), is_immature: false, amount: bitcoin::Amount::from_sat(1_000_000), derivation_index: bip32::ChildNumber::from_normal_idx(1).unwrap(), @@ -2776,7 +2791,7 @@ CREATE TABLE labels ( // Spend `coin_tx_a` in `tx_c` with change `coin_tx_c`. let tx_c = dummy_tx(1, 2); let coin_tx_c: Coin = Coin { - outpoint: bitcoin::OutPoint::new(tx_c.txid(), 0), + outpoint: bitcoin::OutPoint::new(tx_c.compute_txid(), 0), is_immature: false, amount: bitcoin::Amount::from_sat(1_000_000), derivation_index: bip32::ChildNumber::from_normal_idx(2).unwrap(), @@ -2787,7 +2802,7 @@ CREATE TABLE labels ( is_from_self: false, }; conn.new_txs(&[tx_c.clone()]); - conn.spend_coins(&[(coin_tx_a.outpoint, tx_c.txid())]); + conn.spend_coins(&[(coin_tx_a.outpoint, tx_c.compute_txid())]); conn.new_unspent_coins(&[coin_tx_c]); // Although `coin_tx_c` has only one parent, `coin_tx_a` is @@ -2800,7 +2815,7 @@ CREATE TABLE labels ( // Now refresh `coin_tx_c` in `tx_d`, creating `coin_tx_d`. let tx_d = dummy_tx(1, 3); let coin_tx_d: Coin = Coin { - outpoint: bitcoin::OutPoint::new(tx_d.txid(), 0), + outpoint: bitcoin::OutPoint::new(tx_d.compute_txid(), 0), is_immature: false, amount: bitcoin::Amount::from_sat(1_000_000), derivation_index: bip32::ChildNumber::from_normal_idx(3).unwrap(), @@ -2811,7 +2826,7 @@ CREATE TABLE labels ( is_from_self: false, }; conn.new_txs(&[tx_d.clone()]); - conn.spend_coins(&[(coin_tx_c.outpoint, tx_d.txid())]); + conn.spend_coins(&[(coin_tx_c.outpoint, tx_d.compute_txid())]); conn.new_unspent_coins(&[coin_tx_d]); // All coins are unconfirmed and none are from self. @@ -2823,7 +2838,7 @@ CREATE TABLE labels ( // together in `tx_e`, creating `coin_tx_e`. let tx_e = dummy_tx(2, 4); // 2 inputs let coin_tx_e: Coin = Coin { - outpoint: bitcoin::OutPoint::new(tx_e.txid(), 0), + outpoint: bitcoin::OutPoint::new(tx_e.compute_txid(), 0), is_immature: false, amount: bitcoin::Amount::from_sat(1_000_000), derivation_index: bip32::ChildNumber::from_normal_idx(4).unwrap(), @@ -2835,8 +2850,8 @@ CREATE TABLE labels ( }; conn.new_txs(&[tx_e.clone()]); conn.spend_coins(&[ - (coin_tx_b.outpoint, tx_e.txid()), - (coin_tx_d.outpoint, tx_e.txid()), + (coin_tx_b.outpoint, tx_e.compute_txid()), + (coin_tx_d.outpoint, tx_e.compute_txid()), ]); conn.new_unspent_coins(&[coin_tx_e]); @@ -2848,7 +2863,7 @@ CREATE TABLE labels ( // Finally, refresh `coin_tx_e` in transaction `tx_f`, creating `coin_tx_f`. let tx_f = dummy_tx(1, 5); let coin_tx_f: Coin = Coin { - outpoint: bitcoin::OutPoint::new(tx_f.txid(), 0), + outpoint: bitcoin::OutPoint::new(tx_f.compute_txid(), 0), is_immature: false, amount: bitcoin::Amount::from_sat(1_000_000), derivation_index: bip32::ChildNumber::from_normal_idx(5).unwrap(), @@ -2859,7 +2874,7 @@ CREATE TABLE labels ( is_from_self: false, }; conn.new_txs(&[tx_f.clone()]); - conn.spend_coins(&[(coin_tx_e.outpoint, tx_f.txid())]); + conn.spend_coins(&[(coin_tx_e.outpoint, tx_f.compute_txid())]); conn.new_unspent_coins(&[coin_tx_f]); // Still no coins are from self. @@ -2872,7 +2887,7 @@ CREATE TABLE labels ( (coin_tx_a.outpoint, 100, 1_000), (coin_tx_c.outpoint, 101, 1_001), ]); - conn.confirm_spend(&[(coin_tx_a.outpoint, tx_c.txid(), 101, 1_001)]); + conn.confirm_spend(&[(coin_tx_a.outpoint, tx_c.compute_txid(), 101, 1_001)]); // Coins are still not marked as from self. assert!(conn.coins(&[], &[]).iter().all(|c| !c.is_from_self)); // Now update from self for coins confirmed after 101, which excludes the two coins above. @@ -3082,7 +3097,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( @@ -3134,14 +3149,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, @@ -3190,7 +3205,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), @@ -3269,12 +3284,14 @@ CREATE TABLE labels ( // - coin_e is the unconfirmed output of coin_d's spend. // - coin_imma_a is confirmed. // - coin_imma_b is still immature. - let coin_d_outpoint = bitcoin::OutPoint::new(bitcoin_txs.get(3).unwrap().txid(), 1456); - let coin_e_outpoint = bitcoin::OutPoint::new(bitcoin_txs.get(4).unwrap().txid(), 4633); + let coin_d_outpoint = + bitcoin::OutPoint::new(bitcoin_txs.get(3).unwrap().compute_txid(), 1456); + let coin_e_outpoint = + bitcoin::OutPoint::new(bitcoin_txs.get(4).unwrap().compute_txid(), 4633); let coin_a = DbCoinV3 { id: 1, wallet_id: WALLET_ID, - 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(), @@ -3283,7 +3300,7 @@ CREATE TABLE labels ( height: 175500, time: 1755001001, }), - spend_txid: Some(bitcoin_txs.get(7).unwrap().txid()), + spend_txid: Some(bitcoin_txs.get(7).unwrap().compute_txid()), spend_block: Some(DbBlockInfo { height: 245500, time: 1755003000, @@ -3292,7 +3309,7 @@ CREATE TABLE labels ( let coin_b = DbCoinV3 { id: 2, wallet_id: WALLET_ID, - 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(), @@ -3307,7 +3324,7 @@ CREATE TABLE labels ( let coin_c = DbCoinV3 { id: 3, wallet_id: WALLET_ID, - 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(), @@ -3346,7 +3363,7 @@ CREATE TABLE labels ( let coin_imma_a = DbCoinV3 { id: 6, wallet_id: WALLET_ID, - 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(), @@ -3361,7 +3378,7 @@ CREATE TABLE labels ( let coin_imma_b = DbCoinV3 { id: 7, wallet_id: WALLET_ID, - 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(), @@ -3470,7 +3487,7 @@ CREATE TABLE labels ( id: i.try_into().unwrap(), wallet_id: WALLET_ID, outpoint: bitcoin::OutPoint { - txid: tx.txid(), + txid: tx.compute_txid(), vout: i as u32, }, is_immature: (i % 10) == 0, @@ -3486,7 +3503,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 }, @@ -3528,7 +3545,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/lianad/src/database/sqlite/schema.rs b/lianad/src/database/sqlite/schema.rs index 0f4ec7a88..0b67675f2 100644 --- a/lianad/src/database/sqlite/schema.rs +++ b/lianad/src/database/sqlite/schema.rs @@ -322,7 +322,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/lianad/src/database/sqlite/utils.rs b/lianad/src/database/sqlite/utils.rs index ad7aab6bc..f881de4be 100644 --- a/lianad/src/database/sqlite/utils.rs +++ b/lianad/src/database/sqlite/utils.rs @@ -260,7 +260,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/lianad/src/testutils.rs b/lianad/src/testutils.rs index 75aa5ef10..aacf64a31 100644 --- a/lianad/src/testutils.rs +++ b/lianad/src/testutils.rs @@ -381,7 +381,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() @@ -480,7 +480,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()); } }