From c5f5284f57e37e578cc3001a7da1790c724437a3 Mon Sep 17 00:00:00 2001 From: Michael Mallan Date: Fri, 24 Jan 2025 13:57:29 +0000 Subject: [PATCH] upgrade bitcoin to 0.32 and related This upgrades bitcoin to 0.32 and related dependencies accordingly. Note that the bdk_electrum crate has not been upgraded to the most recent version available as the Electrum syncing there takes much longer due to the fetching and validation of Merkle proofs. There are ongoing BDK changes in relation to that and so we can further upgrade this dependency once those changes have been completed. Non-BDK changes to the liana and lianad crates are taken from darosior's commit 20ee8b1e7b77cbf65942bc4f083afea95f2d1506 in draft PR https://github.com/wizardsardine/liana/pull/1228. --- Cargo.lock | 280 ++++++++++++------ fuzz/Cargo.toml | 2 +- fuzz/fuzz_targets/descriptors.rs | 2 +- liana-gui/Cargo.toml | 2 +- liana-gui/src/app/state/psbt.rs | 7 +- liana-gui/src/app/state/psbts.rs | 3 +- liana-gui/src/app/state/spend/step.rs | 14 +- liana-gui/src/app/state/transactions.rs | 15 +- liana-gui/src/app/view/home.rs | 17 +- liana-gui/src/app/view/psbt.rs | 8 +- liana-gui/src/app/view/psbts.rs | 2 +- liana-gui/src/app/view/transactions.rs | 6 +- liana-gui/src/daemon/mod.rs | 8 +- liana-gui/src/daemon/model.rs | 18 +- .../installer/step/descriptor/editor/key.rs | 43 ++- liana-gui/src/installer/step/share_xpubs.rs | 4 +- liana-gui/src/lianalite/client/backend/mod.rs | 4 +- liana-ui/Cargo.toml | 2 +- liana/Cargo.toml | 2 +- liana/src/descriptors/analysis.rs | 112 ++++--- liana/src/descriptors/mod.rs | 18 +- liana/src/signer.rs | 34 +-- liana/src/spend.rs | 2 +- lianad/Cargo.toml | 7 +- lianad/src/bitcoin/electrum/client.rs | 50 ++-- lianad/src/bitcoin/electrum/mod.rs | 12 +- lianad/src/bitcoin/electrum/utils.rs | 2 +- lianad/src/bitcoin/electrum/wallet.rs | 7 +- lianad/src/commands/mod.rs | 58 ++-- lianad/src/database/sqlite/mod.rs | 163 +++++----- lianad/src/database/sqlite/schema.rs | 2 +- lianad/src/database/sqlite/utils.rs | 2 +- lianad/src/testutils.rs | 8 +- 33 files changed, 561 insertions(+), 355 deletions(-) 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()); } }