diff --git a/src/block_hash.rs b/src/block_hash.rs index 879a196..6a47694 100644 --- a/src/block_hash.rs +++ b/src/block_hash.rs @@ -3,3 +3,6 @@ pub mod event_commitment; pub mod receipt_commitment; pub mod state_diff_hash; pub mod transaction_commitment; + +#[cfg(test)] +pub mod test_utils; diff --git a/src/block_hash/block_hash_calculator.rs b/src/block_hash/block_hash_calculator.rs index 1b5f544..cd6ea42 100644 --- a/src/block_hash/block_hash_calculator.rs +++ b/src/block_hash/block_hash_calculator.rs @@ -1,10 +1,84 @@ +use super::event_commitment::{calculate_events_commitment, EventLeafElement}; +use super::receipt_commitment::{calculate_receipt_commitment, ReceiptElement}; +use super::state_diff_hash::calculate_state_diff_hash; +use super::transaction_commitment::{calculate_transactions_commitment, TransactionLeafElement}; +use crate::block::GasPricePerToken; +use crate::core::{EventCommitment, ReceiptCommitment, StateDiffCommitment, TransactionCommitment}; use crate::data_availability::L1DataAvailabilityMode; -use crate::hash::StarkFelt; +use crate::hash::{PoseidonHashCalculator, StarkFelt}; +use crate::state::ThinStateDiff; +use crate::transaction::{ + TransactionHash, TransactionOutput, TransactionSignature, TransactionVersion, +}; #[cfg(test)] #[path = "block_hash_calculator_test.rs"] mod block_hash_calculator_test; +pub struct TransactionHashingData { + pub transaction_signature: Option, + pub transaction_output: TransactionOutput, + pub transaction_hash: TransactionHash, + pub transaction_version: TransactionVersion, +} + +/// Commitments of a block. +pub struct BlockHeaderCommitments { + pub transactions_commitment: TransactionCommitment, + pub events_commitment: EventCommitment, + pub receipts_commitment: ReceiptCommitment, + pub state_diff_commitment: StateDiffCommitment, + pub concated_counts: StarkFelt, +} + +/// Calculates the commitments of the transactions data for the block hash. +pub fn calculate_block_commitments( + transactions_data: &[TransactionHashingData], + state_diff: &ThinStateDiff, + l1_data_gas_price_per_token: GasPricePerToken, + l1_gas_price_per_token: GasPricePerToken, + l1_da_mode: L1DataAvailabilityMode, +) -> BlockHeaderCommitments { + let transaction_leaf_elements: Vec = + transactions_data.iter().map(TransactionLeafElement::from).collect(); + let transactions_commitment = + calculate_transactions_commitment::(&transaction_leaf_elements); + + let event_leaf_elements: Vec = transactions_data + .iter() + .flat_map(|transaction_data| { + transaction_data.transaction_output.events().iter().map(|event| EventLeafElement { + event: event.clone(), + transaction_hash: transaction_data.transaction_hash, + }) + }) + .collect(); + let events_commitment = + calculate_events_commitment::(&event_leaf_elements); + + let receipt_elements: Vec = + transactions_data.iter().map(ReceiptElement::from).collect(); + let receipts_commitment = calculate_receipt_commitment::( + &receipt_elements, + l1_data_gas_price_per_token, + l1_gas_price_per_token, + ); + let state_diff_commitment = calculate_state_diff_hash(state_diff); + let concated_counts = concat_counts( + transactions_data.len(), + event_leaf_elements.len(), + state_diff.len(), + l1_da_mode, + ); + BlockHeaderCommitments { + transactions_commitment, + events_commitment, + receipts_commitment, + state_diff_commitment, + concated_counts, + } +} + // A single felt: [ // transaction_count (64 bits) | event_count (64 bits) | state_diff_length (64 bits) // | L1 data availability mode: 0 for calldata, 1 for blob (1 bit) | 0 ... diff --git a/src/block_hash/event_commitment.rs b/src/block_hash/event_commitment.rs index 53c4ae3..f0e66b2 100644 --- a/src/block_hash/event_commitment.rs +++ b/src/block_hash/event_commitment.rs @@ -11,8 +11,8 @@ mod event_commitment_test; /// The elements used to calculate a leaf in the transactions Patricia tree. #[derive(Clone)] pub struct EventLeafElement { - event: Event, - transaction_hash: TransactionHash, + pub(crate) event: Event, + pub(crate) transaction_hash: TransactionHash, } /// Returns the root of a Patricia tree where each leaf is an event hash. diff --git a/src/block_hash/event_commitment_test.rs b/src/block_hash/event_commitment_test.rs index 7e95b85..80c0739 100644 --- a/src/block_hash/event_commitment_test.rs +++ b/src/block_hash/event_commitment_test.rs @@ -1,5 +1,5 @@ -use super::calculate_event_hash; -use crate::block_hash::event_commitment::{calculate_events_commitment, EventLeafElement}; +use super::{calculate_event_hash, EventLeafElement}; +use crate::block_hash::event_commitment::calculate_events_commitment; use crate::core::{ContractAddress, EventCommitment, PatriciaKey}; use crate::hash::{PoseidonHashCalculator, StarkFelt, StarkHash}; use crate::transaction::{Event, EventContent, EventData, EventKey, TransactionHash}; diff --git a/src/block_hash/receipt_commitment.rs b/src/block_hash/receipt_commitment.rs index 7f070cb..8924509 100644 --- a/src/block_hash/receipt_commitment.rs +++ b/src/block_hash/receipt_commitment.rs @@ -1,34 +1,47 @@ +use super::block_hash_calculator::TransactionHashingData; use crate::block::{GasPrice, GasPricePerToken}; use crate::core::ReceiptCommitment; use crate::crypto::patricia_hash::calculate_root; use crate::crypto::utils::HashChain; use crate::hash::{starknet_keccak_hash, HashFunction, StarkFelt}; use crate::transaction::{ - ExecutionResources, Fee, MessageToL1, TransactionExecutionStatus, TransactionReceipt, - TransactionVersion, + ExecutionResources, Fee, MessageToL1, TransactionExecutionStatus, TransactionHash, + TransactionOutput, TransactionVersion, }; #[cfg(test)] #[path = "receipt_commitment_test.rs"] mod receipt_commitment_test; +// The elements used to calculate a leaf in the transactions Patricia tree. +#[derive(Clone)] +pub struct ReceiptElement { + pub transaction_hash: TransactionHash, + pub transaction_output: TransactionOutput, + pub transaction_version: TransactionVersion, +} + +impl From<&TransactionHashingData> for ReceiptElement { + fn from(transaction_data: &TransactionHashingData) -> Self { + ReceiptElement { + transaction_hash: transaction_data.transaction_hash, + transaction_output: transaction_data.transaction_output.clone(), + transaction_version: transaction_data.transaction_version, + } + } +} + /// Returns the root of a Patricia tree where each leaf is a receipt hash. pub fn calculate_receipt_commitment( - transactions_receipt: &[TransactionReceipt], - transaction_version: &TransactionVersion, + receipt_elements: &[ReceiptElement], l1_data_gas_price_per_token: GasPricePerToken, l1_gas_price_per_token: GasPricePerToken, ) -> ReceiptCommitment { ReceiptCommitment(calculate_root::( - transactions_receipt + receipt_elements .iter() .map(|receipt| { - calculate_receipt_hash( - receipt, - transaction_version, - l1_data_gas_price_per_token, - l1_gas_price_per_token, - ) + calculate_receipt_hash(receipt, l1_data_gas_price_per_token, l1_gas_price_per_token) }) .collect(), )) @@ -39,22 +52,23 @@ pub fn calculate_receipt_commitment( // execution resources // ). fn calculate_receipt_hash( - transaction_receipt: &TransactionReceipt, - transaction_version: &TransactionVersion, + receipt_element: &ReceiptElement, l1_data_gas_price_per_token: GasPricePerToken, l1_gas_price_per_token: GasPricePerToken, ) -> StarkFelt { - let l1_gas_price = get_price_by_version(l1_gas_price_per_token, transaction_version); - let l1_data_gas_price = get_price_by_version(l1_data_gas_price_per_token, transaction_version); + let l1_gas_price = + get_price_by_version(l1_gas_price_per_token, &receipt_element.transaction_version); + let l1_data_gas_price = + get_price_by_version(l1_data_gas_price_per_token, &receipt_element.transaction_version); let hash_chain = HashChain::new() - .chain(&transaction_receipt.transaction_hash) - .chain(&transaction_receipt.output.actual_fee().0.into()) - .chain(&calculate_messages_sent_hash(transaction_receipt.output.messages_sent())) - .chain(&get_revert_reason_hash(transaction_receipt.output.execution_status())); + .chain(&receipt_element.transaction_hash) + .chain(&receipt_element.transaction_output.actual_fee().0.into()) + .chain(&calculate_messages_sent_hash(receipt_element.transaction_output.messages_sent())) + .chain(&get_revert_reason_hash(receipt_element.transaction_output.execution_status())); chain_execution_resources( hash_chain, - transaction_receipt.output.execution_resources(), - transaction_receipt.output.actual_fee(), + receipt_element.transaction_output.execution_resources(), + receipt_element.transaction_output.actual_fee(), l1_data_gas_price, l1_gas_price, ) diff --git a/src/block_hash/receipt_commitment_test.rs b/src/block_hash/receipt_commitment_test.rs index 037b6bf..eb49f7b 100644 --- a/src/block_hash/receipt_commitment_test.rs +++ b/src/block_hash/receipt_commitment_test.rs @@ -1,45 +1,22 @@ -use std::collections::HashMap; - -use primitive_types::H160; - use super::calculate_messages_sent_hash; -use crate::block::{BlockHash, BlockNumber, GasPrice, GasPricePerToken}; +use crate::block::{GasPrice, GasPricePerToken}; use crate::block_hash::receipt_commitment::{ - calculate_receipt_commitment, calculate_receipt_hash, get_revert_reason_hash, + calculate_receipt_commitment, calculate_receipt_hash, get_revert_reason_hash, ReceiptElement, }; -use crate::core::{ContractAddress, EthAddress, ReceiptCommitment}; +use crate::block_hash::test_utils::{generate_message_to_l1, get_transaction_output}; +use crate::core::ReceiptCommitment; use crate::hash::{PoseidonHashCalculator, StarkFelt}; use crate::transaction::{ - Builtin, ExecutionResources, Fee, InvokeTransactionOutput, L2ToL1Payload, MessageToL1, RevertedTransactionExecutionStatus, TransactionExecutionStatus, TransactionHash, - TransactionOutput, TransactionReceipt, TransactionVersion, + TransactionVersion, }; #[test] fn test_receipt_hash_regression() { - let execution_status = - TransactionExecutionStatus::Reverted(RevertedTransactionExecutionStatus { - revert_reason: "aborted".to_string(), - }); - let execution_resources = ExecutionResources { - steps: 98, - builtin_instance_counter: HashMap::from([(Builtin::Bitwise, 11), (Builtin::EcOp, 22)]), - memory_holes: 76, - da_l1_gas_consumed: 54, - da_l1_data_gas_consumed: 32, - }; - let invoke_output = TransactionOutput::Invoke(InvokeTransactionOutput { - actual_fee: Fee(99804), - messages_sent: vec![generate_message_to_l1(34), generate_message_to_l1(56)], - events: vec![], - execution_status, - execution_resources, - }); - let transaction_receipt = TransactionReceipt { + let mut transaction_receipt = ReceiptElement { transaction_hash: TransactionHash(StarkFelt::from(1234_u16)), - block_hash: BlockHash(StarkFelt::from(5678_u16)), - block_number: BlockNumber(99), - output: invoke_output, + transaction_output: get_transaction_output(), + transaction_version: TransactionVersion::TWO, }; let l1_data_gas_price = GasPricePerToken { price_in_fri: GasPrice(123), price_in_wei: GasPrice(456) }; @@ -50,12 +27,7 @@ fn test_receipt_hash_regression() { StarkFelt::try_from("0x06cb27bfc55dee54e6d0fc7a6790e39f0f3c003576d50f7b8e8a1be24c351bcf") .unwrap(); assert_eq!( - calculate_receipt_hash( - &transaction_receipt, - &TransactionVersion::TWO, - l1_data_gas_price, - l1_gas_price - ), + calculate_receipt_hash(&transaction_receipt, l1_data_gas_price, l1_gas_price), expected_hash ); @@ -63,10 +35,12 @@ fn test_receipt_hash_regression() { StarkFelt::try_from("0x03a0af1272fc3b0b83894fd7b6b70d89acb07772bc28efc9091e3cc1c2c72493") .unwrap(), ); + + // Test for a V3 transactions. + transaction_receipt.transaction_version = TransactionVersion::THREE; assert_eq!( calculate_receipt_commitment::( &[transaction_receipt], - &TransactionVersion::THREE, l1_data_gas_price, l1_gas_price ), @@ -84,14 +58,6 @@ fn test_messages_sent_regression() { assert_eq!(messages_hash, expected_hash); } -fn generate_message_to_l1(seed: u64) -> MessageToL1 { - MessageToL1 { - from_address: ContractAddress::from(seed), - to_address: EthAddress(H160::from_low_u64_be(seed + 1)), - payload: L2ToL1Payload(vec![StarkFelt::from(seed + 2), StarkFelt::from(seed + 3)]), - } -} - #[test] fn test_revert_reason_hash_regression() { let execution_succeeded = TransactionExecutionStatus::Succeeded; diff --git a/src/block_hash/test_utils.rs b/src/block_hash/test_utils.rs new file mode 100644 index 0000000..d895d2d --- /dev/null +++ b/src/block_hash/test_utils.rs @@ -0,0 +1,39 @@ +use std::collections::HashMap; + +use primitive_types::H160; + +use crate::core::{ContractAddress, EthAddress}; +use crate::hash::StarkFelt; +use crate::transaction::{ + Builtin, ExecutionResources, Fee, InvokeTransactionOutput, L2ToL1Payload, MessageToL1, + RevertedTransactionExecutionStatus, TransactionExecutionStatus, TransactionOutput, +}; + +pub(crate) fn get_transaction_output() -> TransactionOutput { + let execution_status = + TransactionExecutionStatus::Reverted(RevertedTransactionExecutionStatus { + revert_reason: "aborted".to_string(), + }); + let execution_resources = ExecutionResources { + steps: 98, + builtin_instance_counter: HashMap::from([(Builtin::Bitwise, 11), (Builtin::EcOp, 22)]), + memory_holes: 76, + da_l1_gas_consumed: 54, + da_l1_data_gas_consumed: 32, + }; + TransactionOutput::Invoke(InvokeTransactionOutput { + actual_fee: Fee(99804), + messages_sent: vec![generate_message_to_l1(34), generate_message_to_l1(56)], + events: vec![], + execution_status, + execution_resources, + }) +} + +pub(crate) fn generate_message_to_l1(seed: u64) -> MessageToL1 { + MessageToL1 { + from_address: ContractAddress::from(seed), + to_address: EthAddress(H160::from_low_u64_be(seed + 1)), + payload: L2ToL1Payload(vec![StarkFelt::from(seed + 2), StarkFelt::from(seed + 3)]), + } +} diff --git a/src/block_hash/transaction_commitment.rs b/src/block_hash/transaction_commitment.rs index 0347e48..0e3ae5d 100644 --- a/src/block_hash/transaction_commitment.rs +++ b/src/block_hash/transaction_commitment.rs @@ -1,3 +1,4 @@ +use super::block_hash_calculator::TransactionHashingData; use crate::core::TransactionCommitment; use crate::crypto::patricia_hash::calculate_root; use crate::crypto::utils::HashChain; @@ -11,13 +12,22 @@ mod transaction_commitment_test; /// The elements used to calculate a leaf in the transactions Patricia tree. #[derive(Clone)] pub struct TransactionLeafElement { - pub transaction_hash: TransactionHash, - pub transaction_signature: Option, + pub(crate) transaction_hash: TransactionHash, + pub(crate) transaction_signature: Option, +} + +impl From<&TransactionHashingData> for TransactionLeafElement { + fn from(transaction_data: &TransactionHashingData) -> Self { + TransactionLeafElement { + transaction_hash: transaction_data.transaction_hash, + transaction_signature: transaction_data.transaction_signature.clone(), + } + } } /// Returns the root of a Patricia tree where each leaf is -/// Poseidon(transaction_hash, transaction_signature). -/// The leaf of a transaction types without a signature field is: Poseidon(transaction_hash, 0). +/// H(transaction_hash, transaction_signature). +/// The leaf of a transaction types without a signature field is: H(transaction_hash, 0). pub fn calculate_transactions_commitment( transaction_leaf_elements: &[TransactionLeafElement], ) -> TransactionCommitment {