Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: calculate commitments within the block #251

Merged
merged 2 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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::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<TransactionSignature>,
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 concatenated_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<TransactionLeafElement> =
transactions_data.iter().map(TransactionLeafElement::from).collect();
let transactions_commitment =
calculate_transactions_commitment::<PoseidonHashCalculator>(&transaction_leaf_elements);

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

let receipt_elements: Vec<ReceiptElement> =
transactions_data.iter().map(ReceiptElement::from).collect();
let receipts_commitment = calculate_receipt_commitment::<PoseidonHashCalculator>(
&receipt_elements,
l1_data_gas_price_per_token,
l1_gas_price_per_token,
);
let state_diff_commitment = calculate_state_diff_hash(state_diff);
let concatenated_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,
concatenated_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 ...
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(crate) event: Event,
pub(crate) transaction_hash: TransactionHash,
}

/// Returns the root of a Patricia tree where each leaf is an event hash.
Expand Down
4 changes: 2 additions & 2 deletions src/block_hash/event_commitment_test.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down
56 changes: 35 additions & 21 deletions src/block_hash/receipt_commitment.rs
Original file line number Diff line number Diff line change
@@ -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 {
Self {
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<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 +52,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
58 changes: 12 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(123), price_in_wei: GasPrice(456) };
Expand All @@ -50,23 +27,20 @@ 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("0x03a0af1272fc3b0b83894fd7b6b70d89acb07772bc28efc9091e3cc1c2c72493")
.unwrap(),
);

// Test for a V3 transactions.
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 +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;
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)]),
}
}
32 changes: 25 additions & 7 deletions src/block_hash/transaction_commitment.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -10,24 +11,41 @@ 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(crate) transaction_hash: TransactionHash,
pub(crate) transaction_signature: Option<TransactionSignature>,
}

impl From<&TransactionHashingData> for TransactionLeafElement {
fn from(transaction_data: &TransactionHashingData) -> Self {
Self {
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).
/// 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<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
Loading