Skip to content

Commit

Permalink
median time past method
Browse files Browse the repository at this point in the history
  • Loading branch information
fishonamos committed Oct 6, 2024
1 parent 2a4f20b commit 91e2c60
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 56 deletions.
11 changes: 8 additions & 3 deletions packages/consensus/src/types/chain_state.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use core::fmt::{Display, Formatter, Error};
use crate::validation::{
difficulty::{validate_bits, adjust_difficulty}, coinbase::validate_coinbase,
timestamp::{validate_timestamp, next_prev_timestamps},
timestamp::{validate_timestamp, next_prev_timestamps, compute_median_time_past},
work::{validate_proof_of_work, compute_total_work},
block::{compute_and_validate_tx_data, validate_bip30_block_hash},
};
Expand All @@ -34,6 +34,8 @@ pub struct ChainState {
/// it's possible that one block could have an earlier timestamp
/// than a block that came before it in the chain.
pub prev_timestamps: Span<u32>,
/// Median Time Past (MTP) of the current block
pub median_time_past: u32,
}

/// Represents the initial state after genesis block.
Expand All @@ -49,7 +51,7 @@ impl ChainStateDefault of Default<ChainState> {
epoch_start_time: 1231006505,
prev_timestamps: [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1231006505
].span(),
].span(), median_time_past: 1231006505, // Genesis block MTP
}
}
}
Expand All @@ -62,9 +64,11 @@ pub impl BlockValidatorImpl of BlockValidator {
) -> Result<ChainState, ByteArray> {
let block_height = self.block_height + 1;

validate_timestamp(self.prev_timestamps, block.header.time)?;
let prev_block_time = *self.prev_timestamps[self.prev_timestamps.len() - 1];
let prev_timestamps = next_prev_timestamps(self.prev_timestamps, block.header.time);
let median_time_past = compute_median_time_past(prev_timestamps);

validate_timestamp(median_time_past, block.header.time)?;

let txid_root = match block.data {
TransactionData::MerkleRoot(root) => root,
Expand Down Expand Up @@ -101,6 +105,7 @@ pub impl BlockValidatorImpl of BlockValidator {
current_target,
epoch_start_time,
prev_timestamps,
median_time_past,
}
)
}
Expand Down
8 changes: 4 additions & 4 deletions packages/consensus/src/validation/locktime.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ pub fn validate_absolute_locktime(
/// If relative locktime is enabled, ensure the input's locktime is respected.
/// https://learnmeabitcoin.com/technical/transaction/input/sequence/
pub fn validate_relative_locktime(
input: @TxIn, block_height: u32, block_time: u32
input: @TxIn, block_height: u32, median_time_past: u32
) -> Result<(), ByteArray> {
let sequence = *input.sequence;
if sequence & SEQUENCE_LOCKTIME_DISABLE_FLAG != 0 {
Expand All @@ -86,11 +86,11 @@ pub fn validate_relative_locktime(
// https://github.com/bitcoin/bitcoin/blob/712a2b5453cdf2568fece94b969d6e0923b6ba16/src/consensus/tx_verify.cpp#L74
let lock_time = value * 512;
let absolute_lock_time = *input.previous_output.median_time_past + lock_time;
if absolute_lock_time >= block_time {
if absolute_lock_time >= median_time_past {
return Result::Err(
format!(
"Relative time-based lock time is not respected: current time {}, outpoint time {}, lock time {} seconds",
block_time,
"Relative time-based lock time is not respected: current MTP {}, outpoint MTP {}, lock time {} seconds",
median_time_past,
*input.previous_output.median_time_past,
lock_time
)
Expand Down
95 changes: 46 additions & 49 deletions packages/consensus/src/validation/timestamp.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
//!
//! Read more: https://learnmeabitcoin.com/technical/block/time/

/// Check that the block time is greater than the median of the 11 most recent timestamps.
pub fn validate_timestamp(prev_timestamps: Span<u32>, block_time: u32) -> Result<(), ByteArray> {
/// Compute the Median Time Past (MTP) from the previous timestamps.
pub fn compute_median_time_past(prev_timestamps: Span<u32>) -> u32 {
// Sort the last 11 timestamps
// adapted from :
// https://github.com/keep-starknet-strange/alexandria/blob/main/packages/sorting/src/bubble_sort.cairo
Expand Down Expand Up @@ -37,10 +37,15 @@ pub fn validate_timestamp(prev_timestamps: Span<u32>, block_time: u32) -> Result
};
};

if block_time > *sorted_prev_timestamps.at(sorted_prev_timestamps.len() - 6) {
*sorted_prev_timestamps.at(sorted_prev_timestamps.len() - 6)
}

/// Check that the block time is greater than the Median Time Past (MTP).
pub fn validate_timestamp(median_time_past: u32, block_time: u32) -> Result<(), ByteArray> {
if block_time > median_time_past {
Result::Ok(())
} else {
Result::Err("Median time is greater than or equal to block's timestamp")
Result::Err("Median time past is greater than or equal to block's timestamp")
}
}

Expand All @@ -54,26 +59,37 @@ pub fn next_prev_timestamps(prev_timestamps: Span<u32>, block_time: u32) -> Span

#[cfg(test)]
mod tests {
use super::{validate_timestamp, next_prev_timestamps};
use super::{compute_median_time_past, validate_timestamp, next_prev_timestamps};

#[test]
fn test_validate_timestamp() {
fn test_compute_median_time_past() {
let prev_timestamps = array![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].span();
let mut block_time = 12_u32;
let mtp = compute_median_time_past(prev_timestamps);
assert_eq!(mtp, 6, "Expected MTP to be 6");

let unsorted_timestamps = array![1, 3, 2, 5, 4, 6, 8, 7, 9, 10, 11].span();
let mtp = compute_median_time_past(unsorted_timestamps);
assert_eq!(mtp, 6, "Expected MTP to be 6 for unsorted");
}

// new timestamp is greater than the last timestamp
let result = validate_timestamp(prev_timestamps, block_time);
assert(result.is_ok(), 'Expected target to be valid');
#[test]
fn test_validate_timestamp() {
let mtp = 6_u32;
let mut block_time = 7_u32;

// new timestamp is strictly greater than the median of the last 11 timestamps
block_time = 7;
let result = validate_timestamp(prev_timestamps, block_time);
assert(result.is_ok(), 'Expected target to be valid');
// new timestamp is greater than MTP
let result = validate_timestamp(mtp, block_time);
assert(result.is_ok(), 'Expected timestamp to be valid');

// new timestamp is equal to the median of the last 11 timestamps
// new timestamp is equal to MTP
block_time = 6;
let result = validate_timestamp(prev_timestamps, block_time);
assert!(result.is_err(), "Median time is greater than block's timestamp");
let result = validate_timestamp(mtp, block_time);
assert!(result.is_err(), "MTP is greater than or equal to block's timestamp");

// new timestamp is less than MTP
block_time = 5;
let result = validate_timestamp(mtp, block_time);
assert!(result.is_err(), "MTP is greater than block's timestamp");
}

#[test]
Expand All @@ -87,42 +103,23 @@ mod tests {
}

#[test]
fn test_validate_timestamp_single_unchronological() {
let prev_timestamps = array![1, 2, 3, 4, 5, 7, 6, 8, 9, 10, 11].span();
let mut block_time = 12_u32;

// new timestamp is greater than the last timestamp
let result = validate_timestamp(prev_timestamps, block_time);
assert(result.is_ok(), 'Expected target to be valid');

// new timestamp is strictly greater than the median of the last 11 timestamps(sorted)
block_time = 7;
let result = validate_timestamp(prev_timestamps, block_time);
assert(result.is_ok(), 'Expected target to be valid');

// new timestamp is equal to the median of the last 11 timestamps(sorted)
block_time = 6;
let result = validate_timestamp(prev_timestamps, block_time);
assert!(result.is_err(), "Median time is greater than block's timestamp");
}

#[test]
fn test_validate_timestamp_unsorted() {
fn test_validate_timestamp_with_unsorted_input() {
let prev_timestamps = array![1, 3, 2, 5, 4, 6, 8, 7, 9, 10, 11].span();
let mtp = compute_median_time_past(prev_timestamps);
let mut block_time = 12_u32;

// new timestamp is greater than the last timestamp
let result = validate_timestamp(prev_timestamps, block_time);
assert(result.is_ok(), 'Expected target to be valid');
// new timestamp is greater than MTP
let result = validate_timestamp(mtp, block_time);
assert(result.is_ok(), 'Expected timestamp to be valid');

// new timestamp is strictly greater than the median of the last 11 timestamps(sorted)
block_time = 7;
let result = validate_timestamp(prev_timestamps, block_time);
assert(result.is_ok(), 'Expected target to be valid');

// new timestamp is equal to the median of the last 11 timestamps(sorted)
// new timestamp is equal to MTP
block_time = 6;
let result = validate_timestamp(prev_timestamps, block_time);
assert!(result.is_err(), "Median time is greater than block's timestamp");
let result = validate_timestamp(mtp, block_time);
assert!(result.is_err(), "MTP is greater than or equal to block's timestamp");

// new timestamp is less than MTP
block_time = 5;
let result = validate_timestamp(mtp, block_time);
assert!(result.is_err(), "MTP is greater than block's timestamp");
}
}

0 comments on commit 91e2c60

Please sign in to comment.