From 24da19b6bbb04c9485f4f529d2e983a43ddad680 Mon Sep 17 00:00:00 2001 From: Michael Zaikin Date: Wed, 26 Jun 2024 15:20:24 +0100 Subject: [PATCH] EVM: add FA bridge precompile and FA withdrawal execution Co-authored-by: Rodi-Can Bozman Co-authored-by: Valentin Chaboche --- etherlink/CHANGES_KERNEL.md | 1 + .../kernel_evm/evm_evaluation/src/runner.rs | 2 +- etherlink/kernel_evm/evm_execution/Cargo.toml | 3 +- .../evm_execution/src/fa_bridge/mod.rs | 2 +- .../evm_execution/src/fa_bridge/test_utils.rs | 7 +- .../kernel_evm/evm_execution/src/handler.rs | 66 +++--- etherlink/kernel_evm/evm_execution/src/lib.rs | 84 +++---- .../src/precompiles/fa_bridge.rs | 16 +- .../evm_execution/src/precompiles/mod.rs | 20 +- etherlink/kernel_evm/kernel/Cargo.toml | 3 +- etherlink/kernel_evm/kernel/src/block.rs | 5 +- etherlink/kernel_evm/kernel/src/inbox.rs | 14 +- etherlink/kernel_evm/kernel/src/lib.rs | 128 ++++++++++- etherlink/kernel_evm/kernel/src/simulation.rs | 26 ++- etherlink/kernel_evm/kernel/src/stage_one.rs | 8 +- .../fa_bridge/ticket_router_tester.tz | 14 +- etherlink/tezos_contracts/fa_withdrawal.abi | 121 ++++++++++ etherlink/tezt/lib/contract_path.ml | 3 + etherlink/tezt/lib/dune | 3 +- etherlink/tezt/lib/helpers.ml | 65 ++++++ etherlink/tezt/lib/helpers.mli | 14 ++ etherlink/tezt/tests/evm_rollup.ml | 63 ----- etherlink/tezt/tests/evm_sequencer.ml | 215 +++++++++++++++++- manifest/product_etherlink.ml | 2 +- 24 files changed, 679 insertions(+), 206 deletions(-) create mode 100644 etherlink/tezos_contracts/fa_withdrawal.abi diff --git a/etherlink/CHANGES_KERNEL.md b/etherlink/CHANGES_KERNEL.md index 8b74badb8e56..b837c92c95c1 100644 --- a/etherlink/CHANGES_KERNEL.md +++ b/etherlink/CHANGES_KERNEL.md @@ -12,6 +12,7 @@ error of 5 minutes. 5 minutes is the default value but can be configured by the installer. (!13827) - FA deposits are applied if FA bridge feature is enabled. (!13835) +- FA withdrawals are applied if FA bridge feature is enabled. (!13958) ## Bug fixes diff --git a/etherlink/kernel_evm/evm_evaluation/src/runner.rs b/etherlink/kernel_evm/evm_evaluation/src/runner.rs index 113cd0c595d1..385e43cdb95e 100644 --- a/etherlink/kernel_evm/evm_evaluation/src/runner.rs +++ b/etherlink/kernel_evm/evm_evaluation/src/runner.rs @@ -319,7 +319,7 @@ pub fn run_test( if output.log { write_out!(output_file, "Running unit test: {}", name); } - let precompiles = precompile_set::(); + let precompiles = precompile_set::(false); let mut evm_account_storage = init_account_storage().unwrap(); let filler_source = prepare_filler_source(&host, &unit, opt)?; diff --git a/etherlink/kernel_evm/evm_execution/Cargo.toml b/etherlink/kernel_evm/evm_execution/Cargo.toml index b0bb1bf587b4..5243db920457 100644 --- a/etherlink/kernel_evm/evm_execution/Cargo.toml +++ b/etherlink/kernel_evm/evm_execution/Cargo.toml @@ -66,7 +66,8 @@ alloy-primitives.workspace = true [features] default = ["evm_execution"] -testing = ["rand", "proptest", "dep:tezos-smart-rollup-mock", "dep:alloy-primitives", "dep:alloy-sol-types"] +testing = ["rand", "proptest", "dep:tezos-smart-rollup-mock"] +fa_bridge_testing = ["dep:tezos-smart-rollup-mock", "dep:alloy-primitives", "dep:alloy-sol-types"] evm_execution = [] debug = ["tezos-evm-logging/debug"] # the `benchmark` and `benchmark-opcodes` feature flags instrument the kernel for profiling 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 70fbf0e6c7f8..9d97d3f17c98 100644 --- a/etherlink/kernel_evm/evm_execution/src/fa_bridge/mod.rs +++ b/etherlink/kernel_evm/evm_execution/src/fa_bridge/mod.rs @@ -59,7 +59,7 @@ pub mod withdrawal; #[cfg(test)] mod tests; -#[cfg(any(test, feature = "testing"))] +#[cfg(any(test, feature = "fa_bridge_testing"))] pub mod test_utils; /// TODO: Gas limit for calling "deposit" method of the proxy contract call. 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 7e6fd19954af..c4041418186a 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 @@ -2,6 +2,7 @@ // // SPDX-License-Identifier: MIT +pub use alloy_sol_types::SolCall; use alloy_sol_types::{sol, SolConstructor}; use crypto::hash::ContractKt1Hash; use evm::Config; @@ -63,7 +64,7 @@ pub fn deploy_mock_wrapper( )); let block = dummy_block_constants(); - let precompiles = precompile_set::(); + let precompiles = precompile_set::(false); set_balance(host, evm_account_storage, caller, U256::from(1_000_000)); run_transaction( @@ -96,7 +97,7 @@ pub fn run_fa_deposit( caller: &H160, ) -> ExecutionOutcome { let block = dummy_block_constants(); - let precompiles = precompile_set::(); + let precompiles = precompile_set::(false); execute_fa_deposit( host, @@ -306,7 +307,7 @@ pub fn fa_bridge_precompile_call_withdraw( caller: H160, ) -> ExecutionOutcome { let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let config = Config::shanghai(); let mut handler = EvmHandler::new( diff --git a/etherlink/kernel_evm/evm_execution/src/handler.rs b/etherlink/kernel_evm/evm_execution/src/handler.rs index 55c846c1a1f0..a334da591c3c 100644 --- a/etherlink/kernel_evm/evm_execution/src/handler.rs +++ b/etherlink/kernel_evm/evm_execution/src/handler.rs @@ -2503,7 +2503,7 @@ mod test { fn legacy_create_to_correct_address() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); @@ -2546,7 +2546,7 @@ mod test { fn create2_to_correct_address() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); let caller: H160 = @@ -2588,7 +2588,7 @@ mod test { fn create2_to_correct_address_nonzero_salt() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); @@ -2634,7 +2634,7 @@ mod test { fn origin_instruction_returns_origin_address() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -2699,7 +2699,7 @@ mod test { fn contract_call_produces_correct_output() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -2793,7 +2793,7 @@ mod test { fn contract_call_fails_beyond_max_stack_depth() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -2888,7 +2888,7 @@ mod test { fn contract_call_succeeds_at_maximum_stack_depth() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -2981,7 +2981,7 @@ mod test { fn contract_can_use_durable_storage() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -3053,7 +3053,7 @@ mod test { fn contract_create_can_use_durable_storage() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -3111,7 +3111,7 @@ mod test { fn contract_create_has_return_when_revert() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -3177,7 +3177,7 @@ mod test { fn contract_call_does_transfer() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -3240,7 +3240,7 @@ mod test { fn contract_call_fails_when_insufficient_funds_for_transfer() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -3302,7 +3302,7 @@ mod test { fn revert_can_return_a_value() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -3373,7 +3373,7 @@ mod test { fn return_hash_of_zero_for_unavailable_block() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -3401,7 +3401,7 @@ mod test { fn transactions_fails_if_not_enough_allocated_ticks() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -3462,7 +3462,7 @@ mod test { fn store_after_offset_1024() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -3514,7 +3514,7 @@ mod test { fn dont_crash_on_blockhash_instruction() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -3582,7 +3582,7 @@ mod test { fn prevent_collision_create2_selfdestruct() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); @@ -3651,7 +3651,7 @@ mod test { //Init let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); @@ -3699,7 +3699,7 @@ mod test { fn inter_call_with_non_zero_transfer_value_gets_call_stipend() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -3775,7 +3775,7 @@ mod test { fn code_hash_of_zero_for_non_existing_address() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -3804,7 +3804,7 @@ mod test { fn create_contract_with_selfdestruct_init_code() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); @@ -3848,7 +3848,7 @@ mod test { fn contract_that_selfdestruct_not_deleted_within_same_transaction() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -3931,7 +3931,7 @@ mod test { fn contract_that_selfdestruct_can_be_called_again_in_same_transaction() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -4031,7 +4031,7 @@ mod test { fn contract_selfdestruct_itself_has_no_balance_left() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); @@ -4097,7 +4097,7 @@ mod test { fn address_still_marked_as_hot_after_creation_fails() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); @@ -4171,7 +4171,7 @@ mod test { fn precompile_failure_are_not_fatal() { let mut host = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -4230,7 +4230,7 @@ mod test { fn inner_create_costs_gas() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -4317,7 +4317,7 @@ mod test { fn exceed_max_create_init_code_size_fail_with_error() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -4357,7 +4357,7 @@ mod test { fn create_fails_with_max_nonce() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_account_storage().unwrap(); let config = Config::shanghai(); @@ -4399,7 +4399,7 @@ mod test { fn record_call_stipend_when_balance_not_enough_for_inner_call() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -4477,7 +4477,7 @@ mod test { fn eip161_gas_consumption_rules_for_suicide() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -4549,7 +4549,7 @@ mod test { fn eip161_gas_consumption_rules_for_call() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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 eeeb6faf4693..e452b61b2f9b 100644 --- a/etherlink/kernel_evm/evm_execution/src/lib.rs +++ b/etherlink/kernel_evm/evm_execution/src/lib.rs @@ -444,7 +444,7 @@ mod test { fn transfer_without_sufficient_funds_fails() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let callee = H160::from_low_u64_be(234213); @@ -511,7 +511,7 @@ mod test { fn transfer_funds_with_sufficient_balance() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let callee = H160::from_low_u64_be(82193); @@ -579,7 +579,7 @@ mod test { fn create_contract_fails_with_insufficient_funds() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let callee = None; @@ -635,7 +635,7 @@ mod test { fn create_contract_succeeds_with_valid_initialization() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let callee = None; @@ -786,7 +786,7 @@ mod test { fn create_contract_erc20_succeeds() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let callee = None; @@ -842,7 +842,7 @@ mod test { fn create_contract_fails_when_initialization_fails() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let callee = None; @@ -902,7 +902,7 @@ mod test { // Arange let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -1041,7 +1041,7 @@ mod test { // Arrange let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -1105,7 +1105,7 @@ mod test { // Arrange let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -1176,7 +1176,7 @@ mod test { // Arrange let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -1230,7 +1230,7 @@ mod test { fn no_transfer_when_contract_call_fails() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -1297,7 +1297,7 @@ mod test { // Arrange let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -1354,7 +1354,7 @@ mod test { // Arrange let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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) @@ -1415,7 +1415,7 @@ mod test { // Arrange let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -1517,7 +1517,7 @@ mod test { fn static_calls_cannot_update_storage() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -1613,7 +1613,7 @@ mod test { fn static_calls_fail_when_logging() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -1709,7 +1709,7 @@ mod test { fn logs_get_written_to_output() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -1832,7 +1832,7 @@ mod test { fn no_logs_when_contract_reverts() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -1940,7 +1940,7 @@ mod test { fn contract_selfdestruct_deletes_contract() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -2053,7 +2053,7 @@ mod test { fn selfdestruct_is_ignored_when_call_fails() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -2188,7 +2188,7 @@ mod test { u64::MAX, H160::zero(), ); - let precompiles = precompiles::precompile_set::(); + 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); @@ -2272,7 +2272,7 @@ mod test { u64::MAX, H160::zero(), ); - let precompiles = precompiles::precompile_set::(); + 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); @@ -2363,7 +2363,7 @@ mod test { fn evm_should_fail_gracefully_when_balance_overflow_occurs() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -2411,7 +2411,7 @@ mod test { fn create_contract_gas_cost() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let callee = None; @@ -2470,7 +2470,7 @@ mod test { fn create_contract_fail_gas_cost() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let callee = None; @@ -2540,7 +2540,7 @@ mod test { u64::MAX, H160::zero(), ); - let precompiles = precompiles::precompile_set::(); + 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); @@ -2610,7 +2610,7 @@ mod test { u64::MAX, H160::zero(), ); - let precompiles = precompiles::precompile_set::(); + 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); @@ -2686,7 +2686,7 @@ mod test { // Arrange let mut mock_runtime = MockHost::default(); let block = first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -2768,7 +2768,7 @@ mod test { fn test_create_to_address_with_code_returns_error() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let caller = @@ -2841,7 +2841,7 @@ mod test { fn test_caller_nonce_after_create() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let caller = @@ -2941,7 +2941,7 @@ mod test { fn test_caller_nonce_after_create_collision() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let caller = @@ -3049,7 +3049,7 @@ mod test { fn test_create_to_address_with_non_zero_nonce_returns_error() { let mut mock_runtime = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let caller = @@ -3114,7 +3114,7 @@ mod test { fn created_contract_start_at_nonce_one() { let mut host = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let callee = None; @@ -3160,7 +3160,7 @@ mod test { fn call_contract_create_contract_with_insufficient_funds() { let mut host = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let callee = H160::from_str("095e7baea6a6c7c4c2dfeb977efac326af552d87").unwrap(); @@ -3225,7 +3225,7 @@ mod test { fn nested_create_check_nonce_start_at_one() { let mut host = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let caller = H160::from_str("a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap(); @@ -3347,7 +3347,7 @@ mod test { ) { let mut host = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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); @@ -3451,7 +3451,7 @@ mod test { fn multiple_call_all_the_way_to_1024() { let mut host = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let caller = H160::from_str("a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap(); @@ -3538,7 +3538,7 @@ mod test { fn multiple_call_fails_right_after_1024() { let mut host = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let caller = H160::from_str("a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap(); @@ -3623,7 +3623,7 @@ mod test { fn call_too_deep_not_revert() { let mut host = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let caller = H160::from_str("a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap(); @@ -3674,7 +3674,7 @@ mod test { let mut host = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let address_1 = @@ -3779,7 +3779,7 @@ mod test { let mut host = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = init_evm_account_storage().unwrap(); let address_1 = @@ -3856,7 +3856,7 @@ mod test { fn nonce_bump_before_tx() { let mut host = MockHost::default(); let block = dummy_first_block(); - let precompiles = precompiles::precompile_set::(); + 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/fa_bridge.rs b/etherlink/kernel_evm/evm_execution/src/precompiles/fa_bridge.rs index 12d34ff4131a..d71fde9a4d55 100644 --- a/etherlink/kernel_evm/evm_execution/src/precompiles/fa_bridge.rs +++ b/etherlink/kernel_evm/evm_execution/src/precompiles/fa_bridge.rs @@ -132,13 +132,11 @@ mod tests { }, }, handler::{EvmHandler, ExecutionOutcome, ExtendedExitReason}, - precompiles::{self, PrecompileFn, FA_BRIDGE_PRECOMPILE_ADDRESS}, + precompiles::{self, FA_BRIDGE_PRECOMPILE_ADDRESS}, transaction::TransactionContext, utilities::{bigint_to_u256, keccak256_hash}, }; - use super::fa_bridge_precompile; - fn execute_precompile( host: &mut MockHost, evm_account_storage: &mut EthereumAccountStorage, @@ -152,11 +150,7 @@ mod tests { let config = Config::shanghai(); let callee = FA_BRIDGE_PRECOMPILE_ADDRESS; - let mut precompiles = precompiles::precompile_set::(); - precompiles.insert( - FA_BRIDGE_PRECOMPILE_ADDRESS, - fa_bridge_precompile as PrecompileFn, - ); + let precompiles = precompiles::precompile_set::(true); let mut handler = EvmHandler::new( host, @@ -276,11 +270,7 @@ mod tests { let block = dummy_first_block(); let config = Config::shanghai(); - let mut precompiles = precompiles::precompile_set::(); - precompiles.insert( - FA_BRIDGE_PRECOMPILE_ADDRESS, - fa_bridge_precompile as PrecompileFn, - ); + let precompiles = precompiles::precompile_set::(true); let mut handler = EvmHandler::new( &mut mock_runtime, diff --git a/etherlink/kernel_evm/evm_execution/src/precompiles/mod.rs b/etherlink/kernel_evm/evm_execution/src/precompiles/mod.rs index 37a6cf61cb0e..3c0b33bf8314 100644 --- a/etherlink/kernel_evm/evm_execution/src/precompiles/mod.rs +++ b/etherlink/kernel_evm/evm_execution/src/precompiles/mod.rs @@ -28,6 +28,7 @@ use alloc::collections::btree_map::BTreeMap; use blake2::blake2f_precompile; use ecdsa::ecrecover_precompile; use evm::{Context, ExitReason, Handler, Transfer}; +use fa_bridge::fa_bridge_precompile; use hash::{ripemd160_precompile, sha256_precompile}; use host::runtime::Runtime; use identity::identity_precompile; @@ -156,8 +157,10 @@ pub const WITHDRAWAL_ADDRESS: H160 = H160([ ]); /// Factory function for generating the precompileset that the EVM kernel uses. -pub fn precompile_set() -> PrecompileBTreeMap { - BTreeMap::from([ +pub fn precompile_set( + enable_fa_withdrawals: bool, +) -> PrecompileBTreeMap { + let mut precompiles = BTreeMap::from([ ( H160::from_low_u64_be(1u64), ecrecover_precompile as PrecompileFn, @@ -198,7 +201,16 @@ pub fn precompile_set() -> PrecompileBTreeMap { WITHDRAWAL_ADDRESS, withdrawal_precompile as PrecompileFn, ), - ]) + ]); + + if enable_fa_withdrawals { + precompiles.insert( + FA_BRIDGE_PRECOMPILE_ADDRESS, + fa_bridge_precompile as PrecompileFn, + ); + } + + precompiles } #[macro_export] @@ -306,7 +318,7 @@ mod test_helpers { H160::zero(), ); let mut evm_account_storage = init_evm_account_storage().unwrap(); - let precompiles = precompile_set::(); + let precompiles = precompile_set::(false); let config = Config::shanghai(); let gas_price = U256::from(21000); diff --git a/etherlink/kernel_evm/kernel/Cargo.toml b/etherlink/kernel_evm/kernel/Cargo.toml index e91583528d03..d44f4eb9477f 100644 --- a/etherlink/kernel_evm/kernel/Cargo.toml +++ b/etherlink/kernel_evm/kernel/Cargo.toml @@ -66,11 +66,12 @@ proptest.workspace = true getrandom = { version = "=0.2.15", features = ["custom"] } pretty_assertions.workspace = true +evm-execution = { workspace = true, features = ["fa_bridge_testing"] } [features] default = ["panic-hook"] panic-hook = [] -testing = ["proptest", "debug"] +testing = ["proptest", "debug", "evm-execution/testing"] debug = ["tezos-evm-logging/debug"] benchmark = ["tezos-evm-logging/benchmark", "evm-execution/benchmark"] benchmark-bypass-stage2 = ["benchmark"] diff --git a/etherlink/kernel_evm/kernel/src/block.rs b/etherlink/kernel_evm/kernel/src/block.rs index e34947a17a7c..21246c047264 100644 --- a/etherlink/kernel_evm/kernel/src/block.rs +++ b/etherlink/kernel_evm/kernel/src/block.rs @@ -381,7 +381,8 @@ pub fn produce( internal: &mut internal_storage, }; let outbox_queue = OutboxQueue::new(&WITHDRAWAL_OUTBOX_QUEUE, u32::MAX)?; - let precompiles = precompiles::precompile_set::>(); + let precompiles = + precompiles::precompile_set::>(config.enable_fa_bridge); // Check if there's a BIP in storage to resume its execution match storage::read_block_in_progress(&safe_host)? { @@ -1324,7 +1325,7 @@ mod tests { let mut host = MockHost::default(); let block_constants = first_block(&mut host); - let precompiles = precompiles::precompile_set(); + let precompiles = precompiles::precompile_set(false); let mut accounts_index = init_account_index().unwrap(); //provision sender account diff --git a/etherlink/kernel_evm/kernel/src/inbox.rs b/etherlink/kernel_evm/kernel/src/inbox.rs index 017bf877aba1..46369c1155e0 100644 --- a/etherlink/kernel_evm/kernel/src/inbox.rs +++ b/etherlink/kernel_evm/kernel/src/inbox.rs @@ -519,7 +519,7 @@ fn read_and_dispatch_input( parsing_context: &mut Mode::Context, inbox_is_empty: &mut bool, res: &mut Mode::Inbox, - enable_fa_deposits: bool, + enable_fa_bridge: bool, ) -> anyhow::Result { let input: InputResult = read_input( host, @@ -527,7 +527,7 @@ fn read_and_dispatch_input( tezos_contracts, inbox_is_empty, parsing_context, - enable_fa_deposits, + enable_fa_bridge, )?; match input { InputResult::NoInput => { @@ -547,7 +547,7 @@ fn read_and_dispatch_input( // kernel enters in simulation mode, reading will be done by the // simulation and all the previous and next transactions are // discarded. - simulation::start_simulation_mode(host)?; + simulation::start_simulation_mode(host, enable_fa_bridge)?; Ok(ReadStatus::FinishedIgnore) } InputResult::Input(input) => { @@ -561,7 +561,7 @@ pub fn read_proxy_inbox( host: &mut Host, smart_rollup_address: [u8; 20], tezos_contracts: &TezosContracts, - enable_fa_deposits: bool, + enable_fa_bridge: bool, ) -> Result, anyhow::Error> { let mut res = ProxyInboxContent { transactions: vec![], @@ -579,7 +579,7 @@ pub fn read_proxy_inbox( &mut (), &mut inbox_is_empty, &mut res, - enable_fa_deposits, + enable_fa_bridge, ) { Err(err) => // If we failed to read or dispatch the input. @@ -627,7 +627,7 @@ pub fn read_sequencer_inbox( delayed_bridge: ContractKt1Hash, sequencer: PublicKey, delayed_inbox: &mut DelayedInbox, - enable_fa_deposits: bool, + enable_fa_bridge: bool, ) -> Result { // The mutable variable is used to retrieve the information of whether the // inbox was empty or not. As we consume all the inbox in one go, if the @@ -663,7 +663,7 @@ pub fn read_sequencer_inbox( &mut parsing_context, &mut inbox_is_empty, delayed_inbox, - enable_fa_deposits, + enable_fa_bridge, ) { Err(err) => // If we failed to read or dispatch the input. diff --git a/etherlink/kernel_evm/kernel/src/lib.rs b/etherlink/kernel_evm/kernel/src/lib.rs index 41219e85595c..80fe70a16b47 100644 --- a/etherlink/kernel_evm/kernel/src/lib.rs +++ b/etherlink/kernel_evm/kernel/src/lib.rs @@ -369,8 +369,13 @@ mod tests { }; use evm_execution::account_storage::{self, EthereumAccountStorage}; use evm_execution::fa_bridge::deposit::{ticket_hash, FaDeposit}; + use evm_execution::fa_bridge::test_utils::{ + convert_h160, convert_u256, dummy_ticket, kernel_wrapper, ticket_balance_add, + ticket_id, SolCall, + }; use evm_execution::handler::RouterInterface; - use evm_execution::utilities::keccak256_hash; + use evm_execution::precompiles::FA_BRIDGE_PRECOMPILE_ADDRESS; + use evm_execution::utilities::{bigint_to_u256, keccak256_hash}; use evm_execution::NATIVE_TOKEN_TICKETER_PATH; use pretty_assertions::assert_eq; use primitive_types::{H160, U256}; @@ -871,4 +876,125 @@ mod tests { fn test_fa_deposit_rejected_if_feature_disabled() { assert_eq!(send_fa_deposit(false), None); } + + fn send_fa_withdrawal(enable_fa_bridge: bool) -> Vec> { + // init host + let mut mock_host = MockHost::default(); + let mut internal = MockInternal(); + let mut safe_storage = SafeStorage { + host: &mut mock_host, + internal: &mut internal, + }; + + // enable FA bridge feature + if enable_fa_bridge { + safe_storage + .store_write_all(&ENABLE_FA_BRIDGE, &[1u8]) + .unwrap(); + } + + // run level in order to initialize outbox counter (by SOL message) + let level = safe_storage.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(); + set_balance( + &mut safe_storage, + &mut evm_account_storage, + &sender, + sender_initial_balance, + ); + + // construct ticket + let ticket = dummy_ticket(); + let ticket_hash = ticket_hash(&ticket).unwrap(); + let amount = bigint_to_u256(ticket.amount()).unwrap(); + + // patch ticket table + ticket_balance_add( + &mut safe_storage, + &mut evm_account_storage, + &ticket_hash, + &sender, + amount, + ); + + // construct withdraw calldata + let (ticketer, content) = ticket_id(&ticket); + let routing_info = hex::decode("0000000000000000000000000000000000000000000001000000000000000000000000000000000000000000").unwrap(); + + let data = kernel_wrapper::withdrawCall::new(( + convert_h160(&sender), + routing_info.into(), + convert_u256(&amount), + ticketer.into(), + content.into(), + )) + .abi_encode(); + + // create and sign precompile call + let gas_price = U256::from(40000000000u64); + let to = FA_BRIDGE_PRECOMPILE_ADDRESS; + let tx = EthereumTransactionCommon::new( + TransactionType::Legacy, + Some(U256::from(1337)), + 0, + gas_price, + gas_price, + 2000000, + Some(to), + U256::zero(), + data, + vec![], + None, + ); + + // corresponding caller's address is 0xaf1276cbb260bb13deddb4209ae99ae6e497f446 + let tx_payload = tx + .sign_transaction( + "dcdff53b4f013dbcdc717f89fe3bf4d8b10512aae282b48e01d7530470382701" + .to_string(), + ) + .unwrap() + .to_bytes(); + + let tx_hash = keccak256_hash(&tx_payload); + + // encode as external message and submit to inbox + let mut contents = Vec::new(); + contents.push(0x00); // simple tx tag + contents.extend_from_slice(tx_hash.as_bytes()); + contents.extend_from_slice(&tx_payload); + + let message = ExternalMessageFrame::Targetted { + address: SmartRollupAddress::from_b58check( + "sr163Lv22CdE8QagCwf48PWDTquk6isQwv57", + ) + .unwrap(), + contents, + }; + + safe_storage.host.add_external(message); + + // run kernel + main(&mut safe_storage).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"); + + safe_storage.host.outbox_at(level + 1) + } + + #[test] + fn test_fa_withdrawal_applied_if_feature_enabled() { + // verify outbox is not empty + assert_eq!(send_fa_withdrawal(true).len(), 1); + } + + #[test] + fn test_fa_withdrawal_rejected_if_feature_disabled() { + // verify outbox is empty + assert!(send_fa_withdrawal(false).is_empty()); + } } diff --git a/etherlink/kernel_evm/kernel/src/simulation.rs b/etherlink/kernel_evm/kernel/src/simulation.rs index 724e3a287b38..26051f4aca0b 100644 --- a/etherlink/kernel_evm/kernel/src/simulation.rs +++ b/etherlink/kernel_evm/kernel/src/simulation.rs @@ -315,6 +315,7 @@ impl Evaluation { &self, host: &mut Host, tracer_config: Option, + enable_fa_withdrawals: bool, ) -> Result, Error> { let chain_id = retrieve_chain_id(host)?; let block_fees = retrieve_block_fees(host)?; @@ -348,7 +349,7 @@ impl Evaluation { let mut evm_account_storage = account_storage::init_account_storage() .map_err(|_| Error::Storage(StorageError::AccountInitialisation))?; - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(enable_fa_withdrawals); let default_caller = H160::zero(); let tx_data_size = self.data.len() as u64; let limits = fetch_limits(host); @@ -419,6 +420,7 @@ impl TxValidation { host: &mut Host, transaction: &EthereumTransactionCommon, caller: &H160, + enable_fa_withdrawals: bool, ) -> Result, anyhow::Error> { let chain_id = retrieve_chain_id(host)?; let block_fees = retrieve_block_fees(host)?; @@ -453,7 +455,7 @@ impl TxValidation { let mut evm_account_storage = account_storage::init_account_storage() .map_err(|_| Error::Storage(StorageError::AccountInitialisation))?; - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(enable_fa_withdrawals); let tx_data_size = transaction.data.len() as u64; let limits = fetch_limits(host); let allocated_ticks = @@ -522,6 +524,7 @@ impl TxValidation { pub fn run( &self, host: &mut Host, + enable_fa_withdrawals: bool, ) -> Result, anyhow::Error> { let tx = &self.transaction; let evm_account_storage = account_storage::init_account_storage()?; @@ -552,7 +555,7 @@ impl TxValidation { } // Check if running the transaction (assuming it is valid) would run out // of ticks, or fail validation for another reason. - Self::validate(host, tx, &caller) + Self::validate(host, tx, &caller, enable_fa_withdrawals) } } @@ -691,6 +694,7 @@ impl VersionedEncoding for SimulationResult pub fn start_simulation_mode( host: &mut Host, + enable_fa_withdrawals: bool, ) -> Result<(), anyhow::Error> { log!(host, Debug, "Starting simulation mode "); let simulation = parse_inbox(host)?; @@ -698,11 +702,11 @@ pub fn start_simulation_mode( Message::Evaluation(simulation) => { let trace_input = read_tracer_input(host)?; let tracer_config = get_tracer_config(None, &trace_input); - let outcome = simulation.run(host, tracer_config)?; + let outcome = simulation.run(host, tracer_config, enable_fa_withdrawals)?; storage::store_simulation_result(host, outcome) } Message::TxValidation(tx_validation) => { - let outcome = tx_validation.run(host)?; + let outcome = tx_validation.run(host, enable_fa_withdrawals)?; storage::store_simulation_result(host, outcome) } } @@ -809,7 +813,7 @@ mod tests { crate::block::GAS_LIMIT, H160::zero(), ); - let precompiles = precompiles::precompile_set::(); + let precompiles = precompiles::precompile_set::(false); let mut evm_account_storage = account_storage::init_account_storage().unwrap(); let callee = None; @@ -865,7 +869,7 @@ mod tests { value: None, with_da_fees: false, }; - let outcome = evaluation.run(&mut host, None); + let outcome = evaluation.run(&mut host, None, false); assert!(outcome.is_ok(), "evaluation should have succeeded"); let outcome = outcome.unwrap(); @@ -890,7 +894,7 @@ mod tests { value: None, with_da_fees: false, }; - let outcome = evaluation.run(&mut host, None); + let outcome = evaluation.run(&mut host, None, false); assert!(outcome.is_ok(), "simulation should have succeeded"); let outcome = outcome.unwrap(); @@ -921,7 +925,7 @@ mod tests { value: None, with_da_fees: false, }; - let outcome = evaluation.run(&mut host, None); + let outcome = evaluation.run(&mut host, None, false); assert!(outcome.is_ok(), "evaluation should have succeeded"); let outcome = outcome.unwrap(); @@ -1151,7 +1155,7 @@ mod tests { .unwrap(), ) .unwrap(); - let result = simulation.run(&mut host); + let result = simulation.run(&mut host, false); println!("{result:?}"); assert!(result.is_ok()); assert_eq!( @@ -1200,7 +1204,7 @@ mod tests { .unwrap(), ) .unwrap(); - let result = simulation.run(&mut host); + let result = simulation.run(&mut host, false); assert!(result.is_ok()); assert_eq!( diff --git a/etherlink/kernel_evm/kernel/src/stage_one.rs b/etherlink/kernel_evm/kernel/src/stage_one.rs index 546a2126b7f2..cdfc7789506b 100644 --- a/etherlink/kernel_evm/kernel/src/stage_one.rs +++ b/etherlink/kernel_evm/kernel/src/stage_one.rs @@ -25,13 +25,13 @@ pub fn fetch_proxy_blueprints( host: &mut Host, smart_rollup_address: [u8; RAW_ROLLUP_ADDRESS_SIZE], tezos_contracts: &TezosContracts, - enable_fa_deposits: bool, + enable_fa_bridge: bool, ) -> Result { if let Some(ProxyInboxContent { transactions }) = read_proxy_inbox( host, smart_rollup_address, tezos_contracts, - enable_fa_deposits, + enable_fa_bridge, )? { let timestamp = current_timestamp(host); let blueprint = Blueprint { @@ -103,7 +103,7 @@ fn fetch_sequencer_blueprints( delayed_inbox: &mut DelayedInbox, sequencer: PublicKey, _dal: Option, - enable_fa_deposits: bool, + enable_fa_bridge: bool, ) -> Result { match read_sequencer_inbox( host, @@ -112,7 +112,7 @@ fn fetch_sequencer_blueprints( delayed_bridge, sequencer, delayed_inbox, - enable_fa_deposits, + enable_fa_bridge, )? { StageOneStatus::Done => { // Check if there are timed-out transactions in the delayed inbox diff --git a/etherlink/tezos_contracts/fa_bridge/ticket_router_tester.tz b/etherlink/tezos_contracts/fa_bridge/ticket_router_tester.tz index 3c6331f7b305..ada4cbe8d2f2 100644 --- a/etherlink/tezos_contracts/fa_bridge/ticket_router_tester.tz +++ b/etherlink/tezos_contracts/fa_bridge/ticket_router_tester.tz @@ -18,13 +18,10 @@ (mutez %xtz_amount)) (big_map %metadata string bytes)) ; code { LAMBDA - (pair (ticket (pair nat (option bytes))) - (pair address (or unit (or address bytes)) mutez) - (big_map string bytes)) + (pair (ticket (pair nat (option bytes))) address (or unit (or address bytes)) mutez) operation { UNPAIR ; SWAP ; - CAR ; UNPAIR 3 ; SWAP ; IF_LEFT @@ -68,6 +65,7 @@ DUP 2 ; NIL operation ; DIG 3 ; + CAR ; DIG 3 ; PAIR ; DIG 3 ; @@ -75,11 +73,14 @@ EXEC ; CONS } { IF_LEFT - { DUP 2 ; + { UNPAIR ; + DUP 3 ; NIL operation ; + DIG 4 ; + CAR ; DIG 3 ; + UPDATE 1 ; DIG 3 ; - CDR ; PAIR ; DIG 3 ; SWAP ; @@ -89,6 +90,7 @@ { DUP 2 ; NIL operation ; DIG 3 ; + CAR ; DIG 3 ; PAIR ; DIG 3 ; diff --git a/etherlink/tezos_contracts/fa_withdrawal.abi b/etherlink/tezos_contracts/fa_withdrawal.abi new file mode 100644 index 000000000000..00f07254daa6 --- /dev/null +++ b/etherlink/tezos_contracts/fa_withdrawal.abi @@ -0,0 +1,121 @@ +[ + { + "type": "function", + "name": "withdraw", + "inputs": [ + { + "name": "ticketOwner", + "type": "address", + "internalType": "address" + }, + { + "name": "receiver", + "type": "bytes", + "internalType": "bytes" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "ticketer", + "type": "bytes22", + "internalType": "bytes22" + }, + { + "name": "content", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "Deposit", + "inputs": [ + { + "name": "ticketHash", + "type": "uint256", + "indexed": true, + "internalType": "uint256" + }, + { + "name": "ticketOwner", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "receiver", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "inboxLevel", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "inboxMsgId", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Withdrawal", + "inputs": [ + { + "name": "ticketHash", + "type": "uint256", + "indexed": true, + "internalType": "uint256" + }, + { + "name": "sender", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "ticketOwner", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "receiver", + "type": "bytes22", + "indexed": false, + "internalType": "bytes22" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "withdrawalId", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + } +] diff --git a/etherlink/tezt/lib/contract_path.ml b/etherlink/tezt/lib/contract_path.ml index 6f6ec2d9e404..26975877f89e 100644 --- a/etherlink/tezt/lib/contract_path.ml +++ b/etherlink/tezt/lib/contract_path.ml @@ -16,6 +16,9 @@ let admin_path () = Base.(project_root // "etherlink/tezos_contracts/admin.tz") let withdrawal_abi_path () = Base.(project_root // "etherlink/tezos_contracts/withdrawal.abi") +let fa_withdrawal_abi_path () = + Base.(project_root // "etherlink/tezos_contracts/fa_withdrawal.abi") + let delayed_path () = Base.( project_root // "etherlink/tezos_contracts/delayed_transaction_bridge.tz") diff --git a/etherlink/tezt/lib/dune b/etherlink/tezt/lib/dune index 1b300fcf3486..2c4a0631ac5c 100644 --- a/etherlink/tezt/lib/dune +++ b/etherlink/tezt/lib/dune @@ -14,4 +14,5 @@ -open Tezt_wrapper -open Tezt_wrapper.Base -open Tezt_tezos_tezt_performance_regression - -open Tezt_tezos)) + -open Tezt_tezos + -open Tezt_tezos.Runnable.Syntax)) diff --git a/etherlink/tezt/lib/helpers.ml b/etherlink/tezt/lib/helpers.ml index 3bbc2b9e5887..8a4aac8b7eca 100644 --- a/etherlink/tezt/lib/helpers.ml +++ b/etherlink/tezt/lib/helpers.ml @@ -24,6 +24,8 @@ (* *) (*****************************************************************************) +open Sc_rollup_helpers + let evm_type = "or (or (pair bytes (ticket (pair nat (option bytes)))) bytes) bytes" @@ -296,3 +298,66 @@ let l1_timestamp client = JSON.( l1_header |-> "timestamp" |> as_string |> Tezos_base.Time.Protocol.of_notation_exn) + +let find_and_execute_withdrawal ~withdrawal_level ~commitment_period + ~challenge_window ~evm_node ~sc_rollup_node ~sc_rollup_address ~client = + (* Bake enough levels to have a commitment and cement it. *) + let* _ = + repeat + ((commitment_period * challenge_window) + 3) + (fun () -> + let* _ = next_rollup_node_level ~sc_rollup_node ~client in + unit) + in + + (* Construct and execute the outbox proof. *) + let find_outbox level = + let rec aux level' = + if level' > level + 10 then + Test.fail "Looked for an outbox for 10 levels, stopping the loop" + else + let* outbox = + Sc_rollup_node.RPC.call sc_rollup_node + @@ Sc_rollup_rpc.get_global_block_outbox ~outbox_level:level' () + in + if + JSON.is_null outbox + || (JSON.is_list outbox && JSON.as_list outbox = []) + then aux (level' + 1) + else return (JSON.as_list outbox |> List.length, level') + in + aux level + in + let* size, withdrawal_level = find_outbox withdrawal_level in + let execute_withdrawal withdrawal_level message_index = + let* outbox_proof = + Sc_rollup_node.RPC.call sc_rollup_node + @@ Sc_rollup_rpc.outbox_proof_simple + ~message_index + ~outbox_level:withdrawal_level + () + in + let Sc_rollup_rpc.{proof; commitment_hash} = + match outbox_proof with + | Some r -> r + | None -> Test.fail "No outbox proof found for the withdrawal" + in + let*! () = + Client.Sc_rollup.execute_outbox_message + ~hooks + ~burn_cap:(Tez.of_int 10) + ~rollup:sc_rollup_address + ~src:Constant.bootstrap1.alias + ~commitment_hash + ~proof + client + in + let* _ = next_evm_level ~evm_node ~sc_rollup_node ~client in + unit + in + let* () = + Lwt_list.iter_s + (fun message_index -> execute_withdrawal withdrawal_level message_index) + (List.init size Fun.id) + in + return withdrawal_level diff --git a/etherlink/tezt/lib/helpers.mli b/etherlink/tezt/lib/helpers.mli index 3ff3336c3ad9..36cd5970fad2 100644 --- a/etherlink/tezt/lib/helpers.mli +++ b/etherlink/tezt/lib/helpers.mli @@ -191,3 +191,17 @@ val default_bootstrap_account_balance : Wei.t (** Returns the timestamp of the L1 head block. *) val l1_timestamp : Client.t -> Tezos_base.Time.Protocol.t Lwt.t + +(** [find_and_execute_withdrawal ~withdrawal_level ~commitment_period ~challenge_window + ~evm_node ~sc_rollup_node ~sc_rollup_address ~client] bakes enough levels to have + a commitment and cement it, then constructs outbox proof + and executes the outbox message *) +val find_and_execute_withdrawal : + withdrawal_level:int -> + commitment_period:int -> + challenge_window:int -> + evm_node:Evm_node.t -> + sc_rollup_node:Sc_rollup_node.t -> + sc_rollup_address:string -> + client:Client.t -> + int Lwt.t diff --git a/etherlink/tezt/tests/evm_rollup.ml b/etherlink/tezt/tests/evm_rollup.ml index 93cca316965f..76220f3e5e74 100644 --- a/etherlink/tezt/tests/evm_rollup.ml +++ b/etherlink/tezt/tests/evm_rollup.ml @@ -2275,69 +2275,6 @@ let deposit ~amount_mutez ~bridge ~depositor ~receiver ~evm_node ~sc_rollup_node let* _ = next_evm_level ~evm_node ~sc_rollup_node ~client in unit -let find_and_execute_withdrawal ~withdrawal_level ~commitment_period - ~challenge_window ~evm_node ~sc_rollup_node ~sc_rollup_address ~client = - (* Bake enough levels to have a commitment and cement it. *) - let* _ = - repeat - ((commitment_period * challenge_window) + 3) - (fun () -> - let* _ = next_rollup_node_level ~sc_rollup_node ~client in - unit) - in - - (* Construct and execute the outbox proof. *) - let find_outbox level = - let rec aux level' = - if level' > level + 10 then - Test.fail "Looked for an outbox for 10 levels, stopping the loop" - else - let* outbox = - Sc_rollup_node.RPC.call sc_rollup_node - @@ Sc_rollup_rpc.get_global_block_outbox ~outbox_level:level' () - in - if - JSON.is_null outbox - || (JSON.is_list outbox && JSON.as_list outbox = []) - then aux (level' + 1) - else return (JSON.as_list outbox |> List.length, level') - in - aux level - in - let* size, withdrawal_level = find_outbox withdrawal_level in - let execute_withdrawal withdrawal_level message_index = - let* outbox_proof = - Sc_rollup_node.RPC.call sc_rollup_node - @@ Sc_rollup_rpc.outbox_proof_simple - ~message_index - ~outbox_level:withdrawal_level - () - in - let Sc_rollup_rpc.{proof; commitment_hash} = - match outbox_proof with - | Some r -> r - | None -> Test.fail "No outbox proof found for the withdrawal" - in - let*! () = - Client.Sc_rollup.execute_outbox_message - ~hooks - ~burn_cap:(Tez.of_int 10) - ~rollup:sc_rollup_address - ~src:Constant.bootstrap1.alias - ~commitment_hash - ~proof - client - in - let* _ = next_evm_level ~evm_node ~sc_rollup_node ~client in - unit - in - let* () = - Lwt_list.iter_s - (fun message_index -> execute_withdrawal withdrawal_level message_index) - (List.init size Fun.id) - in - return withdrawal_level - let call_withdraw ?expect_failure ~sender ~endpoint ~value ~evm_node ~sc_rollup_node ~client ~receiver () = let* () = diff --git a/etherlink/tezt/tests/evm_sequencer.ml b/etherlink/tezt/tests/evm_sequencer.ml index ebf3ff89f1e8..174cd853a8ab 100644 --- a/etherlink/tezt/tests/evm_sequencer.ml +++ b/etherlink/tezt/tests/evm_sequencer.ml @@ -167,7 +167,7 @@ let setup_l1_contracts ?(dictator = Constant.bootstrap2) client = let setup_sequencer ~mainnet_compat ?genesis_timestamp ?time_between_blocks ?max_blueprints_lag ?max_blueprints_ahead ?max_blueprints_catchup ?catchup_cooldown ?delayed_inbox_timeout ?delayed_inbox_min_levels - ?max_number_of_chunks + ?max_number_of_chunks ?commitment_period ?challenge_window ?(bootstrap_accounts = List.map (fun account -> account.Eth_account.address) @@ -179,12 +179,18 @@ let setup_sequencer ~mainnet_compat ?genesis_timestamp ?time_between_blocks ?(threshold_encryption = false) ?(wal_sqlite_journal_mode = true) ?(drop_duplicate_when_injection = true) ?history_mode ~enable_dal ?dal_slots protocol = - let* node, client = setup_l1 ?timestamp:genesis_timestamp protocol in + let* node, client = + setup_l1 + ?commitment_period + ?challenge_window + ?timestamp:genesis_timestamp + protocol + in let* l1_contracts = setup_l1_contracts client in let sc_rollup_node = Sc_rollup_node.create ~default_operator:Constant.bootstrap1.public_key_hash - Batcher + Operator node ~base_dir:(Client.base_dir client) ?history_mode @@ -424,9 +430,9 @@ let register_test ~mainnet_compat ?genesis_timestamp ?time_between_blocks ?max_number_of_chunks ?bootstrap_accounts ?sequencer ?sequencer_pool_address ~kernel ?da_fee ?minimum_base_fee_per_gas ?preimages_dir ?maximum_allowed_ticks ?maximum_gas_per_transaction - ?max_blueprint_lookahead_in_seconds ?enable_fa_bridge - ?(threshold_encryption = false) ?(uses = uses) ?(additional_uses = []) - ?history_mode ~enable_dal + ?max_blueprint_lookahead_in_seconds ?enable_fa_bridge ?commitment_period + ?challenge_window ?(threshold_encryption = false) ?(uses = uses) + ?(additional_uses = []) ?history_mode ~enable_dal ?(dal_slots = if enable_dal then Some [4] else None) body ~title ~tags protocols = let additional_uses = @@ -438,6 +444,8 @@ let register_test ~mainnet_compat ?genesis_timestamp ?time_between_blocks let* sequencer_setup = setup_sequencer ~mainnet_compat + ?commitment_period + ?challenge_window ?genesis_timestamp ?time_between_blocks ?max_blueprints_lag @@ -483,11 +491,14 @@ let register_both ?genesis_timestamp ?time_between_blocks ?max_blueprints_lag ?(kernels = Kernel.all) ?da_fee ?minimum_base_fee_per_gas ?preimages_dir ?maximum_allowed_ticks ?maximum_gas_per_transaction ?max_blueprint_lookahead_in_seconds ?enable_fa_bridge ?history_mode - ?additional_uses ~title ~tags body protocols = + ?commitment_period ?challenge_window ?additional_uses ~title ~tags body + protocols = let register ~kernel ~threshold_encryption ~title ~tags = let _, kernel_use = Kernel.to_uses_and_tags kernel in register_test ~mainnet_compat:Kernel.(mainnet_compat_kernel_config kernel) + ?commitment_period + ?challenge_window ?genesis_timestamp ?time_between_blocks ?max_blueprints_lag @@ -518,6 +529,8 @@ let register_both ?genesis_timestamp ?time_between_blocks ?max_blueprints_lag protocols ; register_test ~mainnet_compat:Kernel.(mainnet_compat_kernel_config kernel) + ?commitment_period + ?challenge_window ?genesis_timestamp ?time_between_blocks ?max_blueprints_lag @@ -1287,10 +1300,7 @@ let encode_data json codec = let hex = `Hex (Durable_storage_path.no_0x hex_string) in return (Hex.to_bytes hex) -let ticket_hash ticketer token_id = - let* ticketer_bytes = - encode_data (Ezjsonm.string ticketer) "alpha.contract" - in +let ticket_content token_id = let* content_bytes = encode_data (Ezjsonm.from_string @@ -1300,6 +1310,17 @@ let ticket_hash ticketer token_id = token_id)) "alpha.script.expr" in + return content_bytes + +let ticket_creator ticketer = + let* ticketer_bytes = + encode_data (Ezjsonm.string ticketer) "alpha.contract" + in + return ticketer_bytes + +let ticket_hash ticketer token_id = + let* ticketer_bytes = ticket_creator ticketer in + let* content_bytes = ticket_content token_id in let payload = Bytes.concat Bytes.empty [ticketer_bytes; content_bytes] in let payload_digest = Tezos_crypto.Hacl.Hash.Keccak_256.digest payload in return (payload_digest |> Hex.of_bytes |> Hex.show) @@ -1465,6 +1486,177 @@ let test_delayed_fa_deposit_is_ignored_if_feature_disabled = "The deposit should have been refused by sequencer, but balance is %R" ; unit +let call_fa_withdraw ?expect_failure ~sender ~endpoint ~evm_node ~ticket_owner + ~routing_info ~amount ~ticketer ~content () = + let* () = + Eth_cli.add_abi ~label:"fa_withdrawal" ~abi:(fa_withdrawal_abi_path ()) () + in + send_transaction + (Eth_cli.contract_send + ?expect_failure + ~source_private_key:sender.Eth_account.private_key + ~endpoint + ~abi_label:"fa_withdrawal" + ~address:"0xff00000000000000000000000000000000000002" + ~method_call: + (sf + {|withdraw("%s", "0x%s", %d, "0x%s", "0x%s")|} + ticket_owner + routing_info + amount + ticketer + content)) + evm_node + +let test_fa_withdrawal_is_included = + register_both + ~da_fee:Wei.one + ~tags: + [ + "evm"; + "sequencer"; + "delayed_outbox"; + "inclusion"; + "fa_withdrawal"; + "enabled"; + ] + ~title:"FA withdrawal is included" + ~enable_fa_bridge:true + ~commitment_period:5 + ~challenge_window:5 + ~kernels:[Kernel.Latest] + ~additional_uses:[Constant.octez_codec] + @@ fun { + client; + l1_contracts; + sc_rollup_address; + sc_rollup_node; + sequencer; + proxy; + _; + } + _protocol -> + (* 1. Deposit some tickets *) + let amount = 42 in + let depositor = Constant.bootstrap5 in + let receiver = Eth_account.bootstrap_accounts.(0).address in + + let* () = + send_fa_deposit_to_delayed_inbox + ~amount + ~l1_contracts + ~depositor + ~receiver + ~sc_rollup_node + ~sc_rollup_address + client + in + + let* () = + wait_for_delayed_inbox_add_tx_and_injected + ~sequencer + ~sc_rollup_node + ~client + in + let* () = bake_until_sync ~sc_rollup_node ~proxy ~sequencer ~client () in + let* () = check_delayed_inbox_is_empty ~sc_rollup_node in + + (* Check that deposit is successful *) + let* zero_ticket_hash = ticket_hash l1_contracts.ticket_router_tester 0 in + + let* ticket_balance_after_deposit = + ticket_balance + ~ticket_hash:zero_ticket_hash + ~account:receiver + (Either.Right sequencer) + in + Check.((amount = ticket_balance_after_deposit) int) + ~error_msg: + "After deposit we expect %L ticket balance in the sequencer, got %R" ; + + let* ticketer = ticket_creator l1_contracts.ticket_router_tester in + let* content = ticket_content 0 in + (* Withdrawing to the zero implicit account *) + let routing_info = + String.concat + "" + [ + "00000000000000000000000000000000000000000000"; + ticketer |> Hex.of_bytes |> Hex.show; + ] + in + + (* Initiate withdrawal *) + let* withdrawal_level = Client.level client in + let* _tx = + call_fa_withdraw + ~sender:Eth_account.bootstrap_accounts.(0) + ~endpoint:(Evm_node.endpoint sequencer) + ~evm_node:sequencer + ~ticket_owner:receiver + ~routing_info + ~amount + ~ticketer:(ticketer |> Hex.of_bytes |> Hex.show) + ~content:(content |> Hex.of_bytes |> Hex.show) + () + in + + let* () = bake_until_sync ~sequencer ~sc_rollup_node ~proxy ~client () in + + (* Check that tickets are gone *) + let* ticket_balance_after_withdraw = + ticket_balance + ~ticket_hash:zero_ticket_hash + ~account:receiver + (Either.Right sequencer) + in + Check.((0 = ticket_balance_after_withdraw) int) + ~error_msg: + "After withdrawal we expect %L ticket balance in the sequencer, got %R" ; + + (* Switch ticket tester contract to proxy mode *) + let* () = + Client.transfer + ~entrypoint:"set" + ~arg:(sf "Pair %S (Pair (Left Unit) 0)" depositor.public_key_hash) + ~amount:Tez.zero + ~giver:depositor.Account.public_key_hash + ~receiver:l1_contracts.ticket_router_tester + ~burn_cap:Tez.one + client + in + let* () = Client.bake_for_and_wait ~keys:[] client in + + (* Wait till the cementation and execute outbox message *) + let* _ = + find_and_execute_withdrawal + ~withdrawal_level + ~commitment_period:5 + ~challenge_window:5 + ~evm_node:proxy + ~sc_rollup_node + ~sc_rollup_address + ~client + in + + let* _ = next_rollup_node_level ~sc_rollup_node ~client in + + (* Check ticket balance for the zero account on L1 *) + let* l1_balance = + Client.ticket_balance + ~contract:"tz1Ke2h7sDdakHJQh8WX4Z372du1KChsksyU" + ~ticketer:l1_contracts.ticket_router_tester + ~content_type:"pair nat (option bytes)" + ~content:"Pair 0 None" + client + in + Check.(("42" = String.trim l1_balance) string) + ~error_msg: + "After outbox message execution we expect %L ticket balance for the \ + receiver, got %R" ; + + unit + let test_delayed_deposit_from_init_rollup_node = register_both ~da_fee:arb_da_fee_for_delayed_inbox @@ -4412,6 +4604,7 @@ let () = test_delayed_deposit_is_included protocols ; test_delayed_fa_deposit_is_included protocols ; test_delayed_fa_deposit_is_ignored_if_feature_disabled protocols ; + test_fa_withdrawal_is_included protocols ; test_largest_delayed_transfer_is_included protocols ; test_delayed_deposit_from_init_rollup_node protocols ; test_init_from_rollup_node_data_dir protocols ; diff --git a/manifest/product_etherlink.ml b/manifest/product_etherlink.ml index 0fb25fd74b55..2bbb13a48942 100644 --- a/manifest/product_etherlink.ml +++ b/manifest/product_etherlink.ml @@ -29,7 +29,7 @@ let tezt_etherlink = tezt_wrapper |> open_ |> open_ ~m:"Base"; tezt_performance_regression |> open_; octez_crypto; - tezt_tezos |> open_; + tezt_tezos |> open_ |> open_ ~m:"Runnable.Syntax"; ] ~release_status:Unreleased -- GitLab