Skip to content

Commit

Permalink
fragments deploy EVM contracts CREATE CREATE2 CALL
Browse files Browse the repository at this point in the history
Added precompiles for Berlin hard fork as default
This implementation is still not updating the evm::Ledger state
and it does not know how to handle ExitReason yet.

Also, for CALL transactions, it is still undefined how the returned
bytecode is to be handled.
  • Loading branch information
saibatizoku committed Nov 23, 2021
1 parent f6c7cba commit 1d9ee42
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 37 deletions.
134 changes: 114 additions & 20 deletions chain-evm/src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,25 @@
//! ## StackState<'config>
//!

use std::collections::BTreeMap;
use std::rc::Rc;

use evm::{
backend::{Apply, ApplyBackend, Backend, Basic, Log},
executor::stack::{MemoryStackState, PrecompileFn, StackExecutor, StackSubstateMetadata},
backend::{Apply, ApplyBackend, Backend, Basic},
executor::stack::{MemoryStackState, StackExecutor, StackSubstateMetadata},
Context, Runtime,
};
use primitive_types::{H160, H256, U256};

use crate::state::AccountTrie;
use crate::{
precompiles::Precompiles,
state::{AccountTrie, ByteCode, Key},
};

/// Export EVM types
pub use evm::{backend::MemoryVicinity as Environment, Config};
pub use evm::{
backend::{Log, MemoryVicinity as Environment},
Config, ExitReason,
};

/// An address of an EVM account.
pub type Address = H160;
Expand Down Expand Up @@ -69,8 +74,6 @@ pub type Value = U256;
/// The context of the EVM runtime
pub type RuntimeContext = Context;

type Precompiles = BTreeMap<H160, PrecompileFn>;

/// Top-level abstraction for the EVM with the
/// necessary types used to get the runtime going.
pub struct VirtualMachine<'runtime> {
Expand All @@ -83,8 +86,7 @@ pub struct VirtualMachine<'runtime> {
}

fn precompiles() -> Precompiles {
// TODO: provide adequate precompiles
Default::default()
Precompiles::new_berlin()
}

impl<'runtime> VirtualMachine<'runtime> {
Expand All @@ -108,17 +110,6 @@ impl<'runtime> VirtualMachine<'runtime> {
}
}

/// Returns an initialized instance of `evm::executor::StackExecutor`.
pub fn executor(
&self,
gas_limit: u64,
) -> StackExecutor<'_, '_, MemoryStackState<'_, '_, VirtualMachine>, BTreeMap<H160, PrecompileFn>>
{
let metadata = StackSubstateMetadata::new(gas_limit, self.config);
let memory_stack_state = MemoryStackState::new(metadata, self);
StackExecutor::new_with_precompiles(memory_stack_state, self.config, &self.precompiles)
}

/// Returns an initialized instance of `evm::Runtime`.
pub fn runtime(
&self,
Expand All @@ -128,6 +119,109 @@ impl<'runtime> VirtualMachine<'runtime> {
) -> Runtime<'_> {
Runtime::new(code, data, context, self.config)
}

/// Execute a CREATE transaction
pub fn transact_create(
&mut self,
caller: Address,
value: Value,
init_code: ByteCode,
gas_limit: u64,
access_list: Vec<(Address, Vec<Key>)>,
delete_empty: bool,
) -> ExitReason {
{
let metadata = StackSubstateMetadata::new(gas_limit, self.config);
let memory_stack_state = MemoryStackState::new(metadata, self);
let mut executor = StackExecutor::new_with_precompiles(
memory_stack_state,
self.config,
&self.precompiles,
);

let exit_reason =
executor.transact_create(caller, value, init_code.to_vec(), gas_limit, access_list);
// apply changes to the state, this consumes the executor
let state = executor.into_state();
// Next, we consume the stack state and extract the values and logs
// used to modify the accounts trie in the VirtualMachine.
let (values, logs) = state.deconstruct();

self.apply(values, logs, delete_empty);
exit_reason
}
}

/// Execute a CREATE2 transaction
pub fn transact_create2(
&mut self,
caller: Address,
value: Value,
init_code: ByteCode,
salt: H256,
gas_limit: u64,
access_list: Vec<(Address, Vec<Key>)>,
delete_empty: bool,
) -> ExitReason {
{
let metadata = StackSubstateMetadata::new(gas_limit, self.config);
let memory_stack_state = MemoryStackState::new(metadata, self);
let mut executor = StackExecutor::new_with_precompiles(
memory_stack_state,
self.config,
&self.precompiles,
);
let exit_reason = executor.transact_create2(
caller,
value,
init_code.to_vec(),
salt,
gas_limit,
access_list,
);
// apply changes to the state, this consumes the executor
let state = executor.into_state();
// Next, we consume the stack state and extract the values and logs
// used to modify the accounts trie in the VirtualMachine.
let (values, logs) = state.deconstruct();

self.apply(values, logs, delete_empty);
exit_reason
}
}

/// Execute a CALL transaction
pub fn transact_call(
&mut self,
caller: Address,
address: Address,
value: Value,
data: ByteCode,
gas_limit: u64,
access_list: Vec<(Address, Vec<Key>)>,
delete_empty: bool,
) -> (ExitReason, ByteCode) {
let metadata = StackSubstateMetadata::new(gas_limit, self.config);
let memory_stack_state = MemoryStackState::new(metadata, self);
let mut executor =
StackExecutor::new_with_precompiles(memory_stack_state, self.config, &self.precompiles);
let (exit_reason, byte_output) = executor.transact_call(
caller,
address,
value,
data.to_vec(),
gas_limit,
access_list,
);
// apply changes to the state, this consumes the executor
let state = executor.into_state();
// Next, we consume the stack state and extract the values and logs
// used to modify the accounts trie in the VirtualMachine.
let (values, logs) = state.deconstruct();

self.apply(values, logs, delete_empty);
(exit_reason, byte_output.into_boxed_slice())
}
}

impl<'runtime> Backend for VirtualMachine<'runtime> {
Expand Down
2 changes: 1 addition & 1 deletion chain-evm/src/precompiles/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ impl Precompiles {
}

#[allow(dead_code)]
fn new_berlin() -> Self {
pub fn new_berlin() -> Self {
Self::new_istanbul()
}
}
Expand Down
66 changes: 52 additions & 14 deletions chain-impl-mockchain/src/ledger/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,85 @@ use super::Error;
use crate::smartcontract::Contract;
#[cfg(feature = "evm")]
use chain_evm::{
machine::{Config, Environment, VirtualMachine},
machine::{Config, Environment, ExitReason, Log, VirtualMachine},
state::{AccountTrie, Balance},
};

#[derive(Default, Clone, PartialEq, Eq)]
pub struct Ledger {
#[cfg(feature = "evm")]
pub(crate) accounts: AccountTrie,
#[cfg(feature = "evm")]
pub(crate) logs: Box<[Log]>,
}

impl Ledger {
pub fn new() -> Self {
Default::default()
Self {
#[cfg(feature = "evm")]
accounts: Default::default(),
#[cfg(feature = "evm")]
logs: Default::default(),
}
}
#[cfg(feature = "evm")]
pub fn deploy_contract<'runtime>(
&mut self,
contract: Contract,
config: &'runtime Config,
environment: &'runtime Environment,
) -> Result<(), Error> {
) -> Result<ExitReason, Error> {
let mut vm = self.virtual_machine(config, environment);
match contract {
Contract::Create {
caller: _,
value: _,
init_code: _,
gas_limit: _,
access_list: _,
caller,
value,
init_code,
gas_limit,
access_list,
} => {
//
let _vm = self.virtual_machine(config, environment);
let exit_reason =
vm.transact_create(caller, value, init_code, gas_limit, access_list, true);
Ok(exit_reason)
}
Contract::Create2 {
caller,
value,
init_code,
salt,
gas_limit,
access_list,
} => {
let exit_reason = vm.transact_create2(
caller,
value,
init_code,
salt,
gas_limit,
access_list,
true,
);

todo!("execute the contract and update ledger.evm.accounts");
Ok(exit_reason)
}
Contract::Call {
caller,
address,
value,
data,
gas_limit,
access_list,
} => {
let (exit_reason, _byte_code_msg) =
vm.transact_call(caller, address, value, data, gas_limit, access_list, true);
Ok(exit_reason)
}
Contract::Create2 { .. } => todo!(),
Contract::Call { .. } => todo!(),
}
}

#[cfg(feature = "evm")]
pub fn virtual_machine<'runtime>(
pub(crate) fn virtual_machine<'runtime>(
&self,
config: &'runtime Config,
environment: &'runtime Environment,
Expand All @@ -67,7 +105,7 @@ impl Ledger {
#[cfg(feature = "evm")]
impl Ledger {
pub(crate) fn stats(&self) -> Option<String> {
let Ledger { accounts } = self;
let Ledger { accounts, .. } = self;
let mut count = 0;
let mut total = Balance::zero();
for (_, account) in accounts {
Expand Down
21 changes: 19 additions & 2 deletions chain-impl-mockchain/src/ledger/ledger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,8 @@ impl Ledger {
let contract = tx.as_slice().payload().into_payload();
let config = &ledger.settings.evm_params.config.into();
let environment = &ledger.settings.evm_params.environment;
ledger.evm.deploy_contract(contract, config, environment)?;
let _exit_reason =
ledger.evm.deploy_contract(contract, config, environment)?;
}
#[cfg(not(feature = "evm"))]
{
Expand Down Expand Up @@ -1019,7 +1020,23 @@ impl Ledger {
tx.payload_auth().into_payload_auth(),
)?;
}
Fragment::SmartContractDeploy(_deployment) => todo!(),
Fragment::SmartContractDeploy(tx) => {
#[cfg(feature = "evm")]
{
// WIP: deploying contract
let contract = tx.as_slice().payload().into_payload();
let config = &new_ledger.settings.evm_params.config.into();
let environment = &new_ledger.settings.evm_params.environment;
let _exit_reason =
new_ledger
.evm
.deploy_contract(contract, config, environment)?;
}
#[cfg(not(feature = "evm"))]
{
let _ = tx;
}
}
}

Ok(new_ledger)
Expand Down

0 comments on commit 1d9ee42

Please sign in to comment.