Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ use std::process::{Child, Command, Output};
use std::time::{Duration, Instant};

use corepc_node::{Conf, Node, P2P};
use kernel_node::server_capnp::server;
use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};

const READY_TIMEOUT: Duration = Duration::from_secs(30);
const STOP_TIMEOUT: Duration = Duration::from_secs(45);
const POLL_INTERVAL: Duration = Duration::from_millis(50);
const TIP_POLL_INTERVAL: Duration = Duration::from_millis(200);

const CLOSED_PEER: &str = "127.0.0.1:1";

Expand All @@ -20,10 +23,28 @@ pub fn start_bitcoind() -> Node {
Node::with_conf(exe, &conf).unwrap()
}

async fn connect(socket_path: &Path) -> server::Client {
let stream = tokio::net::UnixStream::connect(socket_path).await.unwrap();
let (reader, writer) = stream.into_split();
let buf_reader = futures::io::BufReader::new(reader.compat());
let buf_writer = futures::io::BufWriter::new(writer.compat_write());
let network = capnp_rpc::twoparty::VatNetwork::new(
buf_reader,
buf_writer,
capnp_rpc::rpc_twoparty_capnp::Side::Client,
Default::default(),
);
let mut rpc_system = capnp_rpc::RpcSystem::new(Box::new(network), None);
let client: server::Client = rpc_system.bootstrap(capnp_rpc::rpc_twoparty_capnp::Side::Server);
tokio::task::spawn_local(rpc_system);
client
}

pub struct TestNode {
process: Child,
datadir: PathBuf,
_tempdir: tempfile::TempDir,
rt: tokio::runtime::Runtime,
}

impl TestNode {
Expand All @@ -43,10 +64,15 @@ impl TestNode {
.arg(peer.to_string())
.spawn()
.unwrap();
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
let node = Self {
process,
datadir,
_tempdir: tempdir,
rt,
};
node.wait_until_ready();
node
Expand All @@ -61,6 +87,50 @@ impl TestNode {
.unwrap()
}

pub fn tip(&self) -> (u32, bitcoin::BlockHash) {
let socket = self.socket_path();
self.rt
.block_on(tokio::task::LocalSet::new().run_until(async move {
let client = connect(&socket).await;
let chain = client
.make_chain_request()
.send()
.promise
.await
.unwrap()
.get()
.unwrap()
.get_chain()
.unwrap();
let response = chain.get_tip_request().send().promise.await.unwrap();
let reply = response.get().unwrap();
let height = reply.get_height();
let hash = reply
.get_hash()
.unwrap()
.to_string()
.unwrap()
.parse::<bitcoin::BlockHash>()
.unwrap();
(height, hash)
}))
}

pub fn wait_for_tip(&self, height: u64, hash: bitcoin::BlockHash, timeout: Duration) {
let deadline = Instant::now() + timeout;
loop {
let (h, block_hash) = self.tip();
if u64::from(h) == height && block_hash == hash {
return;
}
assert!(
Instant::now() < deadline,
"node did not reach tip {height} within {timeout:?} (node at {h})"
);
std::thread::sleep(TIP_POLL_INTERVAL);
}
}

pub fn stop(mut self) {
let out = self.cli(&["stop"]);
assert!(
Expand Down
52 changes: 52 additions & 0 deletions tests/core_reorg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
mod common;

use std::time::Duration;

use bitcoin::BlockHash;
use common::{start_bitcoind, TestNode};

const SYNC_TIMEOUT: Duration = Duration::from_secs(60);
const REORG_TIMEOUT: Duration = Duration::from_secs(60);
const INITIAL_BLOCKS: usize = 10;
const FORK_HEIGHT: u64 = 8;
const NEW_BLOCKS: usize = 5;

#[test]
fn follows_bitcoin_core_reorg() {
let core = start_bitcoind();
let address = core.client.new_address().expect("new address");
core.client
.generate_to_address(INITIAL_BLOCKS, &address)
.expect("mine initial blocks");

let initial_height = core.client.get_block_count().expect("block count").0;
let initial_hash = core.client.best_block_hash().expect("best block hash");

let p2p = core.params.p2p_socket.expect("bitcoind p2p socket");
let node = TestNode::start_connected(p2p);
node.wait_for_tip(initial_height, initial_hash, SYNC_TIMEOUT);

let fork_block = core
.client
.get_block_hash(FORK_HEIGHT)
.expect("block hash at fork height")
.0
.parse::<BlockHash>()
.expect("parse fork block hash");
core.client
.invalidate_block(fork_block)
.expect("invalidate block");
let reorg_address = core.client.new_address().expect("new address");
core.client
.generate_to_address(NEW_BLOCKS, &reorg_address)
.expect("mine competing branch");

let reorg_height = core.client.get_block_count().expect("block count").0;
let reorg_hash = core.client.best_block_hash().expect("best block hash");
assert!(reorg_height > initial_height);
assert_ne!(reorg_hash, initial_hash);

node.wait_for_tip(reorg_height, reorg_hash, REORG_TIMEOUT);

node.stop();
}
28 changes: 28 additions & 0 deletions tests/core_sync.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
mod common;

use std::time::Duration;

use common::{start_bitcoind, TestNode};

const SYNC_TIMEOUT: Duration = Duration::from_secs(60);
const BLOCKS: usize = 10;

#[test]
fn follows_bitcoin_core_chain() {
let core = start_bitcoind();
let address = core.client.new_address().expect("new address");
core.client
.generate_to_address(BLOCKS, &address)
.expect("mine blocks");

let core_height = core.client.get_block_count().expect("block count").0;
let core_hash = core.client.best_block_hash().expect("best block hash");
assert_eq!(core_height, BLOCKS as u64);

let p2p = core.params.p2p_socket.expect("bitcoind p2p socket");
let node = TestNode::start_connected(p2p);

node.wait_for_tip(core_height, core_hash, SYNC_TIMEOUT);

node.stop();
}
Loading