Compare commits

..

23 Commits

Author SHA1 Message Date
Steve Myers
cd64d12835 Bump version to 0.26.0 2023-01-04 11:15:10 -06:00
Steve Myers
7e8e9921a9 Bump version to 0.26.0-rc.2 2023-01-02 20:26:40 -06:00
rajarshimaitra
31fa4f723a Update esplora-client 2023-01-02 20:24:53 -06:00
Steve Myers
c741df746b Bump version to 0.26.0-rc.1 2022-12-27 09:14:29 -08:00
Steve Myers
3205f0c16d Bump version to 0.26.0 2022-12-26 13:55:48 -08:00
Steve Myers
5f0870a741 Merge bitcoindevkit/bdk#821: [#344] Add assert_matches
14bc9c0e35 [#344] Add assert_matches Replace assert!(matches! with assert_matches! everywhere Convert assert! to assert_eq! in 2 places (Jeremy Mawson)

Pull request description:

  [#344] Add assert_matches

ACKs for top commit:
  notmandatory:
    ACK 14bc9c0e35
  danielabrozzoni:
    utACK 14bc9c0e35

Tree-SHA512: 730fed9c8c22b0725b1337140636def1a059ac78e4ae16f1abd4c7f379628d9329ccd3ed28e4cbab58e9ace5f349333cb5fa83ec43d507d7a7609601efebc9e1
2022-12-26 13:38:48 -08:00
Steve Myers
5a483472c1 Merge bitcoindevkit/bdk#825: Bump hwi to 0.4.0
4cad18bbca Bump hwi to 0.4.0 (Daniela Brozzoni)

Pull request description:

  <!-- You can erase any parts of this template not applicable to your Pull Request. -->

  ### Description

  <!-- Describe the purpose of this PR, what's being adding and/or fixed -->

  ### Notes to the reviewers

  <!-- In this section you can include notes directed to the reviewers, like explaining why some parts
  of the PR were done in a specific way -->

  ### Changelog notice

  - Bump hwi to 0.4.0

  ### Checklists

  #### All Submissions:

  * [x] I've signed all my commits
  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)
  * [x] I ran `cargo fmt` and `cargo clippy` before committing

ACKs for top commit:
  notmandatory:
    ACK 4cad18bbca

Tree-SHA512: 6c73c091da743734ed87e4792f3c2a838ac7aa252388309ad46e017cc7e9b338bfdb7ed70925c8041a7d22a4d12e2a984e902619b1b7735c645e6c1b6855aeb3
2022-12-26 13:37:05 -08:00
Steve Myers
8d4cc3920a Merge bitcoindevkit/bdk#805: electrum: add validate_domain to ElectrumBlockchainConfig
2451c00268 electrum: add validate_domain to ElectrumBlockchainConfig (Igor Cota)

Pull request description:

  ### Description

  The purpose of the PR is to be able to configure both `stop_gap` **and** `validate_domain`. Perhaps there are nicer ways.

  #### All Submissions:

  * [x] I've signed all my commits
  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)
  * [x] I ran `cargo fmt` and `cargo clippy` before committing

  #### Bugfixes:

  * [x] This pull request breaks the existing API
  * [x] I'm linking the issue being fixed by this PR

  Issue in https://github.com/bitcoindevkit/bdk/issues/804

ACKs for top commit:
  notmandatory:
    ACK 2451c00268

Tree-SHA512: e10e3a027c202c8e680e5181f2a98c9ca30fa7773660cc47bef93f557cdc94c09e46b154b0edee65e498a49bc997cc9172eb3782dc4310d5775c0763d8c2ca4a
2022-12-23 16:28:50 -08:00
Jeremy Mawson
14bc9c0e35 [#344] Add assert_matches
Replace assert!(matches! with assert_matches! everywhere
Convert assert! to assert_eq! in 2 places
2022-12-23 22:05:53 +10:00
Igor Cota
2451c00268 electrum: add validate_domain to ElectrumBlockchainConfig 2022-12-20 10:44:57 +01:00
Daniela Brozzoni
4cad18bbca Bump hwi to 0.4.0 2022-12-19 11:18:44 +01:00
Daniela Brozzoni
634a0575cb Merge bitcoindevkit/bdk#812: Implement ordering for TransactionDetails
d3d07564f2 Implement ordering for TransactionDetails (benthecarman)

Pull request description:

  <!-- You can erase any parts of this template not applicable to your Pull Request. -->

  ### Description

  Pulled from https://github.com/BitcoinDevShop/mutiny-web-poc/pull/189

  Wallets should be able to sort the transactions easily, this makes it so you can just all `sort` on a list of tx details instead of needing to implement the sort_by yourself

  ### Checklists

  #### All Submissions:

  * [x] I've signed all my commits
  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)
  * [x] I ran `cargo fmt` and `cargo clippy` before committing

ACKs for top commit:
  danielabrozzoni:
    utACK d3d07564f2

Tree-SHA512: d2b53dc959897b71d71794f3c919f86f8b7886e6ea5f6ac511cfaca0c19b2f78784a23491b3010380cf41da7ef69fd9ca1be75437c53eca1c60bd6651d1fec41
2022-12-19 09:43:26 +01:00
benthecarman
d3d07564f2 Implement ordering for TransactionDetails 2022-12-17 14:34:09 -06:00
Steve Myers
0b768d6f0b Merge bitcoindevkit/bdk#818: Fix wrong key origin path in public descriptor templates
d6e730f18a Fix wrong key origin path in public descriptor templates (Yuki Kishimoto)

Pull request description:

  ### Description

  Fixes #817

  ### Checklists

  #### All Submissions:

  * [X] I've signed all my commits
  * [X] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)
  * [X] I ran `cargo fmt` and `cargo clippy` before committing

  #### Bugfixes:

  * [ ] This pull request breaks the existing API
  * [ ] I've added tests to reproduce the issue which are now passing
  * [X] I'm linking the issue being fixed by this PR

Top commit has no ACKs.

Tree-SHA512: df08007b27d44a889cf94fe3e2c608cb92f963737c937ca952288d0468636308b09124f04b5dbd793de81d6cb06cf076c89358356abfe8417775ab1ab5650698
2022-12-16 15:40:29 -06:00
Steve Myers
ec9aefac6b Merge bitcoindevkit/bdk#808: fix: make save_tx order independent
d72aa7ebc0 chore: make TxCache.save_txs can order independent (bodymindarts)

Pull request description:

  fulcrum doesn't return txs in the order they are requested in. This PR makes the `TxCache` insensitive to this behaviour.

Top commit has no ACKs.

Tree-SHA512: fe17345ba26dd047ee18d23eb7341849a80ada71a72826dc653616014ca1371b07bddcdc4983e842d4cbfd951b192de21849a5f00662d40fdc8033f13ac2bb75
2022-12-16 15:37:45 -06:00
bodymindarts
d72aa7ebc0 chore: make TxCache.save_txs can order independent 2022-12-16 15:09:44 -06:00
Daniela Brozzoni
99930af12e Merge bitcoindevkit/bdk#820: Make README.md code examples compile without errors
d1e5b87bfc Make README.md code examples compile without errors (Peter Todd)

Pull request description:

ACKs for top commit:
  danielabrozzoni:
    ACK d1e5b87bfc

Tree-SHA512: 343c62acfcfe9e2ea9af8a89d5a1653f9076c50a6fe2efcb23bc838d10d388395872080f2d98f21dd98fdd18a1b79457eb68dae3d73f02aa817f1b9045f19a25
2022-12-15 09:06:17 +01:00
Yuki Kishimoto
d6e730f18a Fix wrong key origin path in public descriptor templates 2022-12-14 21:31:22 +01:00
Peter Todd
d1e5b87bfc Make README.md code examples compile without errors 2022-12-14 15:26:18 -05:00
Daniela Brozzoni
c101dea460 Merge bitcoindevkit/bdk#822: Update ci nightly-docs workflow to use nightly-2022-12-14
9ddd502538 Update ci nightly-docs workflow to use nightly-2022-12-14 (Steve Myers)

Pull request description:

  ### Description

  The current nightly version we were using for the nightly-docs workflow is failing so I updated it to tonight's (2022-12-14)
  version.

  ### Notes to the reviewers

  I decided to select another hard-coded date nightly version so we don't run the risk of some random nightly release breaking this workflow.

  ### Checklists

  #### All Submissions:

  * [x] I've signed all my commits
  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)
  * [x] I ran `cargo fmt` and `cargo clippy` before committing

ACKs for top commit:
  danielabrozzoni:
    utACK 9ddd502538

Tree-SHA512: 650fc4880c1de37f3cd581ba1e2f416ac732d06e03b1dea2878b5051bf38ff809dd965baa0987c7ce47e5aa5e30932cedad3dce17fca6b787e6ace72d604c591
2022-12-14 20:15:47 +01:00
Steve Myers
9ddd502538 Update ci nightly-docs workflow to use nightly-2022-12-14 2022-12-14 11:53:54 -06:00
Daniela Brozzoni
a5d345fff2 Merge bitcoindevkit/bdk#811: Downgrade ubuntu to 20.04 for test_hardware_wallet CI job
11dcc14374 Downgrade ubuntu to 20.04 for test_hardware_wallet CI job (Steve Myers)

Pull request description:

  ### Description

  As suggested by bitcoindevkit/rust-hwi#61, downgrade ubuntu version to 20.04 (instead of using latest), to fix `test_hardware_wallet` CI job.

  ### Checklists

  #### All Submissions:

  * [x] I've signed all my commits
  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)
  * [x] I ran `cargo fmt` and `cargo clippy` before committing

ACKs for top commit:
  danielabrozzoni:
    ACK 11dcc14374

Tree-SHA512: 26de7bb0e562065a9d1071fb292f8532894f921c3557c418e95b05dfff529d8e5bf1751d8b5136618d3b768f10d8af714882f7c453c91a08a9c5e9aafef976b5
2022-12-06 08:17:20 +01:00
Steve Myers
11dcc14374 Downgrade ubuntu to 20.04 for test_hardware_wallet CI job 2022-12-05 11:23:40 -06:00
20 changed files with 339 additions and 266 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-01-25 run: rustup default nightly-2022-12-14
- 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.25.0" version = "0.26.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.2", default-features = false, optional = true } esplora-client = { version = "0.3", 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.3.0", optional = true } hwi = { version = "0.4.0", optional = true, features = [ "use-miniscript"] }
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,6 +109,7 @@ 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,12 +68,13 @@ 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/*)"),
bitcoin::Network::Testnet, Network::Testnet,
MemoryDatabase::default(), MemoryDatabase::default(),
)?; )?;
@@ -96,14 +97,15 @@ use bdk::electrum_client::Client;
use bdk::wallet::AddressIndex::New; use bdk::wallet::AddressIndex::New;
use base64; use base64;
use bitcoin::consensus::serialize; use bdk::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/*)"),
bitcoin::Network::Testnet, Network::Testnet,
MemoryDatabase::default(), MemoryDatabase::default(),
)?; )?;
@@ -133,20 +135,21 @@ fn main() -> Result<(), bdk::Error> {
use bdk::{Wallet, SignOptions, database::MemoryDatabase}; use bdk::{Wallet, SignOptions, database::MemoryDatabase};
use base64; use base64;
use bitcoin::consensus::deserialize; use bdk::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/*)"),
bitcoin::Network::Testnet, 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,6 +2,7 @@ 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};
@@ -23,26 +24,27 @@ 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 devices = HWIClient::enumerate()?; let mut devices = HWIClient::enumerate()?;
let first_device = devices if devices.is_empty() {
.first() panic!("No devices found. Either plug in a hardware wallet, or start a simulator.");
.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(None)?; let descriptors = client.get_descriptors::<Descriptor<DescriptorPublicKey>>(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], descriptors.receive[0].clone(),
Some(&descriptors.internal[0]), Some(descriptors.internal[0].clone()),
Network::Testnet, Network::Testnet,
MemoryDatabase::default(), MemoryDatabase::default(),
)?; )?;

View File

@@ -178,7 +178,8 @@ 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();
@@ -190,6 +191,7 @@ 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!(resp.previous_filter_header == checkpoint); assert_eq!(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,9 +281,11 @@ 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)?;
for (tx, _txid) in txs.into_iter().zip(need_fetch) { let mut txs: HashMap<_, _> = txs.into_iter().map(|tx| (tx.txid(), tx)).collect();
debug_assert_eq!(*_txid, tx.txid()); for txid in need_fetch {
self.cache.insert(tx.txid(), tx); if let Some(tx) = txs.remove(txid) {
self.cache.insert(*txid, tx);
}
} }
} }
@@ -310,6 +312,8 @@ 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 {
@@ -321,6 +325,7 @@ 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 {
@@ -415,6 +420,7 @@ mod test {
retry: 0, retry: 0,
timeout: None, timeout: None,
stop_gap: stop_gap, stop_gap: stop_gap,
validate_domain: true,
}) })
} }
} }

View File

@@ -1137,12 +1137,10 @@ 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!( assert_eq!(
count == 1, count, 1,
"keychain={}, child={}, count={}", "keychain={}, child={}, count={}",
keychain, keychain, child, count
child,
count
); );
} }
} }

View File

@@ -133,6 +133,7 @@ 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]
@@ -155,16 +156,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).err(), calc_checksum(desc),
Some(DescriptorError::InvalidDescriptorChecksum) Err(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).err(), calc_checksum(desc),
Some(DescriptorError::InvalidDescriptorChecksum) Err(DescriptorError::InvalidDescriptorChecksum)
)); );
} }
#[test] #[test]
@@ -172,9 +173,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).err(), calc_checksum(&invalid_desc),
Some(DescriptorError::InvalidDescriptorCharacter(invalid_char)) if invalid_char == sparkle_heart.as_bytes()[0] Err(DescriptorError::InvalidDescriptorCharacter(invalid_char)) if invalid_char == sparkle_heart.as_bytes()[0]
)); );
} }
} }

View File

@@ -581,6 +581,7 @@ 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;
@@ -763,17 +764,11 @@ 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!( assert_matches!(desc, Err(DescriptorError::InvalidDescriptorChecksum));
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!( assert_matches!(desc, Err(DescriptorError::InvalidDescriptorChecksum));
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
@@ -807,17 +802,11 @@ 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!( assert_matches!(desc, Err(DescriptorError::Key(KeyError::InvalidNetwork)));
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!( assert_matches!(desc, Err(DescriptorError::Key(KeyError::InvalidNetwork)));
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
@@ -851,11 +840,7 @@ 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!(result.is_err()); assert_matches!(result, Err(DescriptorError::HardenedDerivationXpub));
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,6 +1139,7 @@ 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;
@@ -1182,8 +1183,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
@@ -1195,10 +1196,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!( assert_matches!(&policy.contribution, Satisfaction::Complete {condition} if condition.csv == None && condition.timelock == None);
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
@@ -1217,19 +1216,16 @@ mod test {
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert!( assert_matches!(&policy.item, Multisig { keys, threshold } if threshold == &2usize
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!( assert_matches!(&policy.contribution, Satisfaction::Partial { n, m, items, conditions, ..} if n == &2usize
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()
)
); );
} }
@@ -1248,18 +1244,15 @@ mod test {
.extract_policy(&signers_container, BuildSatisfaction::None, &secp) .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert!( assert_matches!(&policy.item, Multisig { keys, threshold } if threshold == &2usize
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!( assert_matches!(&policy.contribution, Satisfaction::Partial { n, m, items, conditions, ..} if n == &2usize
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)
)
); );
} }
@@ -1281,18 +1274,15 @@ mod test {
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert!( assert_matches!(&policy.item, Multisig { keys, threshold } if threshold == &1
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!( assert_matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &2
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])
)
); );
} }
@@ -1313,18 +1303,15 @@ mod test {
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert!( assert_matches!(&policy.item, Multisig { keys, threshold } if threshold == &2
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!( assert_matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &2
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])
)
); );
} }
@@ -1345,8 +1332,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
@@ -1358,10 +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!( assert_matches!(policy.contribution, Satisfaction::Complete {condition} if condition.csv == None && condition.timelock == None);
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
@@ -1382,18 +1367,15 @@ mod test {
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert!( assert_matches!(policy.item, Multisig { keys, threshold } if threshold == 1
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!( assert_matches!(policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == 2
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])
)
); );
} }
@@ -1425,18 +1407,14 @@ mod test {
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert!( assert_matches!(&policy.item, Thresh { items, threshold } if items.len() == 3 && threshold == &2);
matches!(&policy.item, Thresh { items, threshold } if items.len() == 3 && threshold == &2)
);
assert!( assert_matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &3
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))
)
); );
} }
@@ -1599,11 +1577,9 @@ mod test {
.unwrap(); .unwrap();
//println!("{}", serde_json::to_string(&policy_alice_psbt).unwrap()); //println!("{}", serde_json::to_string(&policy_alice_psbt).unwrap());
assert!( assert_matches!(&policy_alice_psbt.satisfaction, Satisfaction::Partial { n, m, items, .. } if n == &2
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();
@@ -1613,11 +1589,9 @@ mod test {
.unwrap(); .unwrap();
//println!("{}", serde_json::to_string(&policy_bob_psbt).unwrap()); //println!("{}", serde_json::to_string(&policy_bob_psbt).unwrap());
assert!( assert_matches!(&policy_bob_psbt.satisfaction, Satisfaction::Partial { n, m, items, .. } if n == &2
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();
@@ -1625,11 +1599,9 @@ mod test {
.extract_policy(&signers_container, BuildSatisfaction::Psbt(&psbt), &secp) .extract_policy(&signers_container, BuildSatisfaction::Psbt(&psbt), &secp)
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert!( assert_matches!(&policy_alice_bob_psbt.satisfaction, Satisfaction::PartialComplete { n, m, items, .. } if n == &2
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]
)
); );
} }
@@ -1673,11 +1645,9 @@ mod test {
.extract_policy(&signers_container, build_sat, &secp) .extract_policy(&signers_container, build_sat, &secp)
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert!( assert_matches!(&policy.satisfaction, Satisfaction::Partial { n, m, items, .. } if n == &3
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());
@@ -1691,11 +1661,9 @@ mod test {
.extract_policy(&signers_container, build_sat_expired, &secp) .extract_policy(&signers_container, build_sat_expired, &secp)
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert!( assert_matches!(&policy_expired.satisfaction, Satisfaction::Partial { n, m, items, .. } if n == &3
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());
@@ -1711,11 +1679,9 @@ 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!( assert_matches!(&policy_expired_signed.satisfaction, Satisfaction::PartialComplete { n, m, items, .. } if n == &3
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());
} }
@@ -1790,12 +1756,8 @@ mod test {
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert!( assert_matches!(policy.item, SatisfiableItem::Thresh { ref items, threshold: 1 } if items.len() == 2);
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]);
);
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));
@@ -1887,19 +1849,11 @@ mod test {
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert!( assert_matches!(policy_unsigned.item, SatisfiableItem::Thresh { ref items, threshold: 1 } if items.len() == 2);
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_unsigned.satisfaction, Satisfaction::Partial { n: 2, m: 1, items, .. } if items.is_empty())
);
assert!( assert_matches!(policy_signed.item, SatisfiableItem::Thresh { ref items, threshold: 1 } if items.len() == 2);
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]);
);
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,14 +235,17 @@ 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'/0'/0']tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU/0/*)#xgaaevjx"); /// assert_eq!(wallet.public_descriptor(KeychainKind::External)?.unwrap().to_string(), "pkh([c55b303f/44'/1'/0']tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU/0/*)#cfhumdqz");
/// # 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(44, self.0, self.1, self.2)?).build(network) P2Pkh(legacy::make_bipxx_public(
44, self.0, self.1, self.2, network,
)?)
.build(network)
} }
} }
@@ -311,14 +314,17 @@ 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'/0'/0']tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L/0/*))#gsmdv4xr"); /// assert_eq!(wallet.public_descriptor(KeychainKind::External)?.unwrap().to_string(), "sh(wpkh([c55b303f/49'/1'/0']tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L/0/*))#3tka9g0q");
/// # 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(49, self.0, self.1, self.2)?).build(network) P2Wpkh_P2Sh(segwit_v0::make_bipxx_public(
49, self.0, self.1, self.2, network,
)?)
.build(network)
} }
} }
@@ -387,14 +393,17 @@ 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\'/0\'/0\']tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q/0/*)#nkk5dtkg"); /// assert_eq!(wallet.public_descriptor(KeychainKind::External)?.unwrap().to_string(), "wpkh([c55b303f/84'/1'/0']tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q/0/*)#dhu402yv");
/// # 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(84, self.0, self.1, self.2)?).build(network) P2Wpkh(segwit_v0::make_bipxx_public(
84, self.0, self.1, self.2, network,
)?)
.build(network)
} }
} }
@@ -440,6 +449,7 @@ 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(),
@@ -448,7 +458,10 @@ 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)?,
bip32::ChildNumber::from_hardened_idx(0)?, match network {
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)?,
]); ]);
@@ -470,6 +483,7 @@ 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;
@@ -488,9 +502,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();
@@ -502,9 +516,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,6 +256,9 @@ 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 avaliable tests. /// Runs all available tests.
fn run(&self) { fn run(&self) {
let test_client = &mut TestClient::default(); let test_client = &mut TestClient::default();

View File

@@ -247,6 +247,20 @@ 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 {
@@ -256,6 +270,20 @@ 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`
@@ -334,6 +362,95 @@ 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,6 +671,7 @@ 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);
@@ -1522,24 +1523,22 @@ mod test {
let database = MemoryDatabase::default(); let database = MemoryDatabase::default();
let drain_script = Script::default(); let drain_script = Script::default();
let err = BranchAndBoundCoinSelection::default() let selection = BranchAndBoundCoinSelection::default().coin_select(
.coin_select( &database,
&database, vec![],
vec![], utxos,
utxos, FeeRate::from_sat_per_vb(10.0),
FeeRate::from_sat_per_vb(10.0), 500_000,
500_000, &drain_script,
&drain_script, );
)
.unwrap_err();
assert!(matches!( assert_matches!(
err, selection,
Error::InsufficientFunds { Err(Error::InsufficientFunds {
available: 300_000, available: 300_000,
.. ..
} })
)); );
} }
#[test] #[test]
@@ -1552,24 +1551,22 @@ 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 err = BranchAndBoundCoinSelection::default() let selection = BranchAndBoundCoinSelection::default().coin_select(
.coin_select( &database,
&database, required,
required, optional,
optional, FeeRate::from_sat_per_vb(10.0),
FeeRate::from_sat_per_vb(10.0), 500_000,
500_000, &drain_script,
&drain_script, );
)
.unwrap_err();
assert!(matches!( assert_matches!(
err, selection,
Error::InsufficientFunds { Err(Error::InsufficientFunds {
available: 300_010, available: 300_010,
.. ..
} })
)); );
} }
#[test] #[test]
@@ -1578,23 +1575,21 @@ mod test {
let database = MemoryDatabase::default(); let database = MemoryDatabase::default();
let drain_script = Script::default(); let drain_script = Script::default();
let err = BranchAndBoundCoinSelection::default() let selection = BranchAndBoundCoinSelection::default().coin_select(
.coin_select( &database,
&database, utxos,
utxos, vec![],
vec![], FeeRate::from_sat_per_vb(10_000.0),
FeeRate::from_sat_per_vb(10_000.0), 500_000,
500_000, &drain_script,
&drain_script, );
)
.unwrap_err();
assert!(matches!( assert_matches!(
err, selection,
Error::InsufficientFunds { Err(Error::InsufficientFunds {
available: 300_010, available: 300_010,
.. ..
} })
)); );
} }
} }

View File

@@ -24,9 +24,12 @@
//! # 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 devices = HWIClient::enumerate()?; //! let mut devices = HWIClient::enumerate()?;
//! let first_device = devices.first().expect("No devices found!"); //! if devices.is_empty() {
//! let custom_signer = HWISigner::from_device(first_device, HWIChain::Test)?; //! panic!("No devices found!");
//! }
//! 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,6 +1851,7 @@ 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;
@@ -4425,11 +4426,9 @@ 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!( assert_matches!(
matches!( result,
result.unwrap_err(), Err(Error::Signer(SignerError::NonStandardSighash)),
Error::Signer(SignerError::NonStandardSighash)
),
"Signing failed with the wrong error type" "Signing failed with the wrong error type"
); );
@@ -4912,16 +4911,10 @@ pub(crate) mod test {
..Default::default() ..Default::default()
}, },
); );
assert!( assert_matches!(
result.is_err(), result,
"Signing should have failed because the witness_utxo is missing" Err(Error::Signer(SignerError::MissingWitnessUtxo)),
); "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
@@ -4935,9 +4928,9 @@ pub(crate) mod test {
}, },
); );
assert!(result.is_ok(), "Signing should have worked"); assert_matches!(
assert!( result,
result.unwrap(), Ok(true),
"Should finalize the input since we can produce signatures" "Should finalize the input since we can produce signatures"
); );
} }
@@ -5262,11 +5255,9 @@ 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!( assert_matches!(
matches!( result,
result.unwrap_err(), Err(Error::Signer(SignerError::NonStandardSighash)),
Error::Signer(SignerError::NonStandardSighash)
),
"Signing failed with the wrong error type" "Signing failed with the wrong error type"
); );
@@ -5282,11 +5273,9 @@ 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!( assert_matches!(
matches!( result,
result.unwrap_err(), Err(Error::Signer(SignerError::MissingWitnessUtxo)),
Error::Signer(SignerError::MissingWitnessUtxo)
),
"Signing failed with the wrong error type" "Signing failed with the wrong error type"
); );
@@ -5367,26 +5356,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().unwrap_err(), builder.finish(),
Error::InsufficientFunds { Err(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().unwrap_err(), builder.finish(),
Error::InsufficientFunds { Err(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 {
@@ -5428,10 +5417,7 @@ pub(crate) mod test {
builder.add_recipient(addr.script_pubkey(), 0); builder.add_recipient(addr.script_pubkey(), 0);
assert!(matches!( assert_matches!(builder.finish(), Err(Error::OutputBelowDustLimit(0)));
builder.finish().unwrap_err(),
Error::OutputBelowDustLimit(0)
));
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
@@ -5546,11 +5532,14 @@ pub(crate) mod test {
use hwi::types::HWIChain; use hwi::types::HWIChain;
use hwi::HWIClient; use hwi::HWIClient;
let devices = HWIClient::enumerate().unwrap(); let mut devices = HWIClient::enumerate().unwrap();
let device = devices.first().expect("No devices found"); if devices.is_empty() {
let client = HWIClient::get_client(device, true, HWIChain::Regtest).unwrap(); panic!("No devices found!");
let descriptors = client.get_descriptors(None).unwrap(); }
let custom_signer = HWISigner::from_device(device, HWIChain::Regtest).unwrap(); let device = devices.remove(0).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,6 +998,7 @@ 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;
@@ -1067,17 +1068,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,6 +108,7 @@ 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};
@@ -137,9 +138,7 @@ mod test {
} }
let result = verify_tx(&signed_tx, &database, &blockchain); let result = verify_tx(&signed_tx, &database, &blockchain);
assert!(result.is_err(), "Should fail with missing input tx"); assert_matches!(result, Err(VerifyError::MissingInputTx(txid)) if txid == prev_tx.txid(),
assert!(
matches!(result, Err(VerifyError::MissingInputTx(txid)) if txid == prev_tx.txid()),
"Error should be a `MissingInputTx` error" "Error should be a `MissingInputTx` error"
); );
@@ -147,9 +146,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!(result.is_err(), "Should fail since the TX is unsigned"); assert_matches!(
assert!( result,
matches!(result, Err(VerifyError::Consensus(_))), Err(VerifyError::Consensus(_)),
"Error should be a `Consensus` error" "Error should be a `Consensus` error"
); );