From 48ae9d05090ad7b46881881268e222eb99f3ef24 Mon Sep 17 00:00:00 2001 From: Michael Zaikin Date: Mon, 10 Jun 2024 10:26:12 +0100 Subject: [PATCH 1/2] EVM: move FA bridge crate into a evm_execution subcrate --- etherlink/kernel_evm/Cargo.lock | 4 --- etherlink/kernel_evm/Cargo.toml | 11 +----- .../tests/contracts/Makefile | 0 .../tests/contracts}/README.md | 8 ++--- .../contracts/artifacts/MockPrecompile.abi | 0 .../tests/contracts/artifacts/MockWrapper.abi | 0 .../contracts/artifacts/MockWrapper.bytecode | Bin .../tests/contracts/foundry.toml | 0 .../tests/contracts/src/MockPrecompile.sol | 0 .../tests/contracts/src/MockWrapper.sol | 0 etherlink/kernel_evm/fa_bridge/Cargo.toml | 12 ------- etherlink/kernel_evm/fa_bridge/src/lib.rs | 32 ------------------ etherlink/kernel_evm/kernel/Cargo.toml | 1 + 13 files changed, 6 insertions(+), 62 deletions(-) rename etherlink/kernel_evm/{fa_bridge => evm_execution}/tests/contracts/Makefile (100%) rename etherlink/kernel_evm/{fa_bridge => evm_execution/tests/contracts}/README.md (52%) rename etherlink/kernel_evm/{fa_bridge => evm_execution}/tests/contracts/artifacts/MockPrecompile.abi (100%) rename etherlink/kernel_evm/{fa_bridge => evm_execution}/tests/contracts/artifacts/MockWrapper.abi (100%) rename etherlink/kernel_evm/{fa_bridge => evm_execution}/tests/contracts/artifacts/MockWrapper.bytecode (100%) rename etherlink/kernel_evm/{fa_bridge => evm_execution}/tests/contracts/foundry.toml (100%) rename etherlink/kernel_evm/{fa_bridge => evm_execution}/tests/contracts/src/MockPrecompile.sol (100%) rename etherlink/kernel_evm/{fa_bridge => evm_execution}/tests/contracts/src/MockWrapper.sol (100%) delete mode 100644 etherlink/kernel_evm/fa_bridge/Cargo.toml delete mode 100644 etherlink/kernel_evm/fa_bridge/src/lib.rs diff --git a/etherlink/kernel_evm/Cargo.lock b/etherlink/kernel_evm/Cargo.lock index fb0c1ef5a547..59cec9037c25 100644 --- a/etherlink/kernel_evm/Cargo.lock +++ b/etherlink/kernel_evm/Cargo.lock @@ -634,10 +634,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "fa-bridge" -version = "0.1.0" - [[package]] name = "fastrand" version = "1.9.0" diff --git a/etherlink/kernel_evm/Cargo.toml b/etherlink/kernel_evm/Cargo.toml index f4b1ff07ce4c..dabc09189fc8 100644 --- a/etherlink/kernel_evm/Cargo.toml +++ b/etherlink/kernel_evm/Cargo.toml @@ -8,15 +8,7 @@ [workspace] -members = [ - "ethereum", - "fa_bridge", - "kernel", - "evm_execution", - "evm_evaluation", - "indexable_storage", - "logging", -] +members = ["ethereum", "kernel", "evm_execution", "evm_evaluation", "indexable_storage", "logging"] [workspace.dependencies] @@ -56,7 +48,6 @@ libsecp256k1 = { version = "0.7", default-features = false, features = [ ] } # kernel crates -fa-bridge = { path = "./fa_bridge" } tezos_ethereum = { path = "./ethereum" } evm-execution = { path = "./evm_execution" } tezos-evm-logging = { path = "./logging" } diff --git a/etherlink/kernel_evm/fa_bridge/tests/contracts/Makefile b/etherlink/kernel_evm/evm_execution/tests/contracts/Makefile similarity index 100% rename from etherlink/kernel_evm/fa_bridge/tests/contracts/Makefile rename to etherlink/kernel_evm/evm_execution/tests/contracts/Makefile diff --git a/etherlink/kernel_evm/fa_bridge/README.md b/etherlink/kernel_evm/evm_execution/tests/contracts/README.md similarity index 52% rename from etherlink/kernel_evm/fa_bridge/README.md rename to etherlink/kernel_evm/evm_execution/tests/contracts/README.md index 7c32b18ece90..3c0a95a10ada 100644 --- a/etherlink/kernel_evm/fa_bridge/README.md +++ b/etherlink/kernel_evm/evm_execution/tests/contracts/README.md @@ -1,12 +1,12 @@ -# FA token bridge +# FA bridge test contracts -This crate contains primitives that are necessary to implement a permissionless ticket based asset bridge, as described in the [specification](https://hackmd.io/I_5FJBwoQcqOa2bTtRYH-w); +Solidity contracts for FA bridge integration tests. -## How to build test contracts +## How to build [Foundry](https://github.com/foundry-rs/foundry) is required to compile test smart contracts. Please use actual [installation guide](https://book.getfoundry.sh/getting-started/installation). After installing Foundry to build artifacts do: ``` make artifacts -``` \ No newline at end of file +``` diff --git a/etherlink/kernel_evm/fa_bridge/tests/contracts/artifacts/MockPrecompile.abi b/etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockPrecompile.abi similarity index 100% rename from etherlink/kernel_evm/fa_bridge/tests/contracts/artifacts/MockPrecompile.abi rename to etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockPrecompile.abi diff --git a/etherlink/kernel_evm/fa_bridge/tests/contracts/artifacts/MockWrapper.abi b/etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockWrapper.abi similarity index 100% rename from etherlink/kernel_evm/fa_bridge/tests/contracts/artifacts/MockWrapper.abi rename to etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockWrapper.abi diff --git a/etherlink/kernel_evm/fa_bridge/tests/contracts/artifacts/MockWrapper.bytecode b/etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockWrapper.bytecode similarity index 100% rename from etherlink/kernel_evm/fa_bridge/tests/contracts/artifacts/MockWrapper.bytecode rename to etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockWrapper.bytecode diff --git a/etherlink/kernel_evm/fa_bridge/tests/contracts/foundry.toml b/etherlink/kernel_evm/evm_execution/tests/contracts/foundry.toml similarity index 100% rename from etherlink/kernel_evm/fa_bridge/tests/contracts/foundry.toml rename to etherlink/kernel_evm/evm_execution/tests/contracts/foundry.toml diff --git a/etherlink/kernel_evm/fa_bridge/tests/contracts/src/MockPrecompile.sol b/etherlink/kernel_evm/evm_execution/tests/contracts/src/MockPrecompile.sol similarity index 100% rename from etherlink/kernel_evm/fa_bridge/tests/contracts/src/MockPrecompile.sol rename to etherlink/kernel_evm/evm_execution/tests/contracts/src/MockPrecompile.sol diff --git a/etherlink/kernel_evm/fa_bridge/tests/contracts/src/MockWrapper.sol b/etherlink/kernel_evm/evm_execution/tests/contracts/src/MockWrapper.sol similarity index 100% rename from etherlink/kernel_evm/fa_bridge/tests/contracts/src/MockWrapper.sol rename to etherlink/kernel_evm/evm_execution/tests/contracts/src/MockWrapper.sol diff --git a/etherlink/kernel_evm/fa_bridge/Cargo.toml b/etherlink/kernel_evm/fa_bridge/Cargo.toml deleted file mode 100644 index e36e8662d014..000000000000 --- a/etherlink/kernel_evm/fa_bridge/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-FileCopyrightText: 2023 PK Lab -# -# SPDX-License-Identifier: MIT - -[package] -name = "fa-bridge" -version = "0.1.0" -edition = "2021" -license = "MIT" - -[lib] -crate-type = ["cdylib", "rlib"] diff --git a/etherlink/kernel_evm/fa_bridge/src/lib.rs b/etherlink/kernel_evm/fa_bridge/src/lib.rs deleted file mode 100644 index d4fc52fa58f5..000000000000 --- a/etherlink/kernel_evm/fa_bridge/src/lib.rs +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: 2023 PK Lab -// -// SPDX-License-Identifier: MIT - -//! FA token bridge. -//! -//! A permissionless transport protocol, that enables ticket transfers -//! from L1 to L2 and back, supporting two destination types: -//! 1. Simple address, which can be both externally owner account, -//! or a smart contract wallet (that supports tickets) -//! 2. Proxy contract, exposing standard methods for deposits (on L2) -//! and withdrawals (on L1); must handle both ticket and -//! routing info that carries the final receiver address. -//! -//! FA bridge maintains the global ticket table, which is a ledger -//! tracking internal ticket ownerships on Etherlink side. -//! -//! FA bridge consists of two main parts: -//! * The one responsible for deposit handling: integrates with the -//! inbox handling flow, results in a pseudo transaction from -//! Zero account. -//! * The one responsible for withdrawal handling: implemented as -//! as precompiled contract, which can be invoked both by EOA -//! or another smart contract. -//! -//! It should be noted that FA withdrawal precompile DOES NOT post any -//! messages to the outbox since it cannot know if the outer transaction -//! fails or succeeds. -//! -//! All the state updates (ticket table, outbox message counter) are done -//! using the transactional Eth account storage, so that they are discarded -//! in case of a revert/failure. diff --git a/etherlink/kernel_evm/kernel/Cargo.toml b/etherlink/kernel_evm/kernel/Cargo.toml index b97601377d67..ccbf95a1bc70 100644 --- a/etherlink/kernel_evm/kernel/Cargo.toml +++ b/etherlink/kernel_evm/kernel/Cargo.toml @@ -57,6 +57,7 @@ proptest = { workspace = true, optional = true } [dev-dependencies] tezos-smart-rollup-mock.workspace = true tezos-smart-rollup-panic-hook.workspace = true +proptest.workspace = true [features] default = ["panic-hook"] -- GitLab From c583f08c398970feca3a604ee04e205088b87da0 Mon Sep 17 00:00:00 2001 From: Michael Zaikin Date: Mon, 10 Jun 2024 10:27:04 +0100 Subject: [PATCH 2/2] EVM: add ticket table for tracking FA deposits and withdrawals --- etherlink/CHANGES_KERNEL.md | 1 + etherlink/kernel_evm/Cargo.lock | 9 +- etherlink/kernel_evm/evm_execution/Cargo.toml | 2 +- .../evm_execution/src/account_storage.rs | 61 ++++++- .../evm_execution/src/fa_bridge/mod.rs | 34 ++++ .../src/fa_bridge/ticket_table.rs | 152 ++++++++++++++++++ etherlink/kernel_evm/evm_execution/src/lib.rs | 1 + .../kernel_evm/evm_execution/src/utilities.rs | 3 +- .../evm_execution/tests/contracts/Makefile | 6 +- ...compile.abi => MockFaBridgePrecompile.abi} | 138 ++++++++-------- .../artifacts/MockFaBridgeWrapper.abi | 112 +++++++++++++ ....bytecode => MockFaBridgeWrapper.bytecode} | Bin 1322 -> 1322 bytes .../tests/contracts/artifacts/MockWrapper.abi | 112 ------------- ...compile.sol => MockFaBridgePrecompile.sol} | 2 +- ...ockWrapper.sol => MockFaBridgeWrapper.sol} | 2 +- etherlink/kernel_evm/kernel/Cargo.toml | 5 + 16 files changed, 445 insertions(+), 195 deletions(-) create mode 100644 etherlink/kernel_evm/evm_execution/src/fa_bridge/mod.rs create mode 100644 etherlink/kernel_evm/evm_execution/src/fa_bridge/ticket_table.rs rename etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/{MockPrecompile.abi => MockFaBridgePrecompile.abi} (52%) create mode 100644 etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockFaBridgeWrapper.abi rename etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/{MockWrapper.bytecode => MockFaBridgeWrapper.bytecode} (91%) delete mode 100644 etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockWrapper.abi rename etherlink/kernel_evm/evm_execution/tests/contracts/src/{MockPrecompile.sol => MockFaBridgePrecompile.sol} (96%) rename etherlink/kernel_evm/evm_execution/tests/contracts/src/{MockWrapper.sol => MockFaBridgeWrapper.sol} (98%) diff --git a/etherlink/CHANGES_KERNEL.md b/etherlink/CHANGES_KERNEL.md index 850d8feb0e6f..735325d4f820 100644 --- a/etherlink/CHANGES_KERNEL.md +++ b/etherlink/CHANGES_KERNEL.md @@ -122,6 +122,7 @@ kernel. (!12046) - Da fee is sent to sequencer pool address. (!12113) - Gas price adjusts itself to handle congestion. (!12167) +- Add ticket table to account for FA deposits. (!12072) ### Bug fixes diff --git a/etherlink/kernel_evm/Cargo.lock b/etherlink/kernel_evm/Cargo.lock index 59cec9037c25..a3ed51e5b6db 100644 --- a/etherlink/kernel_evm/Cargo.lock +++ b/etherlink/kernel_evm/Cargo.lock @@ -608,6 +608,7 @@ dependencies = [ "ethereum", "evm", "evm-execution", + "getrandom", "hex", "libsecp256k1", "num-traits", @@ -726,9 +727,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -982,9 +983,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.150" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libm" diff --git a/etherlink/kernel_evm/evm_execution/Cargo.toml b/etherlink/kernel_evm/evm_execution/Cargo.toml index 791cf643abd3..12747925c852 100644 --- a/etherlink/kernel_evm/evm_execution/Cargo.toml +++ b/etherlink/kernel_evm/evm_execution/Cargo.toml @@ -45,6 +45,7 @@ tezos-smart-rollup-host.workspace = true tezos-smart-rollup-debug.workspace = true tezos-smart-rollup-encoding.workspace = true tezos-smart-rollup-storage.workspace = true +tezos_data_encoding.workspace = true # Adding these to 'dev_dependencies' causes the rand feature in crypto to be enabled # on wasm builds, when building the entire workspace. @@ -53,7 +54,6 @@ proptest = { workspace = true, optional = true } [dev-dependencies] tezos-smart-rollup-mock.workspace = true -tezos_data_encoding.workspace = true [features] default = ["evm_execution"] diff --git a/etherlink/kernel_evm/evm_execution/src/account_storage.rs b/etherlink/kernel_evm/evm_execution/src/account_storage.rs index ff8845fc35ec..9aa1bc755e8d 100644 --- a/etherlink/kernel_evm/evm_execution/src/account_storage.rs +++ b/etherlink/kernel_evm/evm_execution/src/account_storage.rs @@ -157,7 +157,7 @@ const CODE_HASH_BYTES: [u8; WORD_SIZE] = Decoder::Hex pub const CODE_HASH_DEFAULT: H256 = H256(CODE_HASH_BYTES); /// Read a single unsigned 256 bit value from storage at the path given. -fn read_u256( +pub fn read_u256( host: &impl Runtime, path: &impl Path, default: U256, @@ -169,6 +169,17 @@ fn read_u256( } } +/// Write a single unsigned 256 but value to the storage at the given path +pub fn write_u256( + host: &mut impl Runtime, + path: &impl Path, + x: U256, +) -> Result<(), RuntimeError> { + let mut x_bytes = [0u8; 32]; + x.to_little_endian(&mut x_bytes); + host.store_write(path, &x_bytes, 0) +} + /// Read a single unsigned 64 bit value from storage at the path given. fn read_u64( host: &impl Runtime, @@ -204,7 +215,7 @@ fn read_h256( /// Get the path corresponding to an index of H256. This is used to /// find the path to a value a contract stores in durable storage. -fn path_from_h256(index: &H256) -> Result { +pub fn path_from_h256(index: &H256) -> Result { let path_string = alloc::format!("/{}", hex::encode(index.to_fixed_bytes())); OwnedPath::try_from(path_string).map_err(AccountStorageError::from) } @@ -351,6 +362,14 @@ impl EthereumAccount { .map_err(AccountStorageError::from) } + /// 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) + } + /// 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)?; @@ -1168,4 +1187,42 @@ mod test { sample_code_hash ); } + + #[test] + fn test_read_u256_le() { + let mut host = MockHost::default(); + let path = RefPath::assert_from(b"/value"); + assert_eq!( + read_u256(&host, &path, U256::from(128)).unwrap(), + U256::from(128) + ); + + host.store_write_all(&path, &[1u8; 20]).unwrap(); + assert_eq!(read_u256(&host, &path, U256::zero()).unwrap(), U256::zero()); + + host.store_write_all( + &path, + &hex::decode( + "ff00000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(), + ) + .unwrap(); + assert_eq!( + read_u256(&host, &path, U256::zero()).unwrap(), + U256::from(255) + ); + } + + #[test] + fn test_write_u256_le() { + let mut host = MockHost::default(); + let path = RefPath::assert_from(b"/value"); + + write_u256(&mut host, &path, U256::from(255)).unwrap(); + assert_eq!( + hex::encode(host.store_read_all(&path).unwrap()), + "ff00000000000000000000000000000000000000000000000000000000000000" + ); + } } diff --git a/etherlink/kernel_evm/evm_execution/src/fa_bridge/mod.rs b/etherlink/kernel_evm/evm_execution/src/fa_bridge/mod.rs new file mode 100644 index 000000000000..2bd058f32b77 --- /dev/null +++ b/etherlink/kernel_evm/evm_execution/src/fa_bridge/mod.rs @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2023 PK Lab +// +// SPDX-License-Identifier: MIT + +//! FA token bridge. +//! +//! A permissionless transport protocol, that enables ticket transfers +//! from L1 to L2 and back, supporting two destination types: +//! 1. Simple address, which can be both externally owner account, +//! or a smart contract wallet (that supports tickets) +//! 2. Proxy contract, exposing standard methods for deposits (on L2) +//! and withdrawals (on L1); must handle both ticket and +//! routing info that carries the final receiver address. +//! +//! FA bridge maintains the global ticket table, which is a ledger +//! tracking internal ticket ownerships on Etherlink side. +//! +//! FA bridge consists of two main parts: +//! * The one responsible for deposit handling: integrates with the +//! inbox handling flow, results in a pseudo transaction from +//! Zero account. +//! * The one responsible for withdrawal handling: implemented as +//! as precompiled contract, which can be invoked both by EOA +//! or another smart contract. +//! +//! It should be noted that FA withdrawal precompile DOES NOT post any +//! messages to the outbox since it cannot know if the outer transaction +//! fails or succeeds. +//! +//! All the state updates (ticket table, outbox message counter) are done +//! using the transactional Eth account storage, so that they are discarded +//! in case of a revert/failure. + +pub mod ticket_table; 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 new file mode 100644 index 000000000000..dc76fa3c87e5 --- /dev/null +++ b/etherlink/kernel_evm/evm_execution/src/fa_bridge/ticket_table.rs @@ -0,0 +1,152 @@ +// SPDX-FileCopyrightText: 2023 PK Lab +// +// SPDX-License-Identifier: MIT + +//! Global ticket table. +//! +//! Maintains a ledger that tracks ownership of deposited tickets. +//! Any EVM account can be ticket owner, whether it's EOA or smart contract. + +use primitive_types::{H160, H256, U256}; +use tezos_smart_rollup_host::{ + path::{concat, OwnedPath, RefPath}, + runtime::Runtime, +}; + +use crate::account_storage::{ + account_path, path_from_h256, read_u256, write_u256, AccountStorageError, + EthereumAccount, +}; + +/// Path where global ticket table is stored +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, + ticket_hash: &H256, + address: &H160, + amount: U256, + ) -> Result; + + /// Decreases ticket balance + fn ticket_balance_remove( + &mut self, + host: &mut impl Runtime, + ticket_hash: &H256, + address: &H160, + amount: U256, + ) -> Result; +} + +fn ticket_balance_path( + ticket_hash: &H256, + address: &H160, +) -> Result { + let suffix = concat(&path_from_h256(ticket_hash)?, &account_path(address)?)?; + concat(&TICKET_TABLE_PATH, &suffix).map_err(Into::into) +} + +impl TicketTable for EthereumAccount { + fn ticket_balance_add( + &mut self, + host: &mut impl Runtime, + ticket_hash: &H256, + owner: &H160, + amount: U256, + ) -> Result { + let path = self.custom_path(&ticket_balance_path(ticket_hash, owner)?)?; + let balance = read_u256(host, &path, U256::zero())?; + + if let Some(new_balance) = balance.checked_add(amount) { + write_u256(host, &path, new_balance)?; + Ok(true) + } else { + Ok(false) + } + } + + fn ticket_balance_remove( + &mut self, + host: &mut impl Runtime, + ticket_hash: &H256, + owner: &H160, + amount: U256, + ) -> Result { + let path = self.custom_path(&ticket_balance_path(ticket_hash, owner)?)?; + let balance = read_u256(host, &path, U256::zero())?; + + if let Some(new_balance) = balance.checked_sub(amount) { + write_u256(host, &path, new_balance)?; + Ok(true) + } else { + Ok(false) + } + } +} + +#[cfg(test)] +mod tests { + use tezos_smart_rollup_host::path::RefPath; + use tezos_smart_rollup_mock::MockHost; + + use crate::account_storage::read_u256; + + use super::*; + + #[test] + fn ticket_table_balance_add_succeeds() { + let mut host = MockHost::default(); + + let mut account = EthereumAccount::from_address(&H160([0u8; 20])).unwrap(); + + let ticket_hash: H256 = H256([1u8; 32]); + let address = H160([2u8; 20]); + + account + .ticket_balance_add(&mut host, &ticket_hash, &address, 42.into()) + .unwrap(); + account + .ticket_balance_add(&mut host, &ticket_hash, &address, 42.into()) + .unwrap(); + + let path = b"\ + /evm/world_state/eth_accounts/0000000000000000000000000000000000000000/ticket_table\ + /0101010101010101010101010101010101010101010101010101010101010101\ + /0202020202020202020202020202020202020202"; + let balance = + read_u256(&host, &RefPath::assert_from(path), U256::zero()).unwrap(); + + assert_eq!(U256::from(84), balance); + } + + #[test] + fn ticket_table_balance_add_overflows() { + let mut host = MockHost::default(); + + let mut account = EthereumAccount::from_address(&H160([0u8; 20])).unwrap(); + + let ticket_hash: H256 = H256([1u8; 32]); + let address = H160([2u8; 20]); + + account + .ticket_balance_add(&mut host, &ticket_hash, &address, U256::MAX) + .unwrap(); + + let res = account + .ticket_balance_add(&mut host, &ticket_hash, &address, U256::MAX) + .unwrap(); + assert!(!res); + + let path = b"\ + /evm/world_state/eth_accounts/0000000000000000000000000000000000000000/ticket_table\ + /0101010101010101010101010101010101010101010101010101010101010101\ + /0202020202020202020202020202020202020202"; + let balance = + read_u256(&host, &RefPath::assert_from(path), U256::zero()).unwrap(); + + assert_eq!(U256::MAX, balance); + } +} diff --git a/etherlink/kernel_evm/evm_execution/src/lib.rs b/etherlink/kernel_evm/evm_execution/src/lib.rs index 4878a2b88c92..2308cd0450bf 100644 --- a/etherlink/kernel_evm/evm_execution/src/lib.rs +++ b/etherlink/kernel_evm/evm_execution/src/lib.rs @@ -24,6 +24,7 @@ mod access_record; pub mod abi; pub mod account_storage; +pub mod fa_bridge; pub mod handler; pub mod precompiles; pub mod storage; diff --git a/etherlink/kernel_evm/evm_execution/src/utilities.rs b/etherlink/kernel_evm/evm_execution/src/utilities.rs index 8e8bc0ed0588..7e5fadd3c32c 100644 --- a/etherlink/kernel_evm/evm_execution/src/utilities.rs +++ b/etherlink/kernel_evm/evm_execution/src/utilities.rs @@ -7,8 +7,7 @@ use core::cmp::min; use alloc::vec::Vec; use primitive_types::{H160, H256}; -use sha2::Digest; -use sha3::Keccak256; +use sha3::{Digest, Keccak256}; /// Get an array from the data, if data does not contain `start` to `len` bytes, add right padding with /// zeroes diff --git a/etherlink/kernel_evm/evm_execution/tests/contracts/Makefile b/etherlink/kernel_evm/evm_execution/tests/contracts/Makefile index 022a426fd4ff..bef969bae60c 100644 --- a/etherlink/kernel_evm/evm_execution/tests/contracts/Makefile +++ b/etherlink/kernel_evm/evm_execution/tests/contracts/Makefile @@ -7,6 +7,6 @@ artifacts: mkdir artifacts || true forge build - jq ".abi" build/MockWrapper.sol/MockWrapper.json > artifacts/MockWrapper.abi - jq -r ".bytecode.object" build/MockWrapper.sol/MockWrapper.json | xxd -r -p > artifacts/MockWrapper.bytecode - jq ".abi" build/MockPrecompile.sol/MockPrecompile.json > artifacts/MockPrecompile.abi + jq ".abi" build/MockFaBridgeWrapper.sol/MockFaBridgeWrapper.json > artifacts/MockFaBridgeWrapper.abi + jq ".abi" build/MockFaBridgePrecompile.sol/MockFaBridgePrecompile.json > artifacts/MockFaBridgePrecompile.abi + jq -r ".bytecode.object" build/MockFaBridgeWrapper.sol/MockFaBridgeWrapper.json | xxd -r -p > artifacts/MockFaBridgeWrapper.bytecode diff --git a/etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockPrecompile.abi b/etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockFaBridgePrecompile.abi similarity index 52% rename from etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockPrecompile.abi rename to etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockFaBridgePrecompile.abi index f80b97c7af32..75da6e09a2fc 100644 --- a/etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockPrecompile.abi +++ b/etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockFaBridgePrecompile.abi @@ -1,127 +1,127 @@ [ { - "anonymous": false, + "type": "function", + "name": "withdraw", "inputs": [ { - "indexed": true, - "internalType": "uint256", - "name": "ticketHash", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "address", "name": "ticketOwner", - "type": "address" + "type": "address", + "internalType": "address" }, { - "indexed": false, - "internalType": "address", "name": "receiver", - "type": "address" + "type": "bytes", + "internalType": "bytes" }, { - "indexed": false, - "internalType": "uint256", "name": "amount", - "type": "uint256" + "type": "uint256", + "internalType": "uint256" }, { - "indexed": false, - "internalType": "uint256", - "name": "inboxLevel", - "type": "uint256" + "name": "ticketer", + "type": "bytes22", + "internalType": "bytes22" }, { - "indexed": false, - "internalType": "uint256", - "name": "inboxMsgId", - "type": "uint256" + "name": "content", + "type": "bytes", + "internalType": "bytes" } ], - "name": "Deposit", - "type": "event" + "outputs": [], + "stateMutability": "nonpayable" }, { - "anonymous": false, + "type": "event", + "name": "Deposit", "inputs": [ { - "indexed": true, - "internalType": "uint256", "name": "ticketHash", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "address", - "name": "sender", - "type": "address" + "type": "uint256", + "indexed": true, + "internalType": "uint256" }, { - "indexed": false, - "internalType": "address", "name": "ticketOwner", - "type": "address" + "type": "address", + "indexed": false, + "internalType": "address" }, { - "indexed": false, - "internalType": "bytes22", "name": "receiver", - "type": "bytes22" + "type": "address", + "indexed": false, + "internalType": "address" }, { - "indexed": false, - "internalType": "uint256", "name": "amount", - "type": "uint256" + "type": "uint256", + "indexed": false, + "internalType": "uint256" }, { + "name": "inboxLevel", + "type": "uint256", "indexed": false, - "internalType": "uint256", - "name": "outboxLevel", - "type": "uint256" + "internalType": "uint256" }, { + "name": "inboxMsgId", + "type": "uint256", "indexed": false, - "internalType": "uint256", - "name": "outboxMsgId", - "type": "uint256" + "internalType": "uint256" } ], - "name": "Withdrawal", - "type": "event" + "anonymous": false }, { + "type": "event", + "name": "Withdrawal", "inputs": [ { - "internalType": "address", + "name": "ticketHash", + "type": "uint256", + "indexed": true, + "internalType": "uint256" + }, + { + "name": "sender", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { "name": "ticketOwner", - "type": "address" + "type": "address", + "indexed": false, + "internalType": "address" }, { - "internalType": "bytes", "name": "receiver", - "type": "bytes" + "type": "bytes22", + "indexed": false, + "internalType": "bytes22" }, { - "internalType": "uint256", "name": "amount", - "type": "uint256" + "type": "uint256", + "indexed": false, + "internalType": "uint256" }, { - "internalType": "bytes22", - "name": "ticketer", - "type": "bytes22" + "name": "outboxLevel", + "type": "uint256", + "indexed": false, + "internalType": "uint256" }, { - "internalType": "bytes", - "name": "content", - "type": "bytes" + "name": "outboxMsgId", + "type": "uint256", + "indexed": false, + "internalType": "uint256" } ], - "name": "withdraw", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" + "anonymous": false } ] diff --git a/etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockFaBridgeWrapper.abi b/etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockFaBridgeWrapper.abi new file mode 100644 index 000000000000..84e7a6b9994e --- /dev/null +++ b/etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockFaBridgeWrapper.abi @@ -0,0 +1,112 @@ +[ + { + "type": "constructor", + "inputs": [ + { + "name": "ticketer_", + "type": "bytes22", + "internalType": "bytes22" + }, + { + "name": "content_", + "type": "bytes", + "internalType": "bytes" + }, + { + "name": "kernel_", + "type": "address", + "internalType": "address" + }, + { + "name": "flag_", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "deposit", + "inputs": [ + { + "name": "receiver", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "ticketHash", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "withdraw", + "inputs": [ + { + "name": "sender", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "ticketHash", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "Burn", + "inputs": [ + { + "name": "sender", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Mint", + "inputs": [ + { + "name": "receiver", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + } +] diff --git a/etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockWrapper.bytecode b/etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockFaBridgeWrapper.bytecode similarity index 91% rename from etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockWrapper.bytecode rename to etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockFaBridgeWrapper.bytecode index fa1c1ed28b37fc4947839bcfff727881cee09453..9cf073fd6d9b1bfc29f8a0476bff8f239223c87f 100644 GIT binary patch delta 51 zcmZ3*wTf%Qe->?v!v{;cqRUM^ZI=Z#-nHX;asNZ!#>f>nKD0jc6|?ioPASgMNp@!7 I5MwX~0B#}{FaQ7m delta 51 zcmZ3*wTf%Qe-`bZ!8c!jU9Yxz>2kMfD{-N}|IhuNe|SRgT0zd+6Q<6)usEeSKPTCl JfkTwR7y!mx83+IX diff --git a/etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockWrapper.abi b/etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockWrapper.abi deleted file mode 100644 index 9005a8bbe2ba..000000000000 --- a/etherlink/kernel_evm/evm_execution/tests/contracts/artifacts/MockWrapper.abi +++ /dev/null @@ -1,112 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "bytes22", - "name": "ticketer_", - "type": "bytes22" - }, - { - "internalType": "bytes", - "name": "content_", - "type": "bytes" - }, - { - "internalType": "address", - "name": "kernel_", - "type": "address" - }, - { - "internalType": "uint256", - "name": "flag_", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "Burn", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "receiver", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "Mint", - "type": "event" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "receiver", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "ticketHash", - "type": "uint256" - } - ], - "name": "deposit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "ticketHash", - "type": "uint256" - } - ], - "name": "withdraw", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] diff --git a/etherlink/kernel_evm/evm_execution/tests/contracts/src/MockPrecompile.sol b/etherlink/kernel_evm/evm_execution/tests/contracts/src/MockFaBridgePrecompile.sol similarity index 96% rename from etherlink/kernel_evm/evm_execution/tests/contracts/src/MockPrecompile.sol rename to etherlink/kernel_evm/evm_execution/tests/contracts/src/MockFaBridgePrecompile.sol index a6d752b20da8..21d49046d8c1 100644 --- a/etherlink/kernel_evm/evm_execution/tests/contracts/src/MockPrecompile.sol +++ b/etherlink/kernel_evm/evm_execution/tests/contracts/src/MockFaBridgePrecompile.sol @@ -8,7 +8,7 @@ pragma solidity >=0.8.19; * MockWrapper is a helper contract to generate ABIs * for the FA brige precompile. */ -contract MockPrecompile { +contract MockFaBridgePrecompile { event Deposit( uint256 indexed ticketHash, diff --git a/etherlink/kernel_evm/evm_execution/tests/contracts/src/MockWrapper.sol b/etherlink/kernel_evm/evm_execution/tests/contracts/src/MockFaBridgeWrapper.sol similarity index 98% rename from etherlink/kernel_evm/evm_execution/tests/contracts/src/MockWrapper.sol rename to etherlink/kernel_evm/evm_execution/tests/contracts/src/MockFaBridgeWrapper.sol index ec6e28e44e91..85be5b63b507 100644 --- a/etherlink/kernel_evm/evm_execution/tests/contracts/src/MockWrapper.sol +++ b/etherlink/kernel_evm/evm_execution/tests/contracts/src/MockFaBridgeWrapper.sol @@ -14,7 +14,7 @@ function calcTicketHash(bytes22 ticketer, bytes memory content) /** * MockWrapper is a mock token contract which represents a L1 token on L2. */ -contract MockWrapper { +contract MockFaBridgeWrapper { uint256 private _ticketHash; address private _kernel; diff --git a/etherlink/kernel_evm/kernel/Cargo.toml b/etherlink/kernel_evm/kernel/Cargo.toml index ccbf95a1bc70..3cabb2d577f5 100644 --- a/etherlink/kernel_evm/kernel/Cargo.toml +++ b/etherlink/kernel_evm/kernel/Cargo.toml @@ -59,6 +59,11 @@ tezos-smart-rollup-mock.workspace = true tezos-smart-rollup-panic-hook.workspace = true proptest.workspace = true +# Hack: getrandom will use custom implementation if kernel is being built for wasm32-unknown-unknown +# See https://github.com/rust-random/getrandom/blob/a39033a34a0b81c5b15ef1fba28696ab93aac9db/src/custom.rs +# Generally getrandom is not supposed to be used in wasm env, this trick is just to overcome build errors +getrandom = { version = "=0.2.15", features = ["custom"] } + [features] default = ["panic-hook"] panic-hook = [] -- GitLab