Skip to content

Commit

Permalink
Check if tx is final
Browse files Browse the repository at this point in the history
  • Loading branch information
liuchengxu committed Jul 18, 2024
1 parent 571a0b7 commit ba71c19
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 9 deletions.
41 changes: 36 additions & 5 deletions crates/sc-consensus-nakamoto/src/verification.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod header_verifier;

use bitcoin::absolute::{LockTime, LOCK_TIME_THRESHOLD};
use bitcoin::blockdata::constants::MAX_BLOCK_SIGOPS_COST;
use bitcoin::blockdata::weight::WITNESS_SCALE_FACTOR;
use bitcoin::consensus::Params;
Expand Down Expand Up @@ -58,8 +59,14 @@ pub enum Error {
FirstTransactionIsNotCoinbase,
#[error("Block contains multiple coinbase transactions")]
MultipleCoinbase,
#[error("Transaction script contains too many sigops (max: {MAX_BLOCK_SIGOPS_COST})")]
TooManySigOps { block_number: u32, tx_index: usize },
#[error("Transaction input script contains too many sigops (max: {MAX_BLOCK_SIGOPS_COST})")]
TooManySigOps {
block_number: u32,
txid: Txid,
index: usize,
},
#[error("Transaction is not finalized")]
TransactionNotFinal,
#[error("Block contains duplicate transaction at index {0}")]
DuplicateTransaction(usize),
#[error("Transaction contains duplicate inputs at index {0}")]
Expand Down Expand Up @@ -140,8 +147,8 @@ where

match self.block_verification {
BlockVerification::Full => {
self.header_verifier.verify_header(&block.header)?;
self.verify_transactions(block_number, block, txids)?;
let time = self.header_verifier.verify_header(&block.header)?;
self.verify_transactions(block_number, block, txids, time)?;
}
BlockVerification::HeaderOnly => {
self.header_verifier.verify_header(&block.header)?;
Expand Down Expand Up @@ -208,7 +215,8 @@ where
{
return Err(Error::TooManySigOps {
block_number,
tx_index: index,
txid,
index,
});
}

Expand Down Expand Up @@ -242,6 +250,7 @@ where
block_number: u32,
block: &BitcoinBlock,
txids: HashMap<usize, Txid>,
time: u32,
) -> Result<(), Error> {
let transactions = &block.txdata;

Expand Down Expand Up @@ -305,6 +314,10 @@ where
continue;
}

if !is_final(tx, block_number, time) {
return Err(Error::TransactionNotFinal);
}

let tx_fee = verify_transaction(index, tx)?;

block_fee += tx_fee;
Expand Down Expand Up @@ -394,6 +407,24 @@ fn tx_serialize_size_no_witness(tx: &Transaction) -> usize {
+ VarInt(tx.output.len() as u64).size() + output_size // Vec<TxOut>
}

fn is_final(tx: &Transaction, height: u32, block_time: u32) -> bool {
if tx.lock_time == LockTime::ZERO {
return true;
}

let lock_time = if tx.lock_time.to_consensus_u32() < LOCK_TIME_THRESHOLD {
height
} else {
block_time
};

if tx.lock_time.to_consensus_u32() < lock_time {
return true;
}

tx.input.iter().all(|txin| txin.sequence.is_final())
}

// Find a UTXO from the previous transactions in current block.
fn find_utxo_in_current_block(
block: &BitcoinBlock,
Expand Down
12 changes: 8 additions & 4 deletions crates/sc-consensus-nakamoto/src/verification/header_verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,14 @@ where
Block: BlockT,
Client: HeaderBackend<Block> + AuxStore,
{
/// Verifies the validity of header.
/// Verifies the validity of header and returns the block time for verifying the finality of
/// transactions.
///
/// - Check proof of work.
/// - Check the timestamp of the block is in the range:
/// - Time is not greater than 2 hours from now.
/// - Time is not the median time of last 11 blocks or before.
pub fn verify_header(&self, header: &BitcoinHeader) -> Result<(), Error> {
pub fn verify_header(&self, header: &BitcoinHeader) -> Result<u32, Error> {
let last_block_header = self.client.block_header(header.prev_blockhash).ok_or(
sp_blockchain::Error::MissingHeader(header.prev_blockhash.to_string()),
)?;
Expand Down Expand Up @@ -104,14 +105,17 @@ where

// TODO: check deployment state properly.
const MAINNET_CSV_HEIGHT: u32 = 419328;

if block_number >= MAINNET_CSV_HEIGHT {
let median_time = self.calculate_median_time_past(header);
if header.time <= median_time {
return Err(Error::TimeTooOld);
}
}

Ok(())
Ok(median_time)
} else {
Ok(header.time)
}
}

/// Calculates the median time of the previous few blocks prior to the header (inclusive).
Expand Down

0 comments on commit ba71c19

Please sign in to comment.