trigger electrs when polling
This commit is contained in:
parent
18dcda844f
commit
696647b893
@ -70,7 +70,7 @@ lazy_static = "1.4"
|
|||||||
env_logger = "0.7"
|
env_logger = "0.7"
|
||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
bitcoind = "0.11.0"
|
bitcoind = "0.11.0"
|
||||||
electrsd = "0.3.0"
|
electrsd = { version="0.3.0", features = ["trigger"] }
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "address_validator"
|
name = "address_validator"
|
||||||
|
@ -434,229 +434,3 @@ crate::bdk_blockchain_tests! {
|
|||||||
RpcBlockchain::from_config(&config).unwrap()
|
RpcBlockchain::from_config(&config).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "test-rpc")]
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::{RpcBlockchain, RpcConfig};
|
|
||||||
use crate::bitcoin::consensus::deserialize;
|
|
||||||
use crate::bitcoin::{Address, Amount, Network, Transaction};
|
|
||||||
use crate::blockchain::rpc::wallet_name_from_descriptor;
|
|
||||||
use crate::blockchain::{noop_progress, Blockchain, Capability, ConfigurableBlockchain};
|
|
||||||
use crate::database::MemoryDatabase;
|
|
||||||
use crate::wallet::AddressIndex;
|
|
||||||
use crate::Wallet;
|
|
||||||
use bitcoin::secp256k1::Secp256k1;
|
|
||||||
use bitcoin::Txid;
|
|
||||||
use bitcoincore_rpc::json::CreateRawTransactionInput;
|
|
||||||
use bitcoincore_rpc::RawTx;
|
|
||||||
use bitcoincore_rpc::{Auth, RpcApi};
|
|
||||||
use bitcoind::{BitcoinD, Conf};
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
fn create_rpc(
|
|
||||||
bitcoind: &BitcoinD,
|
|
||||||
desc: &str,
|
|
||||||
network: Network,
|
|
||||||
) -> Result<RpcBlockchain, crate::Error> {
|
|
||||||
let secp = Secp256k1::new();
|
|
||||||
let wallet_name = wallet_name_from_descriptor(desc, None, network, &secp).unwrap();
|
|
||||||
|
|
||||||
let config = RpcConfig {
|
|
||||||
url: bitcoind.rpc_url(),
|
|
||||||
auth: Auth::CookieFile(bitcoind.params.cookie_file.clone()),
|
|
||||||
network,
|
|
||||||
wallet_name,
|
|
||||||
skip_blocks: None,
|
|
||||||
};
|
|
||||||
RpcBlockchain::from_config(&config)
|
|
||||||
}
|
|
||||||
fn create_bitcoind(args: Vec<&str>) -> BitcoinD {
|
|
||||||
let exe = std::env::var("BITCOIND_EXE").unwrap();
|
|
||||||
let mut conf = Conf::default();
|
|
||||||
conf.args.extend(args);
|
|
||||||
bitcoind::BitcoinD::with_conf(exe, &conf).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
const DESCRIPTOR_PUB: &'static str = "wpkh(tpubD6NzVbkrYhZ4X2yy78HWrr1M9NT8dKeWfzNiQqDdMqqa9UmmGztGGz6TaLFGsLfdft5iu32gxq1T4eMNxExNNWzVCpf9Y6JZi5TnqoC9wJq/*)";
|
|
||||||
const DESCRIPTOR_PRIV: &'static str = "wpkh(tprv8ZgxMBicQKsPdZxBDUcvTSMEaLwCTzTc6gmw8KBKwa3BJzWzec4g6VUbQBHJcutDH6mMEmBeVyN27H1NF3Nu8isZ1Sts4SufWyfLE6Mf1MB/*)";
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rpc_wallet_setup() {
|
|
||||||
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();
|
|
||||||
let db = MemoryDatabase::new();
|
|
||||||
let wallet = Wallet::new(DESCRIPTOR_PRIV, None, Network::Regtest, db, blockchain).unwrap();
|
|
||||||
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
generate(&bitcoind, 101);
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
let address = wallet.get_address(AddressIndex::New).unwrap();
|
|
||||||
let expected_address = "bcrt1q8dyvgt4vhr8ald4xuwewcxhdjha9a5k78wxm5t";
|
|
||||||
assert_eq!(expected_address, address.to_string());
|
|
||||||
send_to_address(&bitcoind, &address, 100_000);
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet.get_balance().unwrap(), 100_000);
|
|
||||||
|
|
||||||
let mut builder = wallet.build_tx();
|
|
||||||
builder.add_recipient(node_address.script_pubkey(), 50_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();
|
|
||||||
wallet.broadcast(tx).unwrap();
|
|
||||||
wallet.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
wallet.get_balance().unwrap(),
|
|
||||||
100_000 - 50_000 - details.fee.unwrap_or(0)
|
|
||||||
);
|
|
||||||
drop(wallet);
|
|
||||||
|
|
||||||
// test skip_blocks
|
|
||||||
generate(&bitcoind, 5);
|
|
||||||
let config = RpcConfig {
|
|
||||||
url: bitcoind.rpc_url(),
|
|
||||||
auth: Auth::CookieFile(bitcoind.params.cookie_file.clone()),
|
|
||||||
network: Network::Regtest,
|
|
||||||
wallet_name: "another-name".to_string(),
|
|
||||||
skip_blocks: Some(103),
|
|
||||||
};
|
|
||||||
let blockchain_skip = RpcBlockchain::from_config(&config).unwrap();
|
|
||||||
let db = MemoryDatabase::new();
|
|
||||||
let wallet_skip =
|
|
||||||
Wallet::new(DESCRIPTOR_PRIV, None, Network::Regtest, db, blockchain_skip).unwrap();
|
|
||||||
wallet_skip.sync(noop_progress(), None).unwrap();
|
|
||||||
send_to_address(&bitcoind, &address, 100_000);
|
|
||||||
wallet_skip.sync(noop_progress(), None).unwrap();
|
|
||||||
assert_eq!(wallet_skip.get_balance().unwrap(), 100_000);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rpc_from_config() {
|
|
||||||
let bitcoind = create_bitcoind(vec![]);
|
|
||||||
let blockchain = create_rpc(&bitcoind, DESCRIPTOR_PUB, Network::Regtest);
|
|
||||||
assert!(blockchain.is_ok());
|
|
||||||
let blockchain = create_rpc(&bitcoind, DESCRIPTOR_PUB, Network::Testnet);
|
|
||||||
assert!(blockchain.is_err(), "wrong network doesn't error");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rpc_capabilities_get_tx() {
|
|
||||||
let bitcoind = create_bitcoind(vec![]);
|
|
||||||
let rpc = create_rpc(&bitcoind, DESCRIPTOR_PUB, Network::Regtest).unwrap();
|
|
||||||
let capabilities = rpc.get_capabilities();
|
|
||||||
assert!(capabilities.contains(&Capability::FullHistory) && capabilities.len() == 1);
|
|
||||||
let bitcoind_indexed = create_bitcoind(vec!["-txindex"]);
|
|
||||||
let rpc_indexed = create_rpc(&bitcoind_indexed, DESCRIPTOR_PUB, Network::Regtest).unwrap();
|
|
||||||
assert_eq!(rpc_indexed.get_capabilities().len(), 3);
|
|
||||||
let address = generate(&bitcoind_indexed, 101);
|
|
||||||
let txid = send_to_address(&bitcoind_indexed, &address, 100_000);
|
|
||||||
assert!(rpc_indexed.get_tx(&txid).unwrap().is_some());
|
|
||||||
assert!(rpc.get_tx(&txid).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rpc_estimate_fee_get_height() {
|
|
||||||
let bitcoind = create_bitcoind(vec![]);
|
|
||||||
let rpc = create_rpc(&bitcoind, DESCRIPTOR_PUB, Network::Regtest).unwrap();
|
|
||||||
let result = rpc.estimate_fee(2);
|
|
||||||
assert!(result.is_err());
|
|
||||||
let address = generate(&bitcoind, 100);
|
|
||||||
// create enough tx so that core give some fee estimation
|
|
||||||
for _ in 0..15 {
|
|
||||||
let _ = bitcoind.client.generate_to_address(1, &address).unwrap();
|
|
||||||
for _ in 0..2 {
|
|
||||||
send_to_address(&bitcoind, &address, 100_000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let result = rpc.estimate_fee(2);
|
|
||||||
assert!(result.is_ok());
|
|
||||||
assert_eq!(rpc.get_height().unwrap(), 115);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rpc_node_synced_height() {
|
|
||||||
let bitcoind = create_bitcoind(vec![]);
|
|
||||||
let rpc = create_rpc(&bitcoind, DESCRIPTOR_PUB, Network::Regtest).unwrap();
|
|
||||||
let synced_height = rpc.get_node_synced_height().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(synced_height, 0);
|
|
||||||
rpc.set_node_synced_height(1).unwrap();
|
|
||||||
|
|
||||||
let synced_height = rpc.get_node_synced_height().unwrap();
|
|
||||||
assert_eq!(synced_height, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rpc_broadcast() {
|
|
||||||
let bitcoind = create_bitcoind(vec![]);
|
|
||||||
let rpc = create_rpc(&bitcoind, DESCRIPTOR_PUB, Network::Regtest).unwrap();
|
|
||||||
let address = generate(&bitcoind, 101);
|
|
||||||
let utxo = bitcoind
|
|
||||||
.client
|
|
||||||
.list_unspent(None, None, None, None, None)
|
|
||||||
.unwrap();
|
|
||||||
let input = CreateRawTransactionInput {
|
|
||||||
txid: utxo[0].txid,
|
|
||||||
vout: utxo[0].vout,
|
|
||||||
sequence: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let out: HashMap<_, _> = vec![(
|
|
||||||
address.to_string(),
|
|
||||||
utxo[0].amount - Amount::from_sat(100_000),
|
|
||||||
)]
|
|
||||||
.into_iter()
|
|
||||||
.collect();
|
|
||||||
let tx = bitcoind
|
|
||||||
.client
|
|
||||||
.create_raw_transaction(&[input], &out, None, None)
|
|
||||||
.unwrap();
|
|
||||||
let signed_tx = bitcoind
|
|
||||||
.client
|
|
||||||
.sign_raw_transaction_with_wallet(tx.raw_hex(), None, None)
|
|
||||||
.unwrap();
|
|
||||||
let parsed_tx: Transaction = deserialize(&signed_tx.hex).unwrap();
|
|
||||||
rpc.broadcast(&parsed_tx).unwrap();
|
|
||||||
assert!(bitcoind
|
|
||||||
.client
|
|
||||||
.get_raw_mempool()
|
|
||||||
.unwrap()
|
|
||||||
.contains(&tx.txid()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_rpc_wallet_name() {
|
|
||||||
let secp = Secp256k1::new();
|
|
||||||
let name =
|
|
||||||
wallet_name_from_descriptor(DESCRIPTOR_PUB, None, Network::Regtest, &secp).unwrap();
|
|
||||||
assert_eq!("tmg7aqay", name);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate(bitcoind: &BitcoinD, blocks: u64) -> Address {
|
|
||||||
let address = bitcoind.client.get_new_address(None, None).unwrap();
|
|
||||||
bitcoind
|
|
||||||
.client
|
|
||||||
.generate_to_address(blocks, &address)
|
|
||||||
.unwrap();
|
|
||||||
address
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_to_address(bitcoind: &BitcoinD, address: &Address, amount: u64) -> Txid {
|
|
||||||
bitcoind
|
|
||||||
.client
|
|
||||||
.send_to_address(
|
|
||||||
&address,
|
|
||||||
Amount::from_sat(amount),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -383,9 +383,9 @@ impl BatchDatabase for Tree {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use lazy_static::lazy_static;
|
||||||
use std::sync::{Arc, Condvar, Mutex, Once};
|
use std::sync::{Arc, Condvar, Mutex, Once};
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
use lazy_static::lazy_static;
|
|
||||||
|
|
||||||
use sled::{Db, Tree};
|
use sled::{Db, Tree};
|
||||||
|
|
||||||
|
@ -234,6 +234,7 @@ pub extern crate reqwest;
|
|||||||
#[cfg(feature = "key-value-db")]
|
#[cfg(feature = "key-value-db")]
|
||||||
pub extern crate sled;
|
pub extern crate sled;
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub(crate) mod error;
|
pub(crate) mod error;
|
||||||
pub mod blockchain;
|
pub mod blockchain;
|
||||||
|
@ -41,6 +41,7 @@ impl TestClient {
|
|||||||
fn wait_for_tx(&mut self, txid: Txid, monitor_script: &Script) {
|
fn wait_for_tx(&mut self, txid: Txid, monitor_script: &Script) {
|
||||||
// wait for electrs to index the tx
|
// wait for electrs to index the tx
|
||||||
exponential_backoff_poll(|| {
|
exponential_backoff_poll(|| {
|
||||||
|
self.electrsd.trigger().unwrap();
|
||||||
trace!("wait_for_tx {}", txid);
|
trace!("wait_for_tx {}", txid);
|
||||||
|
|
||||||
self.electrsd
|
self.electrsd
|
||||||
@ -57,6 +58,7 @@ impl TestClient {
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
let header = exponential_backoff_poll(|| {
|
let header = exponential_backoff_poll(|| {
|
||||||
|
self.electrsd.trigger().unwrap();
|
||||||
self.electrsd.client.ping().unwrap();
|
self.electrsd.client.ping().unwrap();
|
||||||
self.electrsd.client.block_headers_pop().unwrap()
|
self.electrsd.client.block_headers_pop().unwrap()
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user