From db1e869f12af96052a8fca1cfeb497edb25ff885 Mon Sep 17 00:00:00 2001 From: Pierrick Couderc Date: Mon, 12 Feb 2024 19:49:05 +0100 Subject: [PATCH 1/6] EVM/Kernel: add SimulationResult to rule them all --- .../kernel_evm/ethereum/src/rlp_helpers.rs | 40 ++++++ etherlink/kernel_evm/kernel/src/simulation.rs | 125 +++++++++++++++++- 2 files changed, 163 insertions(+), 2 deletions(-) diff --git a/etherlink/kernel_evm/ethereum/src/rlp_helpers.rs b/etherlink/kernel_evm/ethereum/src/rlp_helpers.rs index ea33bb6f784a..615abe9bfa0c 100644 --- a/etherlink/kernel_evm/ethereum/src/rlp_helpers.rs +++ b/etherlink/kernel_evm/ethereum/src/rlp_helpers.rs @@ -16,6 +16,16 @@ pub fn next<'a, 'v>(decoder: &mut RlpIterator<'a, 'v>) -> Result, Decode decoder.next().ok_or(DecoderError::RlpIncorrectListLen) } +pub fn check_list(decoder: &Rlp<'_>, length: usize) -> Result<(), DecoderError> { + if !decoder.is_list() { + Err(DecoderError::RlpExpectedToBeList) + } else if decoder.item_count() != Ok(length) { + Err(DecoderError::RlpIncorrectListLen) + } else { + Ok(()) + } +} + pub fn decode_field( decoder: &Rlp<'_>, field_name: &'static str, @@ -268,3 +278,33 @@ pub fn decode_timestamp(decoder: &Rlp<'_>) -> Result { .into(); Ok(timestamp) } + +/// Hardcoding the option RLP encoding for u64 in little endian. This is +/// unfortunately necessary as we cannot redefine the u64 encoding. +pub fn append_option_u64_le(v: &Option, stream: &mut rlp::RlpStream) { + match v { + None => { + stream.begin_list(0); + } + Some(value) => { + stream.begin_list(1); + append_u64_le(stream, value); + } + } +} + +/// See [append_option_u64_le] +pub fn decode_option_u64_le( + decoder: &Rlp<'_>, + field_name: &'static str, +) -> Result, DecoderError> { + let items = decoder.item_count()?; + match items { + 1 => { + let mut it = decoder.iter(); + Ok(Some(decode_field_u64_le(&next(&mut it)?, field_name)?)) + } + 0 => Ok(None), + _ => Err(DecoderError::RlpIncorrectListLen), + } +} diff --git a/etherlink/kernel_evm/kernel/src/simulation.rs b/etherlink/kernel_evm/kernel/src/simulation.rs index 894e44738bb2..261ce7b0e7c2 100644 --- a/etherlink/kernel_evm/kernel/src/simulation.rs +++ b/etherlink/kernel_evm/kernel/src/simulation.rs @@ -19,9 +19,12 @@ use evm_execution::handler::ExtendedExitReason; use evm_execution::{account_storage, handler::ExecutionOutcome, precompiles}; use evm_execution::{run_transaction, EthereumError}; use primitive_types::{H160, U256}; -use rlp::{Decodable, DecoderError, Rlp}; +use rlp::{Decodable, DecoderError, Encodable, Rlp}; use tezos_ethereum::block::BlockConstants; -use tezos_ethereum::rlp_helpers::{decode_field, decode_option, next}; +use tezos_ethereum::rlp_helpers::{ + append_option_u64_le, check_list, decode_field, decode_option, decode_option_u64_le, + next, +}; use tezos_ethereum::tx_common::EthereumTransactionCommon; use tezos_evm_logging::{log, Level::*}; use tezos_smart_rollup_host::runtime::Runtime; @@ -37,6 +40,91 @@ pub const EVALUATION_TAG: u8 = 0x00; /// Tag indicating simulation is a validation. pub const VALIDATION_TAG: u8 = 0x01; +pub const OK_TAG: u8 = 0x1; +pub const ERR_TAG: u8 = 0x2; + +// Redefined Result as we cannot implement Decodable and Encodable traits on Result +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum SimulationResult { + Ok(T), + Err(E), +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct ExecutionResult { + value: Option>, + gas_used: Option, +} + +type CallResult = SimulationResult>; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct ValidationResult { + address: H160, +} + +impl Encodable for SimulationResult { + fn rlp_append(&self, stream: &mut rlp::RlpStream) { + stream.begin_list(2); + match self { + Self::Ok(value) => { + stream.append(&OK_TAG); + stream.append(value) + } + Self::Err(e) => { + stream.append(&ERR_TAG); + stream.append(e) + } + }; + } +} + +impl Decodable for SimulationResult { + fn decode(decoder: &Rlp<'_>) -> Result { + check_list(decoder, 2)?; + + let mut it = decoder.iter(); + match decode_field(&next(&mut it)?, "tag")? { + OK_TAG => Ok(Self::Ok(decode_field(&next(&mut it)?, "ok")?)), + ERR_TAG => Ok(Self::Err(decode_field(&next(&mut it)?, "error")?)), + _ => Err(DecoderError::Custom("Invalid execution tag")), + } + } +} + +impl Encodable for ExecutionResult { + fn rlp_append(&self, stream: &mut rlp::RlpStream) { + stream.begin_list(2); + stream.append(&self.value); + append_option_u64_le(&self.gas_used, stream); + } +} + +impl Decodable for ExecutionResult { + fn decode(decoder: &Rlp<'_>) -> Result { + check_list(decoder, 2)?; + + let mut it = decoder.iter(); + let value = decode_field(&next(&mut it)?, "value")?; + let gas_used = decode_option_u64_le(&next(&mut it)?, "gas_used")?; + Ok(ExecutionResult { value, gas_used }) + } +} + +impl Encodable for ValidationResult { + fn rlp_append(&self, stream: &mut rlp::RlpStream) { + stream.append(&self.address); + } +} + +impl Decodable for ValidationResult { + fn decode(decoder: &Rlp) -> Result { + Ok(ValidationResult { + address: decode_field(decoder, "caller")?, + }) + } +} + /// Container for eth_call data, used in messages sent by the rollup node /// simulation. /// @@ -1043,4 +1131,37 @@ mod tests { assert!(result.is_ok()); assert_eq!(TxValidationOutcome::MaxGasFeeTooLow, result.unwrap()); } + + pub fn check_roundtrip( + v: R, + ) { + let bytes = v.rlp_bytes(); + let decoder = Rlp::new(&bytes); + println!("{:?}", bytes); + let decoded = R::decode(&decoder).expect("Value should be decodable"); + assert_eq!(v, decoded, "Roundtrip failed on {:?}", v) + } + + #[test] + fn test_simulation_result_encoding_roundtrip() { + let valid: SimulationResult = + SimulationResult::Ok(ValidationResult { + address: address_from_str("f95abdf6ede4c3703e0e9453771fbee8592d31e9") + .unwrap(), + }); + let call: SimulationResult = + SimulationResult::Ok(SimulationResult::Ok(ExecutionResult { + value: Some(vec![0, 1, 2, 3]), + gas_used: Some(123), + })); + let revert: SimulationResult = + SimulationResult::Ok(SimulationResult::Err(vec![3, 2, 1, 0])); + let error: SimulationResult = + SimulationResult::Err(String::from("Un festival de GADTs")); + + check_roundtrip(valid); + check_roundtrip(call); + check_roundtrip(revert); + check_roundtrip(error) + } } -- GitLab From 3929e1226ebf48766b914896376045832e94f6ab Mon Sep 17 00:00:00 2001 From: Pierrick Couderc Date: Mon, 12 Feb 2024 19:49:45 +0100 Subject: [PATCH 2/6] EVM/Kernel: remove EvaluationOutcome, use unified SimulationResult --- etherlink/kernel_evm/kernel/src/simulation.rs | 179 +++++++++--------- etherlink/kernel_evm/kernel/src/storage.rs | 16 +- 2 files changed, 94 insertions(+), 101 deletions(-) diff --git a/etherlink/kernel_evm/kernel/src/simulation.rs b/etherlink/kernel_evm/kernel/src/simulation.rs index 261ce7b0e7c2..533cdbab1775 100644 --- a/etherlink/kernel_evm/kernel/src/simulation.rs +++ b/etherlink/kernel_evm/kernel/src/simulation.rs @@ -15,6 +15,7 @@ use crate::{ tick_model, CONFIG, }; +use evm::ExitReason; use evm_execution::handler::ExtendedExitReason; use evm_execution::{account_storage, handler::ExecutionOutcome, precompiles}; use evm_execution::{run_transaction, EthereumError}; @@ -164,11 +165,50 @@ pub struct Evaluation { pub data: Vec, } -#[derive(Debug, PartialEq)] -pub enum EvaluationOutcome { - EvaluationError(EthereumError), - Outcome(Option), - OutOfTicks, +impl From for SimulationResult { + fn from(err: EthereumError) -> Self { + let msg = format!("The transaction failed: {:?}.", err); + Self::Err(msg) + } +} + +impl From, EthereumError>> + for SimulationResult +{ + fn from(result: Result, EthereumError>) -> Self { + match result { + Ok(Some(ExecutionOutcome { + gas_used, + reason: ExtendedExitReason::Exit(ExitReason::Succeed(_)), + result, + .. + })) => Self::Ok(SimulationResult::Ok(ExecutionResult { + value: result, + gas_used: Some(gas_used), + })), + Ok(Some(ExecutionOutcome { + reason: ExtendedExitReason::Exit(ExitReason::Revert(_)), + result, + .. + })) => Self::Ok(SimulationResult::Err(result.unwrap_or_default())), + Ok(Some(ExecutionOutcome { + reason: ExtendedExitReason::OutOfTicks, + .. + })) => Self::Err(String::from( + "The transaction would exhaust all the ticks it is allocated. \ + Try reducing its gas consumption or splitting the call in \ + multiple steps, if possible.", + )), + Ok(Some(ExecutionOutcome { reason, .. })) => { + let msg = format!("The transaction failed: {:?}.", reason); + Self::Err(msg) + } + Ok(None) => Self::Err(String::from( + "No outcome was produced when the transaction was ran", + )), + Err(err) => err.into(), + } + } } impl Evaluation { @@ -182,7 +222,7 @@ impl Evaluation { pub fn run( &self, host: &mut Host, - ) -> Result { + ) -> Result, Error> { let chain_id = retrieve_chain_id(host)?; let block_fees = retrieve_block_fees(host)?; @@ -229,15 +269,6 @@ impl Evaluation { false, false, ) { - Ok(Some(ExecutionOutcome { - reason: ExtendedExitReason::OutOfTicks, - .. - })) - | Err(evm_execution::EthereumError::OutOfTicks) => { - Ok(EvaluationOutcome::OutOfTicks) - } - Err(err) => Ok(EvaluationOutcome::EvaluationError(err)), - Ok(None) => Ok(EvaluationOutcome::Outcome(None)), Ok(Some(outcome)) => { let outcome = simulation_add_gas_for_fees( outcome, @@ -247,8 +278,12 @@ impl Evaluation { ) .map_err(Error::Simulation)?; - Ok(EvaluationOutcome::Outcome(Some(outcome))) + let result: SimulationResult = + Result::Ok(Some(outcome)).into(); + + Ok(result) } + result => Ok(result.into()), } } } @@ -531,44 +566,6 @@ fn parse_inbox(host: &mut Host) -> Result { } } -fn store_simulation_outcome( - host: &mut Host, - outcome: EvaluationOutcome, -) -> Result<(), anyhow::Error> { - log!(host, Debug, "outcome={:?} ", outcome); - match outcome { - EvaluationOutcome::Outcome(Some(outcome)) => { - storage::store_simulation_status(host, outcome.is_success)?; - storage::store_evaluation_gas(host, outcome.gas_used)?; - storage::store_simulation_result(host, outcome.result) - } - EvaluationOutcome::Outcome(None) => { - storage::store_simulation_status(host, false)?; - storage::store_simulation_result( - host, - Some(b"No outcome was produced when the transaction was ran".to_vec()), - ) - } - EvaluationOutcome::OutOfTicks => { - storage::store_simulation_status(host, false)?; - storage::store_simulation_result( - host, - Some( - b"The transaction would exhaust all the ticks it is allocated. \ - Try reducing its gas consumption or splitting the call in \ - multiple steps, if possible." - .to_vec(), - ), - ) - } - EvaluationOutcome::EvaluationError(err) => { - storage::store_simulation_status(host, false)?; - let msg = format!("The transaction failed: {:?}.", err); - storage::store_simulation_result(host, Some(msg.as_bytes().to_vec())) - } - } -} - fn store_tx_validation_outcome( host: &mut Host, outcome: TxValidationOutcome, @@ -576,27 +573,36 @@ fn store_tx_validation_outcome( match outcome { TxValidationOutcome::Valid(caller) => { storage::store_simulation_status(host, true)?; - storage::store_simulation_result(host, Some(caller.to_fixed_bytes().to_vec())) + storage::store_tx_validation_result( + host, + Some(caller.to_fixed_bytes().to_vec()), + ) } TxValidationOutcome::NonceTooLow => { storage::store_simulation_status(host, false)?; - storage::store_simulation_result(host, Some(b"Nonce too low.".to_vec())) + storage::store_tx_validation_result(host, Some(b"Nonce too low.".to_vec())) } TxValidationOutcome::NotCorrectSignature => { storage::store_simulation_status(host, false)?; - storage::store_simulation_result(host, Some(b"Incorrect signature.".to_vec())) + storage::store_tx_validation_result( + host, + Some(b"Incorrect signature.".to_vec()), + ) } TxValidationOutcome::InvalidChainId => { storage::store_simulation_status(host, false)?; - storage::store_simulation_result(host, Some(b"Invalid chain id.".to_vec())) + storage::store_tx_validation_result(host, Some(b"Invalid chain id.".to_vec())) } TxValidationOutcome::MaxGasFeeTooLow => { storage::store_simulation_status(host, false)?; - storage::store_simulation_result(host, Some(b"Max gas fee too low.".to_vec())) + storage::store_tx_validation_result( + host, + Some(b"Max gas fee too low.".to_vec()), + ) } TxValidationOutcome::OutOfTicks => { storage::store_simulation_status(host, false)?; - storage::store_simulation_result( + storage::store_tx_validation_result( host, Some( b"The transaction would exhaust all the ticks it is allocated. \ @@ -617,7 +623,7 @@ pub fn start_simulation_mode( match simulation { Message::Evaluation(simulation) => { let outcome = simulation.run(host)?; - store_simulation_outcome(host, outcome) + storage::store_simulation_result(host, outcome) } Message::TxValidation(tx_validation) => { let outcome = tx_validation.run(host)?; @@ -790,17 +796,12 @@ mod tests { assert!(outcome.is_ok(), "evaluation should have succeeded"); let outcome = outcome.unwrap(); - if let EvaluationOutcome::Outcome(outcome) = outcome { - assert!( - outcome.is_some(), - "simulation should have produced some outcome" - ); - let outcome = outcome.unwrap(); - assert_eq!( - Some(vec![0u8; 32]), - outcome.result, - "simulation result should be 0" - ); + if let SimulationResult::Ok(SimulationResult::Ok(ExecutionResult { + value, + gas_used: _, + })) = outcome + { + assert_eq!(Some(vec![0u8; 32]), value, "simulation result should be 0"); } else { panic!("evaluation should have reached outcome"); } @@ -818,17 +819,12 @@ mod tests { assert!(outcome.is_ok(), "simulation should have succeeded"); let outcome = outcome.unwrap(); - if let EvaluationOutcome::Outcome(outcome) = outcome { - assert!( - outcome.is_some(), - "simulation should have produced some outcome" - ); - let outcome = outcome.unwrap(); - assert_eq!( - Some(vec![0u8; 32]), - outcome.result, - "evaluation result should be 0" - ); + if let SimulationResult::Ok(SimulationResult::Ok(ExecutionResult { + value, + gas_used: _, + })) = outcome + { + assert_eq!(Some(vec![0u8; 32]), value, "evaluation result should be 0"); } else { panic!("evaluation should have reached outcome"); } @@ -853,17 +849,12 @@ mod tests { assert!(outcome.is_ok(), "evaluation should have succeeded"); let outcome = outcome.unwrap(); - if let EvaluationOutcome::Outcome(outcome) = outcome { - assert!( - outcome.is_some(), - "simulation should have produced some outcome" - ); - let outcome = outcome.unwrap(); - assert_eq!( - Some(vec![0u8; 32]), - outcome.result, - "evaluation result should be 0" - ); + if let SimulationResult::Ok(SimulationResult::Ok(ExecutionResult { + value, + gas_used: _, + })) = outcome + { + assert_eq!(Some(vec![0u8; 32]), value, "evaluation result should be 0"); } else { panic!("evaluation should have reached outcome"); } diff --git a/etherlink/kernel_evm/kernel/src/storage.rs b/etherlink/kernel_evm/kernel/src/storage.rs index c5e043120752..e1b9d31d320a 100644 --- a/etherlink/kernel_evm/kernel/src/storage.rs +++ b/etherlink/kernel_evm/kernel/src/storage.rs @@ -8,6 +8,7 @@ use crate::block_in_progress::BlockInProgress; use crate::event::Event; use crate::indexable_storage::IndexableStorage; +use crate::simulation::SimulationResult; use anyhow::Context; use evm_execution::account_storage::EthereumAccount; use evm_execution::storage::blocks::add_new_block_hash; @@ -81,7 +82,6 @@ const EVM_INFO_PER_LEVEL_STATS_TOTAL: RefPath = pub const SIMULATION_RESULT: RefPath = RefPath::assert_from(b"/simulation_result"); pub const SIMULATION_STATUS: RefPath = RefPath::assert_from(b"/simulation_status"); -pub const SIMULATION_GAS: RefPath = RefPath::assert_from(b"/simulation_gas"); pub const DEPOSIT_NONCE: RefPath = RefPath::assert_from(b"/deposit_nonce"); @@ -298,21 +298,23 @@ pub fn store_current_block( } } -pub fn store_simulation_result( +pub fn store_tx_validation_result( host: &mut Host, result: Option>, ) -> Result<(), anyhow::Error> { if let Some(result) = result { - host.store_write(&SIMULATION_RESULT, &result, 0)? + host.store_write(&SIMULATION_STATUS, &result, 0)? } Ok(()) } -pub fn store_evaluation_gas( +pub fn store_simulation_result( host: &mut Host, - result: u64, -) -> Result<(), Error> { - write_u256(host, &SIMULATION_GAS.into(), U256::from(result)) + result: SimulationResult, +) -> Result<(), anyhow::Error> { + let encoded = result.rlp_bytes(); + host.store_write(&SIMULATION_RESULT, &encoded, 0) + .context("Failed to write the simulation result.") } pub fn store_simulation_status( -- GitLab From 9ddd30a2274ab39c26283f654ed3a2c9a9f4d4b6 Mon Sep 17 00:00:00 2001 From: Pierrick Couderc Date: Mon, 12 Feb 2024 19:45:26 +0100 Subject: [PATCH 3/6] EVM/Kernel: remove TxValidation in favor of SimulationResult --- etherlink/kernel_evm/kernel/src/simulation.rs | 111 ++++++------------ etherlink/kernel_evm/kernel/src/storage.rs | 20 ---- 2 files changed, 35 insertions(+), 96 deletions(-) diff --git a/etherlink/kernel_evm/kernel/src/simulation.rs b/etherlink/kernel_evm/kernel/src/simulation.rs index 533cdbab1775..0ceeb66359aa 100644 --- a/etherlink/kernel_evm/kernel/src/simulation.rs +++ b/etherlink/kernel_evm/kernel/src/simulation.rs @@ -44,6 +44,14 @@ pub const VALIDATION_TAG: u8 = 0x01; pub const OK_TAG: u8 = 0x1; pub const ERR_TAG: u8 = 0x2; +const INCORRECT_SIGNATURE: &str = "Incorrect signature."; +const INVALID_CHAIN_ID: &str = "Invalid chain id."; +const NONCE_TOO_LOW: &str = "Nonce too low."; +const MAX_GAS_FEE_TOO_LOW: &str = "Max gas fee too low."; +const OUT_OF_TICKS_MSG: &str = "The transaction would exhaust all the ticks it + is allocated. Try reducing its gas consumption or splitting the call in + multiple steps, if possible."; + // Redefined Result as we cannot implement Decodable and Encodable traits on Result #[derive(Debug, PartialEq, Eq, Clone)] pub enum SimulationResult { @@ -194,11 +202,7 @@ impl From, EthereumError>> Ok(Some(ExecutionOutcome { reason: ExtendedExitReason::OutOfTicks, .. - })) => Self::Err(String::from( - "The transaction would exhaust all the ticks it is allocated. \ - Try reducing its gas consumption or splitting the call in \ - multiple steps, if possible.", - )), + })) => Self::Err(String::from(OUT_OF_TICKS_MSG)), Ok(Some(ExecutionOutcome { reason, .. })) => { let msg = format!("The transaction failed: {:?}.", reason); Self::Err(msg) @@ -335,16 +339,6 @@ struct TxValidation { transaction: EthereumTransactionCommon, } -#[derive(Debug, PartialEq)] -enum TxValidationOutcome { - Valid(H160), - NonceTooLow, - NotCorrectSignature, - InvalidChainId, - MaxGasFeeTooLow, - OutOfTicks, -} - impl TxValidation { // Run the transaction and ensure // - it won't fail with out-of-ticks @@ -353,7 +347,7 @@ impl TxValidation { host: &mut Host, transaction: &EthereumTransactionCommon, caller: &H160, - ) -> Result { + ) -> Result, anyhow::Error> { let chain_id = retrieve_chain_id(host)?; let block_fees = retrieve_block_fees(host)?; @@ -377,7 +371,7 @@ impl TxValidation { ); let Ok(gas_limit) = tx_execution_gas_limit(transaction, &block_fees) else { - return Ok(TxValidationOutcome::MaxGasFeeTooLow); + return Self::to_error(MAX_GAS_FEE_TOO_LOW); }; match run_transaction( @@ -400,20 +394,28 @@ impl TxValidation { Ok(Some(ExecutionOutcome { reason: ExtendedExitReason::OutOfTicks, .. - })) => Ok(TxValidationOutcome::OutOfTicks), - _ => Ok(TxValidationOutcome::Valid(*caller)), + })) => Self::to_error(OUT_OF_TICKS_MSG), + // TODO: #6498, 6813 + // Check for PREPAY + _ => Ok(SimulationResult::Ok(ValidationResult { address: *caller })), } } + pub fn to_error( + msg: &str, + ) -> Result, anyhow::Error> { + Ok(SimulationResult::Err(String::from(msg))) + } + /// Execute the simulation pub fn run( &self, host: &mut Host, - ) -> Result { + ) -> Result, anyhow::Error> { let tx = &self.transaction; let evm_account_storage = account_storage::init_account_storage()?; // Get the caller - let Ok(caller) = tx.caller() else {return Ok(TxValidationOutcome::NotCorrectSignature)}; + let Ok(caller) = tx.caller() else {return Self::to_error(INCORRECT_SIGNATURE)}; // Get the caller account let caller_account_path = evm_execution::account_storage::account_path(&caller)?; let caller_account = evm_account_storage.get(host, &caller_account_path)?; @@ -427,15 +429,15 @@ impl TxValidation { let chain_id = storage::read_chain_id(host)?; // Check if nonce is too low if tx.nonce < caller_nonce { - return Ok(TxValidationOutcome::NonceTooLow); + return Self::to_error(NONCE_TOO_LOW); } // Check if the chain id is correct if tx.chain_id.is_some() && tx.chain_id != Some(chain_id) { - return Ok(TxValidationOutcome::InvalidChainId); + return Self::to_error(INVALID_CHAIN_ID); } // Check if the gas price is high enough if tx.max_fee_per_gas < block_fees.base_fee_per_gas() { - return Ok(TxValidationOutcome::MaxGasFeeTooLow); + return Self::to_error(MAX_GAS_FEE_TOO_LOW); } // Check if running the transaction (assuming it is valid) would run out // of ticks, or fail validation for another reason. @@ -566,55 +568,6 @@ fn parse_inbox(host: &mut Host) -> Result { } } -fn store_tx_validation_outcome( - host: &mut Host, - outcome: TxValidationOutcome, -) -> Result<(), anyhow::Error> { - match outcome { - TxValidationOutcome::Valid(caller) => { - storage::store_simulation_status(host, true)?; - storage::store_tx_validation_result( - host, - Some(caller.to_fixed_bytes().to_vec()), - ) - } - TxValidationOutcome::NonceTooLow => { - storage::store_simulation_status(host, false)?; - storage::store_tx_validation_result(host, Some(b"Nonce too low.".to_vec())) - } - TxValidationOutcome::NotCorrectSignature => { - storage::store_simulation_status(host, false)?; - storage::store_tx_validation_result( - host, - Some(b"Incorrect signature.".to_vec()), - ) - } - TxValidationOutcome::InvalidChainId => { - storage::store_simulation_status(host, false)?; - storage::store_tx_validation_result(host, Some(b"Invalid chain id.".to_vec())) - } - TxValidationOutcome::MaxGasFeeTooLow => { - storage::store_simulation_status(host, false)?; - storage::store_tx_validation_result( - host, - Some(b"Max gas fee too low.".to_vec()), - ) - } - TxValidationOutcome::OutOfTicks => { - storage::store_simulation_status(host, false)?; - storage::store_tx_validation_result( - host, - Some( - b"The transaction would exhaust all the ticks it is allocated. \ - Try reducing its gas consumption or splitting the call in \ - multiple steps, if possible." - .to_vec(), - ), - ) - } - } -} - pub fn start_simulation_mode( host: &mut Host, ) -> Result<(), anyhow::Error> { @@ -627,7 +580,7 @@ pub fn start_simulation_mode( } Message::TxValidation(tx_validation) => { let outcome = tx_validation.run(host)?; - store_tx_validation_outcome(host, outcome) + storage::store_simulation_result(host, outcome) } } } @@ -1074,7 +1027,10 @@ mod tests { let result = simulation.run(&mut host); println!("{result:?}"); assert!(result.is_ok()); - assert_eq!(TxValidationOutcome::MaxGasFeeTooLow, result.unwrap()); + assert_eq!( + TxValidation::to_error(MAX_GAS_FEE_TOO_LOW).unwrap(), + result.unwrap() + ); } #[test] @@ -1120,7 +1076,10 @@ mod tests { let result = simulation.run(&mut host); assert!(result.is_ok()); - assert_eq!(TxValidationOutcome::MaxGasFeeTooLow, result.unwrap()); + assert_eq!( + SimulationResult::Err(String::from(super::MAX_GAS_FEE_TOO_LOW)), + result.unwrap() + ); } pub fn check_roundtrip( diff --git a/etherlink/kernel_evm/kernel/src/storage.rs b/etherlink/kernel_evm/kernel/src/storage.rs index e1b9d31d320a..7707864c9a1f 100644 --- a/etherlink/kernel_evm/kernel/src/storage.rs +++ b/etherlink/kernel_evm/kernel/src/storage.rs @@ -81,7 +81,6 @@ const EVM_INFO_PER_LEVEL_STATS_TOTAL: RefPath = RefPath::assert_from(b"/info_per_level/stats/total"); pub const SIMULATION_RESULT: RefPath = RefPath::assert_from(b"/simulation_result"); -pub const SIMULATION_STATUS: RefPath = RefPath::assert_from(b"/simulation_status"); pub const DEPOSIT_NONCE: RefPath = RefPath::assert_from(b"/deposit_nonce"); @@ -297,17 +296,6 @@ pub fn store_current_block( } } } - -pub fn store_tx_validation_result( - host: &mut Host, - result: Option>, -) -> Result<(), anyhow::Error> { - if let Some(result) = result { - host.store_write(&SIMULATION_STATUS, &result, 0)? - } - Ok(()) -} - pub fn store_simulation_result( host: &mut Host, result: SimulationResult, @@ -317,14 +305,6 @@ pub fn store_simulation_result( .context("Failed to write the simulation result.") } -pub fn store_simulation_status( - host: &mut Host, - result: bool, -) -> Result<(), anyhow::Error> { - host.store_write(&SIMULATION_STATUS, &[result.into()], 0) - .context("Failed to write the simulation status.") -} - pub fn store_transaction_receipt( host: &mut Host, receipt: &TransactionReceipt, -- GitLab From 8c974564249e72282281a7c82e4afcf92d097aeb Mon Sep 17 00:00:00 2001 From: Pierrick Couderc Date: Wed, 7 Feb 2024 15:15:58 +0100 Subject: [PATCH 4/6] EVM/Test: remove test trying to use insights This one cannot work anymore, or we add RLP encodings in Tezt. These kind of tests should go through the EVM node. --- etherlink/tezt/tests/evm_rollup.ml | 43 ------------------------------ 1 file changed, 43 deletions(-) diff --git a/etherlink/tezt/tests/evm_rollup.ml b/etherlink/tezt/tests/evm_rollup.ml index 50a0b37c6c3a..9282b95b4d73 100644 --- a/etherlink/tezt/tests/evm_rollup.ml +++ b/etherlink/tezt/tests/evm_rollup.ml @@ -2608,48 +2608,6 @@ let test_rpc_getTransactionByBlockNumberAndIndex = ~config @@ fun ~protocol:_ -> test_rpc_getTransactionByBlockArgAndIndex ~by:`Number -let test_validation_result = - register_both - ~tags:["evm"; "simulate"] - ~title: - "Ensure validation returns appropriate address for a given transaction." - ~minimum_base_fee_per_gas:base_fee_for_hardcoded_tx - @@ fun ~protocol:_ ~evm_setup:{sc_rollup_node; _} -> - (* tx is a signed legacy transaction obtained with the following data, using - the following private key: - data = { - "nonce": "0x00", - "gasPrice": "0x5208", - "gasLimit": "0x00", - "to": "0x0000000000000000000000000000000000000000", - "value": "0x00", - "data": "0x", - "chainId": 1337 - } - private key=0x84e147b8bc36d99cc6b1676318a0635d8febc9f02897b0563ad27358589ee502 - *) - let tx = - "f86180825208809400000000000000000000000000000000000000008080820a95a0f47140763cf73d6d9b342727e5a0809f7997bb62375060932af9bbc2e74b6212a03a018079a2fd7fefb625451ce2fafcdf873b892ff9d4e3e1f2ada5650012f072" - in - let simulation_msg = "ff0101" ^ tx in - let* simulation_result = - Sc_rollup_node.RPC.call sc_rollup_node - @@ Sc_rollup_rpc.post_global_block_simulate - ~insight_requests: - [ - `Durable_storage_key ["evm"; "simulation_status"]; - `Durable_storage_key ["evm"; "simulation_result"]; - ] - [Hex.to_string @@ `Hex "ff"; Hex.to_string @@ `Hex simulation_msg] - in - let expected_insights = - [Some "01"; Some "f0affc80a5f69f4a9a3ee01a640873b6ba53e539"] - in - Check.( - (simulation_result.insights = expected_insights) (list @@ option string)) - ~error_msg:"Expected result %R, but got %L" ; - unit - type storage_migration_results = { transfer_result : transfer_result; block_result : Block.t; @@ -4842,7 +4800,6 @@ let register_evm_node ~protocols = test_rpc_gasPrice protocols ; test_rpc_getStorageAt protocols ; test_accounts_double_indexing protocols ; - test_validation_result protocols ; test_rpc_sendRawTransaction_with_consecutive_nonce protocols ; test_rpc_sendRawTransaction_not_included protocols ; test_originate_evm_kernel_and_dump_pvm_state protocols ; -- GitLab From d53b793d14bbe8303cb06b9527b0974486efaa29 Mon Sep 17 00:00:00 2001 From: Pierrick Couderc Date: Wed, 7 Feb 2024 15:21:09 +0100 Subject: [PATCH 5/6] EVM/Node: add simulation result encoding --- etherlink/bin_node/lib_dev/encodings/rlp.ml | 54 +++++++++++++++ etherlink/bin_node/lib_dev/encodings/rlp.mli | 11 +++ etherlink/bin_node/lib_dev/simulation.ml | 73 +++++++++++++++++++- 3 files changed, 135 insertions(+), 3 deletions(-) diff --git a/etherlink/bin_node/lib_dev/encodings/rlp.ml b/etherlink/bin_node/lib_dev/encodings/rlp.ml index 9daff251ba67..38bfa9f21c6a 100644 --- a/etherlink/bin_node/lib_dev/encodings/rlp.ml +++ b/etherlink/bin_node/lib_dev/encodings/rlp.ml @@ -25,6 +25,18 @@ type error += Rlp_decoding_error of string +let () = + register_error_kind + ~id:"evm-node.dev.rlp-decoding-error" + ~title:"Unable to decode an RLP value" + ~description:"Unable to decode an RLP value" + ~pp:(fun ppf msg -> + Format.fprintf ppf "Unable to decode an RLP value: `%s`" msg) + `Permanent + Data_encoding.(obj1 (req "msg" string)) + (function Rlp_decoding_error msg -> Some msg | _ -> None) + (fun msg -> Rlp_decoding_error msg) + type item = Value of bytes | List of item list let rec encode_int buffer i = @@ -214,3 +226,45 @@ let rec pp ppf = function ~pp_sep:(fun ppf () -> Format.fprintf ppf "; ") pp) items + +(* Implements optional types decoding as in the RLP library from the + kernel's library. *) +let decode_option decode_value = + let open Result_syntax in + function + | List [] -> return_none + | List [v] -> + let* value = decode_value v in + return_some value + | _ -> tzfail (Rlp_decoding_error "Inconsistent encoding for optional type") + +let decode_result decode_value decode_error = + let open Result_syntax in + function + | List [Value tag; payload] -> ( + let* () = + if Bytes.length tag <> 1 then + tzfail + (Rlp_decoding_error + (Format.sprintf "Inconsistent tag size: %d" (Bytes.length tag))) + else return_unit + in + let tag = Bytes.get_uint8 tag 0 in + match tag with + | 1 -> + let* ok = decode_value payload in + return @@ Ok ok + | 2 -> + let* err = decode_error payload in + return @@ Error err + | t -> + tzfail + (Rlp_decoding_error + (Format.sprintf "Inconsistent tag [%d] for the result type" t))) + | rlp -> + tzfail + (Rlp_decoding_error + (Format.asprintf + "Inconsistent encoding for the result type: %a" + pp + rlp)) diff --git a/etherlink/bin_node/lib_dev/encodings/rlp.mli b/etherlink/bin_node/lib_dev/encodings/rlp.mli index dfe0f1ec96a8..c93f7522b254 100644 --- a/etherlink/bin_node/lib_dev/encodings/rlp.mli +++ b/etherlink/bin_node/lib_dev/encodings/rlp.mli @@ -60,5 +60,16 @@ val decode : bytes -> item tzresult fails to decode. *) val decode_exn : bytes -> item +(** [decode_option decode_value optional_value] decodes the option following + Rust's RLP encoding. *) +val decode_option : (item -> 'a tzresult) -> item -> 'a option tzresult + +(** [decode_result decode_ok decode_error value] decodes an encoded result type. *) +val decode_result : + (item -> 'a tzresult) -> + (item -> 'b tzresult) -> + item -> + ('a, 'b) result tzresult + (** [pp ppf item] pretty-prints an item. *) val pp : Format.formatter -> item -> unit diff --git a/etherlink/bin_node/lib_dev/simulation.ml b/etherlink/bin_node/lib_dev/simulation.ml index 655ef4f1a62a..4d69badbe80d 100644 --- a/etherlink/bin_node/lib_dev/simulation.ml +++ b/etherlink/bin_node/lib_dev/simulation.ml @@ -119,6 +119,14 @@ let encode_tx tx = in return @@ List.map encode_message messages +type execution_result = {value : hash option; gas_used : quantity option} + +type call_result = (execution_result, hash) result + +type validation_result = {address : address} + +type 'a simulation_result = ('a, string) result + module Encodings = struct open Data_encoding @@ -233,6 +241,65 @@ module Encodings = struct } | _ -> None + let decode_data = + let open Result_syntax in + function + | Rlp.Value v -> return (decode_hash v) + | Rlp.List _ -> error_with "The simulation returned an ill-encoded data" + + let decode_gas_used = + let open Result_syntax in + function + | Rlp.Value v -> return (decode_number v) + | Rlp.List _ -> error_with "The simulation returned an ill-encoded gas" + + let decode_execution_result = + let open Result_syntax in + function + | Rlp.List [value; gas_used] -> + let* value = Rlp.decode_option decode_data value in + let* gas_used = Rlp.decode_option decode_gas_used gas_used in + return {value; gas_used} + | _ -> + error_with + "The simulation for eth_call/eth_estimateGas returned an ill-encoded \ + format" + + let decode_call_result v = + let open Result_syntax in + let decode_revert = function + | Rlp.Value msg -> return (decode_hash msg) + | _ -> error_with "The revert message is ill-encoded" + in + Rlp.decode_result decode_execution_result decode_revert v + + let decode_validation_result = + let open Result_syntax in + function + | Rlp.Value address -> return {address = decode_address address} + | _ -> error_with "The transaction pool returned an illformed value" + + let simulation_result_from_rlp decode_payload bytes = + let open Result_syntax in + let decode_error_msg = function + | Rlp.Value msg -> return @@ Bytes.to_string msg + | rlp -> + error_with + "The simulation returned an unexpected error message: %a" + Rlp.pp + rlp + in + let* rlp = Rlp.decode bytes in + match Rlp.decode_result decode_payload decode_error_msg rlp with + | Ok v -> Ok v + | Error e -> + error_with + "The simulation returned an unexpected format: %a, with error %a" + Rlp.pp + rlp + pp_print_trace + e + let eval_result = conv (fun {state_hash; status; output; inbox_level; num_ticks; insights} -> @@ -273,7 +340,7 @@ let call_result Encodings.{success; result; gas = _} = return (Ok (Hash (Hex v))) | Some false, Some result -> let error_msg = Bytes.to_string result in - return (Error error_msg) + return (Result.Error error_msg) | _ -> failwith "Insights of 'call_result' cannot be parsed" let gas_estimation Encodings.{success; result; gas} = @@ -296,7 +363,7 @@ let gas_estimation Encodings.{success; result; gas} = return (Ok (quantity_of_z simulated_amount)) | Some false, Some result, _ -> let error_msg = Bytes.to_string result in - return (Error error_msg) + return (Result.Error error_msg) | _ -> failwith "Insights of 'gas_estimation' cannot be parsed" let is_tx_valid Encodings.{success; result; gas = _} = @@ -308,5 +375,5 @@ let is_tx_valid Encodings.{success; result; gas = _} = return (Ok address) else let error_msg = Bytes.to_string payload in - return (Error error_msg) + return (Result.Error error_msg) | _ -> failwith "Insights of 'is_tx_valid' is not [Some _, Some _]" -- GitLab From cd3a1bad007a65015e17eb229ee626c31958dcb6 Mon Sep 17 00:00:00 2001 From: Pierrick Couderc Date: Wed, 7 Feb 2024 15:36:21 +0100 Subject: [PATCH 6/6] EVM/Node: unify all simulation calls --- etherlink/bin_node/lib_dev/observer.ml | 6 +- etherlink/bin_node/lib_dev/rollup_node.ml | 4 +- etherlink/bin_node/lib_dev/sequencer.ml | 6 +- etherlink/bin_node/lib_dev/services.ml | 21 +++- .../bin_node/lib_dev/services_backend_sig.ml | 8 +- etherlink/bin_node/lib_dev/simulation.ml | 95 ++++-------------- etherlink/bin_node/lib_dev/simulator.ml | 98 ++++++++----------- etherlink/bin_node/lib_dev/tx_pool.ml | 4 +- 8 files changed, 91 insertions(+), 151 deletions(-) diff --git a/etherlink/bin_node/lib_dev/observer.ml b/etherlink/bin_node/lib_dev/observer.ml index f3814285e90f..14822fcd6c91 100644 --- a/etherlink/bin_node/lib_dev/observer.ml +++ b/etherlink/bin_node/lib_dev/observer.ml @@ -98,9 +98,9 @@ end) : Services_backend_sig.Backend = struct let simulate_and_read ~input = let open Lwt_result_syntax in let* raw_insights = Evm_context.execute_and_inspect Ctxt.ctxt ~input in - match Simulation.Encodings.insights_from_list raw_insights with - | Some i -> return i - | None -> Error_monad.failwith "Invalid insights format" + match raw_insights with + | [Some bytes] -> return bytes + | _ -> Error_monad.failwith "Invalid insights format" end let inject_kernel_upgrade ~payload = diff --git a/etherlink/bin_node/lib_dev/rollup_node.ml b/etherlink/bin_node/lib_dev/rollup_node.ml index 94f3247d8579..672f93ce4c42 100644 --- a/etherlink/bin_node/lib_dev/rollup_node.ml +++ b/etherlink/bin_node/lib_dev/rollup_node.ml @@ -88,7 +88,9 @@ end) : Services_backend_sig.Backend = struct let eval_result = Data_encoding.Json.destruct Simulation.Encodings.eval_result json in - return eval_result.insights + match eval_result.insights with + | [data] -> return data + | _ -> failwith "Inconsistent simulation results" end let inject_kernel_upgrade ~payload:_ = Lwt_result_syntax.return_unit diff --git a/etherlink/bin_node/lib_dev/sequencer.ml b/etherlink/bin_node/lib_dev/sequencer.ml index 742530028cfd..4d9cee4dee94 100644 --- a/etherlink/bin_node/lib_dev/sequencer.ml +++ b/etherlink/bin_node/lib_dev/sequencer.ml @@ -73,9 +73,9 @@ end) : Services_backend_sig.Backend = struct let simulate_and_read ~input = let open Lwt_result_syntax in let* raw_insights = Evm_context.execute_and_inspect Ctxt.ctxt ~input in - match Simulation.Encodings.insights_from_list raw_insights with - | Some i -> return i - | None -> Error_monad.failwith "Invalid insights format" + match raw_insights with + | [Some bytes] -> return bytes + | _ -> Error_monad.failwith "Invalid insights format" end let inject_kernel_upgrade ~payload = diff --git a/etherlink/bin_node/lib_dev/services.ml b/etherlink/bin_node/lib_dev/services.ml index 2418999039c8..f91c272133f6 100644 --- a/etherlink/bin_node/lib_dev/services.ml +++ b/etherlink/bin_node/lib_dev/services.ml @@ -409,7 +409,12 @@ let dispatch_request (config : 'a Configuration.t) | Some (call, _) -> ( let* call_result = Backend_rpc.simulate_call call in match call_result with - | Ok result -> return (Either.Left result) + | Ok (Ok {value = Some value; gas_used = _}) -> + return (Either.Left value) + | Ok (Ok {value = None; gas_used = _}) -> + return (Either.Left (hash_of_string "")) + | Ok (Error _reason) -> + return (Either.Right "execution reverted:") | Error reason -> (* TODO: https://gitlab.com/tezos/tezos/-/issues/6229 *) return (Either.Right reason)) @@ -421,9 +426,17 @@ let dispatch_request (config : 'a Configuration.t) match input with | None -> return missing_parameter | Some (call, _) -> ( - let* gas_result = Backend_rpc.estimate_gas call in - match gas_result with - | Ok gas -> return (Either.Left gas) + let* result = Backend_rpc.estimate_gas call in + match result with + | Ok (Ok {value = _; gas_used = Some gas}) -> + return (Either.Left gas) + | Ok (Ok {value = _; gas_used = None}) -> + return + (Either.Right + "Simulation failed before execution, cannot estimate \ + gas.") + | Ok (Error _reason) -> + return (Either.Right "execution reverted:") | Error reason -> (* TODO: https://gitlab.com/tezos/tezos/-/issues/6229 *) return (Either.Right reason)) diff --git a/etherlink/bin_node/lib_dev/services_backend_sig.ml b/etherlink/bin_node/lib_dev/services_backend_sig.ml index ef2e4f629b8a..856a4be470ae 100644 --- a/etherlink/bin_node/lib_dev/services_backend_sig.ml +++ b/etherlink/bin_node/lib_dev/services_backend_sig.ml @@ -83,19 +83,21 @@ module type S = sig (** [simulate_call call_info] asks the rollup to simulate a call, and returns the result. *) val simulate_call : - Ethereum_types.call -> (Ethereum_types.hash, string) result tzresult Lwt.t + Ethereum_types.call -> + Simulation.call_result Simulation.simulation_result tzresult Lwt.t (** [estimate_gas call_info] asks the rollup to simulate a call, and returns the gas used to execute the call. *) val estimate_gas : Ethereum_types.call -> - (Ethereum_types.quantity, string) result tzresult Lwt.t + Simulation.call_result Simulation.simulation_result tzresult Lwt.t (** [is_tx_valid tx_raw] checks if the transaction is valid. Checks if the nonce is correct and returns the associated public key of transaction. *) val is_tx_valid : - string -> (Ethereum_types.address, string) result tzresult Lwt.t + string -> + Simulation.validation_result Simulation.simulation_result tzresult Lwt.t (** [storage_at address pos] returns the value at index [pos] of the account [address]'s storage. *) diff --git a/etherlink/bin_node/lib_dev/simulation.ml b/etherlink/bin_node/lib_dev/simulation.ml index 4d69badbe80d..42cd04eb33a8 100644 --- a/etherlink/bin_node/lib_dev/simulation.ml +++ b/etherlink/bin_node/lib_dev/simulation.ml @@ -130,19 +130,13 @@ type 'a simulation_result = ('a, string) result module Encodings = struct open Data_encoding - type insights = { - success : bool option; - result : bytes option; - gas : bytes option; - } - type eval_result = { state_hash : string; status : string; output : unit; inbox_level : unit; num_ticks : Z.t; - insights : insights; + insights : bytes list; (** The simulation can ask to look at values on the state after the simulation. *) } @@ -207,40 +201,6 @@ module Encodings = struct /simulation_kernel_logs/, where is the \ data directory of the rollup node.") - let bool_as_bytes = - let bytes_to_bool b = - b |> Data_encoding.Binary.of_bytes Data_encoding.bool |> Result.to_option - in - - let bool_to_bytes b = - b |> Data_encoding.Binary.to_bytes Data_encoding.bool |> Result.to_option - in - conv - (fun b -> Option.bind b bool_to_bytes) - (fun b -> Option.bind b bytes_to_bool) - (option bytes) - - let insights = - conv - (fun {success; result; gas} -> (gas, result, success)) - (fun (gas, result, success) -> {gas; result; success}) - (tup3 (option bytes) (option bytes) bool_as_bytes) - - let insights_from_list l = - match l with - | [gas; result; success] -> - Some - { - success = - Option.bind success (fun s -> - s - |> Data_encoding.Binary.of_bytes Data_encoding.bool - |> Result.to_option); - result; - gas; - } - | _ -> None - let decode_data = let open Result_syntax in function @@ -328,26 +288,20 @@ module Encodings = struct ~description:"Ticks taken by the PVM for evaluating the messages") (req "insights" - insights + (list bytes) ~description:"PVM state values requested after the simulation") end -let call_result Encodings.{success; result; gas = _} = - let open Lwt_result_syntax in - match (success, result) with - | Some true, Some result -> - let v = result |> Hex.of_bytes |> Hex.show in - return (Ok (Hash (Hex v))) - | Some false, Some result -> - let error_msg = Bytes.to_string result in - return (Result.Error error_msg) - | _ -> failwith "Insights of 'call_result' cannot be parsed" - -let gas_estimation Encodings.{success; result; gas} = - let open Lwt_result_syntax in - match (success, result, gas) with - | Some true, _, Some simulated_amount -> - let simulated_amount = Bytes.to_string simulated_amount |> Z.of_bits in +let simulation_result bytes = + Encodings.simulation_result_from_rlp Encodings.decode_call_result bytes + +let gas_estimation bytes = + let open Result_syntax in + let* result = + Encodings.simulation_result_from_rlp Encodings.decode_call_result bytes + in + match result with + | Ok (Ok {gas_used = Some (Qty gas_used); value}) -> (* See EIP2200 for reference. But the tl;dr is: we cannot do the opcode SSTORE if we have less than 2300 gas available, even if we don't consume it. The simulated amount then gives an @@ -355,25 +309,14 @@ let gas_estimation Encodings.{success; result; gas} = The extra gas units, i.e. 2300, will be refunded. *) - let simulated_amount = Z.(add simulated_amount (of_int 2300)) in + let simulated_amount = Z.(add gas_used (of_int 2300)) in (* add a safety margin of 2%, sufficient to cover a 1/64th difference *) let simulated_amount = Z.(add simulated_amount (cdiv simulated_amount (of_int 50))) in - return (Ok (quantity_of_z simulated_amount)) - | Some false, Some result, _ -> - let error_msg = Bytes.to_string result in - return (Result.Error error_msg) - | _ -> failwith "Insights of 'gas_estimation' cannot be parsed" - -let is_tx_valid Encodings.{success; result; gas = _} = - let open Lwt_result_syntax in - match (result, success) with - | Some payload, Some success -> - if success then - let address = Ethereum_types.decode_address payload in - return (Ok address) - else - let error_msg = Bytes.to_string payload in - return (Result.Error error_msg) - | _ -> failwith "Insights of 'is_tx_valid' is not [Some _, Some _]" + return + @@ Ok (Ok {gas_used = Some (quantity_of_z @@ simulated_amount); value}) + | _ -> return result + +let is_tx_valid bytes = + Encodings.simulation_result_from_rlp Encodings.decode_validation_result bytes diff --git a/etherlink/bin_node/lib_dev/simulator.ml b/etherlink/bin_node/lib_dev/simulator.ml index adc4c34da78b..419df6f88e0b 100644 --- a/etherlink/bin_node/lib_dev/simulator.ml +++ b/etherlink/bin_node/lib_dev/simulator.ml @@ -7,57 +7,47 @@ module type SimulationBackend = sig val simulate_and_read : - input:Simulation.Encodings.simulate_input -> - Simulation.Encodings.insights tzresult Lwt.t + input:Simulation.Encodings.simulate_input -> bytes tzresult Lwt.t end (* This value is a hard maximum used by estimateGas. Set at Int64.max_int / 2 *) let max_gas_limit = Z.of_int64 0x3FFFFFFFFFFFFFFFL module Make (SimulationBackend : SimulationBackend) = struct - let simulate_call call = + let call_simulation ~log_file ~input_encoder ~input = let open Lwt_result_syntax in - let*? messages = Simulation.encode call in + let*? messages = input_encoder input in let insight_requests = - [ - Simulation.Encodings.Durable_storage_key ["evm"; "simulation_gas"]; - Simulation.Encodings.Durable_storage_key ["evm"; "simulation_result"]; - Simulation.Encodings.Durable_storage_key ["evm"; "simulation_status"]; - ] + [Simulation.Encodings.Durable_storage_key ["evm"; "simulation_result"]] in - let* results = - SimulationBackend.simulate_and_read - ~input: - { - messages; - reveal_pages = None; - insight_requests; - log_kernel_debug_file = Some "simulate_call"; - } + SimulationBackend.simulate_and_read + ~input: + { + messages; + reveal_pages = None; + insight_requests; + log_kernel_debug_file = Some log_file; + } + + let simulate_call call = + let open Lwt_result_syntax in + let* bytes = + call_simulation + ~log_file:"simulate_call" + ~input_encoder:Simulation.encode + ~input:call in - Simulation.call_result results + Lwt.return (Simulation.simulation_result bytes) let call_estimate_gas call = let open Lwt_result_syntax in - let*? messages = Simulation.encode call in - let insight_requests = - [ - Simulation.Encodings.Durable_storage_key ["evm"; "simulation_gas"]; - Simulation.Encodings.Durable_storage_key ["evm"; "simulation_result"]; - Simulation.Encodings.Durable_storage_key ["evm"; "simulation_status"]; - ] - in - let* results = - SimulationBackend.simulate_and_read - ~input: - { - messages; - reveal_pages = None; - insight_requests; - log_kernel_debug_file = Some "estimate_gas"; - } + let* bytes = + call_simulation + ~log_file:"estimate_gas" + ~input_encoder:Simulation.encode + ~input:call in - Simulation.gas_estimation results + Lwt.return (Simulation.gas_estimation bytes) let rec confirm_gas (call : Ethereum_types.call) gas = let open Ethereum_types in @@ -67,41 +57,31 @@ module Make (SimulationBackend : SimulationBackend) = struct let new_call = {call with gas = Some gas} in let* result = call_estimate_gas new_call in match result with - | Error _ -> + | Error _ | Ok (Error _) -> (* TODO: https://gitlab.com/tezos/tezos/-/issues/6984 All errors should not be treated the same *) let new_gas = double gas in if reached_max new_gas then failwith "Gas estimate reached max gas limit." else confirm_gas call new_gas - | Ok _ -> return (Ok gas) + | Ok (Ok _) -> return gas let estimate_gas call = let open Lwt_result_syntax in let* res = call_estimate_gas call in match res with - | Ok gas -> confirm_gas call gas - | Error e -> failwith "Couldn't estimate gas: %s" e + | Ok (Ok {gas_used = Some gas; value}) -> + let+ gas_used = confirm_gas call gas in + Ok (Ok {Simulation.gas_used = Some gas_used; value}) + | _ -> return res let is_tx_valid tx_raw = let open Lwt_result_syntax in - let*? messages = Simulation.encode_tx tx_raw in - let insight_requests = - [ - Simulation.Encodings.Durable_storage_key ["evm"; "simulation_gas"]; - Simulation.Encodings.Durable_storage_key ["evm"; "simulation_result"]; - Simulation.Encodings.Durable_storage_key ["evm"; "simulation_status"]; - ] - in - let* results = - SimulationBackend.simulate_and_read - ~input: - { - messages; - reveal_pages = None; - insight_requests; - log_kernel_debug_file = Some "tx_validity"; - } + let* bytes = + call_simulation + ~log_file:"tx_validity" + ~input_encoder:Simulation.encode_tx + ~input:tx_raw in - Simulation.is_tx_valid results + Lwt.return (Simulation.is_tx_valid bytes) end diff --git a/etherlink/bin_node/lib_dev/tx_pool.ml b/etherlink/bin_node/lib_dev/tx_pool.ml index 1c9994560305..3be18cb1e23a 100644 --- a/etherlink/bin_node/lib_dev/tx_pool.ml +++ b/etherlink/bin_node/lib_dev/tx_pool.ml @@ -257,9 +257,9 @@ let on_normal_transaction state tx_raw = ~transaction:(Hex.of_string tx_raw |> Hex.show) in return (Error err) - | Ok pkey -> + | Ok {address} -> (* Add the tx to the pool*) - let*? pool = Pool.add pool pkey base_fee tx_raw in + let*? pool = Pool.add pool address base_fee tx_raw in (* compute the hash *) let tx_hash = Ethereum_types.hash_raw_tx tx_raw in let hash = -- GitLab