Replace bitcoind wallet with bdk wallet
The bitcoind wallet required the user to run a bitcoind node. It was replaced with a bdk wallet which allows the user to connect to an electrum instance hosted remotely. An electrum and bitcoind testcontainer were created to the test the bdk wallet. The electrum container reads the blockdata from the bitcoind testcontainer through a shared volume. bitcoind-harness was removed as bitcoind initialisation code was moved into test_utils. The bdk wallet differs from the bitcoind wallet in that it needs to be manually synced with an electrum node. We synchronise the wallet once upon initialisation to prevent a potentially long running blocking task from interrupting protocol execution. The electrum HTTP API was used to get the latest block height and the transaction block height as this functionality was not present in the bdk wallet API or it required the bdk wallet to be re-synced to get an up to date value.pull/178/head
parent
4d8e801c1e
commit
a0ef1f96ec
@ -0,0 +1,139 @@
|
||||
use std::collections::HashMap;
|
||||
use testcontainers::{
|
||||
core::{Container, Docker, Port, WaitForMessage},
|
||||
Image,
|
||||
};
|
||||
|
||||
pub const RPC_USER: &str = "admin";
|
||||
pub const RPC_PASSWORD: &str = "123";
|
||||
pub const RPC_PORT: u16 = 18443;
|
||||
pub const PORT: u16 = 18886;
|
||||
pub const DATADIR: &str = "/home/bdk";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Bitcoind {
|
||||
tag: String,
|
||||
args: BitcoindArgs,
|
||||
entrypoint: Option<String>,
|
||||
volume: Option<String>,
|
||||
}
|
||||
|
||||
impl Image for Bitcoind {
|
||||
type Args = BitcoindArgs;
|
||||
type EnvVars = HashMap<String, String>;
|
||||
type Volumes = HashMap<String, String>;
|
||||
type EntryPoint = str;
|
||||
|
||||
fn descriptor(&self) -> String {
|
||||
format!("coblox/bitcoin-core:{}", self.tag)
|
||||
}
|
||||
|
||||
fn wait_until_ready<D: Docker>(&self, container: &Container<'_, D, Self>) {
|
||||
container
|
||||
.logs()
|
||||
.stdout
|
||||
.wait_for_message(&"init message: Done loading")
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn args(&self) -> <Self as Image>::Args {
|
||||
self.args.clone()
|
||||
}
|
||||
|
||||
fn volumes(&self) -> Self::Volumes {
|
||||
let mut volumes = HashMap::new();
|
||||
match self.volume.clone() {
|
||||
None => {}
|
||||
Some(volume) => {
|
||||
volumes.insert(volume, DATADIR.to_string());
|
||||
}
|
||||
}
|
||||
volumes
|
||||
}
|
||||
|
||||
fn env_vars(&self) -> Self::EnvVars {
|
||||
HashMap::new()
|
||||
}
|
||||
|
||||
fn ports(&self) -> Option<Vec<Port>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn with_args(self, args: <Self as Image>::Args) -> Self {
|
||||
Bitcoind { args, ..self }
|
||||
}
|
||||
|
||||
fn with_entrypoint(self, entrypoint: &Self::EntryPoint) -> Self {
|
||||
Self {
|
||||
entrypoint: Some(entrypoint.to_string()),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
fn entrypoint(&self) -> Option<String> {
|
||||
self.entrypoint.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Bitcoind {
|
||||
fn default() -> Self {
|
||||
Bitcoind {
|
||||
tag: "v0.19.1".into(),
|
||||
args: BitcoindArgs::default(),
|
||||
entrypoint: Some("/usr/bin/bitcoind".into()),
|
||||
volume: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Bitcoind {
|
||||
pub fn with_tag(self, tag_str: &str) -> Self {
|
||||
Bitcoind {
|
||||
tag: tag_str.to_string(),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_volume(mut self, volume: String) -> Self {
|
||||
self.volume = Some(volume);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BitcoindArgs;
|
||||
|
||||
impl Default for BitcoindArgs {
|
||||
fn default() -> Self {
|
||||
BitcoindArgs
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for BitcoindArgs {
|
||||
type Item = String;
|
||||
type IntoIter = ::std::vec::IntoIter<String>;
|
||||
|
||||
// todo: these "defaults" are only suitable for our tests and need to be looked
|
||||
// at
|
||||
fn into_iter(self) -> <Self as IntoIterator>::IntoIter {
|
||||
let args = vec![
|
||||
"-server".to_string(),
|
||||
"-regtest".to_string(),
|
||||
"-listen=1".to_string(),
|
||||
"-prune=0".to_string(),
|
||||
"-rpcallowip=0.0.0.0/0".to_string(),
|
||||
"-rpcbind=0.0.0.0".to_string(),
|
||||
format!("-rpcuser={}", RPC_USER),
|
||||
format!("-rpcpassword={}", RPC_PASSWORD),
|
||||
"-printtoconsole".to_string(),
|
||||
"-rest".to_string(),
|
||||
"-fallbackfee=0.0002".to_string(),
|
||||
format!("-datadir={}", DATADIR),
|
||||
format!("-rpcport={}", RPC_PORT),
|
||||
format!("-port={}", PORT),
|
||||
"-rest".to_string(),
|
||||
];
|
||||
|
||||
args.into_iter()
|
||||
}
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
use crate::testutils::bitcoind;
|
||||
use bitcoin::Network;
|
||||
use std::collections::HashMap;
|
||||
use testcontainers::{
|
||||
core::{Container, Docker, Port, WaitForMessage},
|
||||
Image,
|
||||
};
|
||||
|
||||
pub const HTTP_PORT: u16 = 60401;
|
||||
pub const RPC_PORT: u16 = 3002;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Electrs {
|
||||
tag: String,
|
||||
args: ElectrsArgs,
|
||||
entrypoint: Option<String>,
|
||||
wait_for_message: String,
|
||||
volume: String,
|
||||
bitcoind_container_name: String,
|
||||
}
|
||||
|
||||
impl Image for Electrs {
|
||||
type Args = ElectrsArgs;
|
||||
type EnvVars = HashMap<String, String>;
|
||||
type Volumes = HashMap<String, String>;
|
||||
type EntryPoint = str;
|
||||
|
||||
fn descriptor(&self) -> String {
|
||||
format!("vulpemventures/electrs:{}", self.tag)
|
||||
}
|
||||
|
||||
fn wait_until_ready<D: Docker>(&self, container: &Container<'_, D, Self>) {
|
||||
container
|
||||
.logs()
|
||||
.stderr
|
||||
.wait_for_message(&self.wait_for_message)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn args(&self) -> <Self as Image>::Args {
|
||||
self.args.clone()
|
||||
}
|
||||
|
||||
fn volumes(&self) -> Self::Volumes {
|
||||
let mut volumes = HashMap::new();
|
||||
volumes.insert(self.volume.clone(), bitcoind::DATADIR.to_string());
|
||||
volumes
|
||||
}
|
||||
|
||||
fn env_vars(&self) -> Self::EnvVars {
|
||||
HashMap::new()
|
||||
}
|
||||
|
||||
fn ports(&self) -> Option<Vec<Port>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn with_args(self, args: <Self as Image>::Args) -> Self {
|
||||
Electrs { args, ..self }
|
||||
}
|
||||
|
||||
fn with_entrypoint(self, entrypoint: &Self::EntryPoint) -> Self {
|
||||
Self {
|
||||
entrypoint: Some(entrypoint.to_string()),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
fn entrypoint(&self) -> Option<String> {
|
||||
self.entrypoint.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Electrs {
|
||||
fn default() -> Self {
|
||||
Electrs {
|
||||
tag: "v0.16.0.3".into(),
|
||||
args: ElectrsArgs::default(),
|
||||
entrypoint: Some("/build/electrs".into()),
|
||||
wait_for_message: "Running accept thread".to_string(),
|
||||
volume: uuid::Uuid::new_v4().to_string(),
|
||||
bitcoind_container_name: uuid::Uuid::new_v4().to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Electrs {
|
||||
pub fn with_tag(self, tag_str: &str) -> Self {
|
||||
Electrs {
|
||||
tag: tag_str.to_string(),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_volume(mut self, volume: String) -> Self {
|
||||
self.volume = volume;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_daemon_rpc_addr(mut self, name: String) -> Self {
|
||||
self.args.daemon_rpc_addr = name;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ElectrsArgs {
|
||||
pub network: Network,
|
||||
pub daemon_dir: String,
|
||||
pub daemon_rpc_addr: String,
|
||||
pub cookie: String,
|
||||
pub http_addr: String,
|
||||
pub electrum_rpc_addr: String,
|
||||
pub cors: String,
|
||||
}
|
||||
|
||||
impl Default for ElectrsArgs {
|
||||
fn default() -> Self {
|
||||
// todo: these "defaults" are only suitable for our tests and need to be looked
|
||||
// at
|
||||
ElectrsArgs {
|
||||
network: Network::Regtest,
|
||||
daemon_dir: bitcoind::DATADIR.to_string(),
|
||||
daemon_rpc_addr: format!("0.0.0.0:{}", bitcoind::RPC_PORT),
|
||||
cookie: format!("{}:{}", bitcoind::RPC_USER, bitcoind::RPC_PASSWORD),
|
||||
http_addr: format!("0.0.0.0:{}", HTTP_PORT),
|
||||
electrum_rpc_addr: format!("0.0.0.0:{}", RPC_PORT),
|
||||
cors: "*".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for ElectrsArgs {
|
||||
type Item = String;
|
||||
type IntoIter = ::std::vec::IntoIter<String>;
|
||||
|
||||
fn into_iter(self) -> <Self as IntoIterator>::IntoIter {
|
||||
let mut args = Vec::new();
|
||||
|
||||
match self.network {
|
||||
Network::Testnet => args.push("--network=testnet".to_string()),
|
||||
Network::Regtest => args.push("--network=regtest".to_string()),
|
||||
Network::Bitcoin => {}
|
||||
}
|
||||
|
||||
args.push("-vvvvv".to_string());
|
||||
args.push(format!("--daemon-dir=={}", self.daemon_dir.as_str()));
|
||||
args.push(format!("--daemon-rpc-addr={}", self.daemon_rpc_addr));
|
||||
args.push(format!("--cookie={}", self.cookie));
|
||||
args.push(format!("--http-addr={}", self.http_addr));
|
||||
args.push(format!("--electrum-rpc-addr={}", self.electrum_rpc_addr));
|
||||
args.push(format!("--cors={}", self.cors));
|
||||
|
||||
args.into_iter()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue