From 8404ea0aee930f59088d839ba6f2271ed5b5221d Mon Sep 17 00:00:00 2001 From: Thomas Letan Date: Wed, 17 Dec 2025 19:08:42 +0100 Subject: [PATCH] EVM Kernel: Enable migration logic for predeployed contracts * What The initialization mechanism for predeployed contracts (such as the withdrawal and bridge contracts) is updated to support overwriting existing bytecode with a newer version, effectively enabling migration logic during kernel initialization. * Why Previously, the initialization logic would strictly check if any code existed at the target address. If the code hash was not empty, it assumed the contract was already correctly deployed and skipped execution. This behavior prevented the kernel from updating these predeployed contracts when their Solidity implementation changed, as the presence of the old version blocked the installation of the new one. * How The `init_precompile_bytecode` function is modified to accept the expected `code_hash` as an argument. Instead of returning early merely if the account code is not empty, it now returns early only if the current on-chain code hash matches the expected hash. If there is a mismatch (and the account is not empty), the function now explicitly deletes the old code via `CodeStorage::delete` before proceeding to write the new bytecode. The call sites in `init_precompile_bytecodes` are updated to pass the specific code hashes for the internal forwarder, withdrawal, and FA bridge contracts. --- .../revm/src/precompiles/initializer.rs | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/etherlink/kernel_latest/revm/src/precompiles/initializer.rs b/etherlink/kernel_latest/revm/src/precompiles/initializer.rs index dbacb1037a8a..4c2f99091142 100644 --- a/etherlink/kernel_latest/revm/src/precompiles/initializer.rs +++ b/etherlink/kernel_latest/revm/src/precompiles/initializer.rs @@ -4,7 +4,7 @@ // SPDX-License-Identifier: MIT use revm::{ - primitives::{hex::FromHex, Address, Bytes, KECCAK_EMPTY}, + primitives::{hex::FromHex, Address, Bytes, FixedBytes, KECCAK_EMPTY}, state::Bytecode, }; use tezos_evm_runtime::runtime::Runtime; @@ -20,22 +20,50 @@ use crate::{ Error, }; +use super::constants::{ + FA_BRIDGE_SOL_CODE_HASH, INTERNAL_FORWARDER_SOL_CODE_HASH, WITHDRAWAL_SOL_CODE_HASH, +}; + pub fn init_precompile_bytecodes(host: &'_ mut Host) -> Result<(), Error> { - init_precompile_bytecode(host, &Address::ZERO, INTERNAL_FORWARDER_SOL_CONTRACT)?; - init_precompile_bytecode(host, &WITHDRAWAL_SOL_ADDR, WITHDRAWAL_SOL_CONTRACT)?; - init_precompile_bytecode(host, &FA_BRIDGE_SOL_ADDR, FA_BRIDGE_SOL_CONTRACT) + init_precompile_bytecode( + host, + &Address::ZERO, + INTERNAL_FORWARDER_SOL_CONTRACT, + &INTERNAL_FORWARDER_SOL_CODE_HASH, + )?; + init_precompile_bytecode( + host, + &WITHDRAWAL_SOL_ADDR, + WITHDRAWAL_SOL_CONTRACT, + &WITHDRAWAL_SOL_CODE_HASH, + )?; + init_precompile_bytecode( + host, + &FA_BRIDGE_SOL_ADDR, + FA_BRIDGE_SOL_CONTRACT, + &FA_BRIDGE_SOL_CODE_HASH, + )?; + + Ok(()) } fn init_precompile_bytecode( host: &'_ mut Host, addr: &Address, hex_bytes: &str, + code_hash: &FixedBytes<32>, ) -> Result<(), Error> { let mut created_account = StorageAccount::from_address(addr)?; let mut account_info = created_account.info(host).map_err(custom)?; - if account_info.code_hash != KECCAK_EMPTY { + + if account_info.code_hash == *code_hash { return Ok(()); } + + if account_info.code_hash != KECCAK_EMPTY { + CodeStorage::delete(host, &account_info.code_hash)?; + } + let code = Bytecode::new_legacy(Bytes::from_hex(hex_bytes).map_err(custom)?); let code_hash = bytes_hash(code.original_byte_slice()); account_info.code_hash = code_hash; -- GitLab