Skip to content

Commit 2949dfa

Browse files
committed
using regtest instead of testnet
1 parent 81e04a1 commit 2949dfa

File tree

4 files changed

+188
-54
lines changed

4 files changed

+188
-54
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ log = "^0.4"
1818
rstest = "^0.11"
1919
bdk-testutils = "^0.4"
2020
bdk = { version = "0.23", default-features = true }
21+
electrsd = { version = "0.20", features = ["bitcoind_22_0", "electrs_0_9_1"] }

tests/mempool.rs

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod regtestenv;
12
use bdk::bitcoin::Network;
23
use bdk::blockchain::{electrum::ElectrumBlockchain, Blockchain, GetHeight};
34
use bdk::database::memory::MemoryDatabase;
@@ -6,27 +7,27 @@ use bdk::wallet::{AddressIndex, SyncOptions, Wallet};
67
use bdk::Error;
78
use bdk::SignOptions;
89
use bdk_reserves::reserves::*;
10+
use regtestenv::RegTestEnv;
911

10-
fn construct_wallet(
11-
desc: &str,
12-
network: Network,
13-
) -> Result<(Wallet<MemoryDatabase>, ElectrumBlockchain), Error> {
14-
let client = Client::new("ssl://electrum.blockstream.info:60002")?;
12+
fn construct_wallet(desc: &str, network: Network) -> Result<Wallet<MemoryDatabase>, Error> {
1513
let wallet = Wallet::new(desc, None, network, MemoryDatabase::default())?;
1614

17-
let blockchain = ElectrumBlockchain::from(client);
18-
wallet.sync(&blockchain, SyncOptions::default())?;
19-
20-
Ok((wallet, blockchain))
15+
Ok(wallet)
2116
}
2217

2318
#[test]
2419
fn unconfirmed() -> Result<(), ProofError> {
25-
let (wallet, blockchain) = construct_wallet(
20+
let wallet = construct_wallet(
2621
"wpkh(cTTgG6x13nQjAeECaCaDrjrUdcjReZBGspcmNavsnSRyXq7zXT7r)",
27-
Network::Testnet,
22+
Network::Regtest,
2823
)?;
2924

25+
let regtestenv = RegTestEnv::new();
26+
regtestenv.generate(&[&wallet]);
27+
let client = Client::new(regtestenv.electrum_url()).unwrap();
28+
let blockchain = ElectrumBlockchain::from(client);
29+
wallet.sync(&blockchain, SyncOptions::default())?;
30+
3031
let balance = wallet.get_balance()?;
3132
assert!(
3233
balance.confirmed > 10_000,
@@ -36,7 +37,7 @@ fn unconfirmed() -> Result<(), ProofError> {
3637
let addr = wallet.get_address(AddressIndex::New).unwrap();
3738
assert_eq!(
3839
addr.to_string(),
39-
"tb1qexxes4qzr3m6a6mcqrp0d4xexagw08fgxv898e"
40+
"bcrt1qexxes4qzr3m6a6mcqrp0d4xexagw08fgy97gss"
4041
);
4142

4243
let mut builder = wallet.build_tx();
@@ -74,12 +75,18 @@ fn unconfirmed() -> Result<(), ProofError> {
7475
#[test]
7576
#[should_panic(expected = "NonSpendableInput")]
7677
fn confirmed() {
77-
let (wallet, blockchain) = construct_wallet(
78+
let wallet = construct_wallet(
7879
"wpkh(cTTgG6x13nQjAeECaCaDrjrUdcjReZBGspcmNavsnSRyXq7zXT7r)",
79-
Network::Testnet,
80+
Network::Regtest,
8081
)
8182
.unwrap();
8283

84+
let regtestenv = RegTestEnv::new();
85+
regtestenv.generate(&[&wallet]);
86+
let client = Client::new(regtestenv.electrum_url()).unwrap();
87+
let blockchain = ElectrumBlockchain::from(client);
88+
wallet.sync(&blockchain, SyncOptions::default()).unwrap();
89+
8390
let balance = wallet.get_balance().unwrap();
8491
assert!(
8592
balance.confirmed > 10_000,
@@ -89,7 +96,7 @@ fn confirmed() {
8996
let addr = wallet.get_address(AddressIndex::New).unwrap();
9097
assert_eq!(
9198
addr.to_string(),
92-
"tb1qexxes4qzr3m6a6mcqrp0d4xexagw08fgxv898e"
99+
"bcrt1qexxes4qzr3m6a6mcqrp0d4xexagw08fgy97gss"
93100
);
94101

95102
let mut builder = wallet.build_tx();

tests/multi_sig.rs

Lines changed: 44 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1+
mod regtestenv;
12
use bdk::bitcoin::secp256k1::Secp256k1;
23
use bdk::bitcoin::util::key::{PrivateKey, PublicKey};
34
use bdk::bitcoin::util::psbt::PartiallySignedTransaction as PSBT;
45
use bdk::bitcoin::Network;
5-
use bdk::blockchain::electrum::ElectrumBlockchain;
66
use bdk::database::memory::MemoryDatabase;
7-
use bdk::electrum_client::Client;
8-
use bdk::wallet::{AddressIndex, SyncOptions, Wallet};
7+
use bdk::wallet::{AddressIndex, Wallet};
98
use bdk::Error;
109
use bdk::SignOptions;
1110
use bdk_reserves::reserves::*;
11+
use regtestenv::RegTestEnv;
1212
use rstest::rstest;
1313

1414
enum MultisigType {
@@ -45,22 +45,18 @@ fn construct_multisig_wallet(
4545
desc
4646
}) + &postfix;
4747

48-
let client = Client::new("ssl://electrum.blockstream.info:60002")?;
49-
let wallet = Wallet::new(&desc, None, Network::Testnet, MemoryDatabase::default())?;
50-
51-
let blockchain = ElectrumBlockchain::from(client);
52-
wallet.sync(&blockchain, SyncOptions::default())?;
48+
let wallet = Wallet::new(&desc, None, Network::Regtest, MemoryDatabase::default())?;
5349

5450
Ok(wallet)
5551
}
5652

5753
#[rstest]
58-
#[case(
54+
#[case::wsh(
5955
MultisigType::Wsh,
60-
"tb1qnmhmxkaqqz4lrruhew5mk6zqr0ezstn3stj6c3r2my6hgkescm0sg3qc0r"
56+
"bcrt1qnmhmxkaqqz4lrruhew5mk6zqr0ezstn3stj6c3r2my6hgkescm0s9g276e"
6157
)]
62-
#[case(MultisigType::ShWsh, "2NDTiUegP4NwKMnxXm6KdCL1B1WHamhZHC1")]
63-
#[case(MultisigType::P2sh, "2N7yrzYXgQzNQQuHNTjcP3iwpzFVsqe6non")]
58+
#[case::shwsh(MultisigType::ShWsh, "2NDTiUegP4NwKMnxXm6KdCL1B1WHamhZHC1")]
59+
#[case::p2sh(MultisigType::P2sh, "2N7yrzYXgQzNQQuHNTjcP3iwpzFVsqe6non")]
6460
fn test_proof_multisig(
6561
#[case] script_type: MultisigType,
6662
#[case] expected_address: &'static str,
@@ -79,30 +75,38 @@ fn test_proof_multisig(
7975
];
8076
pubkeys.sort_by_key(|item| item.to_string());
8177

82-
let wallet1 = construct_multisig_wallet(&signer1, &pubkeys, &script_type)?;
83-
let wallet2 = construct_multisig_wallet(&signer2, &pubkeys, &script_type)?;
84-
let wallet3 = construct_multisig_wallet(&signer3, &pubkeys, &script_type)?;
85-
assert_eq!(
86-
wallet1.get_address(AddressIndex::New)?.to_string(),
87-
expected_address
88-
);
89-
assert_eq!(
90-
wallet2.get_address(AddressIndex::New)?.to_string(),
91-
expected_address
92-
);
93-
assert_eq!(
94-
wallet3.get_address(AddressIndex::New)?.to_string(),
95-
expected_address
96-
);
97-
let balance = wallet1.get_balance()?;
98-
assert!(
99-
(410000..=420000).contains(&balance.confirmed),
100-
"balance is {} but should be between 410000 and 420000",
101-
balance
102-
);
78+
let wallets = [
79+
construct_multisig_wallet(&signer1, &pubkeys, &script_type)?,
80+
construct_multisig_wallet(&signer2, &pubkeys, &script_type)?,
81+
construct_multisig_wallet(&signer3, &pubkeys, &script_type)?,
82+
];
83+
84+
wallets.iter().enumerate().for_each(|(i, wallet)| {
85+
let addr = wallet.get_address(AddressIndex::New).unwrap().to_string();
86+
assert!(
87+
addr == expected_address,
88+
"Wallet {} address is {} instead of {}",
89+
i,
90+
addr,
91+
expected_address
92+
);
93+
});
94+
95+
let regtestenv = RegTestEnv::new();
96+
regtestenv.generate(&[&wallets[0], &wallets[1], &wallets[2]]);
97+
98+
wallets.iter().enumerate().for_each(|(i, wallet)| {
99+
let balance = wallet.get_balance().unwrap();
100+
assert!(
101+
(4_999_999_256..=4_999_999_596).contains(&balance.confirmed),
102+
"balance of wallet {} is {} but should be between 4'999'999'256 and 4'999'999'596",
103+
i,
104+
balance
105+
);
106+
});
103107

104108
let message = "All my precious coins";
105-
let mut psbt = wallet1.create_proof(message)?;
109+
let mut psbt = wallets[2].create_proof(message)?;
106110
let num_inp = psbt.inputs.len();
107111
assert!(
108112
num_inp > 1,
@@ -131,33 +135,34 @@ fn test_proof_multisig(
131135
remove_partial_sigs: false,
132136
..Default::default()
133137
};
134-
let finalized = wallet1.sign(&mut psbt, signopts.clone())?;
138+
let finalized = wallets[0].sign(&mut psbt, signopts.clone())?;
135139
assert_eq!(count_signatures(&psbt), (num_inp - 1, 1, 0));
136140
assert!(!finalized);
137141

138-
let finalized = wallet2.sign(&mut psbt, signopts.clone())?;
142+
let finalized = wallets[1].sign(&mut psbt, signopts.clone())?;
139143
assert_eq!(
140144
count_signatures(&psbt),
141145
((num_inp - 1) * 2, num_inp, num_inp - 1)
142146
);
143147
assert!(finalized);
144148

145149
// 2 signatures are enough. Just checking what happens...
146-
let finalized = wallet3.sign(&mut psbt, signopts.clone())?;
150+
let finalized = wallets[2].sign(&mut psbt, signopts.clone())?;
147151
assert_eq!(
148152
count_signatures(&psbt),
149153
((num_inp - 1) * 2, num_inp, num_inp - 1)
150154
);
151155
assert!(finalized);
152156

153-
let finalized = wallet1.finalize_psbt(&mut psbt, signopts)?;
157+
let finalized = wallets[0].finalize_psbt(&mut psbt, signopts)?;
154158
assert_eq!(
155159
count_signatures(&psbt),
156160
((num_inp - 1) * 2, num_inp, num_inp - 1)
157161
);
158162
assert!(finalized);
159163

160-
let spendable = wallet1.verify_proof(&psbt, message, None)?;
164+
let spendable = wallets[0].verify_proof(&psbt, message, None)?;
165+
let balance = wallets[0].get_balance()?;
161166
assert_eq!(spendable, balance.confirmed);
162167

163168
Ok(())

tests/regtestenv.rs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
use bdk::blockchain::{electrum::ElectrumBlockchain, Blockchain};
2+
use bdk::database::memory::MemoryDatabase;
3+
use bdk::electrum_client::Client;
4+
use bdk::electrum_client::ElectrumApi;
5+
use bdk::wallet::{AddressIndex, SyncOptions, Wallet};
6+
use bdk::SignOptions;
7+
use electrsd::bitcoind::bitcoincore_rpc::{bitcoin::Address, RpcApi};
8+
use electrsd::bitcoind::BitcoinD;
9+
use electrsd::ElectrsD;
10+
use std::str::FromStr;
11+
use std::time::Duration;
12+
13+
/// The environment to run a single test, while many of them can run in parallel.
14+
pub struct RegTestEnv {
15+
/// Instance of the bitcoin core daemon
16+
bitcoind: BitcoinD,
17+
/// Instance of the electrs electrum server
18+
electrsd: ElectrsD,
19+
}
20+
21+
impl RegTestEnv {
22+
/// set up local bitcoind and electrs instances in regtest mode
23+
pub fn new() -> Self {
24+
let mut bitcoind_conf = electrsd::bitcoind::Conf::default();
25+
bitcoind_conf.p2p = electrsd::bitcoind::P2P::Yes;
26+
27+
let bitcoind_exe = electrsd::bitcoind::downloaded_exe_path()
28+
.expect("We should always have downloaded path");
29+
let bitcoind = BitcoinD::with_conf(bitcoind_exe, &bitcoind_conf).unwrap();
30+
31+
let mut elect_conf = electrsd::Conf::default();
32+
elect_conf.view_stderr = false; // setting this to true will lead to very verbose logging
33+
let elect_exe =
34+
electrsd::downloaded_exe_path().expect("We should always have downloaded path");
35+
let electrsd = ElectrsD::with_conf(elect_exe, &bitcoind, &elect_conf).unwrap();
36+
37+
RegTestEnv { bitcoind, electrsd }
38+
}
39+
40+
/// returns the URL where a client can connect to the embedded electrum server
41+
pub fn electrum_url(&self) -> &str {
42+
&self.electrsd.electrum_url
43+
}
44+
45+
/// generates some blocks to have some coins to test with
46+
pub fn generate(&self, wallets: &[&Wallet<MemoryDatabase>]) {
47+
let addr2 = wallets[0].get_address(AddressIndex::Peek(1)).unwrap();
48+
let addr1 = wallets[0].get_address(AddressIndex::Peek(0)).unwrap();
49+
const MY_FOREIGN_ADDR: &str = "mpSFfNURcFTz2yJxBzRY9NhnozxeJ2AUC8";
50+
let foreign_addr = Address::from_str(MY_FOREIGN_ADDR).unwrap();
51+
52+
// generate to the first receiving address of the test wallet
53+
self.generate_to_address(10, &addr2);
54+
// make the newly mined coins spendable
55+
self.generate_to_address(100, &foreign_addr);
56+
57+
let client = Client::new(self.electrum_url()).unwrap();
58+
let blockchain = ElectrumBlockchain::from(client);
59+
wallets.iter().enumerate().for_each(|(i, wallet)| {
60+
wallet.sync(&blockchain, SyncOptions::default()).unwrap();
61+
let balance = wallet.get_balance().unwrap();
62+
assert!(
63+
balance.confirmed == 5_000_000_000,
64+
"balance of wallet {} is {} but should be 5'000'000'000",
65+
i,
66+
balance
67+
);
68+
});
69+
70+
let mut builder = wallets[0].build_tx();
71+
builder
72+
.add_recipient(addr1.script_pubkey(), 1_000_000)
73+
.fee_rate(bdk::FeeRate::from_sat_per_vb(2.0));
74+
let (mut psbt, _) = builder.finish().unwrap();
75+
let signopts = SignOptions {
76+
..Default::default()
77+
};
78+
let finalized = wallets
79+
.iter()
80+
.any(|wallet| wallet.sign(&mut psbt, signopts.clone()).unwrap());
81+
assert!(finalized);
82+
blockchain.broadcast(&psbt.extract_tx()).unwrap();
83+
84+
// make the newly moved coins spendable
85+
self.generate_to_address(6, &foreign_addr);
86+
87+
wallets
88+
.iter()
89+
.for_each(|wallet| wallet.sync(&blockchain, SyncOptions::default()).unwrap());
90+
}
91+
92+
fn generate_to_address(&self, blocks: usize, address: &Address) {
93+
let old_height = self
94+
.electrsd
95+
.client
96+
.block_headers_subscribe()
97+
.unwrap()
98+
.height;
99+
100+
self.bitcoind
101+
.client
102+
.generate_to_address(blocks as u64, address)
103+
.unwrap();
104+
105+
let header = loop {
106+
std::thread::sleep(Duration::from_secs(1));
107+
let header = self.electrsd.client.block_headers_subscribe().unwrap();
108+
if header.height >= old_height + blocks {
109+
break header;
110+
}
111+
};
112+
113+
assert_eq!(header.height, old_height + blocks);
114+
}
115+
}
116+
117+
impl Default for RegTestEnv {
118+
fn default() -> Self {
119+
Self::new()
120+
}
121+
}

0 commit comments

Comments
 (0)