diff --git a/etherlink/kernel_bifrost/evm_evaluation/src/evalhost.rs b/etherlink/kernel_bifrost/evm_evaluation/src/evalhost.rs index a2973b4fa14fb0bd32b7bc1cd3b56b9bd0e15d71..57aae084ffa7183fa479a4efc18122d7630da235 100644 --- a/etherlink/kernel_bifrost/evm_evaluation/src/evalhost.rs +++ b/etherlink/kernel_bifrost/evm_evaluation/src/evalhost.rs @@ -34,7 +34,7 @@ impl EvalHost { } } -impl SdkRuntime for EvalHost { +impl SdkRuntime for EvalHost { #[inline(always)] fn write_output(&mut self, from: &[u8]) -> Result<(), RuntimeError> { self.host.write_output(from) @@ -54,12 +54,12 @@ impl SdkRuntime for EvalHost { } #[inline(always)] - fn store_has(&self, path: &T) -> Result, RuntimeError> { + fn store_has>(&self, path: &T) -> Result, RuntimeError> { self.host.store_has(path) } #[inline(always)] - fn store_read( + fn store_read>( &self, path: &T, from_offset: usize, @@ -69,7 +69,7 @@ impl SdkRuntime for EvalHost { } #[inline(always)] - fn store_read_slice( + fn store_read_slice>( &self, path: &T, from_offset: usize, @@ -79,12 +79,12 @@ impl SdkRuntime for EvalHost { } #[inline(always)] - fn store_read_all(&self, path: &impl Path) -> Result, RuntimeError> { + fn store_read_all(&self, path: &impl Path) -> Result, RuntimeError> { self.host.store_read_all(path) } #[inline(always)] - fn store_write( + fn store_write>( &mut self, path: &T, src: &[u8], @@ -94,7 +94,7 @@ impl SdkRuntime for EvalHost { } #[inline(always)] - fn store_write_all( + fn store_write_all>( &mut self, path: &T, src: &[u8], @@ -103,25 +103,25 @@ impl SdkRuntime for EvalHost { } #[inline(always)] - fn store_delete(&mut self, path: &T) -> Result<(), RuntimeError> { + fn store_delete>(&mut self, path: &T) -> Result<(), RuntimeError> { self.host.store_delete(path) } #[inline(always)] - fn store_delete_value(&mut self, path: &T) -> Result<(), RuntimeError> { + fn store_delete_value>(&mut self, path: &T) -> Result<(), RuntimeError> { self.host.store_delete_value(path) } #[inline(always)] - fn store_count_subkeys(&self, prefix: &T) -> Result { + fn store_count_subkeys>(&self, prefix: &T) -> Result { self.host.store_count_subkeys(prefix) } #[inline(always)] fn store_move( &mut self, - from_path: &impl Path, - to_path: &impl Path, + from_path: &impl Path, + to_path: &impl Path, ) -> Result<(), RuntimeError> { self.host.store_move(from_path, to_path) } @@ -129,8 +129,8 @@ impl SdkRuntime for EvalHost { #[inline(always)] fn store_copy( &mut self, - from_path: &impl Path, - to_path: &impl Path, + from_path: &impl Path, + to_path: &impl Path, ) -> Result<(), RuntimeError> { self.host.store_copy(from_path, to_path) } @@ -145,7 +145,7 @@ impl SdkRuntime for EvalHost { } #[inline(always)] - fn store_value_size(&self, path: &impl Path) -> Result { + fn store_value_size(&self, path: &impl Path) -> Result { self.host.store_value_size(path) } @@ -203,7 +203,7 @@ impl SdkRuntime for EvalHost { } impl InternalRuntime for EvalHost { - fn __internal_store_get_hash( + fn __internal_store_get_hash>( &mut self, path: &T, ) -> Result, tezos_smart_rollup_host::runtime::RuntimeError> { @@ -212,7 +212,7 @@ impl InternalRuntime for EvalHost { } impl ExtendedRuntime for EvalHost { - fn store_get_hash( + fn store_get_hash>( &mut self, path: &T, ) -> Result, tezos_smart_rollup_host::runtime::RuntimeError> { diff --git a/etherlink/kernel_bifrost/evm_execution/src/account_storage.rs b/etherlink/kernel_bifrost/evm_execution/src/account_storage.rs index edd710855a0c52739916b1859a92a652a39dd7c9..6526309ee4f12e0ed0b512961a00a9e0fa77e22c 100644 --- a/etherlink/kernel_bifrost/evm_execution/src/account_storage.rs +++ b/etherlink/kernel_bifrost/evm_execution/src/account_storage.rs @@ -6,7 +6,7 @@ //! Ethereum account state and storage use const_decoder::Decoder; -use host::path::{concat, OwnedPath, Path, RefPath}; +use host::path::{concat, force_concat, OwnedPath, Path, RefPath}; use host::runtime::{RuntimeError, ValueType}; use primitive_types::{H160, H256, U256}; use rlp::DecoderError; @@ -120,44 +120,44 @@ pub struct StorageEffect { /// an account. We don't currently require it, and so it's omitted. #[derive(Debug, PartialEq)] pub struct EthereumAccount { - path: OwnedPath, + path: OwnedPath, } -impl From for EthereumAccount { - fn from(path: OwnedPath) -> Self { +impl From> for EthereumAccount { + fn from(path: OwnedPath) -> Self { Self { path } } } /// Path where Ethereum accounts are stored -pub const EVM_ACCOUNTS_PATH: RefPath = +pub const EVM_ACCOUNTS_PATH: RefPath = RefPath::assert_from(b"/evm/world_state/eth_accounts"); /// Path where an account nonce is stored. This should be prefixed with the path to /// where the account is stored for the world state or for the current transaction. -const NONCE_PATH: RefPath = RefPath::assert_from(b"/nonce"); +const NONCE_PATH: RefPath = RefPath::assert_from(b"/nonce"); /// Path where an account balance, ether held, is stored. This should be prefixed with the path to /// where the account is stored for the world state or for the current transaction. -const BALANCE_PATH: RefPath = RefPath::assert_from(b"/balance"); +const BALANCE_PATH: RefPath = RefPath::assert_from(b"/balance"); /// "Internal" accounts - accounts with contract code have a contract code hash. /// This value is computed when the code is stored and kept for future queries. This /// path should be prefixed with the path to /// where the account is stored for the world state or for the current transaction. -const CODE_HASH_PATH: RefPath = RefPath::assert_from(b"/code.hash"); +const CODE_HASH_PATH: RefPath = RefPath::assert_from(b"/code.hash"); /// "Internal" accounts - accounts with contract code, have their code stored here. /// This /// path should be prefixed with the path to /// where the account is stored for the world state or for the current transaction. -const CODE_PATH: RefPath = RefPath::assert_from(b"/code"); +const CODE_PATH: RefPath = RefPath::assert_from(b"/code"); /// The contracts of "internal" accounts have their own storage area. The account /// location prefixed to this path gives the root path (prefix) to where such storage /// values are kept. Each index in durable storage gives one complete path to one /// such 256 bit integer value in storage. -const STORAGE_ROOT_PATH: RefPath = RefPath::assert_from(b"/storage"); +const STORAGE_ROOT_PATH: RefPath = RefPath::assert_from(b"/storage"); /// If a contract tries to read a value from storage and it has previously not written /// anything to this location or if it wrote the default value, then it gets this @@ -182,7 +182,7 @@ const CODE_HASH_BYTES: [u8; WORD_SIZE] = Decoder::Hex pub const CODE_HASH_DEFAULT: H256 = H256(CODE_HASH_BYTES); /// Turn an Ethereum address - a H160 - into a valid path -pub fn account_path(address: &H160) -> Result { +pub fn account_path(address: &H160) -> Result, DurableStorageError> { let path_string = alloc::format!("/{}", hex::encode(address.to_fixed_bytes())); OwnedPath::try_from(path_string).map_err(DurableStorageError::from) } @@ -211,7 +211,7 @@ impl EthereumAccount { /// Get the **nonce** for the Ethereum account. Default value is zero, so an account will /// _always_ have this **nonce**. pub fn nonce(&self, host: &impl Runtime) -> Result { - let path = concat(&self.path, &NONCE_PATH)?; + let path = force_concat(&self.path, &NONCE_PATH)?; read_u64_le_default(host, &path, NONCE_DEFAULT_VALUE) .map_err(AccountStorageError::from) } @@ -223,7 +223,7 @@ impl EthereumAccount { &mut self, host: &mut impl Runtime, ) -> Result<(), AccountStorageError> { - let path = concat(&self.path, &NONCE_PATH)?; + let path = force_concat(&self.path, &NONCE_PATH)?; let old_value = self.nonce(host)?; @@ -241,7 +241,7 @@ impl EthereumAccount { &mut self, host: &mut impl Runtime, ) -> Result<(), AccountStorageError> { - let path = concat(&self.path, &NONCE_PATH)?; + let path = force_concat(&self.path, &NONCE_PATH)?; let old_value = self.nonce(host)?; @@ -258,7 +258,7 @@ impl EthereumAccount { host: &mut impl Runtime, nonce: u64, ) -> Result<(), AccountStorageError> { - let path = concat(&self.path, &NONCE_PATH)?; + let path = force_concat(&self.path, &NONCE_PATH)?; let value_bytes: [u8; NONCE_ENCODING_SIZE] = nonce.to_le_bytes(); @@ -268,7 +268,7 @@ impl EthereumAccount { /// Get the **balance** of an account in Wei held by the account. pub fn balance(&self, host: &impl Runtime) -> Result { - let path = concat(&self.path, &BALANCE_PATH)?; + let path = force_concat(&self.path, &BALANCE_PATH)?; read_u256_le_default(host, &path, BALANCE_DEFAULT_VALUE) .map_err(AccountStorageError::from) } @@ -280,7 +280,7 @@ impl EthereumAccount { host: &mut impl Runtime, amount: U256, ) -> Result<(), AccountStorageError> { - let path = concat(&self.path, &BALANCE_PATH)?; + let path = force_concat(&self.path, &BALANCE_PATH)?; let value = self.balance(host)?; @@ -304,7 +304,7 @@ impl EthereumAccount { host: &mut impl Runtime, amount: U256, ) -> Result { - let path = concat(&self.path, &BALANCE_PATH)?; + let path = force_concat(&self.path, &BALANCE_PATH)?; let value = self.balance(host)?; @@ -326,7 +326,7 @@ impl EthereumAccount { host: &mut impl Runtime, new_balance: U256, ) -> Result<(), AccountStorageError> { - let path = concat(&self.path, &BALANCE_PATH)?; + let path = force_concat(&self.path, &BALANCE_PATH)?; let mut new_balance_bytes: [u8; WORD_SIZE] = [0; WORD_SIZE]; new_balance.to_little_endian(&mut new_balance_bytes); @@ -338,16 +338,16 @@ impl EthereumAccount { /// Get the path to a custom account state section, can be used by precompiles pub fn custom_path( &self, - suffix: &impl Path, - ) -> Result { - concat(&self.path, suffix).map_err(AccountStorageError::from) + suffix: &impl Path, + ) -> Result, AccountStorageError> { + force_concat(&self.path, suffix).map_err(AccountStorageError::from) } /// Get the path to an index in durable storage for an account. - fn storage_path(&self, index: &H256) -> Result { - let storage_path = concat(&self.path, &STORAGE_ROOT_PATH)?; + fn storage_path(&self, index: &H256) -> Result, AccountStorageError> { + let storage_path = force_concat(&self.path, &STORAGE_ROOT_PATH)?; let index_path = path_from_h256(index)?; - concat(&storage_path, &index_path).map_err(AccountStorageError::from) + force_concat(&storage_path, &index_path).map_err(AccountStorageError::from) } /// Clear the entire storage in durable storage for an account. @@ -355,7 +355,7 @@ impl EthereumAccount { &self, host: &mut impl Runtime, ) -> Result<(), AccountStorageError> { - let storage_path = concat(&self.path, &STORAGE_ROOT_PATH)?; + let storage_path = force_concat(&self.path, &STORAGE_ROOT_PATH)?; if host.store_has(&storage_path)?.is_some() { host.store_delete(&storage_path) .map_err(AccountStorageError::from)? @@ -438,7 +438,7 @@ impl EthereumAccount { /// Find whether the account has any code associated with it. pub fn code_exists(&self, host: &impl Runtime) -> Result { - let path = concat(&self.path, &CODE_HASH_PATH)?; + let path = force_concat(&self.path, &CODE_HASH_PATH)?; match host.store_has(&path) { Ok(Some(ValueType::Value | ValueType::ValueWithSubtree)) => Ok(true), @@ -454,7 +454,7 @@ impl EthereumAccount { // There is a possibility here to migrate lazily all code in order // to have all legacy contract uses the new code storage. pub fn code(&self, host: &impl Runtime) -> Result, AccountStorageError> { - let path = concat(&self.path, &CODE_PATH)?; + let path = force_concat(&self.path, &CODE_PATH)?; // If we ever do the lazy migration of code for account // (i.e. moving code from account to code_storage) then this @@ -472,7 +472,7 @@ impl EthereumAccount { /// Get the hash of the code associated with an account. This value is computed and /// stored when the code of a contract is set. pub fn code_hash(&self, host: &impl Runtime) -> Result { - let path = concat(&self.path, &CODE_HASH_PATH)?; + let path = force_concat(&self.path, &CODE_HASH_PATH)?; read_h256_be_default(host, &path, CODE_HASH_DEFAULT) .map_err(AccountStorageError::from) } @@ -480,7 +480,7 @@ impl EthereumAccount { /// Get the size of a contract in number of bytes used for opcodes. This value is /// computed and stored when the code of a contract is set. pub fn code_size(&self, host: &impl Runtime) -> Result { - let path = concat(&self.path, &CODE_PATH)?; + let path = force_concat(&self.path, &CODE_PATH)?; // If we ever do the lazy migration of code for account // (i.e. moving code from account to code_storage) then this // would be dead code. @@ -507,7 +507,7 @@ impl EthereumAccount { } else { let code_hash = code_storage::CodeStorage::add(host, code)?; let code_hash_bytes: [u8; WORD_SIZE] = code_hash.into(); - let code_hash_path = concat(&self.path, &CODE_HASH_PATH)?; + let code_hash_path = force_concat(&self.path, &CODE_HASH_PATH)?; host.store_write_all(&code_hash_path, &code_hash_bytes) .map_err(AccountStorageError::from) } @@ -518,7 +518,7 @@ impl EthereumAccount { &mut self, host: &mut impl Runtime, ) -> Result<(), AccountStorageError> { - let code_hash_path = concat(&self.path, &CODE_HASH_PATH)?; + let code_hash_path = force_concat(&self.path, &CODE_HASH_PATH)?; if let Some(ValueType::Value | ValueType::ValueWithSubtree) = host.store_has(&code_hash_path)? @@ -526,7 +526,7 @@ impl EthereumAccount { host.store_delete(&code_hash_path)? } - let code_path = concat(&self.path, &CODE_PATH)?; + let code_path = force_concat(&self.path, &CODE_PATH)?; if let Some(ValueType::Value | ValueType::ValueWithSubtree) = host.store_has(&code_path)? @@ -542,12 +542,12 @@ impl EthereumAccount { } /// The type of the storage API for accessing the Ethereum World State. -pub type EthereumAccountStorage = Storage; +pub type EthereumAccountStorage = Storage; /// Get the storage API for accessing the Ethereum World State and do transactions /// on it. pub fn init_account_storage() -> Result { - Storage::::init(&EVM_ACCOUNTS_PATH) + Storage::::init(&EVM_ACCOUNTS_PATH) .map_err(AccountStorageError::from) } @@ -1241,12 +1241,12 @@ mod test { let code_hash_bytes: [u8; WORD_SIZE] = sample_code_hash.into(); let code_hash_path = - concat(&a1.path, &CODE_HASH_PATH).expect("Could not get code hash path"); + force_concat(&a1.path, &CODE_HASH_PATH).expect("Could not get code hash path"); host.store_write_all(&code_hash_path, &code_hash_bytes) .expect("Could not write code hash into code hash path"); - let code_path = concat(&a1.path, &CODE_PATH).expect("Could not get code path"); + let code_path = force_concat(&a1.path, &CODE_PATH).expect("Could not get code path"); host.store_write_all(&code_path, &sample_code) .expect("Could not write code into code path"); @@ -1261,7 +1261,7 @@ mod test { .expect("Could not get account") .expect("Account does not exist"); - let code_path = concat(&a1.path, &CODE_PATH).expect("Could not get code path"); + let code_path = force_concat(&a1.path, &CODE_PATH).expect("Could not get code path"); let code = host .store_read_all(&code_path) diff --git a/etherlink/kernel_bifrost/evm_execution/src/code_storage.rs b/etherlink/kernel_bifrost/evm_execution/src/code_storage.rs index 6474eb67edd415d5f387fe090ff3b7b574f24e5e..a702c2ffa667a9e5030a4d8876255c90a019d04a 100644 --- a/etherlink/kernel_bifrost/evm_execution/src/code_storage.rs +++ b/etherlink/kernel_bifrost/evm_execution/src/code_storage.rs @@ -5,22 +5,22 @@ //! Ethereum code storage -use host::path::{concat, OwnedPath, RefPath}; +use host::path::{force_concat, OwnedPath, RefPath}; use primitive_types::{H256, U256}; use tezos_evm_runtime::runtime::Runtime; use tezos_storage::helpers::bytes_hash; use tezos_storage::{error::Error as GenStorageError, read_u64_le, write_u64_le}; /// Path where Ethereum account's code are stored -const EVM_CODES_PATH: RefPath = RefPath::assert_from(b"/evm/world_state/eth_codes"); +const EVM_CODES_PATH: RefPath = RefPath::assert_from(b"/evm/world_state/eth_codes"); /// Path to the number of accounts to use a particular code -const REFERENCE_PATH: RefPath = RefPath::assert_from(b"/ref_count"); +const REFERENCE_PATH: RefPath = RefPath::assert_from(b"/ref_count"); /// Path to the code -const CODE_PATH: RefPath = RefPath::assert_from(b"/code"); +const CODE_PATH: RefPath = RefPath::assert_from(b"/code"); -fn code_hash_path(code_hash: &H256) -> Result { +fn code_hash_path(code_hash: &H256) -> Result, GenStorageError> { let code_hash_hex = hex::encode(code_hash); let code_hash_path_string = format!("/{}", code_hash_hex); OwnedPath::try_from(code_hash_path_string).map_err(Into::into) @@ -28,11 +28,11 @@ fn code_hash_path(code_hash: &H256) -> Result { #[derive(Debug, PartialEq)] pub struct CodeStorage { - path: OwnedPath, + path: OwnedPath, } -impl From for CodeStorage { - fn from(path: OwnedPath) -> Self { +impl From> for CodeStorage { + fn from(path: OwnedPath) -> Self { Self { path } } } @@ -40,7 +40,7 @@ impl From for CodeStorage { impl CodeStorage { fn new(code_hash: &H256) -> Result { let code_hash_path = code_hash_path(code_hash)?; - let path = concat(&EVM_CODES_PATH, &code_hash_path)?; + let path = force_concat(&EVM_CODES_PATH, &code_hash_path)?; Ok(Self { path }) } @@ -50,7 +50,7 @@ impl CodeStorage { } fn get_ref_count(&self, host: &mut impl Runtime) -> Result { - let reference_path = concat(&self.path, &REFERENCE_PATH)?; + let reference_path = force_concat(&self.path, &REFERENCE_PATH)?; read_u64_le(host, &reference_path).or(Ok(0u64)) } @@ -59,7 +59,7 @@ impl CodeStorage { host: &mut impl Runtime, number_ref: u64, ) -> Result<(), GenStorageError> { - let reference_path = concat(&self.path, &REFERENCE_PATH)?; + let reference_path = force_concat(&self.path, &REFERENCE_PATH)?; write_u64_le(host, &reference_path, number_ref)?; Ok(()) } @@ -90,7 +90,7 @@ impl CodeStorage { let code_hash: H256 = bytes_hash(bytecode); let code = Self::new(&code_hash)?; if !code.exists(host)? { - let code_path = concat(&code.path, &CODE_PATH)?; + let code_path = force_concat(&code.path, &CODE_PATH)?; host.store_write_all(&code_path, bytecode)?; }; code.increment_code_usage(host)?; @@ -118,7 +118,7 @@ impl CodeStorage { ) -> Result, GenStorageError> { let code = Self::new(code_hash)?; if code.exists(host)? { - let code1_path = concat(&code.path, &CODE_PATH)?; + let code1_path = force_concat(&code.path, &CODE_PATH)?; host.store_read_all(&code1_path).map_err(Into::into) } else { Ok(vec![]) @@ -131,7 +131,7 @@ impl CodeStorage { ) -> Result { let code = Self::new(code_hash)?; if code.exists(host)? { - let code_path = concat(&code.path, &CODE_PATH)?; + let code_path = force_concat(&code.path, &CODE_PATH)?; host.store_value_size(&code_path) .map(U256::from) .map_err(Into::into) @@ -178,7 +178,7 @@ mod test { CodeStorage::add(&mut host, &code).expect("Could not create code storage"); let code_storage = CodeStorage::new(&code_hash).expect("Could not find code storage"); - let ref_path = concat(&code_storage.path, &REFERENCE_PATH).unwrap(); + let ref_path = force_concat(&code_storage.path, &REFERENCE_PATH).unwrap(); let ref_count = read_u64_le(&host, &ref_path).expect("reference count not found"); assert_eq!(ref_count, 1u64); diff --git a/etherlink/kernel_bifrost/evm_execution/src/fa_bridge/ticket_table.rs b/etherlink/kernel_bifrost/evm_execution/src/fa_bridge/ticket_table.rs index 119b9fa7886f3c429f17c2e441c8eff2780f0d37..b0a67ec936d043727ea596d50a4d7ae8f16fc1ef 100644 --- a/etherlink/kernel_bifrost/evm_execution/src/fa_bridge/ticket_table.rs +++ b/etherlink/kernel_bifrost/evm_execution/src/fa_bridge/ticket_table.rs @@ -10,11 +10,11 @@ use crate::account_storage::{account_path, AccountStorageError, EthereumAccount}; use primitive_types::{H160, H256, U256}; use tezos_evm_runtime::runtime::Runtime; -use tezos_smart_rollup_host::path::{concat, OwnedPath, RefPath}; +use tezos_smart_rollup_host::path::{concat, force_concat, OwnedPath, RefPath}; use tezos_storage::{path_from_h256, read_u256_le_default, write_u256_le}; /// Path where global ticket table is stored -const TICKET_TABLE_PATH: RefPath = RefPath::assert_from(b"/ticket_table"); +const TICKET_TABLE_PATH: RefPath = RefPath::assert_from(b"/ticket_table"); pub trait TicketTable { /// Increases ticket balance @@ -39,9 +39,9 @@ pub trait TicketTable { pub(crate) fn ticket_balance_path( ticket_hash: &H256, address: &H160, -) -> Result { +) -> Result, AccountStorageError> { let suffix = concat(&path_from_h256(ticket_hash)?, &account_path(address)?)?; - concat(&TICKET_TABLE_PATH, &suffix).map_err(Into::into) + force_concat(&TICKET_TABLE_PATH, &suffix).map_err(Into::into) } impl TicketTable for EthereumAccount { diff --git a/etherlink/kernel_bifrost/evm_execution/src/lib.rs b/etherlink/kernel_bifrost/evm_execution/src/lib.rs index efff45a8512016ca064f53cb2f64c9d832463e08..ca2b730f932c5304ec610652d5b37e6d43d71eeb 100755 --- a/etherlink/kernel_bifrost/evm_execution/src/lib.rs +++ b/etherlink/kernel_bifrost/evm_execution/src/lib.rs @@ -419,7 +419,7 @@ where Ok(None) } } -pub const NATIVE_TOKEN_TICKETER_PATH: RefPath = +pub const NATIVE_TOKEN_TICKETER_PATH: RefPath = RefPath::assert_from(b"/evm/world_state/ticketer"); /// Reads the ticketer address set by the installer, if any, encoded in b58. diff --git a/etherlink/kernel_bifrost/evm_execution/src/storage.rs b/etherlink/kernel_bifrost/evm_execution/src/storage.rs index 11f5c431bca8dae03774fe5a137e3f4ea9df2f50..dde7f349df8fd29ce3b1c72d779bbf8c519c0ffe 100644 --- a/etherlink/kernel_bifrost/evm_execution/src/storage.rs +++ b/etherlink/kernel_bifrost/evm_execution/src/storage.rs @@ -20,12 +20,12 @@ pub mod tracer { use crate::trace::CallTrace; use crate::trace::StructLog; - const EVM_TRACE: RefPath = RefPath::assert_from(b"/evm/trace"); + const EVM_TRACE: RefPath = RefPath::assert_from(b"/evm/trace"); - const GAS: RefPath = RefPath::assert_from(b"/gas"); - const FAILED: RefPath = RefPath::assert_from(b"/failed"); - const RETURN_VALUE: RefPath = RefPath::assert_from(b"/return_value"); - const STRUCT_LOGS: RefPath = RefPath::assert_from(b"/struct_logs"); + const GAS: RefPath = RefPath::assert_from(b"/gas"); + const FAILED: RefPath = RefPath::assert_from(b"/failed"); + const RETURN_VALUE: RefPath = RefPath::assert_from(b"/return_value"); + const STRUCT_LOGS: RefPath = RefPath::assert_from(b"/struct_logs"); #[derive(Eq, Error, Debug, PartialEq)] pub enum Error { @@ -39,8 +39,8 @@ pub mod tracer { pub fn trace_tx_path( hash: &Option, - field: &RefPath, - ) -> Result { + field: &RefPath, + ) -> Result, Error> { let trace_tx_path = match hash { None => EVM_TRACE.into(), Some(hash) => { @@ -50,7 +50,7 @@ pub mod tracer { concat(&EVM_TRACE, &tx_path)? } }; - concat(&trace_tx_path, field).map_err(Error::PathError) + force_concat(&trace_tx_path, field).map_err(Error::PathError) } pub fn store_trace_gas( @@ -101,7 +101,7 @@ pub mod tracer { Ok(()) } - const CALL_TRACE: RefPath = RefPath::assert_from(b"/call_trace"); + const CALL_TRACE: RefPath = RefPath::assert_from(b"/call_trace"); pub fn store_call_trace( host: &mut Host, @@ -167,7 +167,7 @@ pub mod blocks { /// Get block hash by block number. pub fn get_block_hash( - host: &impl Runtime, + host: &impl Runtime, block_number: U256, ) -> Result { let block_path = to_block_hash_path(block_number)?; @@ -180,7 +180,7 @@ pub mod blocks { } } - fn to_block_hash_path(block_number: U256) -> Result { + fn to_block_hash_path(block_number: U256) -> Result, EvmBlockStorageError> { let path: Vec = format!("/evm/world_state/indexes/blocks/{}", block_number).into(); let owned_path = OwnedPath::try_from(path)?; diff --git a/etherlink/kernel_bifrost/evm_execution/src/withdrawal_counter.rs b/etherlink/kernel_bifrost/evm_execution/src/withdrawal_counter.rs index d40d220b32bb86ba02fa926270697179f2530ca6..2620b6dff5ed1338cbc26a45c2501a628ed7f83a 100644 --- a/etherlink/kernel_bifrost/evm_execution/src/withdrawal_counter.rs +++ b/etherlink/kernel_bifrost/evm_execution/src/withdrawal_counter.rs @@ -19,7 +19,7 @@ use tezos_storage::{read_u256_le_default, write_u256_le}; use crate::account_storage::{AccountStorageError, EthereumAccount}; /// Path where withdrawal counter is stored (relative to account) -pub const WITHDRAWAL_COUNTER_PATH: RefPath = RefPath::assert_from(b"/withdrawal_counter"); +pub const WITHDRAWAL_COUNTER_PATH: RefPath = RefPath::assert_from(b"/withdrawal_counter"); pub trait WithdrawalCounter { /// Returns current withdrawal ID from the storage (or 0 if it's not initialized) diff --git a/etherlink/kernel_bifrost/indexable_storage/src/lib.rs b/etherlink/kernel_bifrost/indexable_storage/src/lib.rs index 383d8733762318c003013d6132abd08f5e9e6f83..7a827eb3442988b2eb65aa6630479fb8e152dfb7 100644 --- a/etherlink/kernel_bifrost/indexable_storage/src/lib.rs +++ b/etherlink/kernel_bifrost/indexable_storage/src/lib.rs @@ -7,13 +7,13 @@ use rlp::DecoderError; use tezos_evm_logging::log; use tezos_evm_logging::Level::Error; use tezos_evm_runtime::runtime::Runtime; -use tezos_smart_rollup_host::path::{concat, OwnedPath, PathError, RefPath}; +use tezos_smart_rollup_host::path::{force_concat, OwnedPath, PathError, RefPath}; use tezos_smart_rollup_host::runtime::RuntimeError; use tezos_smart_rollup_storage::StorageError; use tezos_storage::{error::Error as GenStorageError, read_u64_le, write_u64_le}; use thiserror::Error; -const LENGTH: RefPath = RefPath::assert_from(b"/length"); +const LENGTH: RefPath = RefPath::assert_from(b"/length"); /// An indexable storage is a push-only mapping between increasing integers to /// bytes. It can serve as a replacement for the combination of the host @@ -22,7 +22,7 @@ pub struct IndexableStorage { /// An indexable storage is stored at a given path and consists of: /// - `/length`: the number of keys /// - `/` where keys are from `0` to `length - 1` - pub path: OwnedPath, + pub path: OwnedPath, } #[derive(Error, Debug, Eq, PartialEq)] @@ -58,20 +58,20 @@ impl From for IndexableStorageError { } impl IndexableStorage { - pub fn new(path: &RefPath<'_>) -> Result { + pub fn new(path: &RefPath<'_, true>) -> Result { Ok(Self { path: path.into() }) } - pub fn new_owned_path(path: OwnedPath) -> Self { + pub fn new_owned_path(path: OwnedPath) -> Self { Self { path } } - fn value_path(&self, index: u64) -> Result { + fn value_path(&self, index: u64) -> Result, PathError> { let index_as_path: Vec = format!("/{}", index).into(); // The key being an integer value, it will always be valid as a path, // `assert_from` cannot fail. let index_subkey = RefPath::assert_from(&index_as_path); - concat(&self.path, &index_subkey) + force_concat(&self.path, &index_subkey) } fn store_index( @@ -89,7 +89,7 @@ impl IndexableStorage { &self, host: &mut Host, ) -> Result { - let path = concat(&self.path, &LENGTH)?; + let path = force_concat(&self.path, &LENGTH)?; let length = read_u64_le(host, &path).unwrap_or(0); write_u64_le(host, &path, length + 1)?; Ok(length) @@ -102,7 +102,7 @@ impl IndexableStorage { &self, host: &Host, ) -> Result { - let path = concat(&self.path, &LENGTH)?; + let path = force_concat(&self.path, &LENGTH)?; match read_u64_le(host, &path) { Ok(l) => Ok(l), Err( diff --git a/etherlink/kernel_bifrost/kernel/src/apply.rs b/etherlink/kernel_bifrost/kernel/src/apply.rs index af0d9971a1fa546fcf7620767be4b59510f713c2..b17b6338a7babf47fae675590507d9ada844c77a 100644 --- a/etherlink/kernel_bifrost/kernel/src/apply.rs +++ b/etherlink/kernel_bifrost/kernel/src/apply.rs @@ -512,9 +512,12 @@ fn apply_fa_deposit( })) } -pub const WITHDRAWAL_OUTBOX_QUEUE: RefPath = +pub const WITHDRAWAL_OUTBOX_QUEUE: RefPath = RefPath::assert_from(b"/evm/world_state/__outbox_queue"); +pub const WITHDRAWAL_OUTBOX_QUEUE_META: RefPath = + RefPath::assert_from(b"/evm/world_state/__outbox_queue/meta"); + pub struct ExecutionInfo { pub receipt_info: TransactionReceiptInfo, pub object_info: TransactionObjectInfo, @@ -539,7 +542,7 @@ impl From> for ExecutionResult { #[allow(clippy::too_many_arguments)] pub fn handle_transaction_result( host: &mut Host, - outbox_queue: &OutboxQueue<'_, impl Path>, + outbox_queue: &OutboxQueue<'_, true, impl Path>, block_constants: &BlockConstants, transaction: &Transaction, index: u32, @@ -599,7 +602,7 @@ pub fn handle_transaction_result( #[allow(clippy::too_many_arguments)] pub fn apply_transaction( host: &mut Host, - outbox_queue: &OutboxQueue<'_, impl Path>, + outbox_queue: &OutboxQueue<'_, true, impl Path>, block_constants: &BlockConstants, precompiles: &PrecompileBTreeMap, transaction: &Transaction, diff --git a/etherlink/kernel_bifrost/kernel/src/block.rs b/etherlink/kernel_bifrost/kernel/src/block.rs index 1d3e53687255308a2cf5deac687cd5faaaa4c22a..e855148db41e2dae54cb8b7fb4091092c28d1fad 100644 --- a/etherlink/kernel_bifrost/kernel/src/block.rs +++ b/etherlink/kernel_bifrost/kernel/src/block.rs @@ -6,7 +6,7 @@ // SPDX-License-Identifier: MIT use crate::apply::{ - apply_transaction, ExecutionInfo, ExecutionResult, WITHDRAWAL_OUTBOX_QUEUE, + apply_transaction, ExecutionInfo, ExecutionResult, WITHDRAWAL_OUTBOX_QUEUE, WITHDRAWAL_OUTBOX_QUEUE_META, }; use crate::block_storage; use crate::blueprint_storage::{drop_blueprint, read_next_blueprint}; @@ -76,7 +76,7 @@ pub enum ComputationResult { #[allow(clippy::too_many_arguments)] fn compute( host: &mut Host, - outbox_queue: &OutboxQueue<'_, impl Path>, + outbox_queue: &OutboxQueue<'_, true, impl Path>, block_in_progress: &mut BlockInProgress, block_constants: &BlockConstants, precompiles: &PrecompileBTreeMap, @@ -252,7 +252,7 @@ fn next_bip_from_blueprints( #[allow(clippy::too_many_arguments)] fn compute_bip( host: &mut Host, - outbox_queue: &OutboxQueue<'_, impl Path>, + outbox_queue: &OutboxQueue<'_, true, impl Path>, mut block_in_progress: BlockInProgress, current_block_number: &mut U256, current_block_parent_hash: &mut H256, @@ -351,7 +351,7 @@ fn clean_delayed_transactions( fn promote_block( safe_host: &mut SafeStorage<&mut Host>, - outbox_queue: &OutboxQueue<'_, impl Path>, + outbox_queue: &OutboxQueue<'_, true, impl Path>, block_in_progress: bool, number: U256, config: &mut Configuration, @@ -386,7 +386,7 @@ fn promote_block( Ok(()) } -const AT_MOST_ONE_BLOCK: RefPath = RefPath::assert_from(b"/__at_most_one_block"); +const AT_MOST_ONE_BLOCK: RefPath = RefPath::assert_from(b"/__at_most_one_block"); pub fn produce( host: &mut Host, @@ -424,7 +424,7 @@ pub fn produce( let at_most_one_block = host.store_has(&AT_MOST_ONE_BLOCK)?.is_some(); let mut safe_host = SafeStorage { host }; - let outbox_queue = OutboxQueue::new(&WITHDRAWAL_OUTBOX_QUEUE, u32::MAX)?; + let outbox_queue = OutboxQueue::new(&WITHDRAWAL_OUTBOX_QUEUE, &WITHDRAWAL_OUTBOX_QUEUE_META, u32::MAX)?; let precompiles = precompiles::precompile_set::>(config.enable_fa_bridge); diff --git a/etherlink/kernel_bifrost/kernel/src/block_in_progress.rs b/etherlink/kernel_bifrost/kernel/src/block_in_progress.rs index ea5ed9ee5ff952d34475698f5d1a97cedf29f32a..a7d806b48bb04245a1427502955257a6977aeb89 100644 --- a/etherlink/kernel_bifrost/kernel/src/block_in_progress.rs +++ b/etherlink/kernel_bifrost/kernel/src/block_in_progress.rs @@ -28,7 +28,7 @@ use tezos_ethereum::Bloom; use tezos_evm_logging::{log, Level::*}; use tezos_evm_runtime::runtime::Runtime; use tezos_smart_rollup_encoding::timestamp::Timestamp; -use tezos_smart_rollup_host::path::{concat, RefPath}; +use tezos_smart_rollup_host::path::{force_concat, RefPath}; #[derive(Debug, PartialEq, Clone)] /// Container for all data needed during block computation @@ -313,7 +313,7 @@ impl BlockInProgress { fn safe_store_get_hash( host: &mut Host, - path: &RefPath, + path: &RefPath, ) -> Result, anyhow::Error> { match host.store_get_hash(path) { Ok(hash) => Ok(hash), @@ -321,8 +321,8 @@ impl BlockInProgress { } } - const RECEIPTS: RefPath<'static> = RefPath::assert_from(b"/receipts"); - const RECEIPTS_PREVIOUS_ROOT: RefPath<'static> = + const RECEIPTS: RefPath<'static, true> = RefPath::assert_from(b"/receipts"); + const RECEIPTS_PREVIOUS_ROOT: RefPath<'static, true> = RefPath::assert_from(b"/receipts/previous_root"); fn receipts_root( @@ -335,7 +335,7 @@ impl BlockInProgress { } else { for hash in &self.valid_txs { let receipt_path = receipt_path(hash)?; - let new_receipt_path = concat(&Self::RECEIPTS, &receipt_path)?; + let new_receipt_path = force_concat(&Self::RECEIPTS, &receipt_path)?; host.store_copy(&receipt_path, &new_receipt_path)?; } host.store_write_all(&Self::RECEIPTS_PREVIOUS_ROOT, &previous_receipts_root)?; @@ -345,8 +345,8 @@ impl BlockInProgress { } } - const OBJECTS: RefPath<'static> = RefPath::assert_from(b"/objects"); - const OBJECTS_PREVIOUS_ROOT: RefPath<'static> = + const OBJECTS: RefPath<'static, true> = RefPath::assert_from(b"/objects"); + const OBJECTS_PREVIOUS_ROOT: RefPath<'static, true> = RefPath::assert_from(b"/objects/previous_root"); fn transactions_root( @@ -359,7 +359,7 @@ impl BlockInProgress { } else { for hash in &self.valid_txs { let object_path = object_path(hash)?; - let new_object_path = concat(&Self::OBJECTS, &object_path)?; + let new_object_path = force_concat(&Self::OBJECTS, &object_path)?; host.store_copy(&object_path, &new_object_path)?; } host.store_write_all( diff --git a/etherlink/kernel_bifrost/kernel/src/block_storage.rs b/etherlink/kernel_bifrost/kernel/src/block_storage.rs index b423f3565b24211b0a4291a2cda0103d9ba9beee..7af312ee37c6ceec047b25a6443a56eb31f35eaf 100644 --- a/etherlink/kernel_bifrost/kernel/src/block_storage.rs +++ b/etherlink/kernel_bifrost/kernel/src/block_storage.rs @@ -16,18 +16,18 @@ use tezos_storage::{read_h256_be, read_u256_le, write_h256_be, write_u256_le}; mod path { use super::*; - const PATH: RefPath = RefPath::assert_from(b"/evm/world_state/blocks"); + const PATH: RefPath = RefPath::assert_from(b"/evm/world_state/blocks"); - pub const CURRENT_NUMBER: RefPath = + pub const CURRENT_NUMBER: RefPath = RefPath::assert_from(b"/evm/world_state/blocks/current/number"); - pub const CURRENT_HASH: RefPath = + pub const CURRENT_HASH: RefPath = RefPath::assert_from(b"/evm/world_state/blocks/current/hash"); - pub const INDEXES: RefPath = RefPath::assert_from(b"/evm/world_state/indexes/blocks"); + pub const INDEXES: RefPath = RefPath::assert_from(b"/evm/world_state/indexes/blocks"); /// Path to the block in the storage. The path to the block is /// indexed by its hash. - pub fn path(hash: H256) -> anyhow::Result { + pub fn path(hash: H256) -> anyhow::Result> { let hash = hex::encode(hash); let raw_hash_path: Vec = format!("/{}", &hash).into(); let hash_path = OwnedPath::try_from(raw_hash_path)?; diff --git a/etherlink/kernel_bifrost/kernel/src/blueprint_storage.rs b/etherlink/kernel_bifrost/kernel/src/blueprint_storage.rs index 690213da5cc76604943d33287b55c7280995726a..78bdcc658c8b702a03b6ae202ff17794b71e1224 100644 --- a/etherlink/kernel_bifrost/kernel/src/blueprint_storage.rs +++ b/etherlink/kernel_bifrost/kernel/src/blueprint_storage.rs @@ -29,9 +29,9 @@ use tezos_storage::{ error::Error as GenStorageError, read_rlp, store_read_slice, store_rlp, }; -pub const EVM_BLUEPRINTS: RefPath = RefPath::assert_from(b"/evm/blueprints"); +pub const EVM_BLUEPRINTS: RefPath = RefPath::assert_from(b"/evm/blueprints"); -const EVM_BLUEPRINT_NB_CHUNKS: RefPath = RefPath::assert_from(b"/nb_chunks"); +const EVM_BLUEPRINT_NB_CHUNKS: RefPath = RefPath::assert_from(b"/nb_chunks"); /// The store representation of a blueprint. /// It's designed to support storing sequencer blueprints, @@ -87,32 +87,32 @@ impl Decodable for StoreBlueprint { } } -pub fn blueprint_path(number: U256) -> Result { +pub fn blueprint_path(number: U256) -> Result, StorageError> { let number_as_path: Vec = format!("/{}", number).into(); // The key being an integer value, it will always be valid as a path, // `assert_from` cannot fail. let number_subkey = RefPath::assert_from(&number_as_path); - concat(&EVM_BLUEPRINTS, &number_subkey).map_err(StorageError::from) + force_concat(&EVM_BLUEPRINTS, &number_subkey).map_err(StorageError::from) } fn blueprint_chunk_path( - blueprint_path: &OwnedPath, + blueprint_path: &OwnedPath, chunk_index: u16, -) -> Result { +) -> Result, StorageError> { let chunk_index_as_path: Vec = format!("/{}", chunk_index).into(); let chunk_index_subkey = RefPath::assert_from(&chunk_index_as_path); - concat(blueprint_path, &chunk_index_subkey).map_err(StorageError::from) + force_concat(blueprint_path, &chunk_index_subkey).map_err(StorageError::from) } fn blueprint_nb_chunks_path( - blueprint_path: &OwnedPath, -) -> Result { - concat(blueprint_path, &EVM_BLUEPRINT_NB_CHUNKS).map_err(StorageError::from) + blueprint_path: &OwnedPath, +) -> Result, StorageError> { + force_concat(blueprint_path, &EVM_BLUEPRINT_NB_CHUNKS).map_err(StorageError::from) } fn read_blueprint_nb_chunks( host: &Host, - blueprint_path: &OwnedPath, + blueprint_path: &OwnedPath, ) -> Result { let path = blueprint_nb_chunks_path(blueprint_path)?; let mut buffer = [0u8; 2]; @@ -122,7 +122,7 @@ fn read_blueprint_nb_chunks( fn store_blueprint_nb_chunks( host: &mut Host, - blueprint_path: &OwnedPath, + blueprint_path: &OwnedPath, nb_chunks: u16, ) -> Result<(), Error> { let path = blueprint_nb_chunks_path(blueprint_path)?; @@ -345,7 +345,7 @@ fn parse_and_validate_blueprint( fn invalidate_blueprint( host: &mut Host, - blueprint_path: &OwnedPath, + blueprint_path: &OwnedPath, error: &BlueprintValidity, ) -> Result<(), RuntimeError> { log!( @@ -361,7 +361,7 @@ fn invalidate_blueprint( fn read_all_chunks_and_validate( host: &mut Host, - blueprint_path: &OwnedPath, + blueprint_path: &OwnedPath, nb_chunks: u16, config: &mut Configuration, ) -> anyhow::Result<(Option, usize)> { diff --git a/etherlink/kernel_bifrost/kernel/src/delayed_inbox.rs b/etherlink/kernel_bifrost/kernel/src/delayed_inbox.rs index a5a97bc54cf352955c51be4eb83d40b01209d16e..d5181b793d09d9d7e4bf3de2e4a3f6ffa8acbf95 100644 --- a/etherlink/kernel_bifrost/kernel/src/delayed_inbox.rs +++ b/etherlink/kernel_bifrost/kernel/src/delayed_inbox.rs @@ -24,7 +24,7 @@ use tezos_smart_rollup_host::path::RefPath; pub struct DelayedInbox(LinkedList); -pub const DELAYED_INBOX_PATH: RefPath = RefPath::assert_from(b"/evm/delayed-inbox"); +pub const DELAYED_INBOX_PATH: RefPath = RefPath::assert_from(b"/evm/delayed-inbox"); // Maximum number of transaction included in a blueprint when // forcing timed-out transactions from the delayed inbox. diff --git a/etherlink/kernel_bifrost/kernel/src/evm_node_entrypoint.rs b/etherlink/kernel_bifrost/kernel/src/evm_node_entrypoint.rs index f2910ac177aa9ce88338eb77090d87ddae436999..7bd4884e49d0696d1a9012f3bdb5996b0f7dafa3 100644 --- a/etherlink/kernel_bifrost/kernel/src/evm_node_entrypoint.rs +++ b/etherlink/kernel_bifrost/kernel/src/evm_node_entrypoint.rs @@ -13,10 +13,10 @@ use tezos_ethereum::rlp_helpers::FromRlpBytes; use tezos_evm_runtime::{internal_runtime::InternalRuntime, runtime::KernelHost}; use tezos_smart_rollup_host::{path::RefPath, runtime::Runtime}; -const DELAYED_INPUT_PATH: RefPath = RefPath::assert_from(b"/__delayed_input"); +const DELAYED_INPUT_PATH: RefPath = RefPath::assert_from(b"/__delayed_input"); pub fn populate_delayed_inbox< - Host: tezos_smart_rollup_host::runtime::Runtime + InternalRuntime, + Host: tezos_smart_rollup_host::runtime::Runtime + InternalRuntime, >( sdk_host: &mut Host, ) { diff --git a/etherlink/kernel_bifrost/kernel/src/fallback_upgrade.rs b/etherlink/kernel_bifrost/kernel/src/fallback_upgrade.rs index d1a8caecfb6f9fec1c786660d7b2559e2fd32381..170c7fce4ac8e93fd8d494c556b72c39234e1a76 100644 --- a/etherlink/kernel_bifrost/kernel/src/fallback_upgrade.rs +++ b/etherlink/kernel_bifrost/kernel/src/fallback_upgrade.rs @@ -9,10 +9,10 @@ use tezos_smart_rollup_host::{path::RefPath, runtime::RuntimeError, KERNEL_BOOT_ use crate::upgrade::KERNEL_ROOT_HASH; -const BACKUP_KERNEL_BOOT_PATH: RefPath = +const BACKUP_KERNEL_BOOT_PATH: RefPath = RefPath::assert_from(b"/__backup_kernel/boot.wasm"); -const BACKUP_KERNEL_ROOT_HASH: RefPath = +const BACKUP_KERNEL_ROOT_HASH: RefPath = RefPath::assert_from(b"/__backup_kernel/root_hash"); pub fn backup_current_kernel(host: &mut impl Runtime) -> Result<(), RuntimeError> { diff --git a/etherlink/kernel_bifrost/kernel/src/lib.rs b/etherlink/kernel_bifrost/kernel/src/lib.rs index 426aaa40c4b35686616c9efc6afa75d68c190d72..0c3fedba2aee9d1f44f9ff2e74f281901ef35b55 100644 --- a/etherlink/kernel_bifrost/kernel/src/lib.rs +++ b/etherlink/kernel_bifrost/kernel/src/lib.rs @@ -305,7 +305,7 @@ pub fn main(host: &mut Host) -> Result<(), anyhow::Error> { Ok(()) } -pub fn kernel_loop( +pub fn kernel_loop + InternalRuntime>( host: &mut Host, ) { // In order to setup the temporary directory, we need to move something diff --git a/etherlink/kernel_bifrost/kernel/src/linked_list.rs b/etherlink/kernel_bifrost/kernel/src/linked_list.rs index 8ec7d95f9c9a0c5f1a83b0c2d3458c77dfd9da2a..3a30786707684233aeb14515f340090cddcfcb8d 100644 --- a/etherlink/kernel_bifrost/kernel/src/linked_list.rs +++ b/etherlink/kernel_bifrost/kernel/src/linked_list.rs @@ -22,7 +22,7 @@ where Elt: Encodable + Decodable + Clone, { /// Absolute path to queue - path: OwnedPath, + path: OwnedPath, /// None indicates an empty list pointers: Option>, _type: PhantomData<(Id, Elt)>, @@ -50,7 +50,7 @@ struct Pointer { fn decode_path( it: &mut RlpIterator, field_name: &'static str, -) -> Result { +) -> Result, rlp::DecoderError> { let path: Vec = decode_field(&next(it)?, field_name)?; OwnedPath::try_from(path).map_err(|_| DecoderError::Custom("not a path")) } @@ -150,7 +150,7 @@ where impl, Elt: Encodable + Decodable> Pointer { - fn pointer_path(id: &Id, prefix: &impl Path) -> Result { + fn pointer_path(id: &Id, prefix: &impl Path) -> Result> { let path = hex::encode(id); let path: Vec = format!("/{}/pointer", path).into(); let path = OwnedPath::try_from(path)?; @@ -161,12 +161,12 @@ impl, Elt: Encodable + Decodable> /// Path to the pointer /// /// This path is used when you want to read a pointer or to remove it. - fn path(&self, prefix: &impl Path) -> Result { + fn path(&self, prefix: &impl Path) -> Result> { Self::pointer_path(&self.id, prefix) } /// Path to the data held by the pointer. - fn data_path(&self, prefix: &impl Path) -> Result { + fn data_path(&self, prefix: &impl Path) -> Result> { let path = hex::encode(&self.id); let path: Vec = format!("/{}/data", path).into(); let path = OwnedPath::try_from(path)?; @@ -177,25 +177,25 @@ impl, Elt: Encodable + Decodable> fn save_data( &self, host: &mut impl Runtime, - prefix: &impl Path, + prefix: &impl Path, data: &Elt, ) -> Result<()> { let path = self.data_path(prefix)?; store_rlp(data, host, &path).context("cannot save the pointer's data") } - fn get_data(&self, host: &impl Runtime, prefix: &impl Path) -> Result { + fn get_data(&self, host: &impl Runtime, prefix: &impl Path) -> Result { let path = self.data_path(prefix)?; read_rlp(host, &path).context("cannot read the pointer's data") } /// Load the pointer from the durable storage - fn read(host: &impl Runtime, prefix: &impl Path, id: &Id) -> Result> { + fn read(host: &impl Runtime, prefix: &impl Path, id: &Id) -> Result> { read_optional_rlp(host, &Self::pointer_path(id, prefix)?) } /// Save the pointer in the durable storage - fn save(&self, host: &mut impl Runtime, prefix: &impl Path) -> Result<()> { + fn save(&self, host: &mut impl Runtime, prefix: &impl Path) -> Result<()> { store_rlp(self, host, &self.path(prefix)?) .context("cannot save pointer to storage") } @@ -204,7 +204,7 @@ impl, Elt: Encodable + Decodable> fn remove_with_data( &self, host: &mut impl Runtime, - prefix: &impl Path, + prefix: &impl Path, ) -> Result<()> { let path = hex::encode(&self.id); let path: Vec = format!("/{}", path).into(); @@ -224,7 +224,7 @@ where /// Load a list from the storage. /// If the list does not exist, a new empty list is created. /// Otherwise the existing list is read from the storage. - pub fn new(path: &impl Path, host: &impl Runtime) -> Result { + pub fn new(path: &impl Path, host: &impl Runtime) -> Result { let list = Self::read(host, path)?.unwrap_or(Self { path: path.into(), pointers: None, @@ -234,7 +234,7 @@ where } /// Path to the metadata that defines the list. - fn metadata_path(root: &impl Path) -> Result { + fn metadata_path(root: &impl Path) -> Result> { let meta_path: Vec = "/meta".into(); let meta_path = OwnedPath::try_from(meta_path)?; let path = concat(root, &meta_path)?; @@ -250,7 +250,7 @@ where } /// Load the LinkedList from the durable storage. - fn read(host: &impl Runtime, path: &impl Path) -> Result> { + fn read(host: &impl Runtime, path: &impl Path) -> Result> { let path = Self::metadata_path(path)?; read_optional_rlp(host, &path) } @@ -539,7 +539,7 @@ mod tests { fn traverse_f( host: &MockKernelHost, - list_path: &OwnedPath, + list_path: &OwnedPath, pointer: Pointer, f: &dyn Fn(&Pointer) -> Option, ) -> (Pointer, usize) { diff --git a/etherlink/kernel_bifrost/kernel/src/reveal_storage.rs b/etherlink/kernel_bifrost/kernel/src/reveal_storage.rs index 294670a9728202bf8700366474f7d2d8a3623396..11da770422ac7068f4411d3840f33804cc0a23ef 100644 --- a/etherlink/kernel_bifrost/kernel/src/reveal_storage.rs +++ b/etherlink/kernel_bifrost/kernel/src/reveal_storage.rs @@ -17,11 +17,11 @@ use tezos_smart_rollup_host::runtime::ValueType; /// chain into a new deployment. It is not tick-safe, and should obviously not be used in a /// production setup. -const CONFIG_PATH: RefPath = RefPath::assert_from(b"/__tmp/reveal_config"); +const CONFIG_PATH: RefPath = RefPath::assert_from(b"/__tmp/reveal_config"); #[derive(Debug)] struct Set { - to: OwnedPath, + to: OwnedPath, value: Vec, } @@ -36,8 +36,8 @@ impl Decodable for Set { let mut it = decoder.iter(); let to: Vec = decode_field(&next(&mut it)?, "to")?; - let to: RefPath = RefPath::assert_from(&to); - let to: OwnedPath = to.into(); + let to: RefPath = RefPath::assert_from(&to); + let to: OwnedPath = to.into(); let value: Vec = decode_field(&next(&mut it)?, "value")?; Ok(Self { to, value }) diff --git a/etherlink/kernel_bifrost/kernel/src/storage.rs b/etherlink/kernel_bifrost/kernel/src/storage.rs index 4fcdc259c55515e3b8035e02d18640747bb2a1c3..93056d88af7fa5278c4abd3e8fd069b950a91dd3 100644 --- a/etherlink/kernel_bifrost/kernel/src/storage.rs +++ b/etherlink/kernel_bifrost/kernel/src/storage.rs @@ -70,115 +70,115 @@ impl StorageVersion { pub const STORAGE_VERSION: StorageVersion = StorageVersion::V22; -pub const PRIVATE_FLAG_PATH: RefPath = RefPath::assert_from(b"/evm/remove_whitelist"); +pub const PRIVATE_FLAG_PATH: RefPath = RefPath::assert_from(b"/evm/remove_whitelist"); -pub const STORAGE_VERSION_PATH: RefPath = RefPath::assert_from(b"/evm/storage_version"); +pub const STORAGE_VERSION_PATH: RefPath = RefPath::assert_from(b"/evm/storage_version"); -const KERNEL_VERSION_PATH: RefPath = RefPath::assert_from(b"/evm/kernel_version"); +const KERNEL_VERSION_PATH: RefPath = RefPath::assert_from(b"/evm/kernel_version"); -pub const ADMIN: RefPath = RefPath::assert_from(b"/evm/admin"); -pub const SEQUENCER_GOVERNANCE: RefPath = +pub const ADMIN: RefPath = RefPath::assert_from(b"/evm/admin"); +pub const SEQUENCER_GOVERNANCE: RefPath = RefPath::assert_from(b"/evm/sequencer_governance"); -pub const KERNEL_GOVERNANCE: RefPath = RefPath::assert_from(b"/evm/kernel_governance"); -pub const KERNEL_SECURITY_GOVERNANCE: RefPath = +pub const KERNEL_GOVERNANCE: RefPath = RefPath::assert_from(b"/evm/kernel_governance"); +pub const KERNEL_SECURITY_GOVERNANCE: RefPath = RefPath::assert_from(b"/evm/kernel_security_governance"); -pub const DELAYED_BRIDGE: RefPath = RefPath::assert_from(b"/evm/delayed_bridge"); +pub const DELAYED_BRIDGE: RefPath = RefPath::assert_from(b"/evm/delayed_bridge"); -pub const MAXIMUM_ALLOWED_TICKS: RefPath = +pub const MAXIMUM_ALLOWED_TICKS: RefPath = RefPath::assert_from(b"/evm/maximum_allowed_ticks"); -pub const MAXIMUM_GAS_PER_TRANSACTION: RefPath = +pub const MAXIMUM_GAS_PER_TRANSACTION: RefPath = RefPath::assert_from(b"/evm/maximum_gas_per_transaction"); // Path to the block in progress, used between reboots -const EVM_BLOCK_IN_PROGRESS: RefPath = +const EVM_BLOCK_IN_PROGRESS: RefPath = RefPath::assert_from(b"/evm/world_state/blocks/in_progress"); -const EVENTS: RefPath = RefPath::assert_from(b"/evm/events"); +const EVENTS: RefPath = RefPath::assert_from(b"/evm/events"); -pub const EVM_TRANSACTIONS_RECEIPTS: RefPath = +pub const EVM_TRANSACTIONS_RECEIPTS: RefPath = RefPath::assert_from(b"/evm/world_state/transactions_receipts"); -pub const EVM_TRANSACTIONS_OBJECTS: RefPath = +pub const EVM_TRANSACTIONS_OBJECTS: RefPath = RefPath::assert_from(b"/evm/world_state/transactions_objects"); -const EVM_CHAIN_ID: RefPath = RefPath::assert_from(b"/evm/chain_id"); +const EVM_CHAIN_ID: RefPath = RefPath::assert_from(b"/evm/chain_id"); -pub const EVM_BASE_FEE_PER_GAS: RefPath = +pub const EVM_BASE_FEE_PER_GAS: RefPath = RefPath::assert_from(b"/evm/world_state/fees/base_fee_per_gas"); -const EVM_MINIMUM_BASE_FEE_PER_GAS: RefPath = +const EVM_MINIMUM_BASE_FEE_PER_GAS: RefPath = RefPath::assert_from(b"/evm/world_state/fees/minimum_base_fee_per_gas"); -const EVM_DA_FEE: RefPath = +const EVM_DA_FEE: RefPath = RefPath::assert_from(b"/evm/world_state/fees/da_fee_per_byte"); -const TICK_BACKLOG_PATH: RefPath = RefPath::assert_from(b"/evm/world_state/fees/backlog"); -const TICK_BACKLOG_TIMESTAMP_PATH: RefPath = +const TICK_BACKLOG_PATH: RefPath = RefPath::assert_from(b"/evm/world_state/fees/backlog"); +const TICK_BACKLOG_TIMESTAMP_PATH: RefPath = RefPath::assert_from(b"/evm/world_state/fees/last_timestamp"); /// The sequencer pool is the designated account that the data-availability fees are sent to. /// /// This may be updated by the governance mechanism over time. If it is not set, the data-availability /// fees are instead burned. -pub const SEQUENCER_POOL_PATH: RefPath = +pub const SEQUENCER_POOL_PATH: RefPath = RefPath::assert_from(b"/evm/sequencer_pool_address"); /// Path to the last L1 level seen. -const EVM_L1_LEVEL: RefPath = RefPath::assert_from(b"/evm/l1_level"); +const EVM_L1_LEVEL: RefPath = RefPath::assert_from(b"/evm/l1_level"); -const EVM_BURNED_FEES: RefPath = RefPath::assert_from(b"/evm/world_state/fees/burned"); +const EVM_BURNED_FEES: RefPath = RefPath::assert_from(b"/evm/world_state/fees/burned"); /// Path to the last info per level timestamp seen. -const EVM_INFO_PER_LEVEL_TIMESTAMP: RefPath = +const EVM_INFO_PER_LEVEL_TIMESTAMP: RefPath = RefPath::assert_from(b"/evm/info_per_level/timestamp"); /// Path to the number of timestamps read, use to compute the average block time. -const EVM_INFO_PER_LEVEL_STATS_NUMBERS: RefPath = +const EVM_INFO_PER_LEVEL_STATS_NUMBERS: RefPath = RefPath::assert_from(b"/evm/info_per_level/stats/numbers"); /// Path to the sum of distance between blocks, used to compute the average block time. -const EVM_INFO_PER_LEVEL_STATS_TOTAL: RefPath = +const EVM_INFO_PER_LEVEL_STATS_TOTAL: RefPath = RefPath::assert_from(b"/evm/info_per_level/stats/total"); -pub const SIMULATION_RESULT: RefPath = RefPath::assert_from(b"/evm/simulation_result"); +pub const SIMULATION_RESULT: RefPath = RefPath::assert_from(b"/evm/simulation_result"); // Path to the number of seconds until delayed txs are timed out. -const EVM_DELAYED_INBOX_TIMEOUT: RefPath = +const EVM_DELAYED_INBOX_TIMEOUT: RefPath = RefPath::assert_from(b"/evm/delayed_inbox_timeout"); // Path to the number of l1 levels that need to pass for a // delayed tx to be timed out. -const EVM_DELAYED_INBOX_MIN_LEVELS: RefPath = +const EVM_DELAYED_INBOX_MIN_LEVELS: RefPath = RefPath::assert_from(b"/evm/delayed_inbox_min_levels"); // Path to the tz1 administrating the sequencer. If there is nothing // at this path, the kernel is in proxy mode. -pub const SEQUENCER: RefPath = RefPath::assert_from(b"/evm/sequencer"); +pub const SEQUENCER: RefPath = RefPath::assert_from(b"/evm/sequencer"); // Path to the DAL feature flag. If there is nothing at this path, DAL // is not used. -pub const ENABLE_DAL: RefPath = RefPath::assert_from(b"/evm/feature_flags/enable_dal"); +pub const ENABLE_DAL: RefPath = RefPath::assert_from(b"/evm/feature_flags/enable_dal"); // Path to the DAL slot indices to use. -pub const DAL_SLOTS: RefPath = RefPath::assert_from(b"/evm/dal_slots"); +pub const DAL_SLOTS: RefPath = RefPath::assert_from(b"/evm/dal_slots"); // Path where the input for the tracer is stored by the sequencer. -const TRACER_INPUT: RefPath = RefPath::assert_from(b"/evm/trace/input"); +const TRACER_INPUT: RefPath = RefPath::assert_from(b"/evm/trace/input"); // If this path contains a value, the fa bridge is enabled in the kernel. -pub const ENABLE_FA_BRIDGE: RefPath = +pub const ENABLE_FA_BRIDGE: RefPath = RefPath::assert_from(b"/evm/feature_flags/enable_fa_bridge"); // If the flag is set, the kernel consider that this is local evm node execution. -const EVM_NODE_FLAG: RefPath = RefPath::assert_from(b"/__evm_node"); +const EVM_NODE_FLAG: RefPath = RefPath::assert_from(b"/__evm_node"); -const MAX_BLUEPRINT_LOOKAHEAD_IN_SECONDS: RefPath = +const MAX_BLUEPRINT_LOOKAHEAD_IN_SECONDS: RefPath = RefPath::assert_from(b"/evm/max_blueprint_lookahead_in_seconds"); -pub fn receipt_path(receipt_hash: &TransactionHash) -> Result { +pub fn receipt_path(receipt_hash: &TransactionHash) -> Result, Error> { let hash = hex::encode(receipt_hash); let raw_receipt_path: Vec = format!("/{}", &hash).into(); let receipt_path = OwnedPath::try_from(raw_receipt_path)?; concat(&EVM_TRANSACTIONS_RECEIPTS, &receipt_path).map_err(Error::from) } -pub fn object_path(object_hash: &TransactionHash) -> Result { +pub fn object_path(object_hash: &TransactionHash) -> Result, Error> { let hash = hex::encode(object_hash); let raw_object_path: Vec = format!("/{}", &hash).into(); let object_path = OwnedPath::try_from(raw_object_path)?; @@ -229,11 +229,11 @@ pub fn store_transaction_object( Ok(encoded.len().try_into()?) } -const CHUNKED_TRANSACTIONS: RefPath = RefPath::assert_from(b"/chunked_transactions"); -const CHUNKED_TRANSACTION_NUM_CHUNKS: RefPath = RefPath::assert_from(b"/num_chunks"); -const CHUNKED_HASHES: RefPath = RefPath::assert_from(b"/chunk_hashes"); +const CHUNKED_TRANSACTIONS: RefPath = RefPath::assert_from(b"/chunked_transactions"); +const CHUNKED_TRANSACTION_NUM_CHUNKS: RefPath = RefPath::assert_from(b"/num_chunks"); +const CHUNKED_HASHES: RefPath = RefPath::assert_from(b"/chunk_hashes"); -pub fn chunked_transaction_path(tx_hash: &TransactionHash) -> Result { +pub fn chunked_transaction_path(tx_hash: &TransactionHash) -> Result, Error> { let hash = hex::encode(tx_hash); let raw_chunked_transaction_path: Vec = format!("/{}", hash).into(); let chunked_transaction_path = OwnedPath::try_from(raw_chunked_transaction_path)?; @@ -241,34 +241,34 @@ pub fn chunked_transaction_path(tx_hash: &TransactionHash) -> Result Result { - concat(chunked_transaction_path, &CHUNKED_TRANSACTION_NUM_CHUNKS).map_err(Error::from) + chunked_transaction_path: &OwnedPath, +) -> Result, Error> { + force_concat(chunked_transaction_path, &CHUNKED_TRANSACTION_NUM_CHUNKS).map_err(Error::from) } pub fn chunked_hash_transaction_path( chunked_hash: &[u8], - chunked_transaction_path: &OwnedPath, -) -> Result { + chunked_transaction_path: &OwnedPath, +) -> Result, Error> { let hash = hex::encode(chunked_hash); let raw_chunked_hash_key: Vec = format!("/{}", hash).into(); let chunked_hash_key = OwnedPath::try_from(raw_chunked_hash_key)?; - let chunked_hash_path = concat(&CHUNKED_HASHES, &chunked_hash_key)?; - concat(chunked_transaction_path, &chunked_hash_path).map_err(Error::from) + let chunked_hash_path = force_concat(&CHUNKED_HASHES, &chunked_hash_key)?; + force_concat(chunked_transaction_path, &chunked_hash_path).map_err(Error::from) } pub fn transaction_chunk_path( - chunked_transaction_path: &OwnedPath, + chunked_transaction_path: &OwnedPath, i: u16, -) -> Result { +) -> Result, Error> { let raw_i_path: Vec = format!("/{}", i).into(); let i_path = OwnedPath::try_from(raw_i_path)?; - concat(chunked_transaction_path, &i_path).map_err(Error::from) + force_concat(chunked_transaction_path, &i_path).map_err(Error::from) } fn is_transaction_complete( host: &mut Host, - chunked_transaction_path: &OwnedPath, + chunked_transaction_path: &OwnedPath, num_chunks: u16, ) -> Result { let n_subkeys = host.store_count_subkeys(chunked_transaction_path)? as u16; @@ -278,7 +278,7 @@ fn is_transaction_complete( fn chunked_transaction_num_chunks_by_path( host: &mut Host, - chunked_transaction_path: &OwnedPath, + chunked_transaction_path: &OwnedPath, ) -> Result { let chunked_transaction_num_chunks_path = chunked_transaction_num_chunks_path(chunked_transaction_path)?; @@ -297,7 +297,7 @@ pub fn chunked_transaction_num_chunks( fn store_transaction_chunk_data( host: &mut Host, - transaction_chunk_path: &OwnedPath, + transaction_chunk_path: &OwnedPath, data: Vec, ) -> Result<(), Error> { match host.store_has(transaction_chunk_path)? { @@ -318,7 +318,7 @@ fn store_transaction_chunk_data( pub fn read_transaction_chunk_data( host: &mut Host, - transaction_chunk_path: &OwnedPath, + transaction_chunk_path: &OwnedPath, ) -> Result, Error> { let data_size = host.store_value_size(transaction_chunk_path)?; @@ -339,7 +339,7 @@ pub fn read_transaction_chunk_data( fn get_full_transaction( host: &mut Host, - chunked_transaction_path: &OwnedPath, + chunked_transaction_path: &OwnedPath, num_chunks: u16, ) -> Result, Error> { let mut buffer = Vec::new(); @@ -353,7 +353,7 @@ fn get_full_transaction( pub fn remove_chunked_transaction_by_path( host: &mut Host, - path: &OwnedPath, + path: &OwnedPath, ) -> Result<(), Error> { host.store_delete(path).map_err(Error::from) } @@ -536,7 +536,7 @@ pub fn store_sequencer_pool_address( pub fn store_timestamp_path( host: &mut Host, - path: &OwnedPath, + path: &OwnedPath, timestamp: &Timestamp, ) -> Result<(), Error> { host.store_write(path, ×tamp.i64().to_le_bytes(), 0)?; @@ -598,7 +598,7 @@ pub fn store_last_info_per_level_timestamp( pub fn read_timestamp_path( host: &Host, - path: &OwnedPath, + path: &OwnedPath, ) -> Result { let mut buffer = [0u8; 8]; store_read_slice(host, path, &mut buffer, 8)?; diff --git a/etherlink/kernel_bifrost/kernel/src/upgrade.rs b/etherlink/kernel_bifrost/kernel/src/upgrade.rs index 32f69aa0ed42aaf1853e2c284839c659bf5cbfdf..e3b3aa17c8fd644db0fc09bdc45ff1df27056711 100644 --- a/etherlink/kernel_bifrost/kernel/src/upgrade.rs +++ b/etherlink/kernel_bifrost/kernel/src/upgrade.rs @@ -34,9 +34,9 @@ use tezos_smart_rollup_host::path::RefPath; use tezos_smart_rollup_installer_config::binary::promote::upgrade_reveal_flow; use tezos_storage::read_optional_rlp; -const KERNEL_UPGRADE: RefPath = RefPath::assert_from(b"/evm/kernel_upgrade"); -pub const KERNEL_ROOT_HASH: RefPath = RefPath::assert_from(b"/evm/kernel_root_hash"); -const SEQUENCER_UPGRADE: RefPath = RefPath::assert_from(b"/evm/sequencer_upgrade"); +const KERNEL_UPGRADE: RefPath = RefPath::assert_from(b"/evm/kernel_upgrade"); +pub const KERNEL_ROOT_HASH: RefPath = RefPath::assert_from(b"/evm/kernel_root_hash"); +const SEQUENCER_UPGRADE: RefPath = RefPath::assert_from(b"/evm/sequencer_upgrade"); #[derive(Debug, PartialEq, Clone)] pub struct KernelUpgrade { @@ -99,7 +99,7 @@ pub fn store_kernel_upgrade( fn read_kernel_upgrade_at( host: &impl Runtime, - path: &impl Path, + path: &impl Path, ) -> anyhow::Result> { read_optional_rlp(host, path).context("Failed to decode kernel upgrade") } diff --git a/etherlink/kernel_bifrost/runtime/src/internal_runtime.rs b/etherlink/kernel_bifrost/runtime/src/internal_runtime.rs index f0d240c3a93cdfee9ea37e69fc9e0d7628b29ac2..1823d94e3c364c344344eb350f08dafc6042bff1 100644 --- a/etherlink/kernel_bifrost/runtime/src/internal_runtime.rs +++ b/etherlink/kernel_bifrost/runtime/src/internal_runtime.rs @@ -8,7 +8,7 @@ use tezos_smart_rollup_host::{path::Path, runtime::RuntimeError}; pub trait InternalRuntime { - fn __internal_store_get_hash( + fn __internal_store_get_hash>( &mut self, path: &T, ) -> Result, RuntimeError>; @@ -19,5 +19,5 @@ pub trait InternalRuntime { // The path is optional to be able to get the hash // of the root directory. pub trait ExtendedRuntime { - fn store_get_hash(&mut self, path: &T) -> Result, RuntimeError>; + fn store_get_hash>(&mut self, path: &T) -> Result, RuntimeError>; } diff --git a/etherlink/kernel_bifrost/runtime/src/mock_internal.rs b/etherlink/kernel_bifrost/runtime/src/mock_internal.rs index 645513f5f8075051f632d1e4d77a3177c5809a8f..ec2fe1381145ce2440d297f1fe811e227c7e7806 100644 --- a/etherlink/kernel_bifrost/runtime/src/mock_internal.rs +++ b/etherlink/kernel_bifrost/runtime/src/mock_internal.rs @@ -8,7 +8,7 @@ use tezos_smart_rollup_host::path::Path; use tezos_smart_rollup_host::runtime::RuntimeError; use tezos_smart_rollup_mock::MockHost; impl InternalRuntime for MockHost { - fn __internal_store_get_hash( + fn __internal_store_get_hash>( &mut self, path: &T, ) -> Result, RuntimeError> { diff --git a/etherlink/kernel_bifrost/runtime/src/runtime.rs b/etherlink/kernel_bifrost/runtime/src/runtime.rs index 9d7eaef46f5f7e9e6e3d354f138a61e3186a531a..c070591e8a3ec68e813b38da967642af56da5ecd 100644 --- a/etherlink/kernel_bifrost/runtime/src/runtime.rs +++ b/etherlink/kernel_bifrost/runtime/src/runtime.rs @@ -19,26 +19,27 @@ use crate::{ }; use tezos_evm_logging::{Level, Verbosity}; use tezos_smart_rollup_core::PREIMAGE_HASH_SIZE; +use tezos_smart_rollup_encoding::smart_rollup::SmartRollupAddress; use tezos_smart_rollup_host::{ dal_parameters::RollupDalParameters, input::Message, metadata::RollupMetadata, path::{Path, RefPath}, runtime::{Runtime as SdkRuntime, RuntimeError, ValueType}, - Error, }; +use tezos_smart_rollup_mock::MockHost; // Set by the node, contains the verbosity for the logs -pub const VERBOSITY_PATH: RefPath = RefPath::assert_from(b"/evm/logging_verbosity"); +pub const VERBOSITY_PATH: RefPath = RefPath::assert_from(b"/evm/logging_verbosity"); pub trait Runtime: - SdkRuntime + InternalRuntime + ExtendedRuntime + Verbosity + WithGas + SdkRuntime + InternalRuntime + ExtendedRuntime + Verbosity + WithGas { } // If a type implements the Runtime, InternalRuntime and ExtendedRuntime traits, // it also implements the kernel Runtime. -impl Runtime +impl + InternalRuntime + ExtendedRuntime + Verbosity + WithGas> Runtime for T { } @@ -66,7 +67,7 @@ pub struct KernelHost + Borrow> { pub _pd: PhantomData, } -impl + Borrow> SdkRuntime +impl + InternalRuntime, Host: BorrowMut + Borrow> SdkRuntime for KernelHost { #[inline(always)] @@ -85,12 +86,12 @@ impl + Borrow> SdkRuntime } #[inline(always)] - fn store_has(&self, path: &T) -> Result, RuntimeError> { + fn store_has>(&self, path: &T) -> Result, RuntimeError> { self.host.borrow().store_has(path) } #[inline(always)] - fn store_read( + fn store_read>( &self, path: &T, from_offset: usize, @@ -100,7 +101,7 @@ impl + Borrow> SdkRuntime } #[inline(always)] - fn store_read_slice( + fn store_read_slice>( &self, path: &T, from_offset: usize, @@ -112,12 +113,12 @@ impl + Borrow> SdkRuntime } #[inline(always)] - fn store_read_all(&self, path: &impl Path) -> Result, RuntimeError> { + fn store_read_all(&self, path: &impl Path) -> Result, RuntimeError> { self.host.borrow().store_read_all(path) } #[inline(always)] - fn store_write( + fn store_write>( &mut self, path: &T, src: &[u8], @@ -127,7 +128,7 @@ impl + Borrow> SdkRuntime } #[inline(always)] - fn store_write_all( + fn store_write_all>( &mut self, path: &T, src: &[u8], @@ -136,25 +137,25 @@ impl + Borrow> SdkRuntime } #[inline(always)] - fn store_delete(&mut self, path: &T) -> Result<(), RuntimeError> { + fn store_delete>(&mut self, path: &T) -> Result<(), RuntimeError> { self.host.borrow_mut().store_delete(path) } #[inline(always)] - fn store_delete_value(&mut self, path: &T) -> Result<(), RuntimeError> { + fn store_delete_value>(&mut self, path: &T) -> Result<(), RuntimeError> { self.host.borrow_mut().store_delete_value(path) } #[inline(always)] - fn store_count_subkeys(&self, prefix: &T) -> Result { + fn store_count_subkeys>(&self, prefix: &T) -> Result { self.host.borrow().store_count_subkeys(prefix) } #[inline(always)] fn store_move( &mut self, - from_path: &impl Path, - to_path: &impl Path, + from_path: &impl Path, + to_path: &impl Path, ) -> Result<(), RuntimeError> { self.host.borrow_mut().store_move(from_path, to_path) } @@ -162,8 +163,8 @@ impl + Borrow> SdkRuntime #[inline(always)] fn store_copy( &mut self, - from_path: &impl Path, - to_path: &impl Path, + from_path: &impl Path, + to_path: &impl Path, ) -> Result<(), RuntimeError> { self.host.borrow_mut().store_copy(from_path, to_path) } @@ -178,7 +179,7 @@ impl + Borrow> SdkRuntime } #[inline(always)] - fn store_value_size(&self, path: &impl Path) -> Result { + fn store_value_size(&self, path: &impl Path) -> Result { self.host.borrow().store_value_size(path) } @@ -247,11 +248,11 @@ impl + Borrow> SdkRuntime } } -impl + BorrowMut> InternalRuntime +impl + InternalRuntime, Host: Borrow + BorrowMut> InternalRuntime for KernelHost { #[inline(always)] - fn __internal_store_get_hash( + fn __internal_store_get_hash>( &mut self, path: &T, ) -> Result, RuntimeError> { @@ -259,16 +260,16 @@ impl + BorrowMut> InternalRu } } -impl + Borrow> ExtendedRuntime +impl + InternalRuntime, Host: BorrowMut + Borrow> ExtendedRuntime for KernelHost { #[inline(always)] - fn store_get_hash(&mut self, path: &T) -> Result, RuntimeError> { + fn store_get_hash>(&mut self, path: &T) -> Result, RuntimeError> { self.__internal_store_get_hash(path) } } -impl + BorrowMut> KernelHost +impl + InternalRuntime, Host: Borrow + BorrowMut> KernelHost where for<'a> &'a mut Host: BorrowMut, { @@ -282,7 +283,7 @@ where } } -impl + Borrow> Verbosity +impl + InternalRuntime, Host: BorrowMut + Borrow> Verbosity for KernelHost { fn verbosity(&self) -> Level { @@ -290,7 +291,7 @@ impl + Borrow> Verbosity } } -pub fn read_logs_verbosity( +pub fn read_logs_verbosity>( host: &Host, ) -> Level { match host.store_read_all(&VERBOSITY_PATH) { @@ -301,7 +302,7 @@ pub fn read_logs_verbosity( } } -impl + Borrow> WithGas +impl + InternalRuntime, Host: BorrowMut + Borrow> WithGas for KernelHost { fn add_execution_gas(&mut self, gas: u64) { @@ -309,7 +310,7 @@ impl + Borrow> WithGas } } -impl + Borrow> +impl + InternalRuntime, Host: BorrowMut + Borrow> KernelHost { pub fn init(host: Host) -> Self { @@ -322,3 +323,28 @@ impl + Borrow> } } } + +pub type MockKernelHost = KernelHost; + +impl Default for MockKernelHost { + fn default() -> Self { + Self { + host: MockHost::default(), + logs_verbosity: Level::default(), + execution_gas_used: 0, + _pd: PhantomData, + } + } +} + +impl MockKernelHost { + pub fn with_address(address: SmartRollupAddress) -> Self { + let host = MockHost::with_address(&address); + KernelHost { + host, + logs_verbosity: Level::default(), + execution_gas_used: 0, + _pd: PhantomData, + } + } +} diff --git a/etherlink/kernel_bifrost/runtime/src/safe_storage.rs b/etherlink/kernel_bifrost/runtime/src/safe_storage.rs index d55feb706e33759acb9859300ae784e4abe16905..4f1cb25e4a8325aa7a65ffd028fc192e4472bb15 100644 --- a/etherlink/kernel_bifrost/runtime/src/safe_storage.rs +++ b/etherlink/kernel_bifrost/runtime/src/safe_storage.rs @@ -14,18 +14,18 @@ use tezos_smart_rollup_host::dal_parameters::RollupDalParameters; use tezos_smart_rollup_host::{ input::Message, metadata::RollupMetadata, - path::{concat, OwnedPath, Path, RefPath}, + path::{force_concat, OwnedPath, Path, RefPath}, runtime::{Runtime as SdkRuntime, RuntimeError, ValueType}, }; -pub const TMP_PATH: RefPath = RefPath::assert_from(b"/tmp"); -pub const WORLD_STATE_PATH: RefPath = RefPath::assert_from(b"/evm/world_state"); -pub const TMP_WORLD_STATE_PATH: RefPath = RefPath::assert_from(b"/tmp/evm/world_state"); -pub const TRACE_PATH: RefPath = RefPath::assert_from(b"/evm/trace"); -pub const TMP_TRACE_PATH: RefPath = RefPath::assert_from(b"/tmp/evm/trace"); +pub const TMP_PATH: RefPath = RefPath::assert_from(b"/tmp"); +pub const WORLD_STATE_PATH: RefPath = RefPath::assert_from(b"/evm/world_state"); +pub const TMP_WORLD_STATE_PATH: RefPath = RefPath::assert_from(b"/tmp/evm/world_state"); +pub const TRACE_PATH: RefPath = RefPath::assert_from(b"/evm/trace"); +pub const TMP_TRACE_PATH: RefPath = RefPath::assert_from(b"/tmp/evm/trace"); -pub fn safe_path(path: &T) -> Result { - concat(&TMP_PATH, path).map_err(|_| RuntimeError::PathNotFound) +pub fn safe_path>(path: &T) -> Result, RuntimeError> { + force_concat(&TMP_PATH, path).map_err(|_| RuntimeError::PathNotFound) } pub struct SafeStorage { @@ -33,7 +33,7 @@ pub struct SafeStorage { } impl InternalRuntime for SafeStorage<&mut Host> { - fn __internal_store_get_hash( + fn __internal_store_get_hash>( &mut self, path: &T, ) -> Result, RuntimeError> { @@ -43,13 +43,13 @@ impl InternalRuntime for SafeStorage<&mut Host> { impl ExtendedRuntime for SafeStorage<&mut Host> { #[inline(always)] - fn store_get_hash(&mut self, path: &P) -> Result, RuntimeError> { + fn store_get_hash>(&mut self, path: &P) -> Result, RuntimeError> { let path = safe_path(path)?; self.__internal_store_get_hash(&path) } } -impl SdkRuntime for SafeStorage<&mut Host> { +impl SdkRuntime for SafeStorage<&mut Host> { #[inline(always)] fn write_output(&mut self, from: &[u8]) -> Result<(), RuntimeError> { self.host.write_output(from) @@ -66,13 +66,13 @@ impl SdkRuntime for SafeStorage<&mut Host> { } #[inline(always)] - fn store_has(&self, path: &T) -> Result, RuntimeError> { + fn store_has>(&self, path: &T) -> Result, RuntimeError> { let path = safe_path(path)?; self.host.store_has(&path) } #[inline(always)] - fn store_read( + fn store_read>( &self, path: &T, from_offset: usize, @@ -83,7 +83,7 @@ impl SdkRuntime for SafeStorage<&mut Host> { } #[inline(always)] - fn store_read_slice( + fn store_read_slice>( &self, path: &T, from_offset: usize, @@ -94,13 +94,13 @@ impl SdkRuntime for SafeStorage<&mut Host> { } #[inline(always)] - fn store_read_all(&self, path: &impl Path) -> Result, RuntimeError> { + fn store_read_all(&self, path: &impl Path) -> Result, RuntimeError> { let path = safe_path(path)?; self.host.store_read_all(&path) } #[inline(always)] - fn store_write( + fn store_write>( &mut self, path: &T, src: &[u8], @@ -111,7 +111,7 @@ impl SdkRuntime for SafeStorage<&mut Host> { } #[inline(always)] - fn store_write_all( + fn store_write_all>( &mut self, path: &T, src: &[u8], @@ -121,19 +121,19 @@ impl SdkRuntime for SafeStorage<&mut Host> { } #[inline(always)] - fn store_delete(&mut self, path: &T) -> Result<(), RuntimeError> { + fn store_delete>(&mut self, path: &T) -> Result<(), RuntimeError> { let path = safe_path(path)?; self.host.store_delete(&path) } #[inline(always)] - fn store_delete_value(&mut self, path: &T) -> Result<(), RuntimeError> { + fn store_delete_value>(&mut self, path: &T) -> Result<(), RuntimeError> { let path = safe_path(path)?; self.host.store_delete_value(&path) } #[inline(always)] - fn store_count_subkeys(&self, prefix: &T) -> Result { + fn store_count_subkeys>(&self, prefix: &T) -> Result { let prefix = safe_path(prefix)?; self.host.store_count_subkeys(&prefix) } @@ -141,8 +141,8 @@ impl SdkRuntime for SafeStorage<&mut Host> { #[inline(always)] fn store_move( &mut self, - from_path: &impl Path, - to_path: &impl Path, + from_path: &impl Path, + to_path: &impl Path, ) -> Result<(), RuntimeError> { let from_path = safe_path(from_path)?; let to_path = safe_path(to_path)?; @@ -152,8 +152,8 @@ impl SdkRuntime for SafeStorage<&mut Host> { #[inline(always)] fn store_copy( &mut self, - from_path: &impl Path, - to_path: &impl Path, + from_path: &impl Path, + to_path: &impl Path, ) -> Result<(), RuntimeError> { let from_path = safe_path(from_path)?; let to_path = safe_path(to_path)?; @@ -170,7 +170,7 @@ impl SdkRuntime for SafeStorage<&mut Host> { } #[inline(always)] - fn store_value_size(&self, path: &impl Path) -> Result { + fn store_value_size(&self, path: &impl Path) -> Result { let path = safe_path(path)?; self.host.store_value_size(&path) } diff --git a/etherlink/kernel_bifrost/storage/src/lib.rs b/etherlink/kernel_bifrost/storage/src/lib.rs index a1e73354588e8f33d97ebdefc562e2250bf79a06..9c7ecd0aa1549653d23f91b732ea219a80c6e44e 100644 --- a/etherlink/kernel_bifrost/storage/src/lib.rs +++ b/etherlink/kernel_bifrost/storage/src/lib.rs @@ -28,7 +28,7 @@ pub const WORD_SIZE: usize = 32usize; /// NB: Value is read starting 0. pub fn store_read_slice( host: &impl Runtime, - path: &impl Path, + path: &impl Path, buffer: &mut [u8], expected_size: usize, ) -> Result<(), Error> { @@ -44,7 +44,7 @@ pub fn store_read_slice( } /// Get the path corresponding to an index of H256. -pub fn path_from_h256(index: &H256) -> Result { +pub fn path_from_h256(index: &H256) -> Result, Error> { let path_string = format!("/{}", hex::encode(index.to_fixed_bytes())); OwnedPath::try_from(path_string).map_err(Error::from) } @@ -52,7 +52,7 @@ pub fn path_from_h256(index: &H256) -> Result { /// Return a 32 bytes hash from storage at the given `path`. /// /// NB: The given bytes are interpreted in big endian order. -pub fn read_h256_be(host: &impl Runtime, path: &impl Path) -> anyhow::Result { +pub fn read_h256_be(host: &impl Runtime, path: &impl Path) -> anyhow::Result { let mut buffer = [0_u8; WORD_SIZE]; store_read_slice(host, path, &mut buffer, WORD_SIZE)?; Ok(H256::from_slice(&buffer)) @@ -78,7 +78,7 @@ pub fn read_h256_be_opt( /// NB: The given bytes are interpreted in big endian order. pub fn read_h256_be_default( host: &impl Runtime, - path: &impl Path, + path: &impl Path, default: H256, ) -> Result { match read_h256_be_opt(host, path)? { @@ -92,7 +92,7 @@ pub fn read_h256_be_default( /// NB: The hash is stored in big endian order. pub fn write_h256_be( host: &mut impl Runtime, - path: &impl Path, + path: &impl Path, hash: H256, ) -> anyhow::Result<()> { Ok(host.store_write_all(path, hash.as_bytes())?) @@ -101,7 +101,7 @@ pub fn write_h256_be( /// Return an unsigned 32 bytes value from storage at the given `path`. /// /// NB: The given bytes are interpreted in little endian order. -pub fn read_u256_le(host: &impl Runtime, path: &impl Path) -> Result { +pub fn read_u256_le(host: &impl Runtime, path: &impl Path) -> Result { let bytes = host.store_read_all(path)?; Ok(U256::from_little_endian(&bytes)) } @@ -112,7 +112,7 @@ pub fn read_u256_le(host: &impl Runtime, path: &impl Path) -> Result, default: U256, ) -> Result { match host.store_read_all(path) { @@ -127,7 +127,7 @@ pub fn read_u256_le_default( /// NB: The value is stored in little endian order. pub fn write_u256_le( host: &mut impl Runtime, - path: &impl Path, + path: &impl Path, value: U256, ) -> Result<(), Error> { let mut bytes: [u8; WORD_SIZE] = value.into(); @@ -138,7 +138,7 @@ pub fn write_u256_le( /// Return an unsigned 8 bytes value from storage at the given `path`. /// /// NB: The given bytes are interpreted in little endian order. -pub fn read_u64_le(host: &impl Runtime, path: &impl Path) -> Result { +pub fn read_u64_le(host: &impl Runtime, path: &impl Path) -> Result { let mut bytes = [0; std::mem::size_of::()]; host.store_read_slice(path, 0, bytes.as_mut_slice())?; Ok(u64::from_le_bytes(bytes)) @@ -150,7 +150,7 @@ pub fn read_u64_le(host: &impl Runtime, path: &impl Path) -> Result /// NB: The given bytes are interpreted in little endian order. pub fn read_u64_le_default( host: &impl Runtime, - path: &impl Path, + path: &impl Path, default: u64, ) -> Result { match host.store_read_all(path) { @@ -170,7 +170,7 @@ pub fn read_u64_le_default( /// NB: The value is stored in little endian order. pub fn write_u64_le( host: &mut impl Runtime, - path: &impl Path, + path: &impl Path, value: u64, ) -> Result<(), Error> { host.store_write_all(path, value.to_le_bytes().as_slice()) @@ -182,7 +182,7 @@ pub fn write_u64_le( pub fn store_rlp( src: &T, host: &mut impl Runtime, - path: &impl Path, + path: &impl Path, ) -> Result<(), Error> { host.store_write_all(path, &src.rlp_bytes()) .map_err(Error::from) @@ -190,7 +190,7 @@ pub fn store_rlp( /// Return a decodable value from storage as rlp bytes /// at the given `path`. -pub fn read_rlp(host: &impl Runtime, path: &impl Path) -> Result { +pub fn read_rlp(host: &impl Runtime, path: &impl Path) -> Result { let bytes = host.store_read_all(path)?; FromRlpBytes::from_rlp_bytes(&bytes).map_err(Error::from) } @@ -201,7 +201,7 @@ pub fn read_rlp(host: &impl Runtime, path: &impl Path) -> Result( host: &impl Runtime, - path: &impl Path, + path: &impl Path, ) -> Result, anyhow::Error> { if let Some(ValueType::Value) = host.store_has(path)? { let elt = read_rlp(host, path)?; @@ -212,7 +212,7 @@ pub fn read_optional_rlp( } /// Return a base58 contract address from storage at the given `path`. -pub fn read_b58_kt1(host: &impl Runtime, path: &impl Path) -> Option { +pub fn read_b58_kt1(host: &impl Runtime, path: &impl Path) -> Option { let bytes = host.store_read_all(path).ok()?; let kt1_b58 = String::from_utf8(bytes).ok()?; ContractKt1Hash::from_b58check(&kt1_b58).ok() diff --git a/etherlink/kernel_evm/evm_evaluation/src/evalhost.rs b/etherlink/kernel_evm/evm_evaluation/src/evalhost.rs index a2973b4fa14fb0bd32b7bc1cd3b56b9bd0e15d71..05c7cdb25ad099107d70a79326936fe2e761e9ba 100644 --- a/etherlink/kernel_evm/evm_evaluation/src/evalhost.rs +++ b/etherlink/kernel_evm/evm_evaluation/src/evalhost.rs @@ -8,36 +8,50 @@ use tezos_evm_logging::Verbosity; use tezos_evm_runtime::{ extensions::WithGas, internal_runtime::{ExtendedRuntime, InternalRuntime}, - runtime::MockKernelHost, + runtime::{MockKernelHost, TracePath, TraceRuntime}, }; use tezos_smart_rollup_host::{ dal_parameters::RollupDalParameters, input::Message, metadata::RollupMetadata, - path::Path, + path::{concat, OwnedPath, Path, RefPath}, runtime::{Runtime as SdkRuntime, RuntimeError, ValueType}, }; use tezos_smart_rollup_core::PREIMAGE_HASH_SIZE; -pub struct EvalHost { - pub host: MockKernelHost, +pub struct EvalHost<'a> { + pub host: &'a mut MockKernelHost, pub buffer: RefCell>, } -impl EvalHost { +impl<'a> EvalHost<'a> { /// Create a new instance of the `MockHost`, additionally provide the buffer /// where the logs will be outputed. - pub fn default_with_buffer(buffer: RefCell>) -> Self { - let host = MockKernelHost::default(); + pub fn default_with_buffer( + buffer: RefCell>, + host: &'a mut MockKernelHost, + ) -> Self { Self { host, buffer } } } -impl SdkRuntime for EvalHost { +const WORLD_STATE_PATH: RefPath = RefPath::assert_from(b"/evm/world_state"); +const TRACE_PATH: RefPath = RefPath::assert_from(b"/evm/trace"); + +fn world_state_path(p: &impl Path) -> Result, RuntimeError> { + concat(&WORLD_STATE_PATH, p).map_err(|_| RuntimeError::PathNotFound) +} + +fn trace_path(p: &TracePath) -> Result, RuntimeError> { + let TracePath(p) = p; + concat(&TRACE_PATH, p).map_err(|_| RuntimeError::PathNotFound) +} + +impl<'a> SdkRuntime for EvalHost<'a> { #[inline(always)] fn write_output(&mut self, from: &[u8]) -> Result<(), RuntimeError> { - self.host.write_output(from) + self.host.host.write_output(from) } #[inline(always)] @@ -50,89 +64,111 @@ impl SdkRuntime for EvalHost { #[inline(always)] fn read_input(&mut self) -> Result, RuntimeError> { - self.host.read_input() + self.host.host.read_input() } #[inline(always)] - fn store_has(&self, path: &T) -> Result, RuntimeError> { - self.host.store_has(path) + fn store_has>( + &self, + path: &T, + ) -> Result, RuntimeError> { + let path = world_state_path(path)?; + self.host.store_has(&path) } #[inline(always)] - fn store_read( + fn store_read>( &self, path: &T, from_offset: usize, max_bytes: usize, ) -> Result, RuntimeError> { - self.host.store_read(path, from_offset, max_bytes) + let path = world_state_path(path)?; + self.host.store_read(&path, from_offset, max_bytes) } #[inline(always)] - fn store_read_slice( + fn store_read_slice>( &self, path: &T, from_offset: usize, buffer: &mut [u8], ) -> Result { - self.host.store_read_slice(path, from_offset, buffer) + let path = world_state_path(path)?; + self.host.store_read_slice(&path, from_offset, buffer) } #[inline(always)] - fn store_read_all(&self, path: &impl Path) -> Result, RuntimeError> { - self.host.store_read_all(path) + fn store_read_all(&self, path: &impl Path) -> Result, RuntimeError> { + let path = world_state_path(path)?; + self.host.store_read_all(&path) } #[inline(always)] - fn store_write( + fn store_write>( &mut self, path: &T, src: &[u8], at_offset: usize, ) -> Result<(), RuntimeError> { - self.host.store_write(path, src, at_offset) + let path = world_state_path(path)?; + self.host.store_write(&path, src, at_offset) } #[inline(always)] - fn store_write_all( + fn store_write_all>( &mut self, path: &T, src: &[u8], ) -> Result<(), RuntimeError> { - self.host.store_write_all(path, src) + let path = world_state_path(path)?; + self.host.store_write_all(&path, src) } #[inline(always)] - fn store_delete(&mut self, path: &T) -> Result<(), RuntimeError> { - self.host.store_delete(path) + fn store_delete>(&mut self, path: &T) -> Result<(), RuntimeError> { + let path = world_state_path(path)?; + self.host.store_delete(&path) } #[inline(always)] - fn store_delete_value(&mut self, path: &T) -> Result<(), RuntimeError> { - self.host.store_delete_value(path) + fn store_delete_value>( + &mut self, + path: &T, + ) -> Result<(), RuntimeError> { + let path = world_state_path(path)?; + self.host.store_delete_value(&path) } #[inline(always)] - fn store_count_subkeys(&self, prefix: &T) -> Result { - self.host.store_count_subkeys(prefix) + fn store_count_subkeys>( + &self, + prefix: &T, + ) -> Result { + let prefix = world_state_path(prefix)?; + self.host.store_count_subkeys(&prefix) } #[inline(always)] fn store_move( &mut self, - from_path: &impl Path, - to_path: &impl Path, + from_path: &impl Path, + to_path: &impl Path, ) -> Result<(), RuntimeError> { - self.host.store_move(from_path, to_path) + let from_path = world_state_path(from_path)?; + let to_path = world_state_path(to_path)?; + self.host.store_move(&from_path, &to_path) } #[inline(always)] fn store_copy( &mut self, - from_path: &impl Path, - to_path: &impl Path, + from_path: &impl Path, + to_path: &impl Path, ) -> Result<(), RuntimeError> { - self.host.store_copy(from_path, to_path) + let from_path = world_state_path(from_path)?; + let to_path = world_state_path(to_path)?; + self.host.store_copy(&from_path, &to_path) } #[inline(always)] @@ -145,8 +181,9 @@ impl SdkRuntime for EvalHost { } #[inline(always)] - fn store_value_size(&self, path: &impl Path) -> Result { - self.host.store_value_size(path) + fn store_value_size(&self, path: &impl Path) -> Result { + let path = world_state_path(path)?; + self.host.store_value_size(&path) } #[inline(always)] @@ -202,8 +239,8 @@ impl SdkRuntime for EvalHost { } } -impl InternalRuntime for EvalHost { - fn __internal_store_get_hash( +impl<'a> InternalRuntime for EvalHost<'a> { + fn __internal_store_get_hash>( &mut self, path: &T, ) -> Result, tezos_smart_rollup_host::runtime::RuntimeError> { @@ -211,23 +248,35 @@ impl InternalRuntime for EvalHost { } } -impl ExtendedRuntime for EvalHost { - fn store_get_hash( +impl<'a> ExtendedRuntime for EvalHost<'a> { + fn store_get_hash( &mut self, - path: &T, + path: &impl Path, ) -> Result, tezos_smart_rollup_host::runtime::RuntimeError> { - self.host.store_get_hash(path) + let path = world_state_path(path)?; + self.host.store_get_hash(&path) } } -impl Verbosity for EvalHost { +impl<'a> Verbosity for EvalHost<'a> { fn verbosity(&self) -> tezos_evm_logging::Level { self.host.verbosity() } } -impl WithGas for EvalHost { +impl<'a> WithGas for EvalHost<'a> { // This is a blank implementation on purpose, as this is not useful for the // evaluation fn add_execution_gas(&mut self, _gas: u64) {} } + +impl<'a> TraceRuntime for EvalHost<'a> { + fn store_write_all_trace( + &mut self, + path: &TracePath, + src: &[u8], + ) -> Result<(), RuntimeError> { + let path = trace_path(path)?; + self.host.store_write_all(&path, src) + } +} diff --git a/etherlink/kernel_evm/evm_evaluation/src/runner.rs b/etherlink/kernel_evm/evm_evaluation/src/runner.rs index db46bc957dad0142c1830ca4b351165b3cf6d115..82a75a49ac38a9f4b74d5e955e16d6dbb2763321 100644 --- a/etherlink/kernel_evm/evm_evaluation/src/runner.rs +++ b/etherlink/kernel_evm/evm_evaluation/src/runner.rs @@ -13,6 +13,7 @@ use evm_execution::precompiles::{precompile_set, PrecompileBTreeMap}; use evm_execution::{run_transaction, Config, EthereumError}; use tezos_ethereum::block::{BlockConstants, BlockFees}; +use tezos_evm_runtime::runtime::MockKernelHost; use hex_literal::hex; use primitive_types::{H160, H256, U256}; @@ -85,15 +86,18 @@ fn read_testsuite(path: &Path) -> Result { serde_json::from_reader(&*json_reader).map_err(TestError::from) } -fn prepare_host() -> EvalHost { +fn prepare_host(host: &mut MockKernelHost) -> EvalHost { let execution_buffer = Vec::new(); let buffer = RefCell::new(execution_buffer); - EvalHost::default_with_buffer(buffer) + EvalHost::default_with_buffer(buffer, host) } -fn prepare_host_with_buffer(execution_buffer: Vec) -> EvalHost { +fn prepare_host_with_buffer( + execution_buffer: Vec, + host: &mut MockKernelHost, +) -> EvalHost { let buffer = RefCell::new(execution_buffer); - EvalHost::default_with_buffer(buffer) + EvalHost::default_with_buffer(buffer, host) } fn prepare_filler_source( @@ -182,10 +186,10 @@ fn initialize_env(unit: &TestUnit) -> Result { } #[allow(clippy::too_many_arguments)] -fn execute_transaction( - host: &mut EvalHost, +fn execute_transaction<'a>( + host: &mut EvalHost<'a>, evm_account_storage: &mut EthereumAccountStorage, - precompiles: &PrecompileBTreeMap, + precompiles: &PrecompileBTreeMap>, config: &Config, unit: &TestUnit, env: &mut Env, @@ -315,7 +319,8 @@ pub fn run_test( skip_data: &SkipData, ) -> Result<(), TestError> { let suit = read_testsuite(path)?; - let mut host = prepare_host(); + let mut host = MockKernelHost::default(); + let mut host = prepare_host(&mut host); for (name, unit) in suit.0.into_iter() { if output.log { @@ -361,7 +366,7 @@ pub fn run_test( } continue; } - host = prepare_host_with_buffer(host.buffer.take()); + host = prepare_host_with_buffer(host.buffer.take(), host.host); initialize_accounts(&mut host, &unit); let data_label = info.labels.get(&data); if let Some(data_label) = data_label { diff --git a/etherlink/kernel_evm/evm_execution/src/account_storage.rs b/etherlink/kernel_evm/evm_execution/src/account_storage.rs index edd710855a0c52739916b1859a92a652a39dd7c9..1d61846180614b2b35a1e31714586da8f40cbcad 100644 --- a/etherlink/kernel_evm/evm_execution/src/account_storage.rs +++ b/etherlink/kernel_evm/evm_execution/src/account_storage.rs @@ -120,44 +120,43 @@ pub struct StorageEffect { /// an account. We don't currently require it, and so it's omitted. #[derive(Debug, PartialEq)] pub struct EthereumAccount { - path: OwnedPath, + path: OwnedPath, } -impl From for EthereumAccount { - fn from(path: OwnedPath) -> Self { +impl From> for EthereumAccount { + fn from(path: OwnedPath) -> Self { Self { path } } } /// Path where Ethereum accounts are stored -pub const EVM_ACCOUNTS_PATH: RefPath = - RefPath::assert_from(b"/evm/world_state/eth_accounts"); +pub const EVM_ACCOUNTS_PATH: RefPath = RefPath::assert_from(b"/eth_accounts"); /// Path where an account nonce is stored. This should be prefixed with the path to /// where the account is stored for the world state or for the current transaction. -const NONCE_PATH: RefPath = RefPath::assert_from(b"/nonce"); +const NONCE_PATH: RefPath = RefPath::assert_from(b"/nonce"); /// Path where an account balance, ether held, is stored. This should be prefixed with the path to /// where the account is stored for the world state or for the current transaction. -const BALANCE_PATH: RefPath = RefPath::assert_from(b"/balance"); +const BALANCE_PATH: RefPath = RefPath::assert_from(b"/balance"); /// "Internal" accounts - accounts with contract code have a contract code hash. /// This value is computed when the code is stored and kept for future queries. This /// path should be prefixed with the path to /// where the account is stored for the world state or for the current transaction. -const CODE_HASH_PATH: RefPath = RefPath::assert_from(b"/code.hash"); +const CODE_HASH_PATH: RefPath = RefPath::assert_from(b"/code.hash"); /// "Internal" accounts - accounts with contract code, have their code stored here. /// This /// path should be prefixed with the path to /// where the account is stored for the world state or for the current transaction. -const CODE_PATH: RefPath = RefPath::assert_from(b"/code"); +const CODE_PATH: RefPath = RefPath::assert_from(b"/code"); /// The contracts of "internal" accounts have their own storage area. The account /// location prefixed to this path gives the root path (prefix) to where such storage /// values are kept. Each index in durable storage gives one complete path to one /// such 256 bit integer value in storage. -const STORAGE_ROOT_PATH: RefPath = RefPath::assert_from(b"/storage"); +const STORAGE_ROOT_PATH: RefPath = RefPath::assert_from(b"/storage"); /// If a contract tries to read a value from storage and it has previously not written /// anything to this location or if it wrote the default value, then it gets this @@ -182,7 +181,7 @@ const CODE_HASH_BYTES: [u8; WORD_SIZE] = Decoder::Hex pub const CODE_HASH_DEFAULT: H256 = H256(CODE_HASH_BYTES); /// Turn an Ethereum address - a H160 - into a valid path -pub fn account_path(address: &H160) -> Result { +pub fn account_path(address: &H160) -> Result, DurableStorageError> { let path_string = alloc::format!("/{}", hex::encode(address.to_fixed_bytes())); OwnedPath::try_from(path_string).map_err(DurableStorageError::from) } @@ -210,7 +209,7 @@ impl EthereumAccount { /// Get the **nonce** for the Ethereum account. Default value is zero, so an account will /// _always_ have this **nonce**. - pub fn nonce(&self, host: &impl Runtime) -> Result { + pub fn nonce(&self, host: &impl Runtime) -> Result { let path = concat(&self.path, &NONCE_PATH)?; read_u64_le_default(host, &path, NONCE_DEFAULT_VALUE) .map_err(AccountStorageError::from) @@ -221,7 +220,7 @@ impl EthereumAccount { /// integer. pub fn increment_nonce( &mut self, - host: &mut impl Runtime, + host: &mut impl Runtime, ) -> Result<(), AccountStorageError> { let path = concat(&self.path, &NONCE_PATH)?; @@ -239,7 +238,7 @@ impl EthereumAccount { pub fn decrement_nonce( &mut self, - host: &mut impl Runtime, + host: &mut impl Runtime, ) -> Result<(), AccountStorageError> { let path = concat(&self.path, &NONCE_PATH)?; @@ -255,7 +254,7 @@ impl EthereumAccount { pub fn set_nonce( &mut self, - host: &mut impl Runtime, + host: &mut impl Runtime, nonce: u64, ) -> Result<(), AccountStorageError> { let path = concat(&self.path, &NONCE_PATH)?; @@ -267,7 +266,10 @@ impl EthereumAccount { } /// Get the **balance** of an account in Wei held by the account. - pub fn balance(&self, host: &impl Runtime) -> Result { + pub fn balance( + &self, + host: &impl Runtime, + ) -> Result { let path = concat(&self.path, &BALANCE_PATH)?; read_u256_le_default(host, &path, BALANCE_DEFAULT_VALUE) .map_err(AccountStorageError::from) @@ -277,7 +279,7 @@ impl EthereumAccount { /// final amount exceeds the range of a a 256 bit unsigned integer. pub fn balance_add( &mut self, - host: &mut impl Runtime, + host: &mut impl Runtime, amount: U256, ) -> Result<(), AccountStorageError> { let path = concat(&self.path, &BALANCE_PATH)?; @@ -301,7 +303,7 @@ impl EthereumAccount { /// ie the account held enough funds, the function returns `Ok(true)`. pub fn balance_remove( &mut self, - host: &mut impl Runtime, + host: &mut impl Runtime, amount: U256, ) -> Result { let path = concat(&self.path, &BALANCE_PATH)?; @@ -323,7 +325,7 @@ impl EthereumAccount { /// Set the balance of an account to an amount in Wei pub fn set_balance( &mut self, - host: &mut impl Runtime, + host: &mut impl Runtime, new_balance: U256, ) -> Result<(), AccountStorageError> { let path = concat(&self.path, &BALANCE_PATH)?; @@ -338,13 +340,16 @@ impl EthereumAccount { /// Get the path to a custom account state section, can be used by precompiles pub fn custom_path( &self, - suffix: &impl Path, - ) -> Result { + suffix: &impl Path, + ) -> Result, AccountStorageError> { concat(&self.path, suffix).map_err(AccountStorageError::from) } /// Get the path to an index in durable storage for an account. - fn storage_path(&self, index: &H256) -> Result { + fn storage_path( + &self, + index: &H256, + ) -> Result, AccountStorageError> { let storage_path = concat(&self.path, &STORAGE_ROOT_PATH)?; let index_path = path_from_h256(index)?; concat(&storage_path, &index_path).map_err(AccountStorageError::from) @@ -353,7 +358,7 @@ impl EthereumAccount { /// Clear the entire storage in durable storage for an account. pub fn clear_storage( &self, - host: &mut impl Runtime, + host: &mut impl Runtime, ) -> Result<(), AccountStorageError> { let storage_path = concat(&self.path, &STORAGE_ROOT_PATH)?; if host.store_has(&storage_path)?.is_some() { @@ -366,7 +371,7 @@ impl EthereumAccount { /// Get the value stored in contract permanent storage at a given index for an account. pub fn get_storage( &self, - host: &impl Runtime, + host: &impl Runtime, index: &H256, ) -> Result { let path = self.storage_path(index)?; @@ -376,7 +381,7 @@ impl EthereumAccount { pub fn read_storage( &self, - host: &impl Runtime, + host: &impl Runtime, index: &H256, ) -> Result { let path = self.storage_path(index)?; @@ -393,7 +398,7 @@ impl EthereumAccount { /// non-default to default, et.c. This is for the purpose of calculating gas cost. pub fn set_storage_checked( &mut self, - host: &mut impl Runtime, + host: &mut impl Runtime, index: &H256, value: &H256, ) -> Result { @@ -424,7 +429,7 @@ impl EthereumAccount { /// values being stored. This function does no tracking for the purpose of gas cost. pub fn set_storage( &mut self, - host: &mut impl Runtime, + host: &mut impl Runtime, index: &H256, value: &H256, ) -> Result<(), AccountStorageError> { @@ -437,7 +442,10 @@ impl EthereumAccount { } /// Find whether the account has any code associated with it. - pub fn code_exists(&self, host: &impl Runtime) -> Result { + pub fn code_exists( + &self, + host: &impl Runtime, + ) -> Result { let path = concat(&self.path, &CODE_HASH_PATH)?; match host.store_has(&path) { @@ -453,7 +461,10 @@ impl EthereumAccount { /// retrieve it within the code_storage module. // There is a possibility here to migrate lazily all code in order // to have all legacy contract uses the new code storage. - pub fn code(&self, host: &impl Runtime) -> Result, AccountStorageError> { + pub fn code( + &self, + host: &impl Runtime, + ) -> Result, AccountStorageError> { let path = concat(&self.path, &CODE_PATH)?; // If we ever do the lazy migration of code for account @@ -471,7 +482,10 @@ impl EthereumAccount { /// Get the hash of the code associated with an account. This value is computed and /// stored when the code of a contract is set. - pub fn code_hash(&self, host: &impl Runtime) -> Result { + pub fn code_hash( + &self, + host: &impl Runtime, + ) -> Result { let path = concat(&self.path, &CODE_HASH_PATH)?; read_h256_be_default(host, &path, CODE_HASH_DEFAULT) .map_err(AccountStorageError::from) @@ -479,7 +493,10 @@ impl EthereumAccount { /// Get the size of a contract in number of bytes used for opcodes. This value is /// computed and stored when the code of a contract is set. - pub fn code_size(&self, host: &impl Runtime) -> Result { + pub fn code_size( + &self, + host: &impl Runtime, + ) -> Result { let path = concat(&self.path, &CODE_PATH)?; // If we ever do the lazy migration of code for account // (i.e. moving code from account to code_storage) then this @@ -499,7 +516,7 @@ impl EthereumAccount { /// not before. pub fn set_code( &mut self, - host: &mut impl Runtime, + host: &mut impl Runtime, code: &[u8], ) -> Result<(), AccountStorageError> { if self.code_exists(host)? { @@ -516,7 +533,7 @@ impl EthereumAccount { /// Delete all code associated with a contract. Also sets code length and size accordingly pub fn delete_code( &mut self, - host: &mut impl Runtime, + host: &mut impl Runtime, ) -> Result<(), AccountStorageError> { let code_hash_path = concat(&self.path, &CODE_HASH_PATH)?; @@ -542,12 +559,12 @@ impl EthereumAccount { } /// The type of the storage API for accessing the Ethereum World State. -pub type EthereumAccountStorage = Storage; +pub type EthereumAccountStorage = Storage; /// Get the storage API for accessing the Ethereum World State and do transactions /// on it. pub fn init_account_storage() -> Result { - Storage::::init(&EVM_ACCOUNTS_PATH) + Storage::::init(&EVM_ACCOUNTS_PATH) .map_err(AccountStorageError::from) } @@ -557,6 +574,7 @@ mod test { use host::path::RefPath; use primitive_types::U256; use tezos_evm_runtime::runtime::MockKernelHost; + use tezos_evm_runtime::safe_storage::{SafeStorage, WORLD_STATE_PATH}; use tezos_smart_rollup_host::runtime::Runtime as SdkRuntime; // Used for use tezos_storage::helpers::bytes_hash; use tezos_storage::write_u256_le; @@ -564,6 +582,10 @@ mod test { #[test] fn test_account_nonce_update() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut storage = init_account_storage().expect("Could not create EVM accounts storage API"); @@ -600,6 +622,10 @@ mod test { #[test] fn test_zero_account_balance_for_new_accounts() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut storage = init_account_storage().expect("Could not create EVM accounts storage API"); @@ -638,6 +664,10 @@ mod test { #[test] fn test_account_balance_add() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut storage = init_account_storage().expect("Could not create EVM accounts storage API"); @@ -682,6 +712,10 @@ mod test { #[test] fn test_account_balance_sub() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut storage = init_account_storage().expect("Could not create EVM accounts storage API"); @@ -726,6 +760,10 @@ mod test { #[test] fn test_account_balance_underflow() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut storage = init_account_storage().expect("Could not create EVM accounts storage API"); @@ -768,6 +806,10 @@ mod test { #[test] fn test_account_storage_zero_default() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut storage = init_account_storage().expect("Could not create EVM accounts storage API"); @@ -795,6 +837,10 @@ mod test { #[test] fn test_account_storage_update() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut storage = init_account_storage().expect("Could not create EVM accounts storage API"); @@ -836,6 +882,10 @@ mod test { #[test] fn test_account_storage_update_checked() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut storage = init_account_storage().expect("Could not create EVM accounts storage API"); @@ -917,6 +967,10 @@ mod test { #[test] fn test_account_code_storage_initial_code_is_zero() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut storage = init_account_storage().expect("Could not create EVM accounts storage API"); @@ -965,6 +1019,10 @@ mod test { fn test_account_code_storage_write_code_aux(sample_code: Vec) { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut storage = init_account_storage().expect("Could not create EVM accounts storage API"); @@ -1025,6 +1083,10 @@ mod test { #[test] fn test_account_code_storage_cant_be_overwritten() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut storage = init_account_storage().expect("Could not create EVM accounts storage API"); @@ -1077,6 +1139,10 @@ mod test { #[test] fn test_account_code_storage_delete_code() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut storage = init_account_storage().expect("Could not create EVM accounts storage API"); @@ -1131,6 +1197,10 @@ mod test { #[test] fn test_empty_contract_hash_matches_default() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut storage = init_account_storage().expect("Could not create EVM accounts storage API"); @@ -1180,6 +1250,10 @@ mod test { #[test] fn test_read_u256_le_default_le() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let path = RefPath::assert_from(b"/value"); assert_eq!( read_u256_le_default(&host, &path, U256::from(128)).unwrap(), @@ -1209,6 +1283,10 @@ mod test { #[test] fn test_write_u256_le_le() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let path = RefPath::assert_from(b"/value"); write_u256_le(&mut host, &path, U256::from(255)).unwrap(); @@ -1224,6 +1302,10 @@ mod test { let sample_code_hash: H256 = bytes_hash(&sample_code); let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut storage = init_account_storage().expect("Could not create EVM accounts storage API"); @@ -1291,6 +1373,10 @@ mod test { let sample_code_hash: H256 = bytes_hash(&sample_code); let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut storage = init_account_storage().expect("Could not create EVM accounts storage API"); diff --git a/etherlink/kernel_evm/evm_execution/src/code_storage.rs b/etherlink/kernel_evm/evm_execution/src/code_storage.rs index 6474eb67edd415d5f387fe090ff3b7b574f24e5e..65501f4f061ebad3e0843952ca52115219de3089 100644 --- a/etherlink/kernel_evm/evm_execution/src/code_storage.rs +++ b/etherlink/kernel_evm/evm_execution/src/code_storage.rs @@ -12,15 +12,15 @@ use tezos_storage::helpers::bytes_hash; use tezos_storage::{error::Error as GenStorageError, read_u64_le, write_u64_le}; /// Path where Ethereum account's code are stored -const EVM_CODES_PATH: RefPath = RefPath::assert_from(b"/evm/world_state/eth_codes"); +const EVM_CODES_PATH: RefPath = RefPath::assert_from(b"/eth_codes"); /// Path to the number of accounts to use a particular code -const REFERENCE_PATH: RefPath = RefPath::assert_from(b"/ref_count"); +const REFERENCE_PATH: RefPath = RefPath::assert_from(b"/ref_count"); /// Path to the code -const CODE_PATH: RefPath = RefPath::assert_from(b"/code"); +const CODE_PATH: RefPath = RefPath::assert_from(b"/code"); -fn code_hash_path(code_hash: &H256) -> Result { +fn code_hash_path(code_hash: &H256) -> Result, GenStorageError> { let code_hash_hex = hex::encode(code_hash); let code_hash_path_string = format!("/{}", code_hash_hex); OwnedPath::try_from(code_hash_path_string).map_err(Into::into) @@ -28,11 +28,11 @@ fn code_hash_path(code_hash: &H256) -> Result { #[derive(Debug, PartialEq)] pub struct CodeStorage { - path: OwnedPath, + path: OwnedPath, } -impl From for CodeStorage { - fn from(path: OwnedPath) -> Self { +impl From> for CodeStorage { + fn from(path: OwnedPath) -> Self { Self { path } } } @@ -44,19 +44,22 @@ impl CodeStorage { Ok(Self { path }) } - fn exists(&self, host: &impl Runtime) -> Result { + fn exists(&self, host: &impl Runtime) -> Result { let store_has_code = host.store_has(&self.path)?; Ok(store_has_code.is_some()) } - fn get_ref_count(&self, host: &mut impl Runtime) -> Result { + fn get_ref_count( + &self, + host: &mut impl Runtime, + ) -> Result { let reference_path = concat(&self.path, &REFERENCE_PATH)?; read_u64_le(host, &reference_path).or(Ok(0u64)) } fn set_ref_count( &self, - host: &mut impl Runtime, + host: &mut impl Runtime, number_ref: u64, ) -> Result<(), GenStorageError> { let reference_path = concat(&self.path, &REFERENCE_PATH)?; @@ -66,7 +69,7 @@ impl CodeStorage { fn increment_code_usage( &self, - host: &mut impl Runtime, + host: &mut impl Runtime, ) -> Result<(), GenStorageError> { let number_reference = self.get_ref_count(host)?; let number_reference = number_reference.saturating_add(1u64); @@ -75,7 +78,7 @@ impl CodeStorage { fn decrement_code_usage( &self, - host: &mut impl Runtime, + host: &mut impl Runtime, ) -> Result { let number_reference = self.get_ref_count(host)?; let number_reference = number_reference.saturating_sub(1u64); @@ -84,7 +87,7 @@ impl CodeStorage { } pub fn add( - host: &mut impl Runtime, + host: &mut impl Runtime, bytecode: &[u8], ) -> Result { let code_hash: H256 = bytes_hash(bytecode); @@ -98,7 +101,7 @@ impl CodeStorage { } pub fn delete( - host: &mut impl Runtime, + host: &mut impl Runtime, code_hash: &H256, ) -> Result<(), GenStorageError> { let code = Self::new(code_hash)?; @@ -113,7 +116,7 @@ impl CodeStorage { } pub fn get_code( - host: &impl Runtime, + host: &impl Runtime, code_hash: &H256, ) -> Result, GenStorageError> { let code = Self::new(code_hash)?; @@ -126,7 +129,7 @@ impl CodeStorage { } pub fn code_size( - host: &impl Runtime, + host: &impl Runtime, code_hash: &H256, ) -> Result { let code = Self::new(code_hash)?; @@ -144,13 +147,20 @@ impl CodeStorage { #[cfg(test)] mod test { use crate::account_storage; - use tezos_evm_runtime::runtime::MockKernelHost; + use tezos_evm_runtime::{ + runtime::MockKernelHost, + safe_storage::{SafeStorage, WORLD_STATE_PATH}, + }; use super::*; #[test] fn test_empty_contract_hash_matches_default() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let empty_code: Vec = vec![]; let empty_code_hash: H256 = account_storage::CODE_HASH_DEFAULT; @@ -163,6 +173,10 @@ mod test { #[test] fn test_get_code_matches_given() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let code: Vec = (0..100).collect(); let code_hash = CodeStorage::add(&mut host, &code).expect("Could not create code storage"); @@ -173,6 +187,10 @@ mod test { #[test] fn test_code_ref_is_incremented() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let code: Vec = (0..100).collect(); let code_hash = CodeStorage::add(&mut host, &code).expect("Could not create code storage"); @@ -201,6 +219,10 @@ mod test { #[test] fn test_code_is_deleted() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let code_hash: H256 = account_storage::CODE_HASH_DEFAULT; let code_storage = diff --git a/etherlink/kernel_evm/evm_execution/src/fa_bridge/mod.rs b/etherlink/kernel_evm/evm_execution/src/fa_bridge/mod.rs index 2c31dc8963180a8cba6459aa27399a19eaa49e92..d00227ddd52b2d8320c4649b43ec4e5ef9b4cdb9 100644 --- a/etherlink/kernel_evm/evm_execution/src/fa_bridge/mod.rs +++ b/etherlink/kernel_evm/evm_execution/src/fa_bridge/mod.rs @@ -124,7 +124,7 @@ macro_rules! create_outcome_error { /// account storage transaction, and we can open one. #[allow(clippy::too_many_arguments)] #[cfg_attr(feature = "benchmark", inline(never))] -pub fn execute_fa_deposit<'a, Host: Runtime>( +pub fn execute_fa_deposit<'a, Host: Runtime>( host: &'a mut Host, block: &'a BlockConstants, evm_account_storage: &'a mut EthereumAccountStorage, @@ -193,7 +193,7 @@ pub fn execute_fa_deposit<'a, Host: Runtime>( /// on errors and the caller is responsible of cleaning up intermediate layer in /// case of errors (i.e. using `end_inter_transaction`). It also can return the /// withdrawal if it was succesful. -fn execute_layered_fa_withdrawal( +fn execute_layered_fa_withdrawal>( handler: &mut EvmHandler, caller: H160, withdrawal: FaWithdrawal, @@ -226,7 +226,7 @@ fn execute_layered_fa_withdrawal( /// another smart contract. /// /// We assume there is an open account storage transaction. -pub fn execute_fa_withdrawal( +pub fn execute_fa_withdrawal>( handler: &mut EvmHandler, caller: H160, withdrawal: FaWithdrawal, @@ -311,7 +311,7 @@ pub fn execute_fa_withdrawal( /// Updates ticket table according to the deposit and actual ticket owner. /// Assuming there is an open account storage transaction. -fn inner_execute_deposit( +fn inner_execute_deposit>( handler: &mut EvmHandler, ticket_owner: H160, deposit: &FaDeposit, @@ -344,7 +344,7 @@ fn inner_execute_deposit( /// Updates ticket ledger and outbox counter according to the withdrawal. /// Assuming there is an open account storage transaction. -fn inner_execute_withdrawal( +fn inner_execute_withdrawal>( handler: &mut EvmHandler, withdrawal: &FaWithdrawal, ) -> Result { @@ -378,7 +378,7 @@ fn inner_execute_withdrawal( /// Invokes proxy (ERC wrapper) contract from within a deposit or /// withdrawal handling function. /// Assuming there is an open account storage transaction. -fn inner_execute_proxy( +fn inner_execute_proxy>( handler: &mut EvmHandler, caller: H160, proxy: H160, diff --git a/etherlink/kernel_evm/evm_execution/src/fa_bridge/test_utils.rs b/etherlink/kernel_evm/evm_execution/src/fa_bridge/test_utils.rs index a6674b8644876174312725895b0e2d0dd6f05f76..a0e91220712900b20bedc5b717f8402caeca719c 100644 --- a/etherlink/kernel_evm/evm_execution/src/fa_bridge/test_utils.rs +++ b/etherlink/kernel_evm/evm_execution/src/fa_bridge/test_utils.rs @@ -13,7 +13,6 @@ use tezos_ethereum::{ block::{BlockConstants, BlockFees}, Log, }; -use tezos_evm_runtime::runtime::MockKernelHost; use tezos_evm_runtime::runtime::Runtime; use tezos_smart_rollup_encoding::{ contract::Contract, @@ -60,7 +59,7 @@ const REENTRANCY_TESTER_BYTECODE: &[u8] = /// Create a smart contract in the storage with the mocked token code pub fn deploy_mock_wrapper( - host: &mut MockKernelHost, + host: &mut impl Runtime, evm_account_storage: &mut EthereumAccountStorage, ticket: &FA2_1Ticket, caller: &H160, @@ -76,7 +75,7 @@ pub fn deploy_mock_wrapper( )); let block = dummy_block_constants(); - let precompiles = precompile_set::(false); + let precompiles = precompile_set(false); set_balance(host, evm_account_storage, caller, U256::from(1_000_000)); run_transaction( @@ -103,7 +102,7 @@ pub fn deploy_mock_wrapper( /// Create a smart contract in the storage with the mocked token code pub fn deploy_reentrancy_tester( - host: &mut MockKernelHost, + host: &mut impl Runtime, evm_account_storage: &mut EthereumAccountStorage, ticket: &FA2_1Ticket, caller: &H160, @@ -123,7 +122,7 @@ pub fn deploy_reentrancy_tester( )); let block = dummy_block_constants(); - let precompiles = precompile_set::(false); + let precompiles = precompile_set(false); set_balance(host, evm_account_storage, caller, U256::from(1_000_000)); run_transaction( @@ -150,7 +149,7 @@ pub fn deploy_reentrancy_tester( /// Execute FA deposit pub fn run_fa_deposit( - host: &mut MockKernelHost, + host: &mut impl Runtime, evm_account_storage: &mut EthereumAccountStorage, deposit: &FaDeposit, caller: &H160, @@ -158,7 +157,7 @@ pub fn run_fa_deposit( enable_fa_withdrawals: bool, ) -> ExecutionOutcome { let block = dummy_block_constants(); - let precompiles = precompile_set::(enable_fa_withdrawals); + let precompiles = precompile_set(enable_fa_withdrawals); execute_fa_deposit( host, @@ -199,7 +198,7 @@ pub fn dummy_fa_deposit(ticket: FA2_1Ticket, proxy: Option) -> FaDeposit { /// } /// } pub fn get_storage_flag( - host: &MockKernelHost, + host: &impl Runtime, evm_account_storage: &EthereumAccountStorage, proxy: H160, ) -> u32 { @@ -233,7 +232,7 @@ pub fn dummy_block_constants() -> BlockConstants { /// Provision ticket balance for a specified account pub fn ticket_balance_add( - host: &mut impl Runtime, + host: &mut impl Runtime, evm_account_storage: &mut EthereumAccountStorage, ticket_hash: &H256, address: &H160, @@ -249,7 +248,7 @@ pub fn ticket_balance_add( /// Get ticket balance for a specified account pub fn ticket_balance_get( - host: &impl Runtime, + host: &impl Runtime, evm_account_storage: &EthereumAccountStorage, ticket_hash: &H256, address: &H160, @@ -267,7 +266,7 @@ pub fn ticket_balance_get( /// Get next withdrawal counter value pub fn withdrawal_counter_next( - host: &impl Runtime, + host: &impl Runtime, evm_account_storage: &EthereumAccountStorage, ) -> Option { let system = evm_account_storage @@ -283,7 +282,7 @@ pub fn withdrawal_counter_next( /// Provision TEZ balance for a specified account pub fn set_balance( - host: &mut impl Runtime, + host: &mut impl Runtime, evm_account_storage: &mut EthereumAccountStorage, address: &H160, balance: U256, @@ -382,13 +381,13 @@ pub fn dummy_fa_withdrawal( /// Execute FA withdrawal directly without going through the precompile pub fn fa_bridge_precompile_call_withdraw( - host: &mut MockKernelHost, + host: &mut impl Runtime, evm_account_storage: &mut EthereumAccountStorage, withdrawal: FaWithdrawal, caller: H160, ) -> ExecutionOutcome { let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let config = Config::shanghai(); let mut handler = EvmHandler::new( diff --git a/etherlink/kernel_evm/evm_execution/src/fa_bridge/tests.rs b/etherlink/kernel_evm/evm_execution/src/fa_bridge/tests.rs index 1b4a723c09e72cd08b099af5ea1427de6817a77e..1c3456956e60be93574ce7881f46d88b7b4c887a 100644 --- a/etherlink/kernel_evm/evm_execution/src/fa_bridge/tests.rs +++ b/etherlink/kernel_evm/evm_execution/src/fa_bridge/tests.rs @@ -6,7 +6,10 @@ use alloy_primitives::FixedBytes; use alloy_sol_types::SolEvent; use evm::ExitError; use primitive_types::{H160, U256}; -use tezos_evm_runtime::runtime::MockKernelHost; +use tezos_evm_runtime::{ + runtime::MockKernelHost, + safe_storage::{SafeStorage, WORLD_STATE_PATH}, +}; use crate::{ account_storage::{account_path, init_account_storage}, @@ -27,6 +30,10 @@ use crate::{ #[test] fn fa_deposit_reached_wrapper_contract() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = init_account_storage().unwrap(); let caller = H160::zero(); @@ -109,6 +116,10 @@ fn fa_deposit_reached_wrapper_contract() { #[test] fn fa_deposit_refused_due_non_existing_contract() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = init_account_storage().unwrap(); let caller = H160::zero(); @@ -168,6 +179,10 @@ fn fa_deposit_refused_due_non_existing_contract() { #[test] fn fa_deposit_refused_non_compatible_interface() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = init_account_storage().unwrap(); let caller = H160::zero(); @@ -234,6 +249,10 @@ fn fa_deposit_refused_non_compatible_interface() { #[test] fn fa_deposit_proxy_state_reverted_if_ticket_balance_overflows() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = init_account_storage().unwrap(); let caller = H160::zero(); @@ -297,6 +316,10 @@ fn fa_deposit_proxy_state_reverted_if_ticket_balance_overflows() { #[test] fn fa_withdrawal_executed_via_l2_proxy_contract() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = init_account_storage().unwrap(); let sender = H160::from_low_u64_be(1); @@ -385,6 +408,10 @@ fn fa_withdrawal_executed_via_l2_proxy_contract() { #[test] fn fa_withdrawal_fails_due_to_faulty_l2_proxy() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = init_account_storage().unwrap(); let sender = H160::from_low_u64_be(1); @@ -438,6 +465,10 @@ fn fa_withdrawal_fails_due_to_faulty_l2_proxy() { #[test] fn fa_withdrawal_fails_due_to_insufficient_balance() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = init_account_storage().unwrap(); let sender = H160::from_low_u64_be(1); @@ -470,6 +501,10 @@ fn fa_withdrawal_fails_due_to_insufficient_balance() { #[test] fn fa_deposit_cannot_call_fa_withdrawal_precompile() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = init_account_storage().unwrap(); let caller = H160::zero(); diff --git a/etherlink/kernel_evm/evm_execution/src/fa_bridge/ticket_table.rs b/etherlink/kernel_evm/evm_execution/src/fa_bridge/ticket_table.rs index 119b9fa7886f3c429f17c2e441c8eff2780f0d37..97e67bcb45d729b616663eb8ca842dc13edc92a3 100644 --- a/etherlink/kernel_evm/evm_execution/src/fa_bridge/ticket_table.rs +++ b/etherlink/kernel_evm/evm_execution/src/fa_bridge/ticket_table.rs @@ -14,13 +14,13 @@ use tezos_smart_rollup_host::path::{concat, OwnedPath, RefPath}; use tezos_storage::{path_from_h256, read_u256_le_default, write_u256_le}; /// Path where global ticket table is stored -const TICKET_TABLE_PATH: RefPath = RefPath::assert_from(b"/ticket_table"); +const TICKET_TABLE_PATH: RefPath = RefPath::assert_from(b"/ticket_table"); pub trait TicketTable { /// Increases ticket balance fn ticket_balance_add( &mut self, - host: &mut impl Runtime, + host: &mut impl Runtime, ticket_hash: &H256, address: &H160, amount: U256, @@ -29,7 +29,7 @@ pub trait TicketTable { /// Decreases ticket balance fn ticket_balance_remove( &mut self, - host: &mut impl Runtime, + host: &mut impl Runtime, ticket_hash: &H256, address: &H160, amount: U256, @@ -39,7 +39,7 @@ pub trait TicketTable { pub(crate) fn ticket_balance_path( ticket_hash: &H256, address: &H160, -) -> Result { +) -> Result, AccountStorageError> { let suffix = concat(&path_from_h256(ticket_hash)?, &account_path(address)?)?; concat(&TICKET_TABLE_PATH, &suffix).map_err(Into::into) } @@ -47,7 +47,7 @@ pub(crate) fn ticket_balance_path( impl TicketTable for EthereumAccount { fn ticket_balance_add( &mut self, - host: &mut impl Runtime, + host: &mut impl Runtime, ticket_hash: &H256, owner: &H160, amount: U256, @@ -65,7 +65,7 @@ impl TicketTable for EthereumAccount { fn ticket_balance_remove( &mut self, - host: &mut impl Runtime, + host: &mut impl Runtime, ticket_hash: &H256, owner: &H160, amount: U256, @@ -84,7 +84,10 @@ impl TicketTable for EthereumAccount { #[cfg(test)] mod tests { - use tezos_evm_runtime::runtime::MockKernelHost; + use tezos_evm_runtime::{ + runtime::MockKernelHost, + safe_storage::{SafeStorage, WORLD_STATE_PATH}, + }; use tezos_smart_rollup_host::path::RefPath; use tezos_storage::read_u256_le_default; @@ -95,6 +98,10 @@ mod tests { #[test] fn ticket_table_balance_add_succeeds() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut account = EthereumAccount::from_address(&SYSTEM_ACCOUNT_ADDRESS).unwrap(); @@ -109,12 +116,15 @@ mod tests { .unwrap(); let path = b"\ - /evm/world_state/eth_accounts/0000000000000000000000000000000000000000/ticket_table\ + /eth_accounts/0000000000000000000000000000000000000000/ticket_table\ /0101010101010101010101010101010101010101010101010101010101010101\ /0202020202020202020202020202020202020202"; - let balance = - read_u256_le_default(&host, &RefPath::assert_from(path), U256::zero()) - .unwrap(); + let balance = read_u256_le_default( + &host, + &RefPath::::assert_from(path), + U256::zero(), + ) + .unwrap(); assert_eq!(U256::from(84), balance); } @@ -122,6 +132,10 @@ mod tests { #[test] fn ticket_table_balance_add_overflows() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut account = EthereumAccount::from_address(&SYSTEM_ACCOUNT_ADDRESS).unwrap(); @@ -138,12 +152,15 @@ mod tests { assert!(!res); let path = b"\ - /evm/world_state/eth_accounts/0000000000000000000000000000000000000000/ticket_table\ + /eth_accounts/0000000000000000000000000000000000000000/ticket_table\ /0101010101010101010101010101010101010101010101010101010101010101\ /0202020202020202020202020202020202020202"; - let balance = - read_u256_le_default(&host, &RefPath::assert_from(path), U256::zero()) - .unwrap(); + let balance = read_u256_le_default( + &host, + &RefPath::::assert_from(path), + U256::zero(), + ) + .unwrap(); assert_eq!(U256::MAX, balance); } diff --git a/etherlink/kernel_evm/evm_execution/src/handler.rs b/etherlink/kernel_evm/evm_execution/src/handler.rs index d2c52d374502f817d7d335044cb372208e0d12f3..22b8cfeb920a48f54d6d2f917cc268f7408c1176 100644 --- a/etherlink/kernel_evm/evm_execution/src/handler.rs +++ b/etherlink/kernel_evm/evm_execution/src/handler.rs @@ -236,7 +236,7 @@ mod benchmarks { *b"__wasm_debugger__::start_section(\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0)"; #[inline(always)] - pub fn start_opcode_section(host: &mut Host, opcode: &Opcode) { + pub fn start_opcode_section>(host: &mut Host, opcode: &Opcode) { unsafe { START_OPCODE_SECTION_MSG[33] = opcode.as_u8(); host.write_debug(core::str::from_utf8_unchecked(&START_OPCODE_SECTION_MSG)); @@ -244,7 +244,7 @@ mod benchmarks { } #[inline(always)] - pub fn start_precompile_section( + pub fn start_precompile_section>( host: &mut Host, address: H160, input: &Vec, @@ -272,7 +272,7 @@ mod benchmarks { *b"__wasm_debugger__::end_section()"; #[inline(always)] - pub fn end_opcode_section( + pub fn end_opcode_section, T>( host: &mut Host, gas: u64, step_result: &Result<(), Capture>, @@ -285,7 +285,7 @@ mod benchmarks { } #[inline(always)] - pub fn end_precompile_section(host: &mut Host) { + pub fn end_precompile_section>(host: &mut Host) { unsafe { host.write_debug(core::str::from_utf8_unchecked(&END_PRECOMPILE_SECTION_MSG)); } @@ -329,7 +329,7 @@ pub type LayerCache = HashMap; pub type StorageCache = HashMap; /// The implementation of the SputnikVM [Handler] trait -pub struct EvmHandler<'a, Host: Runtime> { +pub struct EvmHandler<'a, Host: Runtime> { /// The host pub host: &'a mut Host, /// The ethereum accounts storage @@ -369,7 +369,7 @@ pub struct EvmHandler<'a, Host: Runtime> { reentrancy_guard: ReentrancyGuard, } -impl<'a, Host: Runtime> EvmHandler<'a, Host> { +impl<'a, Host: Runtime> EvmHandler<'a, Host> { /// Create a new handler to suit a new, initial EVM call context #[allow(clippy::too_many_arguments)] pub fn new( @@ -2129,7 +2129,7 @@ impl<'a, Host: Runtime> EvmHandler<'a, Host> { } } -fn update_cache( +fn update_cache>( handler: &mut EvmHandler<'_, Host>, address: H160, index: H256, @@ -2145,7 +2145,7 @@ fn update_cache( } } -fn find_storage_key( +fn find_storage_key>( handler: &mut EvmHandler<'_, Host>, address: H160, index: H256, @@ -2163,7 +2163,7 @@ fn find_storage_key( /// Committing the storage cache means that the current layer changes /// are propagated to the previous one and the current layer is popped. -fn commit_storage_cache( +fn commit_storage_cache>( handler: &mut EvmHandler<'_, Host>, current_layer: usize, ) { @@ -2177,7 +2177,7 @@ fn commit_storage_cache( } } -fn cached_storage_access( +fn cached_storage_access>( handler: &mut EvmHandler<'_, Host>, address: H160, index: H256, @@ -2209,7 +2209,7 @@ fn cached_storage_access( } #[allow(unused_variables)] -impl<'a, Host: Runtime> Handler for EvmHandler<'a, Host> { +impl<'a, Host: Runtime> Handler for EvmHandler<'a, Host> { type CreateInterrupt = Infallible; type CreateFeedback = Infallible; type CallInterrupt = Infallible; @@ -2817,12 +2817,15 @@ mod test { use std::str::FromStr; use std::vec; use tezos_ethereum::block::BlockFees; - use tezos_evm_runtime::runtime::MockKernelHost; + use tezos_evm_runtime::{ + runtime::MockKernelHost, + safe_storage::{SafeStorage, WORLD_STATE_PATH}, + }; const DUMMY_ALLOCATED_TICKS: u64 = 1_000_000_000; fn set_code( - handler: &mut EvmHandler<'_, MockKernelHost>, + handler: &mut EvmHandler<'_, impl Runtime>, address: &H160, code: Vec, ) { @@ -2832,7 +2835,7 @@ mod test { } fn set_nonce( - handler: &mut EvmHandler<'_, MockKernelHost>, + handler: &mut EvmHandler<'_, impl Runtime>, address: &H160, nonce: u64, ) { @@ -2840,13 +2843,16 @@ mod test { account.set_nonce(handler.borrow_host(), nonce).unwrap() } - fn get_balance(handler: &mut EvmHandler<'_, MockKernelHost>, address: &H160) -> U256 { + fn get_balance( + handler: &mut EvmHandler<'_, impl Runtime>, + address: &H160, + ) -> U256 { let account = handler.get_or_create_account(*address).unwrap(); account.balance(handler.borrow_host()).unwrap() } fn set_balance( - handler: &mut EvmHandler<'_, MockKernelHost>, + handler: &mut EvmHandler<'_, impl Runtime>, address: &H160, new_balance: U256, ) { @@ -2870,7 +2876,7 @@ mod test { } fn get_durable_slot( - handler: &mut EvmHandler<'_, MockKernelHost>, + handler: &mut EvmHandler<'_, impl Runtime>, address: &H160, index: &H256, ) -> H256 { @@ -2896,8 +2902,12 @@ mod test { #[test] fn legacy_create_to_correct_address() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); @@ -2939,8 +2949,12 @@ mod test { #[test] fn create2_to_correct_address() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller: H160 = @@ -2981,8 +2995,12 @@ mod test { #[test] fn create2_to_correct_address_nonzero_salt() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); @@ -3027,8 +3045,12 @@ mod test { #[test] fn origin_instruction_returns_origin_address() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(28349_u64); @@ -3092,8 +3114,12 @@ mod test { #[test] fn contract_call_produces_correct_output() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(28349_u64); @@ -3186,8 +3212,12 @@ mod test { #[test] fn contract_call_fails_beyond_max_stack_depth() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(2340); @@ -3281,8 +3311,12 @@ mod test { #[test] fn contract_call_succeeds_at_maximum_stack_depth() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(8213); @@ -3374,8 +3408,12 @@ mod test { #[test] fn contract_can_use_durable_storage() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(444); @@ -3446,8 +3484,12 @@ mod test { #[test] fn contract_create_can_use_durable_storage() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(117); @@ -3503,8 +3545,12 @@ mod test { #[test] fn contract_create_has_return_when_revert() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(117); @@ -3569,8 +3615,12 @@ mod test { #[test] fn contract_call_does_transfer() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(118); @@ -3632,8 +3682,12 @@ mod test { #[test] fn contract_call_fails_when_insufficient_funds_for_transfer() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(523_u64); @@ -3694,8 +3748,12 @@ mod test { #[test] fn revert_can_return_a_value() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(523_u64); @@ -3765,8 +3823,12 @@ mod test { #[test] fn return_hash_of_zero_for_unavailable_block() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(523_u64); @@ -3793,8 +3855,12 @@ mod test { #[test] fn transactions_fails_if_not_enough_allocated_ticks() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::london(); let caller = H160::from_low_u64_be(523_u64); @@ -3854,8 +3920,12 @@ mod test { #[test] fn store_after_offset_1024() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(523_u64); @@ -3906,8 +3976,12 @@ mod test { #[test] fn dont_crash_on_blockhash_instruction() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(523_u64); @@ -3974,8 +4048,12 @@ mod test { #[test] fn prevent_collision_create2_selfdestruct() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); @@ -4043,8 +4121,12 @@ mod test { fn create_contract_with_insufficient_funds() { //Init let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); @@ -4086,8 +4168,12 @@ mod test { #[test] fn inter_call_with_non_zero_transfer_value_gets_call_stipend() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(523_u64); @@ -4162,8 +4248,12 @@ mod test { #[test] fn code_hash_of_zero_for_non_existing_address() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(523_u64); @@ -4191,8 +4281,12 @@ mod test { #[test] fn create_contract_with_selfdestruct_init_code() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); @@ -4235,8 +4329,12 @@ mod test { #[test] fn contract_that_selfdestruct_not_deleted_within_same_transaction() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(523_u64); @@ -4318,8 +4416,12 @@ mod test { #[test] fn contract_that_selfdestruct_can_be_called_again_in_same_transaction() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(523_u64); @@ -4418,8 +4520,12 @@ mod test { #[test] fn contract_selfdestruct_itself_has_no_balance_left() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); @@ -4484,8 +4590,12 @@ mod test { #[test] fn address_still_marked_as_hot_after_creation_fails() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); @@ -4558,8 +4668,12 @@ mod test { #[test] fn precompile_failure_are_not_fatal() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(523_u64); @@ -4617,8 +4731,12 @@ mod test { #[test] fn inner_create_costs_gas() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(523_u64); @@ -4699,8 +4817,12 @@ mod test { #[test] fn exceed_max_create_init_code_size_fail_with_error() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(523_u64); @@ -4739,8 +4861,12 @@ mod test { #[test] fn create_fails_with_max_nonce() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); @@ -4781,8 +4907,12 @@ mod test { #[test] fn record_call_stipend_when_balance_not_enough_for_inner_call() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(523_u64); @@ -4859,8 +4989,12 @@ mod test { #[test] fn eip161_gas_consumption_rules_for_suicide() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(111_u64); @@ -4931,8 +5065,12 @@ mod test { #[test] fn eip161_gas_consumption_rules_for_call() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller = H160::from_low_u64_be(111_u64); diff --git a/etherlink/kernel_evm/evm_execution/src/lib.rs b/etherlink/kernel_evm/evm_execution/src/lib.rs index efff45a8512016ca064f53cb2f64c9d832463e08..4d43795004d5ba13f56e0fb08f1bec8fee4d5405 100755 --- a/etherlink/kernel_evm/evm_execution/src/lib.rs +++ b/etherlink/kernel_evm/evm_execution/src/lib.rs @@ -134,7 +134,7 @@ pub enum EthereumError { Tracer(#[from] tracer::Error), } -fn trace_outcome( +fn trace_outcome>( handler: EvmHandler, is_success: bool, output: Option<&[u8]>, @@ -150,7 +150,7 @@ fn trace_outcome( } #[allow(clippy::too_many_arguments)] -fn call_trace_outcome( +fn call_trace_outcome>( handler: EvmHandler, base_call_type: &str, call_data: Vec, @@ -204,7 +204,7 @@ fn call_trace_outcome( } #[allow(clippy::too_many_arguments)] -fn call_trace_error( +fn call_trace_error>( handler: EvmHandler, base_call_type: &str, call_data: Vec, @@ -266,7 +266,7 @@ pub fn run_transaction<'a, Host>( tracer: Option, ) -> Result, EthereumError> where - Host: Runtime, + Host: Runtime, { fn do_refund(outcome: &handler::ExecutionOutcome, pay_for_gas: bool) -> bool { match outcome.result { @@ -419,11 +419,10 @@ where Ok(None) } } -pub const NATIVE_TOKEN_TICKETER_PATH: RefPath = - RefPath::assert_from(b"/evm/world_state/ticketer"); +pub const NATIVE_TOKEN_TICKETER_PATH: RefPath = RefPath::assert_from(b"/ticketer"); /// Reads the ticketer address set by the installer, if any, encoded in b58. -pub fn read_ticketer(host: &impl Runtime) -> Option { +pub fn read_ticketer(host: &impl Runtime) -> Option { let ticketer = host.store_read_all(&NATIVE_TOKEN_TICKETER_PATH).ok()?; let kt1_b58 = String::from_utf8(ticketer.to_vec()).ok()?; @@ -449,6 +448,7 @@ mod test { use tezos_ethereum::tx_common::EthereumTransactionCommon; use tezos_evm_runtime::runtime::MockKernelHost; use tezos_evm_runtime::runtime::Runtime; + use tezos_evm_runtime::safe_storage::{SafeStorage, WORLD_STATE_PATH}; // The compiled initialization code for the Ethereum demo contract given // as an example in kernel_evm/solidity_examples/storage.sol @@ -474,7 +474,7 @@ mod test { const DUMMY_ALLOCATED_TICKS: u64 = 1_000_000_000; fn set_balance( - host: &mut MockKernelHost, + host: &mut impl Runtime, evm_account_storage: &mut EthereumAccountStorage, address: &H160, balance: U256, @@ -495,7 +495,7 @@ mod test { } fn set_storage( - host: &mut MockKernelHost, + host: &mut impl Runtime, evm_account_storage: &mut EthereumAccountStorage, address: &H160, index: &H256, @@ -508,7 +508,7 @@ mod test { } fn get_storage( - host: &mut MockKernelHost, + host: &mut impl Runtime, evm_account_storage: &mut EthereumAccountStorage, address: &H160, index: &H256, @@ -520,7 +520,7 @@ mod test { } fn get_balance( - host: &mut MockKernelHost, + host: &mut impl Runtime, evm_account_storage: &mut EthereumAccountStorage, address: &H160, ) -> U256 { @@ -532,7 +532,7 @@ mod test { // Simple utility function to set some code for an account fn set_account_code( - host: &mut impl Runtime, + host: &mut impl Runtime, evm_account_storage: &mut EthereumAccountStorage, address: &H160, code: &[u8], @@ -543,7 +543,7 @@ mod test { } fn get_code( - host: &mut MockKernelHost, + host: &mut impl Runtime, evm_account_storage: &mut EthereumAccountStorage, address: &H160, ) -> Vec { @@ -554,7 +554,7 @@ mod test { } fn bump_nonce( - host: &mut impl Runtime, + host: &mut impl Runtime, evm_account_storage: &mut EthereumAccountStorage, address: &H160, ) { @@ -564,7 +564,7 @@ mod test { } fn get_nonce( - host: &mut MockKernelHost, + host: &mut impl Runtime, evm_account_storage: &mut EthereumAccountStorage, address: &H160, ) -> u64 { @@ -592,8 +592,12 @@ mod test { #[test] fn transfer_without_sufficient_funds_fails() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let callee = H160::from_low_u64_be(234213); @@ -657,8 +661,12 @@ mod test { #[test] fn transfer_funds_with_sufficient_balance() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let callee = H160::from_low_u64_be(82193); @@ -723,8 +731,12 @@ mod test { #[test] fn create_contract_fails_with_insufficient_funds() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let callee = None; @@ -777,8 +789,12 @@ mod test { #[test] fn create_contract_succeeds_with_valid_initialization() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let callee = None; @@ -938,8 +954,12 @@ mod test { #[test] fn create_contract_erc20_succeeds() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let callee = None; @@ -994,8 +1014,12 @@ mod test { #[test] fn create_contract_fails_when_initialization_fails() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let callee = None; @@ -1052,8 +1076,12 @@ mod test { fn call_non_existing_contract() { // Arange let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let target = H160::from_low_u64_be(117u64); let caller = H160::from_low_u64_be(118u64); @@ -1189,8 +1217,12 @@ mod test { fn call_simple_return_contract() { // Arrange let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let target = H160::from_low_u64_be(117u64); let caller = H160::from_low_u64_be(118u64); @@ -1251,8 +1283,12 @@ mod test { fn call_simple_revert_contract() { // Arrange let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let target = H160::from_low_u64_be(117u64); let caller = H160::from_low_u64_be(118u64); @@ -1320,8 +1356,12 @@ mod test { fn call_contract_with_invalid_opcode() { // Arrange let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let target = H160::from_low_u64_be(117u64); let caller = H160::from_low_u64_be(118u64); @@ -1372,8 +1412,12 @@ mod test { #[test] fn no_transfer_when_contract_call_fails() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let caller = H160::from_low_u64_be(118_u64); let gas_price = U256::from(21000); @@ -1437,8 +1481,12 @@ mod test { fn call_precompiled_contract() { // Arrange let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let target = H160::from_low_u64_be(4u64); // identity contract let mut evm_account_storage = init_evm_account_storage().unwrap(); let caller = H160::from_low_u64_be(118u64); @@ -1492,8 +1540,12 @@ mod test { fn call_ecrecover() { // Arrange let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); // example from https://www.evm.codes/precompiled?fork=shanghai let data_str = "456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3000000000000000000000000000000000000000000000000000000000000001c9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac80388256084f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada"; let data = hex::decode(data_str) @@ -1554,8 +1606,12 @@ mod test { fn create_and_call_contract() { // Arrange let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let target = H160::from_low_u64_be(117u64); let caller = H160::from_low_u64_be(118u64); @@ -1656,8 +1712,12 @@ mod test { #[test] fn static_calls_cannot_update_storage() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let target = H160::from_low_u64_be(117_u64); let caller = H160::from_low_u64_be(118_u64); @@ -1750,8 +1810,12 @@ mod test { #[test] fn static_calls_fail_when_logging() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let target = H160::from_low_u64_be(117_u64); let caller = H160::from_low_u64_be(118_u64); @@ -1844,8 +1908,12 @@ mod test { #[test] fn logs_get_written_to_output() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let target = H160::from_low_u64_be(117_u64); let caller = H160::from_low_u64_be(118_u64); @@ -1965,8 +2033,12 @@ mod test { #[test] fn no_logs_when_contract_reverts() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let target = H160::from_low_u64_be(117_u64); let caller = H160::from_low_u64_be(118_u64); @@ -2071,8 +2143,12 @@ mod test { #[test] fn contract_selfdestruct_deletes_contract() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let target = H160::from_low_u64_be(42_u64); let caller = H160::from_low_u64_be(115_u64); @@ -2181,8 +2257,12 @@ mod test { #[test] fn selfdestruct_is_ignored_when_call_fails() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let target = H160::from_low_u64_be(42_u64); let caller = H160::from_low_u64_be(115_u64); @@ -2300,6 +2380,10 @@ mod test { fn test_chain_id() { // Arrange let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let chain_id = U256::from(42); let mut chain_id_bytes = [0u8; 32]; chain_id.to_big_endian(&mut chain_id_bytes); @@ -2315,7 +2399,7 @@ mod test { u64::MAX, H160::zero(), ); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let target = H160::from_low_u64_be(117u64); let caller = H160::from_low_u64_be(118u64); @@ -2385,6 +2469,10 @@ mod test { fn test_base_fee_per_gas() { // Arrange let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let base_fee_per_gas = U256::from(23000); let mut base_fee_per_gas_bytes = [0u8; 32]; base_fee_per_gas.to_big_endian(&mut base_fee_per_gas_bytes); @@ -2400,7 +2488,7 @@ mod test { u64::MAX, H160::zero(), ); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let target = H160::from_low_u64_be(117u64); let caller = H160::from_low_u64_be(118u64); @@ -2491,8 +2579,12 @@ mod test { #[test] fn evm_should_fail_gracefully_when_balance_overflow_occurs() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let caller = H160::from_low_u64_be(523); let target = H160::from_low_u64_be(210); @@ -2539,8 +2631,12 @@ mod test { #[test] fn create_contract_gas_cost() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let callee = None; @@ -2598,8 +2694,12 @@ mod test { #[test] fn create_contract_fail_gas_cost() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let callee = None; @@ -2656,6 +2756,10 @@ mod test { fn test_transaction_data_cost() { // Arrange let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let base_fee_per_gas = U256::from(23000); let block_fees = BlockFees::new( U256::one(), @@ -2669,7 +2773,7 @@ mod test { u64::MAX, H160::zero(), ); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let target = H160::from_low_u64_be(117u64); let caller = H160::from_low_u64_be(118u64); @@ -2726,6 +2830,10 @@ mod test { fn test_transaction_data_cost_non_zero() { // Arrange let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let base_fee_per_gas = U256::from(23000); let block_fees = BlockFees::new( U256::one(), @@ -2739,7 +2847,7 @@ mod test { u64::MAX, H160::zero(), ); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let target = H160::from_low_u64_be(117u64); let caller = H160::from_low_u64_be(118u64); @@ -2814,8 +2922,12 @@ mod test { ) -> Result, EthereumError> { // Arrange let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let caller = H160::from_low_u64_be(118u64); let gas_price = U256::from(1356); @@ -2894,8 +3006,12 @@ mod test { #[test] fn test_create_to_address_with_code_returns_error() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let caller = @@ -2967,8 +3083,12 @@ mod test { #[test] fn test_caller_nonce_after_create() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let caller = @@ -3067,8 +3187,12 @@ mod test { #[test] fn test_caller_nonce_after_create_collision() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let caller = @@ -3175,8 +3299,12 @@ mod test { #[test] fn test_create_to_address_with_non_zero_nonce_returns_error() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let caller = @@ -3240,8 +3368,12 @@ mod test { #[test] fn created_contract_start_at_nonce_one() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let callee = None; @@ -3285,8 +3417,12 @@ mod test { #[test] fn call_contract_create_contract_with_insufficient_funds() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let callee = H160::from_str("095e7baea6a6c7c4c2dfeb977efac326af552d87").unwrap(); @@ -3350,8 +3486,12 @@ mod test { #[test] fn nested_create_check_nonce_start_at_one() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let caller = H160::from_str("a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap(); @@ -3463,17 +3603,16 @@ mod test { } fn out_of_tick_scenario( + host: &mut impl Runtime, retriable: bool, ) -> ( - MockKernelHost, Result, EthereumError>, EthereumAccount, U256, u64, ) { - let mut host = MockKernelHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let callee = None; let caller = H160::from_low_u64_be(117); @@ -3490,19 +3629,14 @@ mod test { let initial_balance = gas_price .saturating_mul(gas_limit.into()) .saturating_mul(U256::from(2)); - set_balance( - &mut host, - &mut evm_account_storage, - &caller, - initial_balance, - ); + set_balance(host, &mut evm_account_storage, &caller, initial_balance); let path = account_path(&caller).unwrap(); - let account = evm_account_storage.get_or_create(&host, &path).unwrap(); - let initial_caller_nonce = account.nonce(&host).unwrap(); + let account = evm_account_storage.get_or_create(host, &path).unwrap(); + let initial_caller_nonce = account.nonce(host).unwrap(); let result = run_transaction( - &mut host, + host, &block, &mut evm_account_storage, &precompiles, @@ -3520,13 +3654,18 @@ mod test { None, ); - (host, result, account, initial_balance, initial_caller_nonce) + (result, account, initial_balance, initial_caller_nonce) } #[test] fn transaction_has_no_impact_if_retriable() { - let (host, result, caller, initial_caller_balance, initial_caller_nonce) = - out_of_tick_scenario(true); + let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; + let (result, caller, initial_caller_balance, initial_caller_nonce) = + out_of_tick_scenario(&mut host, true); let caller_balance = caller.balance(&host).unwrap(); let caller_nonce = caller.nonce(&host).unwrap(); @@ -3547,8 +3686,13 @@ mod test { #[test] fn non_retriable_transaction_pays_for_exhausted_ticks() { - let (host, result, caller, initial_caller_balance, initial_caller_nonce) = - out_of_tick_scenario(false); + let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; + let (result, caller, initial_caller_balance, initial_caller_nonce) = + out_of_tick_scenario(&mut host, false); let caller_balance = caller.balance(&host).unwrap(); let caller_nonce = caller.nonce(&host).unwrap(); @@ -3576,8 +3720,12 @@ mod test { #[ignore] fn multiple_call_all_the_way_to_1024() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let caller = H160::from_str("a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap(); @@ -3663,8 +3811,12 @@ mod test { #[test] fn multiple_call_fails_right_after_1024() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let caller = H160::from_str("a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap(); @@ -3748,8 +3900,12 @@ mod test { #[ignore] fn call_too_deep_not_revert() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let caller = H160::from_str("a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap(); @@ -3799,8 +3955,12 @@ mod test { // The test was a bit tweaked so that the called contract transfer 1 WEI to the called contract. let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let address_1 = @@ -3904,8 +4064,12 @@ mod test { // with the second data. let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let address_1 = @@ -3981,8 +4145,12 @@ mod test { #[test] fn nonce_bump_before_tx() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let caller = H160::from_str("a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap(); diff --git a/etherlink/kernel_evm/evm_execution/src/precompiles/blake2.rs b/etherlink/kernel_evm/evm_execution/src/precompiles/blake2.rs index 7dc032392128f2db115eb6bdb15e6fc921a3b9fa..ed0edbf304eea8cb818acdb47dc7f20ee29b2d40 100644 --- a/etherlink/kernel_evm/evm_execution/src/precompiles/blake2.rs +++ b/etherlink/kernel_evm/evm_execution/src/precompiles/blake2.rs @@ -112,7 +112,7 @@ fn blake2f_output_for_wrong_input() -> EthereumError { }) } -fn blake2f_precompile_without_gas_draining( +fn blake2f_precompile_without_gas_draining>( handler: &mut EvmHandler, input: &[u8], ) -> Result { @@ -193,7 +193,7 @@ fn blake2f_precompile_without_gas_draining( }) } -pub fn blake2f_precompile( +pub fn blake2f_precompile>( handler: &mut EvmHandler, input: &[u8], _context: &Context, diff --git a/etherlink/kernel_evm/evm_execution/src/precompiles/ecdsa.rs b/etherlink/kernel_evm/evm_execution/src/precompiles/ecdsa.rs index 93b505082685c0eda01b8b39006c8f82a534105d..c39fcd536da15e1d91fdfa87e3f13167d55b5761 100644 --- a/etherlink/kernel_evm/evm_execution/src/precompiles/ecdsa.rs +++ b/etherlink/kernel_evm/evm_execution/src/precompiles/ecdsa.rs @@ -52,7 +52,7 @@ fn erec_parse_inputs(input: &[u8]) -> ([u8; 32], [u8; 32], [u8; 64]) { } // Implementation of 0x01 ECDSA recover -pub fn ecrecover_precompile( +pub fn ecrecover_precompile>( handler: &mut EvmHandler, input: &[u8], _context: &Context, diff --git a/etherlink/kernel_evm/evm_execution/src/precompiles/fa_bridge.rs b/etherlink/kernel_evm/evm_execution/src/precompiles/fa_bridge.rs index 91f613ff816699668a501ad13524e90340ee6148..8344fd26b90dc0d9c25aafe5ff29b441f5c0adb9 100644 --- a/etherlink/kernel_evm/evm_execution/src/precompiles/fa_bridge.rs +++ b/etherlink/kernel_evm/evm_execution/src/precompiles/fa_bridge.rs @@ -80,7 +80,7 @@ macro_rules! precompile_outcome_error { /// FA bridge precompile entrypoint. #[allow(unused)] -pub fn fa_bridge_precompile( +pub fn fa_bridge_precompile>( handler: &mut EvmHandler, input: &[u8], context: &Context, @@ -161,7 +161,10 @@ mod tests { use evm::{Config, ExitError}; use primitive_types::{H160, U256}; use tezos_data_encoding::enc::BinWriter; - use tezos_evm_runtime::runtime::MockKernelHost; + use tezos_evm_runtime::{ + runtime::{MockKernelHost, Runtime}, + safe_storage::{SafeStorage, WORLD_STATE_PATH}, + }; use crate::{ account_storage::{init_account_storage, EthereumAccountStorage}, @@ -181,7 +184,7 @@ mod tests { #[allow(clippy::too_many_arguments)] fn execute_precompile( - host: &mut MockKernelHost, + host: &mut impl Runtime, evm_account_storage: &mut EthereumAccountStorage, caller: H160, value: U256, @@ -194,7 +197,7 @@ mod tests { let config = Config::shanghai(); let callee = FA_BRIDGE_PRECOMPILE_ADDRESS; - let precompiles = precompiles::precompile_set::(true); + let precompiles = precompiles::precompile_set(true); let mut handler = EvmHandler::new( host, @@ -221,6 +224,10 @@ mod tests { #[test] fn fa_bridge_precompile_fails_due_to_bad_selector() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = init_account_storage().unwrap(); let outcome = execute_precompile( @@ -242,6 +249,10 @@ mod tests { #[test] fn fa_bridge_precompile_fails_due_to_low_gas_limit() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = init_account_storage().unwrap(); let outcome = execute_precompile( @@ -264,6 +275,10 @@ mod tests { #[test] fn fa_bridge_precompile_fails_due_to_non_zero_value() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = init_account_storage().unwrap(); let caller = H160::from_low_u64_be(1); @@ -293,6 +308,10 @@ mod tests { #[test] fn fa_bridge_precompile_fails_due_to_static_call() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = init_account_storage().unwrap(); let caller = H160::from_low_u64_be(1); @@ -315,6 +334,10 @@ mod tests { #[test] fn fa_bridge_precompile_fails_due_to_delegate_call() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = init_account_storage().unwrap(); let caller = H160::from_low_u64_be(1); @@ -322,7 +345,7 @@ mod tests { let block = dummy_first_block(); let config = Config::shanghai(); - let precompiles = precompiles::precompile_set::(true); + let precompiles = precompiles::precompile_set(true); let mut handler = EvmHandler::new( &mut mock_runtime, @@ -357,6 +380,10 @@ mod tests { #[test] fn fa_bridge_precompile_fails_due_to_invalid_input() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = init_account_storage().unwrap(); let outcome = execute_precompile( @@ -378,6 +405,10 @@ mod tests { #[test] fn fa_bridge_precompile_succeeds_without_l2_proxy_contract() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = init_account_storage().unwrap(); let ticket_owner = H160::from_low_u64_be(1); @@ -446,6 +477,10 @@ mod tests { #[test] fn fa_bridge_precompile_cannot_call_itself() { let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = init_account_storage().unwrap(); let system = H160::zero(); diff --git a/etherlink/kernel_evm/evm_execution/src/precompiles/hash.rs b/etherlink/kernel_evm/evm_execution/src/precompiles/hash.rs index c3d25159b4227f566bf5544d05b63548976f0a90..6f51701f194bb15ec59c818c030a3a1a46f6ff2c 100644 --- a/etherlink/kernel_evm/evm_execution/src/precompiles/hash.rs +++ b/etherlink/kernel_evm/evm_execution/src/precompiles/hash.rs @@ -14,7 +14,7 @@ use tezos_evm_logging::Level::Debug; use tezos_evm_runtime::runtime::Runtime; // Implementation of 0x03 precompiled (sha256) -pub fn sha256_precompile( +pub fn sha256_precompile>( handler: &mut EvmHandler, input: &[u8], _context: &Context, @@ -49,7 +49,7 @@ pub fn sha256_precompile( } // Implementation of 0x04 precompiled (ripemd160) -pub fn ripemd160_precompile( +pub fn ripemd160_precompile>( handler: &mut EvmHandler, input: &[u8], _context: &Context, diff --git a/etherlink/kernel_evm/evm_execution/src/precompiles/identity.rs b/etherlink/kernel_evm/evm_execution/src/precompiles/identity.rs index 680288ae9f9f23fa630686499d2afbd15c16d3c2..dc262d8e78a91194eea907632c56c5b2d964024f 100644 --- a/etherlink/kernel_evm/evm_execution/src/precompiles/identity.rs +++ b/etherlink/kernel_evm/evm_execution/src/precompiles/identity.rs @@ -13,7 +13,7 @@ use tezos_evm_logging::Level::Debug; use tezos_evm_runtime::runtime::Runtime; // Implementation of 0x02 precompiled (identity) -pub fn identity_precompile( +pub fn identity_precompile>( handler: &mut EvmHandler, input: &[u8], _context: &Context, diff --git a/etherlink/kernel_evm/evm_execution/src/precompiles/mod.rs b/etherlink/kernel_evm/evm_execution/src/precompiles/mod.rs index f11eeee487fe9648e2cc221d6fcd56e2dad24bd6..e78b3bf094f49ee94ef6ebf56c576f92270a3e2f 100644 --- a/etherlink/kernel_evm/evm_execution/src/precompiles/mod.rs +++ b/etherlink/kernel_evm/evm_execution/src/precompiles/mod.rs @@ -84,7 +84,7 @@ pub type PrecompileFn = fn( /// This is adapted from SputnikVM trait with same name. It has been /// modified to take the Host into account, so that precompiles can /// interact with log and durable storage and the rest of the kernel. -pub trait PrecompileSet { +pub trait PrecompileSet> { /// Execute a single contract call to a precompiled contract. Should /// return None (and have no effect), if there is no precompiled contract /// at the address given. @@ -106,7 +106,7 @@ pub trait PrecompileSet { /// One implementation for PrecompileSet above. Adapted from SputnikVM. pub type PrecompileBTreeMap = BTreeMap>; -impl PrecompileSet for PrecompileBTreeMap { +impl> PrecompileSet for PrecompileBTreeMap { fn execute( &self, handler: &mut EvmHandler, @@ -117,7 +117,7 @@ impl PrecompileSet for PrecompileBTreeMap { transfer: Option, ) -> Option> where - Host: Runtime, + Host: Runtime, { self.get(&address) .map(|precompile| (*precompile)(handler, input, context, is_static, transfer)) @@ -134,7 +134,7 @@ impl PrecompileSet for PrecompileBTreeMap { type PrecompileWithoutGasDrainFn = fn(_: &mut EvmHandler, _: &[u8]) -> Result; -pub fn call_precompile_with_gas_draining( +pub fn call_precompile_with_gas_draining>( handler: &mut EvmHandler, input: &[u8], precompile_contract_without_gas: PrecompileWithoutGasDrainFn, @@ -162,7 +162,7 @@ pub const WITHDRAWAL_ADDRESS: H160 = H160([ 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ]); -pub fn evm_precompile_set() -> PrecompileBTreeMap { +pub fn evm_precompile_set>() -> PrecompileBTreeMap { BTreeMap::from([ ( H160::from_low_u64_be(1u64), @@ -204,7 +204,7 @@ pub fn evm_precompile_set() -> PrecompileBTreeMap { } /// Factory function for generating the precompileset that the EVM kernel uses. -pub fn precompile_set( +pub fn precompile_set>( enable_fa_withdrawals: bool, ) -> PrecompileBTreeMap { let mut precompiles = evm_precompile_set(); @@ -223,7 +223,7 @@ pub fn precompile_set( precompiles } -pub fn precompile_set_with_revert_withdrawals( +pub fn precompile_set_with_revert_withdrawals>( enable_fa_withdrawals: bool, ) -> PrecompileBTreeMap { let mut precompiles = evm_precompile_set(); @@ -288,18 +288,20 @@ mod test_helpers { use crate::NATIVE_TOKEN_TICKETER_PATH; use evm::Config; use evm::Transfer; - use host::runtime::Runtime; use primitive_types::{H160, U256}; use tezos_ethereum::block::BlockConstants; use tezos_ethereum::block::BlockFees; - use tezos_evm_runtime::runtime::MockKernelHost; + use tezos_evm_runtime::{ + runtime::{MockKernelHost, Runtime}, + safe_storage::{SafeStorage, WORLD_STATE_PATH}, + }; use super::precompile_set; const DUMMY_ALLOCATED_TICKS: u64 = 100_000_000; pub const DUMMY_TICKETER: &str = "KT1TxqZ8QtKvLu3V3JH7Gx58n7Co8pgtpQU5"; pub fn set_balance( - host: &mut MockKernelHost, + host: &mut impl Runtime, evm_account_storage: &mut EthereumAccountStorage, address: &H160, balance: U256, @@ -328,6 +330,10 @@ mod test_helpers { ) -> Result { let caller = H160::from_low_u64_be(118u64); let mut mock_runtime = MockKernelHost::default(); + let mut mock_runtime = SafeStorage { + host: &mut mock_runtime, + root: WORLD_STATE_PATH, + }; let block_fees = BlockFees::new( U256::from(21000), U256::from(21000), @@ -341,7 +347,7 @@ mod test_helpers { H160::zero(), ); let mut evm_account_storage = init_evm_account_storage().unwrap(); - let precompiles = precompile_set::(false); + let precompiles = precompile_set(false); let config = Config::shanghai(); let gas_price = U256::from(21000); @@ -385,7 +391,7 @@ mod test_helpers { ) } - fn set_ticketer(host: &mut MockKernelHost, address: &str) { + fn set_ticketer(host: &mut impl Runtime, address: &str) { host.store_write(&NATIVE_TOKEN_TICKETER_PATH, address.as_bytes(), 0) .unwrap(); } diff --git a/etherlink/kernel_evm/evm_execution/src/precompiles/modexp.rs b/etherlink/kernel_evm/evm_execution/src/precompiles/modexp.rs index 132bafe5163b20b45489a653458499647490d871..0ea7608b945a42bae9b3649e17d6fc6d2c0fa21a 100644 --- a/etherlink/kernel_evm/evm_execution/src/precompiles/modexp.rs +++ b/etherlink/kernel_evm/evm_execution/src/precompiles/modexp.rs @@ -75,7 +75,7 @@ fn modexp_mod_overflow_exit(reason: &'static str) -> PrecompileOutcome { // to be taken up by the next value const HEADER_LENGTH: usize = 96; -pub fn modexp_precompile( +pub fn modexp_precompile>( handler: &mut EvmHandler, input: &[u8], _context: &Context, diff --git a/etherlink/kernel_evm/evm_execution/src/precompiles/revert.rs b/etherlink/kernel_evm/evm_execution/src/precompiles/revert.rs index 4fa410f69d56edf31de676a7612106d801417fd9..873e06931137be24ad1cdd932be09486c46cebcc 100644 --- a/etherlink/kernel_evm/evm_execution/src/precompiles/revert.rs +++ b/etherlink/kernel_evm/evm_execution/src/precompiles/revert.rs @@ -12,7 +12,7 @@ use crate::{handler::EvmHandler, EthereumError}; use super::PrecompileOutcome; -pub fn revert_precompile( +pub fn revert_precompile>( _handler: &mut EvmHandler, _input: &[u8], _context: &Context, diff --git a/etherlink/kernel_evm/evm_execution/src/precompiles/withdrawal.rs b/etherlink/kernel_evm/evm_execution/src/precompiles/withdrawal.rs index ceeb6eea8e4149990ca0c179508533306ac3fa3a..3703f8151e55dfe02e95f812195cff08defd389d 100644 --- a/etherlink/kernel_evm/evm_execution/src/precompiles/withdrawal.rs +++ b/etherlink/kernel_evm/evm_execution/src/precompiles/withdrawal.rs @@ -92,7 +92,7 @@ fn prepare_message( } /// Implementation of Etherlink specific withdrawals precompiled contract. -pub fn withdrawal_precompile( +pub fn withdrawal_precompile>( handler: &mut EvmHandler, input: &[u8], context: &Context, diff --git a/etherlink/kernel_evm/evm_execution/src/precompiles/zero_knowledge.rs b/etherlink/kernel_evm/evm_execution/src/precompiles/zero_knowledge.rs index c351d7cb53fa62558bdc226f67bbc18922c7052a..5360b7c04cb2ec54b89a4c3262f12c4cecb2d6ab 100644 --- a/etherlink/kernel_evm/evm_execution/src/precompiles/zero_knowledge.rs +++ b/etherlink/kernel_evm/evm_execution/src/precompiles/zero_knowledge.rs @@ -60,7 +60,7 @@ fn read_point(input: &[u8], pos: usize) -> Result { } } -fn ecadd_precompile_without_gas_draining( +fn ecadd_precompile_without_gas_draining>( handler: &mut EvmHandler, input: &[u8], ) -> Result { @@ -103,7 +103,7 @@ fn ecadd_precompile_without_gas_draining( }) } -pub fn ecadd_precompile( +pub fn ecadd_precompile>( handler: &mut EvmHandler, input: &[u8], _context: &Context, @@ -117,7 +117,7 @@ pub fn ecadd_precompile( ) } -fn ecmul_precompile_without_gas_draining( +fn ecmul_precompile_without_gas_draining>( handler: &mut EvmHandler, input: &[u8], ) -> Result { @@ -158,7 +158,7 @@ fn ecmul_precompile_without_gas_draining( }) } -pub fn ecmul_precompile( +pub fn ecmul_precompile>( handler: &mut EvmHandler, input: &[u8], _context: &Context, @@ -172,7 +172,7 @@ pub fn ecmul_precompile( ) } -fn ecpairing_precompile_without_gas_draining( +fn ecpairing_precompile_without_gas_draining>( handler: &mut EvmHandler, input: &[u8], ) -> Result { @@ -281,7 +281,7 @@ fn ecpairing_precompile_without_gas_draining( }) } -pub fn ecpairing_precompile( +pub fn ecpairing_precompile>( handler: &mut EvmHandler, input: &[u8], _context: &Context, diff --git a/etherlink/kernel_evm/evm_execution/src/storage.rs b/etherlink/kernel_evm/evm_execution/src/storage.rs index 11f5c431bca8dae03774fe5a137e3f4ea9df2f50..020266982d54cb4a6a63b148970ee5a053bb14cf 100644 --- a/etherlink/kernel_evm/evm_execution/src/storage.rs +++ b/etherlink/kernel_evm/evm_execution/src/storage.rs @@ -12,7 +12,7 @@ pub mod tracer { use primitive_types::H256; use tezos_evm_logging::log; use tezos_evm_logging::Level::Debug; - use tezos_evm_runtime::runtime::Runtime; + use tezos_evm_runtime::runtime::{Runtime, TracePath}; use tezos_indexable_storage::{IndexableStorage, IndexableStorageError}; use tezos_smart_rollup_host::path::*; use thiserror::Error; @@ -20,12 +20,10 @@ pub mod tracer { use crate::trace::CallTrace; use crate::trace::StructLog; - const EVM_TRACE: RefPath = RefPath::assert_from(b"/evm/trace"); - - const GAS: RefPath = RefPath::assert_from(b"/gas"); - const FAILED: RefPath = RefPath::assert_from(b"/failed"); - const RETURN_VALUE: RefPath = RefPath::assert_from(b"/return_value"); - const STRUCT_LOGS: RefPath = RefPath::assert_from(b"/struct_logs"); + const GAS: RefPath = RefPath::assert_from(b"/gas"); + const FAILED: RefPath = RefPath::assert_from(b"/failed"); + const RETURN_VALUE: RefPath = RefPath::assert_from(b"/return_value"); + const STRUCT_LOGS: RefPath = RefPath::assert_from(b"/struct_logs"); #[derive(Eq, Error, Debug, PartialEq)] pub enum Error { @@ -39,81 +37,81 @@ pub mod tracer { pub fn trace_tx_path( hash: &Option, - field: &RefPath, - ) -> Result { - let trace_tx_path = match hash { - None => EVM_TRACE.into(), + field: &RefPath, + ) -> Result { + let path: OwnedPath = match hash { + None => Ok(field.into()), Some(hash) => { let hash = hex::encode(hash); let raw_tx_path: Vec = format!("/{}", &hash).into(); let tx_path = OwnedPath::try_from(raw_tx_path)?; - concat(&EVM_TRACE, &tx_path)? + concat(&tx_path, field).map_err(Error::PathError) } - }; - concat(&trace_tx_path, field).map_err(Error::PathError) + }?; + Ok(TracePath(path)) } - pub fn store_trace_gas( + pub fn store_trace_gas>( host: &mut Host, gas: u64, hash: &Option, ) -> Result<(), Error> { let path = trace_tx_path(hash, &GAS)?; - host.store_write_all(&path, gas.to_le_bytes().as_slice())?; + host.store_write_all_trace(&path, gas.to_le_bytes().as_slice())?; Ok(()) } - pub fn store_trace_failed( + pub fn store_trace_failed>( host: &mut Host, is_success: bool, hash: &Option, ) -> Result<(), Error> { let path = trace_tx_path(hash, &FAILED)?; - host.store_write_all(&path, &[u8::from(!is_success)])?; + host.store_write_all_trace(&path, &[u8::from(!is_success)])?; log!(host, Debug, "Store trace info: is_success {}", is_success); Ok(()) } - pub fn store_return_value( + pub fn store_return_value>( host: &mut Host, value: &[u8], hash: &Option, ) -> Result<(), Error> { let path = trace_tx_path(hash, &RETURN_VALUE)?; - host.store_write_all(&path, value)?; + host.store_write_all_trace(&path, value)?; log!(host, Debug, "Store trace info: value {:?}", value); Ok(()) } - pub fn store_struct_log( + pub fn store_struct_log>( host: &mut Host, struct_log: StructLog, hash: &Option, ) -> Result<(), Error> { let logs = rlp::encode(&struct_log); - let path = trace_tx_path(hash, &STRUCT_LOGS)?; + let TracePath(path) = trace_tx_path(hash, &STRUCT_LOGS)?; let struct_logs_storage = IndexableStorage::new_owned_path(path); - struct_logs_storage.push_value(host, &logs)?; + struct_logs_storage.push_value_trace(host, &logs)?; log!(host, Debug, "Store trace info: logs {:?}", logs); Ok(()) } - const CALL_TRACE: RefPath = RefPath::assert_from(b"/call_trace"); + const CALL_TRACE: RefPath = RefPath::assert_from(b"/call_trace"); - pub fn store_call_trace( + pub fn store_call_trace>( host: &mut Host, call_trace: CallTrace, hash: &Option, ) -> Result<(), Error> { let encoded_call_trace = rlp::encode(&call_trace); - let path = trace_tx_path(hash, &CALL_TRACE)?; + let TracePath(path) = trace_tx_path(hash, &CALL_TRACE)?; let call_trace_storage = IndexableStorage::new_owned_path(path); - call_trace_storage.push_value(host, &encoded_call_trace)?; + call_trace_storage.push_value_trace(host, &encoded_call_trace)?; log!(host, Debug, "Store call trace: {:?}", call_trace); Ok(()) @@ -167,7 +165,7 @@ pub mod blocks { /// Get block hash by block number. pub fn get_block_hash( - host: &impl Runtime, + host: &impl Runtime, block_number: U256, ) -> Result { let block_path = to_block_hash_path(block_number)?; @@ -180,10 +178,11 @@ pub mod blocks { } } - fn to_block_hash_path(block_number: U256) -> Result { - let path: Vec = - format!("/evm/world_state/indexes/blocks/{}", block_number).into(); - let owned_path = OwnedPath::try_from(path)?; + fn to_block_hash_path( + block_number: U256, + ) -> Result, EvmBlockStorageError> { + let path: Vec = format!("/indexes/blocks/{}", block_number).into(); + let owned_path = OwnedPath::::try_from(path)?; Ok(owned_path) } diff --git a/etherlink/kernel_evm/evm_execution/src/withdrawal_counter.rs b/etherlink/kernel_evm/evm_execution/src/withdrawal_counter.rs index d40d220b32bb86ba02fa926270697179f2530ca6..f68d52ca6e283b3a7ae017ee1206eebcd9b242c8 100644 --- a/etherlink/kernel_evm/evm_execution/src/withdrawal_counter.rs +++ b/etherlink/kernel_evm/evm_execution/src/withdrawal_counter.rs @@ -19,21 +19,22 @@ use tezos_storage::{read_u256_le_default, write_u256_le}; use crate::account_storage::{AccountStorageError, EthereumAccount}; /// Path where withdrawal counter is stored (relative to account) -pub const WITHDRAWAL_COUNTER_PATH: RefPath = RefPath::assert_from(b"/withdrawal_counter"); +pub const WITHDRAWAL_COUNTER_PATH: RefPath = + RefPath::assert_from(b"/withdrawal_counter"); pub trait WithdrawalCounter { /// Returns current withdrawal ID from the storage (or 0 if it's not initialized) /// and increments & store the new value (will fail in case of overflow). fn withdrawal_counter_get_and_increment( &mut self, - host: &mut impl Runtime, + host: &mut impl Runtime, ) -> Result; } impl WithdrawalCounter for EthereumAccount { fn withdrawal_counter_get_and_increment( &mut self, - host: &mut impl Runtime, + host: &mut impl Runtime, ) -> Result { let path = self.custom_path(&WITHDRAWAL_COUNTER_PATH)?; let old_value = read_u256_le_default(host, &path, U256::zero())?; @@ -49,7 +50,10 @@ impl WithdrawalCounter for EthereumAccount { #[cfg(test)] mod tests { use primitive_types::U256; - use tezos_evm_runtime::runtime::MockKernelHost; + use tezos_evm_runtime::{ + runtime::MockKernelHost, + safe_storage::{SafeStorage, WORLD_STATE_PATH}, + }; use tezos_smart_rollup_host::path::RefPath; use tezos_storage::read_u256_le_default; @@ -60,18 +64,26 @@ mod tests { #[test] fn withdrawal_counter_initializes_and_increments() { let mut mock_host = MockKernelHost::default(); + let mut mock_host = SafeStorage { + host: &mut mock_host, + root: WORLD_STATE_PATH, + }; let mut account = EthereumAccount::from_address(&SYSTEM_ACCOUNT_ADDRESS).unwrap(); - let path = b"/evm/world_state/eth_accounts/0000000000000000000000000000000000000000/withdrawal_counter"; + let path = + b"/eth_accounts/0000000000000000000000000000000000000000/withdrawal_counter"; let id = account .withdrawal_counter_get_and_increment(&mut mock_host) .unwrap(); assert_eq!(U256::zero(), id); - let next_id = - read_u256_le_default(&mock_host, &RefPath::assert_from(path), U256::zero()) - .unwrap(); + let next_id = read_u256_le_default( + &mock_host, + &RefPath::::assert_from(path), + U256::zero(), + ) + .unwrap(); assert_eq!(U256::one(), next_id); let id = account @@ -79,9 +91,12 @@ mod tests { .unwrap(); assert_eq!(U256::one(), id); - let next_id = - read_u256_le_default(&mock_host, &RefPath::assert_from(path), U256::zero()) - .unwrap(); + let next_id = read_u256_le_default( + &mock_host, + &RefPath::::assert_from(path), + U256::zero(), + ) + .unwrap(); assert_eq!(U256::from(2), next_id); } } diff --git a/etherlink/kernel_evm/indexable_storage/src/lib.rs b/etherlink/kernel_evm/indexable_storage/src/lib.rs index 383d8733762318c003013d6132abd08f5e9e6f83..8a83012e4b9615f01e259701d32bfc48baf64d66 100644 --- a/etherlink/kernel_evm/indexable_storage/src/lib.rs +++ b/etherlink/kernel_evm/indexable_storage/src/lib.rs @@ -6,23 +6,23 @@ use rlp::DecoderError; use tezos_evm_logging::log; use tezos_evm_logging::Level::Error; -use tezos_evm_runtime::runtime::Runtime; +use tezos_evm_runtime::runtime::{Runtime, TracePath, TraceRuntime}; use tezos_smart_rollup_host::path::{concat, OwnedPath, PathError, RefPath}; use tezos_smart_rollup_host::runtime::RuntimeError; use tezos_smart_rollup_storage::StorageError; use tezos_storage::{error::Error as GenStorageError, read_u64_le, write_u64_le}; use thiserror::Error; -const LENGTH: RefPath = RefPath::assert_from(b"/length"); +const LENGTH: RefPath = RefPath::assert_from(b"/length"); /// An indexable storage is a push-only mapping between increasing integers to /// bytes. It can serve as a replacement for the combination of the host /// functions `store_get_nth` and `store_list_size` that are unsafe. -pub struct IndexableStorage { +pub struct IndexableStorage { /// An indexable storage is stored at a given path and consists of: /// - `/length`: the number of keys /// - `/` where keys are from `0` to `length - 1` - pub path: OwnedPath, + pub path: OwnedPath, } #[derive(Error, Debug, Eq, PartialEq)] @@ -57,16 +57,16 @@ impl From for IndexableStorageError { } } -impl IndexableStorage { - pub fn new(path: &RefPath<'_>) -> Result { +impl IndexableStorage { + pub fn new(path: &RefPath<'_, IS_ABSOLUTE>) -> Result { Ok(Self { path: path.into() }) } - pub fn new_owned_path(path: OwnedPath) -> Self { + pub fn new_owned_path(path: OwnedPath) -> Self { Self { path } } - fn value_path(&self, index: u64) -> Result { + fn value_path(&self, index: u64) -> Result, PathError> { let index_as_path: Vec = format!("/{}", index).into(); // The key being an integer value, it will always be valid as a path, // `assert_from` cannot fail. @@ -74,9 +74,9 @@ impl IndexableStorage { concat(&self.path, &index_subkey) } - fn store_index( + fn store_index( &self, - host: &mut Host, + host: &mut impl Runtime, index: u64, value_repr: &[u8], ) -> Result<(), IndexableStorageError> { @@ -85,9 +85,9 @@ impl IndexableStorage { .map_err(IndexableStorageError::from) } - fn get_length_and_increment( + fn get_length_and_increment( &self, - host: &mut Host, + host: &mut impl Runtime, ) -> Result { let path = concat(&self.path, &LENGTH)?; let length = read_u64_le(host, &path).unwrap_or(0); @@ -98,9 +98,9 @@ impl IndexableStorage { #[allow(dead_code)] /// `length` returns the number of keys in the storage. If `/length` does /// not exists, the storage is considered as empty and returns '0'. - pub fn length( + pub fn length( &self, - host: &Host, + host: &impl Runtime, ) -> Result { let path = concat(&self.path, &LENGTH)?; match read_u64_le(host, &path) { @@ -126,9 +126,9 @@ impl IndexableStorage { #[allow(dead_code)] /// Same as `get_value`, but doesn't check for bounds. - pub fn unsafe_get_value( + pub fn unsafe_get_value( &self, - host: &Host, + host: &impl Runtime, index: u64, ) -> Result, StorageError> { let key_path = self.value_path(index)?; @@ -138,9 +138,9 @@ impl IndexableStorage { /// Returns the value a the given index. Fails if the index is greater or /// equal to the length. #[cfg(debug_assertions)] - pub fn get_value( + pub fn get_value( &self, - host: &Host, + host: &impl Runtime, index: u64, ) -> Result, IndexableStorageError> { let length = self.length(host)?; @@ -152,9 +152,9 @@ impl IndexableStorage { } /// Push a value at index `length`, and increments the length. - pub fn push_value( + pub fn push_value( &self, - host: &mut Host, + host: &mut impl Runtime, value: &[u8], ) -> Result<(), IndexableStorageError> { let new_index = self.get_length_and_increment(host)?; @@ -162,6 +162,28 @@ impl IndexableStorage { } } +impl IndexableStorage { + pub fn store_index_trace( + &self, + host: &mut impl TraceRuntime, + index: u64, + value_repr: &[u8], + ) -> Result<(), IndexableStorageError> { + let key_path = self.value_path(index)?; + host.store_write_all_trace(&TracePath(key_path), value_repr) + .map_err(IndexableStorageError::from) + } + + pub fn push_value_trace( + &self, + host: &mut impl Runtime, + value: &[u8], + ) -> Result<(), IndexableStorageError> { + let new_index = self.get_length_and_increment(host)?; + self.store_index_trace(host, new_index, value) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/etherlink/kernel_evm/kernel/src/apply.rs b/etherlink/kernel_evm/kernel/src/apply.rs index 19e346888a65016e7c76524e1569ff701e91d0e0..29dcc93876b686cddf0a3d93c20f07937919548d 100644 --- a/etherlink/kernel_evm/kernel/src/apply.rs +++ b/etherlink/kernel_evm/kernel/src/apply.rs @@ -175,7 +175,7 @@ fn make_object_info( }) } -fn account( +fn account>( host: &mut Host, caller: H160, evm_account_storage: &mut EthereumAccountStorage, @@ -200,7 +200,7 @@ pub enum Validity { // TODO: https://gitlab.com/tezos/tezos/-/issues/6812 // arguably, effective_gas_price should be set on EthereumTransactionCommon // directly - initialised when constructed. -fn is_valid_ethereum_transaction_common( +fn is_valid_ethereum_transaction_common>( host: &mut Host, evm_account_storage: &mut EthereumAccountStorage, transaction: &EthereumTransactionCommon, @@ -288,7 +288,11 @@ pub struct TransactionResult { /// Technically incorrect: it is possible to do a call without sending any data, /// however it's done for benchmarking only, and benchmarking doesn't include /// such a scenario -fn log_transaction_type(host: &Host, to: Option, data: &[u8]) { +fn log_transaction_type>( + host: &Host, + to: Option, + data: &[u8], +) { if to.is_none() { log!(host, Benchmarking, "Transaction type: CREATE"); } else if data.is_empty() { @@ -299,7 +303,7 @@ fn log_transaction_type(host: &Host, to: Option, data: &[u8 } #[allow(clippy::too_many_arguments)] -fn apply_ethereum_transaction_common( +fn apply_ethereum_transaction_common>( host: &mut Host, block_constants: &BlockConstants, precompiles: &PrecompileBTreeMap, @@ -388,7 +392,7 @@ fn apply_ethereum_transaction_common( } } -fn trace_deposit( +fn trace_deposit>( host: &mut Host, amount: U256, receiver: Option, @@ -420,7 +424,7 @@ fn trace_deposit( } } -fn apply_deposit( +fn apply_deposit>( host: &mut Host, evm_account_storage: &mut EthereumAccountStorage, deposit: &Deposit, @@ -448,7 +452,7 @@ fn apply_deposit( } #[allow(clippy::too_many_arguments)] -fn apply_fa_deposit( +fn apply_fa_deposit>( host: &mut Host, evm_account_storage: &mut EthereumAccountStorage, fa_deposit: &FaDeposit, @@ -498,8 +502,11 @@ fn apply_fa_deposit( })) } -pub const WITHDRAWAL_OUTBOX_QUEUE: RefPath = - RefPath::assert_from(b"/evm/world_state/__outbox_queue"); +pub const WITHDRAWAL_OUTBOX_QUEUE: RefPath = + RefPath::assert_from(b"/__outbox_queue"); + +pub const WITHDRAWAL_OUTBOX_QUEUE_META: RefPath = + RefPath::assert_from(b"/__outbox_queue/meta"); pub struct ExecutionInfo { pub receipt_info: TransactionReceiptInfo, @@ -523,9 +530,9 @@ impl From> for ExecutionResult { } #[allow(clippy::too_many_arguments)] -pub fn handle_transaction_result( +pub fn handle_transaction_result>( host: &mut Host, - outbox_queue: &OutboxQueue<'_, impl Path>, + outbox_queue: &OutboxQueue<'_, false, impl Path>, block_constants: &BlockConstants, transaction: &Transaction, index: u32, @@ -583,9 +590,9 @@ pub fn handle_transaction_result( } #[allow(clippy::too_many_arguments)] -pub fn apply_transaction( +pub fn apply_transaction>( host: &mut Host, - outbox_queue: &OutboxQueue<'_, impl Path>, + outbox_queue: &OutboxQueue<'_, false, impl Path>, block_constants: &BlockConstants, precompiles: &PrecompileBTreeMap, transaction: &Transaction, @@ -690,7 +697,10 @@ mod tests { transaction::TransactionType, tx_common::EthereumTransactionCommon, }; - use tezos_evm_runtime::runtime::MockKernelHost; + use tezos_evm_runtime::{ + runtime::{MockKernelHost, Runtime}, + safe_storage::{SafeStorage, WORLD_STATE_PATH}, + }; use tezos_smart_rollup_encoding::timestamp::Timestamp; use super::is_valid_ethereum_transaction_common; @@ -718,7 +728,7 @@ mod tests { } fn set_balance( - host: &mut MockKernelHost, + host: &mut impl Runtime, evm_account_storage: &mut EthereumAccountStorage, address: &H160, balance: U256, @@ -778,6 +788,10 @@ mod tests { #[test] fn test_tx_is_valid() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = evm_execution::account_storage::init_account_storage().unwrap(); let block_constants = mock_block_constants(); @@ -810,6 +824,10 @@ mod tests { #[test] fn test_tx_is_invalid_cannot_prepay() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = evm_execution::account_storage::init_account_storage().unwrap(); let block_constants = mock_block_constants(); @@ -844,6 +862,10 @@ mod tests { #[test] fn test_tx_is_invalid_signature() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = evm_execution::account_storage::init_account_storage().unwrap(); let block_constants = mock_block_constants(); @@ -878,6 +900,10 @@ mod tests { #[test] fn test_tx_is_invalid_wrong_nonce() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = evm_execution::account_storage::init_account_storage().unwrap(); let block_constants = mock_block_constants(); @@ -914,6 +940,10 @@ mod tests { #[test] fn test_tx_is_invalid_wrong_chain_id() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = evm_execution::account_storage::init_account_storage().unwrap(); let block_constants = mock_block_constants(); @@ -948,6 +978,10 @@ mod tests { #[test] fn test_tx_is_invalid_max_fee_less_than_base_fee() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = evm_execution::account_storage::init_account_storage().unwrap(); let block_constants = mock_block_constants(); @@ -982,6 +1016,10 @@ mod tests { #[test] fn test_tx_invalid_not_enough_gas_for_fee() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = evm_execution::account_storage::init_account_storage().unwrap(); let block_constants = mock_block_constants(); diff --git a/etherlink/kernel_evm/kernel/src/block.rs b/etherlink/kernel_evm/kernel/src/block.rs index 49d59578decc146fbe6b5ffbe68a8c53be2d0ced..9eca2932c0ebde9d8f30aa74a241e6060d959d6b 100644 --- a/etherlink/kernel_evm/kernel/src/block.rs +++ b/etherlink/kernel_evm/kernel/src/block.rs @@ -7,9 +7,13 @@ use crate::apply::{ apply_transaction, ExecutionInfo, ExecutionResult, Validity, WITHDRAWAL_OUTBOX_QUEUE, + WITHDRAWAL_OUTBOX_QUEUE_META, }; use crate::block_storage; -use crate::blueprint_storage::{drop_blueprint, read_next_blueprint}; +use crate::blueprint_storage::{ + drop_blueprint, read_next_blueprint, store_last_block_timestamp, + store_next_blueprint_number, +}; use crate::configuration::ConfigurationMode; use crate::configuration::Limits; use crate::delayed_inbox::DelayedInbox; @@ -31,7 +35,10 @@ use primitive_types::{H160, H256, U256}; use tezos_ethereum::transaction::TransactionHash; use tezos_evm_logging::{log, Level::*, Verbosity}; use tezos_evm_runtime::runtime::Runtime; -use tezos_evm_runtime::safe_storage::SafeStorage; +use tezos_evm_runtime::{ + relative_runtime::RelativeRuntime, + safe_storage::{SafeStorage, WORLD_STATE_PATH}, +}; use tezos_smart_rollup::outbox::OutboxQueue; use tezos_smart_rollup_host::path::{Path, RefPath}; use tick_model::estimate_remaining_ticks_for_transaction_execution; @@ -73,7 +80,7 @@ pub enum ComputationResult { Finished, } -fn on_invalid_transaction( +fn on_invalid_transaction>( host: &mut Host, transaction: &Transaction, block_in_progress: &mut BlockInProgress, @@ -93,9 +100,9 @@ fn on_invalid_transaction( } #[allow(clippy::too_many_arguments)] -fn compute( +fn compute>( host: &mut Host, - outbox_queue: &OutboxQueue<'_, impl Path>, + outbox_queue: &OutboxQueue<'_, false, impl Path>, block_in_progress: &mut BlockInProgress, block_constants: &BlockConstants, precompiles: &PrecompileBTreeMap, @@ -230,7 +237,7 @@ enum BlueprintParsing { } #[cfg_attr(feature = "benchmark", inline(never))] -fn next_bip_from_blueprints( +fn next_bip_from_blueprints>( host: &mut Host, current_block_number: U256, current_block_parent_hash: H256, @@ -251,8 +258,12 @@ fn next_bip_from_blueprints( return Ok(BlueprintParsing::None); } } - let gas_price = crate::gas_price::base_fee_per_gas( + let host = RelativeRuntime { + root: &WORLD_STATE_PATH, host, + }; + let gas_price = crate::gas_price::base_fee_per_gas( + &host, blueprint.timestamp, minimum_base_fee_per_gas, ); @@ -277,9 +288,9 @@ fn next_bip_from_blueprints( } #[allow(clippy::too_many_arguments)] -fn compute_bip( +fn compute_bip>( host: &mut Host, - outbox_queue: &OutboxQueue<'_, impl Path>, + outbox_queue: &OutboxQueue<'_, false, impl Path>, mut block_in_progress: BlockInProgress, current_block_number: &mut U256, current_block_parent_hash: &mut H256, @@ -352,7 +363,7 @@ fn compute_bip( Ok(result) } -fn revert_block( +fn revert_block>( safe_host: &mut SafeStorage<&mut Host>, block_in_progress: bool, number: U256, @@ -372,7 +383,7 @@ fn revert_block( } fn clean_delayed_transactions( - host: &mut impl Runtime, + host: &mut impl Runtime, delayed_inbox: &mut DelayedInbox, delayed_txs: Vec, ) -> anyhow::Result<()> { @@ -382,67 +393,80 @@ fn clean_delayed_transactions( Ok(()) } -fn promote_block( +fn promote_block>( safe_host: &mut SafeStorage<&mut Host>, - outbox_queue: &OutboxQueue<'_, impl Path>, + outbox_queue: &OutboxQueue<'_, false, impl Path>, block_in_progress: bool, number: U256, config: &mut Configuration, delayed_txs: Vec, ) -> anyhow::Result<()> { + let current_timestamp = block_storage::read_current_timestamp(safe_host)?; if block_in_progress { storage::delete_block_in_progress(safe_host)?; } safe_host.promote()?; safe_host.promote_trace()?; drop_blueprint(safe_host.host, number)?; + store_next_blueprint_number(safe_host.host, number.saturating_add(U256::one()))?; + store_last_block_timestamp(safe_host.host, ¤t_timestamp)?; - let number = block_storage::read_current_number(safe_host.host)?; - let hash = block_storage::read_current_hash(safe_host.host)?; + let host: &mut Host = safe_host.host; + let mut relative_runtime = RelativeRuntime { + root: &WORLD_STATE_PATH, + host, + }; + let number = block_storage::read_current_number(&relative_runtime)?; + let hash = block_storage::read_current_hash(&relative_runtime)?; - Event::BlueprintApplied { number, hash }.store(safe_host.host)?; + Event::BlueprintApplied { number, hash }.store(relative_runtime.host)?; - let written = outbox_queue.flush_queue(safe_host.host); + let written = outbox_queue.flush_queue(&mut relative_runtime); // Log to Info only if we flushed messages. let level = if written > 0 { Info } else { Debug }; log!( - safe_host, + relative_runtime, level, "Flushed outbox queue messages ({} flushed)", written ); if let ConfigurationMode::Sequencer { delayed_inbox, .. } = &mut config.mode { - clean_delayed_transactions(safe_host.host, delayed_inbox, delayed_txs)?; + clean_delayed_transactions(relative_runtime.host, delayed_inbox, delayed_txs)?; } Ok(()) } -const AT_MOST_ONE_BLOCK: RefPath = RefPath::assert_from(b"/__at_most_one_block"); +const AT_MOST_ONE_BLOCK: RefPath = RefPath::assert_from(b"/__at_most_one_block"); -pub fn produce( +pub fn produce>( host: &mut Host, chain_id: U256, config: &mut Configuration, sequencer_pool_address: Option, tracer_input: Option, ) -> Result { - let minimum_base_fee_per_gas = crate::retrieve_minimum_base_fee_per_gas(host)?; - let da_fee_per_byte = crate::retrieve_da_fee(host)?; - let kernel_upgrade = upgrade::read_kernel_upgrade(host)?; // If there's a pool address, the coinbase in block constants and miner // in blocks is set to the pool address. let coinbase = sequencer_pool_address.unwrap_or_default(); + let mut relative_host = RelativeRuntime { + root: &WORLD_STATE_PATH, + host, + }; + let minimum_base_fee_per_gas = + crate::retrieve_minimum_base_fee_per_gas(&mut relative_host)?; + let da_fee_per_byte = crate::retrieve_da_fee(&mut relative_host)?; + let ( mut current_block_number, mut current_block_parent_hash, mut previous_receipts_root, mut previous_transactions_root, - ) = match block_storage::read_current(host) { + ) = match block_storage::read_current(&mut relative_host) { Ok(block) => ( block.number + 1, block.hash, @@ -456,10 +480,17 @@ pub fn produce( let mut tick_counter = TickCounter::new(0u64); let mut first_block_of_reboot = true; - let at_most_one_block = host.store_has(&AT_MOST_ONE_BLOCK)?.is_some(); + let at_most_one_block = relative_host.host.store_has(&AT_MOST_ONE_BLOCK)?.is_some(); - let mut safe_host = SafeStorage { host }; - let outbox_queue = OutboxQueue::new(&WITHDRAWAL_OUTBOX_QUEUE, u32::MAX)?; + let mut safe_host = SafeStorage { + root: WORLD_STATE_PATH, + host: relative_host.host, + }; + let outbox_queue = OutboxQueue::new( + &WITHDRAWAL_OUTBOX_QUEUE, + &WITHDRAWAL_OUTBOX_QUEUE_META, + u32::MAX, + )?; let precompiles = precompiles::precompile_set::>(config.enable_fa_bridge); @@ -624,6 +655,7 @@ mod tests { use super::*; use crate::block_storage; use crate::blueprint::Blueprint; + use crate::blueprint_storage::read_last_block_timestamp; use crate::blueprint_storage::store_inbox_blueprint; use crate::blueprint_storage::store_inbox_blueprint_by_number; use crate::fees::DA_FEE_PER_BYTE; @@ -633,7 +665,6 @@ mod tests { use crate::inbox::TransactionContent::Ethereum; use crate::inbox::TransactionContent::EthereumDelayed; use crate::storage::read_block_in_progress; - use crate::storage::read_last_info_per_level_timestamp; use crate::storage::{read_transaction_receipt, read_transaction_receipt_status}; use crate::{retrieve_block_fees, retrieve_chain_id}; use evm_execution::account_storage::{ @@ -648,6 +679,7 @@ mod tests { use tezos_ethereum::tx_common::EthereumTransactionCommon; use tezos_evm_runtime::runtime::MockKernelHost; use tezos_evm_runtime::runtime::Runtime; + use tezos_evm_runtime::safe_storage::{TMP_WORLD_STATE_PATH, WORLD_STATE_PATH}; use tezos_smart_rollup_encoding::timestamp::Timestamp; use tezos_smart_rollup_host::runtime::Runtime as SdkRuntime; @@ -663,7 +695,7 @@ mod tests { Some(H160::from_slice(data)) } - fn set_balance( + fn set_balance>( host: &mut Host, evm_account_storage: &mut EthereumAccountStorage, address: &H160, @@ -684,7 +716,7 @@ mod tests { } } - fn get_balance( + fn get_balance>( host: &mut Host, evm_account_storage: &mut EthereumAccountStorage, address: &H160, @@ -819,14 +851,17 @@ mod tests { ) } - fn store_blueprints(host: &mut Host, blueprints: Vec) { + fn store_blueprints>( + host: &mut Host, + blueprints: Vec, + ) { for (i, blueprint) in blueprints.into_iter().enumerate() { store_inbox_blueprint_by_number(host, blueprint, U256::from(i)) .expect("Should have stored blueprint"); } } - fn store_block_fees( + fn store_block_fees>( host: &mut Host, block_fees: &BlockFees, ) -> anyhow::Result<()> { @@ -838,8 +873,8 @@ mod tests { Ok(()) } - fn produce_block_with_several_valid_txs( - host: &mut Host, + fn produce_block_with_several_valid_txs>( + host: &mut SafeStorage<&mut Host>, evm_account_storage: &mut EthereumAccountStorage, ) { let tx_hash_0 = [0; TRANSACTION_HASH_SIZE]; @@ -856,9 +891,17 @@ mod tests { }, ]; - store_blueprints(host, vec![blueprint(transactions)]); + store_blueprints(host.host, vec![blueprint(transactions)]); let sender = dummy_eth_caller(); + assert!(host + .host + .store_has(&TMP_WORLD_STATE_PATH) + .unwrap() + .is_none()); + if host.host.store_has(&WORLD_STATE_PATH).unwrap().is_some() { + host.start().unwrap() + }; set_balance( host, evm_account_storage, @@ -866,9 +909,10 @@ mod tests { U256::from(10000000000000000000u64), ); store_block_fees(host, &dummy_block_fees()).unwrap(); + host.promote().unwrap(); produce( - host, + host.host, DUMMY_CHAIN_ID, &mut Configuration::default(), None, @@ -877,7 +921,7 @@ mod tests { .expect("The block production failed."); } - fn assert_current_block_reading_validity(host: &mut Host) { + fn assert_current_block_reading_validity>(host: &mut Host) { match block_storage::read_current(host) { Ok(_) => (), Err(e) => { @@ -890,11 +934,16 @@ mod tests { // Test if the invalid transactions are producing receipts fn test_invalid_transactions_receipt_status() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; crate::storage::store_minimum_base_fee_per_gas( &mut host, DUMMY_BASE_FEE_PER_GAS.into(), ) .unwrap(); + host.promote().unwrap(); let tx_hash = [0; TRANSACTION_HASH_SIZE]; @@ -904,10 +953,11 @@ mod tests { }; let transactions: Vec = vec![invalid_tx]; - store_blueprints(&mut host, vec![blueprint(transactions)]); + store_blueprints(host.host, vec![blueprint(transactions)]); let mut evm_account_storage = init_account_storage().unwrap(); let sender = dummy_eth_caller(); + host.start().unwrap(); set_balance( &mut host, &mut evm_account_storage, @@ -915,8 +965,9 @@ mod tests { U256::from(30000u64), ); store_block_fees(&mut host, &dummy_block_fees()).unwrap(); + host.promote().unwrap(); produce( - &mut host, + host.host, DUMMY_CHAIN_ID, &mut Configuration::default(), None, @@ -924,21 +975,28 @@ mod tests { ) .expect("The block production failed."); + host.start().unwrap(); assert!( read_transaction_receipt_status(&mut host, &tx_hash).is_err(), "Invalid transaction should not have a receipt" ); + host.revert().unwrap(); } #[test] // Test if a valid transaction is producing a receipt with a success status fn test_valid_transactions_receipt_status() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; crate::storage::store_minimum_base_fee_per_gas( &mut host, DUMMY_BASE_FEE_PER_GAS.into(), ) .unwrap(); + host.promote().unwrap(); let tx_hash = [0; TRANSACTION_HASH_SIZE]; @@ -948,10 +1006,11 @@ mod tests { }; let transactions: Vec = vec![valid_tx]; - store_blueprints(&mut host, vec![blueprint(transactions)]); + store_blueprints(host.host, vec![blueprint(transactions)]); let sender = dummy_eth_caller(); let mut evm_account_storage = init_account_storage().unwrap(); + host.start().unwrap(); set_balance( &mut host, &mut evm_account_storage, @@ -959,9 +1018,10 @@ mod tests { U256::from(1_000_000_000_000_000_000u64), ); store_block_fees(&mut host, &dummy_block_fees()).unwrap(); + host.promote().unwrap(); produce( - &mut host, + host.host, DUMMY_CHAIN_ID, &mut Configuration::default(), None, @@ -969,8 +1029,10 @@ mod tests { ) .expect("The block production failed."); + host.start().unwrap(); let status = read_transaction_receipt_status(&mut host, &tx_hash) .expect("Should have found receipt"); + host.revert().unwrap(); assert_eq!(TransactionStatus::Success, status); } @@ -978,6 +1040,16 @@ mod tests { // Test if a valid transaction is producing a receipt with a contract address fn test_valid_transactions_receipt_contract_address() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; + crate::storage::store_minimum_base_fee_per_gas( + &mut host, + DUMMY_BASE_FEE_PER_GAS.into(), + ) + .unwrap(); + host.promote().unwrap(); let tx_hash = [0; TRANSACTION_HASH_SIZE]; let tx = dummy_eth_transaction_deploy(); @@ -991,10 +1063,11 @@ mod tests { }; let transactions: Vec = vec![valid_tx]; - store_blueprints(&mut host, vec![blueprint(transactions)]); + store_blueprints(host.host, vec![blueprint(transactions)]); let sender = H160::from_str("af1276cbb260bb13deddb4209ae99ae6e497f446").unwrap(); let mut evm_account_storage = init_account_storage().unwrap(); + host.start().unwrap(); set_balance( &mut host, &mut evm_account_storage, @@ -1002,17 +1075,20 @@ mod tests { U256::from(5000000000000000u64), ); store_block_fees(&mut host, &dummy_block_fees()).unwrap(); + host.promote().unwrap(); produce( - &mut host, + host.host, DUMMY_CHAIN_ID, &mut Configuration::default(), None, None, ) .expect("The block production failed."); + host.start().unwrap(); let receipt = read_transaction_receipt(&mut host, &tx_hash) .expect("should have found receipt"); + host.revert().unwrap(); assert_eq!(TransactionStatus::Success, receipt.status); assert_eq!( H160::from_str("af1276cbb260bb13deddb4209ae99ae6e497f446").unwrap(), @@ -1028,20 +1104,27 @@ mod tests { // Test if several valid transactions can be performed fn test_several_valid_transactions() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; crate::storage::store_minimum_base_fee_per_gas( &mut host, DUMMY_BASE_FEE_PER_GAS.into(), ) .unwrap(); + host.promote().unwrap(); let mut evm_account_storage = init_account_storage().unwrap(); produce_block_with_several_valid_txs(&mut host, &mut evm_account_storage); + host.start().unwrap(); let dest_address = H160::from_str("423163e58aabec5daa3dd1130b759d24bef0f6ea").unwrap(); let dest_balance = get_balance(&mut host, &mut evm_account_storage, &dest_address); + host.revert().unwrap(); assert_eq!(dest_balance, U256::from(1000000000u64)) } @@ -1050,11 +1133,16 @@ mod tests { // Test if several valid proposals can produce valid blocks fn test_several_valid_proposals() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; crate::storage::store_minimum_base_fee_per_gas( &mut host, DUMMY_BASE_FEE_PER_GAS.into(), ) .unwrap(); + host.promote().unwrap(); let tx_hash_0 = [0; TRANSACTION_HASH_SIZE]; let tx_hash_1 = [1; TRANSACTION_HASH_SIZE]; @@ -1070,12 +1158,13 @@ mod tests { }]; store_blueprints( - &mut host, + host.host, vec![blueprint(transaction_0), blueprint(transaction_1)], ); let sender = dummy_eth_caller(); let mut evm_account_storage = init_account_storage().unwrap(); + host.start().unwrap(); set_balance( &mut host, &mut evm_account_storage, @@ -1083,10 +1172,11 @@ mod tests { U256::from(10000000000000000000u64), ); store_block_fees(&mut host, &dummy_block_fees()).unwrap(); + host.promote().unwrap(); // Produce block for blueprint containing transaction_0 produce( - &mut host, + host.host, DUMMY_CHAIN_ID, &mut Configuration::default(), None, @@ -1095,7 +1185,7 @@ mod tests { .expect("The block production failed."); // Produce block for blueprint containing transaction_1 produce( - &mut host, + host.host, DUMMY_CHAIN_ID, &mut Configuration::default(), None, @@ -1103,10 +1193,12 @@ mod tests { ) .expect("The block production failed."); + host.start().unwrap(); let dest_address = H160::from_str("423163e58aabec5daa3dd1130b759d24bef0f6ea").unwrap(); let dest_balance = get_balance(&mut host, &mut evm_account_storage, &dest_address); + host.revert().unwrap(); assert_eq!(dest_balance, U256::from(1000000000u64)) } @@ -1115,8 +1207,14 @@ mod tests { // Test transfers gas consumption consistency fn test_cumulative_transfers_gas_consumption() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let base_gas = U256::from(21000); + crate::storage::store_minimum_base_fee_per_gas(&mut host, base_gas).unwrap(); + host.promote().unwrap(); let dummy_block_fees = dummy_block_fees(); let gas_for_fees = crate::fees::gas_for_fees( dummy_block_fees.da_fee_per_byte(), @@ -1140,10 +1238,11 @@ mod tests { }, ]; - store_blueprints(&mut host, vec![blueprint(transactions)]); + store_blueprints(host.host, vec![blueprint(transactions)]); let sender = dummy_eth_caller(); let mut evm_account_storage = init_account_storage().unwrap(); + host.start().unwrap(); set_balance( &mut host, &mut evm_account_storage, @@ -1151,19 +1250,22 @@ mod tests { U256::from(10000000000000000000u64), ); store_block_fees(&mut host, &dummy_block_fees).unwrap(); + host.promote().unwrap(); produce( - &mut host, + host.host, DUMMY_CHAIN_ID, &mut Configuration::default(), None, None, ) .expect("The block production failed."); + host.start().unwrap(); let receipt0 = read_transaction_receipt(&mut host, &tx_hash_0) .expect("should have found receipt"); let receipt1 = read_transaction_receipt(&mut host, &tx_hash_1) .expect("should have found receipt"); + host.revert().unwrap(); assert_eq!(receipt0.cumulative_gas_used, base_gas + gas_for_fees); assert_eq!( @@ -1177,18 +1279,34 @@ mod tests { // a block production fn test_read_storage_current_block_after_block_production_with_filled_queue() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = init_account_storage().unwrap(); produce_block_with_several_valid_txs(&mut host, &mut evm_account_storage); + host.start().unwrap(); assert_current_block_reading_validity(&mut host); + host.revert().unwrap(); } #[test] // Test that the same transaction can not be replayed twice fn test_replay_attack() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; + crate::storage::store_minimum_base_fee_per_gas( + &mut host, + DUMMY_BASE_FEE_PER_GAS.into(), + ) + .unwrap(); + host.promote().unwrap(); let tx = Transaction { tx_hash: [0; TRANSACTION_HASH_SIZE], @@ -1197,10 +1315,11 @@ mod tests { let transactions = vec![tx.clone(), tx]; store_blueprints( - &mut host, + host.host, vec![blueprint(transactions.clone()), blueprint(transactions)], ); + host.start().unwrap(); let sender = dummy_eth_caller(); let initial_sender_balance = U256::from(10000000000000000000u64); let mut evm_account_storage = init_account_storage().unwrap(); @@ -1211,9 +1330,10 @@ mod tests { initial_sender_balance, ); store_block_fees(&mut host, &dummy_block_fees()).unwrap(); + host.promote().unwrap(); produce( - &mut host, + host.host, DUMMY_CHAIN_ID, &mut Configuration::default(), None, @@ -1221,11 +1341,13 @@ mod tests { ) .expect("The block production failed."); + host.start().unwrap(); let dest_address = H160::from_str("423163e58aabec5daa3dd1130b759d24bef0f6ea").unwrap(); let sender_balance = get_balance(&mut host, &mut evm_account_storage, &sender); let dest_balance = get_balance(&mut host, &mut evm_account_storage, &dest_address); + host.revert().unwrap(); let expected_dest_balance = U256::from(500000000u64); let expected_gas = 21000; @@ -1241,17 +1363,23 @@ mod tests { #[test] fn test_blocks_are_indexed() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; crate::storage::store_minimum_base_fee_per_gas( &mut host, DUMMY_BASE_FEE_PER_GAS.into(), ) .unwrap(); + host.promote().unwrap(); let blocks_index = block_storage::internal_for_tests::init_blocks_index().unwrap(); - store_blueprints(&mut host, vec![blueprint(vec![])]); + store_blueprints(host.host, vec![blueprint(vec![])]); + host.start().unwrap(); let number_of_blocks_indexed = blocks_index.length(&host).unwrap(); let sender = dummy_eth_caller(); let mut evm_account_storage = init_account_storage().unwrap(); @@ -1262,8 +1390,9 @@ mod tests { U256::from(10000000000000000000u64), ); store_block_fees(&mut host, &dummy_block_fees()).unwrap(); + host.promote().unwrap(); produce( - &mut host, + host.host, DUMMY_CHAIN_ID, &mut Configuration::default(), None, @@ -1271,6 +1400,7 @@ mod tests { ) .expect("The block production failed."); + host.start().unwrap(); let new_number_of_blocks_indexed = blocks_index.length(&host).unwrap(); let current_block_hash = block_storage::read_current(&mut host) @@ -1285,13 +1415,16 @@ mod tests { Ok(current_block_hash), blocks_index.get_value(&host, new_number_of_blocks_indexed - 1) ); + host.revert().unwrap(); } - fn first_block(host: &mut MockHost) -> BlockConstants { + fn first_block>( + host: &mut SafeStorage<&mut MockHost>, + ) -> BlockConstants { let timestamp = - read_last_info_per_level_timestamp(host).unwrap_or(Timestamp::from(0)); + read_last_block_timestamp(host.host).unwrap_or(Timestamp::from(0)); let timestamp = U256::from(timestamp.as_u64()); - let chain_id = retrieve_chain_id(host); + let chain_id = retrieve_chain_id(host.host); let block_fees = retrieve_block_fees(host); assert!(chain_id.is_ok(), "chain_id should be defined"); assert!(block_fees.is_ok(), "block fees should be defined"); @@ -1308,6 +1441,10 @@ mod tests { fn test_stop_computation() { // init host let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let block_constants = first_block(&mut host); let precompiles = precompiles::precompile_set(false); @@ -1342,7 +1479,12 @@ mod tests { // act let result = compute( &mut host, - &OutboxQueue::new(&WITHDRAWAL_OUTBOX_QUEUE, u32::MAX).unwrap(), + &OutboxQueue::new( + &WITHDRAWAL_OUTBOX_QUEUE, + &WITHDRAWAL_OUTBOX_QUEUE_META, + u32::MAX, + ) + .unwrap(), &mut block_in_progress, &block_constants, &precompiles, @@ -1390,6 +1532,10 @@ mod tests { #[test] fn invalid_transaction_should_bump_nonce() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = init_account_storage().unwrap(); @@ -1416,12 +1562,12 @@ mod tests { tx_hash, content: Ethereum(tx), }; - store_blueprints(&mut host, vec![blueprint(vec![transaction])]); + store_blueprints(host.host, vec![blueprint(vec![transaction])]); // Apply the transaction store_block_fees(&mut host, &dummy_block_fees()).unwrap(); produce( - &mut host, + host.host, DUMMY_CHAIN_ID, &mut Configuration::default(), None, @@ -1453,7 +1599,7 @@ mod tests { blueprint(transactions) } - fn check_current_block_number(host: &mut Host, nb: usize) { + fn check_current_block_number>(host: &mut Host, nb: usize) { let current_nb = block_storage::read_current_number(host) .expect("Should have manage to check block number"); assert_eq!(current_nb, U256::from(nb), "Incorrect block number"); @@ -1462,46 +1608,57 @@ mod tests { #[test] fn test_first_blocks() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; + store_block_fees(&mut host, &dummy_block_fees()).unwrap(); + host.promote().unwrap(); // first block should be 0 let blueprint = almost_empty_blueprint(); - store_inbox_blueprint(&mut host, blueprint).expect("Should store a blueprint"); - store_block_fees(&mut host, &dummy_block_fees()).unwrap(); + store_inbox_blueprint(host.host, blueprint).expect("Should store a blueprint"); produce( - &mut host, + host.host, DUMMY_CHAIN_ID, &mut Configuration::default(), None, None, ) .expect("Empty block should have been produced"); + host.start().unwrap(); check_current_block_number(&mut host, 0); + host.revert().unwrap(); // second block let blueprint = almost_empty_blueprint(); - store_inbox_blueprint(&mut host, blueprint).expect("Should store a blueprint"); + store_inbox_blueprint(host.host, blueprint).expect("Should store a blueprint"); produce( - &mut host, + host.host, DUMMY_CHAIN_ID, &mut Configuration::default(), None, None, ) .expect("Empty block should have been produced"); + host.start().unwrap(); check_current_block_number(&mut host, 1); + host.revert().unwrap(); // third block let blueprint = almost_empty_blueprint(); - store_inbox_blueprint(&mut host, blueprint).expect("Should store a blueprint"); + store_inbox_blueprint(host.host, blueprint).expect("Should store a blueprint"); produce( - &mut host, + host.host, DUMMY_CHAIN_ID, &mut Configuration::default(), None, None, ) .expect("Empty block should have been produced"); + host.start().unwrap(); check_current_block_number(&mut host, 2); + host.revert().unwrap(); } fn hash_from_nonce(nonce: u64) -> TransactionHash { @@ -1573,6 +1730,10 @@ mod tests { fn test_reboot_many_tx_one_proposal() { // init host let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; crate::storage::store_minimum_base_fee_per_gas( &mut host, DUMMY_BASE_FEE_PER_GAS.into(), @@ -1595,6 +1756,8 @@ mod tests { &sender, sender_initial_balance, ); + store_block_fees(&mut host, &dummy_block_fees()).unwrap(); + host.promote().unwrap(); // These transactions are generated with the loop.sol contract, which are: // - create the contract @@ -1620,9 +1783,9 @@ mod tests { wrap_transaction(2, loop_4600_tx), ]; - store_blueprints(&mut host, vec![blueprint(proposals)]); + store_blueprints(host.host, vec![blueprint(proposals)]); - host.reboot_left().expect("should be some reboot left"); + host.host.reboot_left().expect("should be some reboot left"); // Set the tick limit to 11bn ticks - 2bn, which is the old limit minus the safety margin. let limits = Limits { @@ -1635,16 +1798,17 @@ mod tests { ..Configuration::default() }; - store_block_fees(&mut host, &dummy_block_fees()).unwrap(); let computation_result = - produce(&mut host, DUMMY_CHAIN_ID, &mut configuration, None, None) + produce(host.host, DUMMY_CHAIN_ID, &mut configuration, None, None) .expect("Should have produced"); + host.start().unwrap(); // test no new block assert!( block_storage::read_current_number(&host).is_err(), "Should not have found current block number" ); + host.revert().unwrap(); // test reboot is set matches!(computation_result, ComputationResult::RebootNeeded); @@ -1654,6 +1818,10 @@ mod tests { fn test_reboot_many_tx_many_proposal() { // init host let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; crate::storage::store_minimum_base_fee_per_gas( &mut host, @@ -1676,6 +1844,8 @@ mod tests { &sender, sender_initial_balance, ); + store_block_fees(&mut host, &dummy_block_fees()).unwrap(); + host.promote().unwrap(); // These transactions are generated with the loop.sol contract, which are: // - create the contract @@ -1703,7 +1873,7 @@ mod tests { ]), ]; - store_blueprints(&mut host, proposals); + store_blueprints(host.host, proposals); // Set the tick limit to 11bn ticks - 2bn, which is the old limit minus the safety margin. let limits = Limits { @@ -1715,15 +1885,14 @@ mod tests { limits, ..Configuration::default() }; - store_block_fees(&mut host, &dummy_block_fees()).unwrap(); let computation_result = - produce(&mut host, DUMMY_CHAIN_ID, &mut configuration, None, None) + produce(host.host, DUMMY_CHAIN_ID, &mut configuration, None, None) .expect("Should have produced"); // test reboot is set matches!(computation_result, ComputationResult::RebootNeeded); let computation_result = - produce(&mut host, DUMMY_CHAIN_ID, &mut configuration, None, None) + produce(host.host, DUMMY_CHAIN_ID, &mut configuration, None, None) .expect("Should have produced"); // test no new block @@ -1738,8 +1907,7 @@ mod tests { matches!(computation_result, ComputationResult::RebootNeeded); // The block is in progress, therefore it is in the safe storage. - let safe_host = SafeStorage { host: &mut host }; - let bip = read_block_in_progress(&safe_host) + let bip = read_block_in_progress(&host) .expect("Should be able to read the block in progress") .expect("The reboot context should have a block in progress"); @@ -1749,7 +1917,7 @@ mod tests { ); let _next_blueprint = - read_next_blueprint(&mut host, &mut Configuration::default()) + read_next_blueprint(host.host, &mut Configuration::default()) .expect("The next blueprint should be available"); } @@ -1767,6 +1935,10 @@ mod tests { // init host let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; // see // https://basescan.org/tx/0x07471adfe8f4ec553c1199f495be97fc8be8e0626ae307281c22534460184ed1 @@ -1794,7 +1966,7 @@ mod tests { let transactions: Vec = vec![tx]; - store_blueprints(&mut host, vec![blueprint(transactions)]); + store_blueprints(host.host, vec![blueprint(transactions)]); let sender = H160::from_str("05f32b3cc3888453ff71b01135b34ff8e41263f2").unwrap(); let mut evm_account_storage = init_account_storage().unwrap(); @@ -1804,9 +1976,10 @@ mod tests { &sender, U256::from(1_000_000_000_000_000_000u64), ); + host.promote().unwrap(); produce( - &mut host, + host.host, DUMMY_CHAIN_ID, &mut Configuration::default(), None, @@ -1818,8 +1991,10 @@ mod tests { let expected_created_contract = H160::from_str("0xcA11bde05977b3631167028862bE2a173976CA11").unwrap(); + host.start().unwrap(); let receipt = read_transaction_receipt(&mut host, &tx_hash) .expect("Should have found receipt"); + host.revert().unwrap(); assert_eq!(TransactionStatus::Success, receipt.status); assert_eq!(Some(expected_created_contract), receipt.contract_address); } @@ -1828,6 +2003,10 @@ mod tests { fn test_non_retriable_transaction_are_marked_as_failed() { // init host let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; crate::storage::store_minimum_base_fee_per_gas( &mut host, DUMMY_BASE_FEE_PER_GAS.into(), @@ -1844,6 +2023,8 @@ mod tests { &sender, sender_initial_balance, ); + store_block_fees(&mut host, &dummy_block_fees()).unwrap(); + host.promote().unwrap(); // These transactions are generated with the loop.sol contract, which are: // - create the contract @@ -1863,7 +2044,7 @@ mod tests { let proposals_first_reboot = vec![wrap_transaction(0, create_transaction)]; - store_inbox_blueprint(&mut host, blueprint(proposals_first_reboot)).unwrap(); + store_inbox_blueprint(host.host, blueprint(proposals_first_reboot)).unwrap(); // Set the tick limit to 11bn ticks - 2bn, which is the old limit minus the safety margin. let limits = Limits { @@ -1876,29 +2057,32 @@ mod tests { ..Configuration::default() }; // sanity check: no current block + host.start().unwrap(); assert!( block_storage::read_current_number(&host).is_err(), "Should not have found current block number" ); - store_block_fees(&mut host, &dummy_block_fees()).unwrap(); - produce(&mut host, DUMMY_CHAIN_ID, &mut configuration, None, None) + host.revert().unwrap(); + produce(host.host, DUMMY_CHAIN_ID, &mut configuration, None, None) .expect("Should have produced"); + host.start().unwrap(); assert!( block_storage::read_current_number(&host).is_ok(), "Should have found a block" ); + host.revert().unwrap(); // We start a new proposal, calling produce again simulates a reboot. let proposals_second_reboot = vec![wrap_transaction(2, loop_5800_tx)]; - store_inbox_blueprint(&mut host, blueprint(proposals_second_reboot)).unwrap(); - store_block_fees(&mut host, &dummy_block_fees()).unwrap(); + store_inbox_blueprint(host.host, blueprint(proposals_second_reboot)).unwrap(); - produce(&mut host, DUMMY_CHAIN_ID, &mut configuration, None, None) + produce(host.host, DUMMY_CHAIN_ID, &mut configuration, None, None) .expect("Should have produced"); + host.start().unwrap(); let block = block_storage::read_current(&mut host).expect("Should have found a block"); let failed_loop_hash = block @@ -1908,6 +2092,7 @@ mod tests { let failed_loop_status = storage::read_transaction_receipt_status(&mut host, failed_loop_hash) .expect("There should have been a receipt"); + host.revert().unwrap(); assert_eq!( failed_loop_status, @@ -1939,6 +2124,10 @@ mod tests { // Test if a valid transaction is producing a receipt with a success status fn test_type_propagation() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; crate::storage::store_minimum_base_fee_per_gas( &mut host, DUMMY_BASE_FEE_PER_GAS.into(), @@ -1966,7 +2155,7 @@ mod tests { let transactions: Vec = vec![valid_tx, valid_tx_eip1559, valid_tx_eip2930]; - store_blueprints(&mut host, vec![blueprint(transactions)]); + store_blueprints(host.host, vec![blueprint(transactions)]); let sender = dummy_eth_caller(); let mut evm_account_storage = init_account_storage().unwrap(); @@ -1977,9 +2166,10 @@ mod tests { U256::from(1_000_000_000_000_000_000u64), ); store_block_fees(&mut host, &dummy_block_fees()).unwrap(); + host.promote().unwrap(); produce( - &mut host, + host.host, DUMMY_CHAIN_ID, &mut Configuration::default(), None, @@ -1987,6 +2177,7 @@ mod tests { ) .expect("The block production failed."); + host.start().unwrap(); let receipt = read_transaction_receipt(&mut host, &tx_hash) .expect("Should have found receipt"); assert_eq!(receipt.type_, TransactionType::Legacy); @@ -1998,5 +2189,6 @@ mod tests { let receipt_eip2930 = read_transaction_receipt(&mut host, &tx_hash_eip2930) .expect("Should have found receipt"); assert_eq!(receipt_eip2930.type_, TransactionType::Eip2930); + host.revert().unwrap(); } } diff --git a/etherlink/kernel_evm/kernel/src/block_in_progress.rs b/etherlink/kernel_evm/kernel/src/block_in_progress.rs index 00452ee4dae282654c04c9cc290dd245f13463e2..10d374285e88e56721e13c42b906c8b8fe6ae8ca 100644 --- a/etherlink/kernel_evm/kernel/src/block_in_progress.rs +++ b/etherlink/kernel_evm/kernel/src/block_in_progress.rs @@ -286,7 +286,7 @@ impl BlockInProgress { self.delayed_txs.push(hash); } - pub fn register_valid_transaction( + pub fn register_valid_transaction>( &mut self, transaction: &Transaction, object_info: TransactionObjectInfo, @@ -347,9 +347,9 @@ impl BlockInProgress { self.add_ticks(tick_model::ticks_of_invalid_transaction(tx_data_size)); } - fn safe_store_get_hash( + fn safe_store_get_hash>( host: &mut Host, - path: &RefPath, + path: &RefPath, ) -> Result, anyhow::Error> { match host.store_get_hash(path) { Ok(hash) => Ok(hash), @@ -357,13 +357,13 @@ impl BlockInProgress { } } - const RECEIPTS: RefPath<'static> = RefPath::assert_from(b"/receipts"); - const RECEIPTS_PREVIOUS_ROOT: RefPath<'static> = - RefPath::assert_from(b"/receipts/previous_root"); + const RECEIPTS: RefPath<'static, false> = RefPath::assert_from(b"/__receipts"); + const RECEIPTS_PREVIOUS_ROOT: RefPath<'static, false> = + RefPath::assert_from(b"/__receipts/previous_root"); fn receipts_root( &self, - host: &mut impl Runtime, + host: &mut impl Runtime, previous_receipts_root: Vec, ) -> anyhow::Result> { if self.valid_txs.is_empty() { @@ -381,13 +381,13 @@ impl BlockInProgress { } } - const OBJECTS: RefPath<'static> = RefPath::assert_from(b"/objects"); - const OBJECTS_PREVIOUS_ROOT: RefPath<'static> = - RefPath::assert_from(b"/objects/previous_root"); + const OBJECTS: RefPath<'static, false> = RefPath::assert_from(b"/__objects"); + const OBJECTS_PREVIOUS_ROOT: RefPath<'static, false> = + RefPath::assert_from(b"/__objects/previous_root"); fn transactions_root( &self, - host: &mut impl Runtime, + host: &mut impl Runtime, previous_transactions_root: Vec, ) -> anyhow::Result> { if self.valid_txs.is_empty() { @@ -409,7 +409,7 @@ impl BlockInProgress { } #[cfg_attr(feature = "benchmark", inline(never))] - pub fn finalize_and_store( + pub fn finalize_and_store>( self, host: &mut Host, block_constants: &BlockConstants, diff --git a/etherlink/kernel_evm/kernel/src/block_storage.rs b/etherlink/kernel_evm/kernel/src/block_storage.rs index f090c86b40f9adf14a8aa490cca2fa460350b4bb..c753ff5ca0c9b28a2929a4292a6d2ee4dbb42b82 100644 --- a/etherlink/kernel_evm/kernel/src/block_storage.rs +++ b/etherlink/kernel_evm/kernel/src/block_storage.rs @@ -11,6 +11,7 @@ use tezos_evm_logging::{ }; use tezos_evm_runtime::runtime::Runtime; use tezos_indexable_storage::IndexableStorage; +use tezos_smart_rollup::types::Timestamp; use tezos_smart_rollup_host::path::concat; use tezos_smart_rollup_host::path::OwnedPath; use tezos_smart_rollup_host::path::RefPath; @@ -23,18 +24,18 @@ use crate::storage::EVM_TRANSACTIONS_RECEIPTS; mod path { use super::*; - pub const PATH: RefPath = RefPath::assert_from(b"/evm/world_state/blocks"); + pub const PATH: RefPath = RefPath::assert_from(b"/blocks"); - pub const CURRENT_NUMBER: RefPath = - RefPath::assert_from(b"/evm/world_state/blocks/current/number"); - pub const CURRENT_HASH: RefPath = - RefPath::assert_from(b"/evm/world_state/blocks/current/hash"); + pub const CURRENT_NUMBER: RefPath = + RefPath::assert_from(b"/blocks/current/number"); + pub const CURRENT_HASH: RefPath = + RefPath::assert_from(b"/blocks/current/hash"); - pub const INDEXES: RefPath = RefPath::assert_from(b"/evm/world_state/indexes/blocks"); + pub const INDEXES: RefPath = RefPath::assert_from(b"/indexes/blocks"); /// Path to the block in the storage. The path to the block is /// indexed by its hash. - pub fn path(hash: H256) -> anyhow::Result { + pub fn path(hash: H256) -> anyhow::Result> { let hash = hex::encode(hash); let raw_hash_path: Vec = format!("/{}", &hash).into(); let hash_path = OwnedPath::try_from(raw_hash_path)?; @@ -42,16 +43,19 @@ mod path { } } -fn store_current_number(host: &mut impl Runtime, number: U256) -> anyhow::Result<()> { +fn store_current_number( + host: &mut impl Runtime, + number: U256, +) -> anyhow::Result<()> { Ok(write_u256_le(host, &path::CURRENT_NUMBER, number)?) } -fn store_current_hash(host: &mut impl Runtime, hash: H256) -> anyhow::Result<()> { +fn store_current_hash(host: &mut impl Runtime, hash: H256) -> anyhow::Result<()> { write_h256_be(host, &path::CURRENT_HASH, hash) } fn store_block( - host: &mut impl Runtime, + host: &mut impl Runtime, block: &L2Block, index_block: bool, ) -> anyhow::Result<()> { @@ -67,7 +71,7 @@ fn store_block( } fn store_current_index_or_not( - host: &mut impl Runtime, + host: &mut impl Runtime, block: &L2Block, index_block: bool, ) -> anyhow::Result<()> { @@ -86,23 +90,29 @@ fn store_current_index_or_not( Ok(()) } -pub fn store_current(host: &mut impl Runtime, block: &L2Block) -> anyhow::Result<()> { +pub fn store_current( + host: &mut impl Runtime, + block: &L2Block, +) -> anyhow::Result<()> { store_current_index_or_not(host, block, true) } -pub fn restore_current(host: &mut impl Runtime, block: &L2Block) -> anyhow::Result<()> { +pub fn restore_current( + host: &mut impl Runtime, + block: &L2Block, +) -> anyhow::Result<()> { store_current_index_or_not(host, block, false) } -pub fn read_current_number(host: &impl Runtime) -> anyhow::Result { +pub fn read_current_number(host: &impl Runtime) -> anyhow::Result { Ok(read_u256_le(host, &path::CURRENT_NUMBER)?) } -pub fn read_current_hash(host: &impl Runtime) -> anyhow::Result { +pub fn read_current_hash(host: &impl Runtime) -> anyhow::Result { read_h256_be(host, &path::CURRENT_HASH) } -pub fn read_current(host: &mut impl Runtime) -> anyhow::Result { +pub fn read_current(host: &mut impl Runtime) -> anyhow::Result { let hash = read_current_hash(host)?; let block_path = path::path(hash)?; let bytes = &host.store_read_all(&block_path)?; @@ -110,7 +120,14 @@ pub fn read_current(host: &mut impl Runtime) -> anyhow::Result { Ok(block_from_bytes) } -pub fn garbage_collect_blocks(host: &mut impl Runtime) -> anyhow::Result<()> { +pub fn read_current_timestamp( + host: &mut impl Runtime, +) -> anyhow::Result { + let block = read_current(host)?; + Ok(block.timestamp) +} + +pub fn garbage_collect_blocks(host: &mut impl Runtime) -> anyhow::Result<()> { log!(host, Debug, "Garbage collecting blocks."); if let Ok(block) = read_current(host) { // The kernel needs the current block to process the next one. Therefore @@ -128,12 +145,12 @@ pub fn garbage_collect_blocks(host: &mut impl Runtime) -> anyhow::Result<()> { pub mod internal_for_tests { use super::*; - pub fn init_blocks_index() -> anyhow::Result { + pub fn init_blocks_index() -> anyhow::Result> { Ok(IndexableStorage::new(&path::INDEXES)?) } pub fn store_current_number( - host: &mut impl Runtime, + host: &mut impl Runtime, number: U256, ) -> anyhow::Result<()> { super::store_current_number(host, number) diff --git a/etherlink/kernel_evm/kernel/src/blueprint_storage.rs b/etherlink/kernel_evm/kernel/src/blueprint_storage.rs index 35620856bbc5f351e6f1e8ef35ad0003f9a61067..b94cc9759bb10101cccc368d136bcfd34bf1750c 100644 --- a/etherlink/kernel_evm/kernel/src/blueprint_storage.rs +++ b/etherlink/kernel_evm/kernel/src/blueprint_storage.rs @@ -12,26 +12,38 @@ use crate::inbox::{Transaction, TransactionContent}; use crate::sequencer_blueprint::{ BlueprintWithDelayedHashes, UnsignedSequencerBlueprint, }; -use crate::storage::read_last_info_per_level_timestamp; +use crate::storage::{ + read_last_info_per_level_timestamp, read_timestamp_path, store_timestamp_path, +}; use crate::{delayed_inbox, DelayedInbox}; use primitive_types::U256; use rlp::{Decodable, DecoderError, Encodable}; use sha3::{Digest, Keccak256}; use tezos_ethereum::rlp_helpers; use tezos_ethereum::tx_common::EthereumTransactionCommon; -use tezos_evm_logging::{log, Level::*}; -use tezos_evm_runtime::runtime::Runtime; +use tezos_evm_logging::{log, Level::*, Verbosity}; +use tezos_evm_runtime::{ + runtime::Runtime, + safe_storage::{SafeStorage, WORLD_STATE_PATH}, +}; use tezos_smart_rollup::types::Timestamp; use tezos_smart_rollup_core::MAX_INPUT_MESSAGE_SIZE; use tezos_smart_rollup_host::path::*; use tezos_smart_rollup_host::runtime::RuntimeError; use tezos_storage::{ - error::Error as GenStorageError, read_rlp, store_read_slice, store_rlp, + error::Error as GenStorageError, read_rlp, read_u256_le, store_read_slice, store_rlp, + write_u256_le, }; -pub const EVM_BLUEPRINTS: RefPath = RefPath::assert_from(b"/evm/blueprints"); +pub const EVM_BLUEPRINTS: RefPath = RefPath::assert_from(b"/evm/blueprints"); + +const EVM_BLUEPRINT_NB_CHUNKS: RefPath = RefPath::assert_from(b"/nb_chunks"); + +const NEXT_BLUEPRINT_NUMBER: RefPath = + RefPath::assert_from(b"/evm/blueprints/next"); -const EVM_BLUEPRINT_NB_CHUNKS: RefPath = RefPath::assert_from(b"/nb_chunks"); +const LAST_BLOCK_TIMESTAMP: RefPath = + RefPath::assert_from(b"/evm/blueprints/last_block_timestamp"); /// The store representation of a blueprint. /// It's designed to support storing sequencer blueprints, @@ -87,7 +99,7 @@ impl Decodable for StoreBlueprint { } } -pub fn blueprint_path(number: U256) -> Result { +pub fn blueprint_path(number: U256) -> Result, StorageError> { let number_as_path: Vec = format!("/{}", number).into(); // The key being an integer value, it will always be valid as a path, // `assert_from` cannot fail. @@ -96,23 +108,23 @@ pub fn blueprint_path(number: U256) -> Result { } fn blueprint_chunk_path( - blueprint_path: &OwnedPath, + blueprint_path: &OwnedPath, chunk_index: u16, -) -> Result { +) -> Result, StorageError> { let chunk_index_as_path: Vec = format!("/{}", chunk_index).into(); let chunk_index_subkey = RefPath::assert_from(&chunk_index_as_path); concat(blueprint_path, &chunk_index_subkey).map_err(StorageError::from) } fn blueprint_nb_chunks_path( - blueprint_path: &OwnedPath, -) -> Result { + blueprint_path: &OwnedPath, +) -> Result, StorageError> { concat(blueprint_path, &EVM_BLUEPRINT_NB_CHUNKS).map_err(StorageError::from) } -fn read_blueprint_nb_chunks( +fn read_blueprint_nb_chunks>( host: &Host, - blueprint_path: &OwnedPath, + blueprint_path: &OwnedPath, ) -> Result { let path = blueprint_nb_chunks_path(blueprint_path)?; let mut buffer = [0u8; 2]; @@ -120,9 +132,9 @@ fn read_blueprint_nb_chunks( Ok(u16::from_le_bytes(buffer)) } -fn store_blueprint_nb_chunks( +fn store_blueprint_nb_chunks>( host: &mut Host, - blueprint_path: &OwnedPath, + blueprint_path: &OwnedPath, nb_chunks: u16, ) -> Result<(), Error> { let path = blueprint_nb_chunks_path(blueprint_path)?; @@ -130,7 +142,7 @@ fn store_blueprint_nb_chunks( host.store_write_all(&path, &bytes).map_err(Error::from) } -pub fn store_sequencer_blueprint( +pub fn store_sequencer_blueprint>( host: &mut Host, blueprint: UnsignedSequencerBlueprint, ) -> Result<(), Error> { @@ -142,7 +154,7 @@ pub fn store_sequencer_blueprint( store_rlp(&store_blueprint, host, &blueprint_chunk_path).map_err(Error::from) } -pub fn store_inbox_blueprint_by_number( +pub fn store_inbox_blueprint_by_number>( host: &mut Host, blueprint: Blueprint, number: U256, @@ -154,7 +166,7 @@ pub fn store_inbox_blueprint_by_number( store_rlp(&store_blueprint, host, &chunk_path).map_err(Error::from) } -pub fn store_inbox_blueprint( +pub fn store_inbox_blueprint>( host: &mut Host, blueprint: Blueprint, ) -> anyhow::Result<()> { @@ -162,21 +174,41 @@ pub fn store_inbox_blueprint( Ok(store_inbox_blueprint_by_number(host, blueprint, number)?) } -#[inline(always)] -pub fn read_next_blueprint_number(host: &Host) -> anyhow::Result { - match block_storage::read_current_number(host) { - Err(err) => match err.downcast_ref() { - Some(GenStorageError::Runtime(RuntimeError::PathNotFound)) => { - Ok(U256::zero()) - } - _ => Err(err), - }, - Ok(block_number) => Ok(block_number.saturating_add(U256::one())), +pub fn read_next_blueprint_number>( + host: &Host, +) -> anyhow::Result { + match read_u256_le(host, &NEXT_BLUEPRINT_NUMBER) { + Err(GenStorageError::Runtime(RuntimeError::PathNotFound)) => Ok(U256::zero()), + res => Ok(res?), } } +pub fn store_next_blueprint_number>( + host: &mut Host, + number: U256, +) -> anyhow::Result<()> { + Ok(write_u256_le(host, &NEXT_BLUEPRINT_NUMBER, number)?) +} + +pub fn read_last_block_timestamp>( + host: &Host, +) -> anyhow::Result { + Ok(read_timestamp_path(host, &LAST_BLOCK_TIMESTAMP)?) +} + +pub fn store_last_block_timestamp>( + host: &mut Host, + timestamp: &Timestamp, +) -> anyhow::Result<()> { + Ok(store_timestamp_path( + host, + &LAST_BLOCK_TIMESTAMP, + timestamp, + )?) +} + // Used to store a blueprint made out of forced delayed transactions. -pub fn store_immediate_blueprint( +pub fn store_immediate_blueprint>( host: &mut Host, blueprint: Blueprint, number: U256, @@ -212,7 +244,7 @@ pub enum BlueprintValidity { BlueprintTooLarge, } -fn fetch_delayed_txs( +fn fetch_delayed_txs>( host: &mut Host, blueprint_with_hashes: BlueprintWithDelayedHashes, delayed_inbox: &mut DelayedInbox, @@ -268,7 +300,7 @@ fn fetch_delayed_txs( // wish to refuse such blueprints. pub const DEFAULT_MAX_BLUEPRINT_LOOKAHEAD_IN_SECONDS: i64 = 300i64; -fn parse_and_validate_blueprint( +fn parse_and_validate_blueprint>( host: &mut Host, bytes: &[u8], delayed_inbox: &mut DelayedInbox, @@ -280,7 +312,11 @@ fn parse_and_validate_blueprint( match rlp::decode::(bytes) { Err(e) => Ok((BlueprintValidity::DecoderError(e), bytes.len())), Ok(blueprint_with_hashes) => { - let head = block_storage::read_current(host); + let mut host = SafeStorage { + root: WORLD_STATE_PATH, + host, + }; + let head = block_storage::read_current(&mut host); let (head_hash, head_timestamp) = match head { Ok(block) => (block.hash, block.timestamp), Err(_) => (GENESIS_PARENT_HASH, Timestamp::from(0)), @@ -311,7 +347,8 @@ fn parse_and_validate_blueprint( // timestamps. #[cfg(not(feature = "benchmark"))] { - let last_seen_l1_timestamp = read_last_info_per_level_timestamp(host)?; + let last_seen_l1_timestamp = + read_last_info_per_level_timestamp(host.host)?; let accepted_bound = Timestamp::from( last_seen_l1_timestamp .i64() @@ -334,7 +371,7 @@ fn parse_and_validate_blueprint( // Fetch delayed transactions fetch_delayed_txs( - host, + host.host, blueprint_with_hashes, delayed_inbox, current_blueprint_size, @@ -343,9 +380,9 @@ fn parse_and_validate_blueprint( } } -fn invalidate_blueprint( +fn invalidate_blueprint>( host: &mut Host, - blueprint_path: &OwnedPath, + blueprint_path: &OwnedPath, error: &BlueprintValidity, ) -> Result<(), RuntimeError> { log!( @@ -359,9 +396,9 @@ fn invalidate_blueprint( host.store_delete(blueprint_path) } -fn read_all_chunks_and_validate( +fn read_all_chunks_and_validate>( host: &mut Host, - blueprint_path: &OwnedPath, + blueprint_path: &OwnedPath, nb_chunks: u16, config: &mut Configuration, ) -> anyhow::Result<(Option, usize)> { @@ -423,7 +460,7 @@ fn read_all_chunks_and_validate( } } -pub fn read_next_blueprint( +pub fn read_next_blueprint>( host: &mut Host, config: &mut Configuration, ) -> anyhow::Result<(Option, usize)> { @@ -468,12 +505,15 @@ pub fn read_next_blueprint( } } -pub fn drop_blueprint(host: &mut Host, number: U256) -> Result<(), Error> { +pub fn drop_blueprint>( + host: &mut Host, + number: U256, +) -> Result<(), Error> { let path = blueprint_path(number)?; host.store_delete(&path).map_err(Error::from) } -pub fn clear_all_blueprints(host: &mut Host) -> Result<(), Error> { +pub fn clear_all_blueprints>(host: &mut Host) -> Result<(), Error> { if host.store_has(&EVM_BLUEPRINTS)?.is_some() { Ok(host.store_delete(&EVM_BLUEPRINTS)?) } else { diff --git a/etherlink/kernel_evm/kernel/src/bridge.rs b/etherlink/kernel_evm/kernel/src/bridge.rs index 0fd6fa8924752a2cd452d1d4a842f9879cc43e42..ee23ae46dd60201ec0d84f710d0b9266f8cce951 100644 --- a/etherlink/kernel_evm/kernel/src/bridge.rs +++ b/etherlink/kernel_evm/kernel/src/bridge.rs @@ -177,7 +177,7 @@ impl Decodable for Deposit { } } -pub fn execute_deposit( +pub fn execute_deposit>( host: &mut Host, evm_account_storage: &mut EthereumAccountStorage, deposit: &Deposit, @@ -223,7 +223,10 @@ mod tests { use evm_execution::account_storage::init_account_storage; use primitive_types::{H160, U256}; use rlp::Decodable; - use tezos_evm_runtime::runtime::MockKernelHost; + use tezos_evm_runtime::{ + runtime::MockKernelHost, + safe_storage::{SafeStorage, WORLD_STATE_PATH}, + }; use crate::{bridge::DEPOSIT_EVENT_TOPIC, CONFIG}; @@ -275,6 +278,10 @@ mod tests { #[test] fn deposit_execution_outcome_contains_event() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = init_account_storage().unwrap(); let deposit = dummy_deposit(); @@ -302,6 +309,10 @@ mod tests { #[test] fn deposit_execution_fails_due_to_balance_overflow() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = init_account_storage().unwrap(); let mut deposit = dummy_deposit(); diff --git a/etherlink/kernel_evm/kernel/src/configuration.rs b/etherlink/kernel_evm/kernel/src/configuration.rs index 0c7486f3e384b0bf8a6423d6032e486afb842ee6..596055fdaba23e6d88b446afca9f739ea0e51dad 100644 --- a/etherlink/kernel_evm/kernel/src/configuration.rs +++ b/etherlink/kernel_evm/kernel/src/configuration.rs @@ -13,7 +13,10 @@ use crate::{ use evm_execution::read_ticketer; use tezos_crypto_rs::hash::ContractKt1Hash; use tezos_evm_logging::{log, Level::*}; -use tezos_evm_runtime::runtime::Runtime; +use tezos_evm_runtime::{ + runtime::Runtime, + safe_storage::{SafeStorage, WORLD_STATE_PATH}, +}; use tezos_smart_rollup_encoding::public_key::PublicKey; #[derive(Debug, Clone, Default)] @@ -145,10 +148,15 @@ impl TezosContracts { } } -fn fetch_tezos_contracts(host: &mut impl Runtime) -> TezosContracts { +fn fetch_tezos_contracts(host: &mut impl Runtime) -> TezosContracts { // 1. Fetch the kernel's ticketer, returns `None` if it is badly // encoded or absent. - let ticketer = read_ticketer(host); + let host = SafeStorage { + host, + root: WORLD_STATE_PATH, + }; + let ticketer = read_ticketer(&host); + let host = host.host; // 2. Fetch the kernel's administrator, returns `None` if it is badly // encoded or absent. let admin = read_admin(host); @@ -171,7 +179,7 @@ fn fetch_tezos_contracts(host: &mut impl Runtime) -> TezosContracts { } } -pub fn fetch_limits(host: &mut impl Runtime) -> Limits { +pub fn fetch_limits(host: &mut impl Runtime) -> Limits { let maximum_allowed_ticks = read_maximum_allowed_ticks(host).unwrap_or(MAX_ALLOWED_TICKS); @@ -184,7 +192,9 @@ pub fn fetch_limits(host: &mut impl Runtime) -> Limits { } } -fn fetch_dal_configuration(host: &mut Host) -> Option { +fn fetch_dal_configuration>( + host: &mut Host, +) -> Option { let enable_dal = enable_dal(host).unwrap_or(false); if enable_dal { let slot_indices: Vec = dal_slots(host).unwrap_or(None)?; @@ -194,7 +204,7 @@ fn fetch_dal_configuration(host: &mut Host) -> Option(host: &mut Host) -> Configuration { +pub fn fetch_configuration>(host: &mut Host) -> Configuration { let tezos_contracts = fetch_tezos_contracts(host); let limits = fetch_limits(host); let sequencer = sequencer(host).unwrap_or_default(); diff --git a/etherlink/kernel_evm/kernel/src/dal.rs b/etherlink/kernel_evm/kernel/src/dal.rs index 47194ee950ad3943792e4580d7c92f0df4a74236..79e78bc8ce4f4a1e70ba2be391cd882c7d19ccea 100644 --- a/etherlink/kernel_evm/kernel/src/dal.rs +++ b/etherlink/kernel_evm/kernel/src/dal.rs @@ -24,7 +24,7 @@ enum ParsedInput { } // Import all the pages of a DAL slot and concatenate them. -fn import_dal_slot( +fn import_dal_slot>( host: &mut Host, params: &RollupDalParameters, published_level: u32, @@ -70,13 +70,16 @@ fn rlp_length(data: &[u8]) -> Result { Result::Ok(header_len + value_len) } -fn parse_unsigned_sequencer_blueprint( +fn parse_unsigned_sequencer_blueprint>( host: &mut Host, bytes: &[u8], - head_level: &Option, + next_blueprint_number: &U256, ) -> (Option, usize) { if let Result::Ok(chunk_length) = rlp_length(bytes) { - match parse_unsigned_blueprint_chunk(&bytes[..chunk_length], head_level) { + match parse_unsigned_blueprint_chunk( + &bytes[..chunk_length], + next_blueprint_number, + ) { SequencerBlueprintRes::SequencerBlueprint(unsigned_chunk) => ( Some(ParsedInput::UnsignedSequencerBlueprint(unsigned_chunk)), chunk_length + TAG_SIZE, @@ -94,10 +97,10 @@ fn parse_unsigned_sequencer_blueprint( } } -fn parse_input( +fn parse_input>( host: &mut Host, bytes: &[u8], - head_level: &Option, + next_blueprint_number: &U256, ) -> (Option, usize) { // The expected format is: @@ -107,7 +110,7 @@ fn parse_input( DAL_PADDING_TAG => (Some(ParsedInput::Padding), TAG_SIZE), DAL_BLUEPRINT_INPUT_TAG => { let bytes = &bytes[TAG_SIZE..]; - parse_unsigned_sequencer_blueprint(host, bytes, head_level) + parse_unsigned_sequencer_blueprint(host, bytes, next_blueprint_number) } invalid_tag => { log!( @@ -122,10 +125,10 @@ fn parse_input( } } -fn parse_slot( +fn parse_slot>( host: &mut Host, slot: &[u8], - head_level: &Option, + next_blueprint_number: &U256, ) -> Vec { // The format of a dal slot is // tagged chunk | tagged chunk | .. | padding @@ -145,7 +148,8 @@ fn parse_slot( if offset >= slot.len() { return buffer; }; - let (next_input, length) = parse_input(host, &slot[offset..], head_level); + let (next_input, length) = + parse_input(host, &slot[offset..], next_blueprint_number); match next_input { None => return buffer, // Once an unparsable input has been read, @@ -162,10 +166,10 @@ fn parse_slot( } } -pub fn fetch_and_parse_sequencer_blueprint_from_dal( +pub fn fetch_and_parse_sequencer_blueprint_from_dal>( host: &mut Host, params: &RollupDalParameters, - head_level: &Option, + next_blueprint_number: &U256, slot_index: u8, published_level: u32, ) -> Option> { @@ -182,7 +186,7 @@ pub fn fetch_and_parse_sequencer_blueprint_from_dal( // size, we need to remove this padding before parsing the // slot as a blueprint chunk. - let chunks = parse_slot(host, &slot, head_level); + let chunks = parse_slot(host, &slot, next_blueprint_number); log!( host, Debug, @@ -358,7 +362,7 @@ pub mod tests { let chunks_from_slot = fetch_and_parse_sequencer_blueprint_from_dal( &mut host, &dal_parameters, - &None, + &0.into(), 0, published_level, ); @@ -398,7 +402,7 @@ pub mod tests { let chunks_from_slot = fetch_and_parse_sequencer_blueprint_from_dal( &mut host, &dal_parameters, - &None, + &0.into(), 0, published_level, ); @@ -464,7 +468,7 @@ pub mod tests { let chunks_from_slot = fetch_and_parse_sequencer_blueprint_from_dal( &mut host, &dal_parameters, - &None, + &0.into(), 0, published_level, ); @@ -542,7 +546,7 @@ pub mod tests { let chunks_from_slot = fetch_and_parse_sequencer_blueprint_from_dal( &mut host, &dal_parameters, - &None, + &0.into(), 0, published_level, ); @@ -556,7 +560,7 @@ pub mod tests { let chunks_from_slot = fetch_and_parse_sequencer_blueprint_from_dal( &mut host, &dal_parameters, - &None, + &0.into(), 0, published_level, ); @@ -576,7 +580,7 @@ pub mod tests { fn test_parse_slot_with_blueprints_from_the_past() { let mut host = MockKernelHost::default(); - let head_level = Some(2.into()); + let next_blueprint_number = 3.into(); let chunks = chunk_blueprint_range(0, 5); let expected_chunks = chunk_blueprint_range(3, 5); @@ -590,7 +594,7 @@ pub mod tests { let chunks_from_slot = fetch_and_parse_sequencer_blueprint_from_dal( &mut host, &dal_parameters, - &head_level, + &next_blueprint_number, 0, published_level, ); diff --git a/etherlink/kernel_evm/kernel/src/delayed_inbox.rs b/etherlink/kernel_evm/kernel/src/delayed_inbox.rs index 840236908060d47f567ec00402d408afe4c0c71e..4b8bbdf29a19cccc2f5fe7a777329bdf2e124645 100644 --- a/etherlink/kernel_evm/kernel/src/delayed_inbox.rs +++ b/etherlink/kernel_evm/kernel/src/delayed_inbox.rs @@ -24,14 +24,14 @@ use tezos_storage::read_u16_le_default; pub struct DelayedInbox(LinkedList); -pub const DELAYED_INBOX_PATH: RefPath = RefPath::assert_from(b"/evm/delayed-inbox"); +pub const DELAYED_INBOX_PATH: RefPath = RefPath::assert_from(b"/evm/delayed-inbox"); // Maximum number of transaction included in a blueprint when // forcing timed-out transactions from the delayed inbox. pub const DEFAULT_MAX_DELAYED_INBOX_BLUEPRINT_LENGTH: u16 = 1000; // Path to override the default value. -pub const MAX_DELAYED_INBOX_BLUEPRINT_LENGTH_PATH: RefPath = +pub const MAX_DELAYED_INBOX_BLUEPRINT_LENGTH_PATH: RefPath = RefPath::assert_from(b"/evm/max_delayed_inbox_blueprint_length"); // Tag that indicates the delayed transaction is a eth transaction. @@ -182,12 +182,12 @@ impl Decodable for DelayedInboxItem { } impl DelayedInbox { - pub fn new(host: &mut Host) -> Result { + pub fn new>(host: &mut Host) -> Result { let linked_list = LinkedList::new(&DELAYED_INBOX_PATH, host)?; Ok(Self(linked_list)) } - pub fn save_transaction( + pub fn save_transaction>( &mut self, host: &mut Host, tx: Transaction, @@ -239,7 +239,7 @@ impl DelayedInbox { } } - pub fn find_transaction( + pub fn find_transaction>( &mut self, host: &mut Host, tx_hash: Hash, @@ -262,7 +262,7 @@ impl DelayedInbox { // Returns the oldest tx in the delayed inbox (and its hash) if it // timed out - fn first_if_timed_out( + fn first_if_timed_out>( &mut self, host: &mut Host, now: Timestamp, @@ -298,12 +298,12 @@ impl DelayedInbox { } #[cfg(test)] - pub fn is_empty(&self, host: &mut Host) -> Result { + pub fn is_empty>(&self, host: &mut Host) -> Result { let first = self.0.first_with_id(host)?; Ok(first.is_none()) } - fn pop_first( + fn pop_first>( &mut self, host: &mut Host, ) -> Result> { @@ -320,7 +320,7 @@ impl DelayedInbox { } /// Returns whether the oldest tx in the delayed inbox has timed out. - pub fn first_has_timed_out( + pub fn first_has_timed_out>( &mut self, host: &mut Host, ) -> Result { @@ -338,7 +338,7 @@ impl DelayedInbox { /// signal that we're done. /// Note that this function assumes we're on a "timeout" state, /// which should be checked before calling it. - pub fn next_delayed_inbox_blueprint( + pub fn next_delayed_inbox_blueprint>( &mut self, host: &mut Host, ) -> Result>> { @@ -367,7 +367,7 @@ impl DelayedInbox { /// a transaction is removed or not. The only property ensured by the /// function is that the transaction is not part of the delayed inbox /// after the call. - pub fn delete( + pub fn delete>( &mut self, host: &mut Host, tx_hash: Hash, diff --git a/etherlink/kernel_evm/kernel/src/event.rs b/etherlink/kernel_evm/kernel/src/event.rs index 4a4266307c3e34d0a012e76334fb592f3fc4c826..1bea180f2a854e43b95096f62490f1f5ceaa2f84 100644 --- a/etherlink/kernel_evm/kernel/src/event.rs +++ b/etherlink/kernel_evm/kernel/src/event.rs @@ -70,7 +70,7 @@ impl Encodable for Event<'_> { } impl Event<'_> { - pub fn store(&self, host: &mut Host) -> anyhow::Result<()> { + pub fn store>(&self, host: &mut Host) -> anyhow::Result<()> { storage::store_event(host, self) } } diff --git a/etherlink/kernel_evm/kernel/src/evm_node_entrypoint.rs b/etherlink/kernel_evm/kernel/src/evm_node_entrypoint.rs index c2791fae5d405cf86ad7a2b336e555eec8de85a1..ca98da04694247d40f24336a194a8463ee8f8cb0 100644 --- a/etherlink/kernel_evm/kernel/src/evm_node_entrypoint.rs +++ b/etherlink/kernel_evm/kernel/src/evm_node_entrypoint.rs @@ -14,7 +14,7 @@ use tezos_evm_runtime::runtime::KernelHost; use tezos_smart_rollup_core::rollup_host::RollupHost; use tezos_smart_rollup_host::{path::RefPath, runtime::Runtime}; -const DELAYED_INPUT_PATH: RefPath = RefPath::assert_from(b"/__delayed_input"); +const DELAYED_INPUT_PATH: RefPath = RefPath::assert_from(b"/__delayed_input"); #[allow(dead_code)] #[no_mangle] diff --git a/etherlink/kernel_evm/kernel/src/fallback_upgrade.rs b/etherlink/kernel_evm/kernel/src/fallback_upgrade.rs index d1a8caecfb6f9fec1c786660d7b2559e2fd32381..b19e6fef345a3098a33524036cdb49bdae470c58 100644 --- a/etherlink/kernel_evm/kernel/src/fallback_upgrade.rs +++ b/etherlink/kernel_evm/kernel/src/fallback_upgrade.rs @@ -9,13 +9,13 @@ use tezos_smart_rollup_host::{path::RefPath, runtime::RuntimeError, KERNEL_BOOT_ use crate::upgrade::KERNEL_ROOT_HASH; -const BACKUP_KERNEL_BOOT_PATH: RefPath = +const BACKUP_KERNEL_BOOT_PATH: RefPath = RefPath::assert_from(b"/__backup_kernel/boot.wasm"); -const BACKUP_KERNEL_ROOT_HASH: RefPath = +const BACKUP_KERNEL_ROOT_HASH: RefPath = RefPath::assert_from(b"/__backup_kernel/root_hash"); -pub fn backup_current_kernel(host: &mut impl Runtime) -> Result<(), RuntimeError> { +pub fn backup_current_kernel(host: &mut impl Runtime) -> Result<(), RuntimeError> { // Fallback preparation detected // Storing the current kernel boot path under a temporary path in // order to fallback on it if something goes wrong in the upcoming @@ -38,7 +38,7 @@ pub fn backup_current_kernel(host: &mut impl Runtime) -> Result<(), RuntimeError host.store_copy(&KERNEL_BOOT_PATH, &BACKUP_KERNEL_BOOT_PATH) } -pub fn fallback_backup_kernel(host: &mut impl Runtime) -> Result<(), RuntimeError> { +pub fn fallback_backup_kernel(host: &mut impl Runtime) -> Result<(), RuntimeError> { log!( host, Error, diff --git a/etherlink/kernel_evm/kernel/src/fees.rs b/etherlink/kernel_evm/kernel/src/fees.rs index c2a0e82bf416e52ffa29c973ae8771b45d8c9160..48510641d472645870199d390a99b6d246223f19 100644 --- a/etherlink/kernel_evm/kernel/src/fees.rs +++ b/etherlink/kernel_evm/kernel/src/fees.rs @@ -167,7 +167,7 @@ impl FeeUpdates { pub fn apply( &self, - host: &mut impl Runtime, + host: &mut impl Runtime, accounts: &mut EthereumAccountStorage, caller: H160, sequencer_pool_address: Option, @@ -311,7 +311,10 @@ mod tests { use evm::ExitSucceed; use evm_execution::account_storage::{account_path, EthereumAccountStorage}; use primitive_types::{H160, U256}; - use tezos_evm_runtime::runtime::MockKernelHost; + use tezos_evm_runtime::{ + runtime::MockKernelHost, + safe_storage::{SafeStorage, WORLD_STATE_PATH}, + }; use proptest::prelude::*; @@ -363,6 +366,10 @@ mod tests { fn apply_updates_balances_no_sequencer() { // Arrange let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = evm_execution::account_storage::init_account_storage().unwrap(); @@ -400,6 +407,10 @@ mod tests { fn apply_updates_balances_with_sequencer() { // Arrange let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let sequencer_address = address_from_str("0123456789ABCDEF0123456789ABCDEF01234567"); @@ -457,6 +468,10 @@ mod tests { fn apply_fails_user_charge_too_large() { // Arrange let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let mut evm_account_storage = evm_execution::account_storage::init_account_storage().unwrap(); @@ -510,7 +525,7 @@ mod tests { } fn get_balance( - host: &mut MockKernelHost, + host: &mut impl Runtime, evm_account_storage: &mut EthereumAccountStorage, address: H160, ) -> U256 { @@ -521,7 +536,7 @@ mod tests { } fn set_balance( - host: &mut MockKernelHost, + host: &mut impl Runtime, evm_account_storage: &mut EthereumAccountStorage, address: H160, balance: U256, diff --git a/etherlink/kernel_evm/kernel/src/gas_price.rs b/etherlink/kernel_evm/kernel/src/gas_price.rs index 8d79fec4f7596d40c625fb4630e6f8fc589a3caf..a7e88088f92a2cc0738f4193bd5f7121cd610f31 100644 --- a/etherlink/kernel_evm/kernel/src/gas_price.rs +++ b/etherlink/kernel_evm/kernel/src/gas_price.rs @@ -23,7 +23,7 @@ const ALPHA: F64 = softfloat::f64!(0.000_000_000_007); /// Register a completed block into the tick backlog pub fn register_block( - host: &mut impl Runtime, + host: &mut impl Runtime, bip: &BlockInProgress, ) -> anyhow::Result<()> { if bip.queue_length() > 0 { @@ -37,7 +37,7 @@ pub fn register_block( /// Retrieve *base fee per gas*, according to the current timestamp. pub fn base_fee_per_gas( - host: &impl Runtime, + host: &impl Runtime, timestamp: Timestamp, minimum_gas_price: U256, ) -> U256 { @@ -53,7 +53,7 @@ pub fn base_fee_per_gas( } fn backlog_with_time_elapsed( - host: &impl Runtime, + host: &impl Runtime, extra_ticks: u64, current_timestamp: u64, last_timestamp: u64, @@ -69,7 +69,7 @@ fn backlog_with_time_elapsed( } fn update_tick_backlog( - host: &mut impl Runtime, + host: &mut impl Runtime, ticks_in_block: u64, timestamp: Timestamp, ) -> anyhow::Result<()> { @@ -155,7 +155,10 @@ mod test { use proptest::prelude::*; use std::collections::VecDeque; use tezos_ethereum::block::BlockConstants; - use tezos_evm_runtime::runtime::{MockKernelHost, Runtime}; + use tezos_evm_runtime::{ + runtime::{MockKernelHost, Runtime}, + safe_storage::{SafeStorage, WORLD_STATE_PATH}, + }; proptest! { #[test] @@ -186,6 +189,10 @@ mod test { #[test] fn gas_price_responds_to_load() { let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let timestamp = 0_i64; let block_fees = crate::retrieve_block_fees(&mut host).unwrap(); let dummy_block_constants = BlockConstants::first_block( @@ -238,7 +245,7 @@ mod test { ); } - fn load_gas_price(host: &mut impl Runtime) -> (U256, U256) { + fn load_gas_price(host: &mut impl Runtime) -> (U256, U256) { let bf = crate::retrieve_block_fees(host).unwrap(); (bf.minimum_base_fee_per_gas(), bf.base_fee_per_gas()) diff --git a/etherlink/kernel_evm/kernel/src/inbox.rs b/etherlink/kernel_evm/kernel/src/inbox.rs index 8cf2def8fe40811677a5f565fac5895525a86d6a..f88796ee46cea705b896917f1824ee52da2327d6 100644 --- a/etherlink/kernel_evm/kernel/src/inbox.rs +++ b/etherlink/kernel_evm/kernel/src/inbox.rs @@ -42,7 +42,7 @@ use tezos_ethereum::transaction::{ }; use tezos_ethereum::tx_common::EthereumTransactionCommon; use tezos_evm_logging::{log, Level::*}; -use tezos_evm_runtime::runtime::Runtime; +use tezos_evm_runtime::{relative_runtime::RelativeRuntime, runtime::Runtime}; use tezos_smart_rollup_encoding::public_key::PublicKey; #[allow(clippy::large_enum_variant)] @@ -196,7 +196,7 @@ pub struct ProxyInboxContent { pub transactions: Vec, } -pub fn read_input( +pub fn read_input, Mode: Parsable>( host: &mut Host, smart_rollup_address: [u8; 20], tezos_contracts: &TezosContracts, @@ -230,19 +230,19 @@ where /// Abstracts the type used to store the inputs once handled type Inbox; - fn handle_input( + fn handle_input>( host: &mut Host, input: Self, inbox_content: &mut Self::Inbox, ) -> anyhow::Result<()>; - fn handle_deposit( + fn handle_deposit>( host: &mut Host, deposit: Deposit, inbox_content: &mut Self::Inbox, ) -> anyhow::Result<()>; - fn handle_fa_deposit( + fn handle_fa_deposit>( host: &mut Host, fa_deposit: FaDeposit, inbox_content: &mut Self::Inbox, @@ -254,7 +254,7 @@ impl InputHandler for ProxyInput { // everything is doable in a single kernel_run. type Inbox = ProxyInboxContent; - fn handle_input( + fn handle_input>( host: &mut Host, input: Self, inbox_content: &mut Self::Inbox, @@ -282,7 +282,7 @@ impl InputHandler for ProxyInput { Ok(()) } - fn handle_deposit( + fn handle_deposit>( host: &mut Host, deposit: Deposit, inbox_content: &mut Self::Inbox, @@ -294,7 +294,7 @@ impl InputHandler for ProxyInput { } #[cfg_attr(feature = "benchmark", inline(never))] - fn handle_fa_deposit( + fn handle_fa_deposit>( host: &mut Host, fa_deposit: FaDeposit, inbox_content: &mut Self::Inbox, @@ -306,7 +306,7 @@ impl InputHandler for ProxyInput { } } -fn handle_blueprint_chunk( +fn handle_blueprint_chunk>( host: &mut Host, blueprint: UnsignedSequencerBlueprint, ) -> anyhow::Result<()> { @@ -327,7 +327,7 @@ impl InputHandler for SequencerInput { // there is nothing to return in the end. type Inbox = DelayedInbox; - fn handle_input( + fn handle_input>( host: &mut Host, input: Self, delayed_inbox: &mut Self::Inbox, @@ -359,8 +359,8 @@ impl InputHandler for SequencerInput { }) => { log!(host, Debug, "Importing {} DAL signals", &signals.0.len()); let params = host.reveal_dal_parameters(); - let head_level: Option = - crate::block_storage::read_current_number(host).ok(); + let next_blueprint_number: U256 = + crate::blueprint_storage::read_next_blueprint_number(host)?; for signal in signals.0.iter() { let published_level = signal.published_level; let slot_indices = &signal.slot_indices; @@ -376,7 +376,7 @@ impl InputHandler for SequencerInput { fetch_and_parse_sequencer_blueprint_from_dal( host, ¶ms, - &head_level, + &next_blueprint_number, *slot_index, published_level, ) @@ -398,7 +398,7 @@ impl InputHandler for SequencerInput { } } - fn handle_deposit( + fn handle_deposit>( host: &mut Host, deposit: Deposit, delayed_inbox: &mut Self::Inbox, @@ -410,7 +410,7 @@ impl InputHandler for SequencerInput { } #[cfg_attr(feature = "benchmark", inline(never))] - fn handle_fa_deposit( + fn handle_fa_deposit>( host: &mut Host, fa_deposit: FaDeposit, delayed_inbox: &mut Self::Inbox, @@ -422,7 +422,7 @@ impl InputHandler for SequencerInput { } } -fn handle_transaction_chunk( +fn handle_transaction_chunk>( host: &mut Host, tx_hash: TransactionHash, i: u16, @@ -475,7 +475,7 @@ fn handle_transaction_chunk( Ok(None) } -fn handle_deposit( +fn handle_deposit>( host: &mut Host, deposit: Deposit, ) -> Result { @@ -488,7 +488,7 @@ fn handle_deposit( } #[cfg_attr(feature = "benchmark", inline(never))] -fn handle_fa_deposit( +fn handle_fa_deposit>( host: &mut Host, fa_deposit: FaDeposit, ) -> Result { @@ -500,7 +500,7 @@ fn handle_fa_deposit( }) } -fn force_kernel_upgrade(host: &mut impl Runtime) -> anyhow::Result<()> { +fn force_kernel_upgrade(host: &mut impl Runtime) -> anyhow::Result<()> { match upgrade::read_kernel_upgrade(host)? { Some(kernel_upgrade) => { let current_timestamp = read_last_info_per_level_timestamp(host)?.i64(); @@ -518,7 +518,7 @@ fn force_kernel_upgrade(host: &mut impl Runtime) -> anyhow::Result<()> { } pub fn handle_input( - host: &mut impl Runtime, + host: &mut impl Runtime, input: Input, inbox_content: &mut Mode::Inbox, garbage_collect_blocks: bool, @@ -534,7 +534,11 @@ pub fn handle_input( // New inbox level detected, remove all previous events. clear_events(host)?; if garbage_collect_blocks { - crate::block_storage::garbage_collect_blocks(host)?; + let mut host = RelativeRuntime { + host: &mut *host, + root: &tezos_evm_runtime::safe_storage::WORLD_STATE_PATH, + }; + crate::block_storage::garbage_collect_blocks(&mut host)?; } store_last_info_per_level_timestamp(host, info.info.predecessor_timestamp)?; store_l1_level(host, info.level)? @@ -555,7 +559,7 @@ enum ReadStatus { } #[allow(clippy::too_many_arguments)] -fn read_and_dispatch_input( +fn read_and_dispatch_input, Mode: Parsable + InputHandler>( host: &mut Host, smart_rollup_address: [u8; 20], tezos_contracts: &TezosContracts, @@ -601,7 +605,7 @@ fn read_and_dispatch_input( } } -pub fn read_proxy_inbox( +pub fn read_proxy_inbox>( host: &mut Host, smart_rollup_address: [u8; 20], tezos_contracts: &TezosContracts, @@ -667,7 +671,7 @@ pub enum StageOneStatus { } #[allow(clippy::too_many_arguments)] -pub fn read_sequencer_inbox( +pub fn read_sequencer_inbox>( host: &mut Host, smart_rollup_address: [u8; 20], tezos_contracts: &TezosContracts, @@ -684,7 +688,8 @@ pub fn read_sequencer_inbox( // during this kernel run. let mut inbox_is_empty = true; let limits = fetch_limits(host); - let head_level: Option = crate::block_storage::read_current_number(host).ok(); + let next_blueprint_number: U256 = + crate::blueprint_storage::read_next_blueprint_number(host)?; let mut parsing_context = SequencerParsingContext { sequencer, delayed_bridge, @@ -693,7 +698,7 @@ pub fn read_sequencer_inbox( .saturating_sub(TICKS_FOR_BLUEPRINT_INTERCEPT), dal_configuration: dal, buffer_transaction_chunks: None, - head_level, + next_blueprint_number, }; loop { // Checks there will be enough ticks to handle at least another chunk of @@ -752,7 +757,7 @@ pub fn read_sequencer_inbox( #[cfg(test)] mod tests { use super::*; - use crate::blueprint_storage::blueprint_path; + use crate::blueprint_storage::{blueprint_path, store_next_blueprint_number}; use crate::configuration::TezosContracts; use crate::dal_slot_import_signal::{ DalSlotIndicesList, DalSlotIndicesOfLevel, UnsignedDalSlotSignals, @@ -1365,10 +1370,7 @@ mod tests { // Prepare the host. let mut host = MockKernelHost::default(); let address = smart_rollup_address(); - crate::block_storage::internal_for_tests::store_current_number( - &mut host, head_level, - ) - .unwrap(); + store_next_blueprint_number(&mut host, head_level + 1).unwrap(); // Prepare the blueprint. let mut blueprint_bytes = @@ -1401,7 +1403,7 @@ mod tests { let path = blueprint_path(unsigned_blueprint.number).unwrap(); // The blueprint was valid if it was stored in the storage. - host.host.store_has(&path).unwrap().is_some() + host.store_has(&path).unwrap().is_some() } #[test] diff --git a/etherlink/kernel_evm/kernel/src/lib.rs b/etherlink/kernel_evm/kernel/src/lib.rs index ef13486007cdf4dd8bc7838411a3e55b0d1a8501..13729e10e69ed989df7d40cf3a43b6c111c134cb 100644 --- a/etherlink/kernel_evm/kernel/src/lib.rs +++ b/etherlink/kernel_evm/kernel/src/lib.rs @@ -28,7 +28,7 @@ use storage::{ use tezos_crypto_rs::hash::ContractKt1Hash; use tezos_evm_logging::{log, Level::*, Verbosity}; use tezos_evm_runtime::runtime::{KernelHost, Runtime}; -use tezos_evm_runtime::safe_storage::WORLD_STATE_PATH; +use tezos_evm_runtime::safe_storage::{SafeStorage, WORLD_STATE_PATH}; use tezos_smart_rollup::entrypoint; use tezos_smart_rollup::michelson::MichelsonUnit; use tezos_smart_rollup::outbox::{ @@ -87,7 +87,7 @@ const CONFIG: Config = Config { const KERNEL_VERSION: &str = env!("GIT_HASH"); -fn switch_to_public_rollup(host: &mut Host) -> Result<(), Error> { +fn switch_to_public_rollup(host: &mut impl Runtime) -> Result<(), Error> { if let Some(ValueType::Value) = host.store_has(&PRIVATE_FLAG_PATH)? { log!( host, @@ -107,7 +107,7 @@ fn switch_to_public_rollup(host: &mut Host) -> Result<(), Error> } } -pub fn stage_zero(host: &mut Host) -> Result { +pub fn stage_zero(host: &mut impl Runtime) -> Result { log!(host, Debug, "Entering stage zero."); init_storage_versioning(host)?; switch_to_public_rollup(host)?; @@ -118,8 +118,8 @@ pub fn stage_zero(host: &mut Host) -> Result( - host: &mut Host, +pub fn stage_one( + host: &mut impl Runtime, smart_rollup_address: [u8; 20], configuration: &mut Configuration, ) -> Result { @@ -129,7 +129,7 @@ pub fn stage_one( fetch_blueprints(host, smart_rollup_address, configuration) } -fn set_kernel_version(host: &mut Host) -> Result<(), Error> { +fn set_kernel_version(host: &mut impl Runtime) -> Result<(), Error> { match read_kernel_version(host) { Ok(kernel_version) => { if kernel_version != KERNEL_VERSION { @@ -141,14 +141,14 @@ fn set_kernel_version(host: &mut Host) -> Result<(), Error> { } } -fn init_storage_versioning(host: &mut Host) -> Result<(), Error> { +fn init_storage_versioning(host: &mut impl Runtime) -> Result<(), Error> { match host.store_read(&STORAGE_VERSION_PATH, 0, 0) { Ok(_) => Ok(()), Err(_) => store_storage_version(host, STORAGE_VERSION), } } -fn retrieve_chain_id(host: &mut Host) -> Result { +fn retrieve_chain_id(host: &mut impl Runtime) -> Result { match read_chain_id(host) { Ok(chain_id) => Ok(chain_id), Err(_) => { @@ -159,7 +159,7 @@ fn retrieve_chain_id(host: &mut Host) -> Result { } } -fn retrieve_minimum_base_fee_per_gas( +fn retrieve_minimum_base_fee_per_gas>( host: &mut Host, ) -> Result { match read_minimum_base_fee_per_gas(host) { @@ -173,7 +173,7 @@ fn retrieve_minimum_base_fee_per_gas( } #[cfg(test)] -fn retrieve_base_fee_per_gas( +fn retrieve_base_fee_per_gas>( host: &mut Host, minimum_base_fee_per_gas: U256, ) -> U256 { @@ -190,7 +190,7 @@ fn retrieve_base_fee_per_gas( } } -fn retrieve_da_fee(host: &mut Host) -> Result { +fn retrieve_da_fee>(host: &mut Host) -> Result { match read_da_fee(host) { Ok(da_fee) => Ok(da_fee), Err(_) => { @@ -202,7 +202,7 @@ fn retrieve_da_fee(host: &mut Host) -> Result { } #[cfg(test)] -fn retrieve_block_fees( +fn retrieve_block_fees>( host: &mut Host, ) -> Result { let minimum_base_fee_per_gas = retrieve_minimum_base_fee_per_gas(host)?; @@ -217,7 +217,7 @@ fn retrieve_block_fees( Ok(block_fees) } -pub fn main(host: &mut Host) -> Result<(), anyhow::Error> { +pub fn main(host: &mut impl Runtime) -> Result<(), anyhow::Error> { let chain_id = retrieve_chain_id(host).context("Failed to retrieve chain id")?; // We always start by doing the migration if needed. @@ -290,14 +290,18 @@ pub fn main(host: &mut Host) -> Result<(), anyhow::Error> { return Ok(()); }; - let trace_input = read_tracer_input(host)?; + let host = SafeStorage { + host, + root: WORLD_STATE_PATH, + }; + let trace_input = read_tracer_input(host.host)?; // Start processing blueprints #[cfg(not(feature = "benchmark-bypass-stage2"))] { log!(host, Debug, "Entering stage two."); if let block::ComputationResult::RebootNeeded = block::produce( - host, + host.host, chain_id, &mut configuration, sequencer_pool_address, @@ -305,7 +309,7 @@ pub fn main(host: &mut Host) -> Result<(), anyhow::Error> { ) .context("Failed during stage 2")? { - host.mark_for_reboot()?; + host.host.mark_for_reboot()?; } } @@ -318,7 +322,9 @@ pub fn main(host: &mut Host) -> Result<(), anyhow::Error> { } #[entrypoint::main] -pub fn kernel_loop(host: &mut Host) { +pub fn kernel_loop>( + host: &mut Host, +) { // In order to setup the temporary directory, we need to move something // from /evm to /tmp, so /evm must be non empty, this only happen // at the first run. @@ -413,7 +419,7 @@ mod tests { tx_common::EthereumTransactionCommon, }; use tezos_evm_runtime::runtime::MockKernelHost; - use tezos_evm_runtime::safe_storage::SafeStorage; + use tezos_evm_runtime::safe_storage::{SafeStorage, WORLD_STATE_PATH}; use tezos_evm_runtime::runtime::Runtime; use tezos_smart_rollup::michelson::ticket::FA2_1Ticket; @@ -442,7 +448,7 @@ mod tests { ) } - fn set_balance( + fn set_balance>( host: &mut Host, evm_account_storage: &mut EthereumAccountStorage, address: &H160, @@ -533,12 +539,18 @@ mod tests { fn test_reboot_during_block_production() { // init host let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; + let block_fees = dummy_block_fees(); crate::storage::store_minimum_base_fee_per_gas( &mut host, - DUMMY_BASE_FEE_PER_GAS.into(), + block_fees.minimum_base_fee_per_gas(), ) .unwrap(); + crate::storage::store_da_fee(&mut host, block_fees.da_fee_per_byte()).unwrap(); // sanity check: no current block assert!( @@ -556,6 +568,7 @@ mod tests { &sender, sender_initial_balance, ); + host.promote().unwrap(); // These transactions are generated with the loop.sol contract, which are: // - create the contract @@ -584,7 +597,7 @@ mod tests { ]; // Store blueprints for (i, blueprint) in proposals.into_iter().enumerate() { - store_inbox_blueprint_by_number(&mut host, blueprint, U256::from(i)) + store_inbox_blueprint_by_number(host.host, blueprint, U256::from(i)) .expect("Should have stored blueprint"); } // the upgrade mechanism should not start otherwise it will fail @@ -592,11 +605,9 @@ mod tests { preimage_hash: [0u8; PREIMAGE_HASH_SIZE], activation_timestamp: Timestamp::from(1_000_000i64), }; - crate::upgrade::store_kernel_upgrade(&mut host, &broken_kernel_upgrade) + crate::upgrade::store_kernel_upgrade(host.host, &broken_kernel_upgrade) .expect("Should be able to store kernel upgrade"); - let block_fees = dummy_block_fees(); - // Set the tick limit to 11bn ticks - 2bn, which is the old limit minus the safety margin. let limits = Limits { maximum_allowed_ticks: 9_000_000_000, @@ -608,16 +619,9 @@ mod tests { ..Configuration::default() }; - crate::storage::store_minimum_base_fee_per_gas( - &mut host, - block_fees.minimum_base_fee_per_gas(), - ) - .unwrap(); - crate::storage::store_da_fee(&mut host, block_fees.da_fee_per_byte()).unwrap(); - // If the upgrade is started, it should raise an error let computation_result = crate::block::produce( - &mut host, + host.host, DUMMY_CHAIN_ID, &mut configuration, None, @@ -626,12 +630,14 @@ mod tests { .expect("Should have produced"); // test there is a new block + host.start().unwrap(); assert_eq!( block_storage::read_current_number(&host) .expect("should have found a block number"), U256::zero(), "There should have been a block registered" ); + host.revert().unwrap(); // test reboot is set matches!( @@ -644,6 +650,10 @@ mod tests { fn load_block_fees_new() { // Arrange let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; // Act let result = crate::retrieve_block_fees(&mut host); @@ -661,11 +671,14 @@ mod tests { #[test] fn load_min_block_fees() { - let min_path = - RefPath::assert_from(b"/evm/world_state/fees/minimum_base_fee_per_gas"); + let min_path = RefPath::::assert_from(b"/fees/minimum_base_fee_per_gas"); // Arrange let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let min_base_fee = U256::from(17); tezos_storage::write_u256_le(&mut host, &min_path, min_base_fee).unwrap(); @@ -685,19 +698,24 @@ mod tests { fn test_xtz_withdrawal_applied() { // init host let mut host = MockKernelHost::default(); - let mut safe_storage = SafeStorage { host: &mut host }; + let mut safe_storage = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; safe_storage .store_write_all( &NATIVE_TOKEN_TICKETER_PATH, b"KT1DWVsu4Jtu2ficZ1qtNheGPunm5YVniegT", ) .unwrap(); - store_chain_id(&mut safe_storage, DUMMY_CHAIN_ID).unwrap(); + safe_storage.promote().unwrap(); + store_chain_id(safe_storage.host, DUMMY_CHAIN_ID).unwrap(); // run level in order to initialize outbox counter (by SOL message) let level = safe_storage.host.host.run_level(|_| ()); // provision sender account + safe_storage.start().unwrap(); let sender = H160::from_str("af1276cbb260bb13deddb4209ae99ae6e497f446").unwrap(); let sender_initial_balance = U256::from(10000000000000000000u64); let mut evm_account_storage = account_storage::init_account_storage().unwrap(); @@ -707,6 +725,7 @@ mod tests { &sender, sender_initial_balance, ); + safe_storage.promote().unwrap(); // cast calldata "withdraw_base58(string)" "tz1RjtZUVeLhADFHDL8UwDZA6vjWWhojpu5w": let data = hex::decode( @@ -763,12 +782,14 @@ mod tests { safe_storage.host.host.add_external(message); // run kernel twice to get to the stage with block creation: - main(&mut safe_storage).expect("Kernel error"); - main(&mut safe_storage).expect("Kernel error"); + main(safe_storage.host).expect("Kernel error"); + main(safe_storage.host).expect("Kernel error"); // verify outbox is not empty + safe_storage.start().unwrap(); let outbox = safe_storage.host.host.outbox_at(level + 1); assert!(!outbox.is_empty()); + safe_storage.revert().unwrap(); // check message contents: let message_bytes = &outbox[0]; @@ -805,13 +826,10 @@ mod tests { fn send_fa_deposit(enable_fa_bridge: bool) -> Option { // init host let mut mock_host = MockKernelHost::default(); - let mut safe_storage = SafeStorage { - host: &mut mock_host, - }; // enable FA bridge feature if enable_fa_bridge { - safe_storage + mock_host .store_write_all(&ENABLE_FA_BRIDGE, &[1u8]) .unwrap(); } @@ -849,12 +867,12 @@ mod tests { "KT1TxqZ8QtKvLu3V3JH7Gx58n7Co8pgtpQU5", "tz1P2Po7YM526ughEsRbY4oR9zaUPDZjxFrb", ); - safe_storage.host.host.add_transfer(payload, &metadata); + mock_host.host.add_transfer(payload, &metadata); // run kernel - main(&mut safe_storage).expect("Kernel error"); + main(&mut mock_host).expect("Kernel error"); // QUESTION: looks like to get to the stage with block creation we need to call main twice (maybe check blueprint instead?) [1] - main(&mut safe_storage).expect("Kernel error"); + main(&mut mock_host).expect("Kernel error"); // reconstruct ticket let ticket = FA2_1Ticket::new( @@ -876,7 +894,7 @@ mod tests { let deposit = FaDeposit { amount: 42.into(), proxy: Some(H160([2u8; 20])), - inbox_level: safe_storage.host.host.level(), // level not yet advanced + inbox_level: mock_host.host.level(), // level not yet advanced inbox_msg_id: 2, receiver: H160([1u8; 20]), ticket_hash: ticket_hash(&ticket).unwrap(), @@ -885,7 +903,14 @@ mod tests { let tx_hash = deposit.hash(&[0u8; 20]); // read transaction receipt - read_transaction_receipt_status(&mut safe_storage, &tx_hash.0).ok() + let mut safe_storage = SafeStorage { + host: &mut mock_host, + root: WORLD_STATE_PATH, + }; + safe_storage.start().unwrap(); + let res = read_transaction_receipt_status(&mut safe_storage, &tx_hash.0).ok(); + safe_storage.revert().unwrap(); + res } #[test] @@ -901,24 +926,25 @@ mod tests { fn send_fa_withdrawal(enable_fa_bridge: bool) -> Vec> { // init host let mut mock_host = MockKernelHost::default(); - let mut safe_storage = SafeStorage { - host: &mut mock_host, - }; // enable FA bridge feature if enable_fa_bridge { - safe_storage + mock_host .store_write_all(&ENABLE_FA_BRIDGE, &[1u8]) .unwrap(); } // run level in order to initialize outbox counter (by SOL message) - let level = safe_storage.host.host.run_level(|_| ()); + let level = mock_host.host.run_level(|_| ()); // provision sender account let sender = H160::from_str("af1276cbb260bb13deddb4209ae99ae6e497f446").unwrap(); let sender_initial_balance = U256::from(10000000000000000000u64); let mut evm_account_storage = account_storage::init_account_storage().unwrap(); + let mut safe_storage = SafeStorage { + host: &mut mock_host, + root: WORLD_STATE_PATH, + }; set_balance( &mut safe_storage, &mut evm_account_storage, @@ -939,6 +965,7 @@ mod tests { &sender, amount, ); + safe_storage.promote().unwrap(); // construct withdraw calldata let (ticketer, content) = ticket_id(&ticket); @@ -998,9 +1025,9 @@ mod tests { safe_storage.host.host.add_external(message); // run kernel - main(&mut safe_storage).expect("Kernel error"); + main(safe_storage.host).expect("Kernel error"); // QUESTION: looks like to get to the stage with block creation we need to call main twice (maybe check blueprint instead?) [2] - main(&mut safe_storage).expect("Kernel error"); + main(safe_storage.host).expect("Kernel error"); safe_storage.host.host.outbox_at(level + 1) } diff --git a/etherlink/kernel_evm/kernel/src/linked_list.rs b/etherlink/kernel_evm/kernel/src/linked_list.rs index 8ec7d95f9c9a0c5f1a83b0c2d3458c77dfd9da2a..94a6a84d4efa4dd7575d8dd09e3b02a9524b9408 100644 --- a/etherlink/kernel_evm/kernel/src/linked_list.rs +++ b/etherlink/kernel_evm/kernel/src/linked_list.rs @@ -22,7 +22,7 @@ where Elt: Encodable + Decodable + Clone, { /// Absolute path to queue - path: OwnedPath, + path: OwnedPath, /// None indicates an empty list pointers: Option>, _type: PhantomData<(Id, Elt)>, @@ -50,7 +50,7 @@ struct Pointer { fn decode_path( it: &mut RlpIterator, field_name: &'static str, -) -> Result { +) -> Result, rlp::DecoderError> { let path: Vec = decode_field(&next(it)?, field_name)?; OwnedPath::try_from(path).map_err(|_| DecoderError::Custom("not a path")) } @@ -150,7 +150,7 @@ where impl, Elt: Encodable + Decodable> Pointer { - fn pointer_path(id: &Id, prefix: &impl Path) -> Result { + fn pointer_path(id: &Id, prefix: &impl Path) -> Result> { let path = hex::encode(id); let path: Vec = format!("/{}/pointer", path).into(); let path = OwnedPath::try_from(path)?; @@ -161,12 +161,12 @@ impl, Elt: Encodable + Decodable> /// Path to the pointer /// /// This path is used when you want to read a pointer or to remove it. - fn path(&self, prefix: &impl Path) -> Result { + fn path(&self, prefix: &impl Path) -> Result> { Self::pointer_path(&self.id, prefix) } /// Path to the data held by the pointer. - fn data_path(&self, prefix: &impl Path) -> Result { + fn data_path(&self, prefix: &impl Path) -> Result> { let path = hex::encode(&self.id); let path: Vec = format!("/{}/data", path).into(); let path = OwnedPath::try_from(path)?; @@ -176,26 +176,38 @@ impl, Elt: Encodable + Decodable> fn save_data( &self, - host: &mut impl Runtime, - prefix: &impl Path, + host: &mut impl Runtime, + prefix: &impl Path, data: &Elt, ) -> Result<()> { let path = self.data_path(prefix)?; store_rlp(data, host, &path).context("cannot save the pointer's data") } - fn get_data(&self, host: &impl Runtime, prefix: &impl Path) -> Result { + fn get_data( + &self, + host: &impl Runtime, + prefix: &impl Path, + ) -> Result { let path = self.data_path(prefix)?; read_rlp(host, &path).context("cannot read the pointer's data") } /// Load the pointer from the durable storage - fn read(host: &impl Runtime, prefix: &impl Path, id: &Id) -> Result> { + fn read( + host: &impl Runtime, + prefix: &impl Path, + id: &Id, + ) -> Result> { read_optional_rlp(host, &Self::pointer_path(id, prefix)?) } /// Save the pointer in the durable storage - fn save(&self, host: &mut impl Runtime, prefix: &impl Path) -> Result<()> { + fn save( + &self, + host: &mut impl Runtime, + prefix: &impl Path, + ) -> Result<()> { store_rlp(self, host, &self.path(prefix)?) .context("cannot save pointer to storage") } @@ -203,8 +215,8 @@ impl, Elt: Encodable + Decodable> /// Removes the pointer and its data frm the durable storage. fn remove_with_data( &self, - host: &mut impl Runtime, - prefix: &impl Path, + host: &mut impl Runtime, + prefix: &impl Path, ) -> Result<()> { let path = hex::encode(&self.id); let path: Vec = format!("/{}", path).into(); @@ -224,7 +236,7 @@ where /// Load a list from the storage. /// If the list does not exist, a new empty list is created. /// Otherwise the existing list is read from the storage. - pub fn new(path: &impl Path, host: &impl Runtime) -> Result { + pub fn new(path: &impl Path, host: &impl Runtime) -> Result { let list = Self::read(host, path)?.unwrap_or(Self { path: path.into(), pointers: None, @@ -234,7 +246,7 @@ where } /// Path to the metadata that defines the list. - fn metadata_path(root: &impl Path) -> Result { + fn metadata_path(root: &impl Path) -> Result> { let meta_path: Vec = "/meta".into(); let meta_path = OwnedPath::try_from(meta_path)?; let path = concat(root, &meta_path)?; @@ -244,13 +256,13 @@ where /// Saves the LinkedList in the durable storage. /// /// Only save the back and front pointers. - fn save(&self, host: &mut impl Runtime) -> Result<()> { + fn save(&self, host: &mut impl Runtime) -> Result<()> { let path = Self::metadata_path(&self.path)?; store_rlp(self, host, &path).context("cannot save linked list from the storage") } /// Load the LinkedList from the durable storage. - fn read(host: &impl Runtime, path: &impl Path) -> Result> { + fn read(host: &impl Runtime, path: &impl Path) -> Result> { let path = Self::metadata_path(path)?; read_optional_rlp(host, &path) } @@ -266,7 +278,12 @@ where /// The Id has to be unique by element. /// The Id will be later use to retrieve the element /// (example: it can be the hash of the element). - pub fn push(&mut self, host: &mut impl Runtime, id: &Id, elt: &Elt) -> Result<()> { + pub fn push( + &mut self, + host: &mut impl Runtime, + id: &Id, + elt: &Elt, + ) -> Result<()> { // Check if the path already exist if (Pointer::read(host, &self.path, id)? as Option>).is_some() { return Ok(()); @@ -334,7 +351,7 @@ where /// Returns an element at a given index. /// /// Returns None if the element is not present - pub fn find(&self, host: &impl Runtime, id: &Id) -> Result> { + pub fn find(&self, host: &impl Runtime, id: &Id) -> Result> { let Some::>(pointer) = Pointer::read(host, &self.path, id)? else { return Ok(None); @@ -343,7 +360,11 @@ where } /// Removes and returns the element at position index within the vector. - pub fn remove(&mut self, host: &mut impl Runtime, id: &Id) -> Result> { + pub fn remove( + &mut self, + host: &mut impl Runtime, + id: &Id, + ) -> Result> { // Check if the list is empty let Some(LinkedListPointer { front, back }) = &self.pointers else { return Ok(None); @@ -466,7 +487,7 @@ where /// Returns the first element of the list /// or `None` if it is empty. - pub fn first(&self, host: &impl Runtime) -> Result> { + pub fn first(&self, host: &impl Runtime) -> Result> { let Some(LinkedListPointer { front, .. }) = &self.pointers else { return Ok(None); }; @@ -475,7 +496,7 @@ where /// Returns the first element of the list alongside its id /// or `None` if it is empty. - pub fn first_with_id(&self, host: &impl Runtime) -> Result> { + pub fn first_with_id(&self, host: &impl Runtime) -> Result> { let Some(LinkedListPointer { front, .. }) = &self.pointers else { return Ok(None); }; @@ -483,7 +504,7 @@ where } /// Removes the first element of the list and returns it - pub fn pop_first(&mut self, host: &mut impl Runtime) -> Result> { + pub fn pop_first(&mut self, host: &mut impl Runtime) -> Result> { let Some(LinkedListPointer { front, .. }) = &self.pointers else { return Ok(None); }; @@ -492,7 +513,7 @@ where } /// Deletes the entire list - pub fn delete(&mut self, host: &mut impl Runtime) -> Result<()> { + pub fn delete(&mut self, host: &mut impl Runtime) -> Result<()> { if host.store_has(&self.path)?.is_some() { host.store_delete(&self.path)?; }; @@ -539,7 +560,7 @@ mod tests { fn traverse_f( host: &MockKernelHost, - list_path: &OwnedPath, + list_path: &OwnedPath, pointer: Pointer, f: &dyn Fn(&Pointer) -> Option, ) -> (Pointer, usize) { diff --git a/etherlink/kernel_evm/kernel/src/migration.rs b/etherlink/kernel_evm/kernel/src/migration.rs index abc240d1a1103ed86bc83e3e10f614102d27a9d9..a748a6364173160e6984b07164532f99cd4af067 100644 --- a/etherlink/kernel_evm/kernel/src/migration.rs +++ b/etherlink/kernel_evm/kernel/src/migration.rs @@ -4,7 +4,6 @@ // // SPDX-License-Identifier: MIT -use crate::block_storage; use crate::blueprint_storage::{ blueprint_path, clear_all_blueprints, read_next_blueprint_number, }; @@ -23,8 +22,11 @@ use evm_execution::precompiles::WITHDRAWAL_ADDRESS; use evm_execution::NATIVE_TOKEN_TICKETER_PATH; use primitive_types::U256; use tezos_evm_logging::{log, Level::*}; -use tezos_evm_runtime::runtime::Runtime; -use tezos_smart_rollup::storage::path::RefPath; +use tezos_evm_runtime::{ + runtime::Runtime, + safe_storage::{SafeStorage, WORLD_STATE_PATH}, +}; +use tezos_smart_rollup::storage::path::{concat, RefPath}; use tezos_smart_rollup_host::path::OwnedPath; use tezos_smart_rollup_host::runtime::RuntimeError; @@ -45,7 +47,7 @@ const MAINNET_CHAIN_ID: u64 = 42793; #[allow(dead_code)] fn is_etherlink_network( - host: &impl Runtime, + host: &impl Runtime, expected_chain_id: u64, ) -> Result { match read_chain_id(host) { @@ -66,10 +68,10 @@ pub fn allow_path_not_found(res: Result<(), RuntimeError>) -> Result<(), Runtime } } -const TMP_NEXT_BLUEPRINT_PATH: RefPath = +const TMP_NEXT_BLUEPRINT_PATH: RefPath = RefPath::assert_from(b"/__tmp_next_blueprint_path"); -fn migrate_to( +fn migrate_to>( host: &mut Host, version: StorageVersion, ) -> anyhow::Result { @@ -81,7 +83,9 @@ fn migrate_to( StorageVersion::V12 => { let legacy_ticketer_path = RefPath::assert_from(b"/evm/ticketer"); if host.store_has(&legacy_ticketer_path)?.is_some() { - host.store_move(&legacy_ticketer_path, &NATIVE_TOKEN_TICKETER_PATH)?; + let native_token_ticketer_path = + concat(&WORLD_STATE_PATH, &NATIVE_TOKEN_TICKETER_PATH)?; + host.store_move(&legacy_ticketer_path, &native_token_ticketer_path)?; } Ok(MigrationStatus::Done) @@ -128,7 +132,9 @@ fn migrate_to( // // We need only the former. - let current_number = block_storage::read_current_number(host)?; + let current_number_path: RefPath = + RefPath::assert_from(b"/evm/world_state/blocks/current/number"); + let current_number = tezos_storage::read_u256_le(host, ¤t_number_path)?; let to_clean = U256::min( current_number + 1, evm_execution::storage::blocks::BLOCKS_STORED.into(), @@ -147,20 +153,30 @@ fn migrate_to( // might clean the zero account by accident, and the account // must always exist as the ticket table is stored in the // zero address. + let mut host = SafeStorage { + host, + root: WORLD_STATE_PATH, + }; let account_storage = init_account_storage()?; let account_path = account_path(&SYSTEM_ACCOUNT_ADDRESS)?; - let mut account = account_storage.get_or_create(host, &account_path)?; - account.increment_nonce(host)?; + let mut account = account_storage.get_or_create(&host, &account_path)?; + account.increment_nonce(&mut host)?; + host.promote()?; Ok(MigrationStatus::Done) } StorageVersion::V20 => { + let mut host = SafeStorage { + host, + root: WORLD_STATE_PATH, + }; let account_storage = init_account_storage()?; let mut withdrawal_precompiled = account_storage - .get_or_create(host, &account_path(&WITHDRAWAL_ADDRESS)?)?; - let balance = withdrawal_precompiled.balance(host)?; + .get_or_create(&host, &account_path(&WITHDRAWAL_ADDRESS)?)?; + let balance = withdrawal_precompiled.balance(&host)?; if !balance.is_zero() { - withdrawal_precompiled.balance_remove(host, balance)?; + withdrawal_precompiled.balance_remove(&mut host, balance)?; } + host.promote()?; Ok(MigrationStatus::Done) } StorageVersion::V21 => { @@ -212,7 +228,7 @@ fn migrate_to( Ok(MigrationStatus::Done) } StorageVersion::V24 => { - const EVM_BASE_FEE_PER_GAS: RefPath = + const EVM_BASE_FEE_PER_GAS: RefPath = RefPath::assert_from(b"/evm/world_state/fees/base_fee_per_gas"); host.store_delete(&EVM_BASE_FEE_PER_GAS)?; Ok(MigrationStatus::Done) @@ -239,7 +255,7 @@ fn migrate_to( // in an inconsistent storage. // /!\ // -fn migration(host: &mut Host) -> anyhow::Result { +fn migration>(host: &mut Host) -> anyhow::Result { match read_storage_version(host)?.next() { Some(next_version) => { let status = migrate_to(host, next_version)?; @@ -259,7 +275,7 @@ fn migration(host: &mut Host) -> anyhow::Result } } -pub fn storage_migration( +pub fn storage_migration>( host: &mut Host, ) -> Result { let migration_result = migration(host); diff --git a/etherlink/kernel_evm/kernel/src/parsing.rs b/etherlink/kernel_evm/kernel/src/parsing.rs index 813bcc848aabc57225157f3b6d35236d7655f79b..86e5850bf728c2d5cadcb5c9f16db5a12ceff45d 100644 --- a/etherlink/kernel_evm/kernel/src/parsing.rs +++ b/etherlink/kernel_evm/kernel/src/parsing.rs @@ -303,24 +303,21 @@ pub struct SequencerParsingContext { // Delayed inbox transactions may come in chunks. If the buffer is // [Some _] a chunked transaction is being parsed, pub buffer_transaction_chunks: Option, - // Head level of the chain, handling blueprints before the head is useless. - // It is optional to handle when the first block has not been produced - // yet. - pub head_level: Option, + // Number of the next expected blueprint, handling blueprints + // before this is useless. + pub next_blueprint_number: U256, } fn check_unsigned_blueprint_chunk( unsigned_seq_blueprint: UnsignedSequencerBlueprint, - head_level: &Option, + next_blueprint_number: &U256, ) -> SequencerBlueprintRes { if MAXIMUM_NUMBER_OF_CHUNKS < unsigned_seq_blueprint.nb_chunks { return SequencerBlueprintRes::InvalidNumberOfChunks; } - if let Some(head_level) = head_level { - if unsigned_seq_blueprint.number.le(head_level) { - return SequencerBlueprintRes::InvalidNumber; - } + if unsigned_seq_blueprint.number.lt(next_blueprint_number) { + return SequencerBlueprintRes::InvalidNumber; } SequencerBlueprintRes::SequencerBlueprint(unsigned_seq_blueprint) @@ -328,13 +325,13 @@ fn check_unsigned_blueprint_chunk( pub fn parse_unsigned_blueprint_chunk( bytes: &[u8], - head_level: &Option, + next_blueprint_number: &U256, ) -> SequencerBlueprintRes { // Parse an unsigned sequencer blueprint match UnsignedSequencerBlueprint::from_rlp_bytes(bytes).ok() { None => SequencerBlueprintRes::Unparsable, Some(unsigned_seq_blueprint) => { - check_unsigned_blueprint_chunk(unsigned_seq_blueprint, head_level) + check_unsigned_blueprint_chunk(unsigned_seq_blueprint, next_blueprint_number) } } } @@ -342,7 +339,7 @@ pub fn parse_unsigned_blueprint_chunk( pub fn parse_blueprint_chunk( bytes: &[u8], sequencer: &PublicKey, - head_level: &Option, + next_blueprint_number: &U256, ) -> SequencerBlueprintRes { // Parse the sequencer blueprint match SequencerBlueprint::from_rlp_bytes(bytes).ok() { @@ -350,7 +347,7 @@ pub fn parse_blueprint_chunk( Some(SequencerBlueprint { blueprint, signature, - }) => match check_unsigned_blueprint_chunk(blueprint, head_level) { + }) => match check_unsigned_blueprint_chunk(blueprint, next_blueprint_number) { SequencerBlueprintRes::SequencerBlueprint(unsigned_seq_blueprint) => { let bytes = unsigned_seq_blueprint.rlp_bytes().to_vec(); @@ -384,7 +381,11 @@ impl SequencerInput { .allocated_ticks .saturating_sub(TICKS_FOR_BLUEPRINT_CHUNK_SIGNATURE); - let res = parse_blueprint_chunk(bytes, &context.sequencer, &context.head_level); + let res = parse_blueprint_chunk( + bytes, + &context.sequencer, + &context.next_blueprint_number, + ); InputResult::Input(Input::ModeSpecific(Self::SequencerBlueprint(res))) } @@ -638,7 +639,7 @@ impl InputResult { } } - fn parse_fa_deposit( + fn parse_fa_deposit>( host: &mut Host, ticket: FA2_1Ticket, routing_info: MichelsonBytes, @@ -666,7 +667,7 @@ impl InputResult { } } - fn parse_deposit( + fn parse_deposit>( host: &mut Host, ticket: FA2_1Ticket, receiver: MichelsonBytes, @@ -695,7 +696,7 @@ impl InputResult { } #[allow(clippy::too_many_arguments)] - fn parse_internal_transfer( + fn parse_internal_transfer>( host: &mut Host, transfer: Transfer, smart_rollup_address: &[u8], @@ -778,7 +779,7 @@ impl InputResult { } #[allow(clippy::too_many_arguments)] - fn parse_internal( + fn parse_internal>( host: &mut Host, message: InternalInboxMessage, smart_rollup_address: &[u8], @@ -806,7 +807,7 @@ impl InputResult { } } - pub fn parse( + pub fn parse>( host: &mut Host, input: Message, smart_rollup_address: [u8; 20], @@ -897,7 +898,7 @@ pub mod tests { "edsk422LGdmDnai4Cya6csM6oFmgHpDQKUhatTURJRAY4h7NHNz9sz", ) .unwrap(); - let head_level: Option = Some(6.into()); + let next_blueprint_number: U256 = 7.into(); let blueprint = UnsignedSequencerBlueprint { chunk: vec![], @@ -910,7 +911,7 @@ pub mod tests { let res = parse_blueprint_chunk( &sequencer_signed_blueprint_chunk_bytes(&blueprint, sk.clone()), &pk, - &head_level, + &next_blueprint_number, ); assert!(matches!(res, SequencerBlueprintRes::SequencerBlueprint(_))); @@ -921,7 +922,7 @@ pub mod tests { let res = parse_blueprint_chunk( &sequencer_signed_blueprint_chunk_bytes(&blueprint, invalid_sk), &pk, - &head_level, + &next_blueprint_number, ); assert!(matches!(res, SequencerBlueprintRes::InvalidSignature)); @@ -934,7 +935,7 @@ pub mod tests { sk.clone(), ), &pk, - &head_level, + &next_blueprint_number, ); assert!(matches!(res, SequencerBlueprintRes::InvalidNumber)); @@ -947,7 +948,7 @@ pub mod tests { sk.clone(), ), &pk, - &head_level, + &next_blueprint_number, ); assert!(matches!(res, SequencerBlueprintRes::InvalidNumber)); } diff --git a/etherlink/kernel_evm/kernel/src/reveal_storage.rs b/etherlink/kernel_evm/kernel/src/reveal_storage.rs index 294670a9728202bf8700366474f7d2d8a3623396..cf31fab074e4f6c5ecfbd1a112e758c5bc880e2f 100644 --- a/etherlink/kernel_evm/kernel/src/reveal_storage.rs +++ b/etherlink/kernel_evm/kernel/src/reveal_storage.rs @@ -17,11 +17,11 @@ use tezos_smart_rollup_host::runtime::ValueType; /// chain into a new deployment. It is not tick-safe, and should obviously not be used in a /// production setup. -const CONFIG_PATH: RefPath = RefPath::assert_from(b"/__tmp/reveal_config"); +const CONFIG_PATH: RefPath = RefPath::assert_from(b"/__tmp/reveal_config"); #[derive(Debug)] struct Set { - to: OwnedPath, + to: OwnedPath, value: Vec, } @@ -36,8 +36,8 @@ impl Decodable for Set { let mut it = decoder.iter(); let to: Vec = decode_field(&next(&mut it)?, "to")?; - let to: RefPath = RefPath::assert_from(&to); - let to: OwnedPath = to.into(); + let to: RefPath = RefPath::assert_from(&to); + let to: OwnedPath = to.into(); let value: Vec = decode_field(&next(&mut it)?, "value")?; Ok(Self { to, value }) @@ -54,7 +54,7 @@ impl Decodable for Sets { } } -pub fn is_revealed_storage(host: &impl Runtime) -> bool { +pub fn is_revealed_storage(host: &impl Runtime) -> bool { matches!( host.store_has(&CONFIG_PATH).unwrap_or_default(), Some(ValueType::Value) @@ -62,7 +62,7 @@ pub fn is_revealed_storage(host: &impl Runtime) -> bool { } pub fn reveal_storage( - host: &mut impl Runtime, + host: &mut impl Runtime, sequencer: Option, admin: Option, ) { diff --git a/etherlink/kernel_evm/kernel/src/simulation.rs b/etherlink/kernel_evm/kernel/src/simulation.rs index 9f45101ee2543f8f28c00bdde1a52fc81f36e2d1..f49599f3f1bff574ec46fd1addaf7e124ee27bae 100644 --- a/etherlink/kernel_evm/kernel/src/simulation.rs +++ b/etherlink/kernel_evm/kernel/src/simulation.rs @@ -36,7 +36,9 @@ use tezos_ethereum::rlp_helpers::{ }; use tezos_ethereum::transaction::TransactionObject; use tezos_evm_logging::{log, Level::*}; -use tezos_evm_runtime::runtime::Runtime; +use tezos_evm_runtime::{ + relative_runtime::RelativeRuntime, runtime::Runtime, safe_storage::WORLD_STATE_PATH, +}; use tezos_smart_rollup::types::Timestamp; // SIMULATION/SIMPLE/RLP_ENCODED_SIMULATION @@ -364,16 +366,21 @@ impl Evaluation { } /// Execute the simulation - pub fn run( + pub fn run>( &self, host: &mut Host, tracer_input: Option, enable_fa_withdrawals: bool, ) -> Result, Error> { let chain_id = retrieve_chain_id(host)?; - let minimum_base_fee_per_gas = crate::retrieve_minimum_base_fee_per_gas(host)?; - let da_fee = crate::retrieve_da_fee(host)?; let coinbase = read_sequencer_pool_address(host).unwrap_or_default(); + let mut host = RelativeRuntime { + root: &WORLD_STATE_PATH, + host, + }; + let minimum_base_fee_per_gas = + crate::retrieve_minimum_base_fee_per_gas(&mut host)?; + let da_fee = crate::retrieve_da_fee(&mut host)?; let mut evm_account_storage = account_storage::init_account_storage() .map_err(|_| Error::Storage(StorageError::AccountInitialisation))?; @@ -388,12 +395,12 @@ impl Evaluation { if let Some(value) = self.value { if from.is_zero() { let mut account = - evm_account_storage.get_or_create(host, &account_path(&from)?)?; - account.balance_add(host, value)?; + evm_account_storage.get_or_create(&host, &account_path(&from)?)?; + account.balance_add(&mut host, value)?; } } - let constants = match block_storage::read_current(host) { + let constants = match block_storage::read_current(&mut host) { Ok(block) => { // Timestamp is taken from the simulation caller if provided. // If the timestamp is missing, because of an older evm-node, @@ -427,7 +434,7 @@ impl Evaluation { .map(|timestamp| U256::from(timestamp.as_u64())) .unwrap_or_else(|| { U256::from( - read_last_info_per_level_timestamp(host) + read_last_info_per_level_timestamp(host.host) .unwrap_or(Timestamp::from(0)) .as_u64(), ) @@ -446,9 +453,9 @@ impl Evaluation { } }; - let precompiles = precompiles::precompile_set::(enable_fa_withdrawals); + let precompiles = precompiles::precompile_set(enable_fa_withdrawals); let tx_data_size = self.data.len() as u64; - let limits = fetch_limits(host); + let limits = fetch_limits(host.host); let allocated_ticks = tick_model::estimate_remaining_ticks_for_transaction_execution( limits.maximum_allowed_ticks, @@ -463,7 +470,7 @@ impl Evaluation { }; match run_transaction( - host, + &mut host, &constants, &mut evm_account_storage, &precompiles, @@ -581,7 +588,7 @@ impl Input { } } -fn read_chunks( +fn read_chunks>( host: &mut Host, num_chunks: u16, ) -> Result { @@ -601,14 +608,14 @@ fn read_chunks( Ok(buffer.as_slice().try_into()?) } -fn read_input(host: &mut Host) -> Result { +fn read_input>(host: &mut Host) -> Result { match host.read_input()? { Some(input) => Ok(Input::parse(input.as_ref())), None => Ok(Input::Unparsable), } } -fn parse_inbox(host: &mut Host) -> Result { +fn parse_inbox>(host: &mut Host) -> Result { // we just received simulation tag // next message is either a simulation or the nb of chunks needed match read_input(host)? { @@ -631,7 +638,7 @@ impl VersionedEncoding for SimulationResult } } -pub fn start_simulation_mode( +pub fn start_simulation_mode>( host: &mut Host, enable_fa_withdrawals: bool, ) -> Result<(), anyhow::Error> { @@ -651,7 +658,7 @@ mod tests { use primitive_types::H256; use tezos_ethereum::{block::BlockConstants, tx_signature::TxSignature}; - use tezos_evm_runtime::runtime::MockKernelHost; + use tezos_evm_runtime::{runtime::MockKernelHost, safe_storage::SafeStorage}; use crate::{retrieve_block_fees, retrieve_chain_id}; @@ -727,14 +734,14 @@ mod tests { const DUMMY_ALLOCATED_TICKS: u64 = 1_000_000_000; - fn create_contract(host: &mut Host) -> H160 + fn create_contract(host: &mut SafeStorage<&mut Host>) -> H160 where - Host: Runtime, + Host: Runtime, { let timestamp = - read_last_info_per_level_timestamp(host).unwrap_or(Timestamp::from(0)); + read_last_info_per_level_timestamp(host.host).unwrap_or(Timestamp::from(0)); let timestamp = U256::from(timestamp.as_u64()); - let chain_id = retrieve_chain_id(host); + let chain_id = retrieve_chain_id(host.host); assert!(chain_id.is_ok(), "chain_id should be defined"); let block_fees = retrieve_block_fees(host); assert!(chain_id.is_ok(), "chain_id should be defined"); @@ -746,7 +753,7 @@ mod tests { crate::block::GAS_LIMIT, H160::zero(), ); - let precompiles = precompiles::precompile_set::(false); + let precompiles = precompiles::precompile_set(false); let mut evm_account_storage = account_storage::init_account_storage().unwrap(); let callee = None; @@ -790,7 +797,12 @@ mod tests { fn simulation_result() { // setup let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let new_address = create_contract(&mut host); + host.promote().unwrap(); // run evaluation num let evaluation = Evaluation { @@ -803,7 +815,7 @@ mod tests { with_da_fees: false, timestamp: None, }; - let outcome = evaluation.run(&mut host, None, false); + let outcome = evaluation.run(host.host, None, false); assert!(outcome.is_ok(), "evaluation should have succeeded"); let outcome = outcome.unwrap(); @@ -829,7 +841,7 @@ mod tests { with_da_fees: false, timestamp: None, }; - let outcome = evaluation.run(&mut host, None, false); + let outcome = evaluation.run(host.host, None, false); assert!(outcome.is_ok(), "simulation should have succeeded"); let outcome = outcome.unwrap(); @@ -848,7 +860,12 @@ mod tests { fn evaluation_result_no_gas() { // setup let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let new_address = create_contract(&mut host); + host.promote().unwrap(); // run evaluation num let evaluation = Evaluation { @@ -861,7 +878,7 @@ mod tests { with_da_fees: false, timestamp: None, }; - let outcome = evaluation.run(&mut host, None, false); + let outcome = evaluation.run(host.host, None, false); assert!(outcome.is_ok(), "evaluation should have succeeded"); let outcome = outcome.unwrap(); @@ -916,6 +933,10 @@ mod tests { fn parse_simulation2() { // setup let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let new_address = create_contract(&mut host); let to = Some(new_address); diff --git a/etherlink/kernel_evm/kernel/src/stage_one.rs b/etherlink/kernel_evm/kernel/src/stage_one.rs index 92574fc546711adcaaab032c410f06887f984720..88477b8691b0f43617fd10ab23f642fa94cd3d3a 100644 --- a/etherlink/kernel_evm/kernel/src/stage_one.rs +++ b/etherlink/kernel_evm/kernel/src/stage_one.rs @@ -16,14 +16,13 @@ use crate::storage::read_last_info_per_level_timestamp; use anyhow::Ok; use std::ops::Add; use tezos_crypto_rs::hash::ContractKt1Hash; -use tezos_ethereum::block::L2Block; use tezos_evm_logging::{log, Level::*}; use tezos_evm_runtime::runtime::Runtime; use tezos_smart_rollup_encoding::public_key::PublicKey; use tezos_smart_rollup_encoding::timestamp::Timestamp; use tezos_smart_rollup_host::metadata::RAW_ROLLUP_ADDRESS_SIZE; -pub fn fetch_proxy_blueprints( +pub fn fetch_proxy_blueprints>( host: &mut Host, smart_rollup_address: [u8; RAW_ROLLUP_ADDRESS_SIZE], tezos_contracts: &TezosContracts, @@ -51,7 +50,7 @@ pub fn fetch_proxy_blueprints( } } -fn fetch_delayed_transactions( +fn fetch_delayed_transactions>( host: &mut Host, delayed_inbox: &mut DelayedInbox, ) -> anyhow::Result<()> { @@ -69,11 +68,8 @@ fn fetch_delayed_transactions( timed_out.len() ); - let timestamp = match crate::block_storage::read_current(host) { - Result::Ok(L2Block { - timestamp: head_timestamp, - .. - }) => { + let timestamp = match crate::blueprint_storage::read_last_block_timestamp(host) { + Result::Ok(head_timestamp) => { // Timestamp has to be at least equal or greater than previous timestamp. // If it's not the case, we fallback and take the previous block timestamp. std::cmp::max(timestamp, head_timestamp) @@ -107,7 +103,7 @@ fn fetch_delayed_transactions( } #[allow(clippy::too_many_arguments)] -fn fetch_sequencer_blueprints( +fn fetch_sequencer_blueprints>( host: &mut Host, smart_rollup_address: [u8; RAW_ROLLUP_ADDRESS_SIZE], tezos_contracts: &TezosContracts, @@ -147,7 +143,7 @@ fn fetch_sequencer_blueprints( // Never inlined when the kernel is compiled for benchmarks, to ensure the // function is visible in the profiling results. #[cfg_attr(feature = "benchmark", inline(never))] -pub fn fetch_blueprints( +pub fn fetch_blueprints>( host: &mut Host, smart_rollup_address: [u8; RAW_ROLLUP_ADDRESS_SIZE], config: &mut Configuration, @@ -195,7 +191,10 @@ mod tests { use rlp::Encodable; use tezos_crypto_rs::hash::{HashTrait, SecretKeyEd25519, UnknownSignature}; use tezos_data_encoding::types::Bytes; - use tezos_evm_runtime::runtime::MockKernelHost; + use tezos_evm_runtime::{ + runtime::MockKernelHost, + safe_storage::{SafeStorage, WORLD_STATE_PATH}, + }; use tezos_smart_rollup::{ michelson::{ ticket::FA2_1Ticket, MichelsonBytes, MichelsonOption, MichelsonOr, @@ -212,7 +211,9 @@ mod tests { use crate::{ block_storage::internal_for_tests::store_current_number, - blueprint_storage::read_next_blueprint, dal::tests::*, parsing::RollupType, + blueprint_storage::{read_next_blueprint, store_next_blueprint_number}, + dal::tests::*, + parsing::RollupType, }; use super::*; @@ -366,7 +367,7 @@ mod tests { } } - fn delayed_inbox_is_empty( + fn delayed_inbox_is_empty>( conf: &Configuration, host: &mut Host, ) -> bool { @@ -481,7 +482,7 @@ mod tests { fetch_blueprints(&mut host, DEFAULT_SR_ADDRESS, &mut conf).expect("fetch failed"); // The dummy chunk in the inbox is registered at block 10 - store_current_number(&mut host, U256::from(9)).unwrap(); + store_next_blueprint_number(&mut host, U256::from(10)).unwrap(); if read_next_blueprint(&mut host, &mut conf) .expect("Blueprint reading shouldn't fail") .0 @@ -553,8 +554,12 @@ mod tests { }; // The dummy chunk in the inbox is registered at block 10 + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; store_current_number(&mut host, U256::from(9)).unwrap(); - if read_next_blueprint(&mut host, &mut conf) + if read_next_blueprint(host.host, &mut conf) .expect("Blueprint reading shouldn't fail") .0 .is_some() diff --git a/etherlink/kernel_evm/kernel/src/storage.rs b/etherlink/kernel_evm/kernel/src/storage.rs index 50bfe821a69650048dfdc36d68adf602b3973466..46780d1bc2b217b3714bc67da03e674ded793ee1 100644 --- a/etherlink/kernel_evm/kernel/src/storage.rs +++ b/etherlink/kernel_evm/kernel/src/storage.rs @@ -73,120 +73,124 @@ impl StorageVersion { pub const STORAGE_VERSION: StorageVersion = StorageVersion::V24; -pub const PRIVATE_FLAG_PATH: RefPath = RefPath::assert_from(b"/evm/remove_whitelist"); +pub const PRIVATE_FLAG_PATH: RefPath = + RefPath::assert_from(b"/evm/remove_whitelist"); -pub const STORAGE_VERSION_PATH: RefPath = RefPath::assert_from(b"/evm/storage_version"); +pub const STORAGE_VERSION_PATH: RefPath = + RefPath::assert_from(b"/evm/storage_version"); -const KERNEL_VERSION_PATH: RefPath = RefPath::assert_from(b"/evm/kernel_version"); +const KERNEL_VERSION_PATH: RefPath = RefPath::assert_from(b"/evm/kernel_version"); -pub const ADMIN: RefPath = RefPath::assert_from(b"/evm/admin"); -pub const SEQUENCER_GOVERNANCE: RefPath = +pub const ADMIN: RefPath = RefPath::assert_from(b"/evm/admin"); +pub const SEQUENCER_GOVERNANCE: RefPath = RefPath::assert_from(b"/evm/sequencer_governance"); -pub const KERNEL_GOVERNANCE: RefPath = RefPath::assert_from(b"/evm/kernel_governance"); -pub const KERNEL_SECURITY_GOVERNANCE: RefPath = +pub const KERNEL_GOVERNANCE: RefPath = + RefPath::assert_from(b"/evm/kernel_governance"); +pub const KERNEL_SECURITY_GOVERNANCE: RefPath = RefPath::assert_from(b"/evm/kernel_security_governance"); -pub const DELAYED_BRIDGE: RefPath = RefPath::assert_from(b"/evm/delayed_bridge"); +pub const DELAYED_BRIDGE: RefPath = RefPath::assert_from(b"/evm/delayed_bridge"); -pub const MAXIMUM_ALLOWED_TICKS: RefPath = +pub const MAXIMUM_ALLOWED_TICKS: RefPath = RefPath::assert_from(b"/evm/maximum_allowed_ticks"); -pub const MAXIMUM_GAS_PER_TRANSACTION: RefPath = +pub const MAXIMUM_GAS_PER_TRANSACTION: RefPath = RefPath::assert_from(b"/evm/maximum_gas_per_transaction"); // Path to the block in progress, used between reboots -const EVM_BLOCK_IN_PROGRESS: RefPath = - RefPath::assert_from(b"/evm/world_state/blocks/in_progress"); +const EVM_BLOCK_IN_PROGRESS: RefPath = + RefPath::assert_from(b"/blocks/in_progress"); -const EVENTS: RefPath = RefPath::assert_from(b"/evm/events"); +const EVENTS: RefPath = RefPath::assert_from(b"/evm/events"); -pub const EVM_TRANSACTIONS_RECEIPTS: RefPath = - RefPath::assert_from(b"/evm/world_state/transactions_receipts"); +pub const EVM_TRANSACTIONS_RECEIPTS: RefPath = + RefPath::assert_from(b"/transactions_receipts"); -pub const EVM_TRANSACTIONS_OBJECTS: RefPath = - RefPath::assert_from(b"/evm/world_state/transactions_objects"); +pub const EVM_TRANSACTIONS_OBJECTS: RefPath = + RefPath::assert_from(b"/transactions_objects"); -const EVM_CHAIN_ID: RefPath = RefPath::assert_from(b"/evm/chain_id"); +const EVM_CHAIN_ID: RefPath = RefPath::assert_from(b"/evm/chain_id"); -const EVM_MINIMUM_BASE_FEE_PER_GAS: RefPath = - RefPath::assert_from(b"/evm/world_state/fees/minimum_base_fee_per_gas"); -const EVM_DA_FEE: RefPath = - RefPath::assert_from(b"/evm/world_state/fees/da_fee_per_byte"); -const TICK_BACKLOG_PATH: RefPath = RefPath::assert_from(b"/evm/world_state/fees/backlog"); -const TICK_BACKLOG_TIMESTAMP_PATH: RefPath = - RefPath::assert_from(b"/evm/world_state/fees/last_timestamp"); +const EVM_MINIMUM_BASE_FEE_PER_GAS: RefPath = + RefPath::assert_from(b"/fees/minimum_base_fee_per_gas"); +const EVM_DA_FEE: RefPath = RefPath::assert_from(b"/fees/da_fee_per_byte"); +const TICK_BACKLOG_PATH: RefPath = RefPath::assert_from(b"/fees/backlog"); +const TICK_BACKLOG_TIMESTAMP_PATH: RefPath = + RefPath::assert_from(b"/fees/last_timestamp"); /// The sequencer pool is the designated account that the data-availability fees are sent to. /// /// This may be updated by the governance mechanism over time. If it is not set, the data-availability /// fees are instead burned. -pub const SEQUENCER_POOL_PATH: RefPath = +pub const SEQUENCER_POOL_PATH: RefPath = RefPath::assert_from(b"/evm/sequencer_pool_address"); /// Path to the last L1 level seen. -const EVM_L1_LEVEL: RefPath = RefPath::assert_from(b"/evm/l1_level"); +const EVM_L1_LEVEL: RefPath = RefPath::assert_from(b"/evm/l1_level"); -const EVM_BURNED_FEES: RefPath = RefPath::assert_from(b"/evm/world_state/fees/burned"); +const EVM_BURNED_FEES: RefPath = RefPath::assert_from(b"/fees/burned"); /// Path to the last info per level timestamp seen. -const EVM_INFO_PER_LEVEL_TIMESTAMP: RefPath = +const EVM_INFO_PER_LEVEL_TIMESTAMP: RefPath = RefPath::assert_from(b"/evm/info_per_level/timestamp"); /// Path to the number of timestamps read, use to compute the average block time. -const EVM_INFO_PER_LEVEL_STATS_NUMBERS: RefPath = +const EVM_INFO_PER_LEVEL_STATS_NUMBERS: RefPath = RefPath::assert_from(b"/evm/info_per_level/stats/numbers"); /// Path to the sum of distance between blocks, used to compute the average block time. -const EVM_INFO_PER_LEVEL_STATS_TOTAL: RefPath = +const EVM_INFO_PER_LEVEL_STATS_TOTAL: RefPath = RefPath::assert_from(b"/evm/info_per_level/stats/total"); -pub const SIMULATION_RESULT: RefPath = RefPath::assert_from(b"/evm/simulation_result"); +pub const SIMULATION_RESULT: RefPath = + RefPath::assert_from(b"/evm/simulation_result"); // Path to the number of seconds until delayed txs are timed out. -const EVM_DELAYED_INBOX_TIMEOUT: RefPath = +const EVM_DELAYED_INBOX_TIMEOUT: RefPath = RefPath::assert_from(b"/evm/delayed_inbox_timeout"); // Path to the number of l1 levels that need to pass for a // delayed tx to be timed out. -const EVM_DELAYED_INBOX_MIN_LEVELS: RefPath = +const EVM_DELAYED_INBOX_MIN_LEVELS: RefPath = RefPath::assert_from(b"/evm/delayed_inbox_min_levels"); // Path to the tz1 administrating the sequencer. If there is nothing // at this path, the kernel is in proxy mode. -pub const SEQUENCER: RefPath = RefPath::assert_from(b"/evm/sequencer"); +pub const SEQUENCER: RefPath = RefPath::assert_from(b"/evm/sequencer"); // Path to the DAL feature flag. If there is nothing at this path, DAL // is not used. -pub const ENABLE_DAL: RefPath = RefPath::assert_from(b"/evm/feature_flags/enable_dal"); +pub const ENABLE_DAL: RefPath = + RefPath::assert_from(b"/evm/feature_flags/enable_dal"); // Path to the DAL slot indices to use. -pub const DAL_SLOTS: RefPath = RefPath::assert_from(b"/evm/dal_slots"); +pub const DAL_SLOTS: RefPath = RefPath::assert_from(b"/evm/dal_slots"); // Path where the input for the tracer is stored by the sequencer. -const TRACER_INPUT: RefPath = RefPath::assert_from(b"/evm/trace/input"); +const TRACER_INPUT: RefPath = RefPath::assert_from(b"/evm/trace/input"); // If this path contains a value, the fa bridge is enabled in the kernel. -pub const ENABLE_FA_BRIDGE: RefPath = +pub const ENABLE_FA_BRIDGE: RefPath = RefPath::assert_from(b"/evm/feature_flags/enable_fa_bridge"); // If the flag is set, the kernel consider that this is local evm node execution. -const EVM_NODE_FLAG: RefPath = RefPath::assert_from(b"/__evm_node"); +const EVM_NODE_FLAG: RefPath = RefPath::assert_from(b"/__evm_node"); -const MAX_BLUEPRINT_LOOKAHEAD_IN_SECONDS: RefPath = +const MAX_BLUEPRINT_LOOKAHEAD_IN_SECONDS: RefPath = RefPath::assert_from(b"/evm/max_blueprint_lookahead_in_seconds"); -pub fn receipt_path(receipt_hash: &TransactionHash) -> Result { +pub fn receipt_path(receipt_hash: &TransactionHash) -> Result, Error> { let hash = hex::encode(receipt_hash); let raw_receipt_path: Vec = format!("/{}", &hash).into(); let receipt_path = OwnedPath::try_from(raw_receipt_path)?; concat(&EVM_TRANSACTIONS_RECEIPTS, &receipt_path).map_err(Error::from) } -pub fn object_path(object_hash: &TransactionHash) -> Result { +pub fn object_path(object_hash: &TransactionHash) -> Result, Error> { let hash = hex::encode(object_hash); let raw_object_path: Vec = format!("/{}", &hash).into(); let object_path = OwnedPath::try_from(raw_object_path)?; concat(&EVM_TRANSACTIONS_OBJECTS, &object_path).map_err(Error::from) } -pub fn store_simulation_result( +pub fn store_simulation_result, T: Decodable + Encodable>( host: &mut Host, result: SimulationResult, ) -> Result<(), anyhow::Error> { @@ -199,7 +203,7 @@ pub fn store_simulation_result( // Never inlined when the kernel is compiled for benchmarks, to ensure the // function is visible in the profiling results. #[cfg_attr(feature = "benchmark", inline(never))] -pub fn store_transaction_receipt( +pub fn store_transaction_receipt>( host: &mut Host, receipt: &TransactionReceipt, ) -> Result { @@ -214,7 +218,7 @@ pub fn store_transaction_receipt( // Never inlined when the kernel is compiled for benchmarks, to ensure the // function is visible in the profiling results. #[cfg_attr(feature = "benchmark", inline(never))] -pub fn store_transaction_object( +pub fn store_transaction_object>( host: &mut Host, object: &TransactionObject, ) -> Result { @@ -230,11 +234,15 @@ pub fn store_transaction_object( Ok(encoded.len().try_into()?) } -const CHUNKED_TRANSACTIONS: RefPath = RefPath::assert_from(b"/chunked_transactions"); -const CHUNKED_TRANSACTION_NUM_CHUNKS: RefPath = RefPath::assert_from(b"/num_chunks"); -const CHUNKED_HASHES: RefPath = RefPath::assert_from(b"/chunk_hashes"); +const CHUNKED_TRANSACTIONS: RefPath = + RefPath::assert_from(b"/chunked_transactions"); +const CHUNKED_TRANSACTION_NUM_CHUNKS: RefPath = + RefPath::assert_from(b"/num_chunks"); +const CHUNKED_HASHES: RefPath = RefPath::assert_from(b"/chunk_hashes"); -pub fn chunked_transaction_path(tx_hash: &TransactionHash) -> Result { +pub fn chunked_transaction_path( + tx_hash: &TransactionHash, +) -> Result, Error> { let hash = hex::encode(tx_hash); let raw_chunked_transaction_path: Vec = format!("/{}", hash).into(); let chunked_transaction_path = OwnedPath::try_from(raw_chunked_transaction_path)?; @@ -242,15 +250,15 @@ pub fn chunked_transaction_path(tx_hash: &TransactionHash) -> Result Result { + chunked_transaction_path: &OwnedPath, +) -> Result, Error> { concat(chunked_transaction_path, &CHUNKED_TRANSACTION_NUM_CHUNKS).map_err(Error::from) } pub fn chunked_hash_transaction_path( chunked_hash: &[u8], - chunked_transaction_path: &OwnedPath, -) -> Result { + chunked_transaction_path: &OwnedPath, +) -> Result, Error> { let hash = hex::encode(chunked_hash); let raw_chunked_hash_key: Vec = format!("/{}", hash).into(); let chunked_hash_key = OwnedPath::try_from(raw_chunked_hash_key)?; @@ -259,17 +267,17 @@ pub fn chunked_hash_transaction_path( } pub fn transaction_chunk_path( - chunked_transaction_path: &OwnedPath, + chunked_transaction_path: &OwnedPath, i: u16, -) -> Result { +) -> Result, Error> { let raw_i_path: Vec = format!("/{}", i).into(); let i_path = OwnedPath::try_from(raw_i_path)?; concat(chunked_transaction_path, &i_path).map_err(Error::from) } -fn is_transaction_complete( +fn is_transaction_complete>( host: &mut Host, - chunked_transaction_path: &OwnedPath, + chunked_transaction_path: &OwnedPath, num_chunks: u16, ) -> Result { let n_subkeys = host.store_count_subkeys(chunked_transaction_path)? as u16; @@ -277,9 +285,9 @@ fn is_transaction_complete( Ok(n_subkeys >= num_chunks + 2) } -fn chunked_transaction_num_chunks_by_path( +fn chunked_transaction_num_chunks_by_path>( host: &mut Host, - chunked_transaction_path: &OwnedPath, + chunked_transaction_path: &OwnedPath, ) -> Result { let chunked_transaction_num_chunks_path = chunked_transaction_num_chunks_path(chunked_transaction_path)?; @@ -288,7 +296,7 @@ fn chunked_transaction_num_chunks_by_path( Ok(u16::from_le_bytes(buffer)) } -pub fn chunked_transaction_num_chunks( +pub fn chunked_transaction_num_chunks>( host: &mut Host, tx_hash: &TransactionHash, ) -> Result { @@ -296,9 +304,9 @@ pub fn chunked_transaction_num_chunks( chunked_transaction_num_chunks_by_path(host, &chunked_transaction_path) } -fn store_transaction_chunk_data( +fn store_transaction_chunk_data>( host: &mut Host, - transaction_chunk_path: &OwnedPath, + transaction_chunk_path: &OwnedPath, data: Vec, ) -> Result<(), Error> { match host.store_has(transaction_chunk_path)? { @@ -317,9 +325,9 @@ fn store_transaction_chunk_data( } } -pub fn read_transaction_chunk_data( +pub fn read_transaction_chunk_data>( host: &mut Host, - transaction_chunk_path: &OwnedPath, + transaction_chunk_path: &OwnedPath, ) -> Result, Error> { let data_size = host.store_value_size(transaction_chunk_path)?; @@ -338,9 +346,9 @@ pub fn read_transaction_chunk_data( } } -fn get_full_transaction( +fn get_full_transaction>( host: &mut Host, - chunked_transaction_path: &OwnedPath, + chunked_transaction_path: &OwnedPath, num_chunks: u16, ) -> Result, Error> { let mut buffer = Vec::new(); @@ -352,14 +360,14 @@ fn get_full_transaction( Ok(buffer) } -pub fn remove_chunked_transaction_by_path( +pub fn remove_chunked_transaction_by_path>( host: &mut Host, - path: &OwnedPath, + path: &OwnedPath, ) -> Result<(), Error> { host.store_delete(path).map_err(Error::from) } -pub fn remove_chunked_transaction( +pub fn remove_chunked_transaction>( host: &mut Host, tx_hash: &TransactionHash, ) -> Result<(), Error> { @@ -369,7 +377,7 @@ pub fn remove_chunked_transaction( /// Store the transaction chunk in the storage. Returns the full transaction /// if the last chunk to store is the last missing chunk. -pub fn store_transaction_chunk( +pub fn store_transaction_chunk>( host: &mut Host, tx_hash: &TransactionHash, i: u16, @@ -394,7 +402,7 @@ pub fn store_transaction_chunk( } } -pub fn create_chunked_transaction( +pub fn create_chunked_transaction>( host: &mut Host, tx_hash: &TransactionHash, num_chunks: u16, @@ -435,42 +443,47 @@ pub fn create_chunked_transaction( Ok(()) } -pub fn store_chain_id( +pub fn store_chain_id>( host: &mut Host, chain_id: U256, ) -> Result<(), Error> { write_u256_le(host, &EVM_CHAIN_ID, chain_id).map_err(Error::from) } -pub fn read_chain_id(host: &Host) -> Result { +pub fn read_chain_id>(host: &Host) -> Result { read_u256_le(host, &EVM_CHAIN_ID).map_err(Error::from) } -pub fn read_minimum_base_fee_per_gas(host: &Host) -> Result { +pub fn read_minimum_base_fee_per_gas>( + host: &Host, +) -> Result { read_u256_le(host, &EVM_MINIMUM_BASE_FEE_PER_GAS).map_err(Error::from) } -pub fn read_tick_backlog(host: &impl Runtime) -> Result { +pub fn read_tick_backlog(host: &impl Runtime) -> Result { read_u64_le(host, &TICK_BACKLOG_PATH).map_err(Error::from) } -pub fn store_tick_backlog(host: &mut impl Runtime, value: u64) -> Result<(), Error> { +pub fn store_tick_backlog( + host: &mut impl Runtime, + value: u64, +) -> Result<(), Error> { write_u64_le(host, &TICK_BACKLOG_PATH, value).map_err(Error::from) } -pub fn read_tick_backlog_timestamp(host: &impl Runtime) -> Result { +pub fn read_tick_backlog_timestamp(host: &impl Runtime) -> Result { read_u64_le(host, &TICK_BACKLOG_TIMESTAMP_PATH).map_err(Error::from) } pub fn store_tick_backlog_timestamp( - host: &mut impl Runtime, + host: &mut impl Runtime, value: u64, ) -> Result<(), Error> { write_u64_le(host, &TICK_BACKLOG_TIMESTAMP_PATH, value)?; Ok(()) } -pub fn store_minimum_base_fee_per_gas( +pub fn store_minimum_base_fee_per_gas>( host: &mut Host, price: U256, ) -> Result<(), Error> { @@ -478,18 +491,18 @@ pub fn store_minimum_base_fee_per_gas( } pub fn store_da_fee( - host: &mut impl Runtime, + host: &mut impl Runtime, base_fee_per_gas: U256, ) -> Result<(), Error> { write_u256_le(host, &EVM_DA_FEE, base_fee_per_gas).map_err(Error::from) } -pub fn read_da_fee(host: &impl Runtime) -> Result { +pub fn read_da_fee(host: &impl Runtime) -> Result { read_u256_le(host, &EVM_DA_FEE).map_err(Error::from) } pub fn update_burned_fees( - host: &mut impl Runtime, + host: &mut impl Runtime, burned_fee: U256, ) -> Result<(), Error> { let path = &EVM_BURNED_FEES; @@ -499,12 +512,12 @@ pub fn update_burned_fees( } #[cfg(test)] -pub fn read_burned_fees(host: &mut impl Runtime) -> U256 { +pub fn read_burned_fees(host: &mut impl Runtime) -> U256 { let path = &EVM_BURNED_FEES; read_u256_le(host, path).unwrap_or_else(|_| U256::zero()) } -pub fn read_sequencer_pool_address(host: &impl Runtime) -> Option { +pub fn read_sequencer_pool_address(host: &impl Runtime) -> Option { let mut bytes = [0; std::mem::size_of::()]; let Ok(20) = host.store_read_slice(&SEQUENCER_POOL_PATH, 0, bytes.as_mut_slice()) else { @@ -515,7 +528,7 @@ pub fn read_sequencer_pool_address(host: &impl Runtime) -> Option { } pub fn store_sequencer_pool_address( - host: &mut impl Runtime, + host: &mut impl Runtime, address: H160, ) -> Result<(), Error> { let bytes = address.to_fixed_bytes(); @@ -523,9 +536,9 @@ pub fn store_sequencer_pool_address( Ok(()) } -pub fn store_timestamp_path( +pub fn store_timestamp_path>( host: &mut Host, - path: &OwnedPath, + path: &impl Path, timestamp: &Timestamp, ) -> Result<(), Error> { host.store_write(path, ×tamp.i64().to_le_bytes(), 0)?; @@ -533,20 +546,23 @@ pub fn store_timestamp_path( } #[allow(dead_code)] -pub fn read_l1_level(host: &mut Host) -> Result { +pub fn read_l1_level>(host: &mut Host) -> Result { let mut buffer = [0u8; 4]; store_read_slice(host, &EVM_L1_LEVEL, &mut buffer, 4)?; let level = u32::from_le_bytes(buffer); Ok(level) } -pub fn store_l1_level(host: &mut Host, level: u32) -> Result<(), Error> { +pub fn store_l1_level>( + host: &mut Host, + level: u32, +) -> Result<(), Error> { host.store_write(&EVM_L1_LEVEL, &level.to_le_bytes(), 0)?; Ok(()) } -pub fn read_last_info_per_level_timestamp_stats( - host: &mut Host, +pub fn read_last_info_per_level_timestamp_stats>( + host: &Host, ) -> Result<(i64, i64), Error> { let mut buffer = [0u8; 8]; store_read_slice(host, &EVM_INFO_PER_LEVEL_STATS_NUMBERS, &mut buffer, 8)?; @@ -559,7 +575,7 @@ pub fn read_last_info_per_level_timestamp_stats( Ok((numbers, total)) } -fn store_info_per_level_stats( +fn store_info_per_level_stats>( host: &mut Host, new_timestamp: Timestamp, ) -> Result<(), Error> { @@ -577,17 +593,17 @@ fn store_info_per_level_stats( Ok(()) } -pub fn store_last_info_per_level_timestamp( +pub fn store_last_info_per_level_timestamp>( host: &mut Host, timestamp: Timestamp, ) -> Result<(), Error> { - store_timestamp_path(host, &EVM_INFO_PER_LEVEL_TIMESTAMP.into(), ×tamp)?; + store_timestamp_path(host, &EVM_INFO_PER_LEVEL_TIMESTAMP, ×tamp)?; store_info_per_level_stats(host, timestamp) } -pub fn read_timestamp_path( +pub fn read_timestamp_path>( host: &Host, - path: &OwnedPath, + path: &impl Path, ) -> Result { let mut buffer = [0u8; 8]; store_read_slice(host, path, &mut buffer, 8)?; @@ -595,40 +611,42 @@ pub fn read_timestamp_path( Ok(timestamp_as_i64.into()) } -pub fn read_last_info_per_level_timestamp( +pub fn read_last_info_per_level_timestamp>( host: &Host, ) -> Result { - read_timestamp_path(host, &EVM_INFO_PER_LEVEL_TIMESTAMP.into()) + read_timestamp_path(host, &EVM_INFO_PER_LEVEL_TIMESTAMP) } -pub fn read_admin(host: &mut Host) -> Option { +pub fn read_admin>(host: &mut Host) -> Option { read_b58_kt1(host, &ADMIN) } -pub fn read_sequencer_governance( +pub fn read_sequencer_governance>( host: &mut Host, ) -> Option { read_b58_kt1(host, &SEQUENCER_GOVERNANCE) } -pub fn read_kernel_governance(host: &mut Host) -> Option { +pub fn read_kernel_governance>( + host: &mut Host, +) -> Option { read_b58_kt1(host, &KERNEL_GOVERNANCE) } -pub fn read_kernel_security_governance( +pub fn read_kernel_security_governance>( host: &mut Host, ) -> Option { read_b58_kt1(host, &KERNEL_SECURITY_GOVERNANCE) } -pub fn read_maximum_allowed_ticks(host: &mut Host) -> Option { +pub fn read_maximum_allowed_ticks>(host: &mut Host) -> Option { read_u64_le(host, &MAXIMUM_ALLOWED_TICKS).ok() } /// Reads the maximum gas per transaction. If the value cannot found in the storage, /// we write the kernel default value in the storage. The value becomes accessible /// from outside the kernel. -pub fn read_or_set_maximum_gas_per_transaction( +pub fn read_or_set_maximum_gas_per_transaction>( host: &mut Host, ) -> anyhow::Result { match read_u64_le(host, &MAXIMUM_GAS_PER_TRANSACTION) { @@ -640,7 +658,7 @@ pub fn read_or_set_maximum_gas_per_transaction( } } -pub fn store_storage_version( +pub fn store_storage_version>( host: &mut Host, storage_version: StorageVersion, ) -> Result<(), Error> { @@ -649,7 +667,7 @@ pub fn store_storage_version( .map_err(Error::from) } -pub fn read_storage_version( +pub fn read_storage_version>( host: &mut Host, ) -> Result { match host.store_read_all(&STORAGE_VERSION_PATH) { @@ -665,7 +683,9 @@ pub fn read_storage_version( } } -pub fn read_kernel_version(host: &mut Host) -> Result { +pub fn read_kernel_version>( + host: &mut Host, +) -> Result { match host.store_read_all(&KERNEL_VERSION_PATH) { Ok(bytes) => { let kernel_version = @@ -676,7 +696,7 @@ pub fn read_kernel_version(host: &mut Host) -> Result( +pub fn store_kernel_version>( host: &mut Host, kernel_version: &str, ) -> Result<(), Error> { @@ -689,7 +709,7 @@ pub fn store_kernel_version( // Never inlined when the kernel is compiled for benchmarks, to ensure the // function is visible in the profiling results. #[cfg_attr(feature = "benchmark", inline(never))] -pub fn store_block_in_progress( +pub fn store_block_in_progress>( host: &mut Host, bip: &BlockInProgress, ) -> anyhow::Result<()> { @@ -709,7 +729,7 @@ pub fn store_block_in_progress( // Never inlined when the kernel is compiled for benchmarks, to ensure the // function is visible in the profiling results. #[cfg_attr(feature = "benchmark", inline(never))] -pub fn read_block_in_progress( +pub fn read_block_in_progress>( host: &Host, ) -> anyhow::Result> { let path = OwnedPath::from(EVM_BLOCK_IN_PROGRESS); @@ -732,12 +752,14 @@ pub fn read_block_in_progress( } } -pub fn delete_block_in_progress(host: &mut Host) -> anyhow::Result<()> { +pub fn delete_block_in_progress>( + host: &mut Host, +) -> anyhow::Result<()> { host.store_delete(&EVM_BLOCK_IN_PROGRESS) .context("Failed to delete block in progress") } -pub fn sequencer(host: &Host) -> anyhow::Result> { +pub fn sequencer>(host: &Host) -> anyhow::Result> { if host.store_has(&SEQUENCER)?.is_some() { let bytes = host.store_read_all(&SEQUENCER)?; let Ok(tz1_b58) = String::from_utf8(bytes) else { @@ -752,7 +774,7 @@ pub fn sequencer(host: &Host) -> anyhow::Result } } -pub fn enable_dal(host: &Host) -> anyhow::Result { +pub fn enable_dal>(host: &Host) -> anyhow::Result { if let Some(ValueType::Value) = host.store_has(&ENABLE_DAL)? { // When run from the EVM node, the DAL feature is always // considered as disabled. @@ -763,7 +785,7 @@ pub fn enable_dal(host: &Host) -> anyhow::Result { } } -pub fn dal_slots(host: &Host) -> anyhow::Result>> { +pub fn dal_slots>(host: &Host) -> anyhow::Result>> { if host.store_has(&DAL_SLOTS)?.is_some() { let bytes = host.store_read_all(&DAL_SLOTS)?; Ok(Some(bytes)) @@ -772,11 +794,11 @@ pub fn dal_slots(host: &Host) -> anyhow::Result>> } } -pub fn remove_sequencer(host: &mut Host) -> anyhow::Result<()> { +pub fn remove_sequencer>(host: &mut Host) -> anyhow::Result<()> { host.store_delete(&SEQUENCER).map_err(Into::into) } -pub fn store_sequencer( +pub fn store_sequencer>( host: &mut Host, public_key: &PublicKey, ) -> anyhow::Result<()> { @@ -785,7 +807,7 @@ pub fn store_sequencer( host.store_write_all(&SEQUENCER, bytes).map_err(Into::into) } -pub fn clear_events(host: &mut Host) -> anyhow::Result<()> { +pub fn clear_events>(host: &mut Host) -> anyhow::Result<()> { if host.store_has(&EVENTS)?.is_some() { host.store_delete(&EVENTS) .context("Failed to delete old events") @@ -794,14 +816,17 @@ pub fn clear_events(host: &mut Host) -> anyhow::Result<()> { } } -pub fn store_event(host: &mut Host, event: &Event) -> anyhow::Result<()> { +pub fn store_event>( + host: &mut Host, + event: &Event, +) -> anyhow::Result<()> { let index = IndexableStorage::new(&EVENTS)?; index .push_value(host, &event.rlp_bytes()) .map_err(Into::into) } -pub fn delayed_inbox_timeout(host: &Host) -> anyhow::Result { +pub fn delayed_inbox_timeout>(host: &Host) -> anyhow::Result { // The default timeout is 12 hours let default_timeout = 43200; if host.store_has(&EVM_DELAYED_INBOX_TIMEOUT)?.is_some() { @@ -828,7 +853,7 @@ pub fn delayed_inbox_timeout(host: &Host) -> anyhow::Result } } -pub fn delayed_inbox_min_levels(host: &Host) -> anyhow::Result { +pub fn delayed_inbox_min_levels>(host: &Host) -> anyhow::Result { let default_min_levels = 720; if host.store_has(&EVM_DELAYED_INBOX_MIN_LEVELS)?.is_some() { let mut buffer = [0u8; 4]; @@ -852,7 +877,7 @@ pub fn delayed_inbox_min_levels(host: &Host) -> anyhow::Result( +pub fn read_tracer_input>( host: &mut Host, ) -> anyhow::Result> { if let Some(ValueType::Value) = host.store_has(&TRACER_INPUT).map_err(Error::from)? { @@ -877,7 +902,7 @@ pub fn read_tracer_input( } } -pub fn is_enable_fa_bridge(host: &impl Runtime) -> anyhow::Result { +pub fn is_enable_fa_bridge(host: &impl Runtime) -> anyhow::Result { if let Some(ValueType::Value) = host.store_has(&ENABLE_FA_BRIDGE)? { Ok(true) } else { @@ -885,7 +910,7 @@ pub fn is_enable_fa_bridge(host: &impl Runtime) -> anyhow::Result { } } -pub fn evm_node_flag(host: &impl Runtime) -> anyhow::Result { +pub fn evm_node_flag(host: &impl Runtime) -> anyhow::Result { if let Some(ValueType::Value) = host.store_has(&EVM_NODE_FLAG)? { Ok(true) } else { @@ -893,7 +918,9 @@ pub fn evm_node_flag(host: &impl Runtime) -> anyhow::Result { } } -pub fn max_blueprint_lookahead_in_seconds(host: &impl Runtime) -> anyhow::Result { +pub fn max_blueprint_lookahead_in_seconds( + host: &impl Runtime, +) -> anyhow::Result { let bytes = host.store_read_all(&MAX_BLUEPRINT_LOOKAHEAD_IN_SECONDS)?; let bytes: [u8; 8] = bytes.as_slice().try_into()?; Ok(i64::from_le_bytes(bytes)) @@ -906,7 +933,7 @@ mod internal_for_tests { use tezos_ethereum::transaction::TransactionStatus; /// Reads status from the receipt in storage. - pub fn read_transaction_receipt_status( + pub fn read_transaction_receipt_status>( host: &mut Host, tx_hash: &TransactionHash, ) -> Result { @@ -915,7 +942,7 @@ mod internal_for_tests { } /// Reads a transaction receipt from storage. - pub fn read_transaction_receipt( + pub fn read_transaction_receipt>( host: &mut Host, tx_hash: &TransactionHash, ) -> Result { @@ -933,7 +960,7 @@ pub use internal_for_tests::*; /// /// This smart contract is used to submit transactions to the rollup /// when in sequencer mode -pub fn read_delayed_transaction_bridge( +pub fn read_delayed_transaction_bridge>( host: &Host, ) -> Option { read_b58_kt1(host, &DELAYED_BRIDGE) @@ -941,12 +968,19 @@ pub fn read_delayed_transaction_bridge( #[cfg(test)] mod tests { - use tezos_evm_runtime::runtime::MockKernelHost; + use tezos_evm_runtime::{ + runtime::MockKernelHost, + safe_storage::{SafeStorage, WORLD_STATE_PATH}, + }; #[test] fn update_burned_fees() { // Arrange let mut host = MockKernelHost::default(); + let mut host = SafeStorage { + host: &mut host, + root: WORLD_STATE_PATH, + }; let fst = 17.into(); let snd = 19.into(); diff --git a/etherlink/kernel_evm/kernel/src/upgrade.rs b/etherlink/kernel_evm/kernel/src/upgrade.rs index 90c413ff4f09414aba1aaf040cf26fec046f20e3..689fff9063ae4fecfebf9ed43fcda83edf429797 100644 --- a/etherlink/kernel_evm/kernel/src/upgrade.rs +++ b/etherlink/kernel_evm/kernel/src/upgrade.rs @@ -34,9 +34,10 @@ use tezos_smart_rollup_host::path::RefPath; use tezos_smart_rollup_installer_config::binary::promote::upgrade_reveal_flow; use tezos_storage::read_optional_rlp; -const KERNEL_UPGRADE: RefPath = RefPath::assert_from(b"/evm/kernel_upgrade"); -pub const KERNEL_ROOT_HASH: RefPath = RefPath::assert_from(b"/evm/kernel_root_hash"); -const SEQUENCER_UPGRADE: RefPath = RefPath::assert_from(b"/evm/sequencer_upgrade"); +const KERNEL_UPGRADE: RefPath = RefPath::assert_from(b"/evm/kernel_upgrade"); +pub const KERNEL_ROOT_HASH: RefPath = + RefPath::assert_from(b"/evm/kernel_root_hash"); +const SEQUENCER_UPGRADE: RefPath = RefPath::assert_from(b"/evm/sequencer_upgrade"); #[derive(Debug, PartialEq, Clone)] pub struct KernelUpgrade { @@ -79,7 +80,7 @@ impl Encodable for KernelUpgrade { } } -pub fn store_kernel_upgrade( +pub fn store_kernel_upgrade>( host: &mut Host, kernel_upgrade: &KernelUpgrade, ) -> anyhow::Result<()> { @@ -98,19 +99,19 @@ pub fn store_kernel_upgrade( } fn read_kernel_upgrade_at( - host: &impl Runtime, - path: &impl Path, + host: &impl Runtime, + path: &impl Path, ) -> anyhow::Result> { read_optional_rlp(host, path).context("Failed to decode kernel upgrade") } -pub fn read_kernel_upgrade( +pub fn read_kernel_upgrade>( host: &Host, ) -> anyhow::Result> { read_kernel_upgrade_at(host, &KERNEL_UPGRADE) } -pub fn upgrade( +pub fn upgrade>( host: &mut Host, root_hash: [u8; PREIMAGE_HASH_SIZE], ) -> anyhow::Result<()> { @@ -186,7 +187,7 @@ impl Encodable for SequencerUpgrade { } } -pub fn store_sequencer_upgrade( +pub fn store_sequencer_upgrade>( host: &mut Host, sequencer_upgrade: SequencerUpgrade, ) -> anyhow::Result<()> { @@ -204,19 +205,19 @@ pub fn store_sequencer_upgrade( .context("Failed to store sequencer upgrade") } -pub fn read_sequencer_upgrade( +pub fn read_sequencer_upgrade>( host: &Host, ) -> anyhow::Result> { let path = OwnedPath::from(SEQUENCER_UPGRADE); read_optional_rlp(host, &path).context("Failed to decode sequencer upgrade") } -fn delete_sequencer_upgrade(host: &mut Host) -> anyhow::Result<()> { +fn delete_sequencer_upgrade>(host: &mut Host) -> anyhow::Result<()> { host.store_delete(&SEQUENCER_UPGRADE) .context("Failed to delete sequencer upgrade") } -fn sequencer_upgrade( +fn sequencer_upgrade>( host: &mut Host, pool_address: H160, sequencer: &PublicKey, @@ -230,7 +231,9 @@ fn sequencer_upgrade( Ok(()) } -pub fn possible_sequencer_upgrade(host: &mut Host) -> anyhow::Result<()> { +pub fn possible_sequencer_upgrade>( + host: &mut Host, +) -> anyhow::Result<()> { let upgrade = read_sequencer_upgrade(host)?; if let Some(upgrade) = upgrade { let ipl_timestamp = storage::read_last_info_per_level_timestamp(host)?; diff --git a/etherlink/kernel_evm/runtime/src/internal_runtime.rs b/etherlink/kernel_evm/runtime/src/internal_runtime.rs index 495da6f232de6ec9090f3d53825af9309a540942..3e0277453dd08496a7c8190d4776983e3dbaad33 100644 --- a/etherlink/kernel_evm/runtime/src/internal_runtime.rs +++ b/etherlink/kernel_evm/runtime/src/internal_runtime.rs @@ -20,7 +20,7 @@ extern "C" { } pub trait InternalRuntime { - fn __internal_store_get_hash( + fn __internal_store_get_hash>( &mut self, path: &T, ) -> Result, RuntimeError>; @@ -30,14 +30,17 @@ pub trait InternalRuntime { // to the Runtime for the Kernel to use. // The path is optional to be able to get the hash // of the root directory. -pub trait ExtendedRuntime { - fn store_get_hash(&mut self, path: &T) -> Result, RuntimeError>; +pub trait ExtendedRuntime { + fn store_get_hash( + &mut self, + path: &impl Path, + ) -> Result, RuntimeError>; } pub struct InternalHost(); impl InternalRuntime for InternalHost { - fn __internal_store_get_hash( + fn __internal_store_get_hash>( &mut self, path: &T, ) -> Result, RuntimeError> { diff --git a/etherlink/kernel_evm/runtime/src/lib.rs b/etherlink/kernel_evm/runtime/src/lib.rs index fc7a995790bbb9a759793dba3bbb123002846145..dff1c105fdaf0fe2a56855d7165d83328abb066f 100644 --- a/etherlink/kernel_evm/runtime/src/lib.rs +++ b/etherlink/kernel_evm/runtime/src/lib.rs @@ -5,5 +5,6 @@ pub mod extensions; pub mod internal_runtime; pub mod mock_internal; +pub mod relative_runtime; pub mod runtime; pub mod safe_storage; diff --git a/etherlink/kernel_evm/runtime/src/mock_internal.rs b/etherlink/kernel_evm/runtime/src/mock_internal.rs index 37e820a7b50b29e67f0891f4cbd3e779353e1da6..7c6cf1156f834aeea7ba512c33ce869c42f4559a 100644 --- a/etherlink/kernel_evm/runtime/src/mock_internal.rs +++ b/etherlink/kernel_evm/runtime/src/mock_internal.rs @@ -8,7 +8,7 @@ use tezos_smart_rollup_host::path::Path; use tezos_smart_rollup_host::runtime::RuntimeError; pub struct MockInternal(); impl InternalRuntime for MockInternal { - fn __internal_store_get_hash( + fn __internal_store_get_hash>( &mut self, path: &T, ) -> Result, RuntimeError> { diff --git a/etherlink/kernel_evm/runtime/src/relative_runtime.rs b/etherlink/kernel_evm/runtime/src/relative_runtime.rs new file mode 100644 index 0000000000000000000000000000000000000000..9df2a42f6b0a93c425f55db5ce65c2ec520d7aa1 --- /dev/null +++ b/etherlink/kernel_evm/runtime/src/relative_runtime.rs @@ -0,0 +1,260 @@ +// SPDX-FileCopyrightText: 2023 Nomadic Labs +// +// SPDX-License-Identifier: MIT + +use crate::extensions::WithGas; +use crate::internal_runtime::{ExtendedRuntime, InternalRuntime}; +use crate::runtime::{Runtime, TracePath, TraceRuntime}; +use tezos_evm_logging::Verbosity; +use tezos_smart_rollup_core::PREIMAGE_HASH_SIZE; +use tezos_smart_rollup_host::dal_parameters::RollupDalParameters; +use tezos_smart_rollup_host::{ + input::Message, + metadata::RollupMetadata, + path::{concat, OwnedPath, Path, RefPath}, + runtime::{Runtime as SdkRuntime, RuntimeError, ValueType}, +}; + +pub struct RelativeRuntime { + pub root: &'static RefPath<'static, true>, + pub host: Host, +} + +impl RelativeRuntime { + pub fn absolute_path( + &self, + path: &impl Path, + ) -> Result, RuntimeError> { + concat(self.root, path).map_err(|_| RuntimeError::PathNotFound) + } +} + +impl> InternalRuntime for RelativeRuntime<&mut Host> { + fn __internal_store_get_hash>( + &mut self, + path: &P, + ) -> Result, RuntimeError> { + self.host.__internal_store_get_hash(path) + } +} + +impl> ExtendedRuntime for RelativeRuntime<&mut Host> { + #[inline(always)] + fn store_get_hash( + &mut self, + path: &impl Path, + ) -> Result, RuntimeError> { + let path = self.absolute_path(path)?; + self.__internal_store_get_hash(&path) + } +} + +impl> SdkRuntime for RelativeRuntime<&mut Host> { + #[inline(always)] + fn write_output(&mut self, from: &[u8]) -> Result<(), RuntimeError> { + self.host.write_output(from) + } + + #[inline(always)] + fn write_debug(&self, msg: &str) { + self.host.write_debug(msg) + } + + #[inline(always)] + fn read_input(&mut self) -> Result, RuntimeError> { + self.host.read_input() + } + + #[inline(always)] + fn store_has>( + &self, + path: &P, + ) -> Result, RuntimeError> { + let path = self.absolute_path(path)?; + self.host.store_has(&path) + } + + #[inline(always)] + fn store_read>( + &self, + path: &P, + from_offset: usize, + max_bytes: usize, + ) -> Result, RuntimeError> { + let path = self.absolute_path(path)?; + self.host.store_read(&path, from_offset, max_bytes) + } + + #[inline(always)] + fn store_read_slice>( + &self, + path: &P, + from_offset: usize, + buffer: &mut [u8], + ) -> Result { + let path = self.absolute_path(path)?; + self.host.store_read_slice(&path, from_offset, buffer) + } + + #[inline(always)] + fn store_read_all(&self, path: &impl Path) -> Result, RuntimeError> { + let path = self.absolute_path(path)?; + self.host.store_read_all(&path) + } + + #[inline(always)] + fn store_write>( + &mut self, + path: &P, + src: &[u8], + at_offset: usize, + ) -> Result<(), RuntimeError> { + let path = self.absolute_path(path)?; + self.host.store_write(&path, src, at_offset) + } + + #[inline(always)] + fn store_write_all>( + &mut self, + path: &P, + src: &[u8], + ) -> Result<(), RuntimeError> { + let path = self.absolute_path(path)?; + self.host.store_write_all(&path, src) + } + + #[inline(always)] + fn store_delete>(&mut self, path: &P) -> Result<(), RuntimeError> { + let path = self.absolute_path(path)?; + self.host.store_delete(&path) + } + + #[inline(always)] + fn store_delete_value>( + &mut self, + path: &P, + ) -> Result<(), RuntimeError> { + let path = self.absolute_path(path)?; + self.host.store_delete_value(&path) + } + + #[inline(always)] + fn store_count_subkeys>( + &self, + prefix: &P, + ) -> Result { + let prefix = self.absolute_path(prefix)?; + self.host.store_count_subkeys(&prefix) + } + + #[inline(always)] + fn store_move( + &mut self, + from_path: &impl Path, + to_path: &impl Path, + ) -> Result<(), RuntimeError> { + let from_path = self.absolute_path(from_path)?; + let to_path = self.absolute_path(to_path)?; + self.host.store_move(&from_path, &to_path) + } + + #[inline(always)] + fn store_copy( + &mut self, + from_path: &impl Path, + to_path: &impl Path, + ) -> Result<(), RuntimeError> { + let from_path = self.absolute_path(from_path)?; + let to_path = self.absolute_path(to_path)?; + self.host.store_copy(&from_path, &to_path) + } + + #[inline(always)] + fn reveal_preimage( + &self, + hash: &[u8; PREIMAGE_HASH_SIZE], + destination: &mut [u8], + ) -> Result { + self.host.reveal_preimage(hash, destination) + } + + #[inline(always)] + fn store_value_size(&self, path: &impl Path) -> Result { + let path = self.absolute_path(path)?; + self.host.store_value_size(&path) + } + + #[inline(always)] + fn mark_for_reboot(&mut self) -> Result<(), RuntimeError> { + self.host.mark_for_reboot() + } + + #[inline(always)] + fn reveal_metadata(&self) -> RollupMetadata { + self.host.reveal_metadata() + } + + #[inline(always)] + fn reveal_dal_page( + &self, + published_level: i32, + slot_index: u8, + page_index: i16, + destination: &mut [u8], + ) -> Result { + self.host + .reveal_dal_page(published_level, slot_index, page_index, destination) + } + + #[inline(always)] + fn reveal_dal_parameters(&self) -> RollupDalParameters { + self.host.reveal_dal_parameters() + } + + #[inline(always)] + fn last_run_aborted(&self) -> Result { + self.host.last_run_aborted() + } + + #[inline(always)] + fn upgrade_failed(&self) -> Result { + self.host.upgrade_failed() + } + + #[inline(always)] + fn restart_forced(&self) -> Result { + self.host.restart_forced() + } + + #[inline(always)] + fn reboot_left(&self) -> Result { + self.host.reboot_left() + } + + #[inline(always)] + fn runtime_version(&self) -> Result { + self.host.runtime_version() + } +} + +impl> Verbosity for RelativeRuntime<&mut Host> { + fn verbosity(&self) -> tezos_evm_logging::Level { + self.host.verbosity() + } +} + +impl> WithGas for RelativeRuntime<&mut Host> { + fn add_execution_gas(&mut self, gas: u64) { + self.host.add_execution_gas(gas) + } +} + +impl> TraceRuntime for RelativeRuntime<&mut Host> { + fn store_write_all_trace( + &mut self, + path: &TracePath, + src: &[u8], + ) -> Result<(), RuntimeError> { + self.host.store_write_all_trace(path, src) + } +} diff --git a/etherlink/kernel_evm/runtime/src/runtime.rs b/etherlink/kernel_evm/runtime/src/runtime.rs index 64a24c9fe51a3be7c49cce6f00daac636d5d9c07..1617b1d41ef40f307102ab1a52457672010b3ff8 100644 --- a/etherlink/kernel_evm/runtime/src/runtime.rs +++ b/etherlink/kernel_evm/runtime/src/runtime.rs @@ -25,23 +25,47 @@ use tezos_smart_rollup_host::{ dal_parameters::RollupDalParameters, input::Message, metadata::RollupMetadata, - path::{Path, RefPath}, + path::{concat, OwnedPath, Path, RefPath}, runtime::{Runtime as SdkRuntime, RuntimeError, ValueType}, }; use tezos_smart_rollup_mock::MockHost; // Set by the node, contains the verbosity for the logs -pub const VERBOSITY_PATH: RefPath = RefPath::assert_from(b"/evm/logging_verbosity"); +pub const VERBOSITY_PATH: RefPath = RefPath::assert_from(b"/evm/logging_verbosity"); -pub trait Runtime: - SdkRuntime + InternalRuntime + ExtendedRuntime + Verbosity + WithGas +const TRACE_PATH: RefPath = RefPath::assert_from(b"/evm/trace"); +pub struct TracePath(pub OwnedPath); + +pub trait TraceRuntime { + // Same as store_write_all but path is implicitely prefixed by /evm/trace/ + fn store_write_all_trace( + &mut self, + path: &TracePath, + src: &[u8], + ) -> Result<(), RuntimeError>; +} + +pub trait Runtime: + SdkRuntime + + InternalRuntime + + ExtendedRuntime + + Verbosity + + WithGas + + TraceRuntime { } // If a type implements the Runtime, InternalRuntime and ExtendedRuntime traits, // it also implements the kernel Runtime. -impl Runtime - for T +impl< + const IS_ABSOLUTE: bool, + T: SdkRuntime + + InternalRuntime + + ExtendedRuntime + + Verbosity + + WithGas + + TraceRuntime, + > Runtime for T { } @@ -61,7 +85,7 @@ impl Ru // However it is never used in the type itself, which will be rejected by the // compiler. PhantomData associates `R` to the struct with no cost at // runtime. -pub struct KernelHost + Borrow, Internal> { +pub struct KernelHost, Host: BorrowMut + Borrow, Internal> { pub host: Host, pub internal: Internal, pub logs_verbosity: Level, @@ -69,8 +93,8 @@ pub struct KernelHost + Borrow, Internal> { pub _pd: PhantomData, } -impl + Borrow, Internal: InternalRuntime> SdkRuntime - for KernelHost +impl, Host: BorrowMut + Borrow, Internal: InternalRuntime> + SdkRuntime for KernelHost { #[inline(always)] fn write_output(&mut self, from: &[u8]) -> Result<(), RuntimeError> { @@ -88,12 +112,15 @@ impl + Borrow, Internal: InternalRuntime> S } #[inline(always)] - fn store_has(&self, path: &T) -> Result, RuntimeError> { + fn store_has>( + &self, + path: &T, + ) -> Result, RuntimeError> { self.host.borrow().store_has(path) } #[inline(always)] - fn store_read( + fn store_read>( &self, path: &T, from_offset: usize, @@ -103,7 +130,7 @@ impl + Borrow, Internal: InternalRuntime> S } #[inline(always)] - fn store_read_slice( + fn store_read_slice>( &self, path: &T, from_offset: usize, @@ -115,12 +142,12 @@ impl + Borrow, Internal: InternalRuntime> S } #[inline(always)] - fn store_read_all(&self, path: &impl Path) -> Result, RuntimeError> { + fn store_read_all(&self, path: &impl Path) -> Result, RuntimeError> { self.host.borrow().store_read_all(path) } #[inline(always)] - fn store_write( + fn store_write>( &mut self, path: &T, src: &[u8], @@ -130,7 +157,7 @@ impl + Borrow, Internal: InternalRuntime> S } #[inline(always)] - fn store_write_all( + fn store_write_all>( &mut self, path: &T, src: &[u8], @@ -139,25 +166,31 @@ impl + Borrow, Internal: InternalRuntime> S } #[inline(always)] - fn store_delete(&mut self, path: &T) -> Result<(), RuntimeError> { + fn store_delete>(&mut self, path: &T) -> Result<(), RuntimeError> { self.host.borrow_mut().store_delete(path) } #[inline(always)] - fn store_delete_value(&mut self, path: &T) -> Result<(), RuntimeError> { + fn store_delete_value>( + &mut self, + path: &T, + ) -> Result<(), RuntimeError> { self.host.borrow_mut().store_delete_value(path) } #[inline(always)] - fn store_count_subkeys(&self, prefix: &T) -> Result { + fn store_count_subkeys>( + &self, + prefix: &T, + ) -> Result { self.host.borrow().store_count_subkeys(prefix) } #[inline(always)] fn store_move( &mut self, - from_path: &impl Path, - to_path: &impl Path, + from_path: &impl Path, + to_path: &impl Path, ) -> Result<(), RuntimeError> { self.host.borrow_mut().store_move(from_path, to_path) } @@ -165,8 +198,8 @@ impl + Borrow, Internal: InternalRuntime> S #[inline(always)] fn store_copy( &mut self, - from_path: &impl Path, - to_path: &impl Path, + from_path: &impl Path, + to_path: &impl Path, ) -> Result<(), RuntimeError> { self.host.borrow_mut().store_copy(from_path, to_path) } @@ -181,7 +214,7 @@ impl + Borrow, Internal: InternalRuntime> S } #[inline(always)] - fn store_value_size(&self, path: &impl Path) -> Result { + fn store_value_size(&self, path: &impl Path) -> Result { self.host.borrow().store_value_size(path) } @@ -250,11 +283,11 @@ impl + Borrow, Internal: InternalRuntime> S } } -impl + BorrowMut, Internal: InternalRuntime> +impl, Host: Borrow + BorrowMut, Internal: InternalRuntime> InternalRuntime for KernelHost { #[inline(always)] - fn __internal_store_get_hash( + fn __internal_store_get_hash>( &mut self, path: &T, ) -> Result, RuntimeError> { @@ -262,16 +295,19 @@ impl + BorrowMut, Internal: InternalRuntime> } } -impl + Borrow, Internal: InternalRuntime> - ExtendedRuntime for KernelHost +impl, Host: BorrowMut + Borrow, Internal: InternalRuntime> + ExtendedRuntime for KernelHost { #[inline(always)] - fn store_get_hash(&mut self, path: &T) -> Result, RuntimeError> { + fn store_get_hash( + &mut self, + path: &impl Path, + ) -> Result, RuntimeError> { self.__internal_store_get_hash(path) } } -impl + BorrowMut, Internal> +impl, Host: Borrow + BorrowMut, Internal> KernelHost where for<'a> &'a mut Host: BorrowMut, @@ -287,15 +323,15 @@ where } } -impl + Borrow, Internal: InternalRuntime> Verbosity - for KernelHost +impl, Host: BorrowMut + Borrow, Internal: InternalRuntime> + Verbosity for KernelHost { fn verbosity(&self) -> Level { self.logs_verbosity } } -pub fn read_logs_verbosity( +pub fn read_logs_verbosity>( host: &Host, ) -> Level { match host.store_read_all(&VERBOSITY_PATH) { @@ -306,15 +342,17 @@ pub fn read_logs_verbosity( } } -impl + Borrow, Internal: InternalRuntime> WithGas - for KernelHost +impl, Host: BorrowMut + Borrow, Internal: InternalRuntime> + WithGas for KernelHost { fn add_execution_gas(&mut self, gas: u64) { self.execution_gas_used += gas; } } -impl + Borrow> KernelHost { +impl, Host: BorrowMut + Borrow> + KernelHost +{ pub fn init(host: Host) -> Self { let internal_storage = InternalHost(); let logs_verbosity = read_logs_verbosity(host.borrow()); @@ -327,6 +365,22 @@ impl + Borrow> KernelHost, Host: BorrowMut + Borrow, Internal: InternalRuntime> + TraceRuntime for KernelHost +{ + fn store_write_all_trace( + &mut self, + path: &TracePath, + src: &[u8], + ) -> Result<(), RuntimeError> { + let TracePath(path) = path; + let absolute_path = + concat(&TRACE_PATH, path).map_err(|_| RuntimeError::PathNotFound)?; + self.store_write_all(&absolute_path, src) + } +} + pub type MockKernelHost = KernelHost; impl Default for MockKernelHost { diff --git a/etherlink/kernel_evm/runtime/src/safe_storage.rs b/etherlink/kernel_evm/runtime/src/safe_storage.rs index d55feb706e33759acb9859300ae784e4abe16905..05980f7fe1c71dbd2ca359c2f52e2194f8f5b9ef 100644 --- a/etherlink/kernel_evm/runtime/src/safe_storage.rs +++ b/etherlink/kernel_evm/runtime/src/safe_storage.rs @@ -7,7 +7,7 @@ use crate::extensions::WithGas; use crate::internal_runtime::{ExtendedRuntime, InternalRuntime}; -use crate::runtime::Runtime; +use crate::runtime::{Runtime, TracePath, TraceRuntime}; use tezos_evm_logging::Verbosity; use tezos_smart_rollup_core::PREIMAGE_HASH_SIZE; use tezos_smart_rollup_host::dal_parameters::RollupDalParameters; @@ -18,22 +18,29 @@ use tezos_smart_rollup_host::{ runtime::{Runtime as SdkRuntime, RuntimeError, ValueType}, }; -pub const TMP_PATH: RefPath = RefPath::assert_from(b"/tmp"); -pub const WORLD_STATE_PATH: RefPath = RefPath::assert_from(b"/evm/world_state"); -pub const TMP_WORLD_STATE_PATH: RefPath = RefPath::assert_from(b"/tmp/evm/world_state"); -pub const TRACE_PATH: RefPath = RefPath::assert_from(b"/evm/trace"); -pub const TMP_TRACE_PATH: RefPath = RefPath::assert_from(b"/tmp/evm/trace"); +pub const TMP_PATH: RefPath = RefPath::assert_from(b"/tmp"); +pub const WORLD_STATE_PATH: RefPath = RefPath::assert_from(b"/evm/world_state"); +pub const TMP_WORLD_STATE_PATH: RefPath = + RefPath::assert_from(b"/tmp/evm/world_state"); +pub const TRACE_PATH: RefPath = RefPath::assert_from(b"/evm/trace"); +pub const TMP_TRACE_PATH: RefPath = RefPath::assert_from(b"/tmp/evm/trace"); -pub fn safe_path(path: &T) -> Result { - concat(&TMP_PATH, path).map_err(|_| RuntimeError::PathNotFound) +pub fn safe_path(path: &impl Path) -> Result, RuntimeError> { + concat(&TMP_WORLD_STATE_PATH, path).map_err(|_| RuntimeError::PathNotFound) +} + +pub fn safe_trace_path(path: &TracePath) -> Result, RuntimeError> { + let TracePath(path) = path; + concat(&TMP_TRACE_PATH, path).map_err(|_| RuntimeError::PathNotFound) } pub struct SafeStorage { + pub root: RefPath<'static, true>, pub host: Runtime, } -impl InternalRuntime for SafeStorage<&mut Host> { - fn __internal_store_get_hash( +impl> InternalRuntime for SafeStorage<&mut Host> { + fn __internal_store_get_hash>( &mut self, path: &T, ) -> Result, RuntimeError> { @@ -41,15 +48,18 @@ impl InternalRuntime for SafeStorage<&mut Host> { } } -impl ExtendedRuntime for SafeStorage<&mut Host> { +impl> ExtendedRuntime for SafeStorage<&mut Host> { #[inline(always)] - fn store_get_hash(&mut self, path: &P) -> Result, RuntimeError> { + fn store_get_hash( + &mut self, + path: &impl Path, + ) -> Result, RuntimeError> { let path = safe_path(path)?; self.__internal_store_get_hash(&path) } } -impl SdkRuntime for SafeStorage<&mut Host> { +impl> SdkRuntime for SafeStorage<&mut Host> { #[inline(always)] fn write_output(&mut self, from: &[u8]) -> Result<(), RuntimeError> { self.host.write_output(from) @@ -66,13 +76,16 @@ impl SdkRuntime for SafeStorage<&mut Host> { } #[inline(always)] - fn store_has(&self, path: &T) -> Result, RuntimeError> { + fn store_has>( + &self, + path: &T, + ) -> Result, RuntimeError> { let path = safe_path(path)?; self.host.store_has(&path) } #[inline(always)] - fn store_read( + fn store_read>( &self, path: &T, from_offset: usize, @@ -83,7 +96,7 @@ impl SdkRuntime for SafeStorage<&mut Host> { } #[inline(always)] - fn store_read_slice( + fn store_read_slice>( &self, path: &T, from_offset: usize, @@ -94,13 +107,13 @@ impl SdkRuntime for SafeStorage<&mut Host> { } #[inline(always)] - fn store_read_all(&self, path: &impl Path) -> Result, RuntimeError> { + fn store_read_all(&self, path: &impl Path) -> Result, RuntimeError> { let path = safe_path(path)?; self.host.store_read_all(&path) } #[inline(always)] - fn store_write( + fn store_write>( &mut self, path: &T, src: &[u8], @@ -111,7 +124,7 @@ impl SdkRuntime for SafeStorage<&mut Host> { } #[inline(always)] - fn store_write_all( + fn store_write_all>( &mut self, path: &T, src: &[u8], @@ -121,19 +134,25 @@ impl SdkRuntime for SafeStorage<&mut Host> { } #[inline(always)] - fn store_delete(&mut self, path: &T) -> Result<(), RuntimeError> { + fn store_delete>(&mut self, path: &T) -> Result<(), RuntimeError> { let path = safe_path(path)?; self.host.store_delete(&path) } #[inline(always)] - fn store_delete_value(&mut self, path: &T) -> Result<(), RuntimeError> { + fn store_delete_value>( + &mut self, + path: &T, + ) -> Result<(), RuntimeError> { let path = safe_path(path)?; self.host.store_delete_value(&path) } #[inline(always)] - fn store_count_subkeys(&self, prefix: &T) -> Result { + fn store_count_subkeys>( + &self, + prefix: &T, + ) -> Result { let prefix = safe_path(prefix)?; self.host.store_count_subkeys(&prefix) } @@ -141,8 +160,8 @@ impl SdkRuntime for SafeStorage<&mut Host> { #[inline(always)] fn store_move( &mut self, - from_path: &impl Path, - to_path: &impl Path, + from_path: &impl Path, + to_path: &impl Path, ) -> Result<(), RuntimeError> { let from_path = safe_path(from_path)?; let to_path = safe_path(to_path)?; @@ -152,8 +171,8 @@ impl SdkRuntime for SafeStorage<&mut Host> { #[inline(always)] fn store_copy( &mut self, - from_path: &impl Path, - to_path: &impl Path, + from_path: &impl Path, + to_path: &impl Path, ) -> Result<(), RuntimeError> { let from_path = safe_path(from_path)?; let to_path = safe_path(to_path)?; @@ -170,7 +189,7 @@ impl SdkRuntime for SafeStorage<&mut Host> { } #[inline(always)] - fn store_value_size(&self, path: &impl Path) -> Result { + fn store_value_size(&self, path: &impl Path) -> Result { let path = safe_path(path)?; self.host.store_value_size(&path) } @@ -228,21 +247,19 @@ impl SdkRuntime for SafeStorage<&mut Host> { } } -impl Verbosity for SafeStorage<&mut Host> { +impl> Verbosity for SafeStorage<&mut Host> { fn verbosity(&self) -> tezos_evm_logging::Level { self.host.verbosity() } } -impl SafeStorage<&mut Host> { +impl<'a, Host: Runtime> SafeStorage<&'a mut Host> { pub fn start(&mut self) -> Result<(), RuntimeError> { - self.host - .store_copy(&WORLD_STATE_PATH, &TMP_WORLD_STATE_PATH) + self.host.store_copy(&self.root, &TMP_WORLD_STATE_PATH) } pub fn promote(&mut self) -> Result<(), RuntimeError> { - self.host - .store_move(&TMP_WORLD_STATE_PATH, &WORLD_STATE_PATH) + self.host.store_move(&TMP_WORLD_STATE_PATH, &self.root) } // Only used in tracing mode, so that the trace doesn't polute the world @@ -259,8 +276,19 @@ impl SafeStorage<&mut Host> { } } -impl WithGas for SafeStorage<&mut Host> { +impl> WithGas for SafeStorage<&mut Host> { fn add_execution_gas(&mut self, gas: u64) { self.host.add_execution_gas(gas) } } + +impl> TraceRuntime for SafeStorage<&mut Host> { + fn store_write_all_trace( + &mut self, + path: &TracePath, + src: &[u8], + ) -> Result<(), RuntimeError> { + let path = safe_trace_path(path)?; + self.host.store_write_all(&path, src) + } +} diff --git a/etherlink/kernel_evm/storage/src/lib.rs b/etherlink/kernel_evm/storage/src/lib.rs index a4834e10f972bbc678530d80f904c11cde28f31b..276e3a97efafa2c641f819964cb9c104a138e2de 100644 --- a/etherlink/kernel_evm/storage/src/lib.rs +++ b/etherlink/kernel_evm/storage/src/lib.rs @@ -17,7 +17,7 @@ use tezos_crypto_rs::hash::{ContractKt1Hash, HashTrait}; use tezos_ethereum::rlp_helpers::FromRlpBytes; use tezos_evm_runtime::runtime::Runtime; use tezos_smart_rollup_host::path::*; -use tezos_smart_rollup_host::runtime::{RuntimeError, ValueType}; +use tezos_smart_rollup_host::runtime::{Runtime as SdkRuntime, RuntimeError, ValueType}; /// The size of one 256 bit word. Size in bytes. pub const WORD_SIZE: usize = 32usize; @@ -26,9 +26,9 @@ pub const WORD_SIZE: usize = 32usize; /// store the read slice in `buffer`. /// /// NB: Value is read starting 0. -pub fn store_read_slice( - host: &impl Runtime, - path: &impl Path, +pub fn store_read_slice( + host: &impl Runtime, + path: &impl Path, buffer: &mut [u8], expected_size: usize, ) -> Result<(), Error> { @@ -44,7 +44,7 @@ pub fn store_read_slice( } /// Get the path corresponding to an index of H256. -pub fn path_from_h256(index: &H256) -> Result { +pub fn path_from_h256(index: &H256) -> Result, Error> { let path_string = format!("/{}", hex::encode(index.to_fixed_bytes())); OwnedPath::try_from(path_string).map_err(Error::from) } @@ -52,7 +52,10 @@ pub fn path_from_h256(index: &H256) -> Result { /// Return a 32 bytes hash from storage at the given `path`. /// /// NB: The given bytes are interpreted in big endian order. -pub fn read_h256_be(host: &impl Runtime, path: &impl Path) -> anyhow::Result { +pub fn read_h256_be( + host: &impl Runtime, + path: &impl Path, +) -> anyhow::Result { let mut buffer = [0_u8; WORD_SIZE]; store_read_slice(host, path, &mut buffer, WORD_SIZE)?; Ok(H256::from_slice(&buffer)) @@ -61,9 +64,9 @@ pub fn read_h256_be(host: &impl Runtime, path: &impl Path) -> anyhow::Result( + host: &impl Runtime, + path: &impl Path, ) -> Result, Error> { match host.store_read_all(path) { Ok(bytes) if bytes.len() == WORD_SIZE => Ok(Some(H256::from_slice(&bytes))), @@ -76,9 +79,9 @@ pub fn read_h256_be_opt( /// If the path is not found, `default` is returned. /// /// NB: The given bytes are interpreted in big endian order. -pub fn read_h256_be_default( - host: &impl Runtime, - path: &impl Path, +pub fn read_h256_be_default( + host: &impl Runtime, + path: &impl Path, default: H256, ) -> Result { match read_h256_be_opt(host, path)? { @@ -90,9 +93,9 @@ pub fn read_h256_be_default( /// Write a 32 bytes hash into storage at the given `path`. /// /// NB: The hash is stored in big endian order. -pub fn write_h256_be( - host: &mut impl Runtime, - path: &impl Path, +pub fn write_h256_be( + host: &mut impl Runtime, + path: &impl Path, hash: H256, ) -> anyhow::Result<()> { Ok(host.store_write_all(path, hash.as_bytes())?) @@ -101,7 +104,10 @@ pub fn write_h256_be( /// Return an unsigned 32 bytes value from storage at the given `path`. /// /// NB: The given bytes are interpreted in little endian order. -pub fn read_u256_le(host: &impl Runtime, path: &impl Path) -> Result { +pub fn read_u256_le( + host: &impl Runtime, + path: &impl Path, +) -> Result { let bytes = host.store_read_all(path)?; Ok(U256::from_little_endian(&bytes)) } @@ -110,9 +116,9 @@ pub fn read_u256_le(host: &impl Runtime, path: &impl Path) -> Result( + host: &impl Runtime, + path: &impl Path, default: U256, ) -> Result { match host.store_read_all(path) { @@ -125,9 +131,9 @@ pub fn read_u256_le_default( /// Write an unsigned 32 bytes value into storage at the given `path`. /// /// NB: The value is stored in little endian order. -pub fn write_u256_le( - host: &mut impl Runtime, - path: &impl Path, +pub fn write_u256_le( + host: &mut impl Runtime, + path: &impl Path, value: U256, ) -> Result<(), Error> { let mut bytes: [u8; WORD_SIZE] = value.into(); @@ -138,7 +144,10 @@ pub fn write_u256_le( /// Return an unsigned 8 bytes value from storage at the given `path`. /// /// NB: The given bytes are interpreted in little endian order. -pub fn read_u64_le(host: &impl Runtime, path: &impl Path) -> Result { +pub fn read_u64_le( + host: &impl SdkRuntime, + path: &impl Path, +) -> Result { let mut bytes = [0; std::mem::size_of::()]; host.store_read_slice(path, 0, bytes.as_mut_slice())?; Ok(u64::from_le_bytes(bytes)) @@ -148,9 +157,9 @@ pub fn read_u64_le(host: &impl Runtime, path: &impl Path) -> Result /// If the path is not found, `default` is returned. /// /// NB: The given bytes are interpreted in little endian order. -pub fn read_u64_le_default( - host: &impl Runtime, - path: &impl Path, +pub fn read_u64_le_default( + host: &impl Runtime, + path: &impl Path, default: u64, ) -> Result { match host.store_read_all(path) { @@ -169,9 +178,9 @@ pub fn read_u64_le_default( /// If the path is not found, `default` is returned. /// /// NB: The given bytes are interpreted in little endian order. -pub fn read_u16_le_default( - host: &impl Runtime, - path: &impl Path, +pub fn read_u16_le_default( + host: &impl Runtime, + path: &impl Path, default: u16, ) -> Result { // This is exactly the same function as `read_u64_le_default`, but you know @@ -191,9 +200,9 @@ pub fn read_u16_le_default( /// Write an unsigned 8 bytes value into storage at the given `path`. /// /// NB: The value is stored in little endian order. -pub fn write_u64_le( - host: &mut impl Runtime, - path: &impl Path, +pub fn write_u64_le( + host: &mut impl SdkRuntime, + path: &impl Path, value: u64, ) -> Result<(), Error> { host.store_write_all(path, value.to_le_bytes().as_slice()) @@ -202,10 +211,10 @@ pub fn write_u64_le( /// Store `src` (which must be encodable) as rlp bytes into storage /// at the given `path`. -pub fn store_rlp( - src: &T, - host: &mut impl Runtime, - path: &impl Path, +pub fn store_rlp( + src: &impl Encodable, + host: &mut impl Runtime, + path: &impl Path, ) -> Result<(), Error> { host.store_write_all(path, &src.rlp_bytes()) .map_err(Error::from) @@ -213,7 +222,10 @@ pub fn store_rlp( /// Return a decodable value from storage as rlp bytes /// at the given `path`. -pub fn read_rlp(host: &impl Runtime, path: &impl Path) -> Result { +pub fn read_rlp( + host: &impl Runtime, + path: &impl Path, +) -> Result { let bytes = host.store_read_all(path)?; FromRlpBytes::from_rlp_bytes(&bytes).map_err(Error::from) } @@ -222,9 +234,9 @@ pub fn read_rlp(host: &impl Runtime, path: &impl Path) -> Result( - host: &impl Runtime, - path: &impl Path, +pub fn read_optional_rlp( + host: &impl Runtime, + path: &impl Path, ) -> Result, anyhow::Error> { if let Some(ValueType::Value) = host.store_has(path)? { let elt = read_rlp(host, path)?; @@ -235,7 +247,10 @@ pub fn read_optional_rlp( } /// Return a base58 contract address from storage at the given `path`. -pub fn read_b58_kt1(host: &impl Runtime, path: &impl Path) -> Option { +pub fn read_b58_kt1( + host: &impl Runtime, + path: &impl Path, +) -> Option { let bytes = host.store_read_all(path).ok()?; let kt1_b58 = String::from_utf8(bytes).ok()?; ContractKt1Hash::from_b58check(&kt1_b58).ok() diff --git a/etherlink/lib_wasm_runtime/src/host.rs b/etherlink/lib_wasm_runtime/src/host.rs index 1126d3910bb7256eb556b9897311c3a0f5116c57..605ee5a55b8c25beeef13c381920b75d76a8b916 100644 --- a/etherlink/lib_wasm_runtime/src/host.rs +++ b/etherlink/lib_wasm_runtime/src/host.rs @@ -164,13 +164,13 @@ fn from_binding_error(e: BindingsError) -> RuntimeError { } } -const REBOOT_FLAG_PATH: RefPath = RefPath::assert_from(REBOOT_FLAG.as_bytes()); +const REBOOT_FLAG_PATH: RefPath = RefPath::assert_from(REBOOT_FLAG.as_bytes()); impl Host { // Compare to its counterpart in the kernel SDK, we only check if the path exists on error. fn check_path_exists<'a>( &'a self, - path: &'a impl Path, + path: &'a impl Path, ) -> impl FnOnce(RuntimeError) -> RuntimeError + 'a { |err| { if let Ok(Some(_)) = self.store_has(path) { @@ -184,7 +184,7 @@ impl Host { // Compare to its counterpart in the kernel SDK, we only check if the path exists on error. fn check_path_has_value<'a>( &'a self, - path: &'a impl Path, + path: &'a impl Path, ) -> impl FnOnce(RuntimeError) -> RuntimeError + 'a { |err| { if let Ok(Some(ValueType::Value | ValueType::ValueWithSubtree)) = self.store_has(path) { @@ -196,7 +196,7 @@ impl Host { } } -impl Runtime for Host { +impl Runtime for Host { fn write_output(&mut self, _from: &[u8]) -> Result<(), RuntimeError> { trace!("write_output()"); // The outbox is only useful in the context of the rollup node, in order to enable @@ -221,7 +221,7 @@ impl Runtime for Host { } } - fn store_has(&self, path: &T) -> Result, RuntimeError> { + fn store_has>(&self, path: &T) -> Result, RuntimeError> { trace!("store_has({path})"); let res = bindings::store_has(self.tree(), path.as_bytes()); @@ -237,7 +237,7 @@ impl Runtime for Host { } } - fn store_read( + fn store_read>( &self, path: &T, from_offset: usize, @@ -253,7 +253,7 @@ impl Runtime for Host { Ok(res.as_bytes().to_owned()) } - fn store_read_slice( + fn store_read_slice>( &self, path: &T, from_offset: usize, @@ -269,7 +269,7 @@ impl Runtime for Host { Ok(res.as_bytes().len()) } - fn store_read_all(&self, path: &impl Path) -> Result, RuntimeError> { + fn store_read_all(&self, path: &impl Path) -> Result, RuntimeError> { trace!("store_read_all({path})"); let res = bindings::read_value(self.tree(), path.as_bytes()) .map_err(from_binding_error) @@ -277,7 +277,7 @@ impl Runtime for Host { Ok(res.as_bytes().to_owned()) } - fn store_write( + fn store_write>( &mut self, path: &T, src: &[u8], @@ -291,7 +291,7 @@ impl Runtime for Host { Ok(()) } - fn store_write_all(&mut self, path: &T, src: &[u8]) -> Result<(), RuntimeError> { + fn store_write_all>(&mut self, path: &T, src: &[u8]) -> Result<(), RuntimeError> { trace!("store_write_all({path})"); let new_tree = bindings::store_write_all(self.tree(), path.as_bytes(), src) .map_err(from_binding_error)?; @@ -300,7 +300,7 @@ impl Runtime for Host { Ok(()) } - fn store_delete(&mut self, path: &T) -> Result<(), RuntimeError> { + fn store_delete>(&mut self, path: &T) -> Result<(), RuntimeError> { trace!("store_delete({path})"); if let Ok(None) = Runtime::store_has(self, path) { return Err(RuntimeError::PathNotFound); @@ -313,7 +313,7 @@ impl Runtime for Host { Ok(()) } - fn store_delete_value(&mut self, path: &T) -> Result<(), RuntimeError> { + fn store_delete_value>(&mut self, path: &T) -> Result<(), RuntimeError> { trace!("store_delete_value({path})"); let new_tree = bindings::store_delete(self.tree(), path.as_bytes(), true) .map_err(from_binding_error)?; @@ -322,7 +322,7 @@ impl Runtime for Host { Ok(()) } - fn store_count_subkeys(&self, prefix: &T) -> Result { + fn store_count_subkeys>(&self, prefix: &T) -> Result { trace!("store_count_subkeys({prefix})"); let res = bindings::store_list_size(self.tree(), prefix.as_bytes()) .map_err(from_binding_error)?; @@ -332,8 +332,8 @@ impl Runtime for Host { fn store_move( &mut self, - from_path: &impl Path, - to_path: &impl Path, + from_path: &impl Path, + to_path: &impl Path, ) -> Result<(), RuntimeError> { trace!("store_move({from_path}, {to_path})"); let new_tree = bindings::store_move(self.tree(), from_path.as_bytes(), to_path.as_bytes()) @@ -351,8 +351,8 @@ impl Runtime for Host { fn store_copy( &mut self, - from_path: &impl Path, - to_path: &impl Path, + from_path: &impl Path, + to_path: &impl Path, ) -> Result<(), RuntimeError> { trace!("store_copy({from_path}, {to_path})"); let new_tree = bindings::store_copy(self.tree(), from_path.as_bytes(), to_path.as_bytes()) @@ -416,7 +416,7 @@ impl Runtime for Host { unimplemented!() } - fn store_value_size(&self, path: &impl Path) -> Result { + fn store_value_size(&self, path: &impl Path) -> Result { trace!("store_value_size({path})"); match self.version { RuntimeVersion::V0 => { @@ -485,7 +485,7 @@ impl Runtime for Host { } impl BifrostInternalRuntime for Host { - fn __internal_store_get_hash(&mut self, path: &T) -> Result, RuntimeError> { + fn __internal_store_get_hash>(&mut self, path: &T) -> Result, RuntimeError> { trace!("store_get_hash({path})"); let hash = bindings::store_get_hash(self.tree(), path.as_bytes()).map_err(from_binding_error)?; diff --git a/src/kernel_sdk/encoding/src/dac/certificate.rs b/src/kernel_sdk/encoding/src/dac/certificate.rs index b86b0887c8406cb85e40b39f8359b494ee28bd7a..822cd6b2c22c7ae784ef45e812800ee98466079c 100644 --- a/src/kernel_sdk/encoding/src/dac/certificate.rs +++ b/src/kernel_sdk/encoding/src/dac/certificate.rs @@ -165,10 +165,10 @@ impl Certificate { /// If `reveal_to_store` returns an error, any content succesfully revealed /// up until the error occurred will remain in storage; use [Runtime::store_value_size] to /// determine the size of this value. - pub fn reveal_to_store( + pub fn reveal_to_store>( &self, host: &mut Host, - path: &impl Path, + path: &impl Path, ) -> Result { const MAX_TOP_LEVEL_HASHES: usize = 20; @@ -233,7 +233,7 @@ impl Certificate { } fn fetch_page<'a>( - host: &impl Runtime, + host: &impl Runtime, hash: &[u8; PREIMAGE_HASH_SIZE], buffer: &'a mut [u8], ) -> Result<(SlicePage<'a>, &'a mut [u8]), CertificateError> { diff --git a/src/kernel_sdk/encoding/src/dac/pages.rs b/src/kernel_sdk/encoding/src/dac/pages.rs index 0b06f25260a7bca9f78cfcb14487f2c54c0794ad..6e420c2002c26ae0dbe7c9ce65f3d891bf13515f 100644 --- a/src/kernel_sdk/encoding/src/dac/pages.rs +++ b/src/kernel_sdk/encoding/src/dac/pages.rs @@ -320,7 +320,7 @@ impl<'a> V0SliceHashPage<'a> { /// Fetches a page of data from file `hash` into `buffer` using [Runtime::reveal_preimage]. /// Returns a tuple of the raw page and remaining buffer. -pub fn fetch_page_raw<'a, Host: Runtime>( +pub fn fetch_page_raw<'a, Host: Runtime>( host: &Host, hash: &[u8; PREIMAGE_HASH_SIZE], buffer: &'a mut [u8], @@ -339,7 +339,7 @@ pub fn fetch_page_raw<'a, Host: Runtime>( /// however, to run into tick-limit issues while doing so, depending on what `save_content` does. /// /// Instead, it is recommended to use `DacCertificate::reveal_to_store` where possible. -pub fn reveal_loop( +pub fn reveal_loop>( host: &mut Host, level: usize, hash: &[u8; PREIMAGE_HASH_SIZE], diff --git a/src/kernel_sdk/host/src/lib.rs b/src/kernel_sdk/host/src/lib.rs index 284b56a65e4090c841d625bcc8ff10dcda875f56..bac4be0b314d5bf93ad7b70ed231e3d4cdc8029b 100644 --- a/src/kernel_sdk/host/src/lib.rs +++ b/src/kernel_sdk/host/src/lib.rs @@ -26,7 +26,7 @@ pub use crate::metadata::METADATA_SIZE; use path::RefPath; /// Boot path for kernels -pub const KERNEL_BOOT_PATH: RefPath = RefPath::assert_from(b"/kernel/boot.wasm"); +pub const KERNEL_BOOT_PATH: RefPath = RefPath::assert_from(b"/kernel/boot.wasm"); /// Defines the errors possibly returned by an host functions. #[repr(i32)] diff --git a/src/kernel_sdk/host/src/path.rs b/src/kernel_sdk/host/src/path.rs index 364b27041c59da2c4804e62803eeac63a72930bf..0137050f3db0cd7820de00807c9af454c55edc28 100644 --- a/src/kernel_sdk/host/src/path.rs +++ b/src/kernel_sdk/host/src/path.rs @@ -22,7 +22,7 @@ const DURABLE_STORAGE_PREFIX_INNER: &[u8] = b"/durable"; /// The *wasm-encoded* binary blob of either the currently running kernel, or the next /// kernel to be rebooted into. -pub const PATH_KERNEL_BOOT: RefPath = RefPath::assert_from(b"/kernel/boot.wasm"); +pub const PATH_KERNEL_BOOT: RefPath = RefPath::assert_from(b"/kernel/boot.wasm"); /// Marker trait for methods on types representing *path-encodings*. /// @@ -42,7 +42,7 @@ pub const PATH_KERNEL_BOOT: RefPath = RefPath::assert_from(b"/kernel/boot.wasm") /// `T: impl Path` being correctly path-encoded. /// /// [`Runtime`]: crate::runtime::Runtime -pub unsafe trait Path: core::fmt::Debug + core::fmt::Display { +pub unsafe trait Path: core::fmt::Debug + core::fmt::Display { /// Returns a read-only reference to the underlying path-encoded byte-slice. fn as_bytes(&self) -> &[u8]; @@ -121,11 +121,11 @@ impl core::fmt::Display for PathError { /// /// Otherwise, you can use [OwnedPath]. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct RefPath<'a> { +pub struct RefPath<'a, const IS_ABSOLUTE: bool> { inner: &'a str, } -impl<'a> RefPath<'a> { +impl<'a, const IS_ABSOLUTE: bool> RefPath<'a, IS_ABSOLUTE> { /// Constructs a [`RefPath`] from a byte slice. /// /// # Panics @@ -151,7 +151,7 @@ impl<'a> RefPath<'a> { /// # use tezos_smart_rollup_host::path::RefPath; /// let path = RefPath::assert_from("!&(*(".as_bytes()); /// ``` - pub const fn assert_from(path: &[u8]) -> RefPath { + pub const fn assert_from(path: &'a[u8]) -> Self { assert_ok(validate_path(path)); RefPath { @@ -163,7 +163,7 @@ impl<'a> RefPath<'a> { /// is writable, i.e. not prefixed with `/readonly`. This function /// is to be used only internally to create [`RefPath`] for the /// `readonly` part of the storage. See `runtime.rs` for example. - pub(crate) const fn assert_from_readonly(path: &[u8]) -> RefPath { + pub(crate) const fn assert_from_readonly(path: &'a [u8]) -> Self { assert_ok(validate_path_internal(path)); RefPath { @@ -172,13 +172,13 @@ impl<'a> RefPath<'a> { } } -impl core::fmt::Display for RefPath<'_> { +impl core::fmt::Display for RefPath<'_, IS_ABSOLUTE> { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { self.inner.fmt(f) } } -unsafe impl Path for RefPath<'_> { +unsafe impl Path for RefPath<'_, IS_ABSOLUTE> { fn as_bytes(&self) -> &[u8] { self.inner.as_bytes() } @@ -255,18 +255,18 @@ const fn validate_path(path: &[u8]) -> Result<(), PathError> { } } -impl<'a> TryFrom<&'a str> for RefPath<'a> { +impl<'a, const IS_ABSOLUTE: bool> TryFrom<&'a str> for RefPath<'a, IS_ABSOLUTE> { type Error = PathError; - fn try_from(slice: &'a str) -> Result { + fn try_from(slice: &'a str) -> Result { Self::try_from(slice.as_bytes()) } } -impl<'a> TryFrom<&'a [u8]> for RefPath<'a> { +impl<'a, const IS_ABSOLUTE: bool> TryFrom<&'a [u8]> for RefPath<'a, IS_ABSOLUTE> { type Error = PathError; - fn try_from(slice: &'a [u8]) -> Result { + fn try_from(slice: &'a [u8]) -> Result { validate_path(slice)?; // SAFETY: we've validated that every byte is either alphanumeric or SEPARATOR @@ -292,11 +292,11 @@ mod owned { /// Useful when a new path is being constructed at runtime, which is not a sub-path of an /// already existing path (in which case you may use [RefPath]). #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] - pub struct OwnedPath { + pub struct OwnedPath { inner: String, } - impl OwnedPath { + impl OwnedPath { /// Constructs an [`OwnedPath`] from an arbitrary sequence of bytes. /// /// # Safety @@ -309,21 +309,20 @@ mod owned { } } - unsafe impl Path for OwnedPath { + unsafe impl Path for OwnedPath { fn as_bytes(&self) -> &[u8] { self.inner.as_bytes() } } - impl<'a> From> for OwnedPath { - fn from(path: RefPath<'a>) -> Self { + impl<'a, const IS_ABSOLUTE : bool> From> for OwnedPath { + fn from(path: RefPath<'a, IS_ABSOLUTE>) -> Self { Self { inner: path.inner.to_string(), } } } - - impl From<&P> for OwnedPath { + impl> From<&P> for OwnedPath { fn from(path: &P) -> Self { let path_bytes = path.as_bytes().to_vec(); @@ -335,13 +334,13 @@ mod owned { } } - impl<'a> From<&'a OwnedPath> for RefPath<'a> { - fn from(path: &'a OwnedPath) -> Self { + impl<'a, const IS_ABSOLUTE: bool> From<&'a OwnedPath> for RefPath<'a, IS_ABSOLUTE> { + fn from(path: &'a OwnedPath) -> Self { Self { inner: &path.inner } } } - impl TryFrom for OwnedPath { + impl TryFrom for OwnedPath { type Error = PathError; fn try_from(inner: String) -> Result { @@ -351,10 +350,10 @@ mod owned { } } - impl TryFrom> for OwnedPath { + impl TryFrom> for OwnedPath { type Error = PathError; - fn try_from(bytes: Vec) -> Result { + fn try_from(bytes: Vec) -> Result { validate_path(&bytes)?; // SAFETY: we've validated that every byte is either alphanumeric or SEPARATOR @@ -364,7 +363,7 @@ mod owned { } } - impl core::fmt::Display for OwnedPath { + impl core::fmt::Display for OwnedPath { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { self.inner.fmt(f) } @@ -373,10 +372,32 @@ mod owned { /// Given a prefix and a suffix create a new path that concatenates the two. /// /// Returns error in case the resulting path is too long. - pub fn concat( - prefix: &impl Path, - suffix: &impl Path, - ) -> Result { + pub fn concat( + prefix: &impl Path, + suffix: &impl Path, + ) -> Result, PathError> { + let mut bytes = Vec::with_capacity(prefix.size() + suffix.size()); + + bytes.extend_from_slice(prefix.as_bytes()); + bytes.extend_from_slice(suffix.as_bytes()); + + if bytes.len() <= PATH_MAX_SIZE { + // Since both prefix and suffix are paths, we can assume they only + // contain valid characters and start with '/', ie bytes contain + // a valid path as well. Also knowing that bytes contains valid + // number of bytes, we can use the unsafe, faster call below. + Ok(unsafe { OwnedPath::from_bytes_unchecked(bytes) }) + } else { + Err(PathError::PathTooLong) + } + } + + /// Same as concat but suffix is allowed to be an absolute path. + /// This is useful to backup an absolute path in /tmp/ + pub fn force_concat( + prefix: &impl Path, + suffix: &impl Path, + ) -> Result, PathError> { let mut bytes = Vec::with_capacity(prefix.size() + suffix.size()); bytes.extend_from_slice(prefix.as_bytes()); @@ -393,7 +414,7 @@ mod owned { } } - impl<'a> BinWriter for RefPath<'a> { + impl<'a, const IS_ABSOLUTE: bool> BinWriter for RefPath<'a, IS_ABSOLUTE> { fn bin_write(&self, output: &mut Vec) -> BinResult { let data = self.inner; put_bytes(data.as_bytes(), output); diff --git a/src/kernel_sdk/host/src/runtime.rs b/src/kernel_sdk/host/src/runtime.rs index 66ac91c0a63f8d88257caace81d75880ab11d4b8..a3e7d3c1f6d0ec38126174abac5e260e634d10d5 100644 --- a/src/kernel_sdk/host/src/runtime.rs +++ b/src/kernel_sdk/host/src/runtime.rs @@ -82,7 +82,7 @@ pub enum ValueType { /// - methods that take `&self` will not cause changes to the runtime state. /// - methods taking `&mut self` are expected to cause changes - either to *input*, /// *output* or *durable storage*. -pub trait Runtime { +pub trait Runtime { /// Write contents of the given slice to output. fn write_output(&mut self, from: &[u8]) -> Result<(), RuntimeError>; @@ -99,11 +99,11 @@ pub trait Runtime { fn read_input(&mut self) -> Result, RuntimeError>; /// Returns whether a given path exists in storage. - fn store_has(&self, path: &T) -> Result, RuntimeError>; + fn store_has>(&self, path: &T) -> Result, RuntimeError>; /// Read up to `max_bytes` from the given path in storage, starting `from_offset`. #[cfg(feature = "alloc")] - fn store_read( + fn store_read>( &self, path: &T, from_offset: usize, @@ -117,7 +117,7 @@ pub trait Runtime { /// The total bytes read is returned. /// If the returned value `n` is `n < buffer.len()`, then only the first `n` /// bytes of the buffer will have been written too. - fn store_read_slice( + fn store_read_slice>( &self, path: &T, from_offset: usize, @@ -126,14 +126,14 @@ pub trait Runtime { /// Read an entire value from the given path in storage. #[cfg(feature = "alloc")] - fn store_read_all(&self, path: &impl Path) -> Result, RuntimeError>; + fn store_read_all(&self, path: &impl Path) -> Result, RuntimeError>; /// Write the bytes given by `src` to storage at `path`, starting `at_offset`. /// /// Contrary to `store_write_all`, this does not replace the value (if any) /// previously stored under `path`. This allows for splicing/patching values /// directly in storage, without having to read the entire value from disk. - fn store_write( + fn store_write>( &mut self, path: &T, src: &[u8], @@ -144,30 +144,30 @@ pub trait Runtime { /// /// Contrary to `store_write`, this replaces the value (if any) that /// was previously stored at `path`. - fn store_write_all( + fn store_write_all>( &mut self, path: &T, src: &[u8], ) -> Result<(), RuntimeError>; /// Delete `path` from storage. - fn store_delete(&mut self, path: &T) -> Result<(), RuntimeError>; + fn store_delete>(&mut self, path: &T) -> Result<(), RuntimeError>; /// Delete value under `path` from storage. - fn store_delete_value(&mut self, path: &T) -> Result<(), RuntimeError>; + fn store_delete_value>(&mut self, path: &T) -> Result<(), RuntimeError>; /// Count the number of subkeys under `prefix`. /// /// See [SmartRollupCore::store_list_size]. - fn store_count_subkeys(&self, prefix: &T) -> Result; + fn store_count_subkeys>(&self, prefix: &T) -> Result; /// Move one part of durable storage to a different location /// /// See [SmartRollupCore::store_move]. fn store_move( &mut self, - from_path: &impl Path, - to_path: &impl Path, + from_path: &impl Path, + to_path: &impl Path, ) -> Result<(), RuntimeError>; /// Copy one part of durable storage to a different location @@ -175,8 +175,8 @@ pub trait Runtime { /// See [SmartRollupCore::store_copy]. fn store_copy( &mut self, - from_path: &impl Path, - to_path: &impl Path, + from_path: &impl Path, + to_path: &impl Path, ) -> Result<(), RuntimeError>; /// Reveal pre-image from a hash of size `PREIMAGE_HASH_SIZE` in bytes. @@ -204,7 +204,7 @@ pub trait Runtime { fn reveal_dal_parameters(&self) -> RollupDalParameters; /// Return the size of value stored at `path` - fn store_value_size(&self, path: &impl Path) -> Result; + fn store_value_size(&self, path: &impl Path) -> Result; /// Mark the kernel for reboot. /// @@ -244,9 +244,9 @@ pub trait Runtime { fn runtime_version(&self) -> Result; } -const REBOOT_PATH: RefPath = RefPath::assert_from(b"/kernel/env/reboot"); +const REBOOT_PATH: RefPath = RefPath::assert_from(b"/kernel/env/reboot"); -impl Runtime for Host +impl Runtime for Host where Host: SmartRollupCore, { @@ -299,7 +299,7 @@ where Ok(Some(input)) } - fn store_has(&self, path: &T) -> Result, RuntimeError> { + fn store_has>(&self, path: &T) -> Result, RuntimeError> { let result = unsafe { SmartRollupCore::store_has(self, path.as_ptr(), path.size()) }; @@ -317,7 +317,7 @@ where } #[cfg(feature = "alloc")] - fn store_read( + fn store_read>( &self, path: &T, from_offset: usize, @@ -352,7 +352,7 @@ where Ok(buffer) } - fn store_read_slice( + fn store_read_slice>( &self, path: &T, from_offset: usize, @@ -375,7 +375,7 @@ where } #[cfg(feature = "alloc")] - fn store_read_all(&self, path: &impl Path) -> Result, RuntimeError> { + fn store_read_all(&self, path: &impl Path) -> Result, RuntimeError> { use tezos_smart_rollup_core::MAX_FILE_CHUNK_SIZE; let length = Runtime::store_value_size(self, path)?; @@ -418,7 +418,7 @@ where Ok(buffer) } - fn store_write( + fn store_write>( &mut self, path: &T, mut src: &[u8], @@ -461,7 +461,7 @@ where } } - fn store_write_all( + fn store_write_all>( &mut self, path: &T, value: &[u8], @@ -483,7 +483,7 @@ where Runtime::store_write(self, path, value, 0) } - fn store_delete(&mut self, path: &T) -> Result<(), RuntimeError> { + fn store_delete>(&mut self, path: &T) -> Result<(), RuntimeError> { if let Ok(None) = Runtime::store_has(self, path) { return Err(RuntimeError::PathNotFound); } @@ -496,7 +496,7 @@ where } } - fn store_delete_value(&mut self, path: &T) -> Result<(), RuntimeError> { + fn store_delete_value>(&mut self, path: &T) -> Result<(), RuntimeError> { let res = unsafe { SmartRollupCore::store_delete_value(self, path.as_ptr(), path.size()) }; @@ -506,7 +506,7 @@ where } } - fn store_count_subkeys(&self, path: &T) -> Result { + fn store_count_subkeys>(&self, path: &T) -> Result { let count = unsafe { SmartRollupCore::store_list_size(self, path.as_ptr(), path.size()) }; @@ -519,8 +519,8 @@ where fn store_move( &mut self, - from_path: &impl Path, - to_path: &impl Path, + from_path: &impl Path, + to_path: &impl Path, ) -> Result<(), RuntimeError> { let res = unsafe { SmartRollupCore::store_move( @@ -541,8 +541,8 @@ where fn store_copy( &mut self, - from_path: &impl Path, - to_path: &impl Path, + from_path: &impl Path, + to_path: &impl Path, ) -> Result<(), RuntimeError> { let res = unsafe { SmartRollupCore::store_copy( @@ -665,7 +665,7 @@ where } } - fn store_value_size(&self, path: &impl Path) -> Result { + fn store_value_size(&self, path: &impl Path) -> Result { let res = unsafe { SmartRollupCore::store_value_size(self, path.as_ptr(), path.size()) }; @@ -682,14 +682,14 @@ where } fn last_run_aborted(&self) -> Result { - const PATH_STUCK_FLAG: RefPath = + const PATH_STUCK_FLAG: RefPath = RefPath::assert_from_readonly(b"/readonly/kernel/env/stuck"); let last_run_aborted = Runtime::store_has(self, &PATH_STUCK_FLAG)?.is_some(); Ok(last_run_aborted) } fn upgrade_failed(&self) -> Result { - const PATH_UPGRADE_ERROR_FLAG: RefPath = + const PATH_UPGRADE_ERROR_FLAG: RefPath = RefPath::assert_from_readonly(b"/readonly/kernel/env/upgrade_error"); let upgrade_failed = Runtime::store_has(self, &PATH_UPGRADE_ERROR_FLAG)?.is_some(); @@ -697,7 +697,7 @@ where } fn restart_forced(&self) -> Result { - const PATH_TOO_MANY_REBOOT_FLAG: RefPath = + const PATH_TOO_MANY_REBOOT_FLAG: RefPath = RefPath::assert_from_readonly(b"/readonly/kernel/env/too_many_reboot"); let restart_forced = Runtime::store_has(self, &PATH_TOO_MANY_REBOOT_FLAG)?.is_some(); @@ -705,7 +705,7 @@ where } fn reboot_left(&self) -> Result { - const PATH_REBOOT_COUNTER: RefPath = + const PATH_REBOOT_COUNTER: RefPath = RefPath::assert_from_readonly(b"/readonly/kernel/env/reboot_counter"); const SIZE: usize = core::mem::size_of::(); @@ -718,7 +718,7 @@ where #[cfg(feature = "alloc")] fn runtime_version(&self) -> Result { - const PATH_VERSION: RefPath = + const PATH_VERSION: RefPath = RefPath::assert_from_readonly(b"/readonly/wasm_version"); let bytes = Runtime::store_read(self, &PATH_VERSION, 0, 9)?; // SAFETY: This storage can only contains valid version string which are utf8 safe. @@ -728,8 +728,8 @@ where } fn check_path_has_value<'a>( - runtime: &'a impl Runtime, - path: &'a impl Path, + runtime: &'a impl Runtime, + path: &'a impl Path, ) -> impl FnOnce(RuntimeError) -> RuntimeError + 'a { |err| { if let Ok(Some(ValueType::Value | ValueType::ValueWithSubtree)) = @@ -742,8 +742,8 @@ fn check_path_has_value<'a>( } } -fn check_path_exists<'a, T: Path>( - runtime: &'a impl Runtime, +fn check_path_exists<'a, T: Path>( + runtime: &'a impl Runtime, path: &'a T, ) -> impl FnOnce(RuntimeError) -> RuntimeError + 'a { |err| { @@ -861,7 +861,7 @@ mod tests { fn store_has_existing_return_true() { // Arrange let mut mock = MockSmartRollupCore::new(); - let existing_path = RefPath::assert_from("/an/Existing/path".as_bytes()); + let existing_path = RefPath::::assert_from("/an/Existing/path".as_bytes()); mock.expect_store_has() .withf(move |ptr, size| { @@ -911,7 +911,7 @@ mod tests { fn store_has_not_existing_returns_false() { // Arrange let path_bytes = String::from("/does/not.exist").into_bytes(); - let non_existent_path: OwnedPath = RefPath::assert_from(&path_bytes).into(); + let non_existent_path: OwnedPath = RefPath::assert_from(&path_bytes).into(); let mock = mock_path_not_existing(path_bytes); @@ -926,7 +926,7 @@ mod tests { fn store_read_max_bytes() { // Arrange const FRACTION: usize = 1; - const PATH: RefPath<'static> = RefPath::assert_from("/a/simple/path".as_bytes()); + const PATH: RefPath<'static, true> = RefPath::assert_from("/a/simple/path".as_bytes()); const OFFSET: usize = 5; let mut mock = mock_path_exists(PATH.as_bytes()); @@ -958,7 +958,7 @@ mod tests { fn store_read_lt_max_bytes() { // Arrange const FRACTION: usize = 5; - const PATH: RefPath<'static> = RefPath::assert_from("/a/simple/path".as_bytes()); + const PATH: RefPath<'static, true> = RefPath::assert_from("/a/simple/path".as_bytes()); const OFFSET: usize = 10; let mut mock = mock_path_exists(PATH.as_bytes()); @@ -990,7 +990,7 @@ mod tests { fn store_read_path_not_found() { // Arrange let bytes = "/a/2nd/PATH.which/doesnt/exist".as_bytes().to_vec(); - let path: OwnedPath = RefPath::assert_from(&bytes).into(); + let path: OwnedPath = RefPath::assert_from(&bytes).into(); let offset = 25; let mock = mock_path_not_existing(bytes); @@ -1008,7 +1008,7 @@ mod tests { // The value read is formed of 3 chunks, two of the max chunk value and // the last one being less than the max size. - const PATH: RefPath<'static> = RefPath::assert_from("/a/simple/path".as_bytes()); + const PATH: RefPath<'static, true> = RefPath::assert_from("/a/simple/path".as_bytes()); const VALUE_FIRST_CHUNK: [u8; MAX_FILE_CHUNK_SIZE] = [b'a'; MAX_FILE_CHUNK_SIZE]; const VALUE_SECOND_CHUNK: [u8; MAX_FILE_CHUNK_SIZE] = [b'b'; MAX_FILE_CHUNK_SIZE]; const VALUE_LAST_CHUNK: [u8; MAX_FILE_CHUNK_SIZE / 2] = @@ -1065,7 +1065,7 @@ mod tests { #[test] fn store_write_ok() { // Arrange - const PATH: RefPath<'static> = RefPath::assert_from("/a/simple/path".as_bytes()); + const PATH: RefPath<'static, true> = RefPath::assert_from("/a/simple/path".as_bytes()); const OUTPUT: &[u8] = "One two three four five".as_bytes(); const OFFSET: usize = 12398; @@ -1091,7 +1091,7 @@ mod tests { #[test] fn store_write_too_large() { // Arrange - const PATH: RefPath<'static> = RefPath::assert_from("/a/simple/path".as_bytes()); + const PATH: RefPath<'static, true> = RefPath::assert_from("/a/simple/path".as_bytes()); const OUTPUT: &[u8] = "once I saw a fish alive".as_bytes(); const OFFSET: usize = 0; @@ -1119,7 +1119,7 @@ mod tests { #[test] fn store_write_all() { - const PATH: RefPath<'static> = RefPath::assert_from("/a/simple/path".as_bytes()); + const PATH: RefPath<'static, true> = RefPath::assert_from("/a/simple/path".as_bytes()); const VALUE_FIRST_CHUNK: [u8; MAX_FILE_CHUNK_SIZE] = [b'a'; MAX_FILE_CHUNK_SIZE]; const VALUE_SECOND_CHUNK: [u8; MAX_FILE_CHUNK_SIZE] = [b'b'; MAX_FILE_CHUNK_SIZE]; const VALUE_LAST_CHUNK: [u8; MAX_FILE_CHUNK_SIZE / 2] = @@ -1195,7 +1195,7 @@ mod tests { #[test] fn store_delete() { // Arrange - const PATH: RefPath<'static> = + const PATH: RefPath<'static, true> = RefPath::assert_from("/a/2nd/PATH.which/does/exist".as_bytes()); let mut mock = mock_path_exists(PATH.as_bytes()); @@ -1218,7 +1218,7 @@ mod tests { fn store_delete_path_not_found() { // Arrange let bytes = String::from("/a/2nd/PATH.which/doesnt/exist").into_bytes(); - let path: OwnedPath = RefPath::assert_from(&bytes).into(); + let path: OwnedPath = RefPath::assert_from(&bytes).into(); let mut mock = mock_path_not_existing(bytes); @@ -1232,10 +1232,10 @@ mod tests { #[test] fn store_delete_value() { // Arrange - const PATH: RefPath<'static> = + const PATH: RefPath<'static, true> = RefPath::assert_from("/a/PATH.which/does/exist".as_bytes()); - const REMAINING_PATH: RefPath<'static> = + const REMAINING_PATH: RefPath<'static, true> = RefPath::assert_from("/a/PATH.which/does/exist/and/survived".as_bytes()); let mut mock = mock_path_exists(PATH.as_bytes()); @@ -1265,7 +1265,7 @@ mod tests { #[test] fn store_count_subkeys() { // Arrange - const PATH: RefPath<'static> = + const PATH: RefPath<'static, true> = RefPath::assert_from("/prefix/of/other/keys".as_bytes()); let subkey_count = 14; @@ -1316,7 +1316,7 @@ mod tests { #[test] fn store_value_size() { let mut mock = MockSmartRollupCore::new(); - const PATH: RefPath<'static> = RefPath::assert_from(b"/prefix/of/other/paths"); + const PATH: RefPath<'static, true> = RefPath::assert_from(b"/prefix/of/other/paths"); let size = 256_usize; mock.expect_store_has() .return_const(tezos_smart_rollup_core::VALUE_TYPE_VALUE); @@ -1329,7 +1329,7 @@ mod tests { #[test] fn store_value_size_path_not_found() { let mut mock = MockSmartRollupCore::new(); - const PATH: RefPath<'static> = RefPath::assert_from(b"/prefix/of/other/paths"); + const PATH: RefPath<'static, true> = RefPath::assert_from(b"/prefix/of/other/paths"); mock.expect_store_value_size() .return_const(tezos_smart_rollup_core::STORE_NOT_A_VALUE); mock.expect_store_has() diff --git a/src/kernel_sdk/installer-client/src/config.rs b/src/kernel_sdk/installer-client/src/config.rs index 336612d748aeca4434de30a49d9f37ee4022badc..cfc9281ebdedd770113a5e033d4b0fe672e03935 100644 --- a/src/kernel_sdk/installer-client/src/config.rs +++ b/src/kernel_sdk/installer-client/src/config.rs @@ -24,10 +24,10 @@ pub enum ConfigurationError { } // Path that we write the kernel to, before upgrading. -const PREPARE_KERNEL_PATH: RefPath = RefPath::assert_from(b"/installer/kernel/boot.wasm"); +const PREPARE_KERNEL_PATH: RefPath = RefPath::assert_from(b"/installer/kernel/boot.wasm"); // Path of currently running kernel. -const KERNEL_BOOT_PATH: RefPath = RefPath::assert_from(b"/kernel/boot.wasm"); +const KERNEL_BOOT_PATH: RefPath = RefPath::assert_from(b"/kernel/boot.wasm"); pub fn create_installer_config( root_hash: PreimageHash, diff --git a/src/kernel_sdk/installer-client/src/lib.rs b/src/kernel_sdk/installer-client/src/lib.rs index 6a5e79ff9d71990e3950f5c0a2563a7e9926f97e..f979b69804860ee18384a0b0c9b77d030a933732 100644 --- a/src/kernel_sdk/installer-client/src/lib.rs +++ b/src/kernel_sdk/installer-client/src/lib.rs @@ -9,8 +9,8 @@ pub mod preimages; use tezos_smart_rollup_host::path::RefPath; // Path that we write the kernel to, before upgrading. -pub const PREPARE_KERNEL_PATH: RefPath = +pub const PREPARE_KERNEL_PATH: RefPath = RefPath::assert_from(b"/installer/kernel/boot.wasm"); // Path of currently running kernel. -pub const KERNEL_BOOT_PATH: RefPath = RefPath::assert_from(b"/kernel/boot.wasm"); +pub const KERNEL_BOOT_PATH: RefPath = RefPath::assert_from(b"/kernel/boot.wasm"); diff --git a/src/kernel_sdk/installer-config/src/binary/bin.rs b/src/kernel_sdk/installer-config/src/binary/bin.rs index 2cd4d945f92ccfc83e238922aced2e0d125b685a..c2507c071f3a123e2ee8aa3f692fbe802461dfb8 100644 --- a/src/kernel_sdk/installer-config/src/binary/bin.rs +++ b/src/kernel_sdk/installer-config/src/binary/bin.rs @@ -28,7 +28,7 @@ fn put_le_size(size: usize, out: &mut Vec) -> BinResult { } /// Encode RefPath as a bytes with prepended path size -fn path_dynamic(p: &impl Path, output: &mut Vec) -> BinResult { +fn path_dynamic(p: &impl Path, output: &mut Vec) -> BinResult { let data = p.as_bytes(); // We can cast it to u8 as path length doesn't exceed 250 let size = data.len() as u8; @@ -49,7 +49,7 @@ impl<'a> BinWriter for RefBytes<'a> { } } -impl BinWriter for MoveInstruction

{ +impl> BinWriter for MoveInstruction

{ fn bin_write(&self, out: &mut Vec) -> tezos_data_encoding::enc::BinResult { (|data: &Self, out: &mut Vec| { tezos_data_encoding::enc::field("MoveInstruction::from", path_dynamic)( @@ -63,7 +63,7 @@ impl BinWriter for MoveInstruction

{ } } -impl> BinWriter for RevealInstruction { +impl, B: AsRef<[u8]>> BinWriter for RevealInstruction { fn bin_write(&self, out: &mut Vec) -> tezos_data_encoding::enc::BinResult { (|data: &Self, out: &mut Vec| { field("RevealInstruction::hash", bytes_dynamic)(&data.hash, out)?; @@ -73,7 +73,7 @@ impl> BinWriter for RevealInstruction { } } -impl> BinWriter for SetInstruction { +impl, B: AsRef<[u8]>> BinWriter for SetInstruction { fn bin_write(&self, out: &mut Vec) -> tezos_data_encoding::enc::BinResult { (|data: &Self, out: &mut Vec| { field("SetInstruction::value", bytes_dynamic)(&data.value, out)?; @@ -83,7 +83,7 @@ impl> BinWriter for SetInstruction { } } -impl> BinWriter for ConfigInstruction { +impl, B: AsRef<[u8]>> BinWriter for ConfigInstruction { fn bin_write(&self, out: &mut Vec) -> tezos_data_encoding::enc::BinResult { use tezos_data_encoding::enc::{u8, variant_with_field}; match self { diff --git a/src/kernel_sdk/installer-config/src/binary/instr.rs b/src/kernel_sdk/installer-config/src/binary/instr.rs index 2fccbc676790e58bb7f88861f3ee4cd52f2e24d7..9cd0aa77c4f97cb52cac55943196c6af13f26894 100644 --- a/src/kernel_sdk/installer-config/src/binary/instr.rs +++ b/src/kernel_sdk/installer-config/src/binary/instr.rs @@ -50,7 +50,7 @@ pub enum ConfigInstruction { Set(SetInstruction), } -pub type RefConfigInstruction<'a> = ConfigInstruction, RefBytes<'a>>; +pub type RefConfigInstruction<'a> = ConfigInstruction, RefBytes<'a>>; #[cfg(feature = "alloc")] pub mod owned { @@ -73,13 +73,13 @@ pub mod owned { } } - pub type OwnedConfigInstruction = ConfigInstruction; + pub type OwnedConfigInstruction = ConfigInstruction, OwnedBytes>; #[derive(Debug, PartialEq, Eq)] pub struct OwnedConfigProgram(pub Vec); impl OwnedConfigProgram { - pub fn evaluate(&self, host: &mut impl Runtime) -> Result<(), &'static str> { + pub fn evaluate(&self, host: &mut impl Runtime) -> Result<(), &'static str> { for instruction in self.0.iter() { eval_config_instr(host, instruction)? } @@ -96,18 +96,18 @@ pub mod owned { } impl OwnedConfigInstruction { - pub fn reveal_instr(hash: PreimageHash, to: OwnedPath) -> Self { + pub fn reveal_instr(hash: PreimageHash, to: OwnedPath) -> Self { OwnedConfigInstruction::Reveal(RevealInstruction { hash: OwnedBytes(hash.into()), to, }) } - pub fn move_instr(from: OwnedPath, to: OwnedPath) -> Self { + pub fn move_instr(from: OwnedPath, to: OwnedPath) -> Self { OwnedConfigInstruction::Move(MoveInstruction { from, to }) } - pub fn set_instr(value: OwnedBytes, to: OwnedPath) -> Self { + pub fn set_instr(value: OwnedBytes, to: OwnedPath) -> Self { OwnedConfigInstruction::Set(SetInstruction { value, to }) } } @@ -148,8 +148,8 @@ pub mod evaluation { /// config.evaluate(host) /// } /// ``` - pub fn eval_config_instr>( - host: &mut impl Runtime, + pub fn eval_config_instr, Bytes: AsRef<[u8]>>( + host: &mut impl Runtime, config_instr: &ConfigInstruction, ) -> Result<(), &'static str> { match config_instr { @@ -181,7 +181,7 @@ pub mod promote { KERNEL_BOOT_PATH, }; - const TMP_REVEAL_PATH: RefPath = RefPath::assert_from(b"/__sdk/installer/reveal"); + const TMP_REVEAL_PATH: RefPath = RefPath::assert_from(b"/__sdk/installer/reveal"); pub fn upgrade_reveal_flow( root_hash: [u8; PREIMAGE_HASH_SIZE], diff --git a/src/kernel_sdk/installer-config/src/binary/nom.rs b/src/kernel_sdk/installer-config/src/binary/nom.rs index b6689cfa8eb23c0a8628ca56b5c771229cbff00c..ced462c1ed89441f226834655dcd183fb5525778 100644 --- a/src/kernel_sdk/installer-config/src/binary/nom.rs +++ b/src/kernel_sdk/installer-config/src/binary/nom.rs @@ -67,16 +67,16 @@ fn bounded_u8_size(max: usize) -> impl FnMut(NomInput) -> NomResult { } } -fn nom_read_ref_path(input: &[u8]) -> NomResult { +fn nom_read_ref_path(input: &[u8]) -> NomResult> { map_res( complete(nom::multi::length_data(bounded_u8_size(PATH_MAX_SIZE))), - |bytes| Ok::, NomError<'_>>(RefPath::assert_from(bytes)), + |bytes| Ok::, NomError<'_>>(RefPath::assert_from(bytes)), )(input) } pub fn read_size( - host: &impl Runtime, - path: &impl Path, + host: &impl Runtime, + path: &impl Path, offset: &mut usize, ) -> Result { let mut size_buffer = [0; 4]; @@ -104,7 +104,7 @@ impl<'a> NomReader<'a> for RefBytes<'a> { } } -impl<'a> NomReader<'a> for RevealInstruction, RefBytes<'a>> { +impl<'a> NomReader<'a> for RevealInstruction, RefBytes<'a>> { fn nom_read(bytes: &'a [u8]) -> NomResult { map( nom::sequence::tuple(( @@ -116,7 +116,7 @@ impl<'a> NomReader<'a> for RevealInstruction, RefBytes<'a>> { } } -impl<'a> NomReader<'a> for MoveInstruction> { +impl<'a> NomReader<'a> for MoveInstruction> { fn nom_read(bytes: &'a [u8]) -> NomResult { map( tuple((nom_read_ref_path, nom_read_ref_path)), @@ -125,7 +125,7 @@ impl<'a> NomReader<'a> for MoveInstruction> { } } -impl<'a> NomReader<'a> for SetInstruction, RefBytes<'a>> { +impl<'a> NomReader<'a> for SetInstruction, RefBytes<'a>> { fn nom_read(bytes: &'a [u8]) -> NomResult { map( nom::sequence::tuple(( @@ -137,20 +137,20 @@ impl<'a> NomReader<'a> for SetInstruction, RefBytes<'a>> { } } -impl<'a> NomReader<'a> for ConfigInstruction, RefBytes<'a>> { +impl<'a> NomReader<'a> for ConfigInstruction, RefBytes<'a>> { fn nom_read(bytes: &'a [u8]) -> NomResult { let (input, tag) = nom::number::complete::u8(bytes)?; let (input, variant) = match tag { 0 => (map( - , RefBytes<'a>> as NomReader>::nom_read, + , RefBytes<'a>> as NomReader>::nom_read, ConfigInstruction::Reveal, ))(input)?, 1 => (map( - > as NomReader>::nom_read, + > as NomReader>::nom_read, ConfigInstruction::Move, ))(input)?, 2 => (map( - , RefBytes<'a>> as NomReader>::nom_read, + , RefBytes<'a>> as NomReader>::nom_read, ConfigInstruction::Set, ))(input)?, _ => { diff --git a/src/kernel_sdk/installer-config/src/binary/preimage.rs b/src/kernel_sdk/installer-config/src/binary/preimage.rs index f4a6a354b2367bcfe89a851649a1c7adf8a9878e..c245cb5591147d4af6eaa4b66be27bfd0b9c6686 100644 --- a/src/kernel_sdk/installer-config/src/binary/preimage.rs +++ b/src/kernel_sdk/installer-config/src/binary/preimage.rs @@ -18,9 +18,9 @@ const MAX_DAC_LEVELS: usize = 4; /// This function will reveal the root hash by writing and storing the /// hash at the appropriate and intended path. pub fn reveal_root_hash_to_store( - host: &mut impl Runtime, + host: &mut impl Runtime, root_hash: &[u8; PREIMAGE_HASH_SIZE], - reveal_to: &impl Path, + reveal_to: &impl Path, ) -> Result<(), &'static str> { let mut reveal_buffer = [0; MAX_PAGE_SIZE * MAX_DAC_LEVELS]; @@ -37,8 +37,8 @@ pub fn reveal_root_hash_to_store( } /// Appends the content of the page path given. -fn write_kernel_page( - reveal_to: &impl Path, +fn write_kernel_page>( + reveal_to: &impl Path, ) -> impl FnMut(&mut Host, V0SliceContentPage) -> Result<(), &'static str> + '_ { let mut kernel_size = 0; move |host, page| { @@ -48,11 +48,11 @@ fn write_kernel_page( } } -fn append_content( +fn append_content>( host: &mut Host, kernel_size: usize, content: V0SliceContentPage, - reveal_to: &impl Path, + reveal_to: &impl Path, ) -> Result { let content = content.as_ref(); @@ -69,7 +69,7 @@ mod test { use tezos_smart_rollup_host::{path::RefPath, runtime::Runtime}; use tezos_smart_rollup_mock::MockHost; - const TMP_REVEAL_PATH: RefPath = RefPath::assert_from(b"/__sdk/installer/reveal"); + const TMP_REVEAL_PATH: RefPath = RefPath::assert_from(b"/__sdk/installer/reveal"); fn preliminary_upgrade(host: &mut MockHost) -> (PreimageHash, Vec) { let kernel = [1_u8; 1000]; diff --git a/src/kernel_sdk/installer-config/src/yaml/convert.rs b/src/kernel_sdk/installer-config/src/yaml/convert.rs index 9e475825ced16d3d8d898c0d29391f3a2b6d6339..f61a8324aee681bb0c44c6b80f4f8b6b7f37a49c 100644 --- a/src/kernel_sdk/installer-config/src/yaml/convert.rs +++ b/src/kernel_sdk/installer-config/src/yaml/convert.rs @@ -22,7 +22,7 @@ pub enum ConfigConversionError { #[error("Invalid reveal path: {0}")] PathError(PathError), #[error("Unable to convert set for {0} to reveal")] - SetToReveal(OwnedPath), + SetToReveal(OwnedPath), } pub fn reveal_instr_hex( diff --git a/src/kernel_sdk/installer-kernel/src/instr.rs b/src/kernel_sdk/installer-kernel/src/instr.rs index 90aba7bd3f06027af0e99e038e8464af303382d3..95a348b9051234480c891b2a2a5734448b441180 100644 --- a/src/kernel_sdk/installer-kernel/src/instr.rs +++ b/src/kernel_sdk/installer-kernel/src/instr.rs @@ -9,8 +9,8 @@ use tezos_smart_rollup::storage::path::{Path, RefPath}; use tezos_smart_rollup_installer_config::binary::read_size; pub fn read_config_program_size( - host: &impl Runtime, - config_interpretation_path: &RefPath, + host: &impl Runtime, + config_interpretation_path: &RefPath, ) -> Result { let kernel_size = host .store_value_size(config_interpretation_path) @@ -25,8 +25,8 @@ pub fn read_config_program_size( } pub fn read_instruction_bytes( - host: &impl Runtime, - path: &impl Path, + host: &impl Runtime, + path: &impl Path, offset: &mut usize, mut buffer: &mut [u8], ) -> Result<(), &'static str> { diff --git a/src/kernel_sdk/installer-kernel/src/lib.rs b/src/kernel_sdk/installer-kernel/src/lib.rs index 9235476c689085ee0695e52a2a34e2f9a1d07ae1..e50d74600bc0e2f740cec51961d7bf4f71ed9568 100644 --- a/src/kernel_sdk/installer-kernel/src/lib.rs +++ b/src/kernel_sdk/installer-kernel/src/lib.rs @@ -33,16 +33,16 @@ use tezos_smart_rollup_installer_config::binary::{ }; // Path of currently running kernel. -const KERNEL_BOOT_PATH: RefPath = RefPath::assert_from(b"/kernel/boot.wasm"); +const KERNEL_BOOT_PATH: RefPath = RefPath::assert_from(b"/kernel/boot.wasm"); // Installer kernel will copy to this path before execution of config. // This is done in order avoid rewriting kernel during config execution. -const AUXILIARY_CONFIG_INTERPRETATION_PATH: RefPath = +const AUXILIARY_CONFIG_INTERPRETATION_PATH: RefPath = RefPath::assert_from(b"/__installer_kernel/auxiliary/kernel/boot.wasm"); /// Installer. #[cfg_attr(feature = "entrypoint", entrypoint::main)] -pub fn installer(host: &mut Host) { +pub fn installer>(host: &mut Host) { if let Err(e) = install_kernel(host, KERNEL_BOOT_PATH) { Runtime::write_debug(host, e) } else { @@ -71,8 +71,8 @@ fn panic(_info: &PanicInfo) -> ! { /// was written to. // TODO: provide a concrete example (see https://gitlab.com/tezos/tezos/-/issues/5855) pub fn install_kernel( - host: &mut impl Runtime, - config_interpretation_path: RefPath, + host: &mut impl Runtime, + config_interpretation_path: RefPath, ) -> Result<(), &'static str> { if let Ok(config_program_size) = read_config_program_size(host, &config_interpretation_path) diff --git a/src/kernel_sdk/mock/src/state/in_memory_store.rs b/src/kernel_sdk/mock/src/state/in_memory_store.rs index 08ee6d066bf868f9d9aa8806e0a9519a03ff52e6..8e2d492c1839628e9206e99ed47a41ddd7d178dd 100644 --- a/src/kernel_sdk/mock/src/state/in_memory_store.rs +++ b/src/kernel_sdk/mock/src/state/in_memory_store.rs @@ -129,7 +129,7 @@ impl InMemoryStore { } fn validate_path(s: &[u8]) -> Result { - match RefPath::try_from(s) { + match RefPath::::try_from(s) { Err(PathError::PathTooLong) => Err(Error::StoreKeyTooLarge), Err(_) => Err(Error::StoreInvalidKey), Ok(_) => { @@ -152,7 +152,7 @@ fn validate_path_maybe_readonly(s: &[u8]) -> Result { } else { s }; - match RefPath::try_from(to_check) { + match RefPath::::try_from(to_check) { Err(PathError::PathTooLong) => Err(Error::StoreKeyTooLarge), Err(_) => Err(Error::StoreInvalidKey), Ok(_) => { diff --git a/src/kernel_sdk/sdk/src/outbox.rs b/src/kernel_sdk/sdk/src/outbox.rs index 9bf8b5e798d71467a06311f29449a3914cd2c552..e9adc4e5db41ebf9b186d2ec1df4c9d1af82d9bb 100644 --- a/src/kernel_sdk/sdk/src/outbox.rs +++ b/src/kernel_sdk/sdk/src/outbox.rs @@ -84,9 +84,9 @@ use tezos_smart_rollup_host::{ use alloc::vec::Vec; -const OUTBOX_QUEUE_ROOT: RefPath = RefPath::assert_from(b"/__sdk/outbox"); -const OUTBOX_QUEUE_META: RefPath = RefPath::assert_from(b"/__sdk/outbox/meta"); -const META_SUFFIX: RefPath = RefPath::assert_from(b"/meta"); +const OUTBOX_QUEUE_ROOT: RefPath = RefPath::assert_from(b"/__sdk/outbox"); +const OUTBOX_QUEUE_META: RefPath = RefPath::assert_from(b"/__sdk/outbox/meta"); +const META_SUFFIX: RefPath = RefPath::assert_from(b"/meta"); /// The default outbox queue. /// @@ -95,9 +95,9 @@ const META_SUFFIX: RefPath = RefPath::assert_from(b"/meta"); /// It is configured with a maximum queue size of 65536. If you flush the queue every /// tezos level, it will take roughly 2h45mins for a message to make its way through /// the queue, and be written to the outbox. -pub const OUTBOX_QUEUE: OutboxQueue<'static, RefPath> = OutboxQueue { +pub const OUTBOX_QUEUE: OutboxQueue<'static, true, RefPath> = OutboxQueue { root: &OUTBOX_QUEUE_ROOT, - meta: None, + meta: &OUTBOX_QUEUE_META, max: u16::MAX as u32, }; @@ -105,31 +105,39 @@ pub const OUTBOX_QUEUE: OutboxQueue<'static, RefPath> = OutboxQueue { /// /// See [`OUTBOX_QUEUE`] for more information. #[derive(Debug)] -pub struct OutboxQueue<'a, P: Path> { +pub struct OutboxQueue<'a, const IS_ABSOLUTE: bool, P: Path> { root: &'a P, - meta: Option, + meta: &'a P, max: u32, } -impl<'a, P: Path> OutboxQueue<'a, P> { + + +impl<'a, const IS_ABSOLUTE: bool, P: Path> OutboxQueue<'a, IS_ABSOLUTE, P> { /// Setup the outbox queue to operate over non-default parameters. /// /// - `root` specifies the path in storage that the queue will be stored at. /// - `max` allows you to specify a different maximum queue length to the default. - pub fn new(root: &'a P, max: u32) -> Result { + pub fn new(root: &'a P, meta: &'a P, max: u32) -> Result { // Ensure we have enough room for the hex-encoded u32 indexes if root.size() > PATH_MAX_SIZE - 2 * core::mem::size_of::() + 1 { return Err(PathError::PathTooLong); } // The meta suffix is less long than the index paths. - let meta = concat(root, &META_SUFFIX)?; + let meta_expected = concat(root, &META_SUFFIX)?; + if meta_expected == meta.into() { Ok(Self { root, max, - meta: Some(meta), + meta, }) + } else { + // TODO: add a proper error to PathError for this case + Err(PathError::PathTooLong) + } + } /// Queues a message into the outbox queue. @@ -140,7 +148,7 @@ impl<'a, P: Path> OutboxQueue<'a, P> { /// [`flush_queue`]: Self::flush_queue pub fn queue_message( &self, - host: &mut impl Runtime, + host: &mut impl Runtime, message: impl Into>, ) -> Result { let (start, len) = self.read_meta(host); @@ -183,7 +191,7 @@ impl<'a, P: Path> OutboxQueue<'a, P> { /// `flush_queue` twice, on the same *durable storage state*, then a single message could be /// executed twice - leading to double spending of an L2 account's funds on L1 - this would result /// later in some accounts being unable to execute otherwise valid withdrawals on L1. - pub fn flush_queue(&self, host: &mut impl Runtime) -> usize { + pub fn flush_queue(&self, host: &mut impl Runtime) -> usize { // Consider: `store_read_extend(&mut Vec)` let mut num_flushed = 0; let (mut start, mut len) = self.read_meta(host); @@ -222,7 +230,7 @@ impl<'a, P: Path> OutboxQueue<'a, P> { // if we had a 'path::append_hex_into' method, we could have an alloc free // version of this. // 'concat_into' would be useful also. - fn message_path(&self, idx: u32) -> OwnedPath { + fn message_path(&self, idx: u32) -> OwnedPath { let mut path = [b'/'; 1 + 2 * core::mem::size_of::()]; let idx = idx.to_le_bytes(); @@ -235,15 +243,12 @@ impl<'a, P: Path> OutboxQueue<'a, P> { } #[inline(always)] - fn read_meta(&self, host: &impl Runtime) -> (u32, u32) { + fn read_meta(&self, host: &impl Runtime) -> (u32, u32) { const BUFF_SIZE: usize = 2 * core::mem::size_of::(); let mut buffer = [0; BUFF_SIZE]; - let result = if let Some(meta) = &self.meta { - host.store_read_slice(meta, 0, buffer.as_mut_slice()) - } else { - host.store_read_slice(&OUTBOX_QUEUE_META, 0, buffer.as_mut_slice()) - }; + let result = + host.store_read_slice(self.meta, 0, buffer.as_mut_slice()); let Ok(BUFF_SIZE) = result else { return (0, 0) }; @@ -254,7 +259,7 @@ impl<'a, P: Path> OutboxQueue<'a, P> { } #[inline(always)] - fn save_meta(&self, host: &mut impl Runtime, start: u32, len: u32) { + fn save_meta(&self, host: &mut impl Runtime, start: u32, len: u32) { if len == 0 { // We only call this when we've just emptied the queue let _ = host.store_delete(self.root); @@ -266,12 +271,7 @@ impl<'a, P: Path> OutboxQueue<'a, P> { let buffer = [s0, s1, s2, s3, l0, l1, l2, l3]; - if let Some(meta) = &self.meta { - host.store_write(meta, buffer.as_slice(), 0).unwrap(); - } else { - host.store_write(&OUTBOX_QUEUE_META, buffer.as_slice(), 0) - .unwrap(); - } + host.store_write(self.meta, buffer.as_slice(), 0).unwrap(); } } @@ -340,7 +340,7 @@ mod test { #[test] fn push_overflow_then_flush() { let mut host = MockHost::default(); - let root = RefPath::assert_from(b"/hello"); + let root = RefPath::::assert_from(b"/hello"); let queue = OutboxQueue::new(&root, 15).unwrap(); let start = u32::MAX - 12; diff --git a/src/kernel_sdk/storage/src/layer.rs b/src/kernel_sdk/storage/src/layer.rs index f290c0cf813977f3bd9a98af4f73023a90ce45e0..324eae4a2e6eca7822a045fc995f73c2238bbff4 100644 --- a/src/kernel_sdk/storage/src/layer.rs +++ b/src/kernel_sdk/storage/src/layer.rs @@ -20,8 +20,8 @@ use core::marker::PhantomData; use tezos_smart_rollup_host::path::{concat, OwnedPath, Path}; use tezos_smart_rollup_host::runtime::{Runtime, RuntimeError, ValueType}; -pub(crate) struct Layer> { - pub(crate) path: OwnedPath, +pub(crate) struct Layer>> { + pub(crate) path: OwnedPath, phantom: PhantomData, } @@ -31,7 +31,7 @@ fn has_subtree_res(v: Result, RuntimeError>) -> bool { matches!(v, Ok(Some(Subtree | ValueWithSubtree))) } -impl> Layer { +impl>> Layer { /// Create layer for the path given. /// /// Create a new layer object with all account data stored under path given, ie, @@ -39,7 +39,7 @@ impl> Layer { /// contain all data for account with id "alpha". /// /// This function is used solely for creating the bottom layer of the storage. - pub(crate) fn with_path(name: &impl Path) -> Self { + pub(crate) fn with_path(name: &impl Path) -> Self { Self { path: OwnedPath::from(name), phantom: PhantomData, @@ -56,8 +56,8 @@ impl> Layer { /// responsability of the caller to do so. pub(crate) fn force_make_copy( &self, - host: &mut impl Runtime, - name: &impl Path, + host: &mut impl Runtime, + name: &impl Path, ) -> Result { let copy = Self { path: OwnedPath::from(name), @@ -77,8 +77,8 @@ impl> Layer { /// [make_copy] function. pub(crate) fn consume( &mut self, - host: &mut impl Runtime, - layer: Layer, + host: &mut impl Runtime, + layer: Layer, ) -> Result<(), StorageError> { if let Ok(Some(_)) = host.store_has(&layer.path) { // The layer we consume has content, so move it @@ -100,8 +100,8 @@ impl> Layer { /// when the object does so. pub(crate) fn create_new( &mut self, - host: &impl Runtime, - id: &impl Path, + host: &impl Runtime, + id: &impl Path, ) -> Result, StorageError> { let account_path = concat(&self.path, id)?; @@ -118,8 +118,8 @@ impl> Layer { /// that there is some data in durable storage for the object in this layer. pub(crate) fn get( &self, - host: &impl Runtime, - id: &impl Path, + host: &impl Runtime, + id: &impl Path, ) -> Result, StorageError> { let account_path = concat(&self.path, id)?; @@ -136,8 +136,8 @@ impl> Layer { /// instead. (Use case). pub(crate) fn get_or_create( &self, - _host: &impl Runtime, - id: &impl Path, + _host: &impl Runtime, + id: &impl Path, ) -> Result { // We could get rid of the host parameter, but in the future, it would be nice // if we had the option of interacting with storage when creating an object. @@ -151,8 +151,8 @@ impl> Layer { /// storage. pub(crate) fn delete( &mut self, - host: &mut impl Runtime, - id: &impl Path, + host: &mut impl Runtime, + id: &impl Path, ) -> Result<(), StorageError> { let account_path = concat(&self.path, id)?; @@ -163,7 +163,7 @@ impl> Layer { /// /// Discard the current/self layer. This is the same as cancelling the /// current transaction. - pub(crate) fn discard(self, host: &mut impl Runtime) -> Result<(), StorageError> { + pub(crate) fn discard(self, host: &mut impl Runtime) -> Result<(), StorageError> { if let Ok(Some(_)) = host.store_has(&self.path) { host.store_delete(&self.path).map_err(StorageError::from) } else { diff --git a/src/kernel_sdk/storage/src/storage.rs b/src/kernel_sdk/storage/src/storage.rs index b222eafa7b044771f0c41e5bad9ffcf24c9adef5..96734cb1f14baee8392761443146bf9e6fa43b19 100644 --- a/src/kernel_sdk/storage/src/storage.rs +++ b/src/kernel_sdk/storage/src/storage.rs @@ -13,20 +13,20 @@ use tezos_smart_rollup_host::runtime::Runtime; extern crate alloc; /// Failsafe storage interface -pub struct Storage> { +pub struct Storage>> { prefix: String, - layers: Vec>, + layers: Vec>, } -impl> Storage { +impl>> Storage { /// Create the initial storage - pub fn init(name: &impl Path) -> Result { + pub fn init(name: &impl Path) -> Result { let name_bytes = name.as_bytes().to_vec(); Ok(Self { prefix: String::from_utf8(name_bytes) .map_err(|_| StorageError::InvalidAccountsPath)?, - layers: vec![Layer::::with_path(name)], + layers: vec![Layer::::with_path(name)], }) } @@ -34,8 +34,8 @@ impl> Storage { /// state/transaction and id pub fn get( &self, - host: &impl Runtime, - id: &impl Path, + host: &impl Runtime, + id: &impl Path, ) -> Result, StorageError> { if let Some(top_layer) = self.layers.last() { Ok(top_layer.get(host, id)?) @@ -47,8 +47,8 @@ impl> Storage { /// Get immutable object in state before any transaction began pub fn get_original( &self, - host: &impl Runtime, - id: &impl Path, + host: &impl Runtime, + id: &impl Path, ) -> Result, StorageError> { if let Some(bottom_layer) = self.layers.first() { Ok(bottom_layer.get(host, id)?) @@ -60,8 +60,8 @@ impl> Storage { /// Create a new object as part of current storage state/transaction pub fn create_new( &mut self, - host: &mut impl Runtime, - id: &impl Path, + host: &mut impl Runtime, + id: &impl Path, ) -> Result, StorageError> { if let Some(top_layer) = self.layers.last_mut() { Ok(top_layer.create_new(host, id)?) @@ -72,8 +72,8 @@ impl> Storage { pub fn get_or_create( &self, - host: &impl Runtime, - id: &impl Path, + host: &impl Runtime, + id: &impl Path, ) -> Result { if let Some(top_layer) = self.layers.last() { Ok(top_layer.get_or_create(host, id)?) @@ -85,8 +85,8 @@ impl> Storage { /// Delete an object as part of current storage state/transaction pub fn delete( &mut self, - host: &mut impl Runtime, - id: &impl Path, + host: &mut impl Runtime, + id: &impl Path, ) -> Result<(), StorageError> { if let Some(top_layer) = self.layers.last_mut() { top_layer.delete(host, id) @@ -98,7 +98,7 @@ impl> Storage { /// Begin a new transaction pub fn begin_transaction( &mut self, - host: &mut impl Runtime, + host: &mut impl Runtime, ) -> Result<(), StorageError> { let new_layer_index = self.layers.len() + 1; if let Some(top) = self.layers.last() { @@ -115,7 +115,7 @@ impl> Storage { /// Commit current storage state pub fn commit_transaction( &mut self, - host: &mut impl Runtime, + host: &mut impl Runtime, ) -> Result<(), StorageError> { if self.layers.len() > 1 { if let (Some(top), Some(last)) = (self.layers.pop(), self.layers.last_mut()) { @@ -131,7 +131,7 @@ impl> Storage { /// Abort current storage state pub fn rollback_transaction( &mut self, - host: &mut impl Runtime, + host: &mut impl Runtime, ) -> Result<(), StorageError> { if self.layers.len() > 1 { if let Some(top) = self.layers.pop() { @@ -162,35 +162,35 @@ mod test { #[derive(PartialEq, Debug)] struct TestAccount { - path: OwnedPath, + path: OwnedPath, } - const VALUE_A_PATH: RefPath = RefPath::assert_from(b"/a"); - const VALUE_B_PATH: RefPath = RefPath::assert_from(b"/b"); + const VALUE_A_PATH: RefPath = RefPath::assert_from(b"/a"); + const VALUE_B_PATH: RefPath = RefPath::assert_from(b"/b"); impl TestAccount { - pub fn set_a(&mut self, host: &mut impl Runtime, v: &str) { + pub fn set_a(&mut self, host: &mut impl Runtime, v: &str) { let value_path = concat(&self.path, &VALUE_A_PATH) .expect("The account should have a path for a"); host.store_write(&value_path, v.as_bytes(), 0) .expect("Cannot set value for b") } - pub fn set_b(&mut self, host: &mut impl Runtime, v: &str) { + pub fn set_b(&mut self, host: &mut impl Runtime, v: &str) { let value_path = concat(&self.path, &VALUE_B_PATH) .expect("The account should have a path for b"); host.store_write(&value_path, v.as_bytes(), 0) .expect("Cannot set value for b") } - pub fn get_a(&self, host: &impl Runtime) -> Vec { + pub fn get_a(&self, host: &impl Runtime) -> Vec { let value_path = concat(&self.path, &VALUE_A_PATH) .expect("The account should have a path for a"); host.store_read(&value_path, 0, 1024) .expect("No value for a") } - pub fn get_b(&self, host: &impl Runtime) -> Vec { + pub fn get_b(&self, host: &impl Runtime) -> Vec { let value_path = concat(&self.path, &VALUE_B_PATH) .expect("The account should have a path for b"); host.store_read(&value_path, 0, 1024) @@ -198,13 +198,13 @@ mod test { } } - impl From for TestAccount { - fn from(path: OwnedPath) -> Self { + impl From> for TestAccount { + fn from(path: OwnedPath) -> Self { Self { path } } } - const ACCOUNTS_PATH: RefPath = RefPath::assert_from(b"/accounts"); + const ACCOUNTS_PATH: RefPath = RefPath::assert_from(b"/accounts"); #[test] fn test_commit() {