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
89 changes: 77 additions & 12 deletions src/core/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,21 +128,21 @@ use std::{
};

use libbitcoinkernel_sys::{
btck_Block, btck_BlockHash, btck_BlockHeader, btck_BlockSpentOutputs, btck_Coin,
btck_TransactionSpentOutputs, btck_block_copy, btck_block_count_transactions,
btck_block_create, btck_block_destroy, btck_block_get_hash, btck_block_get_header,
btck_block_get_transaction_at, btck_block_hash_copy, btck_block_hash_create,
btck_block_hash_destroy, btck_block_hash_equals, btck_block_hash_to_bytes,
btck_block_header_copy, btck_block_header_create, btck_block_header_destroy,
btck_block_header_get_bits, btck_block_header_get_hash, btck_block_header_get_nonce,
btck_block_header_get_prev_hash, btck_block_header_get_timestamp,
btck_Block, btck_BlockCheckFlags, btck_BlockHash, btck_BlockHeader, btck_BlockSpentOutputs,
btck_BlockValidationState, btck_Coin, btck_TransactionSpentOutputs, btck_block_check,
btck_block_copy, btck_block_count_transactions, btck_block_create, btck_block_destroy,
btck_block_get_hash, btck_block_get_header, btck_block_get_transaction_at,
btck_block_hash_copy, btck_block_hash_create, btck_block_hash_destroy, btck_block_hash_equals,
btck_block_hash_to_bytes, btck_block_header_copy, btck_block_header_create,
btck_block_header_destroy, btck_block_header_get_bits, btck_block_header_get_hash,
btck_block_header_get_nonce, btck_block_header_get_prev_hash, btck_block_header_get_timestamp,
btck_block_header_get_version, btck_block_header_to_bytes, btck_block_spent_outputs_copy,
btck_block_spent_outputs_count, btck_block_spent_outputs_destroy,
btck_block_spent_outputs_get_transaction_spent_outputs_at, btck_block_to_bytes,
btck_coin_confirmation_height, btck_coin_copy, btck_coin_destroy, btck_coin_get_output,
btck_coin_is_coinbase, btck_transaction_spent_outputs_copy,
btck_transaction_spent_outputs_count, btck_transaction_spent_outputs_destroy,
btck_transaction_spent_outputs_get_coin_at,
btck_chain_parameters_get_consensus_params, btck_coin_confirmation_height, btck_coin_copy,
btck_coin_destroy, btck_coin_get_output, btck_coin_is_coinbase,
btck_transaction_spent_outputs_copy, btck_transaction_spent_outputs_count,
btck_transaction_spent_outputs_destroy, btck_transaction_spent_outputs_get_coin_at,
};

use crate::{
Expand All @@ -151,9 +151,23 @@ use crate::{
c_helpers::present,
sealed::{AsPtr, FromMutPtr, FromPtr},
},
notifications::types::BlockValidationState,
state::context::ChainParams,
KernelError,
};

/// Run only base context-free block checks (no PoW, no Merkle root).
pub const BLOCK_CHECK_BASE: btck_BlockCheckFlags = 0;

/// Enable Proof-of-Work verification via the block header.
pub const BLOCK_CHECK_POW: btck_BlockCheckFlags = 1 << 0;

/// Enable Merkle-root verification (and mutation detection).
pub const BLOCK_CHECK_MERKLE: btck_BlockCheckFlags = 1 << 1;

/// Enable all available context-free block checks (PoW + Merkle root).
pub const BLOCK_CHECK_ALL: btck_BlockCheckFlags = BLOCK_CHECK_POW | BLOCK_CHECK_MERKLE;

use super::transaction::{TransactionRef, TxOutRef};

/// Common operations for block hashes, implemented by both owned and borrowed types.
Expand Down Expand Up @@ -942,6 +956,57 @@ impl Block {
pub fn transactions(&self) -> BlockTransactionIter<'_> {
BlockTransactionIter::new(self)
}

/// Performs context-free validation checks on this block.
///
/// Runs base structural checks (size, weight, coinbase, transactions,
/// sigops) without requiring chainstate or block index access.
/// Proof-of-work and merkle-root checks are optional and toggled via `flags`.
///
/// # Arguments
/// * `chain_params` - Chain parameters providing consensus rules
/// * `flags` - Bitmask of [`BLOCK_CHECK_BASE`], [`BLOCK_CHECK_POW`],
/// [`BLOCK_CHECK_MERKLE`], or [`BLOCK_CHECK_ALL`]
/// * `validation_state` - Filled in-place with the validation outcome
///
/// # Returns
/// `true` if the block passed the checks, `false` otherwise.
///
/// # Examples
/// ```no_run
/// # use bitcoinkernel::{
/// # prelude::*, Block, BlockValidationState, ChainParams, ChainType, BLOCK_CHECK_ALL,
/// # };
/// # fn example() -> Result<(), bitcoinkernel::KernelError> {
/// # let block_data = vec![0u8; 100]; // placeholder
/// # let block = Block::new(&block_data)?;
/// let chain_params = ChainParams::new(ChainType::Mainnet);
/// let mut state = BlockValidationState::new();
///
/// if !block.check_context_free(&chain_params, BLOCK_CHECK_ALL, &mut state) {
/// println!("Failed: {:?}", state.result());
/// }
/// # Ok(())
/// # }
/// ```
pub fn check_context_free(
&self,
chain_params: &ChainParams,
flags: btck_BlockCheckFlags,
validation_state: &mut BlockValidationState,
) -> bool {
let consensus_params =
unsafe { btck_chain_parameters_get_consensus_params(chain_params.as_ptr()) };
let result = unsafe {
btck_block_check(
self.inner,
consensus_params,
flags,
validation_state.as_ptr() as *mut btck_BlockValidationState,
)
};
result == 1
}
}

impl AsPtr<btck_Block> for Block {
Expand Down
6 changes: 6 additions & 0 deletions src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,9 @@ pub mod verify_flags {
VERIFY_DERSIG, VERIFY_NONE, VERIFY_NULLDUMMY, VERIFY_P2SH, VERIFY_TAPROOT, VERIFY_WITNESS,
};
}

pub mod block_check_flags {
pub use super::block::{
BLOCK_CHECK_ALL, BLOCK_CHECK_BASE, BLOCK_CHECK_MERKLE, BLOCK_CHECK_POW,
};
}
12 changes: 8 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,17 +306,21 @@ pub use crate::core::{
pub use crate::log::{disable_logging, Log, LogCategory, LogLevel, Logger};

pub use crate::notifications::{
BlockCheckedCallback, BlockTipCallback, BlockValidationResult, BlockValidationStateRef,
FatalErrorCallback, FlushErrorCallback, HeaderTipCallback, NotificationCallbackRegistry,
ProgressCallback, SynchronizationState, ValidationCallbackRegistry, ValidationMode, Warning,
WarningSetCallback, WarningUnsetCallback,
BlockCheckedCallback, BlockTipCallback, BlockValidationResult, BlockValidationState,
BlockValidationStateRef, FatalErrorCallback, FlushErrorCallback, HeaderTipCallback,
NotificationCallbackRegistry, ProgressCallback, SynchronizationState,
ValidationCallbackRegistry, ValidationMode, Warning, WarningSetCallback, WarningUnsetCallback,
};

pub use crate::state::{
Chain, ChainParams, ChainType, ChainstateManager, ChainstateManagerBuilder, Context,
ContextBuilder, ProcessBlockHeaderResult, ProcessBlockResult,
};

pub use crate::core::block_check_flags::{
BLOCK_CHECK_ALL, BLOCK_CHECK_BASE, BLOCK_CHECK_MERKLE, BLOCK_CHECK_POW,
};

pub use crate::core::verify_flags::{
VERIFY_ALL, VERIFY_ALL_PRE_TAPROOT, VERIFY_CHECKLOCKTIMEVERIFY, VERIFY_CHECKSEQUENCEVERIFY,
VERIFY_DERSIG, VERIFY_NONE, VERIFY_NULLDUMMY, VERIFY_P2SH, VERIFY_TAPROOT, VERIFY_WITNESS,
Expand Down
4 changes: 2 additions & 2 deletions src/notifications/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ pub mod types;
pub mod validation;

pub use types::{
BlockValidationResult, BlockValidationStateExt, BlockValidationStateRef, SynchronizationState,
ValidationMode, Warning,
BlockValidationResult, BlockValidationState, BlockValidationStateExt, BlockValidationStateRef,
SynchronizationState, ValidationMode, Warning,
};

pub use notification::{
Expand Down
6 changes: 6 additions & 0 deletions src/state/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,12 @@ impl Drop for ChainParams {
}
}

impl AsPtr<btck_ChainParameters> for ChainParams {
fn as_ptr(&self) -> *const btck_ChainParameters {
self.inner as *const _
}
}

/// The main context for the Bitcoin Kernel library.
///
/// The [`Context`] manages the global state of the Bitcoin Kernel library
Expand Down
79 changes: 77 additions & 2 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ mod tests {
use bitcoinkernel::state::chainstate::ProcessBlockHeaderResult;
use bitcoinkernel::{
prelude::*, verify, Block, BlockHash, BlockHeader, BlockSpentOutputs, BlockTreeEntry,
BlockValidationStateRef, ChainParams, ChainType, ChainstateManager,
BlockValidationResult, BlockValidationStateRef, ChainParams, ChainType, ChainstateManager,
ChainstateManagerBuilder, Coin, Context, ContextBuilder, KernelError, Log, Logger,
PrecomputedTransactionData, ScriptPubkey, ScriptVerifyError, Transaction,
TransactionSpentOutputs, TxIn, TxOut, ValidationMode, VERIFY_ALL, VERIFY_ALL_PRE_TAPROOT,
TransactionSpentOutputs, TxIn, TxOut, ValidationMode, BLOCK_CHECK_ALL, BLOCK_CHECK_BASE,
BLOCK_CHECK_MERKLE, BLOCK_CHECK_POW, VERIFY_ALL, VERIFY_ALL_PRE_TAPROOT,
VERIFY_CHECKLOCKTIMEVERIFY, VERIFY_CHECKSEQUENCEVERIFY, VERIFY_DERSIG, VERIFY_NONE,
VERIFY_NULLDUMMY, VERIFY_P2SH, VERIFY_TAPROOT, VERIFY_WITNESS,
};
Expand Down Expand Up @@ -918,4 +919,78 @@ mod tests {
// is_sync::<Rc<u8>>(); // won't compile, kept as a failure case.
// is_send::<Rc<u8>>(); // won't compile, kept as a failure case.
}

// Mainnet block 1.
const MAINNET_BLOCK_1_HEX: &str = "010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c\
68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ff\
ff001d01e362990101000000010000000000000000000000000000000000000000000000000000000000000000\
ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae\
1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c8\
58eeac00000000";

#[test]
fn test_block_check_context_free() {
// Byte offsets within the serialized block.
const MERKLE_ROOT_OFFSET: usize = 4 + 32;
const NBITS_OFFSET: usize = 4 + 32 + 32 + 4;
const COINBASE_PREVOUT_N_OFFSET: usize = 4 + 32 + 32 + 4 + 4 + 4 + 1 + 4 + 1 + 32;

let raw_block = hex::decode(MAINNET_BLOCK_1_HEX).unwrap();
let chain_params = ChainParams::new(ChainType::Mainnet);

let block = Block::new(&raw_block).unwrap();
let mut state = BlockValidationState::new();

assert!(block.check_context_free(&chain_params, BLOCK_CHECK_BASE, &mut state));
assert_eq!(state.mode(), ValidationMode::Valid);

assert!(block.check_context_free(&chain_params, BLOCK_CHECK_ALL, &mut state));
assert_eq!(state.mode(), ValidationMode::Valid);

let mut bad_merkle_block_data = raw_block.clone();
bad_merkle_block_data[MERKLE_ROOT_OFFSET] ^= 0x01;
let bad_merkle_block = Block::new(&bad_merkle_block_data).unwrap();

assert!(!bad_merkle_block.check_context_free(
&chain_params,
BLOCK_CHECK_MERKLE,
&mut state
));
assert_eq!(state.mode(), ValidationMode::Invalid);
assert_eq!(state.result(), BlockValidationResult::Mutated);

assert!(bad_merkle_block.check_context_free(&chain_params, BLOCK_CHECK_BASE, &mut state));
assert_eq!(state.mode(), ValidationMode::Valid);

let mut bad_pow_block_data = raw_block.clone();
bad_pow_block_data[NBITS_OFFSET + 3] = 0x1c;
let bad_pow_block = Block::new(&bad_pow_block_data).unwrap();

assert!(!bad_pow_block.check_context_free(&chain_params, BLOCK_CHECK_POW, &mut state));
assert_eq!(state.mode(), ValidationMode::Invalid);
assert_eq!(state.result(), BlockValidationResult::InvalidHeader);

assert!(bad_pow_block.check_context_free(&chain_params, BLOCK_CHECK_MERKLE, &mut state));
assert_eq!(state.mode(), ValidationMode::Valid);

let mut bad_base_block_data = raw_block.clone();
bad_base_block_data[COINBASE_PREVOUT_N_OFFSET] = 0x00;
let bad_base_block = Block::new(&bad_base_block_data).unwrap();

assert!(!bad_base_block.check_context_free(&chain_params, BLOCK_CHECK_BASE, &mut state));
assert_eq!(state.mode(), ValidationMode::Invalid);
assert_eq!(state.result(), BlockValidationResult::Consensus);

// Test with invalid truncated block data.
let truncated_block_data = hex::decode(
"010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000\
982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649\
ffff001d01e36299",
)
.unwrap();
assert!(matches!(
Block::new(&truncated_block_data),
Err(KernelError::Internal(_))
));
}
}
Loading