matrix:
blockchain:
- name: electrum
- container: bitcoindevkit/electrs:0.4.0
- 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
+ container: bitcoindevkit/electrs
- name: rpc
- container: bitcoindevkit/electrs:0.4.0
- start: /root/electrs --network regtest --cookie-file $GITHUB_WORKSPACE/.bitcoin/regtest/.cookie --jsonrpc-import
+ container: bitcoindevkit/electrs
container: ${{ matrix.blockchain.container }}
env:
- BDK_RPC_AUTH: COOKIEFILE
- BDK_RPC_COOKIEFILE: ${{ github.workspace }}/.bitcoin/regtest/.cookie
- 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
+ ELECTRS_EXE: /root/electrs
+ BITCOIND_EXE: /root/bitcoind
steps:
- name: Checkout
uses: actions/checkout@v2
run: $HOME/.cargo/bin/rustup set profile minimal
- name: Update toolchain
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
run: $HOME/.cargo/bin/cargo test --features test-${{ matrix.blockchain.name }},test-blockchains --no-default-features ${{ matrix.blockchain.name }}::bdk_blockchain_tests
Ok(result.wallets.into_iter().map(|n| n.name).collect())
}
+#[cfg(test)]
#[cfg(feature = "test-blockchains")]
crate::bdk_blockchain_tests! {
- fn test_instance() -> 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()),
- )),
- };
+ fn test_instance(test_client: &TestClient) -> RpcBlockchain {
let config = RpcConfig {
- url,
- auth,
+ url: test_client.bitcoind.rpc_url(),
+ auth: Auth::CookieFile(test_client.bitcoind.params.cookie_file.clone()),
network: Network::Regtest,
wallet_name: format!("client-wallet-test-{:?}", std::time::SystemTime::now() ),
skip_blocks: None,
}
fn create_bitcoind(args: Vec<&str>) -> BitcoinD {
let exe = std::env::var("BITCOIND_EXE").unwrap();
- let conf = Conf {
- args,
- ..Default::default()
- };
+ let mut conf = Conf::default();
+ conf.args.extend(args);
bitcoind::BitcoinD::with_conf(exe, &conf).unwrap()
}
#[test]
fn test_rpc_wallet_setup() {
- env_logger::try_init().unwrap();
+ let _ = env_logger::try_init();
let bitcoind = create_bitcoind(vec![]);
let node_address = bitcoind.client.get_new_address(None, None).unwrap();
let blockchain = create_rpc(&bitcoind, DESCRIPTOR_PUB, Network::Regtest).unwrap();
use bitcoin::{Address, Amount, Script, Transaction, Txid};
pub use bitcoincore_rpc::bitcoincore_rpc_json::AddressType;
pub use bitcoincore_rpc::{Auth, Client as RpcClient, RpcApi};
+use bitcoind::BitcoinD;
use core::str::FromStr;
+use electrsd::ElectrsD;
pub use electrum_client::{Client as ElectrumClient, ElectrumApi};
#[allow(unused_imports)]
use log::{debug, error, info, trace};
use std::collections::HashMap;
use std::env;
use std::ops::Deref;
-use std::path::PathBuf;
use std::time::Duration;
pub struct TestClient {
- client: RpcClient,
- electrum: ElectrumClient,
+ pub bitcoind: BitcoinD,
+ pub electrsd: ElectrsD,
}
impl TestClient {
- pub fn new(rpc_host_and_wallet: String, rpc_wallet_name: String) -> Self {
- let client = RpcClient::new(
- format!("http://{}/wallet/{}", rpc_host_and_wallet, rpc_wallet_name),
- get_auth(),
- )
- .unwrap();
- let electrum = ElectrumClient::new(&get_electrum_url()).unwrap();
+ pub fn new(bitcoind_exe: String, electrs_exe: String) -> Self {
+ debug!("launching {} and {}", &bitcoind_exe, &electrs_exe);
+ let bitcoind = BitcoinD::new(bitcoind_exe).unwrap();
+ let electrsd = ElectrsD::new(electrs_exe, &bitcoind, false, false).unwrap(); // TODO http_enabled should be true only for esplora
+
+ let node_address = bitcoind.client.get_new_address(None, None).unwrap();
+ bitcoind
+ .client
+ .generate_to_address(101, &node_address)
+ .unwrap();
- TestClient { client, electrum }
+ 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) {
exponential_backoff_poll(|| {
trace!("wait_for_tx {}", txid);
- self.electrum
+ self.electrsd
+ .client
.script_get_history(monitor_script)
.unwrap()
.iter()
}
fn wait_for_block(&mut self, min_height: usize) {
- self.electrum.block_headers_subscribe().unwrap();
+ self.electrsd.client.block_headers_subscribe().unwrap();
loop {
let header = exponential_backoff_poll(|| {
- self.electrum.ping().unwrap();
- self.electrum.block_headers_pop().unwrap()
+ self.electrsd.client.ping().unwrap();
+ self.electrsd.client.block_headers_pop().unwrap()
});
if header.height >= min_height {
break;
.unwrap();
// broadcast through electrum so that it caches the tx immediately
+
let txid = self
- .electrum
+ .electrsd
+ .client
.transaction_broadcast(&deserialize(&tx.hex).unwrap())
.unwrap();
+ debug!("broadcasted to electrum {}", txid);
if let Some(num) = meta_tx.min_confirmations {
self.generate(num, None);
let block_hex: String = serialize(&block).to_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 =
self.call("submitblock", &[block_hex.into()]).unwrap();
}
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 initial_height = self.get_block_info(&best_hash).unwrap().height;
type Target = RpcClient;
fn deref(&self) -> &Self::Target {
- &self.client
+ &self.bitcoind.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)
+ let bitcoind_exe =
+ env::var("BITCOIND_EXE").unwrap_or_else(|_| "/root/bitcoind".to_string());
+ let electrs_exe = env::var("ELECTRS_EXE").unwrap_or_else(|_| "/root/electrs".to_string());
+ Self::new(bitcoind_exe, electrs_exe)
}
}
}
}
-// 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
/// 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.
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, "incorrect balance after send");