Skip to content

Commit

Permalink
Docs
Browse files Browse the repository at this point in the history
  • Loading branch information
liuchengxu committed Jul 25, 2024
1 parent 0a70b08 commit b1ee490
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 31 deletions.
58 changes: 47 additions & 11 deletions crates/sc-consensus-nakamoto/src/verification.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
//! This module provides block verification functionalities based on Bitcoin's consensus rules.
//! The primary reference for these consensus rules is Bitcoin Core. We utilize the `rust-bitcoinconsensus`
//! from `rust-bitcoin` for handling the most complex aspects of script verification.
//!
//! The main components of this module are:
//! - `header_verify`: Module responsible for verifying block headers.
//! - `tx_verify`: Module responsible for verifying individual transactions within a block.
//!
//! This module ensures that blocks adhere to Bitcoin's consensus rules by performing checks on
//! the proof of work, timestamps, transaction validity, and more.
//!
//! # Components
//!
//! ## Modules
//!
//! - `header_verify`: Contains functions and structures for verifying block headers.
//! - `tx_verify`: Contains functions and structures for verifying transactions.
//!
//! ## Structures
//!
//! - [`BlockVerifier`]: Responsible for verifying Bitcoin blocks, including headers and transactions.
//!
//! ## Enums
//!
//! - [`BlockVerification`]: Represents the level of block verification (None, Full, HeaderOnly).

mod header_verify;
mod tx_verify;

Expand Down Expand Up @@ -202,9 +228,8 @@ where
}

// Size limits, without tx witness data.
if Weight::from_wu(block.txdata.len() as u64 * WITNESS_SCALE_FACTOR as u64)
> MAX_BLOCK_WEIGHT
|| Weight::from_wu(block_base_size(block) as u64 * WITNESS_SCALE_FACTOR as u64)
if Weight::from_wu((block.txdata.len() * WITNESS_SCALE_FACTOR) as u64) > MAX_BLOCK_WEIGHT
|| Weight::from_wu((block_base_size(block) * WITNESS_SCALE_FACTOR) as u64)
> MAX_BLOCK_WEIGHT
{
return Err(Error::BadBlockLength);
Expand Down Expand Up @@ -324,11 +349,16 @@ where
let access_coin = |out_point: OutPoint| -> Option<(TxOut, bool, u32)> {
match self.find_utxo_in_state(parent_hash, out_point) {
Some(coin) => {
let is_coinbase = coin.is_coinbase;
let height = coin.height;
let Coin {
is_coinbase,
amount,
height,
script_pubkey,
} = coin;

let txout = TxOut {
value: Amount::from_sat(coin.amount),
script_pubkey: ScriptBuf::from_bytes(coin.script_pubkey),
value: Amount::from_sat(amount),
script_pubkey: ScriptBuf::from_bytes(script_pubkey),
};

Some((txout, is_coinbase, height))
Expand Down Expand Up @@ -469,6 +499,8 @@ fn find_utxo_in_current_block(
}

/// Returns the script validation flags for the specified block.
///
/// <https://github.com/bitcoin/bitcoin/blob/6f9db1ebcab4064065ccd787161bf2b87e03cc1f/src/validation.cpp#L2360>
fn get_block_script_flags(
height: u32,
block_hash: BlockHash,
Expand All @@ -484,18 +516,22 @@ fn get_block_script_flags(

let mut flags = bitcoinconsensus::VERIFY_P2SH | bitcoinconsensus::VERIFY_WITNESS;

if height >= chain_params.params.bip65_height {
flags |= bitcoinconsensus::VERIFY_CHECKLOCKTIMEVERIFY;
}

// Enforce the DERSIG (BIP66) rule
if height >= chain_params.params.bip66_height {
flags |= bitcoinconsensus::VERIFY_DERSIG;
}

// Enforce CHECKLOCKTIMEVERIFY (BIP65)
if height >= chain_params.params.bip65_height {
flags |= bitcoinconsensus::VERIFY_CHECKLOCKTIMEVERIFY;
}

// Enforce CHECKSEQUENCEVERIFY (BIP112)
if height >= chain_params.csv_height {
flags |= bitcoinconsensus::VERIFY_CHECKSEQUENCEVERIFY;
}

// Enforce BIP147 NULLDUMMY (activated simultaneously with segwit)
if height >= chain_params.segwit_height {
flags |= bitcoinconsensus::VERIFY_NULLDUMMY;
}
Expand Down
20 changes: 10 additions & 10 deletions crates/sc-consensus-nakamoto/src/verification/header_verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ where
Client: HeaderBackend<Block> + AuxStore,
{
/// Validates the header and returns the block time, which is used for verifying the finality of
/// transactions in [`super::tx_verify::is_final`].
/// transactions.
///
/// The validation process includes:
/// - Checking the proof of work.
Expand Down Expand Up @@ -116,6 +116,15 @@ where

let block_number = prev_block_height + 1;

let version = header.version.to_consensus();

if version < 2 && block_number >= self.chain_params.params.bip34_height
|| version < 3 && block_number >= self.chain_params.params.bip66_height
|| version < 4 && block_number >= self.chain_params.params.bip65_height
{
return Err(Error::BadVersion);
}

// BIP 113
let lock_time_cutoff = if block_number >= self.chain_params.csv_height {
let mtp = self.calculate_median_time_past(header);
Expand All @@ -127,15 +136,6 @@ where
header.time
};

let version = header.version.to_consensus();

if version < 2 && block_number >= self.chain_params.params.bip34_height
|| version < 3 && block_number >= self.chain_params.params.bip66_height
|| version < 4 && block_number >= self.chain_params.params.bip65_height
{
return Err(Error::BadVersion);
}

Ok(lock_time_cutoff)
}

Expand Down
21 changes: 11 additions & 10 deletions crates/sc-consensus-nakamoto/src/verification/tx_verify.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::MAX_BLOCK_WEIGHT;
use bitcoin::absolute::{LockTime, LOCK_TIME_THRESHOLD};
use bitcoin::{Amount, Transaction};
use bitcoin::blockdata::weight::WITNESS_SCALE_FACTOR;
use bitcoin::{Amount, Transaction, Weight};
use std::collections::HashSet;

// MinCoinbaseScriptLen is the minimum length a coinbase script can be.
Expand All @@ -17,7 +18,7 @@ pub enum Error {
#[error("Transaction has no outputs")]
EmptyOutput,
#[error("Transaction is too large")]
BadTransactionLength,
TransactionOversize,
#[error("Transaction contains duplicate inputs at index {0}")]
DuplicateTxInput(usize),
#[error("Output value (0) is too large")]
Expand All @@ -30,7 +31,7 @@ pub enum Error {
)]
BadCoinbaseLength(usize),
#[error("Transaction input refers to a previous output that is null")]
PreviousOutPointNull,
PreviousOutputNull,
}

pub fn is_final(tx: &Transaction, height: u32, block_time: u32) -> bool {
Expand Down Expand Up @@ -61,20 +62,20 @@ pub fn check_transaction_sanity(tx: &Transaction) -> Result<(), Error> {
return Err(Error::EmptyOutput);
}

if tx.weight() > MAX_BLOCK_WEIGHT {
return Err(Error::BadTransactionLength);
if Weight::from_wu((tx.base_size() * WITNESS_SCALE_FACTOR) as u64) > MAX_BLOCK_WEIGHT {
return Err(Error::TransactionOversize);
}

let mut total_output_value = Amount::ZERO;
let mut value_out = Amount::ZERO;
tx.output.iter().try_for_each(|txout| {
if txout.value > Amount::MAX_MONEY {
return Err(Error::OutputValueTooLarge(txout.value));
}

total_output_value += txout.value;
value_out += txout.value;

if total_output_value > Amount::MAX_MONEY {
return Err(Error::TotalOutputValueTooLarge(total_output_value));
if value_out > Amount::MAX_MONEY {
return Err(Error::TotalOutputValueTooLarge(value_out));
}

Ok(())
Expand All @@ -99,7 +100,7 @@ pub fn check_transaction_sanity(tx: &Transaction) -> Result<(), Error> {
// Previous transaction outputs referenced by the inputs to this
// transaction must not be null.
if tx.input.iter().any(|txin| txin.previous_output.is_null()) {
return Err(Error::PreviousOutPointNull);
return Err(Error::PreviousOutputNull);
}
}

Expand Down

0 comments on commit b1ee490

Please sign in to comment.