Compare commits
37 Commits
release/0.
...
v0.16.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f65e8c64b | ||
|
|
3f0101d317 | ||
|
|
b1346d4ccf | ||
|
|
5107ff80c1 | ||
|
|
5ac51dfe74 | ||
|
|
04d58f7903 | ||
|
|
380a4f2588 | ||
|
|
9e30a79027 | ||
|
|
fdb272e039 | ||
|
|
d2b6b5545e | ||
|
|
db6ffb90f0 | ||
|
|
947a9c29db | ||
|
|
61ee2a9c1c | ||
|
|
44e4c5dac5 | ||
|
|
e09aaf055a | ||
|
|
c40898ba08 | ||
|
|
2f98db8549 | ||
|
|
4d7c4bc810 | ||
|
|
a0c140bb29 | ||
|
|
bf5994b14a | ||
|
|
ca682819b3 | ||
|
|
ee41d88f25 | ||
|
|
beb1e4114d | ||
|
|
af047f90db | ||
|
|
d01ec6d259 | ||
|
|
77bce06caf | ||
|
|
98c26a1ad9 | ||
|
|
1a907f8a53 | ||
|
|
e82edbb7ac | ||
|
|
57a1185aef | ||
|
|
64e88f0e00 | ||
|
|
f7f9bd2409 | ||
|
|
68a3d2b1cc | ||
|
|
aa13186fb0 | ||
|
|
02980881ac | ||
|
|
084ec036a5 | ||
|
|
7f8103dd76 |
2
.github/ISSUE_TEMPLATE/summer_project.md
vendored
2
.github/ISSUE_TEMPLATE/summer_project.md
vendored
@@ -32,7 +32,7 @@ fundamentals - achievable over 12-weeks. Below are just a few types of ideas:
|
|||||||
create new innovations and new perspectives for your project.
|
create new innovations and new perspectives for your project.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
**Descriptive Title**
|
**Description**
|
||||||
<!-- Description: 3-7 sentences describing the project background and tasks to be done. -->
|
<!-- Description: 3-7 sentences describing the project background and tasks to be done. -->
|
||||||
|
|
||||||
**Expected Outcomes**
|
**Expected Outcomes**
|
||||||
|
|||||||
11
.github/workflows/cont_integration.yml
vendored
11
.github/workflows/cont_integration.yml
vendored
@@ -10,8 +10,9 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
rust:
|
rust:
|
||||||
- 1.56.0 # STABLE
|
- version: 1.56.0 # STABLE
|
||||||
- 1.46.0 # MSRV
|
clippy: true
|
||||||
|
- version: 1.46.0 # MSRV
|
||||||
features:
|
features:
|
||||||
- default
|
- default
|
||||||
- minimal
|
- minimal
|
||||||
@@ -31,7 +32,7 @@ jobs:
|
|||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Generate cache key
|
- name: Generate cache key
|
||||||
run: echo "${{ matrix.rust }} ${{ matrix.features }}" | tee .cache_key
|
run: echo "${{ matrix.rust.version }} ${{ matrix.features }}" | tee .cache_key
|
||||||
- name: cache
|
- name: cache
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
@@ -41,16 +42,18 @@ jobs:
|
|||||||
target
|
target
|
||||||
key: ${{ runner.os }}-cargo-${{ hashFiles('.cache_key') }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
key: ${{ runner.os }}-cargo-${{ hashFiles('.cache_key') }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||||
- name: Set default toolchain
|
- name: Set default toolchain
|
||||||
run: rustup default ${{ matrix.rust }}
|
run: rustup default ${{ matrix.rust.version }}
|
||||||
- name: Set profile
|
- name: Set profile
|
||||||
run: rustup set profile minimal
|
run: rustup set profile minimal
|
||||||
- name: Add clippy
|
- name: Add clippy
|
||||||
|
if: ${{ matrix.rust.clippy }}
|
||||||
run: rustup component add clippy
|
run: rustup component add clippy
|
||||||
- name: Update toolchain
|
- name: Update toolchain
|
||||||
run: rustup update
|
run: rustup update
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cargo build --features ${{ matrix.features }} --no-default-features
|
run: cargo build --features ${{ matrix.features }} --no-default-features
|
||||||
- name: Clippy
|
- name: Clippy
|
||||||
|
if: ${{ matrix.rust.clippy }}
|
||||||
run: cargo clippy --all-targets --features ${{ matrix.features }} --no-default-features -- -D warnings
|
run: cargo clippy --all-targets --features ${{ matrix.features }} --no-default-features -- -D warnings
|
||||||
- name: Test
|
- name: Test
|
||||||
run: cargo test --features ${{ matrix.features }} --no-default-features
|
run: cargo test --features ${{ matrix.features }} --no-default-features
|
||||||
|
|||||||
8
.github/workflows/nightly_docs.yml
vendored
8
.github/workflows/nightly_docs.yml
vendored
@@ -44,18 +44,18 @@ jobs:
|
|||||||
repository: bitcoindevkit/bitcoindevkit.org
|
repository: bitcoindevkit/bitcoindevkit.org
|
||||||
ref: master
|
ref: master
|
||||||
- name: Create directories
|
- name: Create directories
|
||||||
run: mkdir -p ./static/docs-rs/bdk/nightly
|
run: mkdir -p ./docs/.vuepress/public/docs-rs/bdk/nightly
|
||||||
- name: Remove old latest
|
- name: Remove old latest
|
||||||
run: rm -rf ./static/docs-rs/bdk/nightly/latest
|
run: rm -rf ./docs/.vuepress/public/docs-rs/bdk/nightly/latest
|
||||||
- name: Download built docs
|
- name: Download built docs
|
||||||
uses: actions/download-artifact@v1
|
uses: actions/download-artifact@v1
|
||||||
with:
|
with:
|
||||||
name: built-docs
|
name: built-docs
|
||||||
path: ./static/docs-rs/bdk/nightly/latest
|
path: ./docs/.vuepress/public/docs-rs/bdk/nightly/latest
|
||||||
- name: Configure git
|
- name: Configure git
|
||||||
run: git config user.email "github-actions@github.com" && git config user.name "github-actions"
|
run: git config user.email "github-actions@github.com" && git config user.name "github-actions"
|
||||||
- name: Commit
|
- name: Commit
|
||||||
continue-on-error: true # If there's nothing to commit this step fails, but it's fine
|
continue-on-error: true # If there's nothing to commit this step fails, but it's fine
|
||||||
run: git add ./static && git commit -m "Publish autogenerated nightly docs"
|
run: git add ./docs/.vuepress/public/docs-rs && git commit -m "Publish autogenerated nightly docs"
|
||||||
- name: Push
|
- name: Push
|
||||||
run: git push origin master
|
run: git push origin master
|
||||||
|
|||||||
11
CHANGELOG.md
11
CHANGELOG.md
@@ -6,9 +6,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [v0.16.0] - [v0.15.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
|
||||||
|
|
||||||
|
## [v0.15.0] - [v0.14.0]
|
||||||
|
|
||||||
- Overhauled sync logic for electrum and esplora.
|
- 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.
|
- 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.
|
- Fixed esplora fee estimation.
|
||||||
|
- Fixed generating WIF in the correct network format.
|
||||||
|
|
||||||
## [v0.14.0] - [v0.13.0]
|
## [v0.14.0] - [v0.13.0]
|
||||||
|
|
||||||
@@ -401,3 +410,5 @@ final transaction is created by calling `finish` on the builder.
|
|||||||
[v0.12.0]: https://github.com/bitcoindevkit/bdk/compare/v0.11.0...v0.12.0
|
[v0.12.0]: https://github.com/bitcoindevkit/bdk/compare/v0.11.0...v0.12.0
|
||||||
[v0.13.0]: https://github.com/bitcoindevkit/bdk/compare/v0.12.0...v0.13.0
|
[v0.13.0]: https://github.com/bitcoindevkit/bdk/compare/v0.12.0...v0.13.0
|
||||||
[v0.14.0]: https://github.com/bitcoindevkit/bdk/compare/v0.13.0...v0.14.0
|
[v0.14.0]: https://github.com/bitcoindevkit/bdk/compare/v0.13.0...v0.14.0
|
||||||
|
[v0.15.0]: https://github.com/bitcoindevkit/bdk/compare/v0.14.0...v0.15.0
|
||||||
|
[v0.16.0]: https://github.com/bitcoindevkit/bdk/compare/v0.15.0...v0.16.0
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "bdk"
|
name = "bdk"
|
||||||
version = "0.14.1-dev"
|
version = "0.16.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"
|
||||||
@@ -25,7 +25,7 @@ sled = { version = "0.34", optional = true }
|
|||||||
electrum-client = { version = "0.8", optional = true }
|
electrum-client = { version = "0.8", optional = true }
|
||||||
rusqlite = { version = "0.25.3", optional = true }
|
rusqlite = { version = "0.25.3", optional = true }
|
||||||
ahash = { version = "=0.7.4", optional = true }
|
ahash = { version = "=0.7.4", optional = true }
|
||||||
reqwest = { version = "0.11", optional = true, features = ["json"] }
|
reqwest = { version = "0.11", optional = true, default-features = false, features = ["json"] }
|
||||||
ureq = { version = "~2.2.0", features = ["json"], optional = true }
|
ureq = { version = "~2.2.0", features = ["json"], optional = true }
|
||||||
futures = { version = "0.3", optional = true }
|
futures = { version = "0.3", optional = true }
|
||||||
async-trait = { version = "0.1", optional = true }
|
async-trait = { version = "0.1", optional = true }
|
||||||
@@ -82,6 +82,8 @@ use-esplora-ureq = ["esplora", "ureq", "ureq/socks"]
|
|||||||
# Typical configurations will not need to use `esplora` feature directly.
|
# Typical configurations will not need to use `esplora` feature directly.
|
||||||
esplora = []
|
esplora = []
|
||||||
|
|
||||||
|
# Use below feature with `use-esplora-reqwest` to enable reqwest default TLS support
|
||||||
|
reqwest-default-tls = ["reqwest/default-tls"]
|
||||||
|
|
||||||
# Debug/Test features
|
# Debug/Test features
|
||||||
test-blockchains = ["bitcoincore-rpc", "electrum-client"]
|
test-blockchains = ["bitcoincore-rpc", "electrum-client"]
|
||||||
@@ -94,7 +96,7 @@ test-md-docs = ["electrum"]
|
|||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
env_logger = "0.7"
|
env_logger = "0.7"
|
||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
electrsd = { version= "0.12", features = ["trigger", "bitcoind_22_0"] }
|
electrsd = { version= "0.13", features = ["trigger", "bitcoind_22_0"] }
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "address_validator"
|
name = "address_validator"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<h1>BDK</h1>
|
<h1>BDK</h1>
|
||||||
|
|
||||||
<img src="./static/bdk.svg" width="220" />
|
<img src="./static/bdk.png" width="220" />
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<strong>A modern, lightweight, descriptor-based wallet library written in Rust!</strong>
|
<strong>A modern, lightweight, descriptor-based wallet library written in Rust!</strong>
|
||||||
|
|||||||
@@ -398,7 +398,7 @@ impl ChainStore<Full> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete full blocks overriden by snapshot
|
// Delete full blocks overridden by snapshot
|
||||||
let from_key = StoreEntry::Block(Some(snaphost.min_height)).get_key();
|
let from_key = StoreEntry::Block(Some(snaphost.min_height)).get_key();
|
||||||
let to_key = StoreEntry::Block(Some(usize::MAX)).get_key();
|
let to_key = StoreEntry::Block(Some(usize::MAX)).get_key();
|
||||||
batch.delete_range(&from_key, &to_key);
|
batch.delete_range(&from_key, &to_key);
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ fn after(key: &[u8]) -> Vec<u8> {
|
|||||||
/// Once it's dropped its content will be lost.
|
/// Once it's dropped its content will be lost.
|
||||||
///
|
///
|
||||||
/// If you are looking for a permanent storage solution, you can try with the default key-value
|
/// If you are looking for a permanent storage solution, you can try with the default key-value
|
||||||
/// database called [`sled`]. See the [`database`] module documentation for more defailts.
|
/// database called [`sled`]. See the [`database`] module documentation for more details.
|
||||||
///
|
///
|
||||||
/// [`database`]: crate::database
|
/// [`database`]: crate::database
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ pub trait Database: BatchOperations {
|
|||||||
fn get_raw_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error>;
|
fn get_raw_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error>;
|
||||||
/// Fetch the transaction metadata and optionally also the raw transaction
|
/// Fetch the transaction metadata and optionally also the raw transaction
|
||||||
fn get_tx(&self, txid: &Txid, include_raw: bool) -> Result<Option<TransactionDetails>, Error>;
|
fn get_tx(&self, txid: &Txid, include_raw: bool) -> Result<Option<TransactionDetails>, Error>;
|
||||||
/// Return the last defivation index for a keychain.
|
/// Return the last derivation index for a keychain.
|
||||||
fn get_last_index(&self, keychain: KeychainKind) -> Result<Option<u32>, Error>;
|
fn get_last_index(&self, keychain: KeychainKind) -> Result<Option<u32>, Error>;
|
||||||
/// Return the sync time, if present
|
/// Return the sync time, if present
|
||||||
fn get_sync_time(&self) -> Result<Option<SyncTime>, Error>;
|
fn get_sync_time(&self) -> Result<Option<SyncTime>, Error>;
|
||||||
|
|||||||
@@ -521,7 +521,7 @@ macro_rules! fragment_internal {
|
|||||||
// three operands it's (X, (X, (X, ()))), etc.
|
// three operands it's (X, (X, (X, ()))), etc.
|
||||||
//
|
//
|
||||||
// To check that the right number of arguments has been passed we can "cast" those tuples to
|
// To check that the right number of arguments has been passed we can "cast" those tuples to
|
||||||
// more convenient structures like `TupleTwo`. If the conversion succedes, the right number of
|
// more convenient structures like `TupleTwo`. If the conversion succeeds, the right number of
|
||||||
// args was passed. Otherwise the compilation fails entirely.
|
// args was passed. Otherwise the compilation fails entirely.
|
||||||
( @t $op:ident ( $( $args:tt )* ) $( $tail:tt )* ) => ({
|
( @t $op:ident ( $( $args:tt )* ) $( $tail:tt )* ) => ({
|
||||||
($crate::fragment!( $op ( $( $args )* ) ), $crate::fragment_internal!( @t $( $tail )* ))
|
($crate::fragment!( $op ( $( $args )* ) ), $crate::fragment_internal!( @t $( $tail )* ))
|
||||||
|
|||||||
@@ -667,7 +667,7 @@ mod test {
|
|||||||
|
|
||||||
// make a descriptor out of it
|
// make a descriptor out of it
|
||||||
let desc = crate::descriptor!(wpkh(key)).unwrap();
|
let desc = crate::descriptor!(wpkh(key)).unwrap();
|
||||||
// this should conver the key that supports "any_network" to the right network (testnet)
|
// this should convert the key that supports "any_network" to the right network (testnet)
|
||||||
let (wallet_desc, _) = desc
|
let (wallet_desc, _) = desc
|
||||||
.into_wallet_descriptor(&secp, Network::Testnet)
|
.into_wallet_descriptor(&secp, Network::Testnet)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@@ -354,9 +354,9 @@ impl Satisfaction {
|
|||||||
indexes
|
indexes
|
||||||
.into_iter()
|
.into_iter()
|
||||||
// .inspect(|x| println!("--- orig --- {:?}", x))
|
// .inspect(|x| println!("--- orig --- {:?}", x))
|
||||||
// we map each of the combinations of elements into a tuple of ([choosen items], [conditions]). unfortunately, those items have potentially more than one
|
// we map each of the combinations of elements into a tuple of ([chosen items], [conditions]). unfortunately, those items have potentially more than one
|
||||||
// condition (think about ORs), so we also use `mix` to expand those, i.e. [[0], [1, 2]] becomes [[0, 1], [0, 2]]. This is necessary to make sure that we
|
// condition (think about ORs), so we also use `mix` to expand those, i.e. [[0], [1, 2]] becomes [[0, 1], [0, 2]]. This is necessary to make sure that we
|
||||||
// consider every possibile options and check whether or not they are compatible.
|
// consider every possible options and check whether or not they are compatible.
|
||||||
.map(|i_vec| {
|
.map(|i_vec| {
|
||||||
mix(i_vec
|
mix(i_vec
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ pub enum Error {
|
|||||||
Hex(bitcoin::hashes::hex::Error),
|
Hex(bitcoin::hashes::hex::Error),
|
||||||
/// Partially signed bitcoin transaction error
|
/// Partially signed bitcoin transaction error
|
||||||
Psbt(bitcoin::util::psbt::Error),
|
Psbt(bitcoin::util::psbt::Error),
|
||||||
/// Partially signed bitcoin transaction parseerror
|
/// Partially signed bitcoin transaction parse error
|
||||||
PsbtParse(bitcoin::util::psbt::PsbtParseError),
|
PsbtParse(bitcoin::util::psbt::PsbtParseError),
|
||||||
|
|
||||||
//KeyMismatch(bitcoin::secp256k1::PublicKey, bitcoin::secp256k1::PublicKey),
|
//KeyMismatch(bitcoin::secp256k1::PublicKey, bitcoin::secp256k1::PublicKey),
|
||||||
|
|||||||
@@ -319,6 +319,7 @@ impl<Ctx: ScriptContext> ExtendedKey<Ctx> {
|
|||||||
match self {
|
match self {
|
||||||
ExtendedKey::Private((mut xprv, _)) => {
|
ExtendedKey::Private((mut xprv, _)) => {
|
||||||
xprv.network = network;
|
xprv.network = network;
|
||||||
|
xprv.private_key.network = network;
|
||||||
Some(xprv)
|
Some(xprv)
|
||||||
}
|
}
|
||||||
ExtendedKey::Public(_) => None,
|
ExtendedKey::Public(_) => None,
|
||||||
@@ -356,7 +357,7 @@ impl<Ctx: ScriptContext> From<bip32::ExtendedPrivKey> for ExtendedKey<Ctx> {
|
|||||||
|
|
||||||
/// Trait for keys that can be derived.
|
/// Trait for keys that can be derived.
|
||||||
///
|
///
|
||||||
/// When extra metadata are provided, a [`DerivableKey`] can be transofrmed into a
|
/// When extra metadata are provided, a [`DerivableKey`] can be transformed into a
|
||||||
/// [`DescriptorKey`]: the trait [`IntoDescriptorKey`] is automatically implemented
|
/// [`DescriptorKey`]: the trait [`IntoDescriptorKey`] is automatically implemented
|
||||||
/// for `(DerivableKey, DerivationPath)` and
|
/// for `(DerivableKey, DerivationPath)` and
|
||||||
/// `(DerivableKey, KeySource, DerivationPath)` tuples.
|
/// `(DerivableKey, KeySource, DerivationPath)` tuples.
|
||||||
@@ -931,4 +932,43 @@ pub mod test {
|
|||||||
"L2wTu6hQrnDMiFNWA5na6jB12ErGQqtXwqpSL7aWquJaZG8Ai3ch"
|
"L2wTu6hQrnDMiFNWA5na6jB12ErGQqtXwqpSL7aWquJaZG8Ai3ch"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_keys_wif_network() {
|
||||||
|
// test mainnet wif
|
||||||
|
let generated_xprv: GeneratedKey<_, miniscript::Segwitv0> =
|
||||||
|
bip32::ExtendedPrivKey::generate_with_entropy_default(TEST_ENTROPY).unwrap();
|
||||||
|
let xkey = generated_xprv.into_extended_key().unwrap();
|
||||||
|
|
||||||
|
let network = Network::Bitcoin;
|
||||||
|
let xprv = xkey.into_xprv(network).unwrap();
|
||||||
|
let wif = PrivateKey::from_wif(&xprv.private_key.to_wif()).unwrap();
|
||||||
|
assert_eq!(wif.network, network);
|
||||||
|
|
||||||
|
// test testnet wif
|
||||||
|
let generated_xprv: GeneratedKey<_, miniscript::Segwitv0> =
|
||||||
|
bip32::ExtendedPrivKey::generate_with_entropy_default(TEST_ENTROPY).unwrap();
|
||||||
|
let xkey = generated_xprv.into_extended_key().unwrap();
|
||||||
|
|
||||||
|
let network = Network::Testnet;
|
||||||
|
let xprv = xkey.into_xprv(network).unwrap();
|
||||||
|
let wif = PrivateKey::from_wif(&xprv.private_key.to_wif()).unwrap();
|
||||||
|
assert_eq!(wif.network, network);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "keys-bip39")]
|
||||||
|
#[test]
|
||||||
|
fn test_keys_wif_network_bip39() {
|
||||||
|
let xkey: ExtendedKey = bip39::Mnemonic::parse_in(
|
||||||
|
bip39::Language::English,
|
||||||
|
"jelly crash boy whisper mouse ecology tuna soccer memory million news short",
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.into_extended_key()
|
||||||
|
.unwrap();
|
||||||
|
let xprv = xkey.into_xprv(Network::Testnet).unwrap();
|
||||||
|
let wif = PrivateKey::from_wif(&xprv.private_key.to_wif()).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(wif.network, Network::Testnet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,10 @@
|
|||||||
// only enables the `doc_cfg` feature when
|
// only enables the `doc_cfg` feature when
|
||||||
// the `docsrs` configuration attribute is defined
|
// the `docsrs` configuration attribute is defined
|
||||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
|
#![cfg_attr(
|
||||||
|
docsrs,
|
||||||
|
doc(html_logo_url = "https://github.com/bitcoindevkit/bdk/raw/master/static/bdk.png")
|
||||||
|
)]
|
||||||
|
|
||||||
//! A modern, lightweight, descriptor-based wallet library written in Rust.
|
//! A modern, lightweight, descriptor-based wallet library written in Rust.
|
||||||
//!
|
//!
|
||||||
@@ -40,7 +44,7 @@
|
|||||||
//! interact with the bitcoin P2P network.
|
//! interact with the bitcoin P2P network.
|
||||||
//!
|
//!
|
||||||
//! ```toml
|
//! ```toml
|
||||||
//! bdk = "0.14.0"
|
//! bdk = "0.16.0"
|
||||||
//! ```
|
//! ```
|
||||||
# backend can't trick the
|
/// ensure an unstrusted [`Blockchain`](crate::blockchain::Blockchain) backend can't trick the
|
||||||
/// wallet into using an invalid tx as an RBF template.
|
/// wallet into using an invalid tx as an RBF template.
|
||||||
///
|
///
|
||||||
/// The check is only perfomed when the `verify` feature is enabled.
|
/// The check is only performed when the `verify` feature is enabled.
|
||||||
#[serde(default = "bool::default")] // default to `false` if not specified
|
#[serde(default = "bool::default")] // default to `false` if not specified
|
||||||
pub verified: bool,
|
pub verified: bool,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -372,7 +372,7 @@ impl<D: Database> CoinSelectionAlgorithm<D> for BranchAndBoundCoinSelection {
|
|||||||
|
|
||||||
impl BranchAndBoundCoinSelection {
|
impl BranchAndBoundCoinSelection {
|
||||||
// TODO: make this more Rust-onic :)
|
// TODO: make this more Rust-onic :)
|
||||||
// (And perhpaps refactor with less arguments?)
|
// (And perhaps refactor with less arguments?)
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn bnb(
|
fn bnb(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ use address_validator::AddressValidator;
|
|||||||
use coin_selection::DefaultCoinSelectionAlgorithm;
|
use coin_selection::DefaultCoinSelectionAlgorithm;
|
||||||
use signer::{SignOptions, Signer, SignerOrdering, SignersContainer};
|
use signer::{SignOptions, Signer, SignerOrdering, SignersContainer};
|
||||||
use tx_builder::{BumpFee, CreateTx, FeePolicy, TxBuilder, TxParams};
|
use tx_builder::{BumpFee, CreateTx, FeePolicy, TxBuilder, TxParams};
|
||||||
use utils::{check_nlocktime, check_nsequence_rbf, After, Older, SecpCtx, DUST_LIMIT_SATOSHI};
|
use utils::{check_nlocktime, check_nsequence_rbf, After, Older, SecpCtx};
|
||||||
|
|
||||||
use crate::blockchain::{Blockchain, Progress};
|
use crate::blockchain::{Blockchain, Progress};
|
||||||
use crate::database::memory::MemoryDatabase;
|
use crate::database::memory::MemoryDatabase;
|
||||||
@@ -601,7 +601,7 @@ where
|
|||||||
let recipients = params.recipients.iter().map(|(r, v)| (r, *v));
|
let recipients = params.recipients.iter().map(|(r, v)| (r, *v));
|
||||||
|
|
||||||
for (index, (script_pubkey, value)) in recipients.enumerate() {
|
for (index, (script_pubkey, value)) in recipients.enumerate() {
|
||||||
if value.is_dust() && !script_pubkey.is_provably_unspendable() {
|
if value.is_dust(script_pubkey) && !script_pubkey.is_provably_unspendable() {
|
||||||
return Err(Error::OutputBelowDustLimit(index));
|
return Err(Error::OutputBelowDustLimit(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -677,9 +677,9 @@ where
|
|||||||
|
|
||||||
if tx.output.is_empty() {
|
if tx.output.is_empty() {
|
||||||
if params.drain_to.is_some() {
|
if params.drain_to.is_some() {
|
||||||
if drain_val.is_dust() {
|
if drain_val.is_dust(&drain_output.script_pubkey) {
|
||||||
return Err(Error::InsufficientFunds {
|
return Err(Error::InsufficientFunds {
|
||||||
needed: DUST_LIMIT_SATOSHI,
|
needed: drain_output.script_pubkey.dust_value().as_sat(),
|
||||||
available: drain_val,
|
available: drain_val,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -688,7 +688,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if drain_val.is_dust() {
|
if drain_val.is_dust(&drain_output.script_pubkey) {
|
||||||
fee_amount += drain_val;
|
fee_amount += drain_val;
|
||||||
} else {
|
} else {
|
||||||
drain_output.value = drain_val;
|
drain_output.value = drain_val;
|
||||||
@@ -721,7 +721,7 @@ where
|
|||||||
/// Bump the fee of a transaction previously created with this wallet.
|
/// Bump the fee of a transaction previously created with this wallet.
|
||||||
///
|
///
|
||||||
/// Returns an error if the transaction is already confirmed or doesn't explicitly signal
|
/// Returns an error if the transaction is already confirmed or doesn't explicitly signal
|
||||||
/// *repalce by fee* (RBF). If the transaction can be fee bumped then it returns a [`TxBuilder`]
|
/// *replace by fee* (RBF). If the transaction can be fee bumped then it returns a [`TxBuilder`]
|
||||||
/// pre-populated with the inputs and outputs of the original transaction.
|
/// pre-populated with the inputs and outputs of the original transaction.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
@@ -1059,7 +1059,7 @@ where
|
|||||||
&self.secp
|
&self.secp
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the descriptor used to create adddresses for a particular `keychain`.
|
/// Returns the descriptor used to create addresses for a particular `keychain`.
|
||||||
pub fn get_descriptor_for_keychain(&self, keychain: KeychainKind) -> &ExtendedDescriptor {
|
pub fn get_descriptor_for_keychain(&self, keychain: KeychainKind) -> &ExtendedDescriptor {
|
||||||
let (descriptor, _) = self._get_descriptor_for_keychain(keychain);
|
let (descriptor, _) = self._get_descriptor_for_keychain(keychain);
|
||||||
descriptor
|
descriptor
|
||||||
@@ -3424,7 +3424,7 @@ pub(crate) mod test {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut builder = wallet.build_fee_bump(txid).unwrap();
|
let mut builder = wallet.build_fee_bump(txid).unwrap();
|
||||||
builder.fee_rate(FeeRate::from_sat_per_vb(140.0));
|
builder.fee_rate(FeeRate::from_sat_per_vb(141.0));
|
||||||
let (psbt, details) = builder.finish().unwrap();
|
let (psbt, details) = builder.finish().unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ pub enum SignerError {
|
|||||||
InvalidNonWitnessUtxo,
|
InvalidNonWitnessUtxo,
|
||||||
/// The `witness_utxo` field of the transaction is required to sign this input
|
/// The `witness_utxo` field of the transaction is required to sign this input
|
||||||
MissingWitnessUtxo,
|
MissingWitnessUtxo,
|
||||||
/// The `witness_script` field of the transaction is requied to sign this input
|
/// The `witness_script` field of the transaction is required to sign this input
|
||||||
MissingWitnessScript,
|
MissingWitnessScript,
|
||||||
/// The fingerprint and derivation path are missing from the psbt input
|
/// The fingerprint and derivation path are missing from the psbt input
|
||||||
MissingHdKeypath,
|
MissingHdKeypath,
|
||||||
@@ -289,7 +289,7 @@ impl Signer for PrivateKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: use the presence of `witness_utxo` as an indication that we should make a bip143
|
// FIXME: use the presence of `witness_utxo` as an indication that we should make a bip143
|
||||||
// sig. Does this make sense? Should we add an extra argument to explicitly swith between
|
// sig. Does this make sense? Should we add an extra argument to explicitly switch between
|
||||||
// these? The original idea was to declare sign() as sign<Ctx: ScriptContex>() and use Ctx,
|
// these? The original idea was to declare sign() as sign<Ctx: ScriptContex>() and use Ctx,
|
||||||
// but that violates the rules for trait-objects, so we can't do it.
|
// but that violates the rules for trait-objects, so we can't do it.
|
||||||
let (hash, sighash) = match psbt.inputs[input_index].witness_utxo {
|
let (hash, sighash) = match psbt.inputs[input_index].witness_utxo {
|
||||||
|
|||||||
@@ -310,7 +310,7 @@ impl<'a, B, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderConte
|
|||||||
/// 2. `psbt_input`: To know the value.
|
/// 2. `psbt_input`: To know the value.
|
||||||
/// 3. `satisfaction_weight`: To know how much weight/vbytes the input will add to the transaction for fee calculation.
|
/// 3. `satisfaction_weight`: To know how much weight/vbytes the input will add to the transaction for fee calculation.
|
||||||
///
|
///
|
||||||
/// There are several security concerns about adding foregin UTXOs that application
|
/// There are several security concerns about adding foreign UTXOs that application
|
||||||
/// developers should consider. First, how do you know the value of the input is correct? If a
|
/// developers should consider. First, how do you know the value of the input is correct? If a
|
||||||
/// `non_witness_utxo` is provided in the `psbt_input` then this method implicitly verifies the
|
/// `non_witness_utxo` is provided in the `psbt_input` then this method implicitly verifies the
|
||||||
/// value by checking it against the transaction. If only a `witness_utxo` is provided then this
|
/// value by checking it against the transaction. If only a `witness_utxo` is provided then this
|
||||||
|
|||||||
@@ -9,13 +9,11 @@
|
|||||||
// You may not use this file except in accordance with one or both of these
|
// You may not use this file except in accordance with one or both of these
|
||||||
// licenses.
|
// licenses.
|
||||||
|
|
||||||
|
use bitcoin::blockdata::script::Script;
|
||||||
use bitcoin::secp256k1::{All, Secp256k1};
|
use bitcoin::secp256k1::{All, Secp256k1};
|
||||||
|
|
||||||
use miniscript::{MiniscriptKey, Satisfier, ToPublicKey};
|
use miniscript::{MiniscriptKey, Satisfier, ToPublicKey};
|
||||||
|
|
||||||
// De-facto standard "dust limit" (even though it should change based on the output type)
|
|
||||||
pub const DUST_LIMIT_SATOSHI: u64 = 546;
|
|
||||||
|
|
||||||
// MSB of the nSequence. If set there's no consensus-constraint, so it must be disabled when
|
// MSB of the nSequence. If set there's no consensus-constraint, so it must be disabled when
|
||||||
// spending using CSV in order to enforce CSV rules
|
// spending using CSV in order to enforce CSV rules
|
||||||
pub(crate) const SEQUENCE_LOCKTIME_DISABLE_FLAG: u32 = 1 << 31;
|
pub(crate) const SEQUENCE_LOCKTIME_DISABLE_FLAG: u32 = 1 << 31;
|
||||||
@@ -28,18 +26,19 @@ pub(crate) const SEQUENCE_LOCKTIME_MASK: u32 = 0x0000FFFF;
|
|||||||
// Threshold for nLockTime to be considered a block-height-based timelock rather than time-based
|
// Threshold for nLockTime to be considered a block-height-based timelock rather than time-based
|
||||||
pub(crate) const BLOCKS_TIMELOCK_THRESHOLD: u32 = 500000000;
|
pub(crate) const BLOCKS_TIMELOCK_THRESHOLD: u32 = 500000000;
|
||||||
|
|
||||||
/// Trait to check if a value is below the dust limit
|
/// Trait to check if a value is below the dust limit.
|
||||||
|
/// We are performing dust value calculation for a given script public key using rust-bitcoin to
|
||||||
|
/// keep it compatible with network dust rate
|
||||||
// we implement this trait to make sure we don't mess up the comparison with off-by-one like a <
|
// we implement this trait to make sure we don't mess up the comparison with off-by-one like a <
|
||||||
// instead of a <= etc. The constant value for the dust limit is not public on purpose, to
|
// instead of a <= etc.
|
||||||
// encourage the usage of this trait.
|
|
||||||
pub trait IsDust {
|
pub trait IsDust {
|
||||||
/// Check whether or not a value is below dust limit
|
/// Check whether or not a value is below dust limit
|
||||||
fn is_dust(&self) -> bool;
|
fn is_dust(&self, script: &Script) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IsDust for u64 {
|
impl IsDust for u64 {
|
||||||
fn is_dust(&self) -> bool {
|
fn is_dust(&self, script: &Script) -> bool {
|
||||||
*self <= DUST_LIMIT_SATOSHI
|
*self < script.dust_value().as_sat()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,10 +140,29 @@ pub(crate) type SecpCtx = Secp256k1<All>;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::{
|
use super::{
|
||||||
check_nlocktime, check_nsequence_rbf, BLOCKS_TIMELOCK_THRESHOLD,
|
check_nlocktime, check_nsequence_rbf, IsDust, BLOCKS_TIMELOCK_THRESHOLD,
|
||||||
SEQUENCE_LOCKTIME_TYPE_FLAG,
|
SEQUENCE_LOCKTIME_TYPE_FLAG,
|
||||||
};
|
};
|
||||||
|
use crate::bitcoin::Address;
|
||||||
use crate::types::FeeRate;
|
use crate::types::FeeRate;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_dust() {
|
||||||
|
let script_p2pkh = Address::from_str("1GNgwA8JfG7Kc8akJ8opdNWJUihqUztfPe")
|
||||||
|
.unwrap()
|
||||||
|
.script_pubkey();
|
||||||
|
assert!(script_p2pkh.is_p2pkh());
|
||||||
|
assert!(545.is_dust(&script_p2pkh));
|
||||||
|
assert!(!546.is_dust(&script_p2pkh));
|
||||||
|
|
||||||
|
let script_p2wpkh = Address::from_str("bc1qxlh2mnc0yqwas76gqq665qkggee5m98t8yskd8")
|
||||||
|
.unwrap()
|
||||||
|
.script_pubkey();
|
||||||
|
assert!(script_p2wpkh.is_v0_p2wpkh());
|
||||||
|
assert!(293.is_dust(&script_p2wpkh));
|
||||||
|
assert!(!294.is_dust(&script_p2wpkh));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fee_from_btc_per_kb() {
|
fn test_fee_from_btc_per_kb() {
|
||||||
|
|||||||
BIN
static/bdk.png
Normal file
BIN
static/bdk.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
Reference in New Issue
Block a user