Compare commits
32 Commits
v0.15.0
...
release/0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
552765bb58 | ||
|
|
f3e479fa7f | ||
|
|
5698c683c6 | ||
|
|
a83aa0461c | ||
|
|
fcf422752b | ||
|
|
6fb42fdea1 | ||
|
|
3f65e8c64b | ||
|
|
3f0101d317 | ||
|
|
b1346d4ccf | ||
|
|
5107ff80c1 | ||
|
|
5ac51dfe74 | ||
|
|
04d58f7903 | ||
|
|
380a4f2588 | ||
|
|
9e30a79027 | ||
|
|
fdb272e039 | ||
|
|
d2b6b5545e | ||
|
|
db6ffb90f0 | ||
|
|
947a9c29db | ||
|
|
61ee2a9c1c | ||
|
|
44e4c5dac5 | ||
|
|
e09aaf055a | ||
|
|
c40898ba08 | ||
|
|
2f98db8549 | ||
|
|
a0c140bb29 | ||
|
|
bf5994b14a | ||
|
|
ca682819b3 | ||
|
|
ee41d88f25 | ||
|
|
beb1e4114d | ||
|
|
af047f90db | ||
|
|
d01ec6d259 | ||
|
|
77bce06caf | ||
|
|
1a907f8a53 |
10
.github/workflows/nightly_docs.yml
vendored
10
.github/workflows/nightly_docs.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
|||||||
target
|
target
|
||||||
key: nightly-docs-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
key: nightly-docs-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||||
- name: Set default toolchain
|
- name: Set default toolchain
|
||||||
run: rustup default nightly
|
run: rustup default nightly-2022-01-25
|
||||||
- name: Set profile
|
- name: Set profile
|
||||||
run: rustup set profile minimal
|
run: rustup set profile minimal
|
||||||
- name: Update toolchain
|
- name: Update toolchain
|
||||||
@@ -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
|
||||||
|
|||||||
14
CHANGELOG.md
14
CHANGELOG.md
@@ -6,6 +6,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [v0.16.1] - [v0.16.0]
|
||||||
|
|
||||||
|
- Pin tokio dependency version to ~1.14 to prevent errors due to their new MSRV 1.49.0
|
||||||
|
|
||||||
|
## [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
|
||||||
|
- Fixed generating WIF in the correct network format.
|
||||||
|
|
||||||
## [v0.15.0] - [v0.14.0]
|
## [v0.15.0] - [v0.14.0]
|
||||||
|
|
||||||
- Overhauled sync logic for electrum and esplora.
|
- Overhauled sync logic for electrum and esplora.
|
||||||
@@ -403,3 +414,6 @@ 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
|
||||||
|
[v0.16.1]: https://github.com/bitcoindevkit/bdk/compare/v0.16.0...v0.16.1
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "bdk"
|
name = "bdk"
|
||||||
version = "0.15.0"
|
version = "0.16.2-dev"
|
||||||
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 }
|
||||||
@@ -42,7 +42,7 @@ bitcoincore-rpc = { version = "0.14", optional = true }
|
|||||||
|
|
||||||
# Platform-specific dependencies
|
# Platform-specific dependencies
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
tokio = { version = "1", features = ["rt"] }
|
tokio = { version = "~1.14", features = ["rt"] }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
@@ -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"]
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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,
|
||||||
@@ -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.15.0"
|
//! bdk = "0.16.1"
|
||||||
//! ```
|
//! ```
|
||||||
#![cfg_attr(
|
#![cfg_attr(
|
||||||
feature = "electrum",
|
feature = "electrum",
|
||||||
|
|||||||
@@ -843,7 +843,7 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
assert_eq!(wallet.get_balance().unwrap(), details.received, "incorrect received after send");
|
assert_eq!(wallet.get_balance().unwrap(), details.received, "incorrect received after send");
|
||||||
|
|
||||||
let mut builder = wallet.build_fee_bump(details.txid).unwrap();
|
let mut builder = wallet.build_fee_bump(details.txid).unwrap();
|
||||||
builder.fee_rate(FeeRate::from_sat_per_vb(5.0));
|
builder.fee_rate(FeeRate::from_sat_per_vb(5.1));
|
||||||
let (mut new_psbt, new_details) = builder.finish().unwrap();
|
let (mut new_psbt, new_details) = builder.finish().unwrap();
|
||||||
let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
|
let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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!(
|
||||||
|
|||||||
@@ -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