use starknet_types_core::felt::Felt;
use starknet_types_core::hash::StarkHash;
use super::block_hash_calculator::{TransactionHashingData, TransactionOutputForHash};
use crate::core::ReceiptCommitment;
use crate::crypto::patricia_hash::calculate_root;
use crate::crypto::utils::HashChain;
use crate::execution_resources::GasVector;
use crate::hash::starknet_keccak_hash;
use crate::transaction::{MessageToL1, TransactionExecutionStatus, TransactionHash};
#[cfg(test)]
#[path = "receipt_commitment_test.rs"]
mod receipt_commitment_test;
#[derive(Clone)]
pub struct ReceiptElement {
pub transaction_hash: TransactionHash,
pub transaction_output: TransactionOutputForHash,
}
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(),
}
}
}
pub fn calculate_receipt_commitment<H: StarkHash>(
receipt_elements: &[ReceiptElement],
) -> ReceiptCommitment {
ReceiptCommitment(calculate_root::<H>(
receipt_elements.iter().map(calculate_receipt_hash).collect(),
))
}
fn calculate_receipt_hash(receipt_element: &ReceiptElement) -> Felt {
let hash_chain = HashChain::new()
.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_gas_consumed(hash_chain, &receipt_element.transaction_output.gas_consumed)
.get_poseidon_hash()
}
fn calculate_messages_sent_hash(messages_sent: &Vec<MessageToL1>) -> Felt {
let mut messages_hash_chain = HashChain::new().chain(&messages_sent.len().into());
for message_sent in messages_sent {
messages_hash_chain = messages_hash_chain
.chain(&message_sent.from_address)
.chain(&message_sent.to_address.into())
.chain_size_and_elements(&message_sent.payload.0);
}
messages_hash_chain.get_poseidon_hash()
}
fn get_revert_reason_hash(execution_status: &TransactionExecutionStatus) -> Felt {
match execution_status {
TransactionExecutionStatus::Succeeded => Felt::ZERO,
TransactionExecutionStatus::Reverted(reason) => {
starknet_keccak_hash(reason.revert_reason.as_bytes())
}
}
}
fn chain_gas_consumed(hash_chain: HashChain, gas_consumed: &GasVector) -> HashChain {
hash_chain
.chain(&Felt::ZERO) .chain(&gas_consumed.l1_gas.into())
.chain(&gas_consumed.l1_data_gas.into())
}