Compare commits

..

3 Commits

Author SHA1 Message Date
Steve Myers
d288cbbbbc Bump version to 0.25.0 2022-12-05 14:28:30 -06:00
Steve Myers
005447c81e Downgrade ubuntu to 20.04 for test_hardware_wallet CI job 2022-12-05 13:54:05 -06:00
Steve Myers
5168d41a93 Bump version to 0.25.0-rc.1 2022-11-30 09:08:37 -08:00
20 changed files with 266 additions and 339 deletions

View File

@@ -18,7 +18,7 @@ jobs:
target target
key: nightly-docs-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }} key: nightly-docs-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
- name: Set default toolchain - name: Set default toolchain
run: rustup default nightly-2022-12-14 run: rustup default nightly-2022-01-25
- name: Set profile - name: Set profile
run: rustup set profile minimal run: rustup set profile minimal
- name: Update toolchain - name: Update toolchain

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "bdk" name = "bdk"
version = "0.26.0" version = "0.25.0"
edition = "2018" edition = "2018"
authors = ["Alekos Filini <alekos.filini@gmail.com>", "Riccardo Casatta <riccardo@casatta.it>"] authors = ["Alekos Filini <alekos.filini@gmail.com>", "Riccardo Casatta <riccardo@casatta.it>"]
homepage = "https://bitcoindevkit.org" homepage = "https://bitcoindevkit.org"
@@ -23,7 +23,7 @@ rand = "^0.8"
# Optional dependencies # Optional dependencies
sled = { version = "0.34", optional = true } sled = { version = "0.34", optional = true }
electrum-client = { version = "0.12", optional = true } electrum-client = { version = "0.12", optional = true }
esplora-client = { version = "0.3", default-features = false, optional = true } esplora-client = { version = "0.2", default-features = false, optional = true }
rusqlite = { version = "0.27.0", optional = true } rusqlite = { version = "0.27.0", optional = true }
ahash = { version = "0.7.6", optional = true } ahash = { version = "0.7.6", optional = true }
futures = { version = "0.3", optional = true } futures = { version = "0.3", optional = true }
@@ -31,7 +31,7 @@ async-trait = { version = "0.1", optional = true }
rocksdb = { version = "0.14", default-features = false, features = ["snappy"], optional = true } rocksdb = { version = "0.14", default-features = false, features = ["snappy"], optional = true }
cc = { version = ">=1.0.64", optional = true } cc = { version = ">=1.0.64", optional = true }
socks = { version = "0.3", optional = true } socks = { version = "0.3", optional = true }
hwi = { version = "0.4.0", optional = true, features = [ "use-miniscript"] } hwi = { version = "0.3.0", optional = true }
bip39 = { version = "1.0.1", optional = true } bip39 = { version = "1.0.1", optional = true }
bitcoinconsensus = { version = "0.19.0-3", optional = true } bitcoinconsensus = { version = "0.19.0-3", optional = true }
@@ -109,7 +109,6 @@ env_logger = "0.7"
electrsd = "0.21" electrsd = "0.21"
# Move back to importing from rust-bitcoin once https://github.com/rust-bitcoin/rust-bitcoin/pull/1342 is released # Move back to importing from rust-bitcoin once https://github.com/rust-bitcoin/rust-bitcoin/pull/1342 is released
base64 = "^0.13" base64 = "^0.13"
assert_matches = "1.5.0"
[[example]] [[example]]
name = "compact_filters_balance" name = "compact_filters_balance"

View File

@@ -68,13 +68,12 @@ fn main() -> Result<(), bdk::Error> {
```rust ```rust
use bdk::{Wallet, database::MemoryDatabase}; use bdk::{Wallet, database::MemoryDatabase};
use bdk::wallet::AddressIndex::New; use bdk::wallet::AddressIndex::New;
use bdk::bitcoin::Network;
fn main() -> Result<(), bdk::Error> { fn main() -> Result<(), bdk::Error> {
let wallet = Wallet::new( let wallet = Wallet::new(
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"), Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
Network::Testnet, bitcoin::Network::Testnet,
MemoryDatabase::default(), MemoryDatabase::default(),
)?; )?;
@@ -97,15 +96,14 @@ use bdk::electrum_client::Client;
use bdk::wallet::AddressIndex::New; use bdk::wallet::AddressIndex::New;
use base64; use base64;
use bdk::bitcoin::consensus::serialize; use bitcoin::consensus::serialize;
use bdk::bitcoin::Network;
fn main() -> Result<(), bdk::Error> { fn main() -> Result<(), bdk::Error> {
let blockchain = ElectrumBlockchain::from(Client::new("ssl://electrum.blockstream.info:60002")?); let blockchain = ElectrumBlockchain::from(Client::new("ssl://electrum.blockstream.info:60002")?);
let wallet = Wallet::new( let wallet = Wallet::new(
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"), Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
Network::Testnet, bitcoin::Network::Testnet,
MemoryDatabase::default(), MemoryDatabase::default(),
)?; )?;
@@ -135,21 +133,20 @@ fn main() -> Result<(), bdk::Error> {
use bdk::{Wallet, SignOptions, database::MemoryDatabase}; use bdk::{Wallet, SignOptions, database::MemoryDatabase};
use base64; use base64;
use bdk::bitcoin::consensus::deserialize; use bitcoin::consensus::deserialize;
use bdk::bitcoin::Network;
fn main() -> Result<(), bdk::Error> { fn main() -> Result<(), bdk::Error> {
let wallet = Wallet::new( let wallet = Wallet::new(
"wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/0/*)", "wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/0/*)",
Some("wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/1/*)"), Some("wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/1/*)"),
Network::Testnet, bitcoin::Network::Testnet,
MemoryDatabase::default(), MemoryDatabase::default(),
)?; )?;
let psbt = "..."; let psbt = "...";
let mut psbt = deserialize(&base64::decode(psbt).unwrap())?; let mut psbt = deserialize(&base64::decode(psbt).unwrap())?;
let _finalized = wallet.sign(&mut psbt, SignOptions::default())?; let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
Ok(()) Ok(())
} }

View File

@@ -2,7 +2,6 @@ use bdk::bitcoin::{Address, Network};
use bdk::blockchain::{Blockchain, ElectrumBlockchain}; use bdk::blockchain::{Blockchain, ElectrumBlockchain};
use bdk::database::MemoryDatabase; use bdk::database::MemoryDatabase;
use bdk::hwi::{types::HWIChain, HWIClient}; use bdk::hwi::{types::HWIChain, HWIClient};
use bdk::miniscript::{Descriptor, DescriptorPublicKey};
use bdk::signer::SignerOrdering; use bdk::signer::SignerOrdering;
use bdk::wallet::{hardwaresigner::HWISigner, AddressIndex}; use bdk::wallet::{hardwaresigner::HWISigner, AddressIndex};
use bdk::{FeeRate, KeychainKind, SignOptions, SyncOptions, Wallet}; use bdk::{FeeRate, KeychainKind, SignOptions, SyncOptions, Wallet};
@@ -24,27 +23,26 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Hold tight, I'm connecting to your hardware wallet..."); println!("Hold tight, I'm connecting to your hardware wallet...");
// Listing all the available hardware wallet devices... // Listing all the available hardware wallet devices...
let mut devices = HWIClient::enumerate()?; let devices = HWIClient::enumerate()?;
if devices.is_empty() { let first_device = devices
panic!("No devices found. Either plug in a hardware wallet, or start a simulator."); .first()
} .expect("No devices found. Either plug in a hardware wallet, or start a simulator.");
let first_device = devices.remove(0)?;
// ...and creating a client out of the first one // ...and creating a client out of the first one
let client = HWIClient::get_client(&first_device, true, HWIChain::Test)?; let client = HWIClient::get_client(first_device, true, HWIChain::Test)?;
println!("Look what I found, a {}!", first_device.model); println!("Look what I found, a {}!", first_device.model);
// Getting the HW's public descriptors // Getting the HW's public descriptors
let descriptors = client.get_descriptors::<Descriptor<DescriptorPublicKey>>(None)?; let descriptors = client.get_descriptors(None)?;
println!( println!(
"The hardware wallet's descriptor is: {}", "The hardware wallet's descriptor is: {}",
descriptors.receive[0] descriptors.receive[0]
); );
// Creating a custom signer from the device // Creating a custom signer from the device
let custom_signer = HWISigner::from_device(&first_device, HWIChain::Test)?; let custom_signer = HWISigner::from_device(first_device, HWIChain::Test)?;
let mut wallet = Wallet::new( let mut wallet = Wallet::new(
descriptors.receive[0].clone(), &descriptors.receive[0],
Some(descriptors.internal[0].clone()), Some(&descriptors.internal[0]),
Network::Testnet, Network::Testnet,
MemoryDatabase::default(), MemoryDatabase::default(),
)?; )?;

View File

@@ -178,8 +178,7 @@ impl_from!(boxed rpc::RpcBlockchain, AnyBlockchain, Rpc, #[cfg(feature = "rpc")]
/// "type" : "electrum", /// "type" : "electrum",
/// "url" : "ssl://electrum.blockstream.info:50002", /// "url" : "ssl://electrum.blockstream.info:50002",
/// "retry": 2, /// "retry": 2,
/// "stop_gap": 20, /// "stop_gap": 20
/// "validate_domain": true
/// }"#, /// }"#,
/// ) /// )
/// .unwrap(); /// .unwrap();
@@ -191,7 +190,6 @@ impl_from!(boxed rpc::RpcBlockchain, AnyBlockchain, Rpc, #[cfg(feature = "rpc")]
/// socks5: None, /// socks5: None,
/// timeout: None, /// timeout: None,
/// stop_gap: 20, /// stop_gap: 20,
/// validate_domain: true,
/// }) /// })
/// ); /// );
/// # } /// # }

View File

@@ -136,7 +136,7 @@ impl CfSync {
let resp = peer.get_cf_headers(0x00, start_height as u32, stop_hash)?; let resp = peer.get_cf_headers(0x00, start_height as u32, stop_hash)?;
assert_eq!(resp.previous_filter_header, checkpoint); assert!(resp.previous_filter_header == checkpoint);
status = status =
self.cf_store self.cf_store
.advance_to_cf_headers(index, checkpoint, resp.filter_hashes)?; .advance_to_cf_headers(index, checkpoint, resp.filter_hashes)?;

View File

@@ -281,11 +281,9 @@ impl<'a, 'b, D: Database> TxCache<'a, 'b, D> {
.client .client
.batch_transaction_get(need_fetch.clone()) .batch_transaction_get(need_fetch.clone())
.map_err(Error::Electrum)?; .map_err(Error::Electrum)?;
let mut txs: HashMap<_, _> = txs.into_iter().map(|tx| (tx.txid(), tx)).collect(); for (tx, _txid) in txs.into_iter().zip(need_fetch) {
for txid in need_fetch { debug_assert_eq!(*_txid, tx.txid());
if let Some(tx) = txs.remove(txid) { self.cache.insert(tx.txid(), tx);
self.cache.insert(*txid, tx);
}
} }
} }
@@ -312,8 +310,6 @@ pub struct ElectrumBlockchainConfig {
pub timeout: Option<u8>, pub timeout: Option<u8>,
/// Stop searching addresses for transactions after finding an unused gap of this length /// Stop searching addresses for transactions after finding an unused gap of this length
pub stop_gap: usize, pub stop_gap: usize,
/// Validate the domain when using SSL
pub validate_domain: bool,
} }
impl ConfigurableBlockchain for ElectrumBlockchain { impl ConfigurableBlockchain for ElectrumBlockchain {
@@ -325,7 +321,6 @@ impl ConfigurableBlockchain for ElectrumBlockchain {
.retry(config.retry) .retry(config.retry)
.timeout(config.timeout)? .timeout(config.timeout)?
.socks5(socks5)? .socks5(socks5)?
.validate_domain(config.validate_domain)
.build(); .build();
Ok(ElectrumBlockchain { Ok(ElectrumBlockchain {
@@ -420,7 +415,6 @@ mod test {
retry: 0, retry: 0,
timeout: None, timeout: None,
stop_gap: stop_gap, stop_gap: stop_gap,
validate_domain: true,
}) })
} }
} }

View File

@@ -1137,10 +1137,12 @@ pub mod test {
let child: u32 = row.get(1).unwrap(); let child: u32 = row.get(1).unwrap();
let count: usize = row.get(2).unwrap(); let count: usize = row.get(2).unwrap();
assert_eq!( assert!(
count, 1, count == 1,
"keychain={}, child={}, count={}", "keychain={}, child={}, count={}",
keychain, child, count keychain,
child,
count
); );
} }
} }

View File

@@ -133,7 +133,6 @@ pub fn get_checksum(desc: &str) -> Result<String, DescriptorError> {
mod test { mod test {
use super::*; use super::*;
use crate::descriptor::calc_checksum; use crate::descriptor::calc_checksum;
use assert_matches::assert_matches;
// test calc_checksum() function; it should return the same value as Bitcoin Core // test calc_checksum() function; it should return the same value as Bitcoin Core
#[test] #[test]
@@ -156,16 +155,16 @@ mod test {
assert_eq!(calc_checksum(desc).unwrap(), "lasegmfs"); assert_eq!(calc_checksum(desc).unwrap(), "lasegmfs");
let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#tqz0nc26"; let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#tqz0nc26";
assert_matches!( assert!(matches!(
calc_checksum(desc), calc_checksum(desc).err(),
Err(DescriptorError::InvalidDescriptorChecksum) Some(DescriptorError::InvalidDescriptorChecksum)
); ));
let desc = "pkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/44'/1'/0'/0/*)#lasegmsf"; let desc = "pkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/44'/1'/0'/0/*)#lasegmsf";
assert_matches!( assert!(matches!(
calc_checksum(desc), calc_checksum(desc).err(),
Err(DescriptorError::InvalidDescriptorChecksum) Some(DescriptorError::InvalidDescriptorChecksum)
); ));
} }
#[test] #[test]
@@ -173,9 +172,9 @@ mod test {
let sparkle_heart = unsafe { std::str::from_utf8_unchecked(&[240, 159, 146, 150]) }; let sparkle_heart = unsafe { std::str::from_utf8_unchecked(&[240, 159, 146, 150]) };
let invalid_desc = format!("wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcL{}fjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)", sparkle_heart); let invalid_desc = format!("wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcL{}fjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)", sparkle_heart);
assert_matches!( assert!(matches!(
calc_checksum(&invalid_desc), calc_checksum(&invalid_desc).err(),
Err(DescriptorError::InvalidDescriptorCharacter(invalid_char)) if invalid_char == sparkle_heart.as_bytes()[0] Some(DescriptorError::InvalidDescriptorCharacter(invalid_char)) if invalid_char == sparkle_heart.as_bytes()[0]
); ));
} }
} }

View File

@@ -581,7 +581,6 @@ impl DescriptorMeta for ExtendedDescriptor {
mod test { mod test {
use std::str::FromStr; use std::str::FromStr;
use assert_matches::assert_matches;
use bitcoin::consensus::encode::deserialize; use bitcoin::consensus::encode::deserialize;
use bitcoin::hashes::hex::FromHex; use bitcoin::hashes::hex::FromHex;
use bitcoin::secp256k1::Secp256k1; use bitcoin::secp256k1::Secp256k1;
@@ -764,11 +763,17 @@ mod test {
let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#67ju93jw" let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#67ju93jw"
.into_wallet_descriptor(&secp, Network::Testnet); .into_wallet_descriptor(&secp, Network::Testnet);
assert_matches!(desc, Err(DescriptorError::InvalidDescriptorChecksum)); assert!(matches!(
desc.err(),
Some(DescriptorError::InvalidDescriptorChecksum)
));
let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#67ju93jw" let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#67ju93jw"
.into_wallet_descriptor(&secp, Network::Testnet); .into_wallet_descriptor(&secp, Network::Testnet);
assert_matches!(desc, Err(DescriptorError::InvalidDescriptorChecksum)); assert!(matches!(
desc.err(),
Some(DescriptorError::InvalidDescriptorChecksum)
));
} }
// test IntoWalletDescriptor trait from &str with keys from right and wrong network // test IntoWalletDescriptor trait from &str with keys from right and wrong network
@@ -802,11 +807,17 @@ mod test {
let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)" let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)"
.into_wallet_descriptor(&secp, Network::Bitcoin); .into_wallet_descriptor(&secp, Network::Bitcoin);
assert_matches!(desc, Err(DescriptorError::Key(KeyError::InvalidNetwork))); assert!(matches!(
desc.err(),
Some(DescriptorError::Key(KeyError::InvalidNetwork))
));
let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)" let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)"
.into_wallet_descriptor(&secp, Network::Bitcoin); .into_wallet_descriptor(&secp, Network::Bitcoin);
assert_matches!(desc, Err(DescriptorError::Key(KeyError::InvalidNetwork))); assert!(matches!(
desc.err(),
Some(DescriptorError::Key(KeyError::InvalidNetwork))
));
} }
// test IntoWalletDescriptor trait from the output of the descriptor!() macro // test IntoWalletDescriptor trait from the output of the descriptor!() macro
@@ -840,7 +851,11 @@ mod test {
let descriptor = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0'/1/2/*)"; let descriptor = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0'/1/2/*)";
let result = into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet); let result = into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet);
assert_matches!(result, Err(DescriptorError::HardenedDerivationXpub)); assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
DescriptorError::HardenedDerivationXpub
));
let descriptor = "wsh(multi(2,tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0/*,tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0/*))"; let descriptor = "wsh(multi(2,tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0/*,tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0/*))";
let result = into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet); let result = into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet);

View File

@@ -1139,7 +1139,6 @@ mod test {
use crate::descriptor::policy::SatisfiableItem::{EcdsaSignature, Multisig, Thresh}; use crate::descriptor::policy::SatisfiableItem::{EcdsaSignature, Multisig, Thresh};
use crate::keys::{DescriptorKey, IntoDescriptorKey}; use crate::keys::{DescriptorKey, IntoDescriptorKey};
use crate::wallet::signer::SignersContainer; use crate::wallet::signer::SignersContainer;
use assert_matches::assert_matches;
use bitcoin::secp256k1::Secp256k1; use bitcoin::secp256k1::Secp256k1;
use bitcoin::util::bip32; use bitcoin::util::bip32;
use bitcoin::Network; use bitcoin::Network;
@@ -1183,8 +1182,8 @@ mod test {
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_matches!(&policy.item, EcdsaSignature(PkOrF::Fingerprint(f)) if f == &fingerprint); assert!(matches!(&policy.item, EcdsaSignature(PkOrF::Fingerprint(f)) if f == &fingerprint));
assert_matches!(&policy.contribution, Satisfaction::None); assert!(matches!(&policy.contribution, Satisfaction::None));
let desc = descriptor!(wpkh(prvkey)).unwrap(); let desc = descriptor!(wpkh(prvkey)).unwrap();
let (wallet_desc, keymap) = desc let (wallet_desc, keymap) = desc
@@ -1196,8 +1195,10 @@ mod test {
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_matches!(&policy.item, EcdsaSignature(PkOrF::Fingerprint(f)) if f == &fingerprint); assert!(matches!(&policy.item, EcdsaSignature(PkOrF::Fingerprint(f)) if f == &fingerprint));
assert_matches!(&policy.contribution, Satisfaction::Complete {condition} if condition.csv == None && condition.timelock == None); assert!(
matches!(&policy.contribution, Satisfaction::Complete {condition} if condition.csv == None && condition.timelock == None)
);
} }
// 2 pub keys descriptor, required 2 prv keys // 2 pub keys descriptor, required 2 prv keys
@@ -1216,16 +1217,19 @@ mod test {
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_matches!(&policy.item, Multisig { keys, threshold } if threshold == &2usize assert!(
matches!(&policy.item, Multisig { keys, threshold } if threshold == &2usize
&& keys[0] == PkOrF::Fingerprint(fingerprint0) && keys[0] == PkOrF::Fingerprint(fingerprint0)
&& keys[1] == PkOrF::Fingerprint(fingerprint1) && keys[1] == PkOrF::Fingerprint(fingerprint1))
); );
// TODO should this be "Satisfaction::None" since we have no prv keys? // TODO should this be "Satisfaction::None" since we have no prv keys?
// TODO should items and conditions not be empty? // TODO should items and conditions not be empty?
assert_matches!(&policy.contribution, Satisfaction::Partial { n, m, items, conditions, ..} if n == &2usize assert!(
matches!(&policy.contribution, Satisfaction::Partial { n, m, items, conditions, ..} if n == &2usize
&& m == &2usize && m == &2usize
&& items.is_empty() && items.is_empty()
&& conditions.is_empty() && conditions.is_empty()
)
); );
} }
@@ -1244,15 +1248,18 @@ mod test {
.extract_policy(&signers_container, BuildSatisfaction::None, &secp) .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_matches!(&policy.item, Multisig { keys, threshold } if threshold == &2usize assert!(
matches!(&policy.item, Multisig { keys, threshold } if threshold == &2usize
&& keys[0] == PkOrF::Fingerprint(fingerprint0) && keys[0] == PkOrF::Fingerprint(fingerprint0)
&& keys[1] == PkOrF::Fingerprint(fingerprint1) && keys[1] == PkOrF::Fingerprint(fingerprint1))
); );
assert_matches!(&policy.contribution, Satisfaction::Partial { n, m, items, conditions, ..} if n == &2usize assert!(
matches!(&policy.contribution, Satisfaction::Partial { n, m, items, conditions, ..} if n == &2usize
&& m == &2usize && m == &2usize
&& items.len() == 1 && items.len() == 1
&& conditions.contains_key(&0) && conditions.contains_key(&0)
)
); );
} }
@@ -1274,15 +1281,18 @@ mod test {
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_matches!(&policy.item, Multisig { keys, threshold } if threshold == &1 assert!(
matches!(&policy.item, Multisig { keys, threshold } if threshold == &1
&& keys[0] == PkOrF::Fingerprint(fingerprint0) && keys[0] == PkOrF::Fingerprint(fingerprint0)
&& keys[1] == PkOrF::Fingerprint(fingerprint1) && keys[1] == PkOrF::Fingerprint(fingerprint1))
); );
assert_matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &2 assert!(
matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &2
&& m == &1 && m == &1
&& items.len() == 2 && items.len() == 2
&& conditions.contains_key(&vec![0]) && conditions.contains_key(&vec![0])
&& conditions.contains_key(&vec![1]) && conditions.contains_key(&vec![1])
)
); );
} }
@@ -1303,15 +1313,18 @@ mod test {
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_matches!(&policy.item, Multisig { keys, threshold } if threshold == &2 assert!(
matches!(&policy.item, Multisig { keys, threshold } if threshold == &2
&& keys[0] == PkOrF::Fingerprint(fingerprint0) && keys[0] == PkOrF::Fingerprint(fingerprint0)
&& keys[1] == PkOrF::Fingerprint(fingerprint1) && keys[1] == PkOrF::Fingerprint(fingerprint1))
); );
assert_matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &2 assert!(
matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &2
&& m == &2 && m == &2
&& items.len() == 2 && items.len() == 2
&& conditions.contains_key(&vec![0,1]) && conditions.contains_key(&vec![0,1])
)
); );
} }
@@ -1332,8 +1345,8 @@ mod test {
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_matches!(&policy.item, EcdsaSignature(PkOrF::Fingerprint(f)) if f == &fingerprint); assert!(matches!(&policy.item, EcdsaSignature(PkOrF::Fingerprint(f)) if f == &fingerprint));
assert_matches!(&policy.contribution, Satisfaction::None); assert!(matches!(&policy.contribution, Satisfaction::None));
let desc = descriptor!(wpkh(prvkey)).unwrap(); let desc = descriptor!(wpkh(prvkey)).unwrap();
let (wallet_desc, keymap) = desc let (wallet_desc, keymap) = desc
@@ -1345,8 +1358,10 @@ mod test {
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_matches!(policy.item, EcdsaSignature(PkOrF::Fingerprint(f)) if f == fingerprint); assert!(matches!(policy.item, EcdsaSignature(PkOrF::Fingerprint(f)) if f == fingerprint));
assert_matches!(policy.contribution, Satisfaction::Complete {condition} if condition.csv == None && condition.timelock == None); assert!(
matches!(policy.contribution, Satisfaction::Complete {condition} if condition.csv == None && condition.timelock == None)
);
} }
// single key, 1 prv and 1 pub key descriptor, required 1 prv keys // single key, 1 prv and 1 pub key descriptor, required 1 prv keys
@@ -1367,15 +1382,18 @@ mod test {
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_matches!(policy.item, Multisig { keys, threshold } if threshold == 1 assert!(
matches!(policy.item, Multisig { keys, threshold } if threshold == 1
&& keys[0] == PkOrF::Fingerprint(fingerprint0) && keys[0] == PkOrF::Fingerprint(fingerprint0)
&& keys[1] == PkOrF::Fingerprint(fingerprint1) && keys[1] == PkOrF::Fingerprint(fingerprint1))
); );
assert_matches!(policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == 2 assert!(
matches!(policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == 2
&& m == 1 && m == 1
&& items.len() == 2 && items.len() == 2
&& conditions.contains_key(&vec![0]) && conditions.contains_key(&vec![0])
&& conditions.contains_key(&vec![1]) && conditions.contains_key(&vec![1])
)
); );
} }
@@ -1407,14 +1425,18 @@ mod test {
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_matches!(&policy.item, Thresh { items, threshold } if items.len() == 3 && threshold == &2); assert!(
matches!(&policy.item, Thresh { items, threshold } if items.len() == 3 && threshold == &2)
);
assert_matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &3 assert!(
matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &3
&& m == &2 && m == &2
&& items.len() == 3 && items.len() == 3
&& conditions.get(&vec![0,1]).unwrap().iter().next().unwrap().csv.is_none() && conditions.get(&vec![0,1]).unwrap().iter().next().unwrap().csv.is_none()
&& conditions.get(&vec![0,2]).unwrap().iter().next().unwrap().csv == Some(Sequence(sequence)) && conditions.get(&vec![0,2]).unwrap().iter().next().unwrap().csv == Some(Sequence(sequence))
&& conditions.get(&vec![1,2]).unwrap().iter().next().unwrap().csv == Some(Sequence(sequence)) && conditions.get(&vec![1,2]).unwrap().iter().next().unwrap().csv == Some(Sequence(sequence))
)
); );
} }
@@ -1577,9 +1599,11 @@ mod test {
.unwrap(); .unwrap();
//println!("{}", serde_json::to_string(&policy_alice_psbt).unwrap()); //println!("{}", serde_json::to_string(&policy_alice_psbt).unwrap());
assert_matches!(&policy_alice_psbt.satisfaction, Satisfaction::Partial { n, m, items, .. } if n == &2 assert!(
matches!(&policy_alice_psbt.satisfaction, Satisfaction::Partial { n, m, items, .. } if n == &2
&& m == &2 && m == &2
&& items == &vec![0] && items == &vec![0]
)
); );
let psbt = Psbt::from_str(BOB_SIGNED_PSBT).unwrap(); let psbt = Psbt::from_str(BOB_SIGNED_PSBT).unwrap();
@@ -1589,9 +1613,11 @@ mod test {
.unwrap(); .unwrap();
//println!("{}", serde_json::to_string(&policy_bob_psbt).unwrap()); //println!("{}", serde_json::to_string(&policy_bob_psbt).unwrap());
assert_matches!(&policy_bob_psbt.satisfaction, Satisfaction::Partial { n, m, items, .. } if n == &2 assert!(
matches!(&policy_bob_psbt.satisfaction, Satisfaction::Partial { n, m, items, .. } if n == &2
&& m == &2 && m == &2
&& items == &vec![1] && items == &vec![1]
)
); );
let psbt = Psbt::from_str(ALICE_BOB_SIGNED_PSBT).unwrap(); let psbt = Psbt::from_str(ALICE_BOB_SIGNED_PSBT).unwrap();
@@ -1599,9 +1625,11 @@ mod test {
.extract_policy(&signers_container, BuildSatisfaction::Psbt(&psbt), &secp) .extract_policy(&signers_container, BuildSatisfaction::Psbt(&psbt), &secp)
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_matches!(&policy_alice_bob_psbt.satisfaction, Satisfaction::PartialComplete { n, m, items, .. } if n == &2 assert!(
matches!(&policy_alice_bob_psbt.satisfaction, Satisfaction::PartialComplete { n, m, items, .. } if n == &2
&& m == &2 && m == &2
&& items == &vec![0, 1] && items == &vec![0, 1]
)
); );
} }
@@ -1645,9 +1673,11 @@ mod test {
.extract_policy(&signers_container, build_sat, &secp) .extract_policy(&signers_container, build_sat, &secp)
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_matches!(&policy.satisfaction, Satisfaction::Partial { n, m, items, .. } if n == &3 assert!(
matches!(&policy.satisfaction, Satisfaction::Partial { n, m, items, .. } if n == &3
&& m == &2 && m == &2
&& items.is_empty() && items.is_empty()
)
); );
//println!("{}", serde_json::to_string(&policy).unwrap()); //println!("{}", serde_json::to_string(&policy).unwrap());
@@ -1661,9 +1691,11 @@ mod test {
.extract_policy(&signers_container, build_sat_expired, &secp) .extract_policy(&signers_container, build_sat_expired, &secp)
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_matches!(&policy_expired.satisfaction, Satisfaction::Partial { n, m, items, .. } if n == &3 assert!(
matches!(&policy_expired.satisfaction, Satisfaction::Partial { n, m, items, .. } if n == &3
&& m == &2 && m == &2
&& items == &vec![0] && items == &vec![0]
)
); );
//println!("{}", serde_json::to_string(&policy_expired).unwrap()); //println!("{}", serde_json::to_string(&policy_expired).unwrap());
@@ -1679,9 +1711,11 @@ mod test {
.extract_policy(&signers_container, build_sat_expired_signed, &secp) .extract_policy(&signers_container, build_sat_expired_signed, &secp)
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_matches!(&policy_expired_signed.satisfaction, Satisfaction::PartialComplete { n, m, items, .. } if n == &3 assert!(
matches!(&policy_expired_signed.satisfaction, Satisfaction::PartialComplete { n, m, items, .. } if n == &3
&& m == &2 && m == &2
&& items == &vec![0, 1] && items == &vec![0, 1]
)
); );
//println!("{}", serde_json::to_string(&policy_expired_signed).unwrap()); //println!("{}", serde_json::to_string(&policy_expired_signed).unwrap());
} }
@@ -1756,8 +1790,12 @@ mod test {
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_matches!(policy.item, SatisfiableItem::Thresh { ref items, threshold: 1 } if items.len() == 2); assert!(
assert_matches!(policy.contribution, Satisfaction::PartialComplete { n: 2, m: 1, items, .. } if items == vec![1]); matches!(policy.item, SatisfiableItem::Thresh { ref items, threshold: 1 } if items.len() == 2)
);
assert!(
matches!(policy.contribution, Satisfaction::PartialComplete { n: 2, m: 1, items, .. } if items == vec![1])
);
let alice_sig = SatisfiableItem::SchnorrSignature(PkOrF::Fingerprint(alice_fing)); let alice_sig = SatisfiableItem::SchnorrSignature(PkOrF::Fingerprint(alice_fing));
let bob_sig = SatisfiableItem::SchnorrSignature(PkOrF::Fingerprint(bob_fing)); let bob_sig = SatisfiableItem::SchnorrSignature(PkOrF::Fingerprint(bob_fing));
@@ -1849,11 +1887,19 @@ mod test {
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_matches!(policy_unsigned.item, SatisfiableItem::Thresh { ref items, threshold: 1 } if items.len() == 2); assert!(
assert_matches!(policy_unsigned.satisfaction, Satisfaction::Partial { n: 2, m: 1, items, .. } if items.is_empty()); matches!(policy_unsigned.item, SatisfiableItem::Thresh { ref items, threshold: 1 } if items.len() == 2)
);
assert!(
matches!(policy_unsigned.satisfaction, Satisfaction::Partial { n: 2, m: 1, items, .. } if items.is_empty())
);
assert_matches!(policy_signed.item, SatisfiableItem::Thresh { ref items, threshold: 1 } if items.len() == 2); assert!(
assert_matches!(policy_signed.satisfaction, Satisfaction::PartialComplete { n: 2, m: 1, items, .. } if items == vec![0, 1]); matches!(policy_signed.item, SatisfiableItem::Thresh { ref items, threshold: 1 } if items.len() == 2)
);
assert!(
matches!(policy_signed.satisfaction, Satisfaction::PartialComplete { n: 2, m: 1, items, .. } if items == vec![0, 1])
);
let satisfied_items = match policy_signed.item { let satisfied_items = match policy_signed.item {
SatisfiableItem::Thresh { items, .. } => items, SatisfiableItem::Thresh { items, .. } => items,

View File

@@ -235,17 +235,14 @@ impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44<K> {
/// )?; /// )?;
/// ///
/// assert_eq!(wallet.get_address(New)?.to_string(), "miNG7dJTzJqNbFS19svRdTCisC65dsubtR"); /// assert_eq!(wallet.get_address(New)?.to_string(), "miNG7dJTzJqNbFS19svRdTCisC65dsubtR");
/// assert_eq!(wallet.public_descriptor(KeychainKind::External)?.unwrap().to_string(), "pkh([c55b303f/44'/1'/0']tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU/0/*)#cfhumdqz"); /// assert_eq!(wallet.public_descriptor(KeychainKind::External)?.unwrap().to_string(), "pkh([c55b303f/44'/0'/0']tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU/0/*)#xgaaevjx");
/// # Ok::<_, Box<dyn std::error::Error>>(()) /// # Ok::<_, Box<dyn std::error::Error>>(())
/// ``` /// ```
pub struct Bip44Public<K: DerivableKey<Legacy>>(pub K, pub bip32::Fingerprint, pub KeychainKind); pub struct Bip44Public<K: DerivableKey<Legacy>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44Public<K> { impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44Public<K> {
fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> { fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
P2Pkh(legacy::make_bipxx_public( P2Pkh(legacy::make_bipxx_public(44, self.0, self.1, self.2)?).build(network)
44, self.0, self.1, self.2, network,
)?)
.build(network)
} }
} }
@@ -314,17 +311,14 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49<K> {
/// )?; /// )?;
/// ///
/// assert_eq!(wallet.get_address(New)?.to_string(), "2N3K4xbVAHoiTQSwxkZjWDfKoNC27pLkYnt"); /// assert_eq!(wallet.get_address(New)?.to_string(), "2N3K4xbVAHoiTQSwxkZjWDfKoNC27pLkYnt");
/// assert_eq!(wallet.public_descriptor(KeychainKind::External)?.unwrap().to_string(), "sh(wpkh([c55b303f/49'/1'/0']tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L/0/*))#3tka9g0q"); /// assert_eq!(wallet.public_descriptor(KeychainKind::External)?.unwrap().to_string(), "sh(wpkh([c55b303f/49'/0'/0']tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L/0/*))#gsmdv4xr");
/// # Ok::<_, Box<dyn std::error::Error>>(()) /// # Ok::<_, Box<dyn std::error::Error>>(())
/// ``` /// ```
pub struct Bip49Public<K: DerivableKey<Segwitv0>>(pub K, pub bip32::Fingerprint, pub KeychainKind); pub struct Bip49Public<K: DerivableKey<Segwitv0>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49Public<K> { impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49Public<K> {
fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> { fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
P2Wpkh_P2Sh(segwit_v0::make_bipxx_public( P2Wpkh_P2Sh(segwit_v0::make_bipxx_public(49, self.0, self.1, self.2)?).build(network)
49, self.0, self.1, self.2, network,
)?)
.build(network)
} }
} }
@@ -393,17 +387,14 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84<K> {
/// )?; /// )?;
/// ///
/// assert_eq!(wallet.get_address(New)?.to_string(), "tb1qedg9fdlf8cnnqfd5mks6uz5w4kgpk2pr6y4qc7"); /// assert_eq!(wallet.get_address(New)?.to_string(), "tb1qedg9fdlf8cnnqfd5mks6uz5w4kgpk2pr6y4qc7");
/// assert_eq!(wallet.public_descriptor(KeychainKind::External)?.unwrap().to_string(), "wpkh([c55b303f/84'/1'/0']tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q/0/*)#dhu402yv"); /// assert_eq!(wallet.public_descriptor(KeychainKind::External)?.unwrap().to_string(), "wpkh([c55b303f/84\'/0\'/0\']tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q/0/*)#nkk5dtkg");
/// # Ok::<_, Box<dyn std::error::Error>>(()) /// # Ok::<_, Box<dyn std::error::Error>>(())
/// ``` /// ```
pub struct Bip84Public<K: DerivableKey<Segwitv0>>(pub K, pub bip32::Fingerprint, pub KeychainKind); pub struct Bip84Public<K: DerivableKey<Segwitv0>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84Public<K> { impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84Public<K> {
fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> { fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
P2Wpkh(segwit_v0::make_bipxx_public( P2Wpkh(segwit_v0::make_bipxx_public(84, self.0, self.1, self.2)?).build(network)
84, self.0, self.1, self.2, network,
)?)
.build(network)
} }
} }
@@ -449,7 +440,6 @@ macro_rules! expand_make_bipxx {
key: K, key: K,
parent_fingerprint: bip32::Fingerprint, parent_fingerprint: bip32::Fingerprint,
keychain: KeychainKind, keychain: KeychainKind,
network: Network,
) -> Result<impl IntoDescriptorKey<$ctx>, DescriptorError> { ) -> Result<impl IntoDescriptorKey<$ctx>, DescriptorError> {
let derivation_path: bip32::DerivationPath = match keychain { let derivation_path: bip32::DerivationPath = match keychain {
KeychainKind::External => vec![bip32::ChildNumber::from_normal_idx(0)?].into(), KeychainKind::External => vec![bip32::ChildNumber::from_normal_idx(0)?].into(),
@@ -458,10 +448,7 @@ macro_rules! expand_make_bipxx {
let source_path = bip32::DerivationPath::from(vec![ let source_path = bip32::DerivationPath::from(vec![
bip32::ChildNumber::from_hardened_idx(bip)?, bip32::ChildNumber::from_hardened_idx(bip)?,
match network { bip32::ChildNumber::from_hardened_idx(0)?,
Network::Bitcoin => bip32::ChildNumber::from_hardened_idx(0)?,
_ => bip32::ChildNumber::from_hardened_idx(1)?,
},
bip32::ChildNumber::from_hardened_idx(0)?, bip32::ChildNumber::from_hardened_idx(0)?,
]); ]);
@@ -483,7 +470,6 @@ mod test {
use super::*; use super::*;
use crate::descriptor::{DescriptorError, DescriptorMeta}; use crate::descriptor::{DescriptorError, DescriptorMeta};
use crate::keys::ValidNetworks; use crate::keys::ValidNetworks;
use assert_matches::assert_matches;
use bitcoin::network::constants::Network::Regtest; use bitcoin::network::constants::Network::Regtest;
use miniscript::descriptor::{DescriptorPublicKey, KeyMap}; use miniscript::descriptor::{DescriptorPublicKey, KeyMap};
use miniscript::Descriptor; use miniscript::Descriptor;
@@ -502,9 +488,9 @@ mod test {
if let ExtendedDescriptor::Pkh(pkh) = xdesc.0 { if let ExtendedDescriptor::Pkh(pkh) = xdesc.0 {
let path: Vec<ChildNumber> = pkh.into_inner().full_derivation_path().into(); let path: Vec<ChildNumber> = pkh.into_inner().full_derivation_path().into();
let purpose = path.get(0).unwrap(); let purpose = path.get(0).unwrap();
assert_matches!(purpose, Hardened { index: 44 }); assert!(matches!(purpose, Hardened { index: 44 }));
let coin_type = path.get(1).unwrap(); let coin_type = path.get(1).unwrap();
assert_matches!(coin_type, Hardened { index: 0 }); assert!(matches!(coin_type, Hardened { index: 0 }));
} }
let tprvkey = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap(); let tprvkey = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
@@ -516,9 +502,9 @@ mod test {
if let ExtendedDescriptor::Pkh(pkh) = tdesc.0 { if let ExtendedDescriptor::Pkh(pkh) = tdesc.0 {
let path: Vec<ChildNumber> = pkh.into_inner().full_derivation_path().into(); let path: Vec<ChildNumber> = pkh.into_inner().full_derivation_path().into();
let purpose = path.get(0).unwrap(); let purpose = path.get(0).unwrap();
assert_matches!(purpose, Hardened { index: 44 }); assert!(matches!(purpose, Hardened { index: 44 }));
let coin_type = path.get(1).unwrap(); let coin_type = path.get(1).unwrap();
assert_matches!(coin_type, Hardened { index: 1 }); assert!(matches!(coin_type, Hardened { index: 1 }));
} }
} }

View File

@@ -256,9 +256,6 @@ pub extern crate rusqlite;
#[macro_use] #[macro_use]
pub mod testutils; pub mod testutils;
#[cfg(test)]
extern crate assert_matches;
#[allow(unused_imports)] #[allow(unused_imports)]
#[macro_use] #[macro_use]
pub(crate) mod error; pub(crate) mod error;

View File

@@ -23,7 +23,7 @@ pub trait ConfigurableBlockchainTester<B: ConfigurableBlockchain>: Sized {
None None
} }
/// Runs all available tests. /// Runs all avaliable tests.
fn run(&self) { fn run(&self) {
let test_client = &mut TestClient::default(); let test_client = &mut TestClient::default();

View File

@@ -247,20 +247,6 @@ pub struct TransactionDetails {
pub confirmation_time: Option<BlockTime>, pub confirmation_time: Option<BlockTime>,
} }
impl PartialOrd for TransactionDetails {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for TransactionDetails {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.confirmation_time
.cmp(&other.confirmation_time)
.then_with(|| self.txid.cmp(&other.txid))
}
}
/// Block height and timestamp of a block /// Block height and timestamp of a block
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
pub struct BlockTime { pub struct BlockTime {
@@ -270,20 +256,6 @@ pub struct BlockTime {
pub timestamp: u64, pub timestamp: u64,
} }
impl PartialOrd for BlockTime {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for BlockTime {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.height
.cmp(&other.height)
.then_with(|| self.timestamp.cmp(&other.timestamp))
}
}
/// **DEPRECATED**: Confirmation time of a transaction /// **DEPRECATED**: Confirmation time of a transaction
/// ///
/// The structure has been renamed to `BlockTime` /// The structure has been renamed to `BlockTime`
@@ -362,95 +334,6 @@ impl std::iter::Sum for Balance {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use bitcoin::hashes::Hash;
#[test]
fn sort_block_time() {
let block_time_a = BlockTime {
height: 100,
timestamp: 100,
};
let block_time_b = BlockTime {
height: 100,
timestamp: 110,
};
let block_time_c = BlockTime {
height: 0,
timestamp: 0,
};
let mut vec = vec![
block_time_a.clone(),
block_time_b.clone(),
block_time_c.clone(),
];
vec.sort();
let expected = vec![block_time_c, block_time_a, block_time_b];
assert_eq!(vec, expected)
}
#[test]
fn sort_tx_details() {
let block_time_a = BlockTime {
height: 100,
timestamp: 100,
};
let block_time_b = BlockTime {
height: 0,
timestamp: 0,
};
let tx_details_a = TransactionDetails {
transaction: None,
txid: Txid::from_inner([0; 32]),
received: 0,
sent: 0,
fee: None,
confirmation_time: None,
};
let tx_details_b = TransactionDetails {
transaction: None,
txid: Txid::from_inner([0; 32]),
received: 0,
sent: 0,
fee: None,
confirmation_time: Some(block_time_a),
};
let tx_details_c = TransactionDetails {
transaction: None,
txid: Txid::from_inner([0; 32]),
received: 0,
sent: 0,
fee: None,
confirmation_time: Some(block_time_b.clone()),
};
let tx_details_d = TransactionDetails {
transaction: None,
txid: Txid::from_inner([1; 32]),
received: 0,
sent: 0,
fee: None,
confirmation_time: Some(block_time_b),
};
let mut vec = vec![
tx_details_a.clone(),
tx_details_b.clone(),
tx_details_c.clone(),
tx_details_d.clone(),
];
vec.sort();
let expected = vec![tx_details_a, tx_details_c, tx_details_d, tx_details_b];
assert_eq!(vec, expected)
}
#[test] #[test]
fn can_store_feerate_in_const() { fn can_store_feerate_in_const() {

View File

@@ -102,11 +102,11 @@ use crate::{error::Error, Utxo};
use bitcoin::consensus::encode::serialize; use bitcoin::consensus::encode::serialize;
use bitcoin::Script; use bitcoin::Script;
#[cfg(test)]
use assert_matches::assert_matches;
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
#[cfg(not(test))] #[cfg(not(test))]
use rand::thread_rng; use rand::thread_rng;
#[cfg(test)]
use rand::{rngs::StdRng, SeedableRng};
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryInto; use std::convert::TryInto;
@@ -671,7 +671,6 @@ impl BranchAndBoundCoinSelection {
optional_utxos.shuffle(&mut thread_rng()); optional_utxos.shuffle(&mut thread_rng());
#[cfg(test)] #[cfg(test)]
{ {
use rand::{rngs::StdRng, SeedableRng};
let seed = [0; 32]; let seed = [0; 32];
let mut rng: StdRng = SeedableRng::from_seed(seed); let mut rng: StdRng = SeedableRng::from_seed(seed);
optional_utxos.shuffle(&mut rng); optional_utxos.shuffle(&mut rng);
@@ -1523,22 +1522,24 @@ mod test {
let database = MemoryDatabase::default(); let database = MemoryDatabase::default();
let drain_script = Script::default(); let drain_script = Script::default();
let selection = BranchAndBoundCoinSelection::default().coin_select( let err = BranchAndBoundCoinSelection::default()
&database, .coin_select(
vec![], &database,
utxos, vec![],
FeeRate::from_sat_per_vb(10.0), utxos,
500_000, FeeRate::from_sat_per_vb(10.0),
&drain_script, 500_000,
); &drain_script,
)
.unwrap_err();
assert_matches!( assert!(matches!(
selection, err,
Err(Error::InsufficientFunds { Error::InsufficientFunds {
available: 300_000, available: 300_000,
.. ..
}) }
); ));
} }
#[test] #[test]
@@ -1551,22 +1552,24 @@ mod test {
.into_iter() .into_iter()
.partition(|u| matches!(u, WeightedUtxo { utxo, .. } if utxo.txout().value < 1000)); .partition(|u| matches!(u, WeightedUtxo { utxo, .. } if utxo.txout().value < 1000));
let selection = BranchAndBoundCoinSelection::default().coin_select( let err = BranchAndBoundCoinSelection::default()
&database, .coin_select(
required, &database,
optional, required,
FeeRate::from_sat_per_vb(10.0), optional,
500_000, FeeRate::from_sat_per_vb(10.0),
&drain_script, 500_000,
); &drain_script,
)
.unwrap_err();
assert_matches!( assert!(matches!(
selection, err,
Err(Error::InsufficientFunds { Error::InsufficientFunds {
available: 300_010, available: 300_010,
.. ..
}) }
); ));
} }
#[test] #[test]
@@ -1575,21 +1578,23 @@ mod test {
let database = MemoryDatabase::default(); let database = MemoryDatabase::default();
let drain_script = Script::default(); let drain_script = Script::default();
let selection = BranchAndBoundCoinSelection::default().coin_select( let err = BranchAndBoundCoinSelection::default()
&database, .coin_select(
utxos, &database,
vec![], utxos,
FeeRate::from_sat_per_vb(10_000.0), vec![],
500_000, FeeRate::from_sat_per_vb(10_000.0),
&drain_script, 500_000,
); &drain_script,
)
.unwrap_err();
assert_matches!( assert!(matches!(
selection, err,
Err(Error::InsufficientFunds { Error::InsufficientFunds {
available: 300_010, available: 300_010,
.. ..
}) }
); ));
} }
} }

View File

@@ -24,12 +24,9 @@
//! # use std::sync::Arc; //! # use std::sync::Arc;
//! # //! #
//! # fn main() -> Result<(), Box<dyn std::error::Error>> { //! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let mut devices = HWIClient::enumerate()?; //! let devices = HWIClient::enumerate()?;
//! if devices.is_empty() { //! let first_device = devices.first().expect("No devices found!");
//! panic!("No devices found!"); //! let custom_signer = HWISigner::from_device(first_device, HWIChain::Test)?;
//! }
//! let first_device = devices.remove(0)?;
//! let custom_signer = HWISigner::from_device(&first_device, HWIChain::Test)?;
//! //!
//! # let mut wallet = Wallet::new( //! # let mut wallet = Wallet::new(
//! # "", //! # "",

View File

@@ -1851,7 +1851,6 @@ pub fn get_funded_wallet(
#[cfg(test)] #[cfg(test)]
pub(crate) mod test { pub(crate) mod test {
use assert_matches::assert_matches;
use bitcoin::{util::psbt, Network, PackedLockTime, Sequence}; use bitcoin::{util::psbt, Network, PackedLockTime, Sequence};
use crate::database::Database; use crate::database::Database;
@@ -4426,9 +4425,11 @@ pub(crate) mod test {
result.is_err(), result.is_err(),
"Signing should have failed because the TX uses non-standard sighashes" "Signing should have failed because the TX uses non-standard sighashes"
); );
assert_matches!( assert!(
result, matches!(
Err(Error::Signer(SignerError::NonStandardSighash)), result.unwrap_err(),
Error::Signer(SignerError::NonStandardSighash)
),
"Signing failed with the wrong error type" "Signing failed with the wrong error type"
); );
@@ -4911,10 +4912,16 @@ pub(crate) mod test {
..Default::default() ..Default::default()
}, },
); );
assert_matches!( assert!(
result, result.is_err(),
Err(Error::Signer(SignerError::MissingWitnessUtxo)), "Signing should have failed because the witness_utxo is missing"
"Signing should have failed with the correct error because the witness_utxo is missing" );
assert!(
matches!(
result.unwrap_err(),
Error::Signer(SignerError::MissingWitnessUtxo)
),
"Signing failed with the wrong error type"
); );
// restore the witness_utxo // restore the witness_utxo
@@ -4928,9 +4935,9 @@ pub(crate) mod test {
}, },
); );
assert_matches!( assert!(result.is_ok(), "Signing should have worked");
result, assert!(
Ok(true), result.unwrap(),
"Should finalize the input since we can produce signatures" "Should finalize the input since we can produce signatures"
); );
} }
@@ -5255,9 +5262,11 @@ pub(crate) mod test {
result.is_err(), result.is_err(),
"Signing should have failed because the TX uses non-standard sighashes" "Signing should have failed because the TX uses non-standard sighashes"
); );
assert_matches!( assert!(
result, matches!(
Err(Error::Signer(SignerError::NonStandardSighash)), result.unwrap_err(),
Error::Signer(SignerError::NonStandardSighash)
),
"Signing failed with the wrong error type" "Signing failed with the wrong error type"
); );
@@ -5273,9 +5282,11 @@ pub(crate) mod test {
result.is_err(), result.is_err(),
"Signing should have failed because the witness_utxo is missing" "Signing should have failed because the witness_utxo is missing"
); );
assert_matches!( assert!(
result, matches!(
Err(Error::Signer(SignerError::MissingWitnessUtxo)), result.unwrap_err(),
Error::Signer(SignerError::MissingWitnessUtxo)
),
"Signing failed with the wrong error type" "Signing failed with the wrong error type"
); );
@@ -5356,26 +5367,26 @@ pub(crate) mod test {
builder builder
.add_recipient(addr.script_pubkey(), balance.immature / 2) .add_recipient(addr.script_pubkey(), balance.immature / 2)
.current_height(confirmation_time); .current_height(confirmation_time);
assert_matches!( assert!(matches!(
builder.finish(), builder.finish().unwrap_err(),
Err(Error::InsufficientFunds { Error::InsufficientFunds {
needed: _, needed: _,
available: 0 available: 0
}) }
); ));
// Still unspendable... // Still unspendable...
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), balance.immature / 2) .add_recipient(addr.script_pubkey(), balance.immature / 2)
.current_height(not_yet_mature_time); .current_height(not_yet_mature_time);
assert_matches!( assert!(matches!(
builder.finish(), builder.finish().unwrap_err(),
Err(Error::InsufficientFunds { Error::InsufficientFunds {
needed: _, needed: _,
available: 0 available: 0
}) }
); ));
// ...Now the coinbase is mature :) // ...Now the coinbase is mature :)
let sync_time = SyncTime { let sync_time = SyncTime {
@@ -5417,7 +5428,10 @@ pub(crate) mod test {
builder.add_recipient(addr.script_pubkey(), 0); builder.add_recipient(addr.script_pubkey(), 0);
assert_matches!(builder.finish(), Err(Error::OutputBelowDustLimit(0))); assert!(matches!(
builder.finish().unwrap_err(),
Error::OutputBelowDustLimit(0)
));
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
@@ -5532,14 +5546,11 @@ pub(crate) mod test {
use hwi::types::HWIChain; use hwi::types::HWIChain;
use hwi::HWIClient; use hwi::HWIClient;
let mut devices = HWIClient::enumerate().unwrap(); let devices = HWIClient::enumerate().unwrap();
if devices.is_empty() { let device = devices.first().expect("No devices found");
panic!("No devices found!"); let client = HWIClient::get_client(device, true, HWIChain::Regtest).unwrap();
} let descriptors = client.get_descriptors(None).unwrap();
let device = devices.remove(0).unwrap(); let custom_signer = HWISigner::from_device(device, HWIChain::Regtest).unwrap();
let client = HWIClient::get_client(&device, true, HWIChain::Regtest).unwrap();
let descriptors = client.get_descriptors::<String>(None).unwrap();
let custom_signer = HWISigner::from_device(&device, HWIChain::Regtest).unwrap();
let (mut wallet, _, _) = get_funded_wallet(&descriptors.internal[0]); let (mut wallet, _, _) = get_funded_wallet(&descriptors.internal[0]);
wallet.add_signer( wallet.add_signer(

View File

@@ -998,7 +998,6 @@ mod signers_container_tests {
use crate::descriptor; use crate::descriptor;
use crate::descriptor::IntoWalletDescriptor; use crate::descriptor::IntoWalletDescriptor;
use crate::keys::{DescriptorKey, IntoDescriptorKey}; use crate::keys::{DescriptorKey, IntoDescriptorKey};
use assert_matches::assert_matches;
use bitcoin::secp256k1::{All, Secp256k1}; use bitcoin::secp256k1::{All, Secp256k1};
use bitcoin::util::bip32; use bitcoin::util::bip32;
use bitcoin::Network; use bitcoin::Network;
@@ -1068,17 +1067,17 @@ mod signers_container_tests {
signers.add_external(id2.clone(), SignerOrdering(2), signer2.clone()); signers.add_external(id2.clone(), SignerOrdering(2), signer2.clone());
signers.add_external(id3.clone(), SignerOrdering(3), signer3.clone()); signers.add_external(id3.clone(), SignerOrdering(3), signer3.clone());
assert_matches!(signers.find(id1), Some(signer) if is_equal(signer, &signer1)); assert!(matches!(signers.find(id1), Some(signer) if is_equal(signer, &signer1)));
assert_matches!(signers.find(id2), Some(signer) if is_equal(signer, &signer2)); assert!(matches!(signers.find(id2), Some(signer) if is_equal(signer, &signer2)));
assert_matches!(signers.find(id3.clone()), Some(signer) if is_equal(signer, &signer3)); assert!(matches!(signers.find(id3.clone()), Some(signer) if is_equal(signer, &signer3)));
// The `signer4` has the same ID as `signer3` but lower ordering. // The `signer4` has the same ID as `signer3` but lower ordering.
// It should be found by `id3` instead of `signer3`. // It should be found by `id3` instead of `signer3`.
signers.add_external(id3.clone(), SignerOrdering(2), signer4.clone()); signers.add_external(id3.clone(), SignerOrdering(2), signer4.clone());
assert_matches!(signers.find(id3), Some(signer) if is_equal(signer, &signer4)); assert!(matches!(signers.find(id3), Some(signer) if is_equal(signer, &signer4)));
// Can't find anything with ID that doesn't exist // Can't find anything with ID that doesn't exist
assert_matches!(signers.find(id_nonexistent), None); assert!(matches!(signers.find(id_nonexistent), None));
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]

View File

@@ -108,7 +108,6 @@ impl_error!(bitcoinconsensus::Error, Consensus, VerifyError);
mod test { mod test {
use super::*; use super::*;
use crate::database::{BatchOperations, MemoryDatabase}; use crate::database::{BatchOperations, MemoryDatabase};
use assert_matches::assert_matches;
use bitcoin::consensus::encode::deserialize; use bitcoin::consensus::encode::deserialize;
use bitcoin::hashes::hex::FromHex; use bitcoin::hashes::hex::FromHex;
use bitcoin::{Transaction, Txid}; use bitcoin::{Transaction, Txid};
@@ -138,7 +137,9 @@ mod test {
} }
let result = verify_tx(&signed_tx, &database, &blockchain); let result = verify_tx(&signed_tx, &database, &blockchain);
assert_matches!(result, Err(VerifyError::MissingInputTx(txid)) if txid == prev_tx.txid(), assert!(result.is_err(), "Should fail with missing input tx");
assert!(
matches!(result, Err(VerifyError::MissingInputTx(txid)) if txid == prev_tx.txid()),
"Error should be a `MissingInputTx` error" "Error should be a `MissingInputTx` error"
); );
@@ -146,9 +147,9 @@ mod test {
database.set_raw_tx(&prev_tx).unwrap(); database.set_raw_tx(&prev_tx).unwrap();
let result = verify_tx(&unsigned_tx, &database, &blockchain); let result = verify_tx(&unsigned_tx, &database, &blockchain);
assert_matches!( assert!(result.is_err(), "Should fail since the TX is unsigned");
result, assert!(
Err(VerifyError::Consensus(_)), matches!(result, Err(VerifyError::Consensus(_))),
"Error should be a `Consensus` error" "Error should be a `Consensus` error"
); );