Skip to content

Commit

Permalink
feat: calculate commitments within the block (#251)
Browse files Browse the repository at this point in the history
* fix: support txs without signature

* feat: calculate commitments within the block
  • Loading branch information
yoavGrs authored May 21, 2024
1 parent 64dd7ca commit f20bf15
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 90 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::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

0 comments on commit f20bf15

Please sign in to comment.