Roll blockchain tests proc macro into normal macro
This means one less crate in the repo. Had to do a Default on TestClient to satisfy clippy.
This commit is contained in:
parent
aaa9943a5f
commit
465ef6e674
@ -54,13 +54,11 @@ all-keys = ["keys-bip39"]
|
|||||||
keys-bip39 = ["tiny-bip39"]
|
keys-bip39 = ["tiny-bip39"]
|
||||||
|
|
||||||
# Debug/Test features
|
# Debug/Test features
|
||||||
debug-proc-macros = ["bdk-macros/debug", "bdk-testutils-macros/debug"]
|
|
||||||
test-electrum = ["electrum"]
|
test-electrum = ["electrum"]
|
||||||
test-md-docs = ["electrum"]
|
test-md-docs = ["electrum"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
bdk-testutils = "0.4"
|
bdk-testutils = { path = "./testutils" }
|
||||||
bdk-testutils-macros = "0.6"
|
|
||||||
serial_test = "0.4"
|
serial_test = "0.4"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
env_logger = "0.7"
|
env_logger = "0.7"
|
||||||
@ -79,7 +77,7 @@ path = "examples/compiler.rs"
|
|||||||
required-features = ["compiler"]
|
required-features = ["compiler"]
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["macros", "testutils", "testutils-macros"]
|
members = ["macros", "testutils"]
|
||||||
|
|
||||||
# Generate docs with nightly to add the "features required" badge
|
# Generate docs with nightly to add the "features required" badge
|
||||||
# https://stackoverflow.com/questions/61417452/how-to-get-a-feature-requirement-tag-in-the-documentation-generated-by-cargo-do
|
# https://stackoverflow.com/questions/61417452/how-to-get-a-feature-requirement-tag-in-the-documentation-generated-by-cargo-do
|
||||||
|
@ -45,13 +45,6 @@ use crate::FeeRate;
|
|||||||
/// See the [`blockchain::electrum`](crate::blockchain::electrum) module for a usage example.
|
/// See the [`blockchain::electrum`](crate::blockchain::electrum) module for a usage example.
|
||||||
pub struct ElectrumBlockchain(Client);
|
pub struct ElectrumBlockchain(Client);
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
#[cfg(feature = "test-electrum")]
|
|
||||||
#[bdk_blockchain_tests(crate)]
|
|
||||||
fn local_electrs() -> ElectrumBlockchain {
|
|
||||||
ElectrumBlockchain::from(Client::new(&testutils::get_electrum_url()).unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::convert::From<Client> for ElectrumBlockchain {
|
impl std::convert::From<Client> for ElectrumBlockchain {
|
||||||
fn from(client: Client) -> Self {
|
fn from(client: Client) -> Self {
|
||||||
ElectrumBlockchain(client)
|
ElectrumBlockchain(client)
|
||||||
@ -175,3 +168,11 @@ impl ConfigurableBlockchain for ElectrumBlockchain {
|
|||||||
)?))
|
)?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "test-electrum", test))]
|
||||||
|
testutils::bdk_blockchain_tests! {
|
||||||
|
bdk => crate,
|
||||||
|
fn test_instance() -> ElectrumBlockchain {
|
||||||
|
ElectrumBlockchain::from(Client::new(&testutils::get_electrum_url()).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -234,8 +234,6 @@ pub extern crate sled;
|
|||||||
extern crate testutils;
|
extern crate testutils;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[macro_use]
|
|
||||||
extern crate testutils_macros;
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "bdk-testutils-macros"
|
|
||||||
version = "0.6.0"
|
|
||||||
authors = ["Alekos Filini <alekos.filini@gmail.com>"]
|
|
||||||
edition = "2018"
|
|
||||||
homepage = "https://bitcoindevkit.org"
|
|
||||||
repository = "https://github.com/bitcoindevkit/bdk"
|
|
||||||
documentation = "https://docs.rs/bdk-testutils-macros"
|
|
||||||
description = "Supporting testing macros for `bdk`"
|
|
||||||
keywords = ["bdk"]
|
|
||||||
license = "MIT OR Apache-2.0"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
proc-macro = true
|
|
||||||
name = "testutils_macros"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
syn = { version = "1.0", features = ["parsing", "full"] }
|
|
||||||
proc-macro2 = "1.0"
|
|
||||||
quote = "1.0"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
debug = ["syn/extra-traits"]
|
|
@ -1,553 +0,0 @@
|
|||||||
// Bitcoin Dev Kit
|
|
||||||
// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
|
|
||||||
//
|
|
||||||
// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
|
|
||||||
//
|
|
||||||
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
|
|
||||||
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
|
|
||||||
// You may not use this file except in accordance with one or both of these
|
|
||||||
// licenses.
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate quote;
|
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
|
||||||
|
|
||||||
use syn::spanned::Spanned;
|
|
||||||
use syn::{parse, parse2, Ident, ReturnType};
|
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
|
||||||
pub fn bdk_blockchain_tests(attr: TokenStream, item: TokenStream) -> TokenStream {
|
|
||||||
let root_ident = if !attr.is_empty() {
|
|
||||||
match parse::<syn::ExprPath>(attr) {
|
|
||||||
Ok(parsed) => parsed,
|
|
||||||
Err(e) => {
|
|
||||||
let error_string = e.to_string();
|
|
||||||
return (quote! {
|
|
||||||
compile_error!("Invalid crate path: {:?}", #error_string)
|
|
||||||
})
|
|
||||||
.into();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
parse2::<syn::ExprPath>(quote! { bdk }).unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
match parse::<syn::ItemFn>(item) {
|
|
||||||
Err(_) => (quote! {
|
|
||||||
compile_error!("#[bdk_blockchain_tests] can only be used on `fn`s")
|
|
||||||
})
|
|
||||||
.into(),
|
|
||||||
Ok(parsed) => {
|
|
||||||
let parsed_sig_ident = parsed.sig.ident.clone();
|
|
||||||
let mod_name = Ident::new(
|
|
||||||
&format!("generated_tests_{}", parsed_sig_ident.to_string()),
|
|
||||||
parsed.span(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let return_type = match parsed.sig.output {
|
|
||||||
ReturnType::Type(_, ref t) => t.clone(),
|
|
||||||
ReturnType::Default => {
|
|
||||||
return (quote! {
|
|
||||||
compile_error!("The tagged function must return a type that impl `Blockchain`")
|
|
||||||
}).into();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let output = quote! {
|
|
||||||
|
|
||||||
#parsed
|
|
||||||
|
|
||||||
mod #mod_name {
|
|
||||||
use bitcoin::Network;
|
|
||||||
|
|
||||||
use miniscript::Descriptor;
|
|
||||||
|
|
||||||
use testutils::{TestClient, serial};
|
|
||||||
|
|
||||||
use #root_ident::blockchain::{Blockchain, noop_progress};
|
|
||||||
use #root_ident::descriptor::ExtendedDescriptor;
|
|
||||||
use #root_ident::database::MemoryDatabase;
|
|
||||||
use #root_ident::types::KeychainKind;
|
|
||||||
use #root_ident::{Wallet, TxBuilder, FeeRate};
|
|
||||||
use #root_ident::wallet::AddressIndex::New;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
fn get_blockchain() -> #return_type {
|
|
||||||
#parsed_sig_ident()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_wallet_from_descriptors(descriptors: &(String, Option<String>)) -> Wallet<#return_type, MemoryDatabase> {
|
|
||||||
Wallet::new(&descriptors.0.to_string(), descriptors.1.as_ref(), Network::Regtest, MemoryDatabase::new(), get_blockchain()).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_single_sig() -> (Wallet<#return_type, MemoryDatabase>, (String, Option<String>), TestClient) {
|
|
||||||
let descriptors = testutils! {
|
|
||||||
@descriptors ( "wpkh(Alice)" ) ( "wpkh(Alice)" ) ( @keys ( "Alice" => (@generate_xprv "/44'/0'/0'/0/*", "/44'/0'/0'/1/*") ) )
|
|
||||||
};
|
|
||||||
|
|
||||||
let test_client = TestClient::new();
|
|
||||||
let wallet = get_wallet_from_descriptors(&descriptors);
|
|
||||||
|
|
||||||
(wallet, descriptors, test_client)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn test_sync_simple() {
|
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
|
||||||
|
|
||||||
let tx = testutils! {
|
|
||||||
@tx ( (@external descriptors, 0) => 50_000 )
|
|
||||||
};
|
|
||||||
println!("{:?}", tx);
|
|
||||||
let txid = test_client.receive(tx);
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
|
||||||
assert_eq!(wallet.list_unspent().unwrap()[0].keychain, KeychainKind::External);
|
|
||||||
|
|
||||||
let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
|
|
||||||
assert_eq!(list_tx_item.txid, txid);
|
|
||||||
assert_eq!(list_tx_item.received, 50_000);
|
|
||||||
assert_eq!(list_tx_item.sent, 0);
|
|
||||||
assert_eq!(list_tx_item.height, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn test_sync_stop_gap_20() {
|
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
|
||||||
@tx ( (@external descriptors, 5) => 50_000 )
|
|
||||||
});
|
|
||||||
test_client.receive(testutils! {
|
|
||||||
@tx ( (@external descriptors, 25) => 50_000 )
|
|
||||||
});
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 100_000);
|
|
||||||
assert_eq!(wallet.list_transactions(false).unwrap().len(), 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn test_sync_before_and_after_receive() {
|
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 0);
|
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
|
||||||
@tx ( (@external descriptors, 0) => 50_000 )
|
|
||||||
});
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
|
||||||
assert_eq!(wallet.list_transactions(false).unwrap().len(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn test_sync_multiple_outputs_same_tx() {
|
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
|
||||||
|
|
||||||
let txid = test_client.receive(testutils! {
|
|
||||||
@tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000, (@external descriptors, 5) => 30_000 )
|
|
||||||
});
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 105_000);
|
|
||||||
assert_eq!(wallet.list_transactions(false).unwrap().len(), 1);
|
|
||||||
assert_eq!(wallet.list_unspent().unwrap().len(), 3);
|
|
||||||
|
|
||||||
let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
|
|
||||||
assert_eq!(list_tx_item.txid, txid);
|
|
||||||
assert_eq!(list_tx_item.received, 105_000);
|
|
||||||
assert_eq!(list_tx_item.sent, 0);
|
|
||||||
assert_eq!(list_tx_item.height, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn test_sync_receive_multi() {
|
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
|
||||||
@tx ( (@external descriptors, 0) => 50_000 )
|
|
||||||
});
|
|
||||||
test_client.receive(testutils! {
|
|
||||||
@tx ( (@external descriptors, 5) => 25_000 )
|
|
||||||
});
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 75_000);
|
|
||||||
assert_eq!(wallet.list_transactions(false).unwrap().len(), 2);
|
|
||||||
assert_eq!(wallet.list_unspent().unwrap().len(), 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn test_sync_address_reuse() {
|
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
|
||||||
@tx ( (@external descriptors, 0) => 50_000 )
|
|
||||||
});
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
|
||||||
@tx ( (@external descriptors, 0) => 25_000 )
|
|
||||||
});
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 75_000);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn test_sync_receive_rbf_replaced() {
|
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
|
||||||
|
|
||||||
let txid = test_client.receive(testutils! {
|
|
||||||
@tx ( (@external descriptors, 0) => 50_000 ) ( @replaceable true )
|
|
||||||
});
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
|
||||||
assert_eq!(wallet.list_transactions(false).unwrap().len(), 1);
|
|
||||||
assert_eq!(wallet.list_unspent().unwrap().len(), 1);
|
|
||||||
|
|
||||||
let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
|
|
||||||
assert_eq!(list_tx_item.txid, txid);
|
|
||||||
assert_eq!(list_tx_item.received, 50_000);
|
|
||||||
assert_eq!(list_tx_item.sent, 0);
|
|
||||||
assert_eq!(list_tx_item.height, None);
|
|
||||||
|
|
||||||
let new_txid = test_client.bump_fee(&txid);
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
|
||||||
assert_eq!(wallet.list_transactions(false).unwrap().len(), 1);
|
|
||||||
assert_eq!(wallet.list_unspent().unwrap().len(), 1);
|
|
||||||
|
|
||||||
let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
|
|
||||||
assert_eq!(list_tx_item.txid, new_txid);
|
|
||||||
assert_eq!(list_tx_item.received, 50_000);
|
|
||||||
assert_eq!(list_tx_item.sent, 0);
|
|
||||||
assert_eq!(list_tx_item.height, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn test_sync_reorg_block() {
|
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
|
||||||
|
|
||||||
let txid = test_client.receive(testutils! {
|
|
||||||
@tx ( (@external descriptors, 0) => 50_000 ) ( @confirmations 1 ) ( @replaceable true )
|
|
||||||
});
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
|
||||||
assert_eq!(wallet.list_transactions(false).unwrap().len(), 1);
|
|
||||||
assert_eq!(wallet.list_unspent().unwrap().len(), 1);
|
|
||||||
|
|
||||||
let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
|
|
||||||
assert_eq!(list_tx_item.txid, txid);
|
|
||||||
assert!(list_tx_item.height.is_some());
|
|
||||||
|
|
||||||
// Invalidate 1 block
|
|
||||||
test_client.invalidate(1);
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
|
||||||
|
|
||||||
let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
|
|
||||||
assert_eq!(list_tx_item.txid, txid);
|
|
||||||
assert_eq!(list_tx_item.height, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn test_sync_after_send() {
|
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
|
||||||
println!("{}", descriptors.0);
|
|
||||||
let node_addr = test_client.get_node_address(None);
|
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
|
||||||
@tx ( (@external descriptors, 0) => 50_000 )
|
|
||||||
});
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
|
||||||
|
|
||||||
let mut builder = wallet.build_tx();
|
|
||||||
builder.add_recipient(node_addr.script_pubkey(), 25_000);
|
|
||||||
let (mut psbt, details) = builder.finish().unwrap();
|
|
||||||
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
|
||||||
let tx = psbt.extract_tx();
|
|
||||||
println!("{}", bitcoin::consensus::encode::serialize_hex(&tx));
|
|
||||||
wallet.broadcast(tx).unwrap();
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), details.received);
|
|
||||||
|
|
||||||
assert_eq!(wallet.list_transactions(false).unwrap().len(), 2);
|
|
||||||
assert_eq!(wallet.list_unspent().unwrap().len(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn test_sync_outgoing_from_scratch() {
|
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
|
||||||
let node_addr = test_client.get_node_address(None);
|
|
||||||
|
|
||||||
let received_txid = test_client.receive(testutils! {
|
|
||||||
@tx ( (@external descriptors, 0) => 50_000 )
|
|
||||||
});
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
|
||||||
|
|
||||||
let mut builder = wallet.build_tx();
|
|
||||||
builder.add_recipient(node_addr.script_pubkey(), 25_000);
|
|
||||||
let (mut psbt, details) = builder.finish().unwrap();
|
|
||||||
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
|
||||||
let sent_txid = wallet.broadcast(psbt.extract_tx()).unwrap();
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), details.received);
|
|
||||||
|
|
||||||
// empty wallet
|
|
||||||
let wallet = get_wallet_from_descriptors(&descriptors);
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
|
|
||||||
let tx_map = wallet.list_transactions(false).unwrap().into_iter().map(|tx| (tx.txid, tx)).collect::<std::collections::HashMap<_, _>>();
|
|
||||||
|
|
||||||
let received = tx_map.get(&received_txid).unwrap();
|
|
||||||
assert_eq!(received.received, 50_000);
|
|
||||||
assert_eq!(received.sent, 0);
|
|
||||||
|
|
||||||
let sent = tx_map.get(&sent_txid).unwrap();
|
|
||||||
assert_eq!(sent.received, details.received);
|
|
||||||
assert_eq!(sent.sent, details.sent);
|
|
||||||
assert_eq!(sent.fees, details.fees);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn test_sync_long_change_chain() {
|
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
|
||||||
let node_addr = test_client.get_node_address(None);
|
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
|
||||||
@tx ( (@external descriptors, 0) => 50_000 )
|
|
||||||
});
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
|
||||||
|
|
||||||
let mut total_sent = 0;
|
|
||||||
for _ in 0..5 {
|
|
||||||
let mut builder = wallet.build_tx();
|
|
||||||
builder.add_recipient(node_addr.script_pubkey(), 5_000);
|
|
||||||
let (mut psbt, details) = builder.finish().unwrap();
|
|
||||||
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
|
||||||
wallet.broadcast(psbt.extract_tx()).unwrap();
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
|
|
||||||
total_sent += 5_000 + details.fees;
|
|
||||||
}
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000 - total_sent);
|
|
||||||
|
|
||||||
// empty wallet
|
|
||||||
let wallet = get_wallet_from_descriptors(&descriptors);
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000 - total_sent);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn test_sync_bump_fee() {
|
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
|
||||||
let node_addr = test_client.get_node_address(None);
|
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
|
||||||
@tx ( (@external descriptors, 0) => 50_000 ) (@confirmations 1)
|
|
||||||
});
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
|
||||||
|
|
||||||
let mut builder = wallet.build_tx();
|
|
||||||
builder.add_recipient(node_addr.script_pubkey().clone(), 5_000).enable_rbf();
|
|
||||||
let (mut psbt, details) = builder.finish().unwrap();
|
|
||||||
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
|
||||||
wallet.broadcast(psbt.extract_tx()).unwrap();
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000 - details.fees - 5_000);
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), details.received);
|
|
||||||
|
|
||||||
let mut builder = wallet.build_fee_bump(details.txid).unwrap();
|
|
||||||
builder.fee_rate(FeeRate::from_sat_per_vb(2.1));
|
|
||||||
let (mut new_psbt, new_details) = builder.finish().unwrap();
|
|
||||||
let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
|
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
|
||||||
wallet.broadcast(new_psbt.extract_tx()).unwrap();
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000 - new_details.fees - 5_000);
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), new_details.received);
|
|
||||||
|
|
||||||
assert!(new_details.fees > details.fees);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn test_sync_bump_fee_remove_change() {
|
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
|
||||||
let node_addr = test_client.get_node_address(None);
|
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
|
||||||
@tx ( (@external descriptors, 0) => 50_000 ) (@confirmations 1)
|
|
||||||
});
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
|
||||||
|
|
||||||
let mut builder = wallet.build_tx();
|
|
||||||
builder.add_recipient(node_addr.script_pubkey().clone(), 49_000).enable_rbf();
|
|
||||||
let (mut psbt, details) = builder.finish().unwrap();
|
|
||||||
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
|
||||||
wallet.broadcast(psbt.extract_tx()).unwrap();
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 1_000 - details.fees);
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), details.received);
|
|
||||||
|
|
||||||
let mut builder = wallet.build_fee_bump(details.txid).unwrap();
|
|
||||||
builder.fee_rate(FeeRate::from_sat_per_vb(5.0));
|
|
||||||
let (mut new_psbt, new_details) = builder.finish().unwrap();
|
|
||||||
let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
|
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
|
||||||
wallet.broadcast(new_psbt.extract_tx()).unwrap();
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 0);
|
|
||||||
assert_eq!(new_details.received, 0);
|
|
||||||
|
|
||||||
assert!(new_details.fees > details.fees);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn test_sync_bump_fee_add_input() {
|
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
|
||||||
let node_addr = test_client.get_node_address(None);
|
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
|
||||||
@tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000 ) (@confirmations 1)
|
|
||||||
});
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 75_000);
|
|
||||||
|
|
||||||
let mut builder = wallet.build_tx();
|
|
||||||
builder.add_recipient(node_addr.script_pubkey().clone(), 49_000).enable_rbf();
|
|
||||||
let (mut psbt, details) = builder.finish().unwrap();
|
|
||||||
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
|
||||||
wallet.broadcast(psbt.extract_tx()).unwrap();
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 26_000 - details.fees);
|
|
||||||
assert_eq!(details.received, 1_000 - details.fees);
|
|
||||||
|
|
||||||
let mut builder = wallet.build_fee_bump(details.txid).unwrap();
|
|
||||||
builder.fee_rate(FeeRate::from_sat_per_vb(10.0));
|
|
||||||
let (mut new_psbt, new_details) = builder.finish().unwrap();
|
|
||||||
let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
|
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
|
||||||
wallet.broadcast(new_psbt.extract_tx()).unwrap();
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(new_details.sent, 75_000);
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), new_details.received);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn test_sync_bump_fee_add_input_no_change() {
|
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
|
||||||
let node_addr = test_client.get_node_address(None);
|
|
||||||
|
|
||||||
test_client.receive(testutils! {
|
|
||||||
@tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000 ) (@confirmations 1)
|
|
||||||
});
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 75_000);
|
|
||||||
|
|
||||||
let mut builder = wallet.build_tx();
|
|
||||||
builder.add_recipient(node_addr.script_pubkey().clone(), 49_000).enable_rbf();
|
|
||||||
let (mut psbt, details) = builder.finish().unwrap();
|
|
||||||
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
|
||||||
wallet.broadcast(psbt.extract_tx()).unwrap();
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 26_000 - details.fees);
|
|
||||||
assert_eq!(details.received, 1_000 - details.fees);
|
|
||||||
|
|
||||||
let mut builder = wallet.build_fee_bump(details.txid).unwrap();
|
|
||||||
builder.fee_rate(FeeRate::from_sat_per_vb(123.0));
|
|
||||||
let (mut new_psbt, new_details) = builder.finish().unwrap();
|
|
||||||
println!("{:#?}", new_details);
|
|
||||||
|
|
||||||
let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
|
|
||||||
assert!(finalized, "Cannot finalize transaction");
|
|
||||||
wallet.broadcast(new_psbt.extract_tx()).unwrap();
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(new_details.sent, 75_000);
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 0);
|
|
||||||
assert_eq!(new_details.received, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn test_sync_receive_coinbase() {
|
|
||||||
let (wallet, descriptors, mut test_client) = init_single_sig();
|
|
||||||
let wallet_addr = wallet.get_address(New).unwrap();
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 0);
|
|
||||||
|
|
||||||
test_client.generate(1, Some(wallet_addr));
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert!(wallet.get_balance().unwrap() > 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
output.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
491
testutils/src/blockchain_tests.rs
Normal file
491
testutils/src/blockchain_tests.rs
Normal file
@ -0,0 +1,491 @@
|
|||||||
|
/// This macro runs blockchain tests against a `Blockchain` implementation. It requires access to a
|
||||||
|
/// Bitcoin core wallet via RPC. At the moment you have to dig into the code yourself and look at
|
||||||
|
/// the setup required to run the tests yourself.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! bdk_blockchain_tests {
|
||||||
|
(bdk => $bdk:ident,
|
||||||
|
fn test_instance() -> $blockchain:ty $block:block) => {
|
||||||
|
mod bdk_blockchain_tests {
|
||||||
|
use $bdk::bitcoin::Network;
|
||||||
|
use $bdk::miniscript::Descriptor;
|
||||||
|
use $crate::{TestClient, serial};
|
||||||
|
use $bdk::blockchain::{Blockchain, noop_progress};
|
||||||
|
use $bdk::descriptor::ExtendedDescriptor;
|
||||||
|
use $bdk::database::MemoryDatabase;
|
||||||
|
use $bdk::types::KeychainKind;
|
||||||
|
use $bdk::{Wallet, TxBuilder, FeeRate};
|
||||||
|
use $bdk::wallet::AddressIndex::New;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn get_blockchain() -> $blockchain {
|
||||||
|
$block
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_wallet_from_descriptors(descriptors: &(String, Option<String>)) -> Wallet<$blockchain, MemoryDatabase> {
|
||||||
|
Wallet::new(&descriptors.0.to_string(), descriptors.1.as_ref(), Network::Regtest, MemoryDatabase::new(), get_blockchain()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_single_sig() -> (Wallet<$blockchain, MemoryDatabase>, (String, Option<String>), TestClient) {
|
||||||
|
let descriptors = testutils! {
|
||||||
|
@descriptors ( "wpkh(Alice)" ) ( "wpkh(Alice)" ) ( @keys ( "Alice" => (@generate_xprv "/44'/0'/0'/0/*", "/44'/0'/0'/1/*") ) )
|
||||||
|
};
|
||||||
|
|
||||||
|
let test_client = TestClient::default();
|
||||||
|
let wallet = get_wallet_from_descriptors(&descriptors);
|
||||||
|
|
||||||
|
(wallet, descriptors, test_client)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn test_sync_simple() {
|
||||||
|
let (wallet, descriptors, mut test_client) = init_single_sig();
|
||||||
|
|
||||||
|
let tx = testutils! {
|
||||||
|
@tx ( (@external descriptors, 0) => 50_000 )
|
||||||
|
};
|
||||||
|
println!("{:?}", tx);
|
||||||
|
let txid = test_client.receive(tx);
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
||||||
|
assert_eq!(wallet.list_unspent().unwrap()[0].keychain, KeychainKind::External);
|
||||||
|
|
||||||
|
let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
|
||||||
|
assert_eq!(list_tx_item.txid, txid);
|
||||||
|
assert_eq!(list_tx_item.received, 50_000);
|
||||||
|
assert_eq!(list_tx_item.sent, 0);
|
||||||
|
assert_eq!(list_tx_item.height, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn test_sync_stop_gap_20() {
|
||||||
|
let (wallet, descriptors, mut test_client) = init_single_sig();
|
||||||
|
|
||||||
|
test_client.receive(testutils! {
|
||||||
|
@tx ( (@external descriptors, 5) => 50_000 )
|
||||||
|
});
|
||||||
|
test_client.receive(testutils! {
|
||||||
|
@tx ( (@external descriptors, 25) => 50_000 )
|
||||||
|
});
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 100_000);
|
||||||
|
assert_eq!(wallet.list_transactions(false).unwrap().len(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn test_sync_before_and_after_receive() {
|
||||||
|
let (wallet, descriptors, mut test_client) = init_single_sig();
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 0);
|
||||||
|
|
||||||
|
test_client.receive(testutils! {
|
||||||
|
@tx ( (@external descriptors, 0) => 50_000 )
|
||||||
|
});
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
||||||
|
assert_eq!(wallet.list_transactions(false).unwrap().len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn test_sync_multiple_outputs_same_tx() {
|
||||||
|
let (wallet, descriptors, mut test_client) = init_single_sig();
|
||||||
|
|
||||||
|
let txid = test_client.receive(testutils! {
|
||||||
|
@tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000, (@external descriptors, 5) => 30_000 )
|
||||||
|
});
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 105_000);
|
||||||
|
assert_eq!(wallet.list_transactions(false).unwrap().len(), 1);
|
||||||
|
assert_eq!(wallet.list_unspent().unwrap().len(), 3);
|
||||||
|
|
||||||
|
let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
|
||||||
|
assert_eq!(list_tx_item.txid, txid);
|
||||||
|
assert_eq!(list_tx_item.received, 105_000);
|
||||||
|
assert_eq!(list_tx_item.sent, 0);
|
||||||
|
assert_eq!(list_tx_item.height, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn test_sync_receive_multi() {
|
||||||
|
let (wallet, descriptors, mut test_client) = init_single_sig();
|
||||||
|
|
||||||
|
test_client.receive(testutils! {
|
||||||
|
@tx ( (@external descriptors, 0) => 50_000 )
|
||||||
|
});
|
||||||
|
test_client.receive(testutils! {
|
||||||
|
@tx ( (@external descriptors, 5) => 25_000 )
|
||||||
|
});
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 75_000);
|
||||||
|
assert_eq!(wallet.list_transactions(false).unwrap().len(), 2);
|
||||||
|
assert_eq!(wallet.list_unspent().unwrap().len(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn test_sync_address_reuse() {
|
||||||
|
let (wallet, descriptors, mut test_client) = init_single_sig();
|
||||||
|
|
||||||
|
test_client.receive(testutils! {
|
||||||
|
@tx ( (@external descriptors, 0) => 50_000 )
|
||||||
|
});
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
||||||
|
|
||||||
|
test_client.receive(testutils! {
|
||||||
|
@tx ( (@external descriptors, 0) => 25_000 )
|
||||||
|
});
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 75_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn test_sync_receive_rbf_replaced() {
|
||||||
|
let (wallet, descriptors, mut test_client) = init_single_sig();
|
||||||
|
|
||||||
|
let txid = test_client.receive(testutils! {
|
||||||
|
@tx ( (@external descriptors, 0) => 50_000 ) ( @replaceable true )
|
||||||
|
});
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
||||||
|
assert_eq!(wallet.list_transactions(false).unwrap().len(), 1);
|
||||||
|
assert_eq!(wallet.list_unspent().unwrap().len(), 1);
|
||||||
|
|
||||||
|
let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
|
||||||
|
assert_eq!(list_tx_item.txid, txid);
|
||||||
|
assert_eq!(list_tx_item.received, 50_000);
|
||||||
|
assert_eq!(list_tx_item.sent, 0);
|
||||||
|
assert_eq!(list_tx_item.height, None);
|
||||||
|
|
||||||
|
let new_txid = test_client.bump_fee(&txid);
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
||||||
|
assert_eq!(wallet.list_transactions(false).unwrap().len(), 1);
|
||||||
|
assert_eq!(wallet.list_unspent().unwrap().len(), 1);
|
||||||
|
|
||||||
|
let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
|
||||||
|
assert_eq!(list_tx_item.txid, new_txid);
|
||||||
|
assert_eq!(list_tx_item.received, 50_000);
|
||||||
|
assert_eq!(list_tx_item.sent, 0);
|
||||||
|
assert_eq!(list_tx_item.height, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn test_sync_reorg_block() {
|
||||||
|
let (wallet, descriptors, mut test_client) = init_single_sig();
|
||||||
|
|
||||||
|
let txid = test_client.receive(testutils! {
|
||||||
|
@tx ( (@external descriptors, 0) => 50_000 ) ( @confirmations 1 ) ( @replaceable true )
|
||||||
|
});
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
||||||
|
assert_eq!(wallet.list_transactions(false).unwrap().len(), 1);
|
||||||
|
assert_eq!(wallet.list_unspent().unwrap().len(), 1);
|
||||||
|
|
||||||
|
let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
|
||||||
|
assert_eq!(list_tx_item.txid, txid);
|
||||||
|
assert!(list_tx_item.height.is_some());
|
||||||
|
|
||||||
|
// Invalidate 1 block
|
||||||
|
test_client.invalidate(1);
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
||||||
|
|
||||||
|
let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
|
||||||
|
assert_eq!(list_tx_item.txid, txid);
|
||||||
|
assert_eq!(list_tx_item.height, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn test_sync_after_send() {
|
||||||
|
let (wallet, descriptors, mut test_client) = init_single_sig();
|
||||||
|
println!("{}", descriptors.0);
|
||||||
|
let node_addr = test_client.get_node_address(None);
|
||||||
|
|
||||||
|
test_client.receive(testutils! {
|
||||||
|
@tx ( (@external descriptors, 0) => 50_000 )
|
||||||
|
});
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
||||||
|
|
||||||
|
let mut builder = wallet.build_tx();
|
||||||
|
builder.add_recipient(node_addr.script_pubkey(), 25_000);
|
||||||
|
let (mut psbt, details) = builder.finish().unwrap();
|
||||||
|
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
||||||
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
|
let tx = psbt.extract_tx();
|
||||||
|
println!("{}", bitcoin::consensus::encode::serialize_hex(&tx));
|
||||||
|
wallet.broadcast(tx).unwrap();
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), details.received);
|
||||||
|
|
||||||
|
assert_eq!(wallet.list_transactions(false).unwrap().len(), 2);
|
||||||
|
assert_eq!(wallet.list_unspent().unwrap().len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn test_sync_outgoing_from_scratch() {
|
||||||
|
let (wallet, descriptors, mut test_client) = init_single_sig();
|
||||||
|
let node_addr = test_client.get_node_address(None);
|
||||||
|
|
||||||
|
let received_txid = test_client.receive(testutils! {
|
||||||
|
@tx ( (@external descriptors, 0) => 50_000 )
|
||||||
|
});
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
||||||
|
|
||||||
|
let mut builder = wallet.build_tx();
|
||||||
|
builder.add_recipient(node_addr.script_pubkey(), 25_000);
|
||||||
|
let (mut psbt, details) = builder.finish().unwrap();
|
||||||
|
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
||||||
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
|
let sent_txid = wallet.broadcast(psbt.extract_tx()).unwrap();
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), details.received);
|
||||||
|
|
||||||
|
// empty wallet
|
||||||
|
let wallet = get_wallet_from_descriptors(&descriptors);
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
|
||||||
|
let tx_map = wallet.list_transactions(false).unwrap().into_iter().map(|tx| (tx.txid, tx)).collect::<std::collections::HashMap<_, _>>();
|
||||||
|
|
||||||
|
let received = tx_map.get(&received_txid).unwrap();
|
||||||
|
assert_eq!(received.received, 50_000);
|
||||||
|
assert_eq!(received.sent, 0);
|
||||||
|
|
||||||
|
let sent = tx_map.get(&sent_txid).unwrap();
|
||||||
|
assert_eq!(sent.received, details.received);
|
||||||
|
assert_eq!(sent.sent, details.sent);
|
||||||
|
assert_eq!(sent.fees, details.fees);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn test_sync_long_change_chain() {
|
||||||
|
let (wallet, descriptors, mut test_client) = init_single_sig();
|
||||||
|
let node_addr = test_client.get_node_address(None);
|
||||||
|
|
||||||
|
test_client.receive(testutils! {
|
||||||
|
@tx ( (@external descriptors, 0) => 50_000 )
|
||||||
|
});
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
||||||
|
|
||||||
|
let mut total_sent = 0;
|
||||||
|
for _ in 0..5 {
|
||||||
|
let mut builder = wallet.build_tx();
|
||||||
|
builder.add_recipient(node_addr.script_pubkey(), 5_000);
|
||||||
|
let (mut psbt, details) = builder.finish().unwrap();
|
||||||
|
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
||||||
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
|
wallet.broadcast(psbt.extract_tx()).unwrap();
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
|
||||||
|
total_sent += 5_000 + details.fees;
|
||||||
|
}
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 50_000 - total_sent);
|
||||||
|
|
||||||
|
// empty wallet
|
||||||
|
let wallet = get_wallet_from_descriptors(&descriptors);
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 50_000 - total_sent);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn test_sync_bump_fee() {
|
||||||
|
let (wallet, descriptors, mut test_client) = init_single_sig();
|
||||||
|
let node_addr = test_client.get_node_address(None);
|
||||||
|
|
||||||
|
test_client.receive(testutils! {
|
||||||
|
@tx ( (@external descriptors, 0) => 50_000 ) (@confirmations 1)
|
||||||
|
});
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
||||||
|
|
||||||
|
let mut builder = wallet.build_tx();
|
||||||
|
builder.add_recipient(node_addr.script_pubkey().clone(), 5_000).enable_rbf();
|
||||||
|
let (mut psbt, details) = builder.finish().unwrap();
|
||||||
|
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
||||||
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
|
wallet.broadcast(psbt.extract_tx()).unwrap();
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 50_000 - details.fees - 5_000);
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), details.received);
|
||||||
|
|
||||||
|
let mut builder = wallet.build_fee_bump(details.txid).unwrap();
|
||||||
|
builder.fee_rate(FeeRate::from_sat_per_vb(2.1));
|
||||||
|
let (mut new_psbt, new_details) = builder.finish().unwrap();
|
||||||
|
let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
|
||||||
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
|
wallet.broadcast(new_psbt.extract_tx()).unwrap();
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 50_000 - new_details.fees - 5_000);
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), new_details.received);
|
||||||
|
|
||||||
|
assert!(new_details.fees > details.fees);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn test_sync_bump_fee_remove_change() {
|
||||||
|
let (wallet, descriptors, mut test_client) = init_single_sig();
|
||||||
|
let node_addr = test_client.get_node_address(None);
|
||||||
|
|
||||||
|
test_client.receive(testutils! {
|
||||||
|
@tx ( (@external descriptors, 0) => 50_000 ) (@confirmations 1)
|
||||||
|
});
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 50_000);
|
||||||
|
|
||||||
|
let mut builder = wallet.build_tx();
|
||||||
|
builder.add_recipient(node_addr.script_pubkey().clone(), 49_000).enable_rbf();
|
||||||
|
let (mut psbt, details) = builder.finish().unwrap();
|
||||||
|
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
||||||
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
|
wallet.broadcast(psbt.extract_tx()).unwrap();
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 1_000 - details.fees);
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), details.received);
|
||||||
|
|
||||||
|
let mut builder = wallet.build_fee_bump(details.txid).unwrap();
|
||||||
|
builder.fee_rate(FeeRate::from_sat_per_vb(5.0));
|
||||||
|
let (mut new_psbt, new_details) = builder.finish().unwrap();
|
||||||
|
let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
|
||||||
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
|
wallet.broadcast(new_psbt.extract_tx()).unwrap();
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 0);
|
||||||
|
assert_eq!(new_details.received, 0);
|
||||||
|
|
||||||
|
assert!(new_details.fees > details.fees);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn test_sync_bump_fee_add_input() {
|
||||||
|
let (wallet, descriptors, mut test_client) = init_single_sig();
|
||||||
|
let node_addr = test_client.get_node_address(None);
|
||||||
|
|
||||||
|
test_client.receive(testutils! {
|
||||||
|
@tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000 ) (@confirmations 1)
|
||||||
|
});
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 75_000);
|
||||||
|
|
||||||
|
let mut builder = wallet.build_tx();
|
||||||
|
builder.add_recipient(node_addr.script_pubkey().clone(), 49_000).enable_rbf();
|
||||||
|
let (mut psbt, details) = builder.finish().unwrap();
|
||||||
|
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
||||||
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
|
wallet.broadcast(psbt.extract_tx()).unwrap();
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 26_000 - details.fees);
|
||||||
|
assert_eq!(details.received, 1_000 - details.fees);
|
||||||
|
|
||||||
|
let mut builder = wallet.build_fee_bump(details.txid).unwrap();
|
||||||
|
builder.fee_rate(FeeRate::from_sat_per_vb(10.0));
|
||||||
|
let (mut new_psbt, new_details) = builder.finish().unwrap();
|
||||||
|
let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
|
||||||
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
|
wallet.broadcast(new_psbt.extract_tx()).unwrap();
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(new_details.sent, 75_000);
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), new_details.received);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn test_sync_bump_fee_add_input_no_change() {
|
||||||
|
let (wallet, descriptors, mut test_client) = init_single_sig();
|
||||||
|
let node_addr = test_client.get_node_address(None);
|
||||||
|
|
||||||
|
test_client.receive(testutils! {
|
||||||
|
@tx ( (@external descriptors, 0) => 50_000, (@external descriptors, 1) => 25_000 ) (@confirmations 1)
|
||||||
|
});
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 75_000);
|
||||||
|
|
||||||
|
let mut builder = wallet.build_tx();
|
||||||
|
builder.add_recipient(node_addr.script_pubkey().clone(), 49_000).enable_rbf();
|
||||||
|
let (mut psbt, details) = builder.finish().unwrap();
|
||||||
|
let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
|
||||||
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
|
wallet.broadcast(psbt.extract_tx()).unwrap();
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 26_000 - details.fees);
|
||||||
|
assert_eq!(details.received, 1_000 - details.fees);
|
||||||
|
|
||||||
|
let mut builder = wallet.build_fee_bump(details.txid).unwrap();
|
||||||
|
builder.fee_rate(FeeRate::from_sat_per_vb(123.0));
|
||||||
|
let (mut new_psbt, new_details) = builder.finish().unwrap();
|
||||||
|
println!("{:#?}", new_details);
|
||||||
|
|
||||||
|
let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
|
||||||
|
assert!(finalized, "Cannot finalize transaction");
|
||||||
|
wallet.broadcast(new_psbt.extract_tx()).unwrap();
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(new_details.sent, 75_000);
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 0);
|
||||||
|
assert_eq!(new_details.received, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn test_sync_receive_coinbase() {
|
||||||
|
let (wallet, descriptors, mut test_client) = init_single_sig();
|
||||||
|
let wallet_addr = wallet.get_address(New).unwrap();
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert_eq!(wallet.get_balance().unwrap(), 0);
|
||||||
|
|
||||||
|
test_client.generate(1, Some(wallet_addr));
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
assert!(wallet.get_balance().unwrap() > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
mod blockchain_tests;
|
||||||
|
|
||||||
pub use serial_test::serial;
|
pub use serial_test::serial;
|
||||||
|
|
||||||
@ -297,11 +298,12 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TestClient {
|
impl TestClient {
|
||||||
pub fn new() -> Self {
|
pub fn new(rpc_host_and_wallet: String, rpc_wallet_name: String) -> Self {
|
||||||
let url = env::var("BDK_RPC_URL").unwrap_or_else(|_| "127.0.0.1:18443".to_string());
|
let client = RpcClient::new(
|
||||||
let wallet = env::var("BDK_RPC_WALLET").unwrap_or_else(|_| "bdk-test".to_string());
|
format!("http://{}/wallet/{}", rpc_host_and_wallet, rpc_wallet_name),
|
||||||
let client =
|
get_auth(),
|
||||||
RpcClient::new(format!("http://{}/wallet/{}", url, wallet), get_auth()).unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
let electrum = ElectrumClient::new(&get_electrum_url()).unwrap();
|
let electrum = ElectrumClient::new(&get_electrum_url()).unwrap();
|
||||||
|
|
||||||
TestClient { client, electrum }
|
TestClient { client, electrum }
|
||||||
@ -562,3 +564,12 @@ impl Deref for TestClient {
|
|||||||
&self.client
|
&self.client
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for TestClient {
|
||||||
|
fn default() -> Self {
|
||||||
|
let rpc_host_and_port =
|
||||||
|
env::var("BDK_RPC_URL").unwrap_or_else(|_| "127.0.0.1:18443".to_string());
|
||||||
|
let wallet = env::var("BDK_RPC_WALLET").unwrap_or_else(|_| "bdk-test".to_string());
|
||||||
|
Self::new(rpc_host_and_port, wallet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user