Skip to content
Open
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
2 changes: 1 addition & 1 deletion smite-ir/src/operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ pub enum Operation {
RecvChannelReady,
/// Mines the given number of blocks on the Bitcoin network.
MineBlocks(u8),
/// Sign wallet inputs of the transaction and broadcast it via `bitcoin-cli`.
/// Sign wallet inputs of the transaction and broadcast it via `bitcoind`.
/// Input: `FundingTransaction`.
BroadcastTransaction,
// -- Query: read state from outside the program --
Expand Down
44 changes: 22 additions & 22 deletions smite-scenarios/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use bitcoin::secp256k1::ecdsa::Signature;
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
use bitcoin::{OutPoint, ScriptBuf, Txid};
use smite::bitcoin::{BitcoinCli, TxBlockPosition, Utxo};
use smite::bitcoin::{BitcoindClient, TxBlockPosition, Utxo};
use smite::bolt::{
AcceptChannel, AnnouncementSignatures, ChannelAnnouncement, ChannelId, ChannelReady,
ChannelReadyTlvs, ChannelUpdate, Features, FromMessage, FundingCreated, FundingSigned, Message,
Expand Down Expand Up @@ -62,7 +62,7 @@ pub const RECV_IDLE_TIMEOUT: Duration = Duration::from_secs(1);
/// reconfiguring or patching CLN to poll more frequently.
pub const RECV_CHANNEL_READY_TIMEOUT: Duration = Duration::from_secs(5);

/// Abstraction over bitcoin-cli operations, allowing mock implementations in tests.
/// Abstraction over bitcoind operations, allowing mock implementations in tests.
pub trait BitcoinRpc {
/// Mines the given number of blocks, including any transactions in the
/// `private_mempool` in the first block.
Expand Down Expand Up @@ -98,33 +98,33 @@ pub trait BitcoinRpc {
fn get_transaction_block_position(&mut self, txid: Txid) -> Option<TxBlockPosition>;
}

impl BitcoinRpc for BitcoinCli {
impl BitcoinRpc for BitcoindClient {
fn mine_blocks(&mut self, num_blocks: u8, private_mempool: &[String]) {
BitcoinCli::mine_blocks(self, num_blocks, private_mempool);
BitcoindClient::mine_blocks(self, num_blocks, private_mempool);
}

fn get_utxos(&mut self) -> Vec<Utxo> {
BitcoinCli::get_utxos(self)
BitcoindClient::get_utxos(self)
}

fn get_new_address_script_pubkey(&mut self) -> ScriptBuf {
BitcoinCli::get_new_address_script_pubkey(self)
BitcoindClient::get_new_address_script_pubkey(self)
}

fn sign_and_broadcast_tx(&mut self, tx: &bitcoin::Transaction) -> Option<String> {
BitcoinCli::sign_and_broadcast_tx(self, tx)
BitcoindClient::sign_and_broadcast_tx(self, tx)
}

fn lock_utxos(&mut self, outpoints: &[OutPoint]) {
BitcoinCli::lock_utxos(self, outpoints);
BitcoindClient::lock_utxos(self, outpoints);
}

fn get_transaction_confirmations(&mut self, txid: Txid) -> u32 {
BitcoinCli::get_transaction_confirmations(self, txid)
BitcoindClient::get_transaction_confirmations(self, txid)
}

fn get_transaction_block_position(&mut self, txid: Txid) -> Option<TxBlockPosition> {
BitcoinCli::get_transaction_block_position(self, txid)
BitcoindClient::get_transaction_block_position(self, txid)
}
}

Expand Down Expand Up @@ -239,7 +239,7 @@ pub struct Executor<C, B, R> {
/// Connection used to send and receive Lightning messages.
conn: C,
/// Interface to bitcoind for wallet and chain operations.
bitcoin_cli: B,
bitcoind_client: B,
/// Interface for interacting with the target node through RPC.
rpc: R,
/// Immutable state captured during snapshot setup.
Expand Down Expand Up @@ -268,13 +268,13 @@ pub struct Executor<C, B, R> {
}

impl<C: Connection, B: BitcoinRpc, R: TargetRpc> Executor<C, B, R> {
/// Creates an executor with the given connection, bitcoin-cli handle,
/// Creates an executor with the given connection, bitcoind client,
/// program context, and target RPC handle. Channel state and negotiations
/// start empty.
pub fn new(conn: C, bitcoin_cli: B, rpc: R, context: ProgramContext) -> Self {
pub fn new(conn: C, bitcoind_client: B, rpc: R, context: ProgramContext) -> Self {
Self {
conn,
bitcoin_cli,
bitcoind_client,
rpc,
context,
channel_states: HashMap::new(),
Expand Down Expand Up @@ -311,7 +311,7 @@ impl<C: Connection, B: BitcoinRpc, R: TargetRpc> Executor<C, B, R> {
/// - input variable index out of bounds
/// - input variable refers to a void instruction
/// - input variable has the wrong type
/// - `MineBlocks(0)` (panics inside `BitcoinCli::mine_blocks`)
/// - `MineBlocks(0)` (panics inside `BitcoindClient::mine_blocks`)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think this was already wrong. I think mine_blocks doesn't panic anymore on 0 since a22c79b.

/// - `LoadShutdownScript(AnySegwit { .. })` with an out-of-range version or
/// program length (panics inside the encoder)
/// - `LoadBytes` / `LoadFeatures` payload exceeding `MAX_MESSAGE_SIZE` (panics
Expand Down Expand Up @@ -381,7 +381,7 @@ impl<C: Connection, B: BitcoinRpc, R: TargetRpc> Executor<C, B, R> {
let ft = create_funding_transaction(
&variables,
&instr.inputs,
&mut self.bitcoin_cli,
&mut self.bitcoind_client,
)?;
Some(Variable::FundingTransaction(ft))
}
Expand Down Expand Up @@ -530,7 +530,7 @@ impl<C: Connection, B: BitcoinRpc, R: TargetRpc> Executor<C, B, R> {
}

Operation::RecvChannelReady => {
if is_channel_ready_expected(&self.channel_states, &mut self.bitcoin_cli) {
if is_channel_ready_expected(&self.channel_states, &mut self.bitcoind_client) {
log::debug!("[{:?}] RecvChannelReady: waiting", start.elapsed());
recv_channel_ready(&mut self.conn, &mut self.channel_states)?;
log::debug!("[{:?}] RecvChannelReady: received", start.elapsed());
Expand All @@ -545,7 +545,7 @@ impl<C: Connection, B: BitcoinRpc, R: TargetRpc> Executor<C, B, R> {
.into_iter()
.map(|(_, hex)| hex)
.collect();
self.bitcoin_cli.mine_blocks(*v, &private_mempool);
self.bitcoind_client.mine_blocks(*v, &private_mempool);
self.rpc.chain_sync();
self.mined_txids.extend(self.unmined_txids.drain());
log::debug!("[{:?}] MineBlocks: mined {} block(s)", start.elapsed(), v);
Expand All @@ -564,7 +564,7 @@ impl<C: Connection, B: BitcoinRpc, R: TargetRpc> Executor<C, B, R> {
// mempool so they can be mined later. Dedup on txid so the
// same transaction broadcast again before then is queued
// once, regardless of any change to its signed hex.
if let Some(hex) = self.bitcoin_cli.sign_and_broadcast_tx(&ft.tx)
if let Some(hex) = self.bitcoind_client.sign_and_broadcast_tx(&ft.tx)
&& !self.private_mempool.iter().any(|(t, _)| *t == txid)
{
self.private_mempool.push((txid, hex));
Expand All @@ -582,7 +582,7 @@ impl<C: Connection, B: BitcoinRpc, R: TargetRpc> Executor<C, B, R> {
// message will simply fail on-chain validation, which is
// the intended fuzzing behaviour for a valid but
// unconfirmed program.
let scid = match self.bitcoin_cli.get_transaction_block_position(txid) {
let scid = match self.bitcoind_client.get_transaction_block_position(txid) {
Some(pos) => {
let funding_output_index =
u16::try_from(ft.vout).expect("funding output index fits in u16");
Expand Down Expand Up @@ -1234,15 +1234,15 @@ fn recv_channel_ready(
/// `minimum_depth` confirmations (as specified in the received `accept_channel`).
fn is_channel_ready_expected(
channel_states: &HashMap<ChannelId, ChannelState>,
bitcoin_cli: &mut impl BitcoinRpc,
bitcoind_client: &mut impl BitcoinRpc,
) -> bool {
channel_states.values().any(|state| {
state.commitment.commitment_number == 0
&& state.next_counterparty_per_commitment_point().is_none()
&& state.is_funding_outpoint_valid
&& !state.was_funding_mined_prematurely
&& !state.sent_invalid_signature
&& bitcoin_cli.get_transaction_confirmations(state.config.funding_outpoint.txid)
&& bitcoind_client.get_transaction_confirmations(state.config.funding_outpoint.txid)
>= state.config.minimum_depth
})
}
Expand Down
18 changes: 9 additions & 9 deletions smite-scenarios/src/executor/tests/harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ impl Connection for MockConnection {
}
}

// Mocking BitcoinCli via MockBitcoinCli
// Mocking BitcoindClient via MockBitcoindClient

#[derive(Default)]
pub struct MockBitcoinCli {
pub struct MockBitcoindClient {
pub mine_blocks_calls: Vec<u8>,
pub mined_private_mempool: Vec<String>,
pub broadcast_calls: Vec<Transaction>,
Expand All @@ -56,7 +56,7 @@ pub struct MockBitcoinCli {
confirmations: u32,
}

impl BitcoinRpc for MockBitcoinCli {
impl BitcoinRpc for MockBitcoindClient {
fn mine_blocks(&mut self, num_blocks: u8, private_mempool: &[String]) {
self.mine_blocks_calls.push(num_blocks);
self.mined_private_mempool = private_mempool.to_vec();
Expand Down Expand Up @@ -124,21 +124,21 @@ impl TargetRpc for MockTargetRpc {

/// An [`Executor`] wired to a mock peer and a mock bitcoind.
pub struct Fixture {
executor: Executor<MockConnection, MockBitcoinCli, MockTargetRpc>,
executor: Executor<MockConnection, MockBitcoindClient, MockTargetRpc>,
}

impl Fixture {
/// A fixture with a silent peer and a wallet holding [`sample_utxo`].
pub fn new() -> Self {
let bitcoin_cli = MockBitcoinCli {
let bitcoind_client = MockBitcoindClient {
utxos: vec![sample_utxo()],
change_spk: sample_change_spk(),
..Default::default()
};
Self {
executor: Executor::new(
MockConnection::new(),
bitcoin_cli,
bitcoind_client,
MockTargetRpc::default(),
sample_context(),
),
Expand All @@ -147,7 +147,7 @@ impl Fixture {

/// Funds the wallet with `utxos` instead of the default [`sample_utxo`].
pub fn with_utxos(mut self, utxos: Vec<Utxo>) -> Self {
self.executor.bitcoin_cli.utxos = utxos;
self.executor.bitcoind_client.utxos = utxos;
self
}

Expand Down Expand Up @@ -206,8 +206,8 @@ impl Fixture {
}

/// Returns the mock bitcoind the executor drives.
pub fn bitcoin(&self) -> &MockBitcoinCli {
&self.executor.bitcoin_cli
pub fn bitcoin(&self) -> &MockBitcoindClient {
&self.executor.bitcoind_client
}

/// Returns the mock RPC interface to the target.
Expand Down
10 changes: 5 additions & 5 deletions smite-scenarios/src/scenarios/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

use std::marker::PhantomData;

use smite::bitcoin::BitcoinCli;
use smite::bitcoin::BitcoindClient;
use smite::noise::NoiseConnection;
use smite::scenarios::{Scenario, ScenarioError, ScenarioResult};
use smite::violation::Violation;
Expand All @@ -20,13 +20,13 @@ use crate::targets::Target;
/// mutators or generators; the executor panics on invariant violations
/// (out-of-bounds variable refs, type mismatches, `MineBlocks(0)`, etc.).
pub struct IrScenario<T: Target, S: SnapshotSetup<T>> {
/// Executes IR programs and owns the connection, bitcoin-cli handle,
/// Executes IR programs and owns the connection, bitcoind client,
/// program context, and the target's RPC handle. Created once before the
/// snapshot and reused across fuzzing runs.
///
/// Declared before `target` so the peer connection closes first and doesn't
/// stall the target's shutdown.
executor: Executor<NoiseConnection, BitcoinCli, T::Rpc>,
executor: Executor<NoiseConnection, BitcoindClient, T::Rpc>,
target: T,
// S is only used for static dispatch on S::setup(), not stored.
_phantom: PhantomData<S>,
Expand All @@ -36,8 +36,8 @@ impl<T: Target, S: SnapshotSetup<T>> Scenario for IrScenario<T, S> {
fn new(_args: &[String]) -> Result<Self, ScenarioError> {
let target = T::start(T::Config::default())?;
let (conn, context) = S::setup(&target)?;
let bitcoin_cli = target.bitcoin_cli().clone();
let executor = Executor::new(conn, bitcoin_cli, target.rpc(), context);
let bitcoind_client = target.bitcoind_client().clone();
let executor = Executor::new(conn, bitcoind_client, target.rpc(), context);
Ok(Self {
executor,
target,
Expand Down
6 changes: 3 additions & 3 deletions smite-scenarios/src/targets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub use cln::{ClnConfig, ClnRpc, ClnTarget};
pub use eclair::{EclairConfig, EclairRpc, EclairTarget};
pub use ldk::{LdkConfig, LdkRpc, LdkTarget};
pub use lnd::{LndConfig, LndRpc, LndTarget};
use smite::bitcoin::BitcoinCli;
use smite::bitcoin::BitcoindClient;
use smite::scenarios::TargetError;

use bitcoin::secp256k1;
Expand Down Expand Up @@ -76,8 +76,8 @@ pub trait Target: Sized {
/// Target's RPC handle for executing commands.
fn rpc(&self) -> Self::Rpc;

/// `bitcoin-cli` wrapper for the regtest `bitcoind` instance.
fn bitcoin_cli(&self) -> &BitcoinCli;
/// JSON-RPC client for the regtest `bitcoind` instance.
fn bitcoind_client(&self) -> &BitcoindClient;

/// Check if target is still alive. Returns `Err(Crashed)` if dead.
///
Expand Down
69 changes: 20 additions & 49 deletions smite-scenarios/src/targets/bitcoind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::time::Duration;

use smite::bitcoin::BitcoinCli;
use smite::bitcoin::BitcoindClient;
use smite::process::ManagedProcess;

use super::TargetError;
Expand Down Expand Up @@ -63,7 +63,7 @@ pub fn resolve_data_dir() -> Result<(PathBuf, Option<tempfile::TempDir>), Target
pub fn start(
config: &BitcoindConfig,
data_dir: &Path,
) -> Result<(ManagedProcess, BitcoinCli), TargetError> {
) -> Result<(ManagedProcess, BitcoindClient), TargetError> {
log::info!("Starting bitcoind...");

let bitcoind_dir = data_dir.join("bitcoind");
Expand Down Expand Up @@ -101,28 +101,18 @@ pub fn start(
}

let bitcoind = ManagedProcess::spawn(&mut cmd, "bitcoind")?;
let cli = BitcoinCli {
rpc_port: config.rpc_port,
bitcoind_dir,
};
let mut client = BitcoindClient::new(config.rpc_port);

// Wait for bitcoind to be ready
log::info!("Waiting for bitcoind to be ready...");
for _ in 0..30 {
let status = cli
.run()
.arg("getblockchaininfo")
.stdout(Stdio::null())
.stderr(Stdio::null())
.status();

if status.is_ok_and(|s| s.success()) {
std::thread::sleep(Duration::from_secs(1));

if client.is_ready() {
log::info!("bitcoind is ready");
setup_wallet(&cli)?;
return Ok((bitcoind, cli));
setup_wallet(&mut client)?;
return Ok((bitcoind, client));
}

std::thread::sleep(Duration::from_secs(1));
}

Err(TargetError::StartFailed(
Expand All @@ -131,37 +121,18 @@ pub fn start(
}

/// Creates wallet and generates initial blocks.
fn setup_wallet(cli: &BitcoinCli) -> Result<(), TargetError> {
// Create wallet
let status = cli
.run()
.arg("createwallet")
.arg("default")
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()?;

// command fails if wallet already exists (i.e. SMITE_DATA_DIR was mounted)
if !status.success() {
return Err(TargetError::StartFailed(
"failed to create wallet (does it already exist?)".into(),
));
}

// Generate initial blocks
let status = cli
.run()
.arg("-generate")
.arg(INITIAL_BLOCKS.to_string())
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()?;

if !status.success() {
return Err(TargetError::StartFailed(
"failed to generate initial blocks".into(),
));
}
fn setup_wallet(client: &mut BitcoindClient) -> Result<(), TargetError> {
// Fails if the wallet already exists (i.e. SMITE_DATA_DIR was mounted).
client.create_wallet("default").map_err(|e| {
TargetError::StartFailed(format!(
"failed to create wallet (does it already exist?): {e}"
))
})?;

let initial_blocks = u32::try_from(INITIAL_BLOCKS).expect("fits in u32");
client
.generate(initial_blocks)
.map_err(|e| TargetError::StartFailed(format!("failed to generate initial blocks: {e}")))?;

Ok(())
}
Loading
Loading