From 0b37daa467a1f507439ebad42069eddc6920ad54 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Mon, 20 Oct 2025 13:16:28 +0200 Subject: [PATCH 1/7] Tezlink/Kernel: Introduce the function in bridge.rs to execute a Tezlink deposit Also rename old execute_deposit in execute_etherlink_deposit --- etherlink/kernel_latest/kernel/src/apply.rs | 4 +- etherlink/kernel_latest/kernel/src/bridge.rs | 79 ++++++++++++++++--- .../kernel_latest/kernel/src/sub_block.rs | 6 +- 3 files changed, 74 insertions(+), 15 deletions(-) diff --git a/etherlink/kernel_latest/kernel/src/apply.rs b/etherlink/kernel_latest/kernel/src/apply.rs index 6efad5fc60c2..793c91d5eded 100644 --- a/etherlink/kernel_latest/kernel/src/apply.rs +++ b/etherlink/kernel_latest/kernel/src/apply.rs @@ -44,7 +44,7 @@ use tezos_smart_rollup::outbox::{OutboxMessage, OutboxQueue}; use tezos_smart_rollup_host::path::{Path, RefPath}; use tezos_tracing::trace_kernel; -use crate::bridge::{execute_deposit, Deposit, DepositResult}; +use crate::bridge::{execute_etherlink_deposit, Deposit, DepositResult}; use crate::chains::EvmLimits; use crate::error::Error; use crate::fees::{tx_execution_gas_limit, FeeUpdates}; @@ -472,7 +472,7 @@ fn apply_deposit( let DepositResult { outcome: execution_outcome, estimated_ticks_used, - } = execute_deposit(host, deposit).map_err(|e| { + } = execute_etherlink_deposit(host, deposit).map_err(|e| { Error::InvalidRunTransaction(revm_etherlink::Error::Custom(e.to_string())) })?; diff --git a/etherlink/kernel_latest/kernel/src/bridge.rs b/etherlink/kernel_latest/kernel/src/bridge.rs index b89602e85851..c3d3700ddb51 100644 --- a/etherlink/kernel_latest/kernel/src/bridge.rs +++ b/etherlink/kernel_latest/kernel/src/bridge.rs @@ -18,15 +18,20 @@ use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpEncodable}; use sha3::{Digest, Keccak256}; use tezos_data_encoding::enc::BinWriter; use tezos_data_encoding::nom::NomReader; +use tezos_data_encoding::types::Narith; use tezos_ethereum::rlp_helpers::{decode_field_u256_le, decode_option_explicit}; +use tezos_ethereum::wei::mutez_from_wei; use tezos_ethereum::{ rlp_helpers::{decode_field, next}, wei::eth_from_mutez, }; -use tezos_evm_logging::{log, Level::Info}; +use tezos_evm_logging::{log, Level::Error, Level::Info}; use tezos_evm_runtime::runtime::Runtime; +use tezos_execution::account_storage::{TezlinkAccount, TezlinkImplicitAccount}; +use tezos_execution::context::Context; use tezos_protocol::contract::Contract; use tezos_smart_rollup::michelson::{ticket::FA2_1Ticket, MichelsonBytes}; +use tezos_tezlink::operation_result::TransferError; use tezos_tracing::trace_kernel; use crate::tick_model::constants::TICKS_FOR_DEPOSIT; @@ -52,6 +57,8 @@ pub enum BridgeError { InvalidDepositReceiver(Vec), #[error("Invalid RLP bytes: {0}")] RlpError(DecoderError), + #[error("Error when applying the Tezlink deposit: {0}")] + TezExecutionError(#[from] TransferError), } alloy_sol_types::sol! { @@ -95,6 +102,26 @@ impl DepositReceiver { } } } + + pub fn to_contract(&self) -> Result { + match self { + DepositReceiver::Ethereum(address) => { + // A Tezos contract's length is 22 bytes + // The first two bytes here represent: + // - 0u8: means that it's an implicit account + // - 0u8: means that it's a tz1 + // This code is temporary and should be replaced for the same + // reason as in https://linear.app/tezos/issue/L2-641 + let mut contract = vec![0u8, 0u8]; + contract.extend_from_slice(address.as_bytes()); + match Contract::nom_read_exact(&contract) { + Ok(contract) => Ok(contract), + Err(_) => Err(BridgeError::InvalidDepositReceiver(contract)), + } + } + DepositReceiver::Tezos(contract) => Ok(contract.clone()), + } + } } impl rlp::Encodable for DepositReceiver { @@ -343,16 +370,16 @@ impl Decodable for Deposit { } } -pub struct DepositResult { - pub outcome: ExecutionOutcome, +pub struct DepositResult { + pub outcome: Outcome, pub estimated_ticks_used: u64, } #[trace_kernel] -pub fn execute_deposit( +pub fn execute_etherlink_deposit( host: &mut Host, deposit: &Deposit, -) -> Result { +) -> Result, BridgeError> { // We should be able to obtain an account for arbitrary H160 address // otherwise it is a fatal error. let receiver = deposit.receiver.to_h160()?; @@ -368,7 +395,7 @@ pub fn execute_deposit( output: Output::Call(Bytes::from_static(&[1u8])), }, Err(e) => { - log!(host, Info, "Deposit failed because of {}", e); + log!(host, Info, "Deposit failed because of {e}"); ExecutionResult::Revert { gas_used: DEPOSIT_EXECUTION_GAS_COST, output: Bytes::from_static(&[0u8]), @@ -387,6 +414,35 @@ pub fn execute_deposit( }) } +pub fn _execute_tezlink_deposit( + host: &mut Host, + context: &Context, + deposit: &Deposit, +) -> Result, BridgeError> { + // We should be able to obtain an account for arbitrary H160 address + // otherwise it is a fatal error. + let contract = deposit.receiver.to_contract()?; + let to_account = TezlinkImplicitAccount::from_contract(context, &contract) + .map_err(|_| TransferError::FailedToFetchDestinationAccount)?; + let balance = to_account + .balance(host) + .map_err(|_| TransferError::FailedToFetchDestinationBalance)?; + let amount = mutez_from_wei(deposit.amount) + .map_err(|_| TransferError::FailedToApplyBalanceChanges)?; + let outcome = match to_account.set_balance(host, &Narith(balance.0 + amount)) { + Ok(()) => true, + Err(e) => { + log!(host, Error, "Deposit failed because of {}", e); + false + } + }; + + Ok(DepositResult { + outcome, + estimated_ticks_used: TICKS_FOR_DEPOSIT, + }) +} + #[cfg(test)] mod tests { use alloy_sol_types::SolEvent; @@ -406,7 +462,7 @@ mod tests { DepositInfo, DepositReceiver, DepositResult, DEPOSIT_EVENT_TOPIC, }; - use super::{execute_deposit, Deposit}; + use super::{execute_etherlink_deposit, Deposit}; mod events { alloy_sol_types::sol! { @@ -607,7 +663,8 @@ mod tests { let deposit = dummy_deposit(); - let DepositResult { outcome, .. } = execute_deposit(&mut host, &deposit).unwrap(); + let DepositResult { outcome, .. } = + execute_etherlink_deposit(&mut host, &deposit).unwrap(); let logs = outcome.result.logs(); assert!(outcome.result.is_success()); assert_eq!(logs.len(), 1); @@ -629,10 +686,12 @@ mod tests { let mut deposit = dummy_deposit(); deposit.amount = U256::MAX; - let DepositResult { outcome, .. } = execute_deposit(&mut host, &deposit).unwrap(); + let DepositResult { outcome, .. } = + execute_etherlink_deposit(&mut host, &deposit).unwrap(); assert!(outcome.result.is_success()); - let DepositResult { outcome, .. } = execute_deposit(&mut host, &deposit).unwrap(); + let DepositResult { outcome, .. } = + execute_etherlink_deposit(&mut host, &deposit).unwrap(); assert!(!outcome.result.is_success()); assert!(outcome.result.logs().is_empty()); } diff --git a/etherlink/kernel_latest/kernel/src/sub_block.rs b/etherlink/kernel_latest/kernel/src/sub_block.rs index 51470c8a57df..75a8c3adc4bf 100644 --- a/etherlink/kernel_latest/kernel/src/sub_block.rs +++ b/etherlink/kernel_latest/kernel/src/sub_block.rs @@ -6,7 +6,7 @@ use crate::{ apply::{pure_fa_deposit, revm_run_transaction}, block_storage, - bridge::{execute_deposit, DepositResult}, + bridge::{execute_etherlink_deposit, DepositResult}, chains::{ChainConfigTrait, EvmChainConfig, ETHERLINK_SAFE_STORAGE_ROOT_PATH}, configuration::fetch_pure_evm_config, error::{Error, StorageError, TransferError}, @@ -468,8 +468,8 @@ pub fn handle_run_transaction( } } TransactionContent::Deposit(deposit) => { - let DepositResult { outcome, .. } = execute_deposit(&mut safe_host, deposit) - .map_err(|e| { + let DepositResult { outcome, .. } = + execute_etherlink_deposit(&mut safe_host, deposit).map_err(|e| { Error::InvalidRunTransaction(revm_etherlink::Error::Custom( e.to_string(), )) -- GitLab From 312cbcefa1989070153bcd093de2f1ebfba71c3c Mon Sep 17 00:00:00 2001 From: Arnaud Date: Tue, 25 Nov 2025 13:58:26 +0100 Subject: [PATCH 2/7] Tezlink/Kernel: Introduce a new TezlinkOperation enum This new enum is similar to the Transaction one we can found in transaction.rs. The goal of this new enum is to have a deposit case that will be handled later. --- etherlink/kernel_latest/kernel/src/chains.rs | 89 +++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/etherlink/kernel_latest/kernel/src/chains.rs b/etherlink/kernel_latest/kernel/src/chains.rs index a05431b5b0b5..a05f5bf8648c 100644 --- a/etherlink/kernel_latest/kernel/src/chains.rs +++ b/etherlink/kernel_latest/kernel/src/chains.rs @@ -10,6 +10,7 @@ use crate::{ read_current_blueprint_header, BlueprintHeader, DelayedTransactionFetchingResult, EVMBlockHeader, TezBlockHeader, }, + bridge::Deposit, delayed_inbox::DelayedInbox, error, fees::MINIMUM_BASE_FEE_PER_GAS, @@ -23,13 +24,17 @@ use anyhow::Context; use primitive_types::{H160, H256, U256}; use revm::primitives::hardfork::SpecId; use revm_etherlink::inspectors::TracerInput; -use rlp::{Decodable, Encodable}; +use rlp::{Decodable, DecoderError, Encodable}; use std::{ collections::VecDeque, fmt::{Debug, Display}, }; use tezos_crypto_rs::hash::ChainId; use tezos_data_encoding::{enc::BinWriter, nom::NomReader}; +use tezos_ethereum::{ + rlp_helpers::{decode_field, decode_tx_hash, next}, + transaction::TransactionHash, +}; use tezos_evm_logging::{log, Level::*}; use tezos_evm_runtime::runtime::Runtime; use tezos_execution::{context, mir_ctx::BlockCtx}; @@ -151,6 +156,88 @@ impl TransactionsTrait for crate::transaction::Transactions { } } +const TEZOS_OP_TAG: u8 = 1; +const DEPOSIT_OP_TAG: u8 = 2; + +#[derive(Debug)] +pub struct TezlinkOperation { + pub tx_hash: TransactionHash, + pub content: TezlinkContent, +} + +impl Encodable for TezlinkOperation { + fn rlp_append(&self, stream: &mut rlp::RlpStream) { + stream.begin_list(2); + stream.append_iter(self.tx_hash); + stream.append(&self.content); + } +} + +impl Decodable for TezlinkOperation { + fn decode(decoder: &rlp::Rlp) -> Result { + if !decoder.is_list() { + return Err(DecoderError::RlpExpectedToBeList); + } + if decoder.item_count()? != 2 { + return Err(DecoderError::RlpIncorrectListLen); + } + let mut it = decoder.iter(); + let tx_hash: TransactionHash = decode_tx_hash(next(&mut it)?)?; + let content: TezlinkContent = + decode_field(&next(&mut it)?, "Transaction content")?; + Ok(TezlinkOperation { tx_hash, content }) + } +} + +#[derive(Debug)] +pub enum TezlinkContent { + Tezos(Operation), + Deposit(Deposit), +} + +impl Encodable for TezlinkContent { + fn rlp_append(&self, stream: &mut rlp::RlpStream) { + stream.begin_list(2); + match &self { + Self::Tezos(tez) => { + stream.append(&TEZOS_OP_TAG); + // We don't want the kernel to panic if there's an error + // and we can't print a log as we don't have access to + // the host. So we just ignore the result. + let _ = tez.rlp_append(stream); + } + Self::Deposit(dep) => { + stream.append(&DEPOSIT_OP_TAG); + dep.rlp_append(stream) + } + } + } +} + +impl Decodable for TezlinkContent { + fn decode(decoder: &rlp::Rlp) -> Result { + if !decoder.is_list() { + return Err(DecoderError::RlpExpectedToBeList); + } + if decoder.item_count()? != 2 { + return Err(DecoderError::RlpIncorrectListLen); + } + let tag: u8 = decoder.at(0)?.as_val()?; + let tx = decoder.at(1)?; + match tag { + DEPOSIT_OP_TAG => { + let deposit = Deposit::decode(&tx)?; + Ok(Self::Deposit(deposit)) + } + TEZOS_OP_TAG => { + let eth = Operation::decode(&tx)?; + Ok(Self::Tezos(eth)) + } + _ => Err(DecoderError::Custom("Unknown operation tag.")), + } + } +} + #[derive(Debug)] pub struct TezTransactions(pub Vec); -- GitLab From d3ba6e1f043350964b4ebd3d0f40012db9530c40 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Mon, 20 Oct 2025 13:32:12 +0200 Subject: [PATCH 3/7] Tezlink/Kernel: Use the new enum that has deposit instead of just Operation The deposit execution will be handled later --- etherlink/kernel_latest/kernel/src/block.rs | 10 ++ etherlink/kernel_latest/kernel/src/chains.rs | 119 ++++++++++--------- 2 files changed, 74 insertions(+), 55 deletions(-) diff --git a/etherlink/kernel_latest/kernel/src/block.rs b/etherlink/kernel_latest/kernel/src/block.rs index 8213c5ef798d..8ef514f4a261 100644 --- a/etherlink/kernel_latest/kernel/src/block.rs +++ b/etherlink/kernel_latest/kernel/src/block.rs @@ -590,6 +590,8 @@ mod tests { use crate::blueprint::Blueprint; use crate::blueprint_storage::store_inbox_blueprint; use crate::blueprint_storage::store_inbox_blueprint_by_number; + use crate::chains::TezlinkContent; + use crate::chains::TezlinkOperation; use crate::chains::{ EvmChainConfig, ExperimentalFeatures, MichelsonChainConfig, TezTransactions, TEZLINK_SAFE_STORAGE_ROOT_PATH, @@ -800,6 +802,14 @@ mod tests { operations: Vec, timestamp: Timestamp, ) -> Blueprint { + let operations = operations + .into_iter() + .map(|op| { + let tx_hash = op.hash().unwrap().0 .0; + let content = TezlinkContent::Tezos(op); + TezlinkOperation { tx_hash, content } + }) + .collect(); Blueprint { transactions: TezTransactions(operations), timestamp, diff --git a/etherlink/kernel_latest/kernel/src/chains.rs b/etherlink/kernel_latest/kernel/src/chains.rs index a05f5bf8648c..913f6f40ba9e 100644 --- a/etherlink/kernel_latest/kernel/src/chains.rs +++ b/etherlink/kernel_latest/kernel/src/chains.rs @@ -125,7 +125,7 @@ pub struct TezBlockInProgress { timestamp: Timestamp, previous_hash: H256, applied: Vec, - operations: VecDeque, + operations: VecDeque, } impl BlockInProgressTrait for TezBlockInProgress { @@ -239,7 +239,7 @@ impl Decodable for TezlinkContent { } #[derive(Debug)] -pub struct TezTransactions(pub Vec); +pub struct TezTransactions(pub Vec); impl TransactionsTrait for TezTransactions { fn extend(&mut self, other: Self) { @@ -259,10 +259,7 @@ impl Encodable for TezTransactions { let Self(operations) = self; stream.begin_list(operations.len()); for op in operations { - // We don't want the kernel to panic if there's an error - // and we can't print a log as we don't have access to - // the host. So we just ignore the result. - let _ = op.rlp_append(stream); + op.rlp_append(stream); } } } @@ -274,8 +271,8 @@ impl Decodable for TezTransactions { } let operations = decoder .iter() - .map(|rlp| Operation::decode(&rlp)) - .collect::, rlp::DecoderError>>()?; + .map(|rlp| TezlinkOperation::decode(&rlp)) + .collect::, rlp::DecoderError>>()?; Ok(TezTransactions(operations)) } } @@ -551,11 +548,17 @@ impl ChainConfigTrait for MichelsonChainConfig { let operations = bytes .iter() .map(|bytes| { - Operation::nom_read_exact(bytes).map_err(|decode_error| { - error::Error::NomReadError(format!("{decode_error:?}")) + let operation = + Operation::nom_read_exact(bytes).map_err(|decode_error| { + error::Error::NomReadError(format!("{decode_error:?}")) + })?; + let tx_hash = operation.hash()?.0 .0; + Ok(TezlinkOperation { + tx_hash, + content: TezlinkContent::Tezos(operation), }) }) - .collect::, error::Error>>()?; + .collect::, error::Error>>()?; Ok(TezTransactions(operations)) } @@ -603,51 +606,57 @@ impl ChainConfigTrait for MichelsonChainConfig { // Retrieve the next operation in the VecDequeue let operation = operations.pop_front().ok_or(error::Error::Reboot)?; - // Compute the hash of the operation - let hash = operation.hash()?; - - let skip_signature_check = false; - - let branch = operation.branch.clone(); - let signature = operation.signature.clone(); - - // Try to apply the operation with the tezos_execution crate, return a receipt - // on whether it failed or not - let processed_operations = match tezos_execution::validate_and_apply_operation( - host, - &context, - hash.clone(), - operation, - &block_ctx, - skip_signature_check, - ) { - Ok(receipt) => receipt, - Err(OperationError::Validation(err)) => { - log!( - host, - Error, - "Found an invalid operation, dropping it: {:?}", - err - ); - continue; + match operation.content { + TezlinkContent::Tezos(operation) => { + // Compute the hash of the operation + let hash = operation.hash()?; + + let skip_signature_check = false; + + let branch = operation.branch.clone(); + let signature = operation.signature.clone(); + + // Try to apply the operation with the tezos_execution crate, return a receipt + // on whether it failed or not + let processed_operations = + match tezos_execution::validate_and_apply_operation( + host, + &context, + hash.clone(), + operation, + &block_ctx, + skip_signature_check, + ) { + Ok(receipt) => receipt, + Err(OperationError::Validation(err)) => { + log!( + host, + Error, + "Found an invalid operation, dropping it: {:?}", + err + ); + continue; + } + Err(OperationError::RuntimeError(err)) => { + return Err(err.into()); + } + }; + + // Add the applied operation in the block in progress + let applied_operation = AppliedOperation { + hash, + branch, + op_and_receipt: OperationDataAndMetadata::OperationWithMetadata( + OperationBatchWithMetadata { + operations: processed_operations, + signature, + }, + ), + }; + applied.push(applied_operation); } - Err(OperationError::RuntimeError(err)) => { - return Err(err.into()); - } - }; - - // Add the applied operation in the block in progress - let applied_operation = AppliedOperation { - hash, - branch, - op_and_receipt: OperationDataAndMetadata::OperationWithMetadata( - OperationBatchWithMetadata { - operations: processed_operations, - signature, - }, - ), - }; - applied.push(applied_operation); + TezlinkContent::Deposit(_deposit) => (), + } } // Create a Tezos block from the block in progress -- GitLab From b73eba034a56934a56230debb9d72e541e6a63ff Mon Sep 17 00:00:00 2001 From: Arnaud Date: Mon, 20 Oct 2025 13:44:24 +0200 Subject: [PATCH 4/7] Tezlink/Kernel: Reuse already existing functions to retrieve deposit --- etherlink/kernel_latest/kernel/src/bridge.rs | 2 +- etherlink/kernel_latest/kernel/src/chains.rs | 57 ++++++++++++++++---- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/etherlink/kernel_latest/kernel/src/bridge.rs b/etherlink/kernel_latest/kernel/src/bridge.rs index c3d3700ddb51..33486912a343 100644 --- a/etherlink/kernel_latest/kernel/src/bridge.rs +++ b/etherlink/kernel_latest/kernel/src/bridge.rs @@ -414,7 +414,7 @@ pub fn execute_etherlink_deposit( }) } -pub fn _execute_tezlink_deposit( +pub fn execute_tezlink_deposit( host: &mut Host, context: &Context, deposit: &Deposit, diff --git a/etherlink/kernel_latest/kernel/src/chains.rs b/etherlink/kernel_latest/kernel/src/chains.rs index 913f6f40ba9e..2414b8a8ccd0 100644 --- a/etherlink/kernel_latest/kernel/src/chains.rs +++ b/etherlink/kernel_latest/kernel/src/chains.rs @@ -10,14 +10,14 @@ use crate::{ read_current_blueprint_header, BlueprintHeader, DelayedTransactionFetchingResult, EVMBlockHeader, TezBlockHeader, }, - bridge::Deposit, + bridge::{execute_tezlink_deposit, Deposit}, delayed_inbox::DelayedInbox, error, fees::MINIMUM_BASE_FEE_PER_GAS, l2block::L2Block, simulation::start_simulation_mode, tick_model::constants::MAXIMUM_GAS_LIMIT, - transaction::Transactions::EthTxs, + transaction::{TransactionContent, Transactions::EthTxs}, CHAIN_ID, }; use anyhow::Context; @@ -530,15 +530,46 @@ impl ChainConfigTrait for MichelsonChainConfig { } fn fetch_hashes_from_delayed_inbox( - _host: &mut impl Runtime, - _delayed_hashes: Vec, - _delayed_inbox: &mut DelayedInbox, + host: &mut impl Runtime, + delayed_hashes: Vec, + delayed_inbox: &mut DelayedInbox, current_blueprint_size: usize, ) -> anyhow::Result<(DelayedTransactionFetchingResult, usize)> { + // By reusing 'fetch_hashes_from_delayed_inbox', Tezlink don't have to implement + // the logic to retrieve delayed_inbox items. + // + // However, it still needs to do the conversion as the returned items are EthTxs + let (delayed, total_size) = + crate::blueprint_storage::fetch_hashes_from_delayed_inbox( + host, + delayed_hashes, + delayed_inbox, + current_blueprint_size, + )?; Ok(( - DelayedTransactionFetchingResult::Ok(TezTransactions(vec![])), - current_blueprint_size, + match delayed { + DelayedTransactionFetchingResult::Ok(EthTxs(txs)) => { + let mut ops = vec![]; + for tx in txs.into_iter() { + if let TransactionContent::Deposit(deposit) = tx.content { + let operation = TezlinkOperation { + tx_hash: tx.tx_hash, + content: TezlinkContent::Deposit(deposit), + }; + ops.push(operation) + } + } + DelayedTransactionFetchingResult::Ok(TezTransactions(ops)) + } + DelayedTransactionFetchingResult::BlueprintTooLarge => { + DelayedTransactionFetchingResult::BlueprintTooLarge + } + DelayedTransactionFetchingResult::DelayedHashMissing(hash) => { + DelayedTransactionFetchingResult::DelayedHashMissing(hash) + } + }, + total_size, )) } @@ -601,6 +632,8 @@ impl ChainConfigTrait for MichelsonChainConfig { now: ×tamp, chain_id: &self.chain_id, }; + + let mut included_delayed_transactions = vec![]; // Compute operations that are in the block in progress while !operations.is_empty() { // Retrieve the next operation in the VecDequeue @@ -655,8 +688,12 @@ impl ChainConfigTrait for MichelsonChainConfig { }; applied.push(applied_operation); } - TezlinkContent::Deposit(_deposit) => (), - } + TezlinkContent::Deposit(deposit) => { + log!(host, Debug, "Execute Tezlink deposit: {deposit:?}"); + execute_tezlink_deposit(host, &context, &deposit)?; + included_delayed_transactions.push(operation.tx_hash); + } + }; } // Create a Tezos block from the block in progress @@ -666,7 +703,7 @@ impl ChainConfigTrait for MichelsonChainConfig { crate::block_storage::store_current(host, &root, &new_block) .context("Failed to store the current block")?; Ok(BlockComputationResult::Finished { - included_delayed_transactions: vec![], + included_delayed_transactions, block: new_block, }) } -- GitLab From 581f996c3ac5245ee833587276288d3724feb1db Mon Sep 17 00:00:00 2001 From: Arnaud Date: Wed, 22 Oct 2025 11:05:09 +0200 Subject: [PATCH 5/7] Etherlink/Tezt: Move the wait_for_event function from evm_sequencer.ml to test_helpers This function will be used for the deposit test on Tezlink --- etherlink/tezt/lib/test_helpers.ml | 21 +++++++++++++++++++++ etherlink/tezt/lib/test_helpers.mli | 10 ++++++++++ etherlink/tezt/tests/evm_sequencer.ml | 20 -------------------- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/etherlink/tezt/lib/test_helpers.ml b/etherlink/tezt/lib/test_helpers.ml index bdb14fab0985..50245df9d479 100644 --- a/etherlink/tezt/lib/test_helpers.ml +++ b/etherlink/tezt/lib/test_helpers.ml @@ -368,6 +368,27 @@ let wait_for_application ?(time_between_blocks = 5.) ?(max_blocks = 10) in Lwt.pick [application_result; loop 0] +let wait_for_event ?(timeout = 30.) ?(levels = 10) event_watcher ~sequencer + ~sc_rollup_node ~client ~error_msg = + let open Rpc.Syntax in + let event_value = ref None in + let _ = + let* return_value = event_watcher in + event_value := Some return_value ; + unit + in + let rec rollup_node_loop n = + if n = 0 then Test.fail error_msg + else + let* _ = Rollup.next_rollup_node_level ~sc_rollup_node ~client in + let*@ _ = produce_block sequencer in + if Option.is_some !event_value then unit else rollup_node_loop (n - 1) + in + let* () = Lwt.pick [rollup_node_loop levels; Lwt_unix.sleep timeout] in + match !event_value with + | Some value -> return value + | None -> Test.fail ~loc:__LOC__ "Waiting for event failed" + let batch_n_transactions ?websocket ~evm_node txs = let requests = List.map diff --git a/etherlink/tezt/lib/test_helpers.mli b/etherlink/tezt/lib/test_helpers.mli index 8bc78106234f..83011b77f291 100644 --- a/etherlink/tezt/lib/test_helpers.mli +++ b/etherlink/tezt/lib/test_helpers.mli @@ -215,6 +215,16 @@ val wait_for_application : (unit -> 'b Lwt.t) -> 'b Lwt.t +val wait_for_event : + ?timeout:float -> + ?levels:int -> + 'a Lwt.t -> + sequencer:Evm_node.t -> + sc_rollup_node:Sc_rollup_node.t -> + client:Client.t -> + error_msg:(unit Lwt.t, Format.formatter, unit, 'b) format4 -> + 'a Lwt.t + (** [batch_n_transactions ~evm_node raw_transactions] batches [raw_transactions] to the [evm_node] and returns the requests and transaction hashes. *) val batch_n_transactions : diff --git a/etherlink/tezt/tests/evm_sequencer.ml b/etherlink/tezt/tests/evm_sequencer.ml index ad3b687f68f3..f3e4b9079a3b 100644 --- a/etherlink/tezt/tests/evm_sequencer.ml +++ b/etherlink/tezt/tests/evm_sequencer.ml @@ -1476,26 +1476,6 @@ let test_rpc_produceBlock = ~error_msg:"Expected new block number to be %L, but got: %R" ; unit -let wait_for_event ?(timeout = 30.) ?(levels = 10) event_watcher ~sequencer - ~sc_rollup_node ~client ~error_msg = - let event_value = ref None in - let _ = - let* return_value = event_watcher in - event_value := Some return_value ; - unit - in - let rec rollup_node_loop n = - if n = 0 then Test.fail error_msg - else - let* _ = Rollup.next_rollup_node_level ~sc_rollup_node ~client in - let*@ _ = produce_block sequencer in - if Option.is_some !event_value then unit else rollup_node_loop (n - 1) - in - let* () = Lwt.pick [rollup_node_loop levels; Lwt_unix.sleep timeout] in - match !event_value with - | Some value -> return value - | None -> Test.fail ~loc:__LOC__ "Waiting for event failed" - let wait_for_delayed_inbox_add_tx_and_injected ~sequencer ~sc_rollup_node ~client = let event_watcher = -- GitLab From 3586629c42c1bf8377721699a873e6c0b846a552 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Wed, 22 Oct 2025 11:09:25 +0200 Subject: [PATCH 6/7] Etherlink/Tezt: Create a module for handling the delayed inbox This module works for both Tezlink and Etherlink. Tezt_etherlink was moved because with the new DelayedInbox modules it now requires 'evm_node_lib_dev_encoding' --- etherlink/tezt/lib/delayed_inbox.ml | 121 ++++++++++++++++++++++ etherlink/tezt/lib/dune | 1 + etherlink/tezt/tests/evm_sequencer.ml | 139 +++----------------------- manifest/product_etherlink.ml | 35 +++---- opam/tezt-etherlink.opam | 2 +- 5 files changed, 154 insertions(+), 144 deletions(-) create mode 100644 etherlink/tezt/lib/delayed_inbox.ml diff --git a/etherlink/tezt/lib/delayed_inbox.ml b/etherlink/tezt/lib/delayed_inbox.ml new file mode 100644 index 000000000000..d91216e6a5fc --- /dev/null +++ b/etherlink/tezt/lib/delayed_inbox.ml @@ -0,0 +1,121 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* SPDX-FileCopyrightText: 2025 Nomadic Labs *) +(* SPDX-FileCopyrightText: 2025 Trilitech *) +(* SPDX-FileCopyrightText: 2025 Functori *) +(* *) +(*****************************************************************************) + +open Rpc.Syntax +open Test_helpers +open Evm_node_lib_dev_encoding.Rlp + +type deposit_info = {receiver : string; chain_id : int option} + +let encode_option encode_some = function + | None -> List [] + | Some v -> + let value = encode_some v in + List [Value value] + +let encode_deposit_info deposit_info = + let receiver = Hex.to_bytes (`Hex (remove_0x deposit_info.receiver)) in + let bytes = + encode + (List [Value receiver; encode_option encode_int deposit_info.chain_id]) + in + let (`Hex str) = Hex.of_bytes bytes in + add_0x ("01" ^ str) + +type endpoint = Sc_rollup_node of Sc_rollup_node.t | Evm_node of Evm_node.t + +let subkeys path = function + | Sc_rollup_node sc_rollup_node -> + Sc_rollup_node.RPC.call sc_rollup_node + @@ Sc_rollup_rpc.get_global_block_durable_state_value + ~pvm_kind:"wasm_2_0_0" + ~operation:Sc_rollup_rpc.Subkeys + ~key:path + () + | Evm_node evm_node -> + let*@! list = Rpc.state_subkeys evm_node path in + return list + +let content = subkeys Durable_storage_path.delayed_inbox + +let data endpoint hash = + let path = sf "%s/%s/data" Durable_storage_path.delayed_inbox hash in + match endpoint with + | Sc_rollup_node sc_rollup_node -> + Sc_rollup_node.RPC.call sc_rollup_node + @@ Sc_rollup_rpc.get_global_block_durable_state_value + ~pvm_kind:"wasm_2_0_0" + ~operation:Sc_rollup_rpc.Value + ~key:path + () + | Evm_node evm_node -> + let*@ res = Rpc.state_value evm_node path in + return res + +let assert_mem endpoint hash = + let* delayed_transactions_hashes = content endpoint in + Check.(list_mem string hash delayed_transactions_hashes) + ~error_msg:"hash %L should be present in the delayed inbox %R" ; + unit + +let assert_empty endpoint = + let* delayed_transactions_hashes = content endpoint in + Check.is_true + (List.length delayed_transactions_hashes <= 1) + ~error_msg:"Expected empty delayed inbox" ; + unit + +let size endpoint = + let* delayed_transactions_hashes = content endpoint in + let size = List.length delayed_transactions_hashes - 1 in + if size < 0 then + (* /meta is removed, if the delayed inbox was empty it would be (-1) here. *) + return 0 + else return size + +let wait_for_delayed_inbox_add_tx_and_injected ~sequencer ~sc_rollup_node + ~client = + let event_watcher = + let added = Evm_node.wait_for_evm_event New_delayed_transaction sequencer in + let injected = Evm_node.wait_for_block_producer_tx_injected sequencer in + let* (_transaction_kind, added_hash), injected_hash = + Lwt.both added injected + in + Check.((added_hash = injected_hash) string) + ~error_msg:"Injected hash %R is not the expected one %L" ; + Lwt.return_unit + in + wait_for_event + event_watcher + ~sequencer + ~sc_rollup_node + ~client + ~error_msg: + "Timed out while waiting for transaction to be added to the delayed \ + inbox and injected" + +let send_deposit_to_delayed_inbox ?(rlp = false) ~amount ~bridge ~depositor + ~deposit_info ~sc_rollup_node ~sc_rollup_address client = + (* Implements optional types decoding as in the RLP library from the + kernel's library. *) + let infos = + if rlp then encode_deposit_info deposit_info else deposit_info.receiver + in + let* () = + Client.transfer + ~entrypoint:"deposit" + ~arg:(sf "Pair %S %s" sc_rollup_address infos) + ~amount + ~giver:depositor.Account.public_key_hash + ~receiver:bridge + ~burn_cap:Tez.one + client + in + let* _ = Rollup.next_rollup_node_level ~sc_rollup_node ~client in + unit diff --git a/etherlink/tezt/lib/dune b/etherlink/tezt/lib/dune index 1c0152665ec2..02b12111137b 100644 --- a/etherlink/tezt/lib/dune +++ b/etherlink/tezt/lib/dune @@ -5,6 +5,7 @@ (name tezt_etherlink) (package tezt-etherlink) (libraries + octez-evm-node-libs.evm_node_lib_dev_encoding octez-libs.tezt-wrapper tezt-tezos.tezt-performance-regression octez-libs.crypto diff --git a/etherlink/tezt/tests/evm_sequencer.ml b/etherlink/tezt/tests/evm_sequencer.ml index f3e4b9079a3b..da15908ff298 100644 --- a/etherlink/tezt/tests/evm_sequencer.ml +++ b/etherlink/tezt/tests/evm_sequencer.ml @@ -25,6 +25,7 @@ open Sc_rollup_helpers open Rpc.Syntax open Contract_path open Transaction +open Delayed_inbox module Sequencer_rpc = struct let get_blueprint sequencer number = @@ -47,59 +48,6 @@ end open Test_helpers open Setup -module Delayed_inbox = struct - type endpoint = Sc_rollup_node of Sc_rollup_node.t | Evm_node of Evm_node.t - - let subkeys path = function - | Sc_rollup_node sc_rollup_node -> - Sc_rollup_node.RPC.call sc_rollup_node - @@ Sc_rollup_rpc.get_global_block_durable_state_value - ~pvm_kind:"wasm_2_0_0" - ~operation:Sc_rollup_rpc.Subkeys - ~key:path - () - | Evm_node evm_node -> - let*@! list = Rpc.state_subkeys evm_node path in - return list - - let content = subkeys Durable_storage_path.delayed_inbox - - let data endpoint hash = - let path = sf "%s/%s/data" Durable_storage_path.delayed_inbox hash in - match endpoint with - | Sc_rollup_node sc_rollup_node -> - Sc_rollup_node.RPC.call sc_rollup_node - @@ Sc_rollup_rpc.get_global_block_durable_state_value - ~pvm_kind:"wasm_2_0_0" - ~operation:Sc_rollup_rpc.Value - ~key:path - () - | Evm_node evm_node -> - let*@ res = Rpc.state_value evm_node path in - return res - - let assert_mem endpoint hash = - let* delayed_transactions_hashes = content endpoint in - Check.(list_mem string hash delayed_transactions_hashes) - ~error_msg:"hash %L should be present in the delayed inbox %R" ; - unit - - let assert_empty endpoint = - let* delayed_transactions_hashes = content endpoint in - Check.is_true - (List.length delayed_transactions_hashes <= 1) - ~error_msg:"Expected empty delayed inbox" ; - unit - - let size endpoint = - let* delayed_transactions_hashes = content endpoint in - let size = List.length delayed_transactions_hashes - 1 in - if size < 0 then - (* /meta is removed, if the delayed inbox was empty it would be (-1) here. *) - return 0 - else return size -end - let check_kernel_version ~evm_node ~equal expected = let*@ kernel_version = Rpc.tez_kernelVersion evm_node in if equal then @@ -141,46 +89,6 @@ let send_raw_transaction_to_delayed_inbox ?(wait_for_next_level = true) in Lwt.return expected_hash -type deposit_info = {receiver : string; chain_id : int option} - -let encode_option encode_some = - let open Evm_node_lib_dev_encoding.Rlp in - function - | None -> List [] - | Some v -> - let value = encode_some v in - List [Value value] - -let encode_deposit_info deposit_info = - let open Evm_node_lib_dev_encoding.Rlp in - let receiver = Hex.to_bytes (`Hex (remove_0x deposit_info.receiver)) in - let bytes = - encode - (List [Value receiver; encode_option encode_int deposit_info.chain_id]) - in - let (`Hex str) = Hex.of_bytes bytes in - add_0x ("01" ^ str) - -let send_deposit_to_delayed_inbox ?(rlp = false) ~amount ~l1_contracts - ~depositor ~deposit_info ~sc_rollup_node ~sc_rollup_address client = - (* Implements optional types decoding as in the RLP library from the - kernel's library. *) - let infos = - if rlp then encode_deposit_info deposit_info else deposit_info.receiver - in - let* () = - Client.transfer - ~entrypoint:"deposit" - ~arg:(sf "Pair %S %s" sc_rollup_address infos) - ~amount - ~giver:depositor.Account.public_key_hash - ~receiver:l1_contracts.bridge - ~burn_cap:Tez.one - client - in - let* _ = Rollup.next_rollup_node_level ~sc_rollup_node ~client in - unit - let send_fa_deposit_to_delayed_inbox ?(proxy = "") ~amount ~l1_contracts ~depositor ~receiver ~sc_rollup_node ~sc_rollup_address client = let* () = @@ -1426,7 +1334,7 @@ let test_send_deposit_to_delayed_inbox = let* () = send_deposit_to_delayed_inbox ~amount - ~l1_contracts + ~bridge:l1_contracts.bridge ~depositor ~deposit_info ~sc_rollup_node @@ -1476,27 +1384,6 @@ let test_rpc_produceBlock = ~error_msg:"Expected new block number to be %L, but got: %R" ; unit -let wait_for_delayed_inbox_add_tx_and_injected ~sequencer ~sc_rollup_node - ~client = - let event_watcher = - let added = Evm_node.wait_for_evm_event New_delayed_transaction sequencer in - let injected = Evm_node.wait_for_block_producer_tx_injected sequencer in - let* (_transaction_kind, added_hash), injected_hash = - Lwt.both added injected - in - Check.((added_hash = injected_hash) string) - ~error_msg:"Injected hash %R is not the expected one %L" ; - Lwt.return_unit - in - wait_for_event - event_watcher - ~sequencer - ~sc_rollup_node - ~client - ~error_msg: - "Timed out while waiting for transaction to be added to the delayed \ - inbox and injected" - let test_delayed_transfer_is_included = register_all ~__FILE__ @@ -1684,7 +1571,7 @@ let test_delayed_deposit_is_included = let* () = send_deposit_to_delayed_inbox ~amount - ~l1_contracts + ~bridge:l1_contracts.bridge ~depositor ~deposit_info ~sc_rollup_node @@ -1945,7 +1832,7 @@ let test_delayed_transaction_peeked = let* () = send_deposit_to_delayed_inbox ~amount:Tez.one - ~l1_contracts + ~bridge:l1_contracts.bridge ~depositor:Constant.bootstrap5 ~deposit_info ~sc_rollup_node @@ -2402,7 +2289,7 @@ let test_delayed_deposit_from_init_rollup_node = let* () = send_deposit_to_delayed_inbox ~amount - ~l1_contracts + ~bridge:l1_contracts.bridge ~depositor ~deposit_info ~sc_rollup_node @@ -2575,7 +2462,7 @@ let test_init_from_rollup_node_with_delayed_inbox = let* () = send_deposit_to_delayed_inbox ~amount:Tez.one - ~l1_contracts + ~bridge:l1_contracts.bridge ~depositor:Constant.bootstrap5 ~deposit_info ~sc_rollup_node @@ -3417,7 +3304,7 @@ let test_empty_block_on_upgrade = let* () = send_deposit_to_delayed_inbox ~amount:(Tez.of_int 1) - ~l1_contracts + ~bridge:l1_contracts.bridge ~depositor:Constant.bootstrap5 ~deposit_info ~sc_rollup_node @@ -3640,7 +3527,7 @@ let test_legacy_deposits_dispatched_after_kernel_upgrade = let* () = send_deposit_to_delayed_inbox ~amount:Tez.(of_int 16) - ~l1_contracts + ~bridge:l1_contracts.bridge ~depositor:Constant.bootstrap5 ~deposit_info ~sc_rollup_node @@ -6820,7 +6707,7 @@ let test_sequencer_dont_read_level_twice = let* () = send_deposit_to_delayed_inbox ~amount:Tez.(of_int 16) - ~l1_contracts + ~bridge:l1_contracts.bridge ~depositor:Constant.bootstrap5 ~deposit_info ~sc_rollup_node @@ -6908,7 +6795,7 @@ let test_outbox_size_limit_resilience ~slow = let* () = send_deposit_to_delayed_inbox ~amount - ~l1_contracts + ~bridge:l1_contracts.bridge ~depositor ~deposit_info ~sc_rollup_node @@ -8900,7 +8787,7 @@ let test_trace_transaction_calltracer_deposit = let* () = send_deposit_to_delayed_inbox ~amount - ~l1_contracts + ~bridge:l1_contracts.bridge ~depositor ~deposit_info ~sc_rollup_node @@ -9518,7 +9405,7 @@ let test_deposit_and_fast_withdraw = let deposit_info = {receiver = receiver.address; chain_id = None} in let* () = send_deposit_to_delayed_inbox - ~l1_contracts + ~bridge:l1_contracts.bridge ~amount:deposit_amount ~sc_rollup_address ~sc_rollup_node @@ -13022,7 +12909,7 @@ let test_deposit_event = let* () = send_deposit_to_delayed_inbox ~amount:Tez.one - ~l1_contracts + ~bridge:l1_contracts.bridge ~depositor:Constant.bootstrap5 ~deposit_info ~sc_rollup_node diff --git a/manifest/product_etherlink.ml b/manifest/product_etherlink.ml index faa9e1280e00..09660d52b545 100644 --- a/manifest/product_etherlink.ml +++ b/manifest/product_etherlink.ml @@ -23,23 +23,6 @@ include Product (struct let source = ["etherlink"; "src"] @ Product_websocket.product_source end) -let tezt_etherlink = - private_lib - "tezt_etherlink" - ~path:"etherlink/tezt/lib" - ~opam:"tezt-etherlink" - ~bisect_ppx:No - ~deps: - [ - tezt_wrapper |> open_ |> open_ ~m:"Base"; - tezt_performance_regression |> open_; - octez_crypto; - tezt_tezos |> open_ |> open_ ~m:"Runnable.Syntax"; - tezt_cloud |> open_; - octez_test_helpers |> open_; - ] - ~release_status:Unreleased - (* Container of the registered sublibraries of [octez-evm-node] *) let registered_octez_evm_node_libs = Sub_lib.make_container () @@ -248,6 +231,24 @@ let evm_node_lib_dev_encoding = uuidm; ] +let tezt_etherlink = + private_lib + "tezt_etherlink" + ~path:"etherlink/tezt/lib" + ~opam:"tezt-etherlink" + ~bisect_ppx:No + ~deps: + [ + evm_node_lib_dev_encoding; + tezt_wrapper |> open_ |> open_ ~m:"Base"; + tezt_performance_regression |> open_; + octez_crypto; + tezt_tezos |> open_ |> open_ ~m:"Runnable.Syntax"; + tezt_cloud |> open_; + octez_test_helpers |> open_; + ] + ~release_status:Unreleased + let evm_node_lib_dev_tezlink = let tezlink_target_proto = List.find (fun proto -> Protocol.short_hash proto = "PtSeouLo") Protocol.all diff --git a/opam/tezt-etherlink.opam b/opam/tezt-etherlink.opam index a865c327174c..c3b7e00cf817 100644 --- a/opam/tezt-etherlink.opam +++ b/opam/tezt-etherlink.opam @@ -10,6 +10,7 @@ license: "MIT" depends: [ "dune" { >= "3.11.1" } "ocaml" { >= "4.14" } + "octez-evm-node-libs" { = version } "octez-libs" "tezt-tezos" "tezt-cloud" @@ -21,7 +22,6 @@ depends: [ "qcheck-alcotest" { with-test & >= "0.20" } "octez-alcotezt" {with-test} "octez-l2-libs" {with-test} - "octez-evm-node-libs" { with-test & = version } "tezos-protocol-alpha" {with-test} ] build: [ -- GitLab From 2286343d5a4fa298b6b9835ce1cbf5f5242fed11 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Fri, 24 Oct 2025 15:12:13 +0200 Subject: [PATCH 7/7] Tezlink/Tezt: Add tezlink test for deposit Adapt Tezlink tezt new receiver way --- etherlink/tezt/lib/delayed_inbox.ml | 23 ++++++-- etherlink/tezt/tests/evm_sequencer.ml | 52 ++++++++++++++----- etherlink/tezt/tests/tezlink.ml | 75 ++++++++++++++++++++++++++- 3 files changed, 132 insertions(+), 18 deletions(-) diff --git a/etherlink/tezt/lib/delayed_inbox.ml b/etherlink/tezt/lib/delayed_inbox.ml index d91216e6a5fc..6213c97155d8 100644 --- a/etherlink/tezt/lib/delayed_inbox.ml +++ b/etherlink/tezt/lib/delayed_inbox.ml @@ -11,7 +11,11 @@ open Rpc.Syntax open Test_helpers open Evm_node_lib_dev_encoding.Rlp -type deposit_info = {receiver : string; chain_id : int option} +type receiver = TezosAddr of string | EthereumAddr of string + +let raw_addr = function TezosAddr addr | EthereumAddr addr -> addr + +type deposit_info = {receiver : receiver; chain_id : int option} let encode_option encode_some = function | None -> List [] @@ -20,10 +24,18 @@ let encode_option encode_some = function List [Value value] let encode_deposit_info deposit_info = - let receiver = Hex.to_bytes (`Hex (remove_0x deposit_info.receiver)) in + let receiver = + match deposit_info.receiver with + | TezosAddr addr -> + let tag = Bytes.make 1 '\001' in + let receiver = Hex.to_bytes (`Hex (remove_0x addr)) in + List [Value tag; Value receiver] + | EthereumAddr addr -> + let receiver = Hex.to_bytes (`Hex (remove_0x addr)) in + Value receiver + in let bytes = - encode - (List [Value receiver; encode_option encode_int deposit_info.chain_id]) + encode (List [receiver; encode_option encode_int deposit_info.chain_id]) in let (`Hex str) = Hex.of_bytes bytes in add_0x ("01" ^ str) @@ -105,7 +117,8 @@ let send_deposit_to_delayed_inbox ?(rlp = false) ~amount ~bridge ~depositor (* Implements optional types decoding as in the RLP library from the kernel's library. *) let infos = - if rlp then encode_deposit_info deposit_info else deposit_info.receiver + if rlp then encode_deposit_info deposit_info + else raw_addr deposit_info.receiver in let* () = Client.transfer diff --git a/etherlink/tezt/tests/evm_sequencer.ml b/etherlink/tezt/tests/evm_sequencer.ml index da15908ff298..857dbdc26275 100644 --- a/etherlink/tezt/tests/evm_sequencer.ml +++ b/etherlink/tezt/tests/evm_sequencer.ml @@ -1330,7 +1330,9 @@ let test_send_deposit_to_delayed_inbox = "0xb7c548b5442f5b28236f0dcd619f65aaaafd952240908adcf9642d8e616587ee"; } in - let deposit_info = {receiver = receiver.address; chain_id = None} in + let deposit_info = + {receiver = EthereumAddr receiver.address; chain_id = None} + in let* () = send_deposit_to_delayed_inbox ~amount @@ -1567,7 +1569,9 @@ let test_delayed_deposit_is_included = let* receiver_balance_prev = Eth_cli.balance ~account:receiver.address ~endpoint () in - let deposit_info = {receiver = receiver.address; chain_id = None} in + let deposit_info = + {receiver = EthereumAddr receiver.address; chain_id = None} + in let* () = send_deposit_to_delayed_inbox ~amount @@ -1827,7 +1831,10 @@ let test_delayed_transaction_peeked = blueprint that contains a deposit. *) let deposit_info = - {receiver = "0x1074Fd1EC02cbeaa5A90450505cF3B48D834f3EB"; chain_id = None} + { + receiver = EthereumAddr "0x1074Fd1EC02cbeaa5A90450505cF3B48D834f3EB"; + chain_id = None; + } in let* () = send_deposit_to_delayed_inbox @@ -2285,7 +2292,7 @@ let test_delayed_deposit_from_init_rollup_node = event. *) let amount = Tez.of_int 16 in let depositor = Constant.bootstrap5 in - let deposit_info = {receiver; chain_id = None} in + let deposit_info = {receiver = EthereumAddr receiver; chain_id = None} in let* () = send_deposit_to_delayed_inbox ~amount @@ -2457,7 +2464,10 @@ let test_init_from_rollup_node_with_delayed_inbox = let* () = Evm_node.terminate observer in (* Sends a deposit to the delayed inbox. *) let deposit_info = - {receiver = "0xB7A97043983f24991398E5a82f63F4C58a417185"; chain_id = None} + { + receiver = EthereumAddr "0xB7A97043983f24991398E5a82f63F4C58a417185"; + chain_id = None; + } in let* () = send_deposit_to_delayed_inbox @@ -3299,7 +3309,10 @@ let test_empty_block_on_upgrade = -> (* Send a deposit so the sequencer will in theory add it to its next block. *) let deposit_info = - {receiver = "0x1074Fd1EC02cbeaa5A90450505cF3B48D834f3EB"; chain_id = None} + { + receiver = EthereumAddr "0x1074Fd1EC02cbeaa5A90450505cF3B48D834f3EB"; + chain_id = None; + } in let* () = send_deposit_to_delayed_inbox @@ -3522,7 +3535,10 @@ let test_legacy_deposits_dispatched_after_kernel_upgrade = () in let deposit_info = - {receiver = Eth_account.bootstrap_accounts.(1).address; chain_id = None} + { + receiver = EthereumAddr Eth_account.bootstrap_accounts.(1).address; + chain_id = None; + } in let* () = send_deposit_to_delayed_inbox @@ -6702,7 +6718,10 @@ let test_sequencer_dont_read_level_twice = -> (* We deposit some Tez to the rollup *) let deposit_info = - {receiver = Eth_account.bootstrap_accounts.(1).address; chain_id = None} + { + receiver = EthereumAddr Eth_account.bootstrap_accounts.(1).address; + chain_id = None; + } in let* () = send_deposit_to_delayed_inbox @@ -6791,7 +6810,9 @@ let test_outbox_size_limit_resilience ~slow = let* receiver_balance_prev = Eth_cli.balance ~account:receiver.address ~endpoint () in - let deposit_info = {receiver = receiver.address; chain_id = None} in + let deposit_info = + {receiver = EthereumAddr receiver.address; chain_id = None} + in let* () = send_deposit_to_delayed_inbox ~amount @@ -8783,7 +8804,9 @@ let test_trace_transaction_calltracer_deposit = let* receiver_balance_prev = Eth_cli.balance ~account:receiver.address ~endpoint () in - let deposit_info = {receiver = receiver.address; chain_id = None} in + let deposit_info = + {receiver = EthereumAddr receiver.address; chain_id = None} + in let* () = send_deposit_to_delayed_inbox ~amount @@ -9402,7 +9425,9 @@ let test_deposit_and_fast_withdraw = ~error_msg:"Expected %R as initial balance instead of %L" ; (* Execute the deposit of 100 tez to the rollup. The depositor is the admin account, and the receiver is the Ethereum address. *) - let deposit_info = {receiver = receiver.address; chain_id = None} in + let deposit_info = + {receiver = EthereumAddr receiver.address; chain_id = None} + in let* () = send_deposit_to_delayed_inbox ~bridge:l1_contracts.bridge @@ -12904,7 +12929,10 @@ let test_deposit_event = in (* Send a deposit to the delayed inbox *) let deposit_info = - {receiver = Eth_account.bootstrap_accounts.(0).address; chain_id = None} + { + receiver = EthereumAddr Eth_account.bootstrap_accounts.(0).address; + chain_id = None; + } in let* () = send_deposit_to_delayed_inbox diff --git a/etherlink/tezt/tests/tezlink.ml b/etherlink/tezt/tests/tezlink.ml index 44f6abb70c4c..29acec5ad5e6 100644 --- a/etherlink/tezt/tests/tezlink.ml +++ b/etherlink/tezt/tests/tezlink.ml @@ -17,6 +17,7 @@ open Sc_rollup_helpers open Rpc.Syntax open Test_helpers open Setup +open Delayed_inbox let register_tezlink_test ~title ~tags ?bootstrap_accounts ?bootstrap_contracts ?(time_between_blocks = Evm_node.Nothing) ?additional_uses @@ -2727,6 +2728,77 @@ let test_node_catchup_on_multichain = ~error_msg:"Finalized blueprint check failed. Found %L, expected >= %R") ; unit +let test_delayed_deposit_is_included = + register_tezlink_test + ~time_between_blocks:Nothing + ~tags:["sequencer"; "delayed_inbox"; "inclusion"; "deposit"] + ~title:"Tezlink Delayed deposit is included" + @@ + fun { + client; + l1_contracts; + sc_rollup_address; + sc_rollup_node; + sequencer; + proxy; + _; + } + _protocol + -> + let endpoint = + Client.( + Foreign_endpoint + Endpoint. + {(Evm_node.rpc_endpoint_record sequencer) with path = "/tezlink"}) + in + + let* balance = + Client.get_balance_for ~endpoint ~account:Constant.bootstrap1.alias client + in + Check.((balance = Tez.zero) Tez.typ) + ~error_msg:"Expected balance at 0 for bootstrap1" ; + let amount = Tez.of_int 1000 in + let depositor = Constant.bootstrap5 in + let receiver = + Result.get_ok + @@ Tezos_protocol_alpha.Protocol.Contract_repr.of_b58check + Constant.bootstrap1.public_key_hash + in + let bytes = + Data_encoding.Binary.to_bytes_exn + Tezos_protocol_alpha.Protocol.Contract_repr.encoding + receiver + in + let (`Hex receiver) = Hex.of_bytes bytes in + let deposit_info = + Delayed_inbox.{receiver = TezosAddr receiver; chain_id = None} + in + let* () = + send_deposit_to_delayed_inbox + ~rlp:true + ~amount + ~bridge:l1_contracts.bridge + ~depositor + ~deposit_info + ~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* () = Delayed_inbox.assert_empty (Sc_rollup_node sc_rollup_node) in + let* balance = + Client.get_balance_for ~endpoint ~account:Constant.bootstrap1.alias client + in + Check.((balance = Tez.of_int 1000) Tez.typ) + ~error_msg:"Expected a 1000 tez on bootstrap1" ; + unit + let () = test_observer_starts [Alpha] ; test_describe_endpoint [Alpha] ; @@ -2773,4 +2845,5 @@ let () = test_tezlink_origination [Alpha] ; test_tezlink_forge_operations [Alpha] ; test_tezlink_gas_vs_l1 [Alpha] ; - test_node_catchup_on_multichain [Alpha] + test_node_catchup_on_multichain [Alpha] ; + test_delayed_deposit_is_included [Alpha] -- GitLab