use bitcoind and electrsd crate to launch daemons
This commit is contained in:
parent
42adad7dbd
commit
6394c3e209
21
.github/workflows/cont_integration.yml
vendored
21
.github/workflows/cont_integration.yml
vendored
@ -82,22 +82,13 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
blockchain:
|
blockchain:
|
||||||
- name: electrum
|
- name: electrum
|
||||||
container: bitcoindevkit/electrs:0.4.0
|
container: bitcoindevkit/electrs
|
||||||
start: /root/electrs --network regtest --cookie-file $GITHUB_WORKSPACE/.bitcoin/regtest/.cookie --jsonrpc-import
|
|
||||||
- name: esplora
|
|
||||||
container: bitcoindevkit/esplora:0.4.0
|
|
||||||
start: /root/electrs --network regtest -vvv --daemon-dir $GITHUB_WORKSPACE/.bitcoin --jsonrpc-import --electrum-rpc-addr=0.0.0.0:60401 --http-addr 0.0.0.0:3002
|
|
||||||
- name: rpc
|
- name: rpc
|
||||||
container: bitcoindevkit/electrs:0.4.0
|
container: bitcoindevkit/electrs
|
||||||
start: /root/electrs --network regtest --cookie-file $GITHUB_WORKSPACE/.bitcoin/regtest/.cookie --jsonrpc-import
|
|
||||||
container: ${{ matrix.blockchain.container }}
|
container: ${{ matrix.blockchain.container }}
|
||||||
env:
|
env:
|
||||||
BDK_RPC_AUTH: COOKIEFILE
|
ELECTRS_EXE: /root/electrs
|
||||||
BDK_RPC_COOKIEFILE: ${{ github.workspace }}/.bitcoin/regtest/.cookie
|
BITCOIND_EXE: /root/bitcoind
|
||||||
BDK_RPC_URL: 127.0.0.1:18443
|
|
||||||
BDK_RPC_WALLET: bdk-test
|
|
||||||
BDK_ELECTRUM_URL: tcp://127.0.0.1:60401
|
|
||||||
BDK_ESPLORA_URL: http://127.0.0.1:3002
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
@ -119,10 +110,6 @@ jobs:
|
|||||||
run: $HOME/.cargo/bin/rustup set profile minimal
|
run: $HOME/.cargo/bin/rustup set profile minimal
|
||||||
- name: Update toolchain
|
- name: Update toolchain
|
||||||
run: $HOME/.cargo/bin/rustup update
|
run: $HOME/.cargo/bin/rustup update
|
||||||
- name: Start core
|
|
||||||
run: ./ci/start-core.sh
|
|
||||||
- name: start ${{ matrix.blockchain.name }}
|
|
||||||
run: nohup ${{ matrix.blockchain.start }} & sleep 5
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: $HOME/.cargo/bin/cargo test --features test-${{ matrix.blockchain.name }},test-blockchains --no-default-features ${{ matrix.blockchain.name }}::bdk_blockchain_tests
|
run: $HOME/.cargo/bin/cargo test --features test-${{ matrix.blockchain.name }},test-blockchains --no-default-features ${{ matrix.blockchain.name }}::bdk_blockchain_tests
|
||||||
|
|
||||||
|
@ -72,6 +72,7 @@ env_logger = "0.7"
|
|||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
serial_test = "0.4"
|
serial_test = "0.4"
|
||||||
bitcoind = "0.11.0"
|
bitcoind = "0.11.0"
|
||||||
|
electrsd = "0.3.0"
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "address_validator"
|
name = "address_validator"
|
||||||
|
@ -169,9 +169,10 @@ impl ConfigurableBlockchain for ElectrumBlockchain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
#[cfg(feature = "test-blockchains")]
|
#[cfg(feature = "test-blockchains")]
|
||||||
crate::bdk_blockchain_tests! {
|
crate::bdk_blockchain_tests! {
|
||||||
fn test_instance() -> ElectrumBlockchain {
|
fn test_instance(test_client: &TestClient) -> ElectrumBlockchain {
|
||||||
ElectrumBlockchain::from(Client::new(&testutils::blockchain_tests::get_electrum_url()).unwrap())
|
ElectrumBlockchain::from(Client::new(&test_client.electrsd.electrum_url).unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -415,9 +415,10 @@ impl_error!(std::num::ParseIntError, Parsing, EsploraError);
|
|||||||
impl_error!(consensus::encode::Error, BitcoinEncoding, EsploraError);
|
impl_error!(consensus::encode::Error, BitcoinEncoding, EsploraError);
|
||||||
impl_error!(bitcoin::hashes::hex::Error, Hex, EsploraError);
|
impl_error!(bitcoin::hashes::hex::Error, Hex, EsploraError);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
#[cfg(feature = "test-blockchains")]
|
#[cfg(feature = "test-blockchains")]
|
||||||
crate::bdk_blockchain_tests! {
|
crate::bdk_blockchain_tests! {
|
||||||
fn test_instance() -> EsploraBlockchain {
|
fn test_instance(test_client: &TestClient) -> EsploraBlockchain {
|
||||||
EsploraBlockchain::new(std::env::var("BDK_ESPLORA_URL").unwrap_or("127.0.0.1:3002".into()).as_str(), None)
|
EsploraBlockchain::new(test_client.electrsd.esplora_url.as_ref().unwrap(), None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -419,27 +419,14 @@ fn list_wallet_dir(client: &Client) -> Result<Vec<String>, Error> {
|
|||||||
Ok(result.wallets.into_iter().map(|n| n.name).collect())
|
Ok(result.wallets.into_iter().map(|n| n.name).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
#[cfg(feature = "test-blockchains")]
|
#[cfg(feature = "test-blockchains")]
|
||||||
crate::bdk_blockchain_tests! {
|
crate::bdk_blockchain_tests! {
|
||||||
|
|
||||||
fn test_instance() -> RpcBlockchain {
|
fn test_instance(test_client: &TestClient) -> RpcBlockchain {
|
||||||
let url = std::env::var("BDK_RPC_URL").unwrap_or_else(|_| "127.0.0.1:18443".to_string());
|
|
||||||
let url = format!("http://{}", url);
|
|
||||||
|
|
||||||
// TODO same code in `fn get_auth` in testutils, make it public there
|
|
||||||
let auth = match std::env::var("BDK_RPC_AUTH").as_ref().map(String::as_ref) {
|
|
||||||
Ok("USER_PASS") => Auth::UserPass(
|
|
||||||
std::env::var("BDK_RPC_USER").unwrap(),
|
|
||||||
std::env::var("BDK_RPC_PASS").unwrap(),
|
|
||||||
),
|
|
||||||
_ => Auth::CookieFile(std::path::PathBuf::from(
|
|
||||||
std::env::var("BDK_RPC_COOKIEFILE")
|
|
||||||
.unwrap_or_else(|_| "/home/user/.bitcoin/regtest/.cookie".to_string()),
|
|
||||||
)),
|
|
||||||
};
|
|
||||||
let config = RpcConfig {
|
let config = RpcConfig {
|
||||||
url,
|
url: test_client.bitcoind.rpc_url(),
|
||||||
auth,
|
auth: Auth::CookieFile(test_client.bitcoind.params.cookie_file.clone()),
|
||||||
network: Network::Regtest,
|
network: Network::Regtest,
|
||||||
wallet_name: format!("client-wallet-test-{:?}", std::time::SystemTime::now() ),
|
wallet_name: format!("client-wallet-test-{:?}", std::time::SystemTime::now() ),
|
||||||
skip_blocks: None,
|
skip_blocks: None,
|
||||||
@ -486,10 +473,8 @@ mod test {
|
|||||||
}
|
}
|
||||||
fn create_bitcoind(args: Vec<&str>) -> BitcoinD {
|
fn create_bitcoind(args: Vec<&str>) -> BitcoinD {
|
||||||
let exe = std::env::var("BITCOIND_EXE").unwrap();
|
let exe = std::env::var("BITCOIND_EXE").unwrap();
|
||||||
let conf = Conf {
|
let mut conf = Conf::default();
|
||||||
args,
|
conf.args.extend(args);
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
bitcoind::BitcoinD::with_conf(exe, &conf).unwrap()
|
bitcoind::BitcoinD::with_conf(exe, &conf).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -498,7 +483,7 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rpc_wallet_setup() {
|
fn test_rpc_wallet_setup() {
|
||||||
env_logger::try_init().unwrap();
|
let _ = env_logger::try_init();
|
||||||
let bitcoind = create_bitcoind(vec![]);
|
let bitcoind = create_bitcoind(vec![]);
|
||||||
let node_address = bitcoind.client.get_new_address(None, None).unwrap();
|
let node_address = bitcoind.client.get_new_address(None, None).unwrap();
|
||||||
let blockchain = create_rpc(&bitcoind, DESCRIPTOR_PUB, Network::Regtest).unwrap();
|
let blockchain = create_rpc(&bitcoind, DESCRIPTOR_PUB, Network::Regtest).unwrap();
|
||||||
|
@ -5,31 +5,37 @@ use bitcoin::hashes::sha256d;
|
|||||||
use bitcoin::{Address, Amount, Script, Transaction, Txid};
|
use bitcoin::{Address, Amount, Script, Transaction, Txid};
|
||||||
pub use bitcoincore_rpc::bitcoincore_rpc_json::AddressType;
|
pub use bitcoincore_rpc::bitcoincore_rpc_json::AddressType;
|
||||||
pub use bitcoincore_rpc::{Auth, Client as RpcClient, RpcApi};
|
pub use bitcoincore_rpc::{Auth, Client as RpcClient, RpcApi};
|
||||||
|
use bitcoind::BitcoinD;
|
||||||
use core::str::FromStr;
|
use core::str::FromStr;
|
||||||
|
use electrsd::ElectrsD;
|
||||||
pub use electrum_client::{Client as ElectrumClient, ElectrumApi};
|
pub use electrum_client::{Client as ElectrumClient, ElectrumApi};
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use log::{debug, error, info, trace};
|
use log::{debug, error, info, trace};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
pub struct TestClient {
|
pub struct TestClient {
|
||||||
client: RpcClient,
|
pub bitcoind: BitcoinD,
|
||||||
electrum: ElectrumClient,
|
pub electrsd: ElectrsD,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestClient {
|
impl TestClient {
|
||||||
pub fn new(rpc_host_and_wallet: String, rpc_wallet_name: String) -> Self {
|
pub fn new(bitcoind_exe: String, electrs_exe: String) -> Self {
|
||||||
let client = RpcClient::new(
|
debug!("launching {} and {}", &bitcoind_exe, &electrs_exe);
|
||||||
format!("http://{}/wallet/{}", rpc_host_and_wallet, rpc_wallet_name),
|
let bitcoind = BitcoinD::new(bitcoind_exe).unwrap();
|
||||||
get_auth(),
|
let electrsd = ElectrsD::new(electrs_exe, &bitcoind, false, false).unwrap(); // TODO http_enabled should be true only for esplora
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let electrum = ElectrumClient::new(&get_electrum_url()).unwrap();
|
|
||||||
|
|
||||||
TestClient { client, electrum }
|
let node_address = bitcoind.client.get_new_address(None, None).unwrap();
|
||||||
|
bitcoind
|
||||||
|
.client
|
||||||
|
.generate_to_address(101, &node_address)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut test_client = TestClient { bitcoind, electrsd };
|
||||||
|
TestClient::wait_for_block(&mut test_client, 101);
|
||||||
|
test_client
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait_for_tx(&mut self, txid: Txid, monitor_script: &Script) {
|
fn wait_for_tx(&mut self, txid: Txid, monitor_script: &Script) {
|
||||||
@ -37,7 +43,8 @@ impl TestClient {
|
|||||||
exponential_backoff_poll(|| {
|
exponential_backoff_poll(|| {
|
||||||
trace!("wait_for_tx {}", txid);
|
trace!("wait_for_tx {}", txid);
|
||||||
|
|
||||||
self.electrum
|
self.electrsd
|
||||||
|
.client
|
||||||
.script_get_history(monitor_script)
|
.script_get_history(monitor_script)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.iter()
|
.iter()
|
||||||
@ -46,12 +53,12 @@ impl TestClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn wait_for_block(&mut self, min_height: usize) {
|
fn wait_for_block(&mut self, min_height: usize) {
|
||||||
self.electrum.block_headers_subscribe().unwrap();
|
self.electrsd.client.block_headers_subscribe().unwrap();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let header = exponential_backoff_poll(|| {
|
let header = exponential_backoff_poll(|| {
|
||||||
self.electrum.ping().unwrap();
|
self.electrsd.client.ping().unwrap();
|
||||||
self.electrum.block_headers_pop().unwrap()
|
self.electrsd.client.block_headers_pop().unwrap()
|
||||||
});
|
});
|
||||||
if header.height >= min_height {
|
if header.height >= min_height {
|
||||||
break;
|
break;
|
||||||
@ -96,10 +103,13 @@ impl TestClient {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// broadcast through electrum so that it caches the tx immediately
|
// broadcast through electrum so that it caches the tx immediately
|
||||||
|
|
||||||
let txid = self
|
let txid = self
|
||||||
.electrum
|
.electrsd
|
||||||
|
.client
|
||||||
.transaction_broadcast(&deserialize(&tx.hex).unwrap())
|
.transaction_broadcast(&deserialize(&tx.hex).unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
debug!("broadcasted to electrum {}", txid);
|
||||||
|
|
||||||
if let Some(num) = meta_tx.min_confirmations {
|
if let Some(num) = meta_tx.min_confirmations {
|
||||||
self.generate(num, None);
|
self.generate(num, None);
|
||||||
@ -209,7 +219,7 @@ impl TestClient {
|
|||||||
let block_hex: String = serialize(&block).to_hex();
|
let block_hex: String = serialize(&block).to_hex();
|
||||||
debug!("generated block hex: {}", block_hex);
|
debug!("generated block hex: {}", block_hex);
|
||||||
|
|
||||||
self.electrum.block_headers_subscribe().unwrap();
|
self.electrsd.client.block_headers_subscribe().unwrap();
|
||||||
|
|
||||||
let submit_result: serde_json::Value =
|
let submit_result: serde_json::Value =
|
||||||
self.call("submitblock", &[block_hex.into()]).unwrap();
|
self.call("submitblock", &[block_hex.into()]).unwrap();
|
||||||
@ -237,7 +247,7 @@ impl TestClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn invalidate(&mut self, num_blocks: u64) {
|
pub fn invalidate(&mut self, num_blocks: u64) {
|
||||||
self.electrum.block_headers_subscribe().unwrap();
|
self.electrsd.client.block_headers_subscribe().unwrap();
|
||||||
|
|
||||||
let best_hash = self.get_best_block_hash().unwrap();
|
let best_hash = self.get_best_block_hash().unwrap();
|
||||||
let initial_height = self.get_block_info(&best_hash).unwrap().height;
|
let initial_height = self.get_block_info(&best_hash).unwrap().height;
|
||||||
@ -288,16 +298,16 @@ impl Deref for TestClient {
|
|||||||
type Target = RpcClient;
|
type Target = RpcClient;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.client
|
&self.bitcoind.client
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TestClient {
|
impl Default for TestClient {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let rpc_host_and_port =
|
let bitcoind_exe =
|
||||||
env::var("BDK_RPC_URL").unwrap_or_else(|_| "127.0.0.1:18443".to_string());
|
env::var("BITCOIND_EXE").unwrap_or_else(|_| "/root/bitcoind".to_string());
|
||||||
let wallet = env::var("BDK_RPC_WALLET").unwrap_or_else(|_| "bdk-test".to_string());
|
let electrs_exe = env::var("ELECTRS_EXE").unwrap_or_else(|_| "/root/electrs".to_string());
|
||||||
Self::new(rpc_host_and_port, wallet)
|
Self::new(bitcoind_exe, electrs_exe)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,20 +327,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: we currently only support env vars, we could also parse a toml file
|
|
||||||
fn get_auth() -> Auth {
|
|
||||||
match env::var("BDK_RPC_AUTH").as_ref().map(String::as_ref) {
|
|
||||||
Ok("USER_PASS") => Auth::UserPass(
|
|
||||||
env::var("BDK_RPC_USER").unwrap(),
|
|
||||||
env::var("BDK_RPC_PASS").unwrap(),
|
|
||||||
),
|
|
||||||
_ => Auth::CookieFile(PathBuf::from(
|
|
||||||
env::var("BDK_RPC_COOKIEFILE")
|
|
||||||
.unwrap_or_else(|_| "/home/user/.bitcoin/regtest/.cookie".to_string()),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This macro runs blockchain tests against a `Blockchain` implementation. It requires access to a
|
/// 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
|
/// 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.
|
/// the setup required to run the tests yourself.
|
||||||
@ -590,7 +586,6 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
let tx = psbt.extract_tx();
|
let tx = psbt.extract_tx();
|
||||||
println!("{}", bitcoin::consensus::encode::serialize_hex(&tx));
|
println!("{}", bitcoin::consensus::encode::serialize_hex(&tx));
|
||||||
wallet.broadcast(tx).unwrap();
|
wallet.broadcast(tx).unwrap();
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
assert_eq!(wallet.get_balance().unwrap(), details.received, "incorrect balance after send");
|
assert_eq!(wallet.get_balance().unwrap(), details.received, "incorrect balance after send");
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
// licenses.
|
// licenses.
|
||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
#[cfg(feature = "test-blockchains")]
|
#[cfg(feature = "test-blockchains")]
|
||||||
pub mod blockchain_tests;
|
pub mod blockchain_tests;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user