Compare commits
58 Commits
release/0.
...
v0.28.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7231039e81 | ||
|
|
1d840e09f8 | ||
|
|
b6fecc8bc0 | ||
|
|
d0f7543f69 | ||
|
|
7587f1603d | ||
|
|
177c96db5a | ||
|
|
07c1ce9c85 | ||
|
|
6097957650 | ||
|
|
9cffaad71f | ||
|
|
3ccdb84523 | ||
|
|
f7d0852e92 | ||
|
|
15079ee4db | ||
|
|
0f25c6ab29 | ||
|
|
0bf9a0ee2c | ||
|
|
973cd9a286 | ||
|
|
78529b6a42 | ||
|
|
0ad65c7776 | ||
|
|
cbcbdd120d | ||
|
|
f507185729 | ||
|
|
573bf52578 | ||
|
|
10608afb76 | ||
|
|
de46a51208 | ||
|
|
e8acafce8e | ||
|
|
bb2b2d6dd8 | ||
|
|
87c558c9cf | ||
|
|
a4647cfa98 | ||
|
|
b111f97c58 | ||
|
|
7a8e6609b1 | ||
|
|
4ec6f3272e | ||
|
|
553df318ff | ||
|
|
9e2e6411f2 | ||
|
|
5d48e37926 | ||
|
|
c2a42493fd | ||
|
|
0c2570ae07 | ||
|
|
e83bb7c4dc | ||
|
|
46273fe72f | ||
|
|
0b26fa75dc | ||
|
|
35bbe2beef | ||
|
|
9e7bad8afa | ||
|
|
4ada11f358 | ||
|
|
cf8cd2f2b4 | ||
|
|
147a4ed141 | ||
|
|
97f8fe3fd1 | ||
|
|
f0cec015b5 | ||
|
|
ff72078095 | ||
|
|
e678aad3c7 | ||
|
|
41dc7f7d0d | ||
|
|
6b92a169ab | ||
|
|
45d41416ed | ||
|
|
9019793bd4 | ||
|
|
32912eaa05 | ||
|
|
2e7a220e39 | ||
|
|
b02bfb347d | ||
|
|
0cce1ce982 | ||
|
|
fb76c9ed9a | ||
|
|
3a782b3b0d | ||
|
|
eac739d395 | ||
|
|
6e5873ebba |
2
.cargo/audit.toml
Normal file
2
.cargo/audit.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[advisories]
|
||||
ignore = ["RUSTSEC-2022-0046"]
|
||||
1
.github/ISSUE_TEMPLATE/minor_release.md
vendored
1
.github/ISSUE_TEMPLATE/minor_release.md
vendored
@@ -34,6 +34,7 @@ Change the `master` branch to the next MINOR+1 version:
|
||||
- [ ] Create a new PR branch called `bump_dev_MAJOR_MINOR+1`, eg. `bump_dev_0_22`.
|
||||
- [ ] Bump the `bump_dev_MAJOR_MINOR+1` branch to the next development MINOR+1 version.
|
||||
- Change the `Cargo.toml` version value to `MAJOR.MINOR+1.0`.
|
||||
- Update the `CHANGELOG.md` file.
|
||||
- The commit message should be "Bump version to MAJOR.MINOR+1.0".
|
||||
- [ ] Create PR and merge the `bump_dev_MAJOR_MINOR+1` branch to `master`.
|
||||
- Title PR "Bump version to MAJOR.MINOR+1.0".
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/patch_release.md
vendored
1
.github/ISSUE_TEMPLATE/patch_release.md
vendored
@@ -34,6 +34,7 @@ Change the `master` branch to the new PATCH+1 version:
|
||||
- [ ] Create a new PR branch called `bump_dev_MAJOR_MINOR_PATCH+1`, eg. `bump_dev_0_22_1`.
|
||||
- [ ] Bump the `bump_dev_MAJOR_MINOR` branch to the next development PATCH+1 version.
|
||||
- Change the `Cargo.toml` version value to `MAJOR.MINOR.PATCH+1`.
|
||||
- Update the `CHANGELOG.md` file.
|
||||
- The commit message should be "Bump version to MAJOR.MINOR.PATCH+1".
|
||||
- [ ] Create PR and merge the `bump_dev_MAJOR_MINOR_PATCH+1` branch to `master`.
|
||||
- Title PR "Bump version to MAJOR.MINOR.PATCH+1".
|
||||
|
||||
3
.github/workflows/audit.yml
vendored
3
.github/workflows/audit.yml
vendored
@@ -2,6 +2,9 @@ name: Audit
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'release/*'
|
||||
paths:
|
||||
- '**/Cargo.toml'
|
||||
- '**/Cargo.lock'
|
||||
|
||||
10
.github/workflows/code_coverage.yml
vendored
10
.github/workflows/code_coverage.yml
vendored
@@ -1,4 +1,12 @@
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'release/*'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'release/*'
|
||||
|
||||
name: Code Coverage
|
||||
|
||||
|
||||
42
.github/workflows/cont_integration.yml
vendored
42
.github/workflows/cont_integration.yml
vendored
@@ -1,4 +1,12 @@
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'release/*'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'release/*'
|
||||
|
||||
name: CI
|
||||
|
||||
@@ -10,9 +18,9 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
rust:
|
||||
- version: 1.60.0 # STABLE
|
||||
- version: 1.65.0 # STABLE
|
||||
clippy: true
|
||||
- version: 1.56.1 # MSRV
|
||||
- version: 1.57.0 # MSRV
|
||||
features:
|
||||
- default
|
||||
- minimal
|
||||
@@ -51,6 +59,16 @@ jobs:
|
||||
run: rustup component add clippy
|
||||
- name: Update toolchain
|
||||
run: rustup update
|
||||
- name: Pin dependencies for MSRV
|
||||
if: matrix.rust.version == '1.57.0'
|
||||
run: |
|
||||
cargo update -p log --precise "0.4.18"
|
||||
cargo update -p tempfile --precise "3.6.0"
|
||||
cargo update -p hashlink --precise "0.8.1"
|
||||
cargo update -p regex --precise "1.7.3"
|
||||
cargo update -p zip --precise "0.6.3"
|
||||
cargo update -p base64ct --precise "1.5.3"
|
||||
cargo update -p rustix --precise "0.37.23"
|
||||
- name: Build
|
||||
run: cargo build --features ${{ matrix.features }} --no-default-features
|
||||
- name: Clippy
|
||||
@@ -146,7 +164,7 @@ jobs:
|
||||
- run: sudo apt-get update || exit 1
|
||||
- run: sudo apt-get install -y libclang-common-10-dev clang-10 libc6-dev-i386 || exit 1
|
||||
- name: Set default toolchain
|
||||
run: rustup default 1.56.1 # STABLE
|
||||
run: rustup default 1.65.0 # STABLE
|
||||
- name: Set profile
|
||||
run: rustup set profile minimal
|
||||
- name: Add target wasm32
|
||||
@@ -154,7 +172,7 @@ jobs:
|
||||
- name: Update toolchain
|
||||
run: rustup update
|
||||
- name: Check
|
||||
run: cargo check --target wasm32-unknown-unknown --features use-esplora-async,dev-getrandom-wasm --no-default-features
|
||||
run: cargo check --target wasm32-unknown-unknown --features async-interface,use-esplora-async,dev-getrandom-wasm --no-default-features
|
||||
|
||||
fmt:
|
||||
name: Rust fmt
|
||||
@@ -178,8 +196,8 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
rust:
|
||||
- version: 1.60.0 # STABLE
|
||||
- version: 1.56.1 # MSRV
|
||||
- version: 1.65.0 # STABLE
|
||||
- version: 1.57.0 # MSRV
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
@@ -199,5 +217,15 @@ jobs:
|
||||
run: rustup set profile minimal
|
||||
- name: Update toolchain
|
||||
run: rustup update
|
||||
- name: Pin dependencies for MSRV
|
||||
if: matrix.rust.version == '1.57.0'
|
||||
run: |
|
||||
cargo update -p log --precise "0.4.18"
|
||||
cargo update -p tempfile --precise "3.6.0"
|
||||
cargo update -p hashlink --precise "0.8.1"
|
||||
cargo update -p regex --precise "1.7.3"
|
||||
cargo update -p zip --precise "0.6.3"
|
||||
cargo update -p base64ct --precise "1.5.3"
|
||||
cargo update -p rustix --precise "0.37.23"
|
||||
- name: Test
|
||||
run: cargo test --features test-hardware-signer
|
||||
|
||||
10
.github/workflows/nightly_docs.yml
vendored
10
.github/workflows/nightly_docs.yml
vendored
@@ -1,6 +1,14 @@
|
||||
name: Publish Nightly Docs
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'release/*'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'release/*'
|
||||
|
||||
jobs:
|
||||
build_docs:
|
||||
|
||||
231
CHANGELOG.md
231
CHANGELOG.md
@@ -1,13 +1,182 @@
|
||||
# Changelog
|
||||
All notable changes to this project prior to release **0.22.0** are documented in this file. Future
|
||||
changelog information can be found in each release's git tag and can be viewed with `git tag -ln100 "v*"`.
|
||||
Changelog info is also documented on the [GitHub releases](https://github.com/bitcoindevkit/bdk/releases)
|
||||
page. See [DEVELOPMENT_CYCLE.md](DEVELOPMENT_CYCLE.md) for more details.
|
||||
|
||||
All notable changes to this project can be found here and in each release's git tag and can be viewed with `git tag -ln100 "v*"`. See also [DEVELOPMENT_CYCLE.md](DEVELOPMENT_CYCLE.md) for more details.
|
||||
|
||||
Contributors do not need to change this file but do need to add changelog details in their PR descriptions. The person making the next release will collect changelog details from included PRs and edit this file prior to each release.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [v0.21.0] - [v0.20.0]
|
||||
## [Unreleased]
|
||||
|
||||
## [v0.28.1]
|
||||
|
||||
### Summary
|
||||
|
||||
This patch release backports (from the BDK 1.0 dev branch) a fix for a bug in the policy condition calculation and adds a new taproot single key descriptor template (BIP-86). The policy condition calculation bug can cause issues when a policy subtree fails due to missing info even if it's not selected when creating a new transaction, errors on unused policy paths are now ignored.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Backported #932 fix for policy condition calculation #1008
|
||||
|
||||
### Added
|
||||
|
||||
- Backported #840 taproot descriptor template (BIP-86) #1033
|
||||
|
||||
## [v0.28.0]
|
||||
|
||||
### Summary
|
||||
|
||||
Disable default-features for rust-bitcoin and rust-miniscript dependencies, and for rust-esplora-client optional dependency. New default `std` feature must be enabled unless building for wasm.
|
||||
|
||||
### Changed
|
||||
|
||||
- Bump bip39 crate to v2.0.0 #875
|
||||
- Set default-features = false for rust-bitcoin and rust-miniscript #882
|
||||
- Update esplora client dependency to version 0.4 #884
|
||||
- Added new `std` feature as part of default features #930
|
||||
|
||||
## [v0.27.1]
|
||||
|
||||
### Summary
|
||||
|
||||
Fixes [RUSTSEC-2022-0090], this issue is only applicable if you are using the optional sqlite database feature.
|
||||
|
||||
[RUSTSEC-2022-0090]: https://rustsec.org/advisories/RUSTSEC-2022-0090
|
||||
|
||||
### Changed
|
||||
|
||||
- Update optional sqlite dependency from 0.27.0 to 0.28.0. #867
|
||||
|
||||
## [v0.27.0]
|
||||
|
||||
### Summary
|
||||
|
||||
A maintenance release with a bump in project MSRV to 1.57.0, updated dependence and a few developer oriented improvements. Improvements include better error formatting, don't default to async/await for wasm32 and adding derived PartialEq and Eq on SyncTime.
|
||||
|
||||
### Changed
|
||||
|
||||
- Improve display error formatting #814
|
||||
- Don't default to use async/await on wasm32 #831
|
||||
- Project MSRV changed from 1.56.1 to 1.57.0 #842
|
||||
- Update rust-miniscript dependency to latest bug fix release 9.0 #844
|
||||
|
||||
### Added
|
||||
|
||||
- Derive PartialEq, Eq on SyncTime #837
|
||||
|
||||
## [v0.26.0]
|
||||
|
||||
### Summary
|
||||
|
||||
This release improves Fulcrum electrum server compatibility and fixes public descriptor template key origin paths. We also snuck in small enhancements to configure the electrum client to validate the domain using SSL and sort TransactionDetails by block height and timestamp.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Make electrum blockchain client `save_tx` function order independent to work with Fulcrum servers. #808
|
||||
- Fix wrong testnet key origin path in public descriptor templates. #818
|
||||
- Make README.md code examples compile without errors. #820
|
||||
|
||||
### Changed
|
||||
|
||||
- Bump `hwi` dependency to `0.4.0`. #825
|
||||
- Bump `esplora-client` dependency to `0.3` #830
|
||||
|
||||
### Added
|
||||
|
||||
- For electrum blockchain client, allow user to configure whether to validate the domain using SSL. #805
|
||||
- Implement ordering for `TransactionDetails`. #812
|
||||
|
||||
## [v0.25.0]
|
||||
|
||||
### Summary
|
||||
|
||||
This release fixes slow sync time and big script_pubkeys table with SQLite, the wallet rescan height for the FullyNodedExport and setting the network for keys in the KeyMap when using descriptor templates. Also added are new blockchain and mnemonic examples.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Slow sync time and big script_pubkeys table with SQLite.
|
||||
- Wallet rescan height for the FullyNodedExport.
|
||||
- Setting the network for keys in the KeyMap when using descriptor templates.
|
||||
|
||||
### Added
|
||||
|
||||
- Examples for connecting to Esplora, Electrum Server, Neutrino and Bitcoin Core.
|
||||
- Example for using a mnemonic in a descriptors.
|
||||
|
||||
## [v0.24.0]
|
||||
|
||||
### Summary
|
||||
|
||||
This release contains important dependency updates for `rust-bitcoin` to `0.29` and `rust-miniscript` to `8.0`, plus related crates that also depend on the latest version of `rust-bitcoin`. The release also includes a breaking change to the BDK signer which now produces low-R signatures by default, saving one byte. A bug was found in the `get_checksum` and `get_checksum_bytes` functions, which are now deprecated in favor of fixed versions called `calc_checksum` and `calc_checksum_bytes`. And finally a new `hardware-signer` features was added that re-exports the `hwi` crate, along with a new `hardware_signers.rs` example file.
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated dependency versions for `rust-bitcoin` to `0.29` and `rust-miniscript` to `8.0`, plus all related crates. @afilini #770
|
||||
- BDK Signer now produces low-R signatures by default, saving one byte. If you want to preserve the original behavior, set allow_grinding in the SignOptions to false. @vladimirfomene #779
|
||||
- Deprecated `get_checksum`and `get_checksum_bytes` due to bug where they calculates the checksum of a descriptor that already has a checksum. Use `calc_checksum` and `calc_checksum_bytes` instead. @evanlinjin #765
|
||||
- Remove deprecated "address validators". @afilini #770
|
||||
|
||||
### Added
|
||||
|
||||
- New `calc_checksum` and `calc_checksum_bytes`, replace deprecated `get_checksum` and `get_checksum_bytes`. @evanlinjin #765
|
||||
- Re-export the hwi crate when the feature hardware-signer is on. @danielabrozzoni #758
|
||||
- New examples/hardware_signer.rs. @danielabrozzoni #758
|
||||
- Make psbt module public to expose PsbtUtils trait to downstream projects. @notmandatory #782
|
||||
|
||||
## [v0.23.0]
|
||||
|
||||
### Summary
|
||||
|
||||
This release brings new utilities functions on PSBTs like `fee_amount()` and `fee_rate()` and migrates BDK to use our new external esplora client library.
|
||||
As always many bug fixes, docs and tests improvement are also included.
|
||||
|
||||
### Changed
|
||||
|
||||
- Update electrum-client to 0.11.0 by @afilini in https://github.com/bitcoindevkit/bdk/pull/737
|
||||
- Change configs for source-base code coverage by @wszdexdrf in https://github.com/bitcoindevkit/bdk/pull/708
|
||||
- Improve docs regarding PSBT finalization by @tnull in https://github.com/bitcoindevkit/bdk/pull/753
|
||||
- Update compiler example to a Policy example by @rajarshimaitra in https://github.com/bitcoindevkit/bdk/pull/730
|
||||
- Fix the release process by @afilini in https://github.com/bitcoindevkit/bdk/pull/754
|
||||
- Remove redundant duplicated keys check by @afilini in https://github.com/bitcoindevkit/bdk/pull/761
|
||||
- Remove genesis_block lazy initialization by @shobitb in https://github.com/bitcoindevkit/bdk/pull/756
|
||||
- Fix `Wallet::descriptor_checksum` to actually return the checksum by @evanlinjin in https://github.com/bitcoindevkit/bdk/pull/763
|
||||
- Use the esplora client crate by @afilini in https://github.com/bitcoindevkit/bdk/pull/764
|
||||
|
||||
### Added
|
||||
|
||||
- Run code coverage on every PR by @danielabrozzoni in https://github.com/bitcoindevkit/bdk/pull/747
|
||||
- Add psbt_signer.rs example by @notmandatory in https://github.com/bitcoindevkit/bdk/pull/744
|
||||
- Add fee_amount() and fee_rate() functions to PsbtUtils trait by @notmandatory in https://github.com/bitcoindevkit/bdk/pull/728
|
||||
- Add tests to improve coverage by @vladimirfomene in https://github.com/bitcoindevkit/bdk/pull/745
|
||||
- Enable signing taproot transactions with only `non_witness_utxos` by @afilini in https://github.com/bitcoindevkit/bdk/pull/757
|
||||
- Add datatype for is_spent sqlite column by @vladimirfomene in https://github.com/bitcoindevkit/bdk/pull/713
|
||||
- Add vscode filter to gitignore by @evanlinjin in https://github.com/bitcoindevkit/bdk/pull/762
|
||||
|
||||
## [v0.22.0]
|
||||
|
||||
### Summary
|
||||
|
||||
This release brings support for hardware signers on desktop through the HWI library.
|
||||
It also includes fixes and improvements which are part of our ongoing effort of integrating
|
||||
BDK and LDK together.
|
||||
|
||||
### Changed
|
||||
|
||||
- FeeRate function name as_sat_vb to as_sat_per_vb. #678
|
||||
- Verify signatures after signing. #718
|
||||
- Dependency electrum-client to 0.11.0. #737
|
||||
|
||||
### Added
|
||||
|
||||
- Functions to create FeeRate from sats/kvbytes and sats/kwu. #678
|
||||
- Custom hardware wallet signer HwiSigner in wallet::hardwaresigner module. #682
|
||||
- Function allow_dust on TxBuilder. #689
|
||||
- Implementation of Deref<Target=UrlClient> for EsploraBlockchain. #722
|
||||
- Implementation of Deref<Target=Client> for ElectrumBlockchain #705
|
||||
- Implementation of Deref<Target=Client> for RpcBlockchain. #731
|
||||
|
||||
## [v0.21.0]
|
||||
|
||||
- Add `descriptor::checksum::get_checksum_bytes` method.
|
||||
- Add `Excess` enum to handle remaining amount after coin selection.
|
||||
@@ -20,7 +189,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- New `RpcBlockchain` implementation with various fixes.
|
||||
- Return balance in separate categories, namely `confirmed`, `trusted_pending`, `untrusted_pending` & `immature`.
|
||||
|
||||
## [v0.20.0] - [v0.19.0]
|
||||
## [v0.20.0]
|
||||
|
||||
- New MSRV set to `1.56.1`
|
||||
- Fee sniping discouraging through nLockTime - if the user specifies a `current_height`, we use that as a nlocktime, otherwise we use the last sync height (or 0 if we never synced)
|
||||
@@ -31,7 +200,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Deprecate `AddressValidator`
|
||||
- Fix Electrum wallet sync potentially causing address index decrement - compare proposed index and current index before applying batch operations during sync.
|
||||
|
||||
## [v0.19.0] - [v0.18.0]
|
||||
## [v0.19.0]
|
||||
|
||||
- added `OldestFirstCoinSelection` impl to `CoinSelectionAlgorithm`
|
||||
- New MSRV set to `1.56`
|
||||
@@ -47,7 +216,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Support for `tr()` descriptors in the `descriptor!()` macro
|
||||
- Add support for Bitcoin Core 23.0 when using the `rpc` blockchain
|
||||
|
||||
## [v0.18.0] - [v0.17.0]
|
||||
## [v0.18.0]
|
||||
|
||||
- Add `sqlite-bundled` feature for deployments that need a bundled version of sqlite, i.e. for mobile platforms.
|
||||
- Added `Wallet::get_signers()`, `Wallet::descriptor_checksum()` and `Wallet::get_address_validators()`, exposed the `AsDerived` trait.
|
||||
@@ -57,7 +226,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Rename `WalletExport` to `FullyNodedExport`, deprecate the former.
|
||||
- Bump `miniscript` dependency version to `^6.1`.
|
||||
|
||||
## [v0.17.0] - [v0.16.1]
|
||||
## [v0.17.0]
|
||||
|
||||
- Removed default verification from `wallet::sync`. sync-time verification is added in `script_sync` and is activated by `verify` feature flag.
|
||||
- `verify` flag removed from `TransactionDetails`.
|
||||
@@ -78,45 +247,45 @@ To decouple the `Wallet` from the `Blockchain` we've made major changes:
|
||||
- Removed `max_addresses` sync parameter which determined how many addresses to cache before syncing since this can just be done with `ensure_addresses_cached`.
|
||||
- remove `flush` method from the `Database` trait.
|
||||
|
||||
## [v0.16.1] - [v0.16.0]
|
||||
## [v0.16.1]
|
||||
|
||||
- Pin tokio dependency version to ~1.14 to prevent errors due to their new MSRV 1.49.0
|
||||
|
||||
## [v0.16.0] - [v0.15.0]
|
||||
## [v0.16.0]
|
||||
|
||||
- Disable `reqwest` default features.
|
||||
- Added `reqwest-default-tls` feature: Use this to restore the TLS defaults of reqwest if you don't want to add a dependency to it in your own manifest.
|
||||
- Use dust_value from rust-bitcoin
|
||||
- Fixed generating WIF in the correct network format.
|
||||
|
||||
## [v0.15.0] - [v0.14.0]
|
||||
## [v0.15.0]
|
||||
|
||||
- Overhauled sync logic for electrum and esplora.
|
||||
- Unify ureq and reqwest esplora backends to have the same configuration parameters. This means reqwest now has a timeout parameter and ureq has a concurrency parameter.
|
||||
- Fixed esplora fee estimation.
|
||||
|
||||
## [v0.14.0] - [v0.13.0]
|
||||
## [v0.14.0]
|
||||
|
||||
- BIP39 implementation dependency, in `keys::bip39` changed from tiny-bip39 to rust-bip39.
|
||||
- Add new method on the `TxBuilder` to embed data in the transaction via `OP_RETURN`. To allow that a fix to check the dust only on spendable output has been introduced.
|
||||
- Update the `Database` trait to store the last sync timestamp and block height
|
||||
- Rename `ConfirmationTime` to `BlockTime`
|
||||
|
||||
## [v0.13.0] - [v0.12.0]
|
||||
## [v0.13.0]
|
||||
|
||||
- Exposed `get_tx()` method from `Database` to `Wallet`.
|
||||
|
||||
## [v0.12.0] - [v0.11.0]
|
||||
## [v0.12.0]
|
||||
|
||||
- Activate `miniscript/use-serde` feature to allow consumers of the library to access it via the re-exported `miniscript` crate.
|
||||
- Add support for proxies in `EsploraBlockchain`
|
||||
- Added `SqliteDatabase` that implements `Database` backed by a sqlite database using `rusqlite` crate.
|
||||
|
||||
## [v0.11.0] - [v0.10.0]
|
||||
## [v0.11.0]
|
||||
|
||||
- Added `flush` method to the `Database` trait to explicitly flush to disk latest changes on the db.
|
||||
|
||||
## [v0.10.0] - [v0.9.0]
|
||||
## [v0.10.0]
|
||||
|
||||
- Added `RpcBlockchain` in the `AnyBlockchain` struct to allow using Rpc backend where `AnyBlockchain` is used (eg `bdk-cli`)
|
||||
- Removed hard dependency on `tokio`.
|
||||
@@ -130,21 +299,21 @@ To decouple the `Wallet` from the `Blockchain` we've made major changes:
|
||||
- Removed `stop_gap` from `Blockchain` trait and added it to only `ElectrumBlockchain` and `EsploraBlockchain` structs.
|
||||
- Added a `ureq` backend for use when not using feature `async-interface` or target WASM. `ureq` is a blocking HTTP client.
|
||||
|
||||
## [v0.9.0] - [v0.8.0]
|
||||
## [v0.9.0]
|
||||
|
||||
### Wallet
|
||||
|
||||
- Added Bitcoin core RPC added as blockchain backend
|
||||
- Added a `verify` feature that can be enable to verify the unconfirmed txs we download against the consensus rules
|
||||
|
||||
## [v0.8.0] - [v0.7.0]
|
||||
## [v0.8.0]
|
||||
|
||||
### Wallet
|
||||
- Added an option that must be explicitly enabled to allow signing using non-`SIGHASH_ALL` sighashes (#350)
|
||||
#### Changed
|
||||
`get_address` now returns an `AddressInfo` struct that includes the index and derefs to `Address`.
|
||||
|
||||
## [v0.7.0] - [v0.6.0]
|
||||
## [v0.7.0]
|
||||
|
||||
### Policy
|
||||
#### Changed
|
||||
@@ -159,7 +328,7 @@ Timelocks are considered (optionally) in building the `satisfaction` field
|
||||
- Require and validate `non_witness_utxo` for SegWit signatures by default, can be adjusted with `SignOptions`
|
||||
- Replace the opt-in builder option `force_non_witness_utxo` with the opposite `only_witness_utxo`. From now on we will provide the `non_witness_utxo`, unless explicitly asked not to.
|
||||
|
||||
## [v0.6.0] - [v0.5.1]
|
||||
## [v0.6.0]
|
||||
|
||||
### Misc
|
||||
#### Changed
|
||||
@@ -183,13 +352,13 @@ Timelocks are considered (optionally) in building the `satisfaction` field
|
||||
#### Fixed
|
||||
- Fixed `coin_select` calculation for UTXOs where `value < fee` that caused over-/underflow errors.
|
||||
|
||||
## [v0.5.1] - [v0.5.0]
|
||||
## [v0.5.1]
|
||||
|
||||
### Misc
|
||||
#### Changed
|
||||
- Pin `hyper` to `=0.14.4` to make it compile on Rust 1.45
|
||||
|
||||
## [v0.5.0] - [v0.4.0]
|
||||
## [v0.5.0]
|
||||
|
||||
### Misc
|
||||
#### Changed
|
||||
@@ -199,7 +368,7 @@ Timelocks are considered (optionally) in building the `satisfaction` field
|
||||
#### Changed
|
||||
- `FeeRate` constructors `from_sat_per_vb` and `default_min_relay_fee` are now `const` functions
|
||||
|
||||
## [v0.4.0] - [v0.3.0]
|
||||
## [v0.4.0]
|
||||
|
||||
### Keys
|
||||
#### Changed
|
||||
@@ -228,7 +397,7 @@ Timelocks are considered (optionally) in building the `satisfaction` field
|
||||
- Removed unneeded `Result<(), PolicyError>` return type for `Satisfaction::finalize()`
|
||||
- Removed the `TooManyItemsSelected` policy error (see commit message for more details)
|
||||
|
||||
## [v0.3.0] - [v0.2.0]
|
||||
## [v0.3.0]
|
||||
|
||||
### Descriptor
|
||||
#### Changed
|
||||
@@ -265,7 +434,7 @@ final transaction is created by calling `finish` on the builder.
|
||||
#### Changed
|
||||
- Remove `cli.rs` module, `cli-utils` feature and `repl.rs` example; moved to new [`bdk-cli`](https://github.com/bitcoindevkit/bdk-cli) repository
|
||||
|
||||
## [v0.2.0] - [0.1.0-beta.1]
|
||||
## [v0.2.0]
|
||||
|
||||
### Project
|
||||
#### Added
|
||||
@@ -493,3 +662,13 @@ final transaction is created by calling `finish` on the builder.
|
||||
[v0.19.0]: https://github.com/bitcoindevkit/bdk/compare/v0.18.0...v0.19.0
|
||||
[v0.20.0]: https://github.com/bitcoindevkit/bdk/compare/v0.19.0...v0.20.0
|
||||
[v0.21.0]: https://github.com/bitcoindevkit/bdk/compare/v0.20.0...v0.21.0
|
||||
[v0.22.0]: https://github.com/bitcoindevkit/bdk/compare/v0.21.0...v0.22.0
|
||||
[v0.23.0]: https://github.com/bitcoindevkit/bdk/compare/v0.22.0...v0.23.0
|
||||
[v0.24.0]: https://github.com/bitcoindevkit/bdk/compare/v0.23.0...v0.24.0
|
||||
[v0.25.0]: https://github.com/bitcoindevkit/bdk/compare/v0.24.0...v0.25.0
|
||||
[v0.26.0]: https://github.com/bitcoindevkit/bdk/compare/v0.25.0...v0.26.0
|
||||
[v0.27.0]: https://github.com/bitcoindevkit/bdk/compare/v0.26.0...v0.27.0
|
||||
[v0.27.1]: https://github.com/bitcoindevkit/bdk/compare/v0.27.0...v0.27.1
|
||||
[v0.28.0]: https://github.com/bitcoindevkit/bdk/compare/v0.27.1...v0.28.0
|
||||
[v0.28.1]: https://github.com/bitcoindevkit/bdk/compare/v0.28.0...v0.28.1
|
||||
[Unreleased]: https://github.com/bitcoindevkit/bdk/compare/v0.28.1...HEAD
|
||||
|
||||
29
Cargo.toml
29
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "bdk"
|
||||
version = "0.26.0"
|
||||
version = "0.28.1"
|
||||
edition = "2018"
|
||||
authors = ["Alekos Filini <alekos.filini@gmail.com>", "Riccardo Casatta <riccardo@casatta.it>"]
|
||||
homepage = "https://bitcoindevkit.org"
|
||||
@@ -13,9 +13,9 @@ license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
bdk-macros = "^0.6"
|
||||
log = "^0.4"
|
||||
miniscript = { version = "8.0", features = ["serde"] }
|
||||
bitcoin = { version = "0.29.1", features = ["serde", "base64", "rand"] }
|
||||
log = "0.4"
|
||||
miniscript = { version = "9.0", default-features = false, features = ["serde"] }
|
||||
bitcoin = { version = "0.29.2", default-features = false, features = ["serde", "base64", "rand"] }
|
||||
serde = { version = "^1.0", features = ["derive"] }
|
||||
serde_json = { version = "^1.0" }
|
||||
rand = "^0.8"
|
||||
@@ -23,17 +23,17 @@ rand = "^0.8"
|
||||
# Optional dependencies
|
||||
sled = { version = "0.34", optional = true }
|
||||
electrum-client = { version = "0.12", optional = true }
|
||||
esplora-client = { version = "0.3", default-features = false, optional = true }
|
||||
rusqlite = { version = "0.27.0", optional = true }
|
||||
esplora-client = { version = "0.5", default-features = false, optional = true }
|
||||
rusqlite = { version = "0.28.0", optional = true }
|
||||
ahash = { version = "0.7.6", optional = true }
|
||||
futures = { version = "0.3", optional = true }
|
||||
async-trait = { version = "0.1", optional = true }
|
||||
rocksdb = { version = "0.14", default-features = false, features = ["snappy"], optional = true }
|
||||
cc = { version = ">=1.0.64", optional = true }
|
||||
socks = { version = "0.3", optional = true }
|
||||
hwi = { version = "0.4.0", optional = true, features = [ "use-miniscript"] }
|
||||
hwi = { version = "0.5", optional = true, features = ["use-miniscript"] }
|
||||
|
||||
bip39 = { version = "1.0.1", optional = true }
|
||||
bip39 = { version = "2.0.0", optional = true }
|
||||
bitcoinconsensus = { version = "0.19.0-3", optional = true }
|
||||
|
||||
# Needed by bdk_blockchain_tests macro and the `rpc` feature
|
||||
@@ -52,7 +52,10 @@ js-sys = "0.3"
|
||||
minimal = []
|
||||
compiler = ["miniscript/compiler"]
|
||||
verify = ["bitcoinconsensus"]
|
||||
default = ["key-value-db", "electrum"]
|
||||
default = ["std", "key-value-db", "electrum"]
|
||||
# std feature is always required unless building for wasm32-unknown-unknown target
|
||||
# if building for wasm user must add dependencies bitcoin/no-std,miniscript/no-std
|
||||
std = ["bitcoin/std", "miniscript/std"]
|
||||
sqlite = ["rusqlite", "ahash"]
|
||||
sqlite-bundled = ["sqlite", "rusqlite/bundled"]
|
||||
compact_filters = ["rocksdb", "socks", "cc"]
|
||||
@@ -104,10 +107,12 @@ test-hardware-signer = ["hardware-signer"]
|
||||
dev-getrandom-wasm = ["getrandom/js"]
|
||||
|
||||
[dev-dependencies]
|
||||
miniscript = { version = "9.0", features = ["std"] }
|
||||
bitcoin = { version = "0.29.2", features = ["std"] }
|
||||
lazy_static = "1.4"
|
||||
env_logger = "0.7"
|
||||
electrsd = "0.21"
|
||||
# Move back to importing from rust-bitcoin once https://github.com/rust-bitcoin/rust-bitcoin/pull/1342 is released
|
||||
env_logger = { version = "0.7", default-features = false }
|
||||
electrsd = "0.22"
|
||||
# Remove after upgrade to rust-bitcoin ^0.30 where base64 is re-exported
|
||||
base64 = "^0.13"
|
||||
assert_matches = "1.5.0"
|
||||
|
||||
|
||||
25
README.md
25
README.md
@@ -13,7 +13,7 @@
|
||||
<a href="https://github.com/bitcoindevkit/bdk/actions?query=workflow%3ACI"><img alt="CI Status" src="https://github.com/bitcoindevkit/bdk/workflows/CI/badge.svg"></a>
|
||||
<a href="https://coveralls.io/github/bitcoindevkit/bdk?branch=master"><img src="https://coveralls.io/repos/github/bitcoindevkit/bdk/badge.svg?branch=master"/></a>
|
||||
<a href="https://docs.rs/bdk"><img alt="API Docs" src="https://img.shields.io/badge/docs.rs-bdk-green"/></a>
|
||||
<a href="https://blog.rust-lang.org/2021/11/01/Rust-1.56.1.html"><img alt="Rustc Version 1.56.1+" src="https://img.shields.io/badge/rustc-1.56.1%2B-lightgrey.svg"/></a>
|
||||
<a href="https://blog.rust-lang.org/2021/12/02/Rust-1.57.0.html"><img alt="Rustc Version 1.57.0+" src="https://img.shields.io/badge/rustc-1.57.0%2B-lightgrey.svg"/></a>
|
||||
<a href="https://discord.gg/d7NkDKm"><img alt="Chat on Discord" src="https://img.shields.io/discord/753336465005608961?logo=discord"></a>
|
||||
</p>
|
||||
|
||||
@@ -201,3 +201,26 @@ at your option.
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
|
||||
## Minimum Supported Rust Version (MSRV)
|
||||
|
||||
This library should compile with any combination of features with Rust 1.57.0.
|
||||
|
||||
To build with the MSRV you will need to pin dependencies as follows:
|
||||
|
||||
```shell
|
||||
# log 0.4.19 has MSRV 1.60.0
|
||||
cargo update -p log --precise "0.4.18"
|
||||
# tempfile 3.7.0 has MSRV 1.63.0
|
||||
cargo update -p tempfile --precise "3.6.0"
|
||||
# required for sqlite feature, hashlink 0.8.2 has MSRV 1.61.0
|
||||
cargo update -p hashlink --precise "0.8.1"
|
||||
# required for compact_filters feature, regex after 1.7.3 has MSRV 1.60.0
|
||||
cargo update -p regex --precise "1.7.3"
|
||||
# zip 0.6.3 has MSRV 1.59.0 but still works
|
||||
cargo update -p zip --precise "0.6.3"
|
||||
# base64ct 1.6.0 has MSRV 1.60.0
|
||||
cargo update -p base64ct --precise "1.5.3"
|
||||
# rustix 0.38.0 has MSRV 1.65.0
|
||||
cargo update -p rustix --precise "0.37.23"
|
||||
```
|
||||
|
||||
@@ -6,4 +6,4 @@ RUN apt-get install wget -y
|
||||
RUN wget "https://github.com/LedgerHQ/speculos/blob/master/apps/nanos%23btc%232.1%231c8db8da.elf?raw=true" -O /speculos/btc.elf
|
||||
ADD automation.json /speculos/automation.json
|
||||
|
||||
ENTRYPOINT ["python", "./speculos.py", "--automation", "file:automation.json", "--display", "headless", "--vnc-port", "41000", "btc.elf"]
|
||||
ENTRYPOINT ["python", "./speculos.py", "--automation", "file:automation.json", "--model", "nanos", "--display", "headless", "--vnc-port", "41000", "btc.elf"]
|
||||
|
||||
@@ -29,7 +29,7 @@ use bdk::wallet::AddressIndex::New;
|
||||
use bdk::{KeychainKind, Wallet};
|
||||
|
||||
/// Miniscript policy is a high level abstraction of spending conditions. Defined in the
|
||||
/// rust-miscript library here https://docs.rs/miniscript/7.0.0/miniscript/policy/index.html
|
||||
/// rust-miniscript library here https://docs.rs/miniscript/7.0.0/miniscript/policy/index.html
|
||||
/// rust-miniscript provides a `compile()` function that can be used to compile any miniscript policy
|
||||
/// into a descriptor. This descriptor then in turn can be used in bdk a fully functioning wallet
|
||||
/// can be derived from the policy.
|
||||
|
||||
@@ -19,7 +19,7 @@ use syn::{parse, ImplItemMethod, ItemImpl, ItemTrait, Token};
|
||||
|
||||
fn add_async_trait(mut parsed: ItemTrait) -> TokenStream {
|
||||
let output = quote! {
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(feature = "async-interface")))]
|
||||
#[cfg(not(feature = "async-interface"))]
|
||||
#parsed
|
||||
};
|
||||
|
||||
@@ -32,7 +32,7 @@ fn add_async_trait(mut parsed: ItemTrait) -> TokenStream {
|
||||
let output = quote! {
|
||||
#output
|
||||
|
||||
#[cfg(any(target_arch = "wasm32", feature = "async-interface"))]
|
||||
#[cfg(feature = "async-interface")]
|
||||
#[async_trait(?Send)]
|
||||
#parsed
|
||||
};
|
||||
@@ -42,7 +42,7 @@ fn add_async_trait(mut parsed: ItemTrait) -> TokenStream {
|
||||
|
||||
fn add_async_method(mut parsed: ImplItemMethod) -> TokenStream {
|
||||
let output = quote! {
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(feature = "async-interface")))]
|
||||
#[cfg(not(feature = "async-interface"))]
|
||||
#parsed
|
||||
};
|
||||
|
||||
@@ -51,7 +51,7 @@ fn add_async_method(mut parsed: ImplItemMethod) -> TokenStream {
|
||||
let output = quote! {
|
||||
#output
|
||||
|
||||
#[cfg(any(target_arch = "wasm32", feature = "async-interface"))]
|
||||
#[cfg(feature = "async-interface")]
|
||||
#parsed
|
||||
};
|
||||
|
||||
@@ -60,7 +60,7 @@ fn add_async_method(mut parsed: ImplItemMethod) -> TokenStream {
|
||||
|
||||
fn add_async_impl_trait(mut parsed: ItemImpl) -> TokenStream {
|
||||
let output = quote! {
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(feature = "async-interface")))]
|
||||
#[cfg(not(feature = "async-interface"))]
|
||||
#parsed
|
||||
};
|
||||
|
||||
@@ -73,7 +73,7 @@ fn add_async_impl_trait(mut parsed: ItemImpl) -> TokenStream {
|
||||
let output = quote! {
|
||||
#output
|
||||
|
||||
#[cfg(any(target_arch = "wasm32", feature = "async-interface"))]
|
||||
#[cfg(feature = "async-interface")]
|
||||
#[async_trait(?Send)]
|
||||
#parsed
|
||||
};
|
||||
@@ -81,7 +81,7 @@ fn add_async_impl_trait(mut parsed: ItemImpl) -> TokenStream {
|
||||
output.into()
|
||||
}
|
||||
|
||||
/// Makes a method or every method of a trait "async" only if the target_arch is "wasm32"
|
||||
/// Makes a method or every method of a trait `async`, if the `async-interface` feature is enabled.
|
||||
///
|
||||
/// Requires the `async-trait` crate as a dependency whenever this attribute is used on a trait
|
||||
/// definition or trait implementation.
|
||||
@@ -101,18 +101,18 @@ pub fn maybe_async(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
/// Awaits if target_arch is "wasm32", does nothing otherwise
|
||||
/// Awaits, if the `async-interface` feature is enabled.
|
||||
#[proc_macro]
|
||||
pub fn maybe_await(expr: TokenStream) -> TokenStream {
|
||||
let expr: proc_macro2::TokenStream = expr.into();
|
||||
let quoted = quote! {
|
||||
{
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(feature = "async-interface")))]
|
||||
#[cfg(not(feature = "async-interface"))]
|
||||
{
|
||||
#expr
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "wasm32", feature = "async-interface"))]
|
||||
#[cfg(feature = "async-interface")]
|
||||
{
|
||||
#expr.await
|
||||
}
|
||||
@@ -122,20 +122,20 @@ pub fn maybe_await(expr: TokenStream) -> TokenStream {
|
||||
quoted.into()
|
||||
}
|
||||
|
||||
/// Awaits if target_arch is "wasm32", uses `tokio::Runtime::block_on()` otherwise
|
||||
/// Awaits, if the `async-interface` feature is enabled, uses `tokio::Runtime::block_on()` otherwise
|
||||
///
|
||||
/// Requires the `tokio` crate as a dependecy with `rt-core` or `rt-threaded` to build on non-wasm32 platforms.
|
||||
/// Requires the `tokio` crate as a dependecy with `rt-core` or `rt-threaded` to build.
|
||||
#[proc_macro]
|
||||
pub fn await_or_block(expr: TokenStream) -> TokenStream {
|
||||
let expr: proc_macro2::TokenStream = expr.into();
|
||||
let quoted = quote! {
|
||||
{
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(feature = "async-interface")))]
|
||||
#[cfg(not(feature = "async-interface"))]
|
||||
{
|
||||
tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(#expr)
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "wasm32", feature = "async-interface"))]
|
||||
#[cfg(feature = "async-interface")]
|
||||
{
|
||||
#expr.await
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ impl GetBlockHash for AnyBlockchain {
|
||||
impl WalletSync for AnyBlockchain {
|
||||
fn wallet_sync<D: BatchDatabase>(
|
||||
&self,
|
||||
database: &mut D,
|
||||
database: &RefCell<D>,
|
||||
progress_update: Box<dyn Progress>,
|
||||
) -> Result<(), Error> {
|
||||
maybe_await!(impl_inner_method!(
|
||||
@@ -144,7 +144,7 @@ impl WalletSync for AnyBlockchain {
|
||||
|
||||
fn wallet_setup<D: BatchDatabase>(
|
||||
&self,
|
||||
database: &mut D,
|
||||
database: &RefCell<D>,
|
||||
progress_update: Box<dyn Progress>,
|
||||
) -> Result<(), Error> {
|
||||
maybe_await!(impl_inner_method!(
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
use std::ops::DerefMut;
|
||||
use std::path::Path;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
@@ -274,7 +275,7 @@ impl WalletSync for CompactFiltersBlockchain {
|
||||
#[allow(clippy::mutex_atomic)] // Mutex is easier to understand than a CAS loop.
|
||||
fn wallet_setup<D: BatchDatabase>(
|
||||
&self,
|
||||
database: &mut D,
|
||||
database: &RefCell<D>,
|
||||
progress_update: Box<dyn Progress>,
|
||||
) -> Result<(), Error> {
|
||||
let first_peer = &self.peers[0];
|
||||
@@ -322,6 +323,9 @@ impl WalletSync for CompactFiltersBlockchain {
|
||||
|
||||
cf_sync.prepare_sync(Arc::clone(first_peer))?;
|
||||
|
||||
let mut database = database.borrow_mut();
|
||||
let database = database.deref_mut();
|
||||
|
||||
let all_scripts = Arc::new(
|
||||
database
|
||||
.iter_script_pubkeys(None)?
|
||||
@@ -576,7 +580,27 @@ pub enum CompactFiltersError {
|
||||
|
||||
impl fmt::Display for CompactFiltersError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
match self {
|
||||
Self::InvalidResponse => write!(f, "A peer sent an invalid or unexpected response"),
|
||||
Self::InvalidHeaders => write!(f, "Invalid headers"),
|
||||
Self::InvalidFilterHeader => write!(f, "Invalid filter header"),
|
||||
Self::InvalidFilter => write!(f, "Invalid filters"),
|
||||
Self::MissingBlock => write!(f, "The peer is missing a block in the valid chain"),
|
||||
Self::BlockHashNotFound => write!(f, "Block hash not found"),
|
||||
Self::DataCorruption => write!(
|
||||
f,
|
||||
"The data stored in the block filters storage are corrupted"
|
||||
),
|
||||
Self::NotConnected => write!(f, "A peer is not connected"),
|
||||
Self::Timeout => write!(f, "A peer took too long to reply to one of our messages"),
|
||||
Self::PeerBloomDisabled => write!(f, "Peer doesn't advertise the BLOOM service flag"),
|
||||
Self::NoPeers => write!(f, "No peers have been specified"),
|
||||
Self::Db(err) => write!(f, "Internal database error: {}", err),
|
||||
Self::Io(err) => write!(f, "Internal I/O error: {}", err),
|
||||
Self::Bip158(err) => write!(f, "Invalid BIP158 filter: {}", err),
|
||||
Self::Time(err) => write!(f, "Invalid system time: {}", err),
|
||||
Self::Global(err) => write!(f, "Generic error: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -233,7 +233,7 @@ impl ChainStore<Full> {
|
||||
batch.put_cf(
|
||||
cf_handle,
|
||||
StoreEntry::BlockHeaderIndex(Some(genesis.block_hash())).get_key(),
|
||||
&0usize.to_be_bytes(),
|
||||
0usize.to_be_bytes(),
|
||||
);
|
||||
store.write(batch)?;
|
||||
}
|
||||
@@ -302,7 +302,7 @@ impl ChainStore<Full> {
|
||||
batch.put_cf(
|
||||
new_cf_handle,
|
||||
StoreEntry::BlockHeaderIndex(Some(header.block_hash())).get_key(),
|
||||
&from.to_be_bytes(),
|
||||
from.to_be_bytes(),
|
||||
);
|
||||
batch.put_cf(
|
||||
new_cf_handle,
|
||||
@@ -584,7 +584,7 @@ impl<T: StoreType> ChainStore<T> {
|
||||
batch.put_cf(
|
||||
cf_handle,
|
||||
StoreEntry::BlockHeaderIndex(Some(header.block_hash())).get_key(),
|
||||
&(height).to_be_bytes(),
|
||||
(height).to_be_bytes(),
|
||||
);
|
||||
batch.put_cf(
|
||||
cf_handle,
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
//! ```
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::ops::Deref;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use log::{debug, error, info, trace};
|
||||
@@ -117,9 +117,11 @@ impl GetBlockHash for ElectrumBlockchain {
|
||||
impl WalletSync for ElectrumBlockchain {
|
||||
fn wallet_setup<D: BatchDatabase>(
|
||||
&self,
|
||||
database: &mut D,
|
||||
database: &RefCell<D>,
|
||||
_progress_update: Box<dyn Progress>,
|
||||
) -> Result<(), Error> {
|
||||
let mut database = database.borrow_mut();
|
||||
let database = database.deref_mut();
|
||||
let mut request = script_sync::start(database, self.stop_gap)?;
|
||||
let mut block_times = HashMap::<u32, u32>::new();
|
||||
let mut txid_to_height = HashMap::<Txid, u32>::new();
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
//! Esplora by way of `reqwest` HTTP client.
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::ops::Deref;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use bitcoin::{Transaction, Txid};
|
||||
|
||||
@@ -135,10 +135,12 @@ impl GetBlockHash for EsploraBlockchain {
|
||||
impl WalletSync for EsploraBlockchain {
|
||||
fn wallet_setup<D: BatchDatabase>(
|
||||
&self,
|
||||
database: &mut D,
|
||||
database: &RefCell<D>,
|
||||
_progress_update: Box<dyn Progress>,
|
||||
) -> Result<(), Error> {
|
||||
use crate::blockchain::script_sync::Request;
|
||||
let mut database = database.borrow_mut();
|
||||
let database = database.deref_mut();
|
||||
let mut request = script_sync::start(database, self.stop_gap)?;
|
||||
let mut tx_index: HashMap<Txid, Tx> = HashMap::new();
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
//! Esplora by way of `ureq` HTTP client.
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::ops::DerefMut;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use log::{debug, error, info, trace};
|
||||
@@ -117,10 +118,12 @@ impl GetBlockHash for EsploraBlockchain {
|
||||
impl WalletSync for EsploraBlockchain {
|
||||
fn wallet_setup<D: BatchDatabase>(
|
||||
&self,
|
||||
database: &mut D,
|
||||
database: &RefCell<D>,
|
||||
_progress_update: Box<dyn Progress>,
|
||||
) -> Result<(), Error> {
|
||||
use crate::blockchain::script_sync::Request;
|
||||
let mut database = database.borrow_mut();
|
||||
let database = database.deref_mut();
|
||||
let mut request = script_sync::start(database, self.stop_gap)?;
|
||||
let mut tx_index: HashMap<Txid, Tx> = HashMap::new();
|
||||
let batch_update = loop {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
//! [Compact Filters/Neutrino](crate::blockchain::compact_filters), along with a generalized trait
|
||||
//! [`Blockchain`] that can be implemented to build customized backends.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashSet;
|
||||
use std::ops::Deref;
|
||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||
@@ -133,7 +134,7 @@ pub trait WalletSync {
|
||||
/// Populate the internal database with transactions and UTXOs
|
||||
fn wallet_setup<D: BatchDatabase>(
|
||||
&self,
|
||||
database: &mut D,
|
||||
database: &RefCell<D>,
|
||||
progress_update: Box<dyn Progress>,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
@@ -156,7 +157,7 @@ pub trait WalletSync {
|
||||
/// [`BatchOperations::del_utxo`]: crate::database::BatchOperations::del_utxo
|
||||
fn wallet_sync<D: BatchDatabase>(
|
||||
&self,
|
||||
database: &mut D,
|
||||
database: &RefCell<D>,
|
||||
progress_update: Box<dyn Progress>,
|
||||
) -> Result<(), Error> {
|
||||
maybe_await!(self.wallet_setup(database, progress_update))
|
||||
@@ -248,11 +249,8 @@ pub trait BlockchainFactory {
|
||||
/// operations to build a blockchain for a given wallet, so if a wallet needs to be synced
|
||||
/// often it's recommended to use [`BlockchainFactory::build_for_wallet`] to reuse the same
|
||||
/// blockchain multiple times.
|
||||
#[cfg(not(any(target_arch = "wasm32", feature = "async-interface")))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(not(any(target_arch = "wasm32", feature = "async-interface"))))
|
||||
)]
|
||||
#[cfg(not(feature = "async-interface"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "async-interface"))))]
|
||||
fn sync_wallet<D: BatchDatabase>(
|
||||
&self,
|
||||
wallet: &Wallet<D>,
|
||||
@@ -377,7 +375,7 @@ impl<T: GetBlockHash> GetBlockHash for Arc<T> {
|
||||
impl<T: WalletSync> WalletSync for Arc<T> {
|
||||
fn wallet_setup<D: BatchDatabase>(
|
||||
&self,
|
||||
database: &mut D,
|
||||
database: &RefCell<D>,
|
||||
progress_update: Box<dyn Progress>,
|
||||
) -> Result<(), Error> {
|
||||
maybe_await!(self.deref().wallet_setup(database, progress_update))
|
||||
@@ -385,7 +383,7 @@ impl<T: WalletSync> WalletSync for Arc<T> {
|
||||
|
||||
fn wallet_sync<D: BatchDatabase>(
|
||||
&self,
|
||||
database: &mut D,
|
||||
database: &RefCell<D>,
|
||||
progress_update: Box<dyn Progress>,
|
||||
) -> Result<(), Error> {
|
||||
maybe_await!(self.deref().wallet_sync(database, progress_update))
|
||||
|
||||
@@ -49,8 +49,9 @@ use bitcoincore_rpc::Auth as RpcAuth;
|
||||
use bitcoincore_rpc::{Client, RpcApi};
|
||||
use log::{debug, info};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::ops::Deref;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::path::PathBuf;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
@@ -192,10 +193,12 @@ impl GetBlockHash for RpcBlockchain {
|
||||
}
|
||||
|
||||
impl WalletSync for RpcBlockchain {
|
||||
fn wallet_setup<D>(&self, db: &mut D, prog: Box<dyn Progress>) -> Result<(), Error>
|
||||
fn wallet_setup<D>(&self, db: &RefCell<D>, prog: Box<dyn Progress>) -> Result<(), Error>
|
||||
where
|
||||
D: BatchDatabase,
|
||||
{
|
||||
let mut db = db.borrow_mut();
|
||||
let db = db.deref_mut();
|
||||
let batch = DbState::new(db, &self.sync_params, &*prog)?
|
||||
.sync_with_core(&self.client, self.is_descriptors)?
|
||||
.as_db_batch()?;
|
||||
|
||||
@@ -49,7 +49,7 @@ pub use memory::MemoryDatabase;
|
||||
/// Blockchain state at the time of syncing
|
||||
///
|
||||
/// Contains only the block time and height at the moment
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct SyncTime {
|
||||
/// Block timestamp and height at the time of sync
|
||||
pub block_time: BlockTime,
|
||||
|
||||
@@ -53,7 +53,26 @@ impl From<crate::keys::KeyError> for Error {
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
match self {
|
||||
Self::InvalidHdKeyPath => write!(f, "Invalid HD key path"),
|
||||
Self::InvalidDescriptorChecksum => {
|
||||
write!(f, "The provided descriptor doesn't match its checksum")
|
||||
}
|
||||
Self::HardenedDerivationXpub => write!(
|
||||
f,
|
||||
"The descriptor contains hardened derivation steps on public extended keys"
|
||||
),
|
||||
Self::Key(err) => write!(f, "Key error: {}", err),
|
||||
Self::Policy(err) => write!(f, "Policy error: {}", err),
|
||||
Self::InvalidDescriptorCharacter(char) => {
|
||||
write!(f, "Invalid descriptor character: {}", char)
|
||||
}
|
||||
Self::Bip32(err) => write!(f, "BIP32 error: {}", err),
|
||||
Self::Base58(err) => write!(f, "Base58 error: {}", err),
|
||||
Self::Pk(err) => write!(f, "Key-related error: {}", err),
|
||||
Self::Miniscript(err) => write!(f, "Miniscript error: {}", err),
|
||||
Self::Hex(err) => write!(f, "Hex decoding error: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -512,7 +512,14 @@ pub enum PolicyError {
|
||||
|
||||
impl fmt::Display for PolicyError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
match self {
|
||||
Self::NotEnoughItemsSelected(err) => write!(f, "Not enought items selected: {}", err),
|
||||
Self::IndexOutOfRange(index) => write!(f, "Index out of range: {}", index),
|
||||
Self::AddOnLeaf => write!(f, "Add on leaf"),
|
||||
Self::AddOnPartialComplete => write!(f, "Add on partial complete"),
|
||||
Self::MixedTimelockUnits => write!(f, "Mixed timelock units"),
|
||||
Self::IncompatibleConditions => write!(f, "Incompatible conditions"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -652,11 +659,11 @@ impl Policy {
|
||||
(0..*threshold).collect()
|
||||
}
|
||||
SatisfiableItem::Multisig { keys, .. } => (0..keys.len()).collect(),
|
||||
_ => vec![],
|
||||
_ => HashSet::new(),
|
||||
};
|
||||
let selected = match path.get(&self.id) {
|
||||
Some(arr) => arr,
|
||||
_ => &default,
|
||||
let selected: HashSet<_> = match path.get(&self.id) {
|
||||
Some(arr) => arr.iter().copied().collect(),
|
||||
_ => default,
|
||||
};
|
||||
|
||||
match &self.item {
|
||||
@@ -664,14 +671,24 @@ impl Policy {
|
||||
let mapped_req = items
|
||||
.iter()
|
||||
.map(|i| i.get_condition(path))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// if all the requirements are null we don't care about `selected` because there
|
||||
// are no requirements
|
||||
if mapped_req.iter().all(Condition::is_null) {
|
||||
if mapped_req
|
||||
.iter()
|
||||
.all(|cond| matches!(cond, Ok(c) if c.is_null()))
|
||||
{
|
||||
return Ok(Condition::default());
|
||||
}
|
||||
|
||||
// make sure all the indexes in the `selected` list are within range
|
||||
for index in &selected {
|
||||
if *index >= items.len() {
|
||||
return Err(PolicyError::IndexOutOfRange(*index));
|
||||
}
|
||||
}
|
||||
|
||||
// if we have something, make sure we have enough items. note that the user can set
|
||||
// an empty value for this step in case of n-of-n, because `selected` is set to all
|
||||
// the elements above
|
||||
@@ -680,23 +697,18 @@ impl Policy {
|
||||
}
|
||||
|
||||
// check the selected items, see if there are conflicting requirements
|
||||
let mut requirements = Condition::default();
|
||||
for item_index in selected {
|
||||
requirements = requirements.merge(
|
||||
mapped_req
|
||||
.get(*item_index)
|
||||
.ok_or(PolicyError::IndexOutOfRange(*item_index))?,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(requirements)
|
||||
mapped_req
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.filter(|(index, _)| selected.contains(index))
|
||||
.try_fold(Condition::default(), |acc, (_, cond)| acc.merge(&cond?))
|
||||
}
|
||||
SatisfiableItem::Multisig { keys, threshold } => {
|
||||
if selected.len() < *threshold {
|
||||
return Err(PolicyError::NotEnoughItemsSelected(self.id.clone()));
|
||||
}
|
||||
if let Some(item) = selected.iter().find(|i| **i >= keys.len()) {
|
||||
return Err(PolicyError::IndexOutOfRange(*item));
|
||||
if let Some(item) = selected.into_iter().find(|&i| i >= keys.len()) {
|
||||
return Err(PolicyError::IndexOutOfRange(item));
|
||||
}
|
||||
|
||||
Ok(Condition::default())
|
||||
@@ -1197,7 +1209,7 @@ mod test {
|
||||
.unwrap();
|
||||
|
||||
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.is_none() && condition.timelock.is_none());
|
||||
}
|
||||
|
||||
// 2 pub keys descriptor, required 2 prv keys
|
||||
@@ -1346,7 +1358,7 @@ mod test {
|
||||
.unwrap();
|
||||
|
||||
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.is_none() && condition.timelock.is_none());
|
||||
}
|
||||
|
||||
// single key, 1 prv and 1 pub key descriptor, required 1 prv keys
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
use bitcoin::util::bip32;
|
||||
use bitcoin::Network;
|
||||
|
||||
use miniscript::{Legacy, Segwitv0};
|
||||
use miniscript::{Legacy, Segwitv0, Tap};
|
||||
|
||||
use super::{ExtendedDescriptor, IntoWalletDescriptor, KeyMap};
|
||||
use crate::descriptor::DescriptorError;
|
||||
@@ -170,6 +170,35 @@ impl<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh<K> {
|
||||
}
|
||||
}
|
||||
|
||||
/// P2TR template. Expands to a descriptor `tr(key)`
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```
|
||||
/// # use bdk::bitcoin::{PrivateKey, Network};
|
||||
/// # use bdk::Wallet;
|
||||
/// # use bdk::database::MemoryDatabase;
|
||||
/// # use bdk::wallet::AddressIndex::New;
|
||||
/// use bdk::template::P2TR;
|
||||
///
|
||||
/// let key =
|
||||
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
|
||||
/// let mut wallet = Wallet::new(P2TR(key), None, Network::Testnet, MemoryDatabase::default())?;
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// wallet.get_address(New)?.to_string(),
|
||||
/// "tb1pvjf9t34fznr53u5tqhejz4nr69luzkhlvsdsdfq9pglutrpve2xq7hps46"
|
||||
/// );
|
||||
/// # Ok::<_, Box<dyn std::error::Error>>(())
|
||||
/// ```
|
||||
pub struct P2TR<K: IntoDescriptorKey<Tap>>(pub K);
|
||||
|
||||
impl<K: IntoDescriptorKey<Tap>> DescriptorTemplate for P2TR<K> {
|
||||
fn build(self, _network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
|
||||
descriptor!(tr(self.0))
|
||||
}
|
||||
}
|
||||
|
||||
/// BIP44 template. Expands to `pkh(key/44'/{0,1}'/0'/{0,1}/*)`
|
||||
///
|
||||
/// Since there are hardened derivation steps, this template requires a private derivable key (generally a `xprv`/`tprv`).
|
||||
@@ -287,7 +316,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49<K> {
|
||||
|
||||
/// BIP49 public template. Expands to `sh(wpkh(key/{0,1}/*))`
|
||||
///
|
||||
/// This assumes that the key used has already been derived with `m/49'/0'/0'`.
|
||||
/// This assumes that the key used has already been derived with `m/49'/0'/0'` for Mainnet or `m/49'/1'/0'` for Testnet.
|
||||
///
|
||||
/// This template requires the parent fingerprint to populate correctly the metadata of PSBTs.
|
||||
///
|
||||
@@ -366,7 +395,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84<K> {
|
||||
|
||||
/// BIP84 public template. Expands to `wpkh(key/{0,1}/*)`
|
||||
///
|
||||
/// This assumes that the key used has already been derived with `m/84'/0'/0'`.
|
||||
/// This assumes that the key used has already been derived with `m/84'/0'/0'` for Mainnet or `m/84'/1'/0'` for Testnet.
|
||||
///
|
||||
/// This template requires the parent fingerprint to populate correctly the metadata of PSBTs.
|
||||
///
|
||||
@@ -407,6 +436,85 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84Public<K> {
|
||||
}
|
||||
}
|
||||
|
||||
/// BIP86 template. Expands to `tr(key/86'/{0,1}'/0'/{0,1}/*)`
|
||||
///
|
||||
/// Since there are hardened derivation steps, this template requires a private derivable key (generally a `xprv`/`tprv`).
|
||||
///
|
||||
/// See [`Bip86Public`] for a template that can work with a `xpub`/`tpub`.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```
|
||||
/// # use std::str::FromStr;
|
||||
/// # use bdk::bitcoin::{PrivateKey, Network};
|
||||
/// # use bdk::{Wallet, KeychainKind};
|
||||
/// # use bdk::database::MemoryDatabase;
|
||||
/// # use bdk::wallet::AddressIndex::New;
|
||||
/// use bdk::template::Bip86;
|
||||
///
|
||||
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
|
||||
/// let mut wallet = Wallet::new(
|
||||
/// Bip86(key.clone(), KeychainKind::External),
|
||||
/// Some(Bip86(key, KeychainKind::Internal)),
|
||||
/// Network::Testnet,
|
||||
/// MemoryDatabase::default()
|
||||
/// )?;
|
||||
///
|
||||
/// assert_eq!(wallet.get_address(New)?.to_string(), "tb1p5unlj09djx8xsjwe97269kqtxqpwpu2epeskgqjfk4lnf69v4tnqpp35qu");
|
||||
/// assert_eq!(wallet.public_descriptor(KeychainKind::External)?.unwrap().to_string(), "tr([c55b303f/86'/1'/0']tpubDCiHofpEs47kx358bPdJmTZHmCDqQ8qw32upCSxHrSEdeeBs2T5Mq6QMB2ukeMqhNBiyhosBvJErteVhfURPGXPv3qLJPw5MVpHUewsbP2m/0/*)#dkgvr5hm");
|
||||
/// # Ok::<_, Box<dyn std::error::Error>>(())
|
||||
/// ```
|
||||
pub struct Bip86<K: DerivableKey<Tap>>(pub K, pub KeychainKind);
|
||||
|
||||
impl<K: DerivableKey<Tap>> DescriptorTemplate for Bip86<K> {
|
||||
fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
|
||||
P2TR(segwit_v1::make_bipxx_private(86, self.0, self.1, network)?).build(network)
|
||||
}
|
||||
}
|
||||
|
||||
/// BIP86 public template. Expands to `tr(key/{0,1}/*)`
|
||||
///
|
||||
/// This assumes that the key used has already been derived with `m/86'/0'/0'` for Mainnet or `m/86'/1'/0'` for Testnet.
|
||||
///
|
||||
/// This template requires the parent fingerprint to populate correctly the metadata of PSBTs.
|
||||
///
|
||||
/// See [`Bip86`] for a template that does the full derivation, but requires private data
|
||||
/// for the key.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```
|
||||
/// # use std::str::FromStr;
|
||||
/// # use bdk::bitcoin::{PrivateKey, Network};
|
||||
/// # use bdk::{Wallet, KeychainKind};
|
||||
/// # use bdk::database::MemoryDatabase;
|
||||
/// # use bdk::wallet::AddressIndex::New;
|
||||
/// use bdk::template::Bip86Public;
|
||||
///
|
||||
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?;
|
||||
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?;
|
||||
/// let mut wallet = Wallet::new(
|
||||
/// Bip86Public(key.clone(), fingerprint, KeychainKind::External),
|
||||
/// Some(Bip86Public(key, fingerprint, KeychainKind::Internal)),
|
||||
/// Network::Testnet,
|
||||
/// MemoryDatabase::default()
|
||||
/// )?;
|
||||
///
|
||||
/// assert_eq!(wallet.get_address(New)?.to_string(), "tb1pwjp9f2k5n0xq73ecuu0c5njvgqr3vkh7yaylmpqvsuuaafymh0msvcmh37");
|
||||
/// assert_eq!(wallet.public_descriptor(KeychainKind::External)?.unwrap().to_string(), "tr([c55b303f/86'/1'/0']tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q/0/*)#2p65srku");
|
||||
/// # Ok::<_, Box<dyn std::error::Error>>(())
|
||||
/// ```
|
||||
pub struct Bip86Public<K: DerivableKey<Tap>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
|
||||
|
||||
impl<K: DerivableKey<Tap>> DescriptorTemplate for Bip86Public<K> {
|
||||
fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
|
||||
P2TR(segwit_v1::make_bipxx_public(
|
||||
86, self.0, self.1, self.2, network,
|
||||
)?)
|
||||
.build(network)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! expand_make_bipxx {
|
||||
( $mod_name:ident, $ctx:ty ) => {
|
||||
mod $mod_name {
|
||||
@@ -473,6 +581,7 @@ macro_rules! expand_make_bipxx {
|
||||
|
||||
expand_make_bipxx!(legacy, Legacy);
|
||||
expand_make_bipxx!(segwit_v0, Segwitv0);
|
||||
expand_make_bipxx!(segwit_v1, Tap);
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
@@ -484,7 +593,6 @@ mod test {
|
||||
use crate::descriptor::{DescriptorError, DescriptorMeta};
|
||||
use crate::keys::ValidNetworks;
|
||||
use assert_matches::assert_matches;
|
||||
use bitcoin::network::constants::Network::Regtest;
|
||||
use miniscript::descriptor::{DescriptorPublicKey, KeyMap};
|
||||
use miniscript::Descriptor;
|
||||
|
||||
@@ -526,11 +634,14 @@ mod test {
|
||||
fn check(
|
||||
desc: Result<(Descriptor<DescriptorPublicKey>, KeyMap, ValidNetworks), DescriptorError>,
|
||||
is_witness: bool,
|
||||
is_taproot: bool,
|
||||
is_fixed: bool,
|
||||
network: Network,
|
||||
expected: &[&str],
|
||||
) {
|
||||
let (desc, _key_map, _networks) = desc.unwrap();
|
||||
assert_eq!(desc.is_witness(), is_witness);
|
||||
assert_eq!(desc.is_taproot(), is_taproot);
|
||||
assert_eq!(!desc.has_wildcard(), is_fixed);
|
||||
for i in 0..expected.len() {
|
||||
let index = i as u32;
|
||||
@@ -539,7 +650,7 @@ mod test {
|
||||
} else {
|
||||
desc.at_derivation_index(index)
|
||||
};
|
||||
let address = child_desc.address(Regtest).unwrap();
|
||||
let address = child_desc.address(network).unwrap();
|
||||
assert_eq!(address.to_string(), *expected.get(i).unwrap());
|
||||
}
|
||||
}
|
||||
@@ -553,7 +664,9 @@ mod test {
|
||||
check(
|
||||
P2Pkh(prvkey).build(Network::Bitcoin),
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
Network::Regtest,
|
||||
&["mwJ8hxFYW19JLuc65RCTaP4v1rzVU8cVMT"],
|
||||
);
|
||||
|
||||
@@ -564,7 +677,9 @@ mod test {
|
||||
check(
|
||||
P2Pkh(pubkey).build(Network::Bitcoin),
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
Network::Regtest,
|
||||
&["muZpTpBYhxmRFuCjLc7C6BBDF32C8XVJUi"],
|
||||
);
|
||||
}
|
||||
@@ -578,7 +693,9 @@ mod test {
|
||||
check(
|
||||
P2Wpkh_P2Sh(prvkey).build(Network::Bitcoin),
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
Network::Regtest,
|
||||
&["2NB4ox5VDRw1ecUv6SnT3VQHPXveYztRqk5"],
|
||||
);
|
||||
|
||||
@@ -589,7 +706,9 @@ mod test {
|
||||
check(
|
||||
P2Wpkh_P2Sh(pubkey).build(Network::Bitcoin),
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
Network::Regtest,
|
||||
&["2N5LiC3CqzxDamRTPG1kiNv1FpNJQ7x28sb"],
|
||||
);
|
||||
}
|
||||
@@ -603,7 +722,9 @@ mod test {
|
||||
check(
|
||||
P2Wpkh(prvkey).build(Network::Bitcoin),
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
Network::Regtest,
|
||||
&["bcrt1q4525hmgw265tl3drrl8jjta7ayffu6jfcwxx9y"],
|
||||
);
|
||||
|
||||
@@ -614,11 +735,42 @@ mod test {
|
||||
check(
|
||||
P2Wpkh(pubkey).build(Network::Bitcoin),
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
Network::Regtest,
|
||||
&["bcrt1qngw83fg8dz0k749cg7k3emc7v98wy0c7azaa6h"],
|
||||
);
|
||||
}
|
||||
|
||||
// P2TR `tr(key)`
|
||||
#[test]
|
||||
fn test_p2tr_template() {
|
||||
let prvkey =
|
||||
bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")
|
||||
.unwrap();
|
||||
check(
|
||||
P2TR(prvkey).build(Network::Bitcoin),
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
Network::Regtest,
|
||||
&["bcrt1pvjf9t34fznr53u5tqhejz4nr69luzkhlvsdsdfq9pglutrpve2xqnwtkqq"],
|
||||
);
|
||||
|
||||
let pubkey = bitcoin::PublicKey::from_str(
|
||||
"03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd",
|
||||
)
|
||||
.unwrap();
|
||||
check(
|
||||
P2TR(pubkey).build(Network::Bitcoin),
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
Network::Regtest,
|
||||
&["bcrt1pw74tdcrxlzn5r8z6ku2vztr86fgq0m245s72mjktf4afwzsf8ugs4evwdf"],
|
||||
);
|
||||
}
|
||||
|
||||
// BIP44 `pkh(key/44'/0'/0'/{0,1}/*)`
|
||||
#[test]
|
||||
fn test_bip44_template() {
|
||||
@@ -627,6 +779,8 @@ mod test {
|
||||
Bip44(prvkey, KeychainKind::External).build(Network::Bitcoin),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
Network::Regtest,
|
||||
&[
|
||||
"n453VtnjDHPyDt2fDstKSu7A3YCJoHZ5g5",
|
||||
"mvfrrumXgTtwFPWDNUecBBgzuMXhYM7KRP",
|
||||
@@ -637,6 +791,8 @@ mod test {
|
||||
Bip44(prvkey, KeychainKind::Internal).build(Network::Bitcoin),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
Network::Regtest,
|
||||
&[
|
||||
"muHF98X9KxEzdKrnFAX85KeHv96eXopaip",
|
||||
"n4hpyLJE5ub6B5Bymv4eqFxS5KjrewSmYR",
|
||||
@@ -654,6 +810,8 @@ mod test {
|
||||
Bip44Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
Network::Regtest,
|
||||
&[
|
||||
"miNG7dJTzJqNbFS19svRdTCisC65dsubtR",
|
||||
"n2UqaDbCjWSFJvpC84m3FjUk5UaeibCzYg",
|
||||
@@ -664,6 +822,8 @@ mod test {
|
||||
Bip44Public(pubkey, fingerprint, KeychainKind::Internal).build(Network::Bitcoin),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
Network::Regtest,
|
||||
&[
|
||||
"moDr3vJ8wpt5nNxSK55MPq797nXJb2Ru9H",
|
||||
"ms7A1Yt4uTezT2XkefW12AvLoko8WfNJMG",
|
||||
@@ -680,6 +840,8 @@ mod test {
|
||||
Bip49(prvkey, KeychainKind::External).build(Network::Bitcoin),
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
Network::Regtest,
|
||||
&[
|
||||
"2N9bCAJXGm168MjVwpkBdNt6ucka3PKVoUV",
|
||||
"2NDckYkqrYyDMtttEav5hB3Bfw9EGAW5HtS",
|
||||
@@ -690,6 +852,8 @@ mod test {
|
||||
Bip49(prvkey, KeychainKind::Internal).build(Network::Bitcoin),
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
Network::Regtest,
|
||||
&[
|
||||
"2NB3pA8PnzJLGV8YEKNDFpbViZv3Bm1K6CG",
|
||||
"2NBiX2Wzxngb5rPiWpUiJQ2uLVB4HBjFD4p",
|
||||
@@ -707,6 +871,8 @@ mod test {
|
||||
Bip49Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
Network::Regtest,
|
||||
&[
|
||||
"2N3K4xbVAHoiTQSwxkZjWDfKoNC27pLkYnt",
|
||||
"2NCTQfJ1sZa3wQ3pPseYRHbaNEpC3AquEfX",
|
||||
@@ -717,6 +883,8 @@ mod test {
|
||||
Bip49Public(pubkey, fingerprint, KeychainKind::Internal).build(Network::Bitcoin),
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
Network::Regtest,
|
||||
&[
|
||||
"2NF2vttKibwyxigxtx95Zw8K7JhDbo5zPVJ",
|
||||
"2Mtmyd8taksxNVWCJ4wVvaiss7QPZGcAJuH",
|
||||
@@ -733,6 +901,8 @@ mod test {
|
||||
Bip84(prvkey, KeychainKind::External).build(Network::Bitcoin),
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
Network::Regtest,
|
||||
&[
|
||||
"bcrt1qkmvk2nadgplmd57ztld8nf8v2yxkzmdvwtjf8s",
|
||||
"bcrt1qx0v6zgfwe50m4kqc58cqzcyem7ay2sfl3gvqhp",
|
||||
@@ -743,6 +913,8 @@ mod test {
|
||||
Bip84(prvkey, KeychainKind::Internal).build(Network::Bitcoin),
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
Network::Regtest,
|
||||
&[
|
||||
"bcrt1qtrwtz00wxl69e5xex7amy4xzlxkaefg3gfdkxa",
|
||||
"bcrt1qqqasfhxpkkf7zrxqnkr2sfhn74dgsrc3e3ky45",
|
||||
@@ -760,6 +932,8 @@ mod test {
|
||||
Bip84Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
Network::Regtest,
|
||||
&[
|
||||
"bcrt1qedg9fdlf8cnnqfd5mks6uz5w4kgpk2prcdvd0h",
|
||||
"bcrt1q3lncdlwq3lgcaaeyruynjnlccr0ve0kakh6ana",
|
||||
@@ -770,6 +944,8 @@ mod test {
|
||||
Bip84Public(pubkey, fingerprint, KeychainKind::Internal).build(Network::Bitcoin),
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
Network::Regtest,
|
||||
&[
|
||||
"bcrt1qm6wqukenh7guu792lj2njgw9n78cmwsy8xy3z2",
|
||||
"bcrt1q694twxtjn4nnrvnyvra769j0a23rllj5c6cgwp",
|
||||
@@ -777,4 +953,67 @@ mod test {
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// BIP86 `tr(key/86'/0'/0'/{0,1}/*)`
|
||||
// Used addresses in test vector in https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki
|
||||
#[test]
|
||||
fn test_bip86_template() {
|
||||
let prvkey = bitcoin::util::bip32::ExtendedPrivKey::from_str("xprv9s21ZrQH143K3GJpoapnV8SFfukcVBSfeCficPSGfubmSFDxo1kuHnLisriDvSnRRuL2Qrg5ggqHKNVpxR86QEC8w35uxmGoggxtQTPvfUu").unwrap();
|
||||
check(
|
||||
Bip86(prvkey, KeychainKind::External).build(Network::Bitcoin),
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
Network::Bitcoin,
|
||||
&[
|
||||
"bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
|
||||
"bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
|
||||
"bc1p0d0rhyynq0awa9m8cqrcr8f5nxqx3aw29w4ru5u9my3h0sfygnzs9khxz8",
|
||||
],
|
||||
);
|
||||
check(
|
||||
Bip86(prvkey, KeychainKind::Internal).build(Network::Bitcoin),
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
Network::Bitcoin,
|
||||
&[
|
||||
"bc1p3qkhfews2uk44qtvauqyr2ttdsw7svhkl9nkm9s9c3x4ax5h60wqwruhk7",
|
||||
"bc1ptdg60grjk9t3qqcqczp4tlyy3z47yrx9nhlrjsmw36q5a72lhdrs9f00nj",
|
||||
"bc1pgcwgsu8naxp7xlp5p7ufzs7emtfza2las7r2e7krzjhe5qj5xz2q88kmk5",
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// BIP86 public `tr(key/{0,1}/*)`
|
||||
// Used addresses in test vector in https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki
|
||||
#[test]
|
||||
fn test_bip86_public_template() {
|
||||
let pubkey = bitcoin::util::bip32::ExtendedPubKey::from_str("xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ").unwrap();
|
||||
let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("73c5da0a").unwrap();
|
||||
check(
|
||||
Bip86Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
Network::Bitcoin,
|
||||
&[
|
||||
"bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
|
||||
"bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
|
||||
"bc1p0d0rhyynq0awa9m8cqrcr8f5nxqx3aw29w4ru5u9my3h0sfygnzs9khxz8",
|
||||
],
|
||||
);
|
||||
check(
|
||||
Bip86Public(pubkey, fingerprint, KeychainKind::Internal).build(Network::Bitcoin),
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
Network::Bitcoin,
|
||||
&[
|
||||
"bc1p3qkhfews2uk44qtvauqyr2ttdsw7svhkl9nkm9s9c3x4ax5h60wqwruhk7",
|
||||
"bc1ptdg60grjk9t3qqcqczp4tlyy3z47yrx9nhlrjsmw36q5a72lhdrs9f00nj",
|
||||
"bc1pgcwgsu8naxp7xlp5p7ufzs7emtfza2las7r2e7krzjhe5qj5xz2q88kmk5",
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
102
src/error.rs
102
src/error.rs
@@ -107,7 +107,7 @@ pub enum Error {
|
||||
MiniscriptPsbt(MiniscriptPsbtError),
|
||||
/// BIP32 error
|
||||
Bip32(bitcoin::util::bip32::Error),
|
||||
/// An ECDSA error
|
||||
/// A secp256k1 error
|
||||
Secp256k1(bitcoin::secp256k1::Error),
|
||||
/// Error serializing or deserializing JSON data
|
||||
Json(serde_json::Error),
|
||||
@@ -157,6 +157,18 @@ pub enum MiniscriptPsbtError {
|
||||
OutputUpdate(miniscript::psbt::OutputUpdateError),
|
||||
}
|
||||
|
||||
impl fmt::Display for MiniscriptPsbtError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Conversion(err) => write!(f, "Conversion error: {}", err),
|
||||
Self::UtxoUpdate(err) => write!(f, "UTXO update error: {}", err),
|
||||
Self::OutputUpdate(err) => write!(f, "Output update error: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for MiniscriptPsbtError {}
|
||||
|
||||
/// Represents the last failed [`crate::blockchain::WalletSync`] sync attempt in which we were short
|
||||
/// on cached `scriptPubKey`s.
|
||||
#[derive(Debug)]
|
||||
@@ -169,7 +181,93 @@ pub struct MissingCachedScripts {
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
match self {
|
||||
Self::InvalidU32Bytes(_) => write!(
|
||||
f,
|
||||
"Wrong number of bytes found when trying to convert to u32"
|
||||
),
|
||||
Self::Generic(err) => write!(f, "Generic error: {}", err),
|
||||
Self::ScriptDoesntHaveAddressForm => write!(f, "Script doesn't have address form"),
|
||||
Self::NoRecipients => write!(f, "Cannot build tx without recipients"),
|
||||
Self::NoUtxosSelected => write!(f, "No UTXO selected"),
|
||||
Self::OutputBelowDustLimit(limit) => {
|
||||
write!(f, "Output below the dust limit: {}", limit)
|
||||
}
|
||||
Self::InsufficientFunds { needed, available } => write!(
|
||||
f,
|
||||
"Insufficient funds: {} sat available of {} sat needed",
|
||||
available, needed
|
||||
),
|
||||
Self::BnBTotalTriesExceeded => {
|
||||
write!(f, "Branch and bound coin selection: total tries exceeded")
|
||||
}
|
||||
Self::BnBNoExactMatch => write!(f, "Branch and bound coin selection: not exact match"),
|
||||
Self::UnknownUtxo => write!(f, "UTXO not found in the internal database"),
|
||||
Self::TransactionNotFound => {
|
||||
write!(f, "Transaction not found in the internal database")
|
||||
}
|
||||
Self::TransactionConfirmed => write!(f, "Transaction already confirmed"),
|
||||
Self::IrreplaceableTransaction => write!(f, "Transaction can't be replaced"),
|
||||
Self::FeeRateTooLow { required } => write!(
|
||||
f,
|
||||
"Fee rate too low: required {} sat/vbyte",
|
||||
required.as_sat_per_vb()
|
||||
),
|
||||
Self::FeeTooLow { required } => write!(f, "Fee to low: required {} sat", required),
|
||||
Self::FeeRateUnavailable => write!(f, "Fee rate unavailable"),
|
||||
Self::MissingKeyOrigin(err) => write!(f, "Missing key origin: {}", err),
|
||||
Self::Key(err) => write!(f, "Key error: {}", err),
|
||||
Self::ChecksumMismatch => write!(f, "Descriptor checksum mismatch"),
|
||||
Self::SpendingPolicyRequired(keychain_kind) => {
|
||||
write!(f, "Spending policy required: {:?}", keychain_kind)
|
||||
}
|
||||
Self::InvalidPolicyPathError(err) => write!(f, "Invalid policy path: {}", err),
|
||||
Self::Signer(err) => write!(f, "Signer error: {}", err),
|
||||
Self::InvalidNetwork { requested, found } => write!(
|
||||
f,
|
||||
"Invalid network: requested {} but found {}",
|
||||
requested, found
|
||||
),
|
||||
#[cfg(feature = "verify")]
|
||||
Self::Verification(err) => write!(f, "Transaction verification error: {}", err),
|
||||
Self::InvalidProgressValue(progress) => {
|
||||
write!(f, "Invalid progress value: {}", progress)
|
||||
}
|
||||
Self::ProgressUpdateError => write!(
|
||||
f,
|
||||
"Progress update error (maybe the channel has been closed)"
|
||||
),
|
||||
Self::InvalidOutpoint(outpoint) => write!(
|
||||
f,
|
||||
"Requested outpoint doesn't exist in the tx: {}",
|
||||
outpoint
|
||||
),
|
||||
Self::Descriptor(err) => write!(f, "Descriptor error: {}", err),
|
||||
Self::Encode(err) => write!(f, "Encoding error: {}", err),
|
||||
Self::Miniscript(err) => write!(f, "Miniscript error: {}", err),
|
||||
Self::MiniscriptPsbt(err) => write!(f, "Miniscript PSBT error: {}", err),
|
||||
Self::Bip32(err) => write!(f, "BIP32 error: {}", err),
|
||||
Self::Secp256k1(err) => write!(f, "Secp256k1 error: {}", err),
|
||||
Self::Json(err) => write!(f, "Serialize/Deserialize JSON error: {}", err),
|
||||
Self::Hex(err) => write!(f, "Hex decoding error: {}", err),
|
||||
Self::Psbt(err) => write!(f, "PSBT error: {}", err),
|
||||
Self::PsbtParse(err) => write!(f, "Impossible to parse PSBT: {}", err),
|
||||
Self::MissingCachedScripts(missing_cached_scripts) => {
|
||||
write!(f, "Missing cached scripts: {:?}", missing_cached_scripts)
|
||||
}
|
||||
#[cfg(feature = "electrum")]
|
||||
Self::Electrum(err) => write!(f, "Electrum client error: {}", err),
|
||||
#[cfg(feature = "esplora")]
|
||||
Self::Esplora(err) => write!(f, "Esplora client error: {}", err),
|
||||
#[cfg(feature = "compact_filters")]
|
||||
Self::CompactFilters(err) => write!(f, "Compact filters client error: {}", err),
|
||||
#[cfg(feature = "key-value-db")]
|
||||
Self::Sled(err) => write!(f, "Sled database error: {}", err),
|
||||
#[cfg(feature = "rpc")]
|
||||
Self::Rpc(err) => write!(f, "RPC client error: {}", err),
|
||||
#[cfg(feature = "sqlite")]
|
||||
Self::Rusqlite(err) => write!(f, "SQLite error: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ pub mod bip39;
|
||||
/// Set of valid networks for a key
|
||||
pub type ValidNetworks = HashSet<Network>;
|
||||
|
||||
/// Create a set containing mainnet, testnet and regtest
|
||||
/// Create a set containing mainnet, testnet, signet, and regtest
|
||||
pub fn any_network() -> ValidNetworks {
|
||||
vec![
|
||||
Network::Bitcoin,
|
||||
@@ -95,7 +95,7 @@ impl<Ctx: ScriptContext> DescriptorKey<Ctx> {
|
||||
}
|
||||
|
||||
// This method is used internally by `bdk::fragment!` and `bdk::descriptor!`. It has to be
|
||||
// public because it is effectively called by external crates, once the macros are expanded,
|
||||
// public because it is effectively called by external crates once the macros are expanded,
|
||||
// but since it is not meant to be part of the public api we hide it from the docs.
|
||||
#[doc(hidden)]
|
||||
pub fn extract(
|
||||
@@ -375,7 +375,7 @@ impl<Ctx: ScriptContext> From<bip32::ExtendedPrivKey> for ExtendedKey<Ctx> {
|
||||
/// `(DerivableKey, KeySource, DerivationPath)` tuples.
|
||||
///
|
||||
/// For key types that don't encode any indication about the path to use (like bip39), it's
|
||||
/// generally recommended to implemented this trait instead of [`IntoDescriptorKey`]. The same
|
||||
/// generally recommended to implement this trait instead of [`IntoDescriptorKey`]. The same
|
||||
/// rules regarding script context and valid networks apply.
|
||||
///
|
||||
/// ## Examples
|
||||
@@ -935,7 +935,14 @@ impl_error!(bitcoin::util::bip32::Error, Bip32, KeyError);
|
||||
|
||||
impl std::fmt::Display for KeyError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
match self {
|
||||
Self::InvalidScriptContext => write!(f, "Invalid script context"),
|
||||
Self::InvalidNetwork => write!(f, "Invalid network"),
|
||||
Self::InvalidChecksum => write!(f, "Invalid checksum"),
|
||||
Self::Message(err) => write!(f, "{}", err),
|
||||
Self::Bip32(err) => write!(f, "BIP32 error: {}", err),
|
||||
Self::Miniscript(err) => write!(f, "Miniscript error: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ compile_error!(
|
||||
#[cfg(feature = "keys-bip39")]
|
||||
extern crate bip39;
|
||||
|
||||
#[cfg(any(target_arch = "wasm32", feature = "async-interface"))]
|
||||
#[cfg(feature = "async-interface")]
|
||||
#[macro_use]
|
||||
extern crate async_trait;
|
||||
#[macro_use]
|
||||
|
||||
@@ -22,7 +22,7 @@ pub trait PsbtUtils {
|
||||
/// Get the `TxOut` for the specified input index, if it doesn't exist in the PSBT `None` is returned.
|
||||
fn get_utxo_for(&self, input_index: usize) -> Option<TxOut>;
|
||||
|
||||
/// The total transaction fee amount, sum of input amounts minus sum of output amounts, in Sats.
|
||||
/// The total transaction fee amount, sum of input amounts minus sum of output amounts, in sats.
|
||||
/// If the PSBT is missing a TxOut for an input returns None.
|
||||
fn fee_amount(&self) -> Option<u64>;
|
||||
|
||||
|
||||
@@ -230,7 +230,6 @@ pub struct TransactionDetails {
|
||||
pub transaction: Option<Transaction>,
|
||||
/// Transaction id
|
||||
pub txid: Txid,
|
||||
|
||||
/// Received value (sats)
|
||||
/// Sum of owned outputs of this transaction.
|
||||
pub received: u64,
|
||||
@@ -242,7 +241,7 @@ pub struct TransactionDetails {
|
||||
/// Server backend, but it could be `None` with a Bitcoin RPC node without txindex that receive
|
||||
/// funds while offline.
|
||||
pub fee: Option<u64>,
|
||||
/// If the transaction is confirmed, contains height and timestamp of the block containing the
|
||||
/// If the transaction is confirmed, contains height and Unix timestamp of the block containing the
|
||||
/// transaction, unconfirmed transaction contains `None`.
|
||||
pub confirmation_time: Option<BlockTime>,
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -132,7 +132,7 @@ pub enum AddressIndex {
|
||||
Reset(u32),
|
||||
}
|
||||
|
||||
/// A derived address and the index it was found at
|
||||
/// A derived address and the index it was found at.
|
||||
/// For convenience this automatically derefs to `Address`
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct AddressInfo {
|
||||
@@ -1084,7 +1084,7 @@ where
|
||||
}
|
||||
|
||||
/// Sign a transaction with all the wallet's signers, in the order specified by every signer's
|
||||
/// [`SignerOrdering`]
|
||||
/// [`SignerOrdering`]. This function returns the `Result` type with an encapsulated `bool` that has the value true if the PSBT was finalized, or false otherwise.
|
||||
///
|
||||
/// The [`SignOptions`] can be used to tweak the behavior of the software signers, and the way
|
||||
/// the transaction is finalized at the end. Note that it can't be guaranteed that *every*
|
||||
@@ -1719,14 +1719,11 @@ where
|
||||
let max_rounds = if has_wildcard { 100 } else { 1 };
|
||||
|
||||
for _ in 0..max_rounds {
|
||||
let sync_res =
|
||||
if run_setup {
|
||||
maybe_await!(blockchain
|
||||
.wallet_setup(self.database.borrow_mut().deref_mut(), new_progress()))
|
||||
} else {
|
||||
maybe_await!(blockchain
|
||||
.wallet_sync(self.database.borrow_mut().deref_mut(), new_progress()))
|
||||
};
|
||||
let sync_res = if run_setup {
|
||||
maybe_await!(blockchain.wallet_setup(&self.database, new_progress()))
|
||||
} else {
|
||||
maybe_await!(blockchain.wallet_sync(&self.database, new_progress()))
|
||||
};
|
||||
|
||||
// If the error is the special `MissingCachedScripts` error, we return the number of
|
||||
// scripts we should ensure cached.
|
||||
@@ -2939,6 +2936,25 @@ pub(crate) mod test {
|
||||
assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(144));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_policy_path_ignored_subtree_with_csv() {
|
||||
let (wallet, _, _) = get_funded_wallet("wsh(or_d(pk(cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu),or_i(and_v(v:pkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),older(30)),and_v(v:pkh(cMnkdebixpXMPfkcNEjjGin7s94hiehAH4mLbYkZoh9KSiNNmqC8),older(90)))))");
|
||||
|
||||
let external_policy = wallet.policies(KeychainKind::External).unwrap().unwrap();
|
||||
let root_id = external_policy.id;
|
||||
// child #0 is pk(cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu)
|
||||
let path = vec![(root_id, vec![0])].into_iter().collect();
|
||||
|
||||
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
.add_recipient(addr.script_pubkey(), 30_000)
|
||||
.policy_path(path, KeychainKind::External);
|
||||
let (psbt, _) = builder.finish().unwrap();
|
||||
|
||||
assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFE));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_tx_global_xpubs_with_origin() {
|
||||
use bitcoin::hashes::hex::FromHex;
|
||||
|
||||
@@ -180,7 +180,22 @@ impl From<sighash::Error> for SignerError {
|
||||
|
||||
impl fmt::Display for SignerError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
match self {
|
||||
Self::MissingKey => write!(f, "Missing private key"),
|
||||
Self::InvalidKey => write!(f, "The private key in use has the right fingerprint but derives differently than expected"),
|
||||
Self::UserCanceled => write!(f, "The user canceled the operation"),
|
||||
Self::InputIndexOutOfRange => write!(f, "Input index out of range"),
|
||||
Self::MissingNonWitnessUtxo => write!(f, "Missing non-witness UTXO"),
|
||||
Self::InvalidNonWitnessUtxo => write!(f, "Invalid non-witness UTXO"),
|
||||
Self::MissingWitnessUtxo => write!(f, "Missing witness UTXO"),
|
||||
Self::MissingWitnessScript => write!(f, "Missing witness script"),
|
||||
Self::MissingHdKeypath => write!(f, "Missing fingerprint and derivation path"),
|
||||
Self::NonStandardSighash => write!(f, "The psbt contains a non standard sighash"),
|
||||
Self::InvalidSighash => write!(f, "Invalid SIGHASH for the signing context in use"),
|
||||
Self::SighashError(err) => write!(f, "Error while computing the hash to sign: {}", err),
|
||||
#[cfg(feature = "hardware-signer")]
|
||||
Self::HWIError(err) => write!(f, "Error while signing using hardware wallets: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,12 @@ pub enum VerifyError {
|
||||
|
||||
impl fmt::Display for VerifyError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
match self {
|
||||
Self::MissingInputTx(txid) => write!(f, "The transaction being spent is not available in the database or the blockchain client: {}", txid),
|
||||
Self::InvalidInput(outpoint) => write!(f, "The transaction being spent doesn't have the requested output: {}", outpoint),
|
||||
Self::Consensus(err) => write!(f, "Consensus error: {:?}", err),
|
||||
Self::Global(err) => write!(f, "Generic error: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user