run bitcoind locally when cargo test is executed

This commit is contained in:
conduition
2024-03-21 20:10:22 +00:00
parent 358d1e96cc
commit 43ed5c3806
3 changed files with 218 additions and 194 deletions

238
Cargo.lock generated
View File

@@ -2,12 +2,6 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "base16ct"
version = "0.2.0"
@@ -85,12 +79,6 @@ dependencies = [
"serde_json",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "block-buffer"
version = "0.10.4"
@@ -131,19 +119,6 @@ dependencies = [
"typenum",
]
[[package]]
name = "dashmap"
version = "5.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
dependencies = [
"cfg-if",
"hashbrown",
"lock_api",
"once_cell",
"parking_lot_core",
]
[[package]]
name = "digest"
version = "0.10.7"
@@ -164,15 +139,16 @@ dependencies = [
"dotenv",
"hex",
"musig2",
"rand",
"once_cell",
"rand 0.8.5",
"secp",
"secp256k1",
"serde",
"serde_cbor",
"serde_json",
"serdect",
"serial_test",
"sha2",
"tempdir",
]
[[package]]
@@ -181,6 +157,12 @@ version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "generic-array"
version = "0.14.7"
@@ -208,12 +190,6 @@ version = "1.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403"
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "hex"
version = "0.4.3"
@@ -258,28 +234,12 @@ dependencies = [
"serde_json",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "lock_api"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.21"
@@ -295,7 +255,7 @@ dependencies = [
"base16ct",
"hmac",
"once_cell",
"rand",
"rand 0.8.5",
"secp",
"secp256k1",
"serde",
@@ -310,29 +270,6 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
@@ -357,6 +294,19 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
dependencies = [
"fuchsia-cprng",
"libc",
"rand_core 0.3.1",
"rdrand",
"winapi",
]
[[package]]
name = "rand"
version = "0.8.5"
@@ -365,7 +315,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_core 0.6.4",
]
[[package]]
@@ -375,9 +325,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
"rand_core 0.6.4",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
"rand_core 0.4.2",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rand_core"
version = "0.6.4"
@@ -388,12 +353,21 @@ dependencies = [
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
dependencies = [
"bitflags",
"rand_core 0.3.1",
]
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
]
[[package]]
@@ -402,12 +376,6 @@ version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "secp"
version = "0.2.3"
@@ -416,7 +384,7 @@ checksum = "1507279bb0404bb566f85523e48fcf37a158daa5380577ee0d93f3ef4df39ccc"
dependencies = [
"base16ct",
"once_cell",
"rand",
"rand 0.8.5",
"secp256k1",
"serde",
"serdect",
@@ -430,7 +398,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10"
dependencies = [
"bitcoin_hashes",
"rand",
"rand 0.8.5",
"secp256k1-sys",
"serde",
]
@@ -495,29 +463,6 @@ dependencies = [
"serde",
]
[[package]]
name = "serial_test"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "953ad9342b3aaca7cb43c45c097dd008d4907070394bd0751a0aa8817e5a018d"
dependencies = [
"dashmap",
"lazy_static",
"parking_lot",
"serial_test_derive",
]
[[package]]
name = "serial_test_derive"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b93fb4adc70021ac1b47f7d45e8cc4169baaa7ea58483bc5b721d19a26202212"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "sha2"
version = "0.10.8"
@@ -529,12 +474,6 @@ dependencies = [
"digest",
]
[[package]]
name = "smallvec"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
[[package]]
name = "subtle"
version = "2.5.0"
@@ -552,6 +491,16 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "tempdir"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
dependencies = [
"rand 0.4.6",
"remove_dir_all",
]
[[package]]
name = "typenum"
version = "1.17.0"
@@ -577,58 +526,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "windows-targets"
version = "0.48.5"
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@@ -24,10 +24,11 @@ sha2 = { version = "0.10.8", default-features = false }
[dev-dependencies]
bitcoincore-rpc = "0.18.0"
dotenv = "0.15.0"
once_cell = "1.19.0"
serde = { version = "1.0.197", default-features = false, features = ["derive"] }
serde_cbor = { version = "0.11.2", default-features = false, features = ["std"] }
serde_json = { version = "1.0.114", default-features = false, features = [] }
serial_test = { version = "3.0.0", default-features = false }
tempdir = "0.3.7"
[package.metadata.docs.rs]
all-features = true

View File

@@ -2,9 +2,6 @@
use crate::*;
use bitcoincore_rpc::{jsonrpc::serde_json, Auth, Client as BitcoinClient, RpcApi};
use serial_test::serial;
use bitcoin::{
blockdata::transaction::{predict_weight, InputWeightPrediction},
hashes::Hash,
@@ -17,7 +14,16 @@ use musig2::{CompactSignature, LiftedSignature, PartialSignature, PubNonce};
use rand::{CryptoRng, Rng, RngCore, SeedableRng};
use secp::{MaybeScalar, Point, Scalar};
use std::collections::BTreeMap;
use bitcoincore_rpc::{jsonrpc::serde_json, Auth, Client as BitcoinClient, RpcApi};
use once_cell::sync::Lazy;
use tempdir::TempDir;
use std::{
collections::BTreeMap,
process,
sync::{Mutex, MutexGuard},
thread, time,
};
/// Generate a P2TR address which pays to the given pubkey (no tweak added).
fn p2tr_address(pubkey: Point) -> Address {
@@ -55,26 +61,101 @@ fn simple_sweep_tx(
}
}
/// Build a bitcoind RPC client for regtest. Expects the following environment variables
/// to be defined:
const DEFAULT_REGTEST_RPC_USERNAME: &str = "regtest";
const DEFAULT_REGTEST_RPC_PASSWORD: &str = "regtest";
const DEFAULT_REGTEST_RPC_URL: &str = "http://127.0.0.1:18443";
/// This represents a handle to temporary resources which should be
/// cleaned up when the test ends.
#[derive(Debug)]
struct BitcoindSubprocessHandle {
#[allow(dead_code)]
tempdir: TempDir,
#[allow(dead_code)]
child: process::Child,
}
fn run_bitcoind() -> Option<(BitcoindSubprocessHandle, BitcoinClient)> {
let dir = TempDir::new("dlctix").expect("error making tempdir");
let rpc_port: u16 = rand::thread_rng().gen_range(20000..u16::MAX);
let p2p_port: u16 = rpc_port + 1;
let child: process::Child = process::Command::new("bitcoind")
.arg("-regtest")
.arg("-server")
.arg(format!("-rpcport={}", rpc_port))
.arg(format!("-port={}", p2p_port))
.arg(format!("-rpcuser={}", DEFAULT_REGTEST_RPC_USERNAME))
.arg(format!("-rpcpassword={}", DEFAULT_REGTEST_RPC_PASSWORD))
.arg(format!("-datadir={}", dir.path().display()))
.stdout(process::Stdio::null())
.spawn()
.ok()?;
let subproc_handle = BitcoindSubprocessHandle {
tempdir: dir,
child,
};
let auth = Auth::UserPass(
DEFAULT_REGTEST_RPC_USERNAME.to_string(),
DEFAULT_REGTEST_RPC_PASSWORD.to_string(),
);
let bitcoind_rpc_url = format!("http://127.0.0.1:{}", rpc_port);
let rpc_client =
BitcoinClient::new(&bitcoind_rpc_url, auth).expect("failed to create bitcoind RPC client");
Some((subproc_handle, rpc_client))
}
static REMOTE_NODE_SINGLETON: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
/// Build a bitcoind RPC client for regtest. If `bitcoind` is installed and
/// available in the `PATH`, then this executes bitcoind and runs it in regtest
/// mode, pointing to a temporary data directory. In this case we return a handle
/// pointing to the temporary directory being used, as well as the child process
/// handle.
///
/// - `BITCOIND_RPC_URL`
/// Otherwise, the following environment variables should be defined:
///
/// - `BITCOIND_RPC_URL` (if missing, falls back to DEFAULT_REGTEST_RPC_URL)
/// - `BITCOIND_RPC_AUTH_USERNAME`
/// - `BITCOIND_RPC_AUTH_PASSWORD`
fn new_rpc_client() -> BitcoinClient {
fn new_rpc_client() -> (Option<BitcoindSubprocessHandle>, BitcoinClient) {
dotenv::dotenv().unwrap();
let bitcoind_rpc_url =
std::env::var("BITCOIND_RPC_URL").unwrap_or_else(|_| "http://127.0.0.1:18443".to_string());
match run_bitcoind() {
Some((subproc_handle, rpc_client)) => {
// Wait for bitcoind to start.
let start = time::Instant::now();
while start.elapsed() < time::Duration::from_secs(3) {
if rpc_client.get_network_info().is_ok() {
return (Some(subproc_handle), rpc_client);
}
thread::sleep(std::time::Duration::from_millis(50));
}
panic!("cannot reach local bitcoind instance");
}
let bitcoind_auth_username =
std::env::var("BITCOIND_RPC_AUTH_USERNAME").expect("missing BITCOIND_RPC_AUTH_USERNAME");
None => {
let bitcoind_auth_username = std::env::var("BITCOIND_RPC_AUTH_USERNAME")
.expect("bitcoind not installed; missing BITCOIND_RPC_AUTH_USERNAME");
let bitcoind_auth_password =
std::env::var("BITCOIND_RPC_AUTH_PASSWORD").expect("missing BITCOIND_RPC_AUTH_PASSWORD");
let bitcoind_auth_password = std::env::var("BITCOIND_RPC_AUTH_PASSWORD")
.expect("bitcoind not installed; missing BITCOIND_RPC_AUTH_PASSWORD");
let auth = Auth::UserPass(bitcoind_auth_username, bitcoind_auth_password);
BitcoinClient::new(&bitcoind_rpc_url, auth).expect("failed to create bitcoind RPC client")
let auth = Auth::UserPass(bitcoind_auth_username, bitcoind_auth_password);
let bitcoind_rpc_url = std::env::var("BITCOIND_RPC_URL")
.unwrap_or_else(|_| DEFAULT_REGTEST_RPC_URL.to_string());
let rpc_client = BitcoinClient::new(&bitcoind_rpc_url, auth)
.expect("failed to create bitcoind RPC client");
(None, rpc_client)
}
}
}
const FUNDING_VALUE: Amount = Amount::from_sat(200_000);
@@ -334,6 +415,11 @@ struct SimulationManager {
contract: SignedContract,
rpc: BitcoinClient,
bitcoind_handle: Option<BitcoindSubprocessHandle>,
// Used for synchronization only
#[allow(dead_code)]
bitcoind_lock: Option<MutexGuard<'static, ()>>,
}
impl SimulationManager {
@@ -364,7 +450,28 @@ impl SimulationManager {
dave.player.clone(),
];
let rpc = new_rpc_client();
let (bitcoind_handle, rpc) = new_rpc_client();
// If we're using a remote bitcoind instance, we don't want tests
// to interfere with each other, so grab a lock on a global mutex
// which will be released when the SimulationManager is dropped.
let bitcoind_lock = if bitcoind_handle.is_none() {
// Tests panic sometimes, we should ignore poisoned mutex state.
let lock = REMOTE_NODE_SINGLETON
.lock()
.unwrap_or_else(|e| e.into_inner());
Some(lock)
} else {
None
};
// Fund the market maker. This may mine some blocks.
let (mm_utxo_outpoint, mm_utxo_prevout) = take_usable_utxo(
&rpc,
&market_maker_address,
FUNDING_VALUE + Amount::from_sat(50_000),
);
let initial_block_height = rpc.get_block_count().unwrap();
let outcome_payouts = BTreeMap::<Outcome, PayoutWeights>::from([
@@ -402,13 +509,6 @@ impl SimulationManager {
relative_locktime_block_delta: 25,
};
// Fund the market maker
let (mm_utxo_outpoint, mm_utxo_prevout) = take_usable_utxo(
&rpc,
&market_maker_address,
FUNDING_VALUE + Amount::from_sat(50_000),
);
// Prepare a funding transaction
let funding_tx = signed_funding_tx(
market_maker_seckey,
@@ -457,6 +557,8 @@ impl SimulationManager {
contract: signed_contract,
rpc,
bitcoind_handle,
bitcoind_lock,
}
}
@@ -487,8 +589,21 @@ impl SimulationManager {
}
}
/// When the test ends, stop bitcoind and remove its temporary datadir.
impl std::ops::Drop for SimulationManager {
fn drop(&mut self) {
if let Some(mut handle) = self.bitcoind_handle.take() {
self.rpc.stop().expect("failed to stop bitcoind subprocess");
handle.child.wait().unwrap();
handle
.tempdir
.close()
.expect("failed to clean up temporary directory");
}
}
}
#[test]
#[serial]
fn with_on_chain_resolutions() {
let manager = SimulationManager::new();
@@ -722,7 +837,6 @@ fn with_on_chain_resolutions() {
}
#[test]
#[serial]
fn individual_sellback() {
let manager = SimulationManager::new();
@@ -794,7 +908,6 @@ fn individual_sellback() {
}
#[test]
#[serial]
fn all_winners_cooperate() {
let manager = SimulationManager::new();
@@ -852,7 +965,6 @@ fn all_winners_cooperate() {
}
#[test]
#[serial]
fn market_maker_reclaims_outcome_tx() {
let manager = SimulationManager::new();
@@ -952,7 +1064,6 @@ fn market_maker_reclaims_outcome_tx() {
}
#[test]
#[serial]
fn contract_expiry_on_chain_resolution() {
let manager = SimulationManager::new();
@@ -1076,7 +1187,6 @@ fn contract_expiry_on_chain_resolution() {
}
#[test]
#[serial]
fn contract_expiry_all_winners_cooperate() {
let manager = SimulationManager::new();
@@ -1139,7 +1249,6 @@ fn contract_expiry_all_winners_cooperate() {
}
#[test]
#[serial]
fn all_players_cooperate() {
let manager = SimulationManager::new();