Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions crates/revm/src/evm.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
//! Contains the `[RwasmEvm]` type and its implementation of the execution EVM traits.

use crate::{
api::RwasmFrame, executor::run_rwasm_loop, precompiles::RwasmPrecompiles,
types::SystemInterruptionOutcome, upgrade::upgrade_runtime_hook,
api::RwasmFrame,
executor::run_rwasm_loop,
precompiles::RwasmPrecompiles,
types::SystemInterruptionOutcome,
upgrade::{upgrade_runtime_hook_v1, upgrade_runtime_hook_v2},
};
use fluentbase_sdk::{
resolve_precompiled_runtime_from_input, try_resolve_precompile_account_from_input, Address,
Bytes, UPDATE_GENESIS_AUTH, UPDATE_GENESIS_PREFIX,
Bytes, UPDATE_GENESIS_AUTH, UPDATE_GENESIS_PREFIX_V1, UPDATE_GENESIS_PREFIX_V2,
};
use revm::{
bytecode::{ownable_account::OwnableAccountBytecode, Bytecode},
Expand Down Expand Up @@ -224,10 +227,13 @@ where
let _span = tracing::info_span!("revm.frame_init.call_hook").entered();
// a special hook for runtime upgrade
// that is used only for testnet to upgrade genesis without forks
if inputs.caller == UPDATE_GENESIS_AUTH
&& inputs.input.bytes(ctx).starts_with(&UPDATE_GENESIS_PREFIX)
{
return upgrade_runtime_hook(ctx, inputs);
if inputs.caller == UPDATE_GENESIS_AUTH {
let input = inputs.input.bytes(ctx);
if input.starts_with(&UPDATE_GENESIS_PREFIX_V1) {
return upgrade_runtime_hook_v1(ctx, inputs);
} else if input.starts_with(&UPDATE_GENESIS_PREFIX_V2) {
return upgrade_runtime_hook_v2(ctx, inputs);
}
}
// TODO(dmitry123): "do we want to disable it for mainnet?"
if let Some(precompiled_address) = try_resolve_precompile_account_from_input(
Expand Down
76 changes: 58 additions & 18 deletions crates/revm/src/upgrade.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,57 @@
use fluentbase_sdk::UPDATE_GENESIS_PREFIX;
use fluentbase_sdk::{Bytes, UPDATE_GENESIS_AUTH};
use revm::bytecode::Bytecode;
use revm::context::{ContextError, ContextTr, JournalTr};
use revm::handler::{FrameResult, FrameTr, ItemOrResult};
use revm::interpreter::{CallInputs, CallOutcome, Gas, InstructionResult, InterpreterResult};
use revm::Database;
use fluentbase_sdk::{
compile_wasm_to_rwasm, Bytes, RwasmCompilationResult, UPDATE_GENESIS_AUTH,
UPDATE_GENESIS_PREFIX_V1, UPDATE_GENESIS_PREFIX_V2,
};
use revm::{
bytecode::Bytecode,
context::{ContextError, ContextTr, JournalTr},
handler::{FrameResult, FrameTr, ItemOrResult},
interpreter::{CallInputs, CallOutcome, Gas, InstructionResult, InterpreterResult},
Database,
};

pub(crate) fn upgrade_runtime_hook<
macro_rules! upgrade_panic {
($inputs:expr, $message:literal) => {{}
return Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
result: InterpreterResult {
result: InstructionResult::Revert,
output: Bytes::from($message),
gas: Gas::new(0),
},
memory_offset: $inputs.return_memory_offset.clone(),
})));};
}

pub(crate) fn upgrade_runtime_hook_v1<
'a,
CTX: ContextTr,
FRAME: FrameTr<FrameResult = FrameResult>,
>(
ctx: &mut CTX,
inputs: &mut Box<CallInputs>,
) -> Result<
ItemOrResult<&'a mut FRAME, <FRAME as FrameTr>::FrameResult>,
ContextError<<<CTX as ContextTr>::Db as Database>::Error>,
> {
debug_assert_eq!(inputs.caller, UPDATE_GENESIS_AUTH);
let bytecode = inputs.input.bytes(ctx);
debug_assert!(bytecode.starts_with(&UPDATE_GENESIS_PREFIX_V1));
let Ok(bytecode) = Bytecode::new_raw_checked(bytecode.slice(UPDATE_GENESIS_PREFIX_V1.len()..))
else {
upgrade_panic!(inputs, "malformed bytecode");
};
ctx.journal_mut().set_code(inputs.target_address, bytecode);
Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
result: InterpreterResult {
result: InstructionResult::Return,
output: Default::default(),
gas: Gas::new(0),
},
memory_offset: inputs.return_memory_offset.clone(),
})))
}

pub(crate) fn upgrade_runtime_hook_v2<
'a,
CTX: ContextTr,
FRAME: FrameTr<FrameResult = FrameResult>,
Expand All @@ -19,18 +64,13 @@ pub(crate) fn upgrade_runtime_hook<
> {
debug_assert_eq!(inputs.caller, UPDATE_GENESIS_AUTH);
let bytecode = inputs.input.bytes(ctx);
debug_assert!(bytecode.starts_with(&UPDATE_GENESIS_PREFIX));
let Ok(bytecode) = Bytecode::new_raw_checked(bytecode.slice(UPDATE_GENESIS_PREFIX.len()..))
debug_assert!(bytecode.starts_with(&UPDATE_GENESIS_PREFIX_V2));
let wasm_bytecode = bytecode.slice(UPDATE_GENESIS_PREFIX_V2.len()..);
let Ok(RwasmCompilationResult { rwasm_module, .. }) = compile_wasm_to_rwasm(&wasm_bytecode)
else {
return Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
result: InterpreterResult {
result: InstructionResult::Revert,
output: Bytes::from("malformed bytecode"),
gas: Gas::new(0),
},
memory_offset: inputs.return_memory_offset.clone(),
})));
upgrade_panic!(inputs, "malformed wasm bytecode");
};
let bytecode = Bytecode::new_rwasm(rwasm_module.serialize().into());
ctx.journal_mut().set_code(inputs.target_address, bytecode);
Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
result: InterpreterResult {
Expand Down
3 changes: 2 additions & 1 deletion crates/types/src/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ pub const UPDATE_GENESIS_AUTH: Address = address!("0xa7bf6a9168fe8a111307b7c94b8

/// The prefix that must appear at the beginning of the transaction `calldata`
/// to signal that the transaction is intended to perform an account update.
pub const UPDATE_GENESIS_PREFIX: [u8; 4] = hex!("0x69bc6f64");
pub const UPDATE_GENESIS_PREFIX_V1: [u8; 4] = hex!("0x69bc6f64");
pub const UPDATE_GENESIS_PREFIX_V2: [u8; 4] = hex!("0x69bc6f65");

#[derive(Clone)]
pub struct GenesisContract {
Expand Down
90 changes: 86 additions & 4 deletions e2e/src/update_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@ use crate::EvmTestingContextWithGenesis;
use fluentbase_genesis::GENESIS_CONTRACTS_BY_ADDRESS;
use fluentbase_sdk::{
address, bytes, compile_wasm_to_rwasm, Address, PRECOMPILE_EVM_RUNTIME, UPDATE_GENESIS_AUTH,
UPDATE_GENESIS_PREFIX,
UPDATE_GENESIS_PREFIX_V1,
};
use fluentbase_testing::EvmTestingContext;
use fluentbase_types::UPDATE_GENESIS_PREFIX_V2;
use hex_literal::hex;

#[test]
fn test_update_account_code_by_auth() {
fn test_update_account_code_by_auth_v1() {
let mut ctx = EvmTestingContext::default().with_full_genesis();

// deploy evm greeting contract
// ----------------------------------------------------
// 1. Deploy some random EVM bytecode
// ----------------------------------------------------

const DEPLOYER_ADDRESS: Address = address!("0x7777777777777777777777777777777777777777");
let (contract_address, _) = ctx.deploy_evm_tx_with_gas(DEPLOYER_ADDRESS, hex!("60806040526105ae806100115f395ff3fe608060405234801561000f575f80fd5b506004361061003f575f3560e01c80633b2e97481461004357806345773e4e1461007357806348b8bcc314610091575b5f80fd5b61005d600480360381019061005891906102e5565b6100af565b60405161006a919061039a565b60405180910390f35b61007b6100dd565b604051610088919061039a565b60405180910390f35b61009961011a565b6040516100a6919061039a565b60405180910390f35b60605f8273ffffffffffffffffffffffffffffffffffffffff163190506100d58161012f565b915050919050565b60606040518060400160405280600b81526020017f48656c6c6f20576f726c64000000000000000000000000000000000000000000815250905090565b60605f4790506101298161012f565b91505090565b60605f8203610175576040518060400160405280600181526020017f30000000000000000000000000000000000000000000000000000000000000008152509050610282565b5f8290505f5b5f82146101a457808061018d906103f0565b915050600a8261019d9190610464565b915061017b565b5f8167ffffffffffffffff8111156101bf576101be610494565b5b6040519080825280601f01601f1916602001820160405280156101f15781602001600182028036833780820191505090505b5090505b5f851461027b578180610207906104c1565b925050600a8561021791906104e8565b60306102239190610518565b60f81b8183815181106102395761023861054b565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350600a856102749190610464565b94506101f5565b8093505050505b919050565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6102b48261028b565b9050919050565b6102c4816102aa565b81146102ce575f80fd5b50565b5f813590506102df816102bb565b92915050565b5f602082840312156102fa576102f9610287565b5b5f610307848285016102d1565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f5b8381101561034757808201518184015260208101905061032c565b5f8484015250505050565b5f601f19601f8301169050919050565b5f61036c82610310565b610376818561031a565b935061038681856020860161032a565b61038f81610352565b840191505092915050565b5f6020820190508181035f8301526103b28184610362565b905092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f819050919050565b5f6103fa826103e7565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361042c5761042b6103ba565b5b600182019050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f61046e826103e7565b9150610479836103e7565b92508261048957610488610437565b5b828204905092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f6104cb826103e7565b91505f82036104dd576104dc6103ba565b5b600182039050919050565b5f6104f2826103e7565b91506104fd836103e7565b92508261050d5761050c610437565b5b828206905092915050565b5f610522826103e7565b915061052d836103e7565b9250828201905080821115610545576105446103ba565b5b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffdfea2646970667358221220feebf5ace29c3c3146cb63bf7ca9009c2005f349075639d267cfbd817adde3e564736f6c63430008180033").into());

Expand All @@ -30,6 +34,10 @@ fn test_update_account_code_by_auth() {
let code = ctx.get_code(PRECOMPILE_EVM_RUNTIME).unwrap();
assert_eq!(&code.original_bytes(), account.rwasm_bytecode.as_ref());

// ----------------------------------------------------
// 2. Deploy rWasm bytecode using V1
// ----------------------------------------------------

let rwasm_module = compile_wasm_to_rwasm(
&wat::parse_str(
r#"
Expand All @@ -46,7 +54,7 @@ fn test_update_account_code_by_auth() {
.unwrap();
let new_bytecode = rwasm_module.rwasm_module.serialize();

let mut upgrade_input = UPDATE_GENESIS_PREFIX.to_vec();
let mut upgrade_input = UPDATE_GENESIS_PREFIX_V1.to_vec();
upgrade_input.extend_from_slice(new_bytecode.as_ref());

let result = ctx.call_evm_tx(
Expand All @@ -70,3 +78,77 @@ fn test_update_account_code_by_auth() {
);
assert!(result.is_halt());
}

#[test]
fn test_update_account_code_by_auth_v2() {
let mut ctx = EvmTestingContext::default().with_full_genesis();

// ----------------------------------------------------
// 1. Deploy some random EVM bytecode
// ----------------------------------------------------

const DEPLOYER_ADDRESS: Address = address!("0x7777777777777777777777777777777777777777");
let (contract_address, _) = ctx.deploy_evm_tx_with_gas(DEPLOYER_ADDRESS, hex!("60806040526105ae806100115f395ff3fe608060405234801561000f575f80fd5b506004361061003f575f3560e01c80633b2e97481461004357806345773e4e1461007357806348b8bcc314610091575b5f80fd5b61005d600480360381019061005891906102e5565b6100af565b60405161006a919061039a565b60405180910390f35b61007b6100dd565b604051610088919061039a565b60405180910390f35b61009961011a565b6040516100a6919061039a565b60405180910390f35b60605f8273ffffffffffffffffffffffffffffffffffffffff163190506100d58161012f565b915050919050565b60606040518060400160405280600b81526020017f48656c6c6f20576f726c64000000000000000000000000000000000000000000815250905090565b60605f4790506101298161012f565b91505090565b60605f8203610175576040518060400160405280600181526020017f30000000000000000000000000000000000000000000000000000000000000008152509050610282565b5f8290505f5b5f82146101a457808061018d906103f0565b915050600a8261019d9190610464565b915061017b565b5f8167ffffffffffffffff8111156101bf576101be610494565b5b6040519080825280601f01601f1916602001820160405280156101f15781602001600182028036833780820191505090505b5090505b5f851461027b578180610207906104c1565b925050600a8561021791906104e8565b60306102239190610518565b60f81b8183815181106102395761023861054b565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350600a856102749190610464565b94506101f5565b8093505050505b919050565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6102b48261028b565b9050919050565b6102c4816102aa565b81146102ce575f80fd5b50565b5f813590506102df816102bb565b92915050565b5f602082840312156102fa576102f9610287565b5b5f610307848285016102d1565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b5f5b8381101561034757808201518184015260208101905061032c565b5f8484015250505050565b5f601f19601f8301169050919050565b5f61036c82610310565b610376818561031a565b935061038681856020860161032a565b61038f81610352565b840191505092915050565b5f6020820190508181035f8301526103b28184610362565b905092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f819050919050565b5f6103fa826103e7565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361042c5761042b6103ba565b5b600182019050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f61046e826103e7565b9150610479836103e7565b92508261048957610488610437565b5b828204905092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f6104cb826103e7565b91505f82036104dd576104dc6103ba565b5b600182039050919050565b5f6104f2826103e7565b91506104fd836103e7565b92508261050d5761050c610437565b5b828206905092915050565b5f610522826103e7565b915061052d836103e7565b9250828201905080821115610545576105446103ba565b5b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffdfea2646970667358221220feebf5ace29c3c3146cb63bf7ca9009c2005f349075639d267cfbd817adde3e564736f6c63430008180033").into());

let result = ctx.call_evm_tx(
DEPLOYER_ADDRESS,
contract_address,
bytes!("45773e4e"),
None,
None,
);
assert!(result.is_success());

let account = GENESIS_CONTRACTS_BY_ADDRESS
.get(&PRECOMPILE_EVM_RUNTIME)
.unwrap();
let code = ctx.get_code(PRECOMPILE_EVM_RUNTIME).unwrap();
assert_eq!(&code.original_bytes(), account.rwasm_bytecode.as_ref());

// ----------------------------------------------------
// 2. Deploy Wasm bytecode using V2
// ----------------------------------------------------

let wasm_module = wat::parse_str(
r#"
(module
(memory (export "memory") 1)
(func (export "main")
unreachable
)
)
"#,
)
.unwrap();

let mut upgrade_input = UPDATE_GENESIS_PREFIX_V2.to_vec();
upgrade_input.extend_from_slice(&wasm_module);

let result = ctx.call_evm_tx(
UPDATE_GENESIS_AUTH,
PRECOMPILE_EVM_RUNTIME,
upgrade_input.into(),
None,
None,
);
assert!(result.is_success());

let new_code = ctx.get_code(PRECOMPILE_EVM_RUNTIME).unwrap();
let rwasm_bytecode_should_be = compile_wasm_to_rwasm(&wasm_module)
.unwrap()
.rwasm_module
.serialize();
assert_eq!(
new_code.original_bytes().as_ref(),
&rwasm_bytecode_should_be
);

let result = ctx.call_evm_tx(
DEPLOYER_ADDRESS,
contract_address,
bytes!("45773e4e"),
None,
None,
);
assert!(result.is_halt());
}
Loading