Skip to content

Commit

Permalink
Add ord env to spin up a test bitcoin daemon and ord server (#3146)
Browse files Browse the repository at this point in the history
  • Loading branch information
casey authored Feb 20, 2024
1 parent cf02bb8 commit b019184
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 9 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
/.vagrant
/.vscode
/docs/build
/env
/fuzz/artifacts
/fuzz/corpus
/fuzz/coverage
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ brotli = "3.4.0"
chrono = { version = "0.4.19", features = ["serde"] }
ciborium = "0.2.1"
clap = { version = "4.4.2", features = ["derive"] }
colored = "2.0.4"
ctrlc = { version = "3.2.1", features = ["termination"] }
dirs = "5.0.0"
env_logger = "0.10.0"
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ use {
mem,
net::ToSocketAddrs,
path::{Path, PathBuf},
process,
process::{self, Command, Stdio},
str::FromStr,
sync::{
atomic::{self, AtomicBool},
Expand Down
34 changes: 26 additions & 8 deletions src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,14 +233,32 @@ impl Options {
}

let client = Client::new(&rpc_url, auth)
.with_context(|| format!("failed to connect to Bitcoin Core RPC at {rpc_url}"))?;

let rpc_chain = match client.get_blockchain_info()?.chain.as_str() {
"main" => Chain::Mainnet,
"test" => Chain::Testnet,
"regtest" => Chain::Regtest,
"signet" => Chain::Signet,
other => bail!("Bitcoin RPC server on unknown chain: {other}"),
.with_context(|| format!("failed to connect to Bitcoin Core RPC at `{rpc_url}`"))?;

let mut checks = 0;
let rpc_chain = loop {
match client.get_blockchain_info() {
Ok(blockchain_info) => {
break match blockchain_info.chain.as_str() {
"main" => Chain::Mainnet,
"test" => Chain::Testnet,
"regtest" => Chain::Regtest,
"signet" => Chain::Signet,
other => bail!("Bitcoin RPC server on unknown chain: {other}"),
}
}
Err(bitcoincore_rpc::Error::JsonRpc(bitcoincore_rpc::jsonrpc::Error::Rpc(err)))
if err.code == -28 => {}
Err(err) => bail!("Failed to connect to Bitcoin Core RPC at `{rpc_url}`: {err}"),
}

ensure! {
checks < 100,
"Failed to connect to Bitcoin Core RPC at `{rpc_url}`",
}

checks += 1;
thread::sleep(Duration::from_millis(100));
};

let ord_chain = self.chain();
Expand Down
4 changes: 4 additions & 0 deletions src/subcommand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use super::*;

pub mod balances;
pub mod decode;
pub mod env;
pub mod epochs;
pub mod find;
pub mod index;
Expand All @@ -21,6 +22,8 @@ pub(crate) enum Subcommand {
Balances,
#[command(about = "Decode a transaction")]
Decode(decode::Decode),
#[command(about = "Start a regtest ord and bitcoind instance")]
Env(env::Env),
#[command(about = "List the first satoshis of each reward epoch")]
Epochs,
#[command(about = "Find a satoshi's current location")]
Expand Down Expand Up @@ -52,6 +55,7 @@ impl Subcommand {
match self {
Self::Balances => balances::run(options),
Self::Decode(decode) => decode.run(options),
Self::Env(env) => env.run(),
Self::Epochs => epochs::run(),
Self::Find(find) => find.run(options),
Self::Index(index) => index.run(options),
Expand Down
131 changes: 131 additions & 0 deletions src/subcommand/env.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
use {super::*, colored::Colorize, std::net::TcpListener};

struct KillOnDrop(process::Child);

impl Drop for KillOnDrop {
fn drop(&mut self) {
assert!(Command::new("kill")
.arg(self.0.id().to_string())
.status()
.unwrap()
.success());
self.0.wait().unwrap();
}
}

#[derive(Debug, Parser)]
pub(crate) struct Env {
#[arg(default_value = "env", help = "Create env in <DIRECTORY>.")]
directory: PathBuf,
}

impl Env {
pub(crate) fn run(self) -> SubcommandResult {
let (bitcoind_port, ord_port) = (
TcpListener::bind("127.0.0.1:0")
.unwrap()
.local_addr()
.unwrap()
.port(),
TcpListener::bind("127.0.0.1:0")
.unwrap()
.local_addr()
.unwrap()
.port(),
);

let env = std::env::current_dir()?.join(&self.directory);

fs::create_dir_all(&env)?;

let env_string = env
.to_str()
.with_context(|| format!("directory `{}` is not valid unicode", env.display()))?;

let config = env.join("bitcoin.conf").to_str().unwrap().to_string();

fs::write(
env.join("bitcoin.conf"),
format!(
"regtest=1
datadir={env_string}
listen=0
txindex=1
[regtest]
rpcport={bitcoind_port}
",
),
)?;

let _bitcoind = KillOnDrop(
Command::new("bitcoind")
.arg(format!("-conf={config}"))
.stdout(Stdio::null())
.spawn()?,
);

loop {
if env.join("regtest/.cookie").try_exists()? {
break;
}
}

let ord = std::env::current_exe()?;

let rpc_url = format!("https://localhost:{bitcoind_port}");

let _ord = KillOnDrop(
Command::new(&ord)
.arg("--regtest")
.arg("--bitcoin-data-dir")
.arg(&env)
.arg("--data-dir")
.arg(&env)
.arg("--rpc-url")
.arg(&rpc_url)
.arg("server")
.arg("--http-port")
.arg(ord_port.to_string())
.spawn()?,
);

thread::sleep(Duration::from_millis(250));

if !env.join("regtest/wallets/ord").try_exists()? {
let status = Command::new(&ord)
.arg("--regtest")
.arg("--bitcoin-data-dir")
.arg(&env)
.arg("--data-dir")
.arg(&env)
.arg("--rpc-url")
.arg(&rpc_url)
.arg("wallet")
.arg("create")
.status()?;

ensure!(status.success(), "failed to create wallet: {status}");
}

let directory = self.directory.to_str().unwrap().to_string();

eprintln!(
"{}
bitcoin-cli -datadir='{directory}' getblockchaininfo
{}
{} --regtest --bitcoin-data-dir '{directory}' --data-dir '{directory}' --rpc-url '{}' wallet --server-url https://127.0.0.1:{ord_port} balance",
"Example `bitcoin-cli` command:".blue().bold(),
"Example `ord` command:".blue().bold(),
ord.display(),
rpc_url,
);

loop {
if SHUTTING_DOWN.load(atomic::Ordering::Relaxed) {
break Ok(None);
}

thread::sleep(Duration::from_millis(100));
}
}
}

0 comments on commit b019184

Please sign in to comment.