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
14 changes: 11 additions & 3 deletions examples/src/script_debug.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use bitcoin::Script;
use bitcoinkernel::{
prelude::*, verify, PrecomputedTransactionData, ScriptDebugger, ScriptPubkey, Transaction,
TxOut, VERIFY_ALL_PRE_TAPROOT,
prelude::*, verify, PrecomputedTransactionData, ScriptDebugger, ScriptPubkey, SigVersion,
Transaction, TxOut, VERIFY_ALL_PRE_TAPROOT,
};

fn main() {
Expand All @@ -17,12 +17,20 @@ fn main() {
op if op <= 0x4b => "DATA_PUSH",
_ => "OTHER",
};
let sig_version_name = match frame.sig_version {
SigVersion::Base => "BASE",
SigVersion::WitnessV0 => "WITNESS_V0",
SigVersion::Taproot => "TAPROOT",
SigVersion::Tapscript => "TAPSCRIPT",
_ => "UNKNOWN",
};
println!(
"\n[step {}] opcode=0x{:02x} ({}) op_count={} f_exec={} stack_depth={}",
"\n[step {}] opcode=0x{:02x} ({}) op_count={} sig_version={} f_exec={} stack_depth={}",
frame.opcode_pos,
frame.opcode,
opcode_name,
frame.op_count,
sig_version_name,
frame.f_exec,
frame.stack.len(),
);
Expand Down
4 changes: 3 additions & 1 deletion libbitcoinkernel-sys/bitcoin/src/kernel/bitcoinkernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1409,7 +1409,8 @@ void btck_register_script_debug_callback(void* user_data, btck_ScriptDebugCallba
std::span<const std::vector<unsigned char>> altstack,
bool fExec,
uint8_t opcode,
int nOpCount) {
int nOpCount,
uint8_t sigversion) {
std::vector<const unsigned char*> stack_ptrs;
std::vector<size_t> stack_sizes;
stack_ptrs.reserve(stack.size());
Expand Down Expand Up @@ -1441,6 +1442,7 @@ void btck_register_script_debug_callback(void* user_data, btck_ScriptDebugCallba
state.f_exec = fExec ? 1 : 0;
state.opcode = opcode;
state.op_count = nOpCount;
state.sig_version = sigversion;

callback(user_data, &state);
};
Expand Down
7 changes: 7 additions & 0 deletions libbitcoinkernel-sys/bitcoin/src/kernel/bitcoinkernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -1851,6 +1851,12 @@ BITCOINKERNEL_API void btck_block_header_destroy(btck_BlockHeader* header);
*/
///@{

typedef uint8_t btck_SigVersion;
#define btck_SigVersion_BASE ((btck_SigVersion)(0))
#define btck_SigVersion_WITNESS_V0 ((btck_SigVersion)(1))
#define btck_SigVersion_TAPROOT ((btck_SigVersion)(2))
#define btck_SigVersion_TAPSCRIPT ((btck_SigVersion)(3))

/**
* Snapshot of script execution state passed to the debug callback.
*/
Expand All @@ -1867,6 +1873,7 @@ typedef struct {
int f_exec;
uint8_t opcode;
int op_count;
uint8_t sig_version;
} btck_ScriptDebugState;

/**
Expand Down
4 changes: 2 additions & 2 deletions libbitcoinkernel-sys/bitcoin/src/script/debug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
static std::mutex g_script_debug_mutex;
static DebugScriptCallback g_script_debug_callback{nullptr};

void DebugScript(std::span<const std::vector<unsigned char>> stack, const CScript& script, uint32_t opcode_pos, std::span<const std::vector<unsigned char>> altstack, bool fExec, uint8_t opcode, int nOpCount)
void DebugScript(std::span<const std::vector<unsigned char>> stack, const CScript& script, uint32_t opcode_pos, std::span<const std::vector<unsigned char>> altstack, bool fExec, uint8_t opcode, int nOpCount, uint8_t sigversion)
{
std::lock_guard<std::mutex> lock(g_script_debug_mutex);
if (g_script_debug_callback)
g_script_debug_callback(stack, script, opcode_pos, altstack, fExec, opcode, nOpCount);
g_script_debug_callback(stack, script, opcode_pos, altstack, fExec, opcode, nOpCount, sigversion);
}

void RegisterDebugScriptCallback(DebugScriptCallback func)
Expand Down
10 changes: 5 additions & 5 deletions libbitcoinkernel-sys/bitcoin/src/script/debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@
#include <span>
#include <vector>

using DebugScriptCallback = std::function<void(std::span<const std::vector<unsigned char>>, const CScript&, uint32_t, std::span<const std::vector<unsigned char>>, bool, uint8_t, int)>;
using DebugScriptCallback = std::function<void(std::span<const std::vector<unsigned char>>, const CScript&, uint32_t, std::span<const std::vector<unsigned char>>, bool, uint8_t, int, uint8_t)>;

void DebugScript(std::span<const std::vector<unsigned char>> stack, const CScript& script, uint32_t opcode_pos, std::span<const std::vector<unsigned char>> altstack, bool fExec, uint8_t opcode, int nOpCount);
void DebugScript(std::span<const std::vector<unsigned char>> stack, const CScript& script, uint32_t opcode_pos, std::span<const std::vector<unsigned char>> altstack, bool fExec, uint8_t opcode, int nOpCount, uint8_t sigversion);

void RegisterDebugScriptCallback(DebugScriptCallback func);

#ifdef ENABLE_SCRIPT_DEBUG
#define DEBUG_SCRIPT(stack, script, opcode_pos, altstack, fExec, opcode, nOpCount) \
DebugScript(stack, script, opcode_pos, altstack, fExec, opcode, nOpCount);
#define DEBUG_SCRIPT(stack, script, opcode_pos, altstack, fExec, opcode, nOpCount, sigversion) \
DebugScript(stack, script, opcode_pos, altstack, fExec, opcode, nOpCount, sigversion);
#else
#define DEBUG_SCRIPT(stack, script, opcode_pos, altstack, fExec, opcode, nOpCount)
#define DEBUG_SCRIPT(stack, script, opcode_pos, altstack, fExec, opcode, nOpCount, sigversion)
#endif // ENABLE_SCRIPT_DEBUG

#endif // BITCOIN_SCRIPT_DEBUG_H
4 changes: 2 additions & 2 deletions libbitcoinkernel-sys/bitcoin/src/script/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
}
}

DEBUG_SCRIPT(stack, script, opcode_pos, altstack, fExec, static_cast<uint8_t>(opcode), nOpCount);
DEBUG_SCRIPT(stack, script, opcode_pos, altstack, fExec, static_cast<uint8_t>(opcode), nOpCount, static_cast<uint8_t>(sigversion));

if (opcode == OP_CAT ||
opcode == OP_SUBSTR ||
Expand Down Expand Up @@ -1231,7 +1231,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
return set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR);
}

DEBUG_SCRIPT(stack, script, opcode_pos, altstack, vfExec.all_true(), static_cast<uint8_t>(OP_INVALIDOPCODE), nOpCount);
DEBUG_SCRIPT(stack, script, opcode_pos, altstack, vfExec.all_true(), static_cast<uint8_t>(OP_INVALIDOPCODE), nOpCount, static_cast<uint8_t>(sigversion));

if (!vfExec.empty())
return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
Expand Down
37 changes: 37 additions & 0 deletions src/core/script_debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,38 @@ use libbitcoinkernel_sys::{
btck_unregister_script_debug_callback,
};

/// Script execution context (signature version).
///
/// Indicates which script system rules apply during execution.
/// Key-path taproot spends (`Taproot`) bypass `EvalScript` entirely,
/// so they will not appear in debug callbacks.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
#[repr(u8)]
pub enum SigVersion {
/// Bare scripts and BIP16 P2SH-wrapped redeemscripts.
Base = 0,
/// Witness v0 (P2WPKH and P2WSH); see BIP 141.
WitnessV0 = 1,
/// Witness v1 key path spending; see BIP 341.
Taproot = 2,
/// Witness v1 script path spending, leaf version 0xc0; see BIP 342.
Tapscript = 3,
}

impl TryFrom<u8> for SigVersion {
type Error = u8;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(SigVersion::Base),
1 => Ok(SigVersion::WitnessV0),
2 => Ok(SigVersion::Taproot),
3 => Ok(SigVersion::Tapscript),
other => Err(other),
}
}
}

/// A snapshot of script execution state at a single opcode step.
#[derive(Debug, Clone)]
pub struct ScriptDebugFrame {
Expand All @@ -29,6 +61,8 @@ pub struct ScriptDebugFrame {
pub opcode: u8,
/// Cumulative count of non-push opcodes executed so far (tracks the 201-op limit).
pub op_count: u32,
/// Script execution context (legacy, segwit v0, tapscript, etc.).
pub sig_version: SigVersion,
}

/// Guard that keeps a script debug callback registered.
Expand Down Expand Up @@ -104,6 +138,8 @@ unsafe extern "C" fn trampoline(
unsafe { std::slice::from_raw_parts(state.script, state.script_size) }.to_vec()
};

let sig_version = SigVersion::try_from(state.sig_version).unwrap_or(SigVersion::Base);

let frame = ScriptDebugFrame {
stack,
altstack,
Expand All @@ -112,6 +148,7 @@ unsafe extern "C" fn trampoline(
f_exec: state.f_exec != 0,
opcode: state.opcode,
op_count: state.op_count as u32,
sig_version,
};

let closure = unsafe { &mut **(user_data as *mut Box<dyn FnMut(ScriptDebugFrame)>) };
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ pub use crate::state::{
};

#[cfg(feature = "script_debug")]
pub use crate::core::script_debug::{ScriptDebugFrame, ScriptDebugger};
pub use crate::core::script_debug::{ScriptDebugFrame, ScriptDebugger, SigVersion};

pub use crate::core::verify_flags::{
VERIFY_ALL, VERIFY_ALL_PRE_TAPROOT, VERIFY_CHECKLOCKTIMEVERIFY, VERIFY_CHECKSEQUENCEVERIFY,
Expand Down
11 changes: 10 additions & 1 deletion tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,7 @@ mod tests {
#[cfg(feature = "script_debug")]
#[test]
fn test_script_debug() {
use bitcoinkernel::{ScriptDebugFrame, ScriptDebugger};
use bitcoinkernel::{ScriptDebugFrame, ScriptDebugger, SigVersion};
use std::sync::Mutex;

let frames: Arc<Mutex<Vec<ScriptDebugFrame>>> = Arc::new(Mutex::new(Vec::new()));
Expand Down Expand Up @@ -736,6 +736,15 @@ mod tests {
!final_frames.is_empty(),
"should have final callback frame(s) with opcode 0xff"
);

// P2PKH uses SigVersion::Base for all execution phases
for frame in collected.iter() {
assert_eq!(
frame.sig_version,
SigVersion::Base,
"P2PKH execution should use SigVersion::Base"
);
}
}

/// Multiple threads race to register a debugger — at most one should be active at a time.
Expand Down
Loading