From a0c7cd8ddf4b8e597a3f872578d21ba50b1a9f63 Mon Sep 17 00:00:00 2001 From: Michael Zaikin Date: Fri, 1 Sep 2023 13:04:13 +0300 Subject: [PATCH 1/2] WIP: compile time kernel settings --- src/kernel_evm/Cargo.lock | 8 +++++ src/kernel_evm/Cargo.toml | 4 ++- src/kernel_evm/custom_kernel/Cargo.toml | 15 +++++++++ src/kernel_evm/custom_kernel/src/lib.rs | 7 ++++ src/kernel_evm/kernel/Cargo.toml | 2 +- src/kernel_evm/kernel/src/block.rs | 45 +++++++++++++------------ src/kernel_evm/kernel/src/lib.rs | 25 ++++++++------ src/kernel_evm/kernel/src/settings.rs | 25 ++++++++++++++ 8 files changed, 96 insertions(+), 35 deletions(-) create mode 100644 src/kernel_evm/custom_kernel/Cargo.toml create mode 100644 src/kernel_evm/custom_kernel/src/lib.rs create mode 100644 src/kernel_evm/kernel/src/settings.rs diff --git a/src/kernel_evm/Cargo.lock b/src/kernel_evm/Cargo.lock index ecf7c171d4a4..55c758602577 100644 --- a/src/kernel_evm/Cargo.lock +++ b/src/kernel_evm/Cargo.lock @@ -236,6 +236,14 @@ dependencies = [ "subtle", ] +[[package]] +name = "custom_kernel" +version = "0.1.0" +dependencies = [ + "evm_kernel", + "tezos-smart-rollup-entrypoint", +] + [[package]] name = "der" version = "0.4.5" diff --git a/src/kernel_evm/Cargo.toml b/src/kernel_evm/Cargo.toml index fa4deeadd123..445bca93c722 100644 --- a/src/kernel_evm/Cargo.toml +++ b/src/kernel_evm/Cargo.toml @@ -9,7 +9,8 @@ members = [ "ethereum", "kernel", "evm_execution", - "logging" + "logging", + "custom_kernel" ] [workspace.dependencies] @@ -44,6 +45,7 @@ libsecp256k1 = { version = "0.7", default-features = false, features = ["static- tezos_ethereum = { path = "./ethereum" } evm-execution = { path = "./evm_execution" } tezos-evm-logging = { path = "./logging" } +evm_kernel = { path = "./kernel" } # SDK tezos-smart-rollup-core = { path = "../kernel_sdk/core", features = ["proto-nairobi"] } diff --git a/src/kernel_evm/custom_kernel/Cargo.toml b/src/kernel_evm/custom_kernel/Cargo.toml new file mode 100644 index 000000000000..9dbd80827e90 --- /dev/null +++ b/src/kernel_evm/custom_kernel/Cargo.toml @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2023 Baking Bad +# +# SPDX-License-Identifier: MIT + +[package] +name = "custom_kernel" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +tezos-smart-rollup-entrypoint.workspace = true +evm_kernel.workspace = true \ No newline at end of file diff --git a/src/kernel_evm/custom_kernel/src/lib.rs b/src/kernel_evm/custom_kernel/src/lib.rs new file mode 100644 index 000000000000..e833cf57dfbe --- /dev/null +++ b/src/kernel_evm/custom_kernel/src/lib.rs @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Baking Bad +// +// SPDX-License-Identifier: MIT + +use evm_kernel::{kernel_loop, settings::DefaultSettings}; + +tezos_smart_rollup_entrypoint::kernel_entry!(kernel_loop::<_, DefaultSettings>); diff --git a/src/kernel_evm/kernel/Cargo.toml b/src/kernel_evm/kernel/Cargo.toml index 93084e174b7a..f2f306f0ce28 100644 --- a/src/kernel_evm/kernel/Cargo.toml +++ b/src/kernel_evm/kernel/Cargo.toml @@ -12,7 +12,7 @@ edition = '2021' build = "build.rs" [lib] -crate-type = ["cdylib"] +crate-type = ["cdylib", "rlib"] [dependencies] thiserror.workspace = true diff --git a/src/kernel_evm/kernel/src/block.rs b/src/kernel_evm/kernel/src/block.rs index 41ad57619c4d..24c6fe8f0aec 100644 --- a/src/kernel_evm/kernel/src/block.rs +++ b/src/kernel_evm/kernel/src/block.rs @@ -10,13 +10,13 @@ use crate::blueprint::Queue; use crate::current_timestamp; use crate::error::Error; use crate::indexable_storage::IndexableStorage; +use crate::settings::KernelSettings; use crate::storage; use crate::storage::init_account_index; use crate::tick_model; use anyhow::Context; use block_in_progress::BlockInProgress; use evm_execution::account_storage::{init_account_storage, EthereumAccountStorage}; -use evm_execution::precompiles; use evm_execution::precompiles::PrecompileBTreeMap; use primitive_types::U256; use tezos_evm_logging::{log, Level::*}; @@ -86,7 +86,7 @@ fn compute( Ok(ComputationResult::Finished) } -pub fn produce( +pub fn produce( host: &mut Host, queue: Queue, ) -> Result<(), anyhow::Error> { @@ -102,7 +102,7 @@ pub fn produce( let mut evm_account_storage = init_account_storage().context("Failed to initialize EVM account storage")?; let mut accounts_index = init_account_index()?; - let precompiles = precompiles::precompile_set::(); + let precompiles = Settings::precompile_set::(); let mut tick_counter = TickCounter::new(tick_model::top_level_overhead_ticks()); for proposal in queue.proposals { @@ -176,6 +176,7 @@ mod tests { use crate::inbox::TransactionContent; use crate::inbox::TransactionContent::Ethereum; use crate::indexable_storage::internal_for_tests::{get_value, length}; + use crate::settings::DefaultSettings; use crate::storage::internal_for_tests::{ read_transaction_receipt, read_transaction_receipt_status, }; @@ -361,7 +362,7 @@ mod tests { U256::from(10000000000000000000u64), ); - produce(host, queue).expect("The block production failed.") + produce::<_, DefaultSettings>(host, queue).expect("The block production failed.") } fn assert_current_block_reading_validity(host: &mut MockHost) { @@ -391,7 +392,7 @@ mod tests { kernel_upgrade: None, }; - produce(&mut host, queue).expect("The block production failed."); + produce::<_, DefaultSettings>(&mut host, queue).expect("The block production failed."); let status = read_transaction_receipt_status(&mut host, &tx_hash) .expect("Should have found receipt"); @@ -425,7 +426,7 @@ mod tests { U256::from(5000000000000000u64), ); - produce(&mut host, queue).expect("The block production failed."); + produce::<_, DefaultSettings>(&mut host, queue).expect("The block production failed."); let status = read_transaction_receipt_status(&mut host, &tx_hash) .expect("Should have found receipt"); @@ -463,7 +464,7 @@ mod tests { U256::from(5000000000000000u64), ); - produce(&mut host, queue).expect("The block production failed."); + produce::<_, DefaultSettings>(&mut host, queue).expect("The block production failed."); let receipt = read_transaction_receipt(&mut host, &tx_hash) .expect("should have found receipt"); @@ -526,7 +527,7 @@ mod tests { U256::from(10000000000000000000u64), ); - produce(&mut host, queue).expect("The block production failed."); + produce::<_, DefaultSettings>(&mut host, queue).expect("The block production failed."); let dest_address = H160::from_str("423163e58aabec5daa3dd1130b759d24bef0f6ea").unwrap(); @@ -570,7 +571,7 @@ mod tests { U256::from(10000000000000000000u64), ); - produce(&mut host, queue).expect("The block production failed."); + produce::<_, DefaultSettings>(&mut host, queue).expect("The block production failed."); 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) @@ -621,7 +622,7 @@ mod tests { U256::from(10000000000000000000u64), ); - produce(&mut host, queue).expect("The block production failed."); + produce::<_, DefaultSettings>(&mut host, queue).expect("The block production failed."); let dest_address = H160::from_str("423163e58aabec5daa3dd1130b759d24bef0f6ea").unwrap(); @@ -653,7 +654,7 @@ mod tests { let indexed_accounts = length(&host, &accounts_index).unwrap(); - produce(&mut host, queue).expect("The block production failed."); + produce::<_, DefaultSettings>(&mut host, queue).expect("The block production failed."); let indexed_accounts_after_produce = length(&host, &accounts_index).unwrap(); @@ -684,7 +685,7 @@ mod tests { kernel_upgrade: None, }; - produce(&mut host, queue).expect("The block production failed."); + produce::<_, DefaultSettings>(&mut host, queue).expect("The block production failed."); let indexed_accounts = length(&host, &accounts_index).unwrap(); @@ -693,7 +694,7 @@ mod tests { kernel_upgrade: None, }; - produce(&mut host, next_queue).expect("The block production failed."); + produce::<_, DefaultSettings>(&mut host, next_queue).expect("The block production failed."); let indexed_accounts_after_second_produce = length(&host, &accounts_index).unwrap(); @@ -737,7 +738,7 @@ mod tests { &sender, U256::from(10000000000000000000u64), ); - produce(&mut host, queue).expect("The block production failed."); + produce::<_, DefaultSettings>(&mut host, queue).expect("The block production failed."); let new_number_of_blocks_indexed = length(&host, &blocks_index).unwrap(); let new_number_of_transactions_indexed = @@ -781,7 +782,7 @@ mod tests { mut evm_account_storage: EthereumAccountStorage, ) -> BlockInProgress { let block_constants = first_block(host); - let precompiles = precompiles::precompile_set::(); + let precompiles = DefaultSettings::precompile_set::(); let mut accounts_index = init_account_index().unwrap(); // init block in progress @@ -888,7 +889,7 @@ mod tests { // init host let mut host = MockHost::default(); let block_constants = first_block(&mut host); - let precompiles = precompiles::precompile_set::(); + let precompiles = DefaultSettings::precompile_set::(); let mut accounts_index = init_account_index().unwrap(); //provision sender account @@ -976,7 +977,7 @@ mod tests { }; // Apply the transaction - produce(&mut host, queue).expect("The block production failed."); + produce::<_, DefaultSettings>(&mut host, queue).expect("The block production failed."); let receipt = read_transaction_receipt(&mut host, &tx_hash) .expect("should have found receipt"); assert_eq!( @@ -1019,17 +1020,17 @@ mod tests { let mut host = MockHost::default(); // first block should be 0 - produce(&mut host, almost_empty_queue()) + produce::<_, DefaultSettings>(&mut host, almost_empty_queue()) .expect("Empty block should have been produced"); check_current_block_number(&mut host, 0); // second block - produce(&mut host, almost_empty_queue()) + produce::<_, DefaultSettings>(&mut host, almost_empty_queue()) .expect("Empty block should have been produced"); check_current_block_number(&mut host, 1); // third block - produce(&mut host, almost_empty_queue()) + produce::<_, DefaultSettings>(&mut host, almost_empty_queue()) .expect("Empty block should have been produced"); check_current_block_number(&mut host, 2); } @@ -1108,7 +1109,7 @@ mod tests { kernel_upgrade: None, }; - produce(&mut host, queue).expect("Should have produced"); + produce::<_, DefaultSettings>(&mut host, queue).expect("Should have produced"); // test no new block assert!( @@ -1159,7 +1160,7 @@ mod tests { kernel_upgrade: None, }; - produce(&mut host, queue).expect("Should have produced"); + produce::<_, DefaultSettings>(&mut host, queue).expect("Should have produced"); // test no new block assert!( diff --git a/src/kernel_evm/kernel/src/lib.rs b/src/kernel_evm/kernel/src/lib.rs index c1f49c8172ef..f2a9211848b2 100644 --- a/src/kernel_evm/kernel/src/lib.rs +++ b/src/kernel_evm/kernel/src/lib.rs @@ -15,7 +15,6 @@ use storage::{ }; use tezos_crypto_rs::hash::ContractKt1Hash; use tezos_smart_rollup_encoding::timestamp::Timestamp; -use tezos_smart_rollup_entrypoint::kernel_entry; use tezos_smart_rollup_host::path::{concat, OwnedPath, RefPath}; use tezos_smart_rollup_host::runtime::Runtime; @@ -28,6 +27,7 @@ use crate::safe_storage::{SafeStorage, TMP_PATH}; use crate::blueprint::{fetch, Queue}; use crate::error::Error; use crate::error::UpgradeProcessError::Fallback; +use crate::settings::KernelSettings; use crate::storage::{read_smart_rollup_address, store_smart_rollup_address}; use crate::upgrade::upgrade_kernel; use crate::Error::UpgradeError; @@ -47,6 +47,8 @@ mod storage; mod tick_model; mod upgrade; +pub mod settings; + /// The chain id will need to be unique when the EVM rollup is deployed in /// production. pub const CHAIN_ID: u32 = 1337; @@ -117,7 +119,7 @@ pub fn stage_one( Ok(queue) } -fn produce_and_upgrade( +fn produce_and_upgrade( host: &mut Host, queue: Queue, kernel_upgrade: KernelUpgrade, @@ -125,7 +127,7 @@ fn produce_and_upgrade( // Since a kernel upgrade was detected, in case an error is thrown // by the block production, we exceptionally "recover" from it and // still process the kernel upgrade. - if let Err(e) = block::produce(host, queue) { + if let Err(e) = block::produce::<_, Settings>(host, queue) { log!( host, Error, @@ -143,16 +145,16 @@ fn produce_and_upgrade( upgrade_status } -pub fn stage_two( +pub fn stage_two( host: &mut Host, queue: Queue, ) -> Result<(), anyhow::Error> { log!(host, Info, "Entering stage two."); let kernel_upgrade = queue.kernel_upgrade.clone(); if let Some(kernel_upgrade) = kernel_upgrade { - produce_and_upgrade(host, queue, kernel_upgrade) + produce_and_upgrade::<_, Settings>(host, queue, kernel_upgrade) } else { - block::produce(host, queue) + block::produce::<_, Settings>(host, queue) } } @@ -212,7 +214,7 @@ fn fetch_queue_left(host: &mut Host) -> Result(host: &mut Host) -> Result<(), anyhow::Error> { +pub fn main(host: &mut Host) -> Result<(), anyhow::Error> { let queue = if storage::was_rebooted(host)? { // kernel was rebooted log!( @@ -236,7 +238,7 @@ pub fn main(host: &mut Host) -> Result<(), anyhow::Error> { .context("Failed during stage 1")? }; - stage_two(host, queue).context("Failed during stage 2") + stage_two::<_, Settings>(host, queue).context("Failed during stage 2") } const EVM_PATH: RefPath = RefPath::assert_from(b"/evm"); @@ -259,7 +261,7 @@ fn log_error( Ok(()) } -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. @@ -275,7 +277,7 @@ pub fn kernel_loop(host: &mut Host) { .expect("The kernel failed to create the temporary directory"); let mut host = SafeStorage(host); - match main(&mut host) { + match main::<_, Settings>(&mut host) { Ok(()) => { host.promote_upgrade() .expect("Potential kernel upgrade promotion failed"); @@ -309,4 +311,5 @@ pub fn kernel_loop(host: &mut Host) { } } -kernel_entry!(kernel_loop); +#[cfg(crate_type="cdylib")] +tezos_smart_rollup_entrypoint::kernel_entry!(kernel_loop); diff --git a/src/kernel_evm/kernel/src/settings.rs b/src/kernel_evm/kernel/src/settings.rs new file mode 100644 index 000000000000..8b6cf3dbfebe --- /dev/null +++ b/src/kernel_evm/kernel/src/settings.rs @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2023 Baking Bad +// +// SPDX-License-Identifier: MIT + +//! Execution settings for the kernel +//! +//! This module defines a trait used to configure: +//! - EVM precompiles +//! and other settings at compile time via static methods. +//! It also provides a default implementation. + +use evm_execution::precompiles::{PrecompileBTreeMap, precompile_set}; +use tezos_smart_rollup_host::runtime::Runtime; + +pub trait KernelSettings { + fn precompile_set() -> PrecompileBTreeMap; +} + +pub struct DefaultSettings {} + +impl KernelSettings for DefaultSettings { + fn precompile_set() -> PrecompileBTreeMap { + precompile_set() + } +} -- GitLab From f483f5512615b34639bdb0d866d7d0a8d5d2989e Mon Sep 17 00:00:00 2001 From: Michael Zaikin Date: Wed, 20 Sep 2023 21:54:45 +0300 Subject: [PATCH 2/2] Introduce precompile state --- src/kernel_evm/Cargo.lock | 8 --- src/kernel_evm/Cargo.toml | 1 - src/kernel_evm/custom_kernel/Cargo.toml | 15 ------ src/kernel_evm/custom_kernel/src/lib.rs | 7 --- .../evm_execution/src/account_storage.rs | 16 ++++++ src/kernel_evm/evm_execution/src/handler.rs | 2 +- src/kernel_evm/evm_execution/src/lib.rs | 1 + .../evm_execution/src/precompile_state.rs | 49 +++++++++++++++++++ 8 files changed, 67 insertions(+), 32 deletions(-) delete mode 100644 src/kernel_evm/custom_kernel/Cargo.toml delete mode 100644 src/kernel_evm/custom_kernel/src/lib.rs create mode 100644 src/kernel_evm/evm_execution/src/precompile_state.rs diff --git a/src/kernel_evm/Cargo.lock b/src/kernel_evm/Cargo.lock index 55c758602577..ecf7c171d4a4 100644 --- a/src/kernel_evm/Cargo.lock +++ b/src/kernel_evm/Cargo.lock @@ -236,14 +236,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "custom_kernel" -version = "0.1.0" -dependencies = [ - "evm_kernel", - "tezos-smart-rollup-entrypoint", -] - [[package]] name = "der" version = "0.4.5" diff --git a/src/kernel_evm/Cargo.toml b/src/kernel_evm/Cargo.toml index 445bca93c722..6a71ec8f88a0 100644 --- a/src/kernel_evm/Cargo.toml +++ b/src/kernel_evm/Cargo.toml @@ -10,7 +10,6 @@ members = [ "kernel", "evm_execution", "logging", - "custom_kernel" ] [workspace.dependencies] diff --git a/src/kernel_evm/custom_kernel/Cargo.toml b/src/kernel_evm/custom_kernel/Cargo.toml deleted file mode 100644 index 9dbd80827e90..000000000000 --- a/src/kernel_evm/custom_kernel/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Baking Bad -# -# SPDX-License-Identifier: MIT - -[package] -name = "custom_kernel" -version = "0.1.0" -edition = "2021" - -[lib] -crate-type = ["cdylib"] - -[dependencies] -tezos-smart-rollup-entrypoint.workspace = true -evm_kernel.workspace = true \ No newline at end of file diff --git a/src/kernel_evm/custom_kernel/src/lib.rs b/src/kernel_evm/custom_kernel/src/lib.rs deleted file mode 100644 index e833cf57dfbe..000000000000 --- a/src/kernel_evm/custom_kernel/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Baking Bad -// -// SPDX-License-Identifier: MIT - -use evm_kernel::{kernel_loop, settings::DefaultSettings}; - -tezos_smart_rollup_entrypoint::kernel_entry!(kernel_loop::<_, DefaultSettings>); diff --git a/src/kernel_evm/evm_execution/src/account_storage.rs b/src/kernel_evm/evm_execution/src/account_storage.rs index 05ca0df9935e..d2c12bb0b070 100644 --- a/src/kernel_evm/evm_execution/src/account_storage.rs +++ b/src/kernel_evm/evm_execution/src/account_storage.rs @@ -14,6 +14,7 @@ use tezos_smart_rollup_storage::storage::Storage; use thiserror::Error; use crate::DurableStorageError; +use crate::precompile_state::PrecompileState; /// The size of one 256 bit word. Size in bytes pub const WORD_SIZE: usize = 32_usize; @@ -133,6 +134,11 @@ const STORAGE_ROOT_PATH: RefPath = RefPath::assert_from(b"/storage"); /// Flag indicating an account has already been indexed. const INDEXED_PATH: RefPath = RefPath::assert_from(b"/indexed"); +/// Stateful precompiled contracts are allowed to have raw storage access. The account +/// location prefixed to this path gives the root path to a durable storage subtree +/// where particular precompile keeps its state in arbitrary format. +const PRECOMPILE_STATE_ROOT_PATH: RefPath = RefPath::assert_from(b"/precompile_state"); + /// 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 /// value back. @@ -484,6 +490,16 @@ impl EthereumAccount { host.store_write(&path, &[0_u8; 0], 0) .map_err(DurableStorageError::from) } + + /// Returns an instance of PrecompiledState which provides extended storage access + /// to a specific account subfolder `/precompile_state`: + /// - no restrictions on value size + /// - atomicity is preserved (if outer transaction is rolled back, changes won't apply) + /// Intended for stateful precompiles only. + pub fn precompile_state(&self) -> Result { + let path = concat(&self.path, &PRECOMPILE_STATE_ROOT_PATH)?; + Ok(PrecompileState::from_path(path)) + } } /// The type of the storage API for accessing the Ethereum World State. diff --git a/src/kernel_evm/evm_execution/src/handler.rs b/src/kernel_evm/evm_execution/src/handler.rs index 4df10d67e234..537a70c13a7e 100644 --- a/src/kernel_evm/evm_execution/src/handler.rs +++ b/src/kernel_evm/evm_execution/src/handler.rs @@ -585,7 +585,7 @@ impl<'a, Host: Runtime> EvmHandler<'a, Host> { self.end_initial_transaction(Ok((result, None, vec![]))) } - fn get_or_create_account( + pub fn get_or_create_account( &self, address: H160, ) -> Result { diff --git a/src/kernel_evm/evm_execution/src/lib.rs b/src/kernel_evm/evm_execution/src/lib.rs index 182b5fa5a0e3..1ce0df57d166 100644 --- a/src/kernel_evm/evm_execution/src/lib.rs +++ b/src/kernel_evm/evm_execution/src/lib.rs @@ -21,6 +21,7 @@ use thiserror::Error; pub mod account_storage; pub mod handler; pub mod precompiles; +pub mod precompile_state; pub mod storage; pub mod transaction; diff --git a/src/kernel_evm/evm_execution/src/precompile_state.rs b/src/kernel_evm/evm_execution/src/precompile_state.rs new file mode 100644 index 000000000000..a8d58de8f66e --- /dev/null +++ b/src/kernel_evm/evm_execution/src/precompile_state.rs @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2023 Baking Bad +// +// SPDX-License-Identifier: MIT + +use host::path::{OwnedPath, concat}; +use host::runtime::{Runtime, ValueType}; + +use crate::DurableStorageError; + +pub struct PrecompileState { + path: OwnedPath +} + +impl PrecompileState { + pub fn from_path(path: OwnedPath) -> Self { + Self { path } + } + + pub fn get( + &self, + host: &impl Runtime, + key: String, + ) -> Result>, DurableStorageError> { + let key_path = OwnedPath::try_from(key)?; + let path = concat(&self.path, &key_path)?; + match host.store_has(&path)? { + Some(ValueType::Value | ValueType::ValueWithSubtree) => { + let value = host.store_read_all(&path)?; + Ok(Some(value)) + } + _ => Ok(None), + } + } + + pub fn set( + &mut self, + host: &mut impl Runtime, + key: String, + value: Option<&[u8]>, + ) -> Result<(), DurableStorageError> { + let key_path = OwnedPath::try_from(key)?; + let path = concat(&self.path, &key_path)?; + match value { + Some(value) => host.store_write_all(&path, value)?, + None => host.store_delete_value(&path)? + } + Ok(()) + } +} -- GitLab