Skip to content

Commit

Permalink
Refactoring to unify evm Address and U256 types, serializing with ser…
Browse files Browse the repository at this point in the history
…de (#825)

* Refactoring to unify evm Address and U256 types, serializing with serde

* simplify

* remove TODO

* even less conversions

* few more

* fix for experimental dependency
  • Loading branch information
LukaszRozmej authored Sep 8, 2023
1 parent 44db227 commit 83ba024
Show file tree
Hide file tree
Showing 21 changed files with 151 additions and 231 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion examples/demo-stf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ sov-sequencer = { path = "../../full-node/sov-sequencer", optional = true }
sov-stf-runner = { path = "../../full-node/sov-stf-runner", optional = true }
# Only enable the evm on "experimental" feature
sov-evm = { path = "../../module-system/module-implementations/sov-evm", optional = true }
reth-primitives = { workspace = true, optional = true }

[dev-dependencies]
sov-rollup-interface = { path = "../../rollup-interface", features = ["mocks"] }
Expand All @@ -48,7 +49,7 @@ rand = "0.8"

[features]
default = []
experimental = ["sov-evm/experimental"]
experimental = ["sov-evm/experimental", "reth-primitives"]
native = [
"sov-bank/native",
"sov-cli",
Expand Down
9 changes: 5 additions & 4 deletions examples/demo-stf/src/genesis_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,11 @@ pub fn create_demo_genesis_config<C: Context, Da: DaSpec>(
};

#[cfg(feature = "experimental")]
let genesis_evm_address = hex::decode("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266")
.unwrap()
.try_into()
.expect("EVM module initialized with invalid address");
let genesis_evm_address = reth_primitives::Address::from_slice(
hex::decode("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266")
.unwrap()
.as_slice(),
);

let chain_state_config = ChainStateConfig {
// TODO: Put actual value
Expand Down
13 changes: 5 additions & 8 deletions full-node/sov-ethereum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ pub mod experimental {
use ethers::types::{Bytes, H256};
use jsonrpsee::types::ErrorObjectOwned;
use jsonrpsee::RpcModule;
use reth_primitives::TransactionSignedNoHash as RethTransactionSignedNoHash;
use reth_primitives::{Address, TransactionSignedNoHash as RethTransactionSignedNoHash};
use reth_rpc::eth::error::EthApiError;
use sov_evm::call::CallMessage;
use sov_evm::evm::{EthAddress, RawEvmTransaction};
use sov_evm::evm::RawEvmTransaction;
use sov_modules_api::transaction::Transaction;
use sov_modules_api::utils::to_jsonrpsee_error_object;
use sov_modules_api::EncodeCall;
Expand Down Expand Up @@ -48,15 +48,15 @@ pub mod experimental {
}

pub struct Ethereum<Da: DaService> {
nonces: Mutex<HashMap<EthAddress, u64>>,
nonces: Mutex<HashMap<Address, u64>>,
da_service: Da,
batch_builder: Arc<Mutex<EthBatchBuilder>>,
eth_rpc_config: EthRpcConfig,
}

impl<Da: DaService> Ethereum<Da> {
fn new(
nonces: Mutex<HashMap<EthAddress, u64>>,
nonces: Mutex<HashMap<Address, u64>>,
da_service: Da,
batch_builder: Arc<Mutex<EthBatchBuilder>>,
eth_rpc_config: EthRpcConfig,
Expand Down Expand Up @@ -84,10 +84,7 @@ pub mod experimental {
.ok_or(EthApiError::InvalidTransactionSignature)?;

let mut nonces = self.nonces.lock().unwrap();
let nonce = *nonces
.entry(sender.into())
.and_modify(|n| *n += 1)
.or_insert(0);
let nonce = *nonces.entry(sender).and_modify(|n| *n += 1).or_insert(0);

let tx = CallMessage { tx: raw_tx };
let message = <Runtime<DefaultContext, Da::Spec> as EncodeCall<
Expand Down
9 changes: 4 additions & 5 deletions module-system/module-implementations/sov-evm/src/call.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use anyhow::Result;
use revm::primitives::CfgEnv;
use revm::primitives::{CfgEnv, SpecId};
use sov_modules_api::CallResponse;
use sov_state::WorkingSet;

use crate::evm::db::EvmDb;
use crate::evm::executor::{self};
use crate::evm::transaction::{BlockEnv, EvmTransactionSignedEcRecovered};
use crate::evm::{contract_address, EvmChainConfig, RawEvmTransaction};
use crate::experimental::SpecIdWrapper;
use crate::Evm;

#[cfg_attr(
Expand Down Expand Up @@ -42,7 +41,7 @@ impl<C: sov_modules_api::Context> Evm<C> {
let result = executor::execute_tx(evm_db, block_env, &evm_tx_recovered, cfg_env).unwrap();

let from = evm_tx_recovered.signer();
let to = evm_tx_recovered.to().map(|to| to.into());
let to = evm_tx_recovered.to();
let transaction = reth_rpc_types::Transaction::from_recovered(evm_tx_recovered.tx);

self.pending_transactions
Expand Down Expand Up @@ -92,15 +91,15 @@ pub(crate) fn get_cfg_env(
CfgEnv {
chain_id: revm::primitives::U256::from(cfg.chain_id),
limit_contract_code_size: cfg.limit_contract_code_size,
spec_id: get_spec_id(cfg.spec, block_env.number).into(),
spec_id: get_spec_id(cfg.spec, block_env.number),
// disable_gas_refund: !cfg.gas_refunds, // option disabled for now, we could add if needed
..template_cfg.unwrap_or_default()
}
}

/// Get spec id for a given block number
/// Returns the first spec id defined for block >= block_number
pub(crate) fn get_spec_id(spec: Vec<(u64, SpecIdWrapper)>, block_number: u64) -> SpecIdWrapper {
pub(crate) fn get_spec_id(spec: Vec<(u64, SpecId)>, block_number: u64) -> SpecId {
match spec.binary_search_by(|&(k, _)| k.cmp(&block_number)) {
Ok(index) => spec[index].1,
Err(index) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use reth_rpc::eth::error::EthApiError;
use reth_rpc_types::CallRequest;
use revm::primitives::{
AccountInfo as ReVmAccountInfo, BlockEnv as ReVmBlockEnv, Bytecode, CreateScheme, TransactTo,
TxEnv, B160, B256, U256,
TxEnv, U256,
};
use thiserror::Error;

Expand All @@ -21,18 +21,18 @@ impl From<AccountInfo> for ReVmAccountInfo {
fn from(info: AccountInfo) -> Self {
Self {
nonce: info.nonce,
balance: U256::from_le_bytes(info.balance),
balance: info.balance,
code: Some(Bytecode::new_raw(Bytes::from(info.code))),
code_hash: B256::from(info.code_hash),
code_hash: info.code_hash,
}
}
}

impl From<ReVmAccountInfo> for AccountInfo {
fn from(info: ReVmAccountInfo) -> Self {
Self {
balance: info.balance.to_le_bytes(),
code_hash: info.code_hash.to_fixed_bytes(),
balance: info.balance,
code_hash: info.code_hash,
code: info.code.unwrap_or_default().bytes().to_vec(),
nonce: info.nonce,
}
Expand All @@ -43,12 +43,12 @@ impl From<BlockEnv> for ReVmBlockEnv {
fn from(block_env: BlockEnv) -> Self {
Self {
number: U256::from(block_env.number),
coinbase: B160::from_slice(&block_env.coinbase),
timestamp: U256::from_le_bytes(block_env.timestamp),
coinbase: block_env.coinbase,
timestamp: block_env.timestamp,
// TODO: handle difficulty
difficulty: U256::ZERO,
prevrandao: block_env.prevrandao.map(|r| B256::from_slice(&r)),
basefee: U256::from_le_bytes(block_env.basefee),
prevrandao: block_env.prevrandao,
basefee: U256::from(block_env.basefee),
gas_limit: U256::from(block_env.gas_limit),
}
}
Expand Down Expand Up @@ -194,3 +194,7 @@ pub fn prepare_call_env(request: CallRequest) -> TxEnv {
access_list: Default::default(),
}
}

pub fn to_u64(value: U256) -> u64 {
value.try_into().unwrap()
}
18 changes: 10 additions & 8 deletions module-system/module-implementations/sov-evm/src/evm/db.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
use std::convert::Infallible;

use reth_primitives::Address;
use revm::primitives::{AccountInfo as ReVmAccountInfo, Bytecode, B160, B256, U256};
use revm::Database;
use sov_state::codec::BcsCodec;
use sov_state::WorkingSet;

use super::{DbAccount, EthAddress};
use super::DbAccount;

pub(crate) struct EvmDb<'a, C: sov_modules_api::Context> {
pub(crate) accounts: sov_state::StateMap<EthAddress, DbAccount>,
pub(crate) accounts: sov_state::StateMap<Address, DbAccount, BcsCodec>,
pub(crate) working_set: &'a mut WorkingSet<C::Storage>,
}

impl<'a, C: sov_modules_api::Context> EvmDb<'a, C> {
pub(crate) fn new(
accounts: sov_state::StateMap<EthAddress, DbAccount>,
accounts: sov_state::StateMap<Address, DbAccount, BcsCodec>,
working_set: &'a mut WorkingSet<C::Storage>,
) -> Self {
Self {
Expand All @@ -27,7 +29,7 @@ impl<'a, C: sov_modules_api::Context> Database for EvmDb<'a, C> {
type Error = Infallible;

fn basic(&mut self, address: B160) -> Result<Option<ReVmAccountInfo>, Self::Error> {
let db_account = self.accounts.get(&address.0, self.working_set);
let db_account = self.accounts.get(&address, self.working_set);
Ok(db_account.map(|acc| acc.info.into()))
}

Expand All @@ -36,10 +38,10 @@ impl<'a, C: sov_modules_api::Context> Database for EvmDb<'a, C> {
}

fn storage(&mut self, address: B160, index: U256) -> Result<U256, Self::Error> {
let storage_value = if let Some(acc) = self.accounts.get(&address.0, self.working_set) {
let key = index.to_le_bytes();
let storage_value = acc.storage.get(&key, self.working_set).unwrap_or_default();
U256::from_le_bytes(storage_value)
let storage_value: U256 = if let Some(acc) = self.accounts.get(&address, self.working_set) {
acc.storage
.get(&index, self.working_set)
.unwrap_or_default()
} else {
U256::default()
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use super::DbAccount;
impl<'a, C: sov_modules_api::Context> DatabaseCommit for EvmDb<'a, C> {
fn commit(&mut self, changes: HashMap<B160, Account>) {
for (address, account) in changes {
let address = address.0;
let address = address;

// TODO figure out what to do when account is destroyed.
// https://github.com/Sovereign-Labs/sovereign-sdk/issues/425
Expand All @@ -25,8 +25,7 @@ impl<'a, C: sov_modules_api::Context> DatabaseCommit for EvmDb<'a, C> {
db_account.info = account.info.into();

for (key, value) in account.storage.into_iter() {
let key = key.to_le_bytes();
let value = value.present_value().to_le_bytes();
let value = value.present_value();
db_account.storage.set(&key, &value, self.working_set);
}

Expand Down
16 changes: 7 additions & 9 deletions module-system/module-implementations/sov-evm/src/evm/db_init.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
#[cfg(test)]
use revm::{
db::{CacheDB, EmptyDB},
primitives::B160,
};
use revm::db::{CacheDB, EmptyDB};
use revm::primitives::Address;

use super::db::EvmDb;
use super::{AccountInfo, DbAccount, EthAddress};
use super::{AccountInfo, DbAccount};

/// Initializes database with a predefined account.
pub(crate) trait InitEvmDb {
fn insert_account_info(&mut self, address: EthAddress, acc: AccountInfo);
fn insert_account_info(&mut self, address: Address, acc: AccountInfo);
}

impl<'a, C: sov_modules_api::Context> InitEvmDb for EvmDb<'a, C> {
fn insert_account_info(&mut self, sender: EthAddress, info: AccountInfo) {
fn insert_account_info(&mut self, sender: Address, info: AccountInfo) {
let parent_prefix = self.accounts.prefix();
let db_account = DbAccount::new_with_info(parent_prefix, sender, info);

Expand All @@ -23,7 +21,7 @@ impl<'a, C: sov_modules_api::Context> InitEvmDb for EvmDb<'a, C> {

#[cfg(test)]
impl InitEvmDb for CacheDB<EmptyDB> {
fn insert_account_info(&mut self, sender: EthAddress, acc: AccountInfo) {
self.insert_account_info(B160::from_slice(&sender), acc.into());
fn insert_account_info(&mut self, sender: Address, acc: AccountInfo) {
self.insert_account_info(sender, acc.into());
}
}
46 changes: 21 additions & 25 deletions module-system/module-implementations/sov-evm/src/evm/mod.rs
Original file line number Diff line number Diff line change
@@ -1,64 +1,60 @@
use borsh::{BorshDeserialize, BorshSerialize};
use reth_primitives::{Address, H256, U256};
use revm::primitives::specification::SpecId;
use revm::primitives::{ExecutionResult, Output, B160};
use sov_state::Prefix;
use serde::{Deserialize, Serialize};
use sov_state::{Prefix, StateMap};

mod conversions;
pub(crate) mod conversions;
pub(crate) mod db;
mod db_commit;
pub(crate) mod db_init;
pub(crate) mod executor;
mod serialize;
#[cfg(test)]
mod tests;
pub(crate) mod transaction;

pub type EthAddress = [u8; 20];
pub(crate) type Bytes32 = [u8; 32];

pub use conversions::prepare_call_env;
use sov_state::codec::BcsCodec;
pub use transaction::RawEvmTransaction;

use crate::experimental::SpecIdWrapper;

// Stores information about an EVM account
#[derive(borsh::BorshDeserialize, borsh::BorshSerialize, Debug, PartialEq, Clone, Default)]
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone, Default)]
pub(crate) struct AccountInfo {
pub(crate) balance: Bytes32,
pub(crate) code_hash: Bytes32,
pub(crate) balance: U256,
pub(crate) code_hash: H256,
// TODO: `code` can be a huge chunk of data. We can use `StateValue` and lazy load it only when needed.
// https://github.com/Sovereign-Labs/sovereign-sdk/issues/425
pub(crate) code: Vec<u8>,
pub(crate) nonce: u64,
}

/// Stores information about an EVM account and a corresponding account state.
#[derive(borsh::BorshDeserialize, borsh::BorshSerialize, Debug, PartialEq, Clone)]
#[derive(Deserialize, Serialize, Debug, PartialEq, Clone)]
pub(crate) struct DbAccount {
pub(crate) info: AccountInfo,
pub(crate) storage: sov_state::StateMap<Bytes32, Bytes32>,
pub(crate) storage: StateMap<U256, U256, BcsCodec>,
}

impl DbAccount {
fn new(parent_prefix: &Prefix, address: EthAddress) -> Self {
fn new(parent_prefix: &Prefix, address: Address) -> Self {
let prefix = Self::create_storage_prefix(parent_prefix, address);
Self {
info: Default::default(),
storage: sov_state::StateMap::new(prefix),
storage: StateMap::with_codec(prefix, BcsCodec {}),
}
}

fn new_with_info(parent_prefix: &Prefix, address: EthAddress, info: AccountInfo) -> Self {
fn new_with_info(parent_prefix: &Prefix, address: Address, info: AccountInfo) -> Self {
let prefix = Self::create_storage_prefix(parent_prefix, address);
Self {
info,
storage: sov_state::StateMap::new(prefix),
storage: StateMap::with_codec(prefix, BcsCodec {}),
}
}

fn create_storage_prefix(parent_prefix: &Prefix, address: EthAddress) -> Prefix {
fn create_storage_prefix(parent_prefix: &Prefix, address: Address) -> Prefix {
let mut prefix = parent_prefix.as_aligned_vec().clone().into_inner();
prefix.extend_from_slice(&address);
prefix.extend_from_slice(&address.0);
Prefix::new(prefix)
}
}
Expand All @@ -74,7 +70,7 @@ pub(crate) fn contract_address(result: ExecutionResult) -> Option<B160> {
}

/// EVM Chain configuration
#[derive(Debug, Clone, PartialEq, BorshSerialize, BorshDeserialize)]
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct EvmChainConfig {
/// Unique chain id
/// Chains can be registered at <https://github.com/ethereum-lists/chains>.
Expand All @@ -85,10 +81,10 @@ pub struct EvmChainConfig {
pub limit_contract_code_size: Option<usize>,

/// List of EVM hardforks by block number
pub spec: Vec<(u64, SpecIdWrapper)>,
pub spec: Vec<(u64, SpecId)>,

/// Coinbase where all the fees go
pub coinbase: EthAddress,
pub coinbase: Address,

/// Gas limit for single block
pub block_gas_limit: u64,
Expand All @@ -102,8 +98,8 @@ impl Default for EvmChainConfig {
EvmChainConfig {
chain_id: 1,
limit_contract_code_size: None,
spec: vec![(0, SpecIdWrapper::from(SpecId::LATEST))],
coinbase: [0u8; 20],
spec: vec![(0, SpecId::LATEST)],
coinbase: Address::zero(),
block_gas_limit: reth_primitives::constants::ETHEREUM_BLOCK_GAS_LIMIT,
block_timestamp_delta: 1,
}
Expand Down
Loading

0 comments on commit 83ba024

Please sign in to comment.