Skip to content

Commit

Permalink
feat: calculate commitments within the block
Browse files Browse the repository at this point in the history
  • Loading branch information
yoavGrs committed May 20, 2024
1 parent d4f5e94 commit 9711a6b
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 93 deletions.
3 changes: 3 additions & 0 deletions src/block_hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
76 changes: 75 additions & 1 deletion src/block_hash/block_hash_calculator.rs
Original file line number Diff line number Diff line change
@@ -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<TransactionSignature>,
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<TransactionLeafElement> = 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::<PoseidonHashCalculator>(&transaction_leaf_elements);

let mut event_leaf_elements: Vec<EventLeafElement> = 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::<PoseidonHashCalculator>(&event_leaf_elements);

let receipt_elements: Vec<ReceiptElement> = 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::<PoseidonHashCalculator>(
&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 ...
Expand Down
4 changes: 2 additions & 2 deletions src/block_hash/event_commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
12 changes: 6 additions & 6 deletions src/block_hash/event_commitment_test.rs
Original file line number Diff line number Diff line change
@@ -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() {
Expand Down Expand Up @@ -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(
Expand Down
45 changes: 24 additions & 21 deletions src/block_hash/receipt_commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<H: HashFunction>(
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::<H>(
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(),
))
Expand All @@ -39,22 +41,23 @@ pub fn calculate_receipt_commitment<H: HashFunction>(
// 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,
)
Expand Down
56 changes: 10 additions & 46 deletions src/block_hash/receipt_commitment_test.rs
Original file line number Diff line number Diff line change
@@ -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) };
Expand All @@ -50,23 +27,18 @@ 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
);

let expected_root = ReceiptCommitment(
StarkFelt::try_from("0x071cd6ee5b2a9686d0cf42818922b632cbf6940892b47ea2ac454b9938e929a0")
.unwrap(),
);
transaction_receipt.transaction_version = TransactionVersion::THREE;
assert_eq!(
calculate_receipt_commitment::<PoseidonHashCalculator>(
&[transaction_receipt],
&TransactionVersion::THREE,
l1_data_gas_price,
l1_gas_price
),
Expand All @@ -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;
Expand Down
39 changes: 39 additions & 0 deletions src/block_hash/test_utils.rs
Original file line number Diff line number Diff line change
@@ -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)]),
}
}
20 changes: 14 additions & 6 deletions src/block_hash/transaction_commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<TransactionSignature>,
}

/// 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<H: HashFunction>(
transaction_leaf_elements: &[TransactionLeafElements],
transaction_leaf_elements: &[TransactionLeafElement],
) -> TransactionCommitment {
let transaction_leaves =
transaction_leaf_elements.iter().map(calculate_transaction_leaf).collect();
TransactionCommitment(calculate_root::<H>(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()
}
Loading

0 comments on commit 9711a6b

Please sign in to comment.