From 9711a6b89378a4b19b0d16d4ae1383ed0cda4021 Mon Sep 17 00:00:00 2001 From: Yoav Gross Date: Thu, 9 May 2024 14:09:27 +0300 Subject: [PATCH] feat: calculate commitments within the block --- src/block_hash.rs | 3 + src/block_hash/block_hash_calculator.rs | 76 ++++++++++++++++++- src/block_hash/event_commitment.rs | 4 +- src/block_hash/event_commitment_test.rs | 12 +-- src/block_hash/receipt_commitment.rs | 45 ++++++----- src/block_hash/receipt_commitment_test.rs | 56 +++----------- src/block_hash/test_utils.rs | 39 ++++++++++ src/block_hash/transaction_commitment.rs | 20 +++-- src/block_hash/transaction_commitment_test.rs | 34 ++++++--- 9 files changed, 196 insertions(+), 93 deletions(-) create mode 100644 src/block_hash/test_utils.rs 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..0b9c926 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::transaction_commitment::{calculate_transactions_commitment, TransactionLeafElement}; +use crate::block::GasPricePerToken; +use crate::core::{EventCommitment, ReceiptCommitment, TransactionCommitment}; use crate::data_availability::L1DataAvailabilityMode; -use crate::hash::StarkFelt; +use crate::hash::{PoseidonHashCalculator, StarkFelt}; +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, +} + +#[allow(dead_code)] +struct BlockHeaderCommitments { + pub n_transactions: usize, + pub transactions_commitment: TransactionCommitment, + pub n_events: usize, + pub events_commitment: EventCommitment, + pub receipts_commitment: ReceiptCommitment, +} + +// Calculates the commitments of the transactions data for the block hash. +#[allow(dead_code)] +fn calculate_block_commitments( + transactions_data: &[TransactionHashingData], + l1_data_gas_price_per_token: GasPricePerToken, + l1_gas_price_per_token: GasPricePerToken, +) -> BlockHeaderCommitments { + let transaction_leaf_elements: Vec = transactions_data + .iter() + .map(|transaction_data| TransactionLeafElement { + transaction_hash: transaction_data.transaction_hash, + transaction_signature: transaction_data.transaction_signature.clone(), + }) + .collect(); + let transactions_commitment = + calculate_transactions_commitment::(&transaction_leaf_elements); + + let mut event_leaf_elements: Vec = Vec::new(); + for transaction in transactions_data { + let transaction_hash = transaction.transaction_hash; + for event in transaction.transaction_output.events() { + let event_leaf_element = EventLeafElement { event: event.clone(), transaction_hash }; + event_leaf_elements.push(event_leaf_element); + } + } + let events_commitment = + calculate_events_commitment::(&event_leaf_elements); + + let receipt_elements: Vec = transactions_data + .iter() + .map(|transaction_data| ReceiptElement { + transaction_hash: transaction_data.transaction_hash, + transaction_output: transaction_data.transaction_output.clone(), + transaction_version: transaction_data.transaction_version, + }) + .collect(); + let receipts_commitment = calculate_receipt_commitment::( + &receipt_elements, + l1_data_gas_price_per_token, + l1_gas_price_per_token, + ); + BlockHeaderCommitments { + n_transactions: transactions_data.len(), + transactions_commitment, + n_events: event_leaf_elements.len(), + events_commitment, + receipts_commitment, + } +} + // 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..115c22a 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 event: Event, + pub 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..a166993 100644 --- a/src/block_hash/event_commitment_test.rs +++ b/src/block_hash/event_commitment_test.rs @@ -1,9 +1,9 @@ -use super::calculate_event_hash; -use crate::block_hash::event_commitment::{calculate_events_commitment, EventLeafElement}; -use crate::core::{ContractAddress, EventCommitment, PatriciaKey}; -use crate::hash::{PoseidonHashCalculator, StarkFelt, StarkHash}; +use super::{calculate_event_hash, EventLeafElement}; +use crate::block_hash::event_commitment::calculate_events_commitment; +use crate::core::{ContractAddress, EventCommitment}; +use crate::hash::{PoseidonHashCalculator, StarkFelt}; +use crate::stark_felt; use crate::transaction::{Event, EventContent, EventData, EventKey, TransactionHash}; -use crate::{contract_address, patricia_key, stark_felt}; #[test] fn test_events_commitment_regression() { @@ -34,7 +34,7 @@ fn test_event_hash_regression() { fn get_event_leaf_element(seed: u8) -> EventLeafElement { EventLeafElement { event: Event { - from_address: contract_address!(seed + 8), + from_address: ContractAddress::from(seed + 8), content: EventContent { keys: [seed, seed + 1].iter().map(|key| EventKey(stark_felt!(*key))).collect(), data: EventData( diff --git a/src/block_hash/receipt_commitment.rs b/src/block_hash/receipt_commitment.rs index b704c1c..4c391fd 100644 --- a/src/block_hash/receipt_commitment.rs +++ b/src/block_hash/receipt_commitment.rs @@ -4,31 +4,33 @@ 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, +} + /// 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 +41,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 aa7a224..4f19d8e 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(456), 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,10 @@ fn test_receipt_hash_regression() { StarkFelt::try_from("0x071cd6ee5b2a9686d0cf42818922b632cbf6940892b47ea2ac454b9938e929a0") .unwrap(), ); + transaction_receipt.transaction_version = TransactionVersion::THREE; assert_eq!( calculate_receipt_commitment::( &[transaction_receipt], - &TransactionVersion::THREE, l1_data_gas_price, l1_gas_price ), @@ -84,14 +56,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 b56b7fb..0347e48 100644 --- a/src/block_hash/transaction_commitment.rs +++ b/src/block_hash/transaction_commitment.rs @@ -10,24 +10,32 @@ mod transaction_commitment_test; /// The elements used to calculate a leaf in the transactions Patricia tree. #[derive(Clone)] -pub struct TransactionLeafElements { - transaction_hash: TransactionHash, - transaction_signature: TransactionSignature, +pub struct TransactionLeafElement { + pub transaction_hash: TransactionHash, + pub transaction_signature: Option, } /// 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). pub fn calculate_transactions_commitment( - transaction_leaf_elements: &[TransactionLeafElements], + transaction_leaf_elements: &[TransactionLeafElement], ) -> TransactionCommitment { let transaction_leaves = transaction_leaf_elements.iter().map(calculate_transaction_leaf).collect(); TransactionCommitment(calculate_root::(transaction_leaves)) } -fn calculate_transaction_leaf(transaction_leaf_elements: &TransactionLeafElements) -> StarkFelt { +fn calculate_transaction_leaf(transaction_leaf_elements: &TransactionLeafElement) -> StarkFelt { HashChain::new() .chain(&transaction_leaf_elements.transaction_hash.0) - .chain_iter(transaction_leaf_elements.transaction_signature.0.iter()) + .chain_iter( + transaction_leaf_elements + .transaction_signature + .as_ref() + .unwrap_or(&TransactionSignature(vec![StarkFelt::ZERO])) + .0 + .iter(), + ) .get_poseidon_hash() } diff --git a/src/block_hash/transaction_commitment_test.rs b/src/block_hash/transaction_commitment_test.rs index e4f0623..1f7131e 100644 --- a/src/block_hash/transaction_commitment_test.rs +++ b/src/block_hash/transaction_commitment_test.rs @@ -1,5 +1,6 @@ +use super::TransactionLeafElement; use crate::block_hash::transaction_commitment::{ - calculate_transaction_leaf, calculate_transactions_commitment, TransactionLeafElements, + calculate_transaction_leaf, calculate_transactions_commitment, }; use crate::core::TransactionCommitment; use crate::hash::{PoseidonHashCalculator, StarkFelt}; @@ -7,11 +8,7 @@ use crate::transaction::{TransactionHash, TransactionSignature}; #[test] fn test_transaction_leaf_regression() { - let transaction_hash = TransactionHash(StarkFelt::ONE); - let transaction_signature = TransactionSignature(vec![StarkFelt::TWO, StarkFelt::THREE]); - let transaction_leaf_elements = - TransactionLeafElements { transaction_hash, transaction_signature }; - + let transaction_leaf_elements = get_transaction_leaf_element(); let expected_leaf = StarkFelt::try_from("0x2f0d8840bcf3bc629598d8a6cc80cb7c0d9e52d93dab244bbf9cd0dca0ad082") .unwrap(); @@ -20,12 +17,21 @@ fn test_transaction_leaf_regression() { } #[test] -fn test_transactions_commitment_regression() { - let transaction_hash = TransactionHash(StarkFelt::ONE); - let transaction_signature = TransactionSignature(vec![StarkFelt::TWO, StarkFelt::THREE]); - let transaction_leaf_elements = - TransactionLeafElements { transaction_hash, transaction_signature }; +fn test_transaction_leaf_without_signature_regression() { + let transaction_leaf_elements = TransactionLeafElement { + transaction_hash: TransactionHash(StarkFelt::ONE), + transaction_signature: None, + }; + let expected_leaf = + StarkFelt::try_from("0x00a93bf5e58b9378d093aa86ddc2f61a3295a1d1e665bd0ef3384dd07b30e033") + .unwrap(); + assert_eq!(expected_leaf, calculate_transaction_leaf(&transaction_leaf_elements)); +} + +#[test] +fn test_transactions_commitment_regression() { + let transaction_leaf_elements = get_transaction_leaf_element(); let expected_root = StarkFelt::try_from("0x0282b635972328bd1cfa86496fe920d20bd9440cd78ee8dc90ae2b383d664dcf") .unwrap(); @@ -38,3 +44,9 @@ fn test_transactions_commitment_regression() { ],) ); } + +fn get_transaction_leaf_element() -> TransactionLeafElement { + let transaction_hash = TransactionHash(StarkFelt::ONE); + let transaction_signature = TransactionSignature(vec![StarkFelt::TWO, StarkFelt::THREE]); + TransactionLeafElement { transaction_hash, transaction_signature: Some(transaction_signature) } +}