From 486f56052783f16cddb0ae7b799a8060660eb001 Mon Sep 17 00:00:00 2001 From: arnaud Date: Fri, 4 Apr 2025 16:58:49 +0200 Subject: [PATCH 1/4] Tezlink/Kernel: Introduce a crate tezos_execution (also correct Cargo.toml for tezos crate) --- etherlink/kernel_latest/Cargo.lock | 17 +++++++++++-- etherlink/kernel_latest/Cargo.toml | 1 + etherlink/kernel_latest/kernel/Cargo.toml | 1 + etherlink/kernel_latest/tezos/Cargo.toml | 2 -- .../kernel_latest/tezos_execution/Cargo.toml | 24 +++++++++++++++++++ .../kernel_latest/tezos_execution/src/lib.rs | 3 +++ 6 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 etherlink/kernel_latest/tezos_execution/Cargo.toml create mode 100644 etherlink/kernel_latest/tezos_execution/src/lib.rs diff --git a/etherlink/kernel_latest/Cargo.lock b/etherlink/kernel_latest/Cargo.lock index e5e4336ec542..c9afbd16a5b8 100644 --- a/etherlink/kernel_latest/Cargo.lock +++ b/etherlink/kernel_latest/Cargo.lock @@ -785,6 +785,7 @@ dependencies = [ "softfloat", "tezos-evm-logging-latest", "tezos-evm-runtime-latest", + "tezos-execution-latest", "tezos-indexable-storage-latest", "tezos-smart-rollup", "tezos-smart-rollup-core", @@ -2217,6 +2218,20 @@ dependencies = [ "tezos-smart-rollup-mock", ] +[[package]] +name = "tezos-execution-latest" +version = "0.1.0" +dependencies = [ + "hex", + "primitive-types", + "tezos-evm-runtime-latest", + "tezos-smart-rollup", + "tezos-smart-rollup-host", + "tezos-storage-latest", + "tezos_crypto_rs", + "tezos_data_encoding", +] + [[package]] name = "tezos-indexable-storage-latest" version = "0.1.0" @@ -2473,10 +2488,8 @@ version = "0.1.0" dependencies = [ "hex", "primitive-types", - "rlp", "tezos-smart-rollup", "tezos_crypto_rs", - "tezos_ethereum_latest", ] [[package]] diff --git a/etherlink/kernel_latest/Cargo.toml b/etherlink/kernel_latest/Cargo.toml index be890c1c6e3f..a21cd90811ac 100644 --- a/etherlink/kernel_latest/Cargo.toml +++ b/etherlink/kernel_latest/Cargo.toml @@ -63,6 +63,7 @@ libsecp256k1 = { version = "0.7", default-features = false, features = [ tezos_ethereum = { package = "tezos_ethereum_latest", path = "./ethereum" } tezos_tezlink = { package = "tezos_tezlink_latest", path = "./tezos" } evm-execution = { package = "evm-execution-latest", path = "./evm_execution" } +tezos-execution = { package = "tezos-execution-latest", path = "./tezos_execution" } tezos-evm-logging = { package = "tezos-evm-logging-latest", path = "./logging" } tezos-evm-runtime = { package = "tezos-evm-runtime-latest", path = "./runtime" } tezos-indexable-storage = { package = "tezos-indexable-storage-latest", path = "./indexable_storage" } diff --git a/etherlink/kernel_latest/kernel/Cargo.toml b/etherlink/kernel_latest/kernel/Cargo.toml index b1c9ecfaa768..ce106f29f148 100644 --- a/etherlink/kernel_latest/kernel/Cargo.toml +++ b/etherlink/kernel_latest/kernel/Cargo.toml @@ -39,6 +39,7 @@ ethbloom.workspace = true evm.workspace = true evm-execution.workspace = true +tezos-execution.workspace = true tezos_ethereum.workspace = true tezos_tezlink.workspace = true tezos-evm-logging.workspace = true diff --git a/etherlink/kernel_latest/tezos/Cargo.toml b/etherlink/kernel_latest/tezos/Cargo.toml index 77246aa24759..bbcce22825b0 100644 --- a/etherlink/kernel_latest/tezos/Cargo.toml +++ b/etherlink/kernel_latest/tezos/Cargo.toml @@ -11,9 +11,7 @@ license = "MIT" [dependencies] tezos_crypto_rs.workspace = true -rlp.workspace = true hex.workspace = true -tezos_ethereum.workspace = true primitive-types.workspace = true tezos-smart-rollup.workspace = true \ No newline at end of file diff --git a/etherlink/kernel_latest/tezos_execution/Cargo.toml b/etherlink/kernel_latest/tezos_execution/Cargo.toml new file mode 100644 index 000000000000..47975e8633a3 --- /dev/null +++ b/etherlink/kernel_latest/tezos_execution/Cargo.toml @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: 2025 Functori +# +# SPDX-License-Identifier: MIT + +[package] +name = "tezos-execution-latest" +version = "0.1.0" +edition = "2021" +license = "MIT" + +[dependencies] + +tezos_crypto_rs.workspace = true +hex.workspace = true + + +tezos-evm-runtime.workspace = true +tezos-storage.workspace = true + +tezos-smart-rollup-host.workspace = true +tezos_data_encoding.workspace = true + +primitive-types.workspace = true +tezos-smart-rollup.workspace = true diff --git a/etherlink/kernel_latest/tezos_execution/src/lib.rs b/etherlink/kernel_latest/tezos_execution/src/lib.rs new file mode 100644 index 000000000000..a4ab73010df9 --- /dev/null +++ b/etherlink/kernel_latest/tezos_execution/src/lib.rs @@ -0,0 +1,3 @@ +// SPDX-FileCopyrightText: 2025 Functori +// +// SPDX-License-Identifier: MIT -- GitLab From 8c8cf42949522032a606a8fd3df5b5764be723c5 Mon Sep 17 00:00:00 2001 From: arnaud Date: Tue, 25 Mar 2025 11:16:36 +0100 Subject: [PATCH 2/4] Tezlink/Kernel: Introduce a context representation in the tezos_execution crate --- .../tezos_execution/src/context.rs | 55 +++++++++++++++++++ .../kernel_latest/tezos_execution/src/lib.rs | 2 + 2 files changed, 57 insertions(+) create mode 100644 etherlink/kernel_latest/tezos_execution/src/context.rs diff --git a/etherlink/kernel_latest/tezos_execution/src/context.rs b/etherlink/kernel_latest/tezos_execution/src/context.rs new file mode 100644 index 000000000000..60003315b033 --- /dev/null +++ b/etherlink/kernel_latest/tezos_execution/src/context.rs @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: 2025 Functori +// +// SPDX-License-Identifier: MIT + +use tezos_smart_rollup_host::path::{OwnedPath, RefPath}; + +// TODO: https://gitlab.com/tezos/tezos/-/issues/7867: add the missing paths + +// This path should be the only one to refers to '/tezlink/context' +const CONTEXT_PATH: RefPath = RefPath::assert_from(b"/tezlink/context"); + +// Instead of using directly the paths, we construct a Context object that holds the +// path to the context and does the concatenations. +// This will prevent '/tezlink/context' to appear at multiple place like '/evm/world_state' +pub struct Context { + path: OwnedPath, +} + +impl Context { + #[allow(dead_code)] + pub fn init_context() -> Self { + Context { + path: CONTEXT_PATH.into(), + } + } +} + +pub mod contracts { + use tezos_smart_rollup_host::path::{concat, OwnedPath, PathError, RefPath}; + + use super::Context; + + #[allow(dead_code)] + const ROOT: RefPath = RefPath::assert_from(b"/contracts"); + + #[allow(dead_code)] + const INDEX: RefPath = RefPath::assert_from(b"/index"); + + #[allow(dead_code)] + const GLOBAL_COUNTER: RefPath = RefPath::assert_from(b"/global_counter"); + + pub fn root(context: &Context) -> Result { + concat(&context.path, &ROOT) + } + + #[allow(dead_code)] + pub fn index(context: &Context) -> Result { + concat(&root(context)?, &INDEX) + } + + #[allow(dead_code)] + pub fn global_counter(context: &Context) -> Result { + concat(&root(context)?, &GLOBAL_COUNTER) + } +} diff --git a/etherlink/kernel_latest/tezos_execution/src/lib.rs b/etherlink/kernel_latest/tezos_execution/src/lib.rs index a4ab73010df9..1139d21c03e6 100644 --- a/etherlink/kernel_latest/tezos_execution/src/lib.rs +++ b/etherlink/kernel_latest/tezos_execution/src/lib.rs @@ -1,3 +1,5 @@ // SPDX-FileCopyrightText: 2025 Functori // // SPDX-License-Identifier: MIT + +pub mod context; -- GitLab From 0cf8aa399eef43cded166512ec0ebf48281c063c Mon Sep 17 00:00:00 2001 From: arnaud Date: Tue, 25 Mar 2025 11:59:00 +0100 Subject: [PATCH 3/4] Tezlink/Kernel: Introduce Tezlink implicit account storage --- .../tezos_execution/src/account_storage.rs | 99 +++++++++++++++++++ .../kernel_latest/tezos_execution/src/lib.rs | 2 + 2 files changed, 101 insertions(+) create mode 100644 etherlink/kernel_latest/tezos_execution/src/account_storage.rs diff --git a/etherlink/kernel_latest/tezos_execution/src/account_storage.rs b/etherlink/kernel_latest/tezos_execution/src/account_storage.rs new file mode 100644 index 000000000000..dd7a7d94ea32 --- /dev/null +++ b/etherlink/kernel_latest/tezos_execution/src/account_storage.rs @@ -0,0 +1,99 @@ +// SPDX-FileCopyrightText: 2022-2023 TriliTech +// SPDX-FileCopyrightText: 2023 Functori +// +// SPDX-License-Identifier: MIT + +//! Tezos account state and storage + +use primitive_types::U256; +use tezos_data_encoding::enc::BinWriter; +use tezos_evm_runtime::runtime::Runtime; +use tezos_smart_rollup::types::Contract; +use tezos_smart_rollup_host::path::{concat, OwnedPath, RefPath}; +use tezos_storage::{ + read_u256_le_default, read_u64_le_default, write_u256_le, write_u64_le, +}; + +use crate::context; + +#[derive(Debug, PartialEq)] +pub struct TezlinkImplicitAccount { + path: OwnedPath, +} + +impl From for TezlinkImplicitAccount { + fn from(path: OwnedPath) -> Self { + Self { path } + } +} + +const BALANCE_PATH: RefPath = RefPath::assert_from(b"/balance"); + +const COUNTER_PATH: RefPath = RefPath::assert_from(b"/counter"); + +fn account_path(contract: &Contract) -> Result { + // uses the same encoding as in the octez node's representation of the context + // see `octez-codec describe alpha.contract binary schema` + let mut contract_encoded = Vec::new(); + contract + .bin_write(&mut contract_encoded) + .map_err(|_| tezos_smart_rollup::host::RuntimeError::DecodingError)?; + + let path_string = alloc::format!("/{}", hex::encode(&contract_encoded)); + Ok(OwnedPath::try_from(path_string)?) +} + +impl TezlinkImplicitAccount { + // We must provide the context object to get the full path in the durable storage + #[allow(dead_code)] + pub fn from_contract( + context: &context::Context, + contract: &Contract, + ) -> Result { + let index = context::contracts::index(context)?; + let path = concat(&index, &account_path(contract)?)?; + Ok(path.into()) + } + + /// Get the **counter** for the Tezlink account. + #[allow(dead_code)] + pub fn counter( + &self, + host: &impl Runtime, + ) -> Result { + let path = concat(&self.path, &COUNTER_PATH)?; + read_u64_le_default(host, &path, 0u64) + } + + /// Set the **counter** for the Tezlink account. + #[allow(dead_code)] + pub fn set_counter( + &mut self, + host: &mut impl Runtime, + counter: u64, + ) -> Result<(), tezos_storage::error::Error> { + let path = concat(&self.path, &COUNTER_PATH)?; + write_u64_le(host, &path, counter) + } + + /// Get the **balance** of an account in Mutez held by the account. + #[allow(dead_code)] + pub fn balance( + &self, + host: &impl Runtime, + ) -> Result { + let path = concat(&self.path, &BALANCE_PATH)?; + read_u256_le_default(host, &path, U256::zero()) + } + + /// Set the **balance** of an account in Mutez held by the account. + #[allow(dead_code)] + pub fn set_balance( + &mut self, + host: &mut impl Runtime, + balance: U256, + ) -> Result<(), tezos_storage::error::Error> { + let path = concat(&self.path, &BALANCE_PATH)?; + write_u256_le(host, &path, balance) + } +} diff --git a/etherlink/kernel_latest/tezos_execution/src/lib.rs b/etherlink/kernel_latest/tezos_execution/src/lib.rs index 1139d21c03e6..d6c4e42c0c55 100644 --- a/etherlink/kernel_latest/tezos_execution/src/lib.rs +++ b/etherlink/kernel_latest/tezos_execution/src/lib.rs @@ -2,4 +2,6 @@ // // SPDX-License-Identifier: MIT +extern crate alloc; +pub mod account_storage; pub mod context; -- GitLab From 1ba671da44ebf18c608bdc391e4686ef3ffaa83f Mon Sep 17 00:00:00 2001 From: arnaud Date: Fri, 4 Apr 2025 13:02:49 +0200 Subject: [PATCH 4/4] Tezlink/Kernel/Test: Test account modification for Tezlink (balances and counter) --- .../tezos_execution/src/account_storage.rs | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/etherlink/kernel_latest/tezos_execution/src/account_storage.rs b/etherlink/kernel_latest/tezos_execution/src/account_storage.rs index dd7a7d94ea32..6353d91af5a5 100644 --- a/etherlink/kernel_latest/tezos_execution/src/account_storage.rs +++ b/etherlink/kernel_latest/tezos_execution/src/account_storage.rs @@ -97,3 +97,125 @@ impl TezlinkImplicitAccount { write_u256_le(host, &path, balance) } } + +#[cfg(test)] +mod test { + use super::*; + use std::str::FromStr; + use tezos_evm_runtime::runtime::MockKernelHost; + use tezos_smart_rollup::host::Runtime; + + // Read test use hard coded path on purpose to verify the Tezos compatibility. + // These paths comes from the context.json generated by the create mockup command + // of octez-client. + #[test] + fn test_read_balance() { + let mut host = MockKernelHost::default(); + + let balance = U256::from_str("2944").unwrap(); + // octez-codec decode alpha.contract from '000002298c03ed7d454a101eb7022bc95f7e5f41ac78' + // Result: "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" + let path = RefPath::assert_from(b"/tezlink/context/contracts/index/000002298c03ed7d454a101eb7022bc95f7e5f41ac78/balance"); + let mut balance_array: [u8; 32] = [0; 32]; + balance.to_little_endian(&mut balance_array); + host.store_write_all(&path, &balance_array).unwrap(); + + // Initalize path for Tezlink context at /tezlink/context + let context = context::Context::init_context(); + + // Public key hash in b58 for 000002298c03ed7d454a101eb7022bc95f7e5f41ac78 + let contract = Contract::from_b58check("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx") + .expect("Contract base58 conversion should succeed"); + + let account = TezlinkImplicitAccount::from_contract(&context, &contract) + .expect("Account creation should have succeed"); + + let read_balance = account + .balance(&host) + .expect("read_balance should have succeed"); + + assert_eq!(balance, read_balance); + } + + #[test] + fn test_read_counter() { + let mut host = MockKernelHost::default(); + + let counter = 3u64; + // octez-codec decode alpha.contract from '000002298c03ed7d454a101eb7022bc95f7e5f41ac78' + // Result: "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" + let path = RefPath::assert_from(b"/tezlink/context/contracts/index/000002298c03ed7d454a101eb7022bc95f7e5f41ac78/counter"); + let counter_array: [u8; 8] = counter.to_le_bytes(); + host.store_write_all(&path, &counter_array).unwrap(); + + // Initalize path for Tezlink context at /tezlink/context + let context = context::Context::init_context(); + + // Public key hash in b58 for 0002298c03ed7d454a101eb7022bc95f7e5f41ac78 + let contract = Contract::from_b58check("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx") + .expect("Contract base58 conversion should succeed"); + + let account = TezlinkImplicitAccount::from_contract(&context, &contract) + .expect("Account creation should have succeed"); + + let read_counter = account + .counter(&host) + .expect("read_counter should have succeed"); + + assert_eq!(counter, read_counter); + } + + #[test] + fn test_set_and_read_balance() { + let mut host = MockKernelHost::default(); + + let balance = U256::from_str("4579").unwrap(); + + // Initalize path for Tezlink context at /tezlink/context + let context = context::Context::init_context(); + + // Public key hash in b58 for 000002298c03ed7d454a101eb7022bc95f7e5f41ac78 + let contract = Contract::from_b58check("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx") + .expect("Contract base58 conversion should succeed"); + + let mut account = TezlinkImplicitAccount::from_contract(&context, &contract) + .expect("Account creation should have succeed"); + + let () = account + .set_balance(&mut host, balance) + .expect("set_balance should succeed"); + + let read_balance = account + .balance(&host) + .expect("read_balance should have succeed"); + + assert_eq!(balance, read_balance); + } + + #[test] + fn test_set_and_read_counter() { + let mut host = MockKernelHost::default(); + + let counter = 6u64; + + // Initalize path for Tezlink context at /tezlink/context + let context = context::Context::init_context(); + + // Public key hash in b58 for 0002298c03ed7d454a101eb7022bc95f7e5f41ac78 + let contract = Contract::from_b58check("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx") + .expect("Contract base58 conversion should succeed"); + + let mut account = TezlinkImplicitAccount::from_contract(&context, &contract) + .expect("Account creation should have succeed"); + + let () = account + .set_counter(&mut host, counter) + .expect("set_counter should have succeed"); + + let read_counter = account + .counter(&host) + .expect("read_counter should have succeed"); + + assert_eq!(counter, read_counter); + } +} -- GitLab