From d01d35bef2cf1198b9a0bb0248c443ed0f03d34a Mon Sep 17 00:00:00 2001 From: Mohammad Nassar Date: Sun, 7 Jan 2024 12:12:11 +0200 Subject: [PATCH] TransactionExecutionInfo: add serialize --- crates/blockifier/src/execution/call_info.rs | 43 ++++++++++++++++--- .../deprecated_entry_point_execution.rs | 2 +- .../deprecated_syscalls_test.rs | 13 +++--- .../blockifier/src/execution/entry_point.rs | 5 ++- .../src/execution/entry_point_execution.rs | 2 +- .../src/execution/entry_point_test.rs | 1 + .../src/execution/syscalls/syscalls_test.rs | 8 ++-- crates/blockifier/src/test_utils/prices.rs | 1 + crates/blockifier/src/transaction/objects.rs | 5 ++- .../src/transaction/transactions_test.rs | 15 ++++--- .../src/py_block_executor.rs | 2 +- .../src/py_transaction_execution_info.rs | 2 +- crates/native_blockifier/src/py_validator.rs | 5 ++- .../src/transaction_executor.rs | 6 ++- 14 files changed, 76 insertions(+), 34 deletions(-) diff --git a/crates/blockifier/src/execution/call_info.rs b/crates/blockifier/src/execution/call_info.rs index 104b6bc4d6..477474929f 100644 --- a/crates/blockifier/src/execution/call_info.rs +++ b/crates/blockifier/src/execution/call_info.rs @@ -1,6 +1,8 @@ use std::collections::HashSet; use cairo_vm::vm::runners::cairo_runner::ExecutionResources as VmExecutionResources; +use serde::ser::SerializeStruct; +use serde::{Serialize, Serializer}; use starknet_api::core::{ClassHash, EthAddress}; use starknet_api::hash::StarkFelt; use starknet_api::state::StorageKey; @@ -11,7 +13,7 @@ use crate::state::cached_state::StorageEntry; use crate::transaction::errors::TransactionExecutionError; use crate::transaction::objects::TransactionExecutionResult; -#[derive(Clone, Debug, Default, Eq, PartialEq)] +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)] pub struct Retdata(pub Vec); #[macro_export] @@ -22,21 +24,21 @@ macro_rules! retdata { } #[cfg_attr(test, derive(Clone))] -#[derive(Debug, Default, Eq, PartialEq)] +#[derive(Debug, Default, Eq, PartialEq, Serialize)] pub struct OrderedEvent { pub order: usize, pub event: EventContent, } #[cfg_attr(test, derive(Clone))] -#[derive(Debug, Default, Eq, PartialEq)] +#[derive(Debug, Default, Eq, PartialEq, Serialize)] pub struct MessageToL1 { pub to_address: EthAddress, pub payload: L2ToL1Payload, } #[cfg_attr(test, derive(Clone))] -#[derive(Debug, Default, Eq, PartialEq)] +#[derive(Debug, Default, Eq, PartialEq, Serialize)] pub struct OrderedL2ToL1Message { pub order: usize, pub message: MessageToL1, @@ -44,7 +46,7 @@ pub struct OrderedL2ToL1Message { /// Represents the effects of executing a single entry point. #[cfg_attr(test, derive(Clone))] -#[derive(Debug, Default, Eq, PartialEq)] +#[derive(Debug, Default, Eq, PartialEq, Serialize)] pub struct CallExecution { pub retdata: Retdata, pub events: Vec, @@ -53,12 +55,39 @@ pub struct CallExecution { pub gas_consumed: u64, } +#[derive(Debug, Default, Eq, PartialEq, derive_more::Deref, derive_more::From)] +pub struct VmExecutionResourcesWrapper(pub VmExecutionResources); + +impl VmExecutionResourcesWrapper { + pub fn new(vm_execution_resources: VmExecutionResources) -> Self { + VmExecutionResourcesWrapper(vm_execution_resources) + } +} + +impl Serialize for VmExecutionResourcesWrapper { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + // Create a serialize struct with the appropriate number of fields. + let mut state = serializer.serialize_struct("VmExecutionResourcesWrapper", 3)?; + + // Serialize each field individually. + state.serialize_field("builtin_instance_counter", &self.0.builtin_instance_counter)?; + state.serialize_field("n_memory_holes", &self.0.n_memory_holes)?; + state.serialize_field("n_steps", &self.0.n_steps)?; + + // Finish serializing the struct. + state.end() + } +} + /// Represents the full effects of executing an entry point, including the inner calls it invoked. -#[derive(Debug, Default, Eq, PartialEq)] +#[derive(Debug, Default, Eq, PartialEq, Serialize)] pub struct CallInfo { pub call: CallEntryPoint, pub execution: CallExecution, - pub vm_resources: VmExecutionResources, + pub vm_resources: VmExecutionResourcesWrapper, pub inner_calls: Vec, // Additional information gathered during execution. diff --git a/crates/blockifier/src/execution/deprecated_entry_point_execution.rs b/crates/blockifier/src/execution/deprecated_entry_point_execution.rs index 95846cd221..01f6482083 100644 --- a/crates/blockifier/src/execution/deprecated_entry_point_execution.rs +++ b/crates/blockifier/src/execution/deprecated_entry_point_execution.rs @@ -253,7 +253,7 @@ pub fn finalize_execution( failed: false, gas_consumed: 0, }, - vm_resources: full_call_vm_resources.filter_unused_builtins(), + vm_resources: full_call_vm_resources.filter_unused_builtins().into(), inner_calls: syscall_handler.inner_calls, storage_read_values: syscall_handler.read_values, accessed_storage_keys: syscall_handler.accessed_keys, diff --git a/crates/blockifier/src/execution/deprecated_syscalls/deprecated_syscalls_test.rs b/crates/blockifier/src/execution/deprecated_syscalls/deprecated_syscalls_test.rs index 9b65f887dd..03dddde2af 100644 --- a/crates/blockifier/src/execution/deprecated_syscalls/deprecated_syscalls_test.rs +++ b/crates/blockifier/src/execution/deprecated_syscalls/deprecated_syscalls_test.rs @@ -135,7 +135,7 @@ fn test_nested_library_call() { let nested_storage_call_info = CallInfo { call: nested_storage_entry_point, execution: CallExecution::from_retdata(retdata![stark_felt!(value + 1)]), - vm_resources: storage_entry_point_vm_resources.clone(), + vm_resources: storage_entry_point_vm_resources.clone().into(), storage_read_values: vec![stark_felt!(0_u8), stark_felt!(value + 1)], accessed_storage_keys: HashSet::from([StorageKey(patricia_key!(key + 1))]), ..Default::default() @@ -149,14 +149,14 @@ fn test_nested_library_call() { let library_call_info = CallInfo { call: library_entry_point, execution: CallExecution::from_retdata(retdata![stark_felt!(value + 1)]), - vm_resources: library_call_vm_resources.clone(), + vm_resources: library_call_vm_resources.clone().into(), inner_calls: vec![nested_storage_call_info], ..Default::default() }; let storage_call_info = CallInfo { call: storage_entry_point, execution: CallExecution::from_retdata(retdata![stark_felt!(value)]), - vm_resources: storage_entry_point_vm_resources.clone(), + vm_resources: storage_entry_point_vm_resources.clone().into(), storage_read_values: vec![stark_felt!(0_u8), stark_felt!(value)], accessed_storage_keys: HashSet::from([StorageKey(patricia_key!(key))]), ..Default::default() @@ -168,7 +168,7 @@ fn test_nested_library_call() { let expected_call_info = CallInfo { call: main_entry_point.clone(), execution: CallExecution::from_retdata(retdata![stark_felt!(0_u8)]), - vm_resources: main_call_vm_resources, + vm_resources: main_call_vm_resources.into(), inner_calls: vec![library_call_info, storage_call_info], ..Default::default() }; @@ -209,7 +209,7 @@ fn test_call_contract() { ..trivial_external_entry_point() }, execution: expected_execution.clone(), - vm_resources: VmExecutionResources { n_steps: 42, ..Default::default() }, + vm_resources: VmExecutionResources { n_steps: 42, ..Default::default() }.into(), storage_read_values: vec![StarkFelt::ZERO, stark_felt!(value)], accessed_storage_keys: HashSet::from([StorageKey(patricia_key!(key))]), ..Default::default() @@ -229,7 +229,8 @@ fn test_call_contract() { n_steps: 81, builtin_instance_counter: HashMap::from([(RANGE_CHECK_BUILTIN_NAME.to_string(), 1)]), ..Default::default() - }, + } + .into(), ..Default::default() }; diff --git a/crates/blockifier/src/execution/entry_point.rs b/crates/blockifier/src/execution/entry_point.rs index 9dbb4e8f69..3ca63cdb9b 100644 --- a/crates/blockifier/src/execution/entry_point.rs +++ b/crates/blockifier/src/execution/entry_point.rs @@ -5,6 +5,7 @@ use std::sync::Arc; use cairo_vm::vm::runners::cairo_runner::{ ExecutionResources as VmExecutionResources, ResourceTracker, RunResources, }; +use serde::Serialize; use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector}; use starknet_api::deprecated_contract_class::EntryPointType; use starknet_api::hash::StarkFelt; @@ -35,14 +36,14 @@ pub const FAULTY_CLASS_HASH: &str = pub type EntryPointExecutionResult = Result; /// Represents a the type of the call (used for debugging). -#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Serialize)] pub enum CallType { #[default] Call = 0, Delegate = 1, } /// Represents a call to an entry point of a Starknet contract. -#[derive(Clone, Debug, Default, Eq, PartialEq)] +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)] pub struct CallEntryPoint { // The class hash is not given if it can be deduced from the storage address. pub class_hash: Option, diff --git a/crates/blockifier/src/execution/entry_point_execution.rs b/crates/blockifier/src/execution/entry_point_execution.rs index 1bb32b38f6..afda30ad3d 100644 --- a/crates/blockifier/src/execution/entry_point_execution.rs +++ b/crates/blockifier/src/execution/entry_point_execution.rs @@ -347,7 +347,7 @@ pub fn finalize_execution( failed: call_result.failed, gas_consumed: call_result.gas_consumed, }, - vm_resources: full_call_vm_resources.filter_unused_builtins(), + vm_resources: full_call_vm_resources.filter_unused_builtins().into(), inner_calls: syscall_handler.inner_calls, storage_read_values: syscall_handler.read_values, accessed_storage_keys: syscall_handler.accessed_keys, diff --git a/crates/blockifier/src/execution/entry_point_test.rs b/crates/blockifier/src/execution/entry_point_test.rs index c1c8d16611..de7ed570eb 100644 --- a/crates/blockifier/src/execution/entry_point_test.rs +++ b/crates/blockifier/src/execution/entry_point_test.rs @@ -531,6 +531,7 @@ fn test_cairo1_entry_point_segment_arena() { .execute_directly(&mut state) .unwrap() .vm_resources + .0 .builtin_instance_counter .contains_key(BuiltinName::segment_arena.name()) ); diff --git a/crates/blockifier/src/execution/syscalls/syscalls_test.rs b/crates/blockifier/src/execution/syscalls/syscalls_test.rs index 9ae38ac513..608507878b 100644 --- a/crates/blockifier/src/execution/syscalls/syscalls_test.rs +++ b/crates/blockifier/src/execution/syscalls/syscalls_test.rs @@ -554,7 +554,7 @@ fn test_nested_library_call() { gas_consumed: REQUIRED_GAS_STORAGE_READ_WRITE_TEST, ..CallExecution::default() }, - vm_resources: storage_entry_point_vm_resources.clone(), + vm_resources: storage_entry_point_vm_resources.clone().into(), storage_read_values: vec![stark_felt!(value + 1)], accessed_storage_keys: HashSet::from([StorageKey(patricia_key!(key + 1))]), ..Default::default() @@ -571,7 +571,7 @@ fn test_nested_library_call() { gas_consumed: REQUIRED_GAS_LIBRARY_CALL_TEST, ..CallExecution::default() }, - vm_resources: library_call_vm_resources, + vm_resources: library_call_vm_resources.into(), inner_calls: vec![nested_storage_call_info], ..Default::default() }; @@ -582,7 +582,7 @@ fn test_nested_library_call() { gas_consumed: REQUIRED_GAS_STORAGE_READ_WRITE_TEST, ..CallExecution::default() }, - vm_resources: storage_entry_point_vm_resources, + vm_resources: storage_entry_point_vm_resources.into(), storage_read_values: vec![stark_felt!(value)], accessed_storage_keys: HashSet::from([StorageKey(patricia_key!(key))]), ..Default::default() @@ -600,7 +600,7 @@ fn test_nested_library_call() { gas_consumed: 316180, ..CallExecution::default() }, - vm_resources: main_call_vm_resources, + vm_resources: main_call_vm_resources.into(), inner_calls: vec![library_call_info, storage_call_info], ..Default::default() }; diff --git a/crates/blockifier/src/test_utils/prices.rs b/crates/blockifier/src/test_utils/prices.rs index 461c9b48d5..5a7a4e175d 100644 --- a/crates/blockifier/src/test_utils/prices.rs +++ b/crates/blockifier/src/test_utils/prices.rs @@ -81,4 +81,5 @@ fn fee_transfer_resources( ) .unwrap() .vm_resources + .0 } diff --git a/crates/blockifier/src/transaction/objects.rs b/crates/blockifier/src/transaction/objects.rs index f7cd52b1ae..1032c4cf82 100644 --- a/crates/blockifier/src/transaction/objects.rs +++ b/crates/blockifier/src/transaction/objects.rs @@ -3,6 +3,7 @@ use std::collections::{HashMap, HashSet}; use cairo_felt::Felt252; use itertools::concat; use num_traits::Pow; +use serde::Serialize; use starknet_api::core::{ClassHash, ContractAddress, Nonce}; use starknet_api::data_availability::DataAvailabilityMode; use starknet_api::transaction::{ @@ -139,7 +140,7 @@ pub struct CommonAccountFields { } /// Contains the information gathered by the execution of a transaction. -#[derive(Debug, Default, Eq, PartialEq)] +#[derive(Debug, Default, Eq, PartialEq, Serialize)] pub struct TransactionExecutionInfo { /// Transaction validation call info; [None] for `L1Handler`. pub validate_call_info: Option, @@ -187,7 +188,7 @@ impl TransactionExecutionInfo { /// A mapping from a transaction execution resource to its actual usage. #[cfg_attr(test, derive(Clone))] -#[derive(Debug, Default, Eq, PartialEq)] +#[derive(Debug, Default, Eq, PartialEq, Serialize)] pub struct ResourcesMapping(pub HashMap); impl ResourcesMapping { diff --git a/crates/blockifier/src/transaction/transactions_test.rs b/crates/blockifier/src/transaction/transactions_test.rs index e087c8427d..9bc0842049 100644 --- a/crates/blockifier/src/transaction/transactions_test.rs +++ b/crates/blockifier/src/transaction/transactions_test.rs @@ -29,6 +29,7 @@ use crate::abi::sierra_types::next_storage_key; use crate::block_context::BlockContext; use crate::execution::call_info::{ CallExecution, CallInfo, MessageToL1, OrderedEvent, OrderedL2ToL1Message, Retdata, + VmExecutionResourcesWrapper, }; use crate::execution::entry_point::{CallEntryPoint, CallType}; use crate::execution::errors::{EntryPointExecutionError, VirtualMachineExecutionError}; @@ -146,7 +147,7 @@ fn expected_validate_call_info( initial_gas: Transaction::initial_gas(), }, // The account contract we use for testing has trivial `validate` functions. - vm_resources, + vm_resources: vm_resources.into(), execution: CallExecution { retdata, gas_consumed, ..Default::default() }, ..Default::default() }) @@ -207,7 +208,9 @@ fn expected_fee_transfer_call_info( events: vec![expected_fee_transfer_event], ..Default::default() }, - vm_resources: Prices::FeeTransfer(account_address, *fee_type).into(), + vm_resources: VmExecutionResourcesWrapper( + Prices::FeeTransfer(account_address, *fee_type).into(), + ), // We read sender balance, write (which starts with read) sender balance, then the same for // recipient. We read Uint256(BALANCE, 0) twice, then Uint256(0, 0) twice. storage_read_values: vec![ @@ -381,7 +384,7 @@ fn test_invoke_tx( gas_consumed: expected_arguments.execute_gas_consumed, ..Default::default() }, - vm_resources: expected_arguments.vm_resources, + vm_resources: expected_arguments.vm_resources.into(), inner_calls: vec![CallInfo { call: expected_return_result_call, execution: CallExecution::from_retdata(expected_return_result_retdata), @@ -389,7 +392,8 @@ fn test_invoke_tx( n_steps: 23, n_memory_holes: 0, ..Default::default() - }, + } + .into(), ..Default::default() }], ..Default::default() @@ -1626,7 +1630,8 @@ fn test_l1_handler() { n_steps: 143, n_memory_holes: 1, builtin_instance_counter: HashMap::from([(RANGE_CHECK_BUILTIN_NAME.to_string(), 5)]), - }, + } + .into(), accessed_storage_keys: HashSet::from_iter(vec![accessed_storage_key]), ..Default::default() }; diff --git a/crates/native_blockifier/src/py_block_executor.rs b/crates/native_blockifier/src/py_block_executor.rs index f1af7213f9..940941af8f 100644 --- a/crates/native_blockifier/src/py_block_executor.rs +++ b/crates/native_blockifier/src/py_block_executor.rs @@ -85,7 +85,7 @@ impl PyBlockExecutor { &mut self, tx: &PyAny, raw_contract_class: Option<&str>, - ) -> NativeBlockifierResult<(PyTransactionExecutionInfo, PyBouncerInfo)> { + ) -> NativeBlockifierResult<(Vec, PyTransactionExecutionInfo, PyBouncerInfo)> { let charge_fee = true; self.tx_executor().execute(tx, raw_contract_class, charge_fee) } diff --git a/crates/native_blockifier/src/py_transaction_execution_info.rs b/crates/native_blockifier/src/py_transaction_execution_info.rs index a7891690b0..37469cd89f 100644 --- a/crates/native_blockifier/src/py_transaction_execution_info.rs +++ b/crates/native_blockifier/src/py_transaction_execution_info.rs @@ -101,7 +101,7 @@ impl From for PyCallInfo { gas_consumed: execution.gas_consumed, failure_flag: PyFelt::from(execution.failed as u8), retdata: to_py_vec(execution.retdata.0, PyFelt), - execution_resources: PyVmExecutionResources::from(call_info.vm_resources), + execution_resources: PyVmExecutionResources::from(call_info.vm_resources.0), events: to_py_vec(execution.events, PyOrderedEvent::from), l2_to_l1_messages: to_py_vec(execution.l2_to_l1_messages, PyOrderedL2ToL1Message::from), internal_calls: to_py_vec(call_info.inner_calls, PyCallInfo::from), diff --git a/crates/native_blockifier/src/py_validator.rs b/crates/native_blockifier/src/py_validator.rs index 9a3c088292..93eb595264 100644 --- a/crates/native_blockifier/src/py_validator.rs +++ b/crates/native_blockifier/src/py_validator.rs @@ -71,7 +71,8 @@ impl PyValidator { // before `__validate_deploy__`. The execution already includes all necessary validations, // so they are skipped here. if let AccountTransaction::DeployAccount(_deploy_account_tx) = account_tx { - let (_py_tx_execution_info, _py_bouncer_info) = self.execute(tx, raw_contract_class)?; + let (_bytes_tx_execution_info, _py_tx_execution_info, _py_bouncer_info) = + self.execute(tx, raw_contract_class)?; // TODO(Ayelet, 09/11/2023): Check call succeeded. return Ok(()); @@ -131,7 +132,7 @@ impl PyValidator { &mut self, tx: &PyAny, raw_contract_class: Option<&str>, - ) -> NativeBlockifierResult<(PyTransactionExecutionInfo, PyBouncerInfo)> { + ) -> NativeBlockifierResult<(Vec, PyTransactionExecutionInfo, PyBouncerInfo)> { let limit_execution_steps_by_resource_bounds = true; self.tx_executor.execute(tx, raw_contract_class, limit_execution_steps_by_resource_bounds) } diff --git a/crates/native_blockifier/src/transaction_executor.rs b/crates/native_blockifier/src/transaction_executor.rs index ede7fe3578..7baaa7e7ad 100644 --- a/crates/native_blockifier/src/transaction_executor.rs +++ b/crates/native_blockifier/src/transaction_executor.rs @@ -72,7 +72,7 @@ impl TransactionExecutor { tx: &PyAny, raw_contract_class: Option<&str>, charge_fee: bool, - ) -> NativeBlockifierResult<(PyTransactionExecutionInfo, PyBouncerInfo)> { + ) -> NativeBlockifierResult<(Vec, PyTransactionExecutionInfo, PyBouncerInfo)> { let tx: Transaction = py_tx(tx, raw_contract_class)?; let mut tx_executed_class_hashes = HashSet::::new(); @@ -88,6 +88,8 @@ impl TransactionExecutor { // TODO(Elin, 01/06/2024): consider traversing the calls to collect data once. tx_executed_class_hashes.extend(tx_execution_info.get_executed_class_hashes()); tx_visited_storage_entries.extend(tx_execution_info.get_visited_storage_entries()); + let bytes_tx_execution_info = + serde_json::to_string(&tx_execution_info).unwrap().into_bytes(); // TODO(Elin, 01/06/2024): consider moving Bouncer logic to a function. let py_tx_execution_info = PyTransactionExecutionInfo::from(tx_execution_info); @@ -109,7 +111,7 @@ impl TransactionExecutor { self.staged_for_commit_state = Some( transactional_state.stage(tx_executed_class_hashes, tx_visited_storage_entries), ); - Ok((py_tx_execution_info, py_bouncer_info)) + Ok((bytes_tx_execution_info, py_tx_execution_info, py_bouncer_info)) } Err(error) => { transactional_state.abort();