From 211242911612eb48f5f10aec1f5167474de8ad32 Mon Sep 17 00:00:00 2001 From: Edgar Date: Mon, 30 Oct 2023 17:36:58 +0100 Subject: [PATCH] update cairo native to use gas consumed (#1102) * update cairo native to use gas consumed * gas consumed * update native rev * fix gas consumed * remove comments * fixes --------- Co-authored-by: Juan Bono --- Cargo.lock | 4 +- Cargo.toml | 2 +- src/execution/execution_entry_point.rs | 44 ++++-- .../business_logic_syscall_handler.rs | 9 +- src/syscalls/native_syscall_handler.rs | 125 +++++++++++++----- tests/cairo_native.rs | 34 +++-- 6 files changed, 155 insertions(+), 63 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0a838e946..3ad748f47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1320,7 +1320,7 @@ dependencies = [ [[package]] name = "cairo-native" version = "0.1.0" -source = "git+https://github.com/lambdaclass/cairo_native?rev=db1c3f6b044a4a6a3de3ab33e472c2e2263d81ac#db1c3f6b044a4a6a3de3ab33e472c2e2263d81ac" +source = "git+https://github.com/lambdaclass/cairo_native?rev=03cd09ba3e51852da2234fb32a74056787abba8e#03cd09ba3e51852da2234fb32a74056787abba8e" dependencies = [ "bumpalo", "cairo-felt", @@ -1355,7 +1355,7 @@ dependencies = [ [[package]] name = "cairo-native-runtime" version = "0.1.0" -source = "git+https://github.com/lambdaclass/cairo_native?rev=db1c3f6b044a4a6a3de3ab33e472c2e2263d81ac#db1c3f6b044a4a6a3de3ab33e472c2e2263d81ac" +source = "git+https://github.com/lambdaclass/cairo_native?rev=03cd09ba3e51852da2234fb32a74056787abba8e#03cd09ba3e51852da2234fb32a74056787abba8e" dependencies = [ "cairo-felt", "cairo-lang-runner", diff --git a/Cargo.toml b/Cargo.toml index a94ab2037..a3ef7cade 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ cairo-lang-runner = { workspace = true } cairo-lang-sierra = { workspace = true } cairo-lang-starknet = { workspace = true } cairo-lang-utils = { workspace = true } -cairo-native = { git = "https://github.com/lambdaclass/cairo_native", rev = "db1c3f6b044a4a6a3de3ab33e472c2e2263d81ac", optional = true } +cairo-native = { git = "https://github.com/lambdaclass/cairo_native", rev = "03cd09ba3e51852da2234fb32a74056787abba8e", optional = true } cairo-vm = { workspace = true, features = ["cairo-1-hints"] } flate2 = "1.0.25" getset = "0.1.2" diff --git a/src/execution/execution_entry_point.rs b/src/execution/execution_entry_point.rs index 8c600f026..0bf1d442b 100644 --- a/src/execution/execution_entry_point.rs +++ b/src/execution/execution_entry_point.rs @@ -640,8 +640,14 @@ impl ExecutionEntryPoint { tx_execution_context: &TransactionExecutionContext, block_context: &BlockContext, ) -> Result { + use cairo_lang_sierra::{ + extensions::core::{CoreLibfunc, CoreType, CoreTypeConcrete}, + program_registry::ProgramRegistry, + }; use serde_json::json; + use crate::syscalls::business_logic_syscall_handler::SYSCALL_BASE; + let entry_point = match self.entry_point_type { EntryPointType::External => contract_class .entry_points_by_type @@ -664,6 +670,8 @@ impl ExecutionEntryPoint { }; let sierra_program = contract_class.extract_sierra_program().unwrap(); + let program_registry: ProgramRegistry = + ProgramRegistry::new(&sierra_program).unwrap(); let native_context = NativeContext::new(); let mut native_program = native_context.compile(&sierra_program).unwrap(); @@ -672,16 +680,15 @@ impl ExecutionEntryPoint { let syscall_handler = NativeSyscallHandler { starknet_storage_state: contract_storage_state, - n_emitted_events: 0, events: Vec::new(), l2_to_l1_messages: Vec::new(), - n_sent_messages: 0, contract_address: self.contract_address.clone(), internal_calls: Vec::new(), caller_address: self.caller_address.clone(), entry_point_selector: self.entry_point_selector.clone(), tx_execution_context: tx_execution_context.clone(), block_context: block_context.clone(), + resources_manager: Default::default(), }; native_program @@ -694,14 +701,20 @@ impl ExecutionEntryPoint { .as_ptr() .as_ptr() as *const () as usize; - let fn_id = &sierra_program + let entry_point_fn = &sierra_program .funcs .iter() .find(|x| x.id.id == (entry_point.function_idx as u64)) - .unwrap() - .id; + .unwrap(); + let ret_types: Vec<&CoreTypeConcrete> = entry_point_fn + .signature + .ret_types + .iter() + .map(|x| program_registry.get_type(x).unwrap()) + .collect(); + let entry_point_id = &entry_point_fn.id; - let required_init_gas = native_program.get_required_init_gas(fn_id); + let required_init_gas = native_program.get_required_init_gas(entry_point_id); let calldata: Vec<_> = self .calldata @@ -719,13 +732,13 @@ impl ExecutionEntryPoint { */ let wrapped_calldata = vec![calldata]; - let params: Vec = sierra_program.funcs[fn_id.id as usize] + let params: Vec = sierra_program.funcs[entry_point_id.id as usize] .params .iter() .map(|param| { match param.ty.debug_name.as_ref().unwrap().as_str() { "GasBuiltin" => { - json!(self.initial_gas as u64) + json!(self.initial_gas) } "Pedersen" | "SegmentArena" | "RangeCheck" | "Bitwise" | "Poseidon" => { json!(null) @@ -748,11 +761,14 @@ impl ExecutionEntryPoint { let native_executor = NativeExecutor::new(native_program); native_executor - .execute(fn_id, json!(params), returns, required_init_gas) + .execute(entry_point_id, json!(params), returns, required_init_gas) .map_err(|e| TransactionError::CustomError(format!("cairo-native error: {:?}", e)))?; - let result: String = String::from_utf8(writer).unwrap(); - let value = serde_json::from_str::(&result).unwrap(); + let value = NativeExecutionResult::deserialize_from_ret_types( + &mut serde_json::Deserializer::from_slice(&writer), + &ret_types, + ) + .expect("failed to serialize starknet execution result"); Ok(CallInfo { caller_address: self.caller_address.clone(), @@ -773,8 +789,10 @@ impl ExecutionEntryPoint { failure_flag: value.failure_flag, l2_to_l1_messages: syscall_handler.l2_to_l1_messages, internal_calls: syscall_handler.internal_calls, - // TODO: check it's correct - gas_consumed: self.initial_gas - u128::from(value.gas_builtin.unwrap_or(0)), + gas_consumed: self + .initial_gas + .saturating_sub(SYSCALL_BASE) + .saturating_sub(value.remaining_gas), }) } } diff --git a/src/syscalls/business_logic_syscall_handler.rs b/src/syscalls/business_logic_syscall_handler.rs index 4d695da1d..122a03724 100644 --- a/src/syscalls/business_logic_syscall_handler.rs +++ b/src/syscalls/business_logic_syscall_handler.rs @@ -57,9 +57,10 @@ use lazy_static::lazy_static; use crate::services::api::contract_classes::deprecated_contract_class::EntryPointType; use num_traits::{One, ToPrimitive, Zero}; -const STEP: u128 = 100; -const SYSCALL_BASE: u128 = 100 * STEP; -const KECCAK_ROUND_COST: u128 = 180000; +pub(crate) const STEP: u128 = 100; +pub(crate) const SYSCALL_BASE: u128 = 100 * STEP; +pub(crate) const KECCAK_ROUND_COST: u128 = 180000; + lazy_static! { /// Felt->syscall map that was extracted from new_syscalls.json (Cairo 1.0 syscalls) static ref SELECTOR_TO_SYSCALL: HashMap = { @@ -91,7 +92,7 @@ lazy_static! { // Taken from starkware/starknet/constants.py in cairo-lang // See further documentation on cairo_programs/constants.cairo /// Maps syscall name to gas costs - static ref SYSCALL_GAS_COST: HashMap<&'static str, u128> = { + pub(crate) static ref SYSCALL_GAS_COST: HashMap<&'static str, u128> = { let mut map = HashMap::new(); map.insert("initial", 100_000_000 * STEP); diff --git a/src/syscalls/native_syscall_handler.rs b/src/syscalls/native_syscall_handler.rs index f8ae72d18..68aaa8b12 100644 --- a/src/syscalls/native_syscall_handler.rs +++ b/src/syscalls/native_syscall_handler.rs @@ -23,6 +23,7 @@ use crate::{ contract_storage_state::ContractStorageState, state_api::StateReader, ExecutionResourcesManager, }, + syscalls::business_logic_syscall_handler::{SYSCALL_BASE, SYSCALL_GAS_COST}, utils::Address, EntryPointType, }; @@ -37,31 +38,57 @@ where pub(crate) caller_address: Address, pub(crate) entry_point_selector: Felt252, pub(crate) events: Vec, - pub(crate) n_emitted_events: u64, - pub(crate) n_sent_messages: usize, pub(crate) l2_to_l1_messages: Vec, pub(crate) tx_execution_context: TransactionExecutionContext, pub(crate) block_context: BlockContext, - // TODO: This may not be really needed for Cairo Native, just passing - // it to be able to call the `execute` method of ExecutionEntrypoint. pub(crate) internal_calls: Vec, + pub(crate) resources_manager: ExecutionResourcesManager, +} + +impl<'a, S: StateReader> NativeSyscallHandler<'a, S> { + /// Generic code that needs to be run on all syscalls. + fn handle_syscall_request(&mut self, gas: &mut u128, syscall_name: &str) -> SyscallResult<()> { + let required_gas = SYSCALL_GAS_COST + .get(syscall_name) + .map(|&x| x.saturating_sub(SYSCALL_BASE)) + .unwrap_or(0); + + if *gas < required_gas { + let out_of_gas_felt = Felt252::from_bytes_be("Out of gas".as_bytes()); + println!("out of gas!: {:?} < {:?}", *gas, required_gas); // TODO: remove once all other prints are removed + return Err(vec![out_of_gas_felt.clone()]); + } + + *gas = gas.saturating_sub(required_gas); + + self.resources_manager + .increment_syscall_counter(syscall_name, 1); + + Ok(()) + } } impl<'a, S: StateReader> StarkNetSyscallHandler for NativeSyscallHandler<'a, S> { fn get_block_hash( &mut self, block_number: u64, - _gas: &mut u128, + gas: &mut u128, ) -> SyscallResult { println!("Called `get_block_hash({block_number})` from MLIR."); + + self.handle_syscall_request(gas, "get_block_hash")?; + Ok(Felt252::from_bytes_be(b"get_block_hash ok")) } fn get_execution_info( - &self, - _gas: &mut u128, + &mut self, + gas: &mut u128, ) -> SyscallResult { println!("Called `get_execution_info()` from MLIR."); + + self.handle_syscall_request(gas, "get_execution_info")?; + Ok(ExecutionInfo { block_info: BlockInfo { block_number: self.block_context.block_info.block_number, @@ -95,6 +122,8 @@ impl<'a, S: StateReader> StarkNetSyscallHandler for NativeSyscallHandler<'a, S> deploy_from_zero: bool, gas: &mut u128, ) -> SyscallResult<(cairo_vm::felt::Felt252, Vec)> { + self.handle_syscall_request(gas, "deploy")?; + let deployer_address = if deploy_from_zero { Address::default() } else { @@ -131,6 +160,8 @@ impl<'a, S: StateReader> StarkNetSyscallHandler for NativeSyscallHandler<'a, S> ) .map_err(|_| vec![Felt252::from_bytes_be(b"CONSTRUCTOR_ENTRYPOINT_FAILURE")])?; + *gas = gas.saturating_sub(result.gas_consumed); + Ok(( contract_address.0, result @@ -144,9 +175,11 @@ impl<'a, S: StateReader> StarkNetSyscallHandler for NativeSyscallHandler<'a, S> fn replace_class( &mut self, class_hash: cairo_vm::felt::Felt252, - _gas: &mut u128, + gas: &mut u128, ) -> SyscallResult<()> { println!("Called `replace_class({class_hash})` from MLIR."); + + self.handle_syscall_request(gas, "replace_class")?; Ok(()) } @@ -155,11 +188,14 @@ impl<'a, S: StateReader> StarkNetSyscallHandler for NativeSyscallHandler<'a, S> class_hash: cairo_vm::felt::Felt252, function_selector: cairo_vm::felt::Felt252, calldata: &[cairo_vm::felt::Felt252], - _gas: &mut u128, + gas: &mut u128, ) -> SyscallResult> { println!( "Called `library_call({class_hash}, {function_selector}, {calldata:?})` from MLIR." ); + + self.handle_syscall_request(gas, "library_call")?; + Ok(calldata.iter().map(|x| x * &Felt252::new(3)).collect()) } @@ -168,11 +204,14 @@ impl<'a, S: StateReader> StarkNetSyscallHandler for NativeSyscallHandler<'a, S> address: cairo_vm::felt::Felt252, entrypoint_selector: cairo_vm::felt::Felt252, calldata: &[cairo_vm::felt::Felt252], - _gas: &mut u128, + gas: &mut u128, ) -> SyscallResult> { println!( "Called `call_contract({address}, {entrypoint_selector}, {calldata:?})` from MLIR." ); + + self.handle_syscall_request(gas, "call_contract")?; + let address = Address(address); let exec_entry_point = ExecutionEntryPoint::new( address, @@ -182,8 +221,7 @@ impl<'a, S: StateReader> StarkNetSyscallHandler for NativeSyscallHandler<'a, S> EntryPointType::External, Some(CallType::Call), None, - // TODO: The remaining gas must be correctly set someway - u128::MAX, + *gas, ); let ExecutionResult { call_info, .. } = exec_entry_point @@ -191,16 +229,18 @@ impl<'a, S: StateReader> StarkNetSyscallHandler for NativeSyscallHandler<'a, S> self.starknet_storage_state.state, // TODO: This fields dont make much sense in the Cairo Native context, // they are only dummy values for the `execute` method. - &BlockContext::default(), - &mut ExecutionResourcesManager::default(), - &mut TransactionExecutionContext::default(), + &self.block_context, + &mut self.resources_manager, + &mut self.tx_execution_context, false, - u64::MAX, + self.block_context.invoke_tx_max_n_steps, ) .unwrap(); let call_info = call_info.unwrap(); + *gas = gas.saturating_sub(call_info.gas_consumed); + // update syscall handler information self.starknet_storage_state .read_values @@ -208,23 +248,31 @@ impl<'a, S: StateReader> StarkNetSyscallHandler for NativeSyscallHandler<'a, S> self.starknet_storage_state .accessed_keys .extend(call_info.accessed_storage_keys.clone()); - self.internal_calls.push(call_info.clone()); - Ok(call_info.retdata) + let retdata = call_info.retdata.clone(); + self.internal_calls.push(call_info); + + Ok(retdata) } fn storage_read( &mut self, address_domain: u32, address: cairo_vm::felt::Felt252, - _gas: &mut u128, + gas: &mut u128, ) -> SyscallResult { + println!("Called `storage_read({address_domain}, {address})` from MLIR."); + + self.handle_syscall_request(gas, "storage_read")?; + let value = match self.starknet_storage_state.read(&address.to_be_bytes()) { Ok(value) => Ok(value), Err(_e @ StateError::Io(_)) => todo!(), Err(_) => Ok(Felt252::zero()), }; - println!("Called `storage_read({address_domain}, {address}) = {value:?}` from MLIR."); + + println!(" = {value:?}` from MLIR."); + value } @@ -233,9 +281,12 @@ impl<'a, S: StateReader> StarkNetSyscallHandler for NativeSyscallHandler<'a, S> address_domain: u32, address: cairo_vm::felt::Felt252, value: cairo_vm::felt::Felt252, - _gas: &mut u128, + gas: &mut u128, ) -> SyscallResult<()> { println!("Called `storage_write({address_domain}, {address}, {value})` from MLIR."); + + self.handle_syscall_request(gas, "storage_write")?; + self.starknet_storage_state .write(&address.to_be_bytes(), value); Ok(()) @@ -245,13 +296,16 @@ impl<'a, S: StateReader> StarkNetSyscallHandler for NativeSyscallHandler<'a, S> &mut self, keys: &[cairo_vm::felt::Felt252], data: &[cairo_vm::felt::Felt252], - _gas: &mut u128, + gas: &mut u128, ) -> SyscallResult<()> { - let order = self.n_emitted_events; + let order = self.tx_execution_context.n_emitted_events; println!("Called `emit_event(KEYS: {keys:?}, DATA: {data:?})` from MLIR."); + + self.handle_syscall_request(gas, "emit_event")?; + self.events .push(OrderedEvent::new(order, keys.to_vec(), data.to_vec())); - self.n_emitted_events += 1; + self.tx_execution_context.n_emitted_events += 1; Ok(()) } @@ -259,27 +313,34 @@ impl<'a, S: StateReader> StarkNetSyscallHandler for NativeSyscallHandler<'a, S> &mut self, to_address: cairo_vm::felt::Felt252, payload: &[cairo_vm::felt::Felt252], - _gas: &mut u128, + gas: &mut u128, ) -> SyscallResult<()> { println!("Called `send_message_to_l1({to_address}, {payload:?})` from MLIR."); + + self.handle_syscall_request(gas, "send_message_to_l1")?; + let addr = Address(to_address); self.l2_to_l1_messages.push(OrderedL2ToL1Message::new( - self.n_sent_messages, + self.tx_execution_context.n_sent_messages, addr, payload.to_vec(), )); // Update messages count. - self.n_sent_messages += 1; + self.tx_execution_context.n_sent_messages += 1; + Ok(()) } fn keccak( - &self, + &mut self, input: &[u64], - _gas: &mut u128, + gas: &mut u128, ) -> SyscallResult { println!("Called `keccak({input:?})` from MLIR."); + + self.handle_syscall_request(gas, "keccak")?; + Ok(U256(Felt252::from(1234567890).to_le_bytes())) } @@ -478,10 +539,8 @@ where let ExecutionResult { call_info, .. } = call .execute( self.starknet_storage_state.state, - // TODO: This fields dont make much sense in the Cairo Native context, - // they are only dummy values for the `execute` method. - &BlockContext::default(), - &mut ExecutionResourcesManager::default(), + &self.block_context, + &mut self.resources_manager, &mut self.tx_execution_context, false, u64::MAX, diff --git a/tests/cairo_native.rs b/tests/cairo_native.rs index c40c1126b..249ee4e91 100644 --- a/tests/cairo_native.rs +++ b/tests/cairo_native.rs @@ -145,7 +145,6 @@ fn integration_test_erc20() { assert_eq!(native_result.retdata, [].to_vec()); assert_eq!(native_result.execution_resources, None); assert_eq!(native_result.class_hash, Some(NATIVE_CLASS_HASH)); - assert_eq!(native_result.gas_consumed, 18446744073709551615); // (u64::MAX) assert_eq!(vm_result.events, native_result.events); assert_eq!( @@ -153,10 +152,9 @@ fn integration_test_erc20() { native_result.accessed_storage_keys ); assert_eq!(vm_result.l2_to_l1_messages, native_result.l2_to_l1_messages); - // TODO: Make these asserts work - // assert_eq!(vm_result.execution_resources, native_result.execution_resources); - // assert_eq!(vm_result.gas_consumed, native_result.gas_consumed); + assert_eq!(vm_result.gas_consumed, native_result.gas_consumed); + #[allow(clippy::too_many_arguments)] fn compare_results( state_vm: &mut CachedState, state_native: &mut CachedState, @@ -165,6 +163,7 @@ fn integration_test_erc20() { casm_entrypoints: &CasmContractEntryPoints, calldata: &[Felt252], caller_address: &Address, + debug_name: &str, ) { let native_selector = &native_entrypoints .external @@ -206,11 +205,10 @@ fn integration_test_erc20() { ); assert_eq!(vm_result.l2_to_l1_messages, native_result.l2_to_l1_messages); - // TODO: Make these asserts work - // assert_eq!(vm_result.gas_consumed, native_result.gas_consumed); - - // This assert is probably impossible to make work because native doesn't track resources. - // assert_eq!(vm_result.execution_resources, native_result.execution_resources); + assert_eq!( + vm_result.gas_consumed, native_result.gas_consumed, + "gas consumed mismatch for {debug_name}", + ); } // --------------- GET TOTAL SUPPLY ----------------- @@ -223,6 +221,7 @@ fn integration_test_erc20() { &casm_entrypoints, &[], &caller_address, + "get total supply 1", ); // ---------------- GET DECIMALS ---------------------- @@ -235,6 +234,7 @@ fn integration_test_erc20() { &casm_entrypoints, &[], &caller_address, + "get decimals 1", ); // ---------------- GET NAME ---------------------- @@ -247,6 +247,7 @@ fn integration_test_erc20() { &casm_entrypoints, &[], &caller_address, + "get name", ); // // ---------------- GET SYMBOL ---------------------- @@ -259,6 +260,7 @@ fn integration_test_erc20() { &casm_entrypoints, &[], &caller_address, + "get symbol", ); // ---------------- GET BALANCE OF CALLER ---------------------- @@ -271,6 +273,7 @@ fn integration_test_erc20() { &casm_entrypoints, &[caller_address.0.clone()], &caller_address, + "get balance of caller", ); // // ---------------- ALLOWANCE OF ADDRESS 1 ---------------------- @@ -283,6 +286,7 @@ fn integration_test_erc20() { &casm_entrypoints, &[caller_address.0.clone(), 1.into()], &caller_address, + "get allowance of address 1", ); // // ---------------- INCREASE ALLOWANCE OF ADDRESS 1 by 10_000 ---------------------- @@ -295,6 +299,7 @@ fn integration_test_erc20() { &casm_entrypoints, &[1.into(), 10_000.into()], &caller_address, + "increase allowance of address 1 by 10000", ); // ---------------- ALLOWANCE OF ADDRESS 1 ---------------------- @@ -308,6 +313,7 @@ fn integration_test_erc20() { &casm_entrypoints, &[caller_address.0.clone(), 1.into()], &caller_address, + "allowance of address 1 part 2", ); // ---------------- APPROVE ADDRESS 1 TO MAKE TRANSFERS ON BEHALF OF THE CALLER ---------------------- @@ -320,6 +326,7 @@ fn integration_test_erc20() { &casm_entrypoints, &[1.into(), 5000.into()], &caller_address, + "approve address 1 to make transfers", ); // ---------------- TRANSFER 3 TOKENS FROM CALLER TO ADDRESS 2 --------- @@ -332,6 +339,7 @@ fn integration_test_erc20() { &casm_entrypoints, &[2.into(), 3.into()], &caller_address, + "transfer 3 tokens", ); // // ---------------- GET BALANCE OF CALLER ---------------------- @@ -344,6 +352,7 @@ fn integration_test_erc20() { &casm_entrypoints, &[caller_address.0.clone()], &caller_address, + "GET BALANCE OF CALLER", ); // // ---------------- GET BALANCE OF ADDRESS 2 ---------------------- @@ -356,6 +365,7 @@ fn integration_test_erc20() { &casm_entrypoints, &[2.into()], &caller_address, + "GET BALANCE OF ADDRESS 2", ); // // ---------------- TRANSFER 1 TOKEN FROM CALLER TO ADDRESS 2, CALLED FROM ADDRESS 1 ---------------------- @@ -368,6 +378,7 @@ fn integration_test_erc20() { &casm_entrypoints, &[1.into(), 2.into(), 1.into()], &caller_address, + "TRANSFER 1 TOKEN FROM CALLER TO ADDRESS 2, CALLED FROM ADDRESS 1", ); // // ---------------- GET BALANCE OF ADDRESS 2 ---------------------- @@ -380,6 +391,7 @@ fn integration_test_erc20() { &casm_entrypoints, &[2.into()], &caller_address, + "GET BALANCE OF ADDRESS 2 part 2", ); // // ---------------- GET BALANCE OF CALLER ---------------------- @@ -392,6 +404,7 @@ fn integration_test_erc20() { &casm_entrypoints, &[caller_address.0.clone()], &caller_address, + "GET BALANCE OF CALLER last", ); } @@ -564,6 +577,7 @@ fn call_echo_contract_test() { ); assert_eq!(result.retdata, [Felt252::new(99999999)]); + assert_eq!(result.gas_consumed, 89110); } #[test] @@ -673,7 +687,7 @@ fn call_events_contract_test() { storage_read_values: Vec::new(), accessed_storage_keys: HashSet::new(), internal_calls: Vec::new(), - gas_consumed: 340282366920938463463374607431768211455, // TODO: fix gas consumed + gas_consumed: 9640, failure_flag: false, };