Skip to content

Commit

Permalink
EVM: Storage improvements , Blocks, Transactions, Receipts (#830)
Browse files Browse the repository at this point in the history
* Properly create receipts for successful transactions

* move to reth domain types for storage + add our packing types where feasible

* Added block building

* create receipts for failed transactions

* Add finalize_slot_hook

* separating state and storage better, fixes

* fix transaction mapping from hash to index

* fix

* fix

* improve tests

* test & fix state vector extensions

* fix test

* add failed transaction call test

---------

Co-authored-by: bkolad <[email protected]>
  • Loading branch information
2 people authored and preston-evans98 committed Sep 14, 2023
1 parent 096561a commit fa8edcd
Show file tree
Hide file tree
Showing 23 changed files with 899 additions and 274 deletions.
1 change: 1 addition & 0 deletions examples/demo-rollup/tests/evm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ async fn send_tx_test_to_eth(rpc_address: SocketAddr) -> Result<(), Box<dyn std:
test_client.execute().await
}

#[cfg(feature = "experimental")]
#[tokio::test]
async fn evm_tx_tests() -> Result<(), anyhow::Error> {
let (port_tx, port_rx) = tokio::sync::oneshot::channel();
Expand Down
17 changes: 14 additions & 3 deletions examples/demo-stf/src/hooks_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use sov_modules_stf_template::SequencerOutcome;
use sov_rollup_interface::da::BlockHeaderTrait;
use sov_rollup_interface::da::{BlobReaderTrait, DaSpec};
use sov_sequencer_registry::SequencerRegistry;
use sov_state::WorkingSet;
use sov_state::{AccessoryWorkingSet, WorkingSet};
use tracing::info;

use crate::runtime::Runtime;
Expand Down Expand Up @@ -94,12 +94,23 @@ impl<C: Context, Da: DaSpec> SlotHooks<Da> for Runtime<C, Da> {

fn end_slot_hook(
&self,
#[allow(unused_variables)] root_hash: [u8; 32],
#[allow(unused_variables)] working_set: &mut sov_state::WorkingSet<
<Self::Context as Spec>::Storage,
>,
) {
#[cfg(feature = "experimental")]
self.evm.end_slot_hook(root_hash, working_set);
self.evm.end_slot_hook(working_set);
}

fn finalize_slot_hook(
&self,
#[allow(unused_variables)] root_hash: [u8; 32],
#[allow(unused_variables)] accesorry_working_set: &mut AccessoryWorkingSet<
<Self::Context as Spec>::Storage,
>,
) {
#[cfg(feature = "experimental")]
self.evm
.finalize_slot_hook(root_hash, accesorry_working_set);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,18 @@ impl<C: Context, Da: DaSpec> SlotHooks<Da> for TestRuntime<C, Da> {

fn end_slot_hook(
&self,
_root_hash: [u8; 32],
_working_set: &mut sov_state::WorkingSet<<Self::Context as Spec>::Storage>,
) {
}

fn finalize_slot_hook(
&self,
_root_hash: [u8; 32],
_accesorry_working_set: &mut sov_state::AccessoryWorkingSet<
<Self::Context as Spec>::Storage,
>,
) {
}
}

impl<C, Da> BlobSelector<Da> for TestRuntime<C, Da>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use sov_modules_api::hooks::SlotHooks;
use sov_modules_api::{Context, Spec};
use sov_rollup_interface::da::BlockHeaderTrait;
use sov_state::{Storage, WorkingSet};
use sov_state::{AccessoryWorkingSet, Storage, WorkingSet};

use super::ChainState;
use crate::{StateTransitionId, TransitionInProgress};
Expand Down Expand Up @@ -62,10 +62,12 @@ impl<C: Context, Da: sov_modules_api::DaSpec> SlotHooks<Da> for ChainState<C, Da
);
}

fn end_slot_hook(
fn end_slot_hook(&self, _working_set: &mut WorkingSet<<Self::Context as Spec>::Storage>) {}

fn finalize_slot_hook(
&self,
_root_hash: [u8; 32],
_working_set: &mut WorkingSet<<Self::Context as Spec>::Storage>,
_accesorry_working_set: &mut AccessoryWorkingSet<<Self::Context as Spec>::Storage>,
) {
}
}
104 changes: 61 additions & 43 deletions module-system/module-implementations/sov-evm/src/call.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
use anyhow::Result;
use reth_primitives::TransactionSignedEcRecovered;
use revm::primitives::{CfgEnv, SpecId};
use reth_revm::into_reth_log;
use revm::primitives::{CfgEnv, EVMError, 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;
use crate::evm::{contract_address, EvmChainConfig, RlpEvmTransaction};
use crate::evm::transaction::{BlockEnv, Receipt, TransactionSignedAndRecovered};
use crate::evm::{EvmChainConfig, RlpEvmTransaction};
use crate::experimental::PendingTransaction;
use crate::Evm;

#[cfg_attr(
feature = "native",
derive(serde::Serialize),
derive(serde::Deserialize),
derive(schemars::JsonSchema)
derive(serde::Deserialize)
)]
#[derive(borsh::BorshDeserialize, borsh::BorshSerialize, Debug, PartialEq, Clone)]
pub struct CallMessage {
Expand All @@ -29,53 +30,70 @@ impl<C: sov_modules_api::Context> Evm<C> {
working_set: &mut WorkingSet<C::Storage>,
) -> Result<CallResponse> {
let evm_tx_recovered: TransactionSignedEcRecovered = tx.try_into()?;
let block_env = self
.pending_block
.get(working_set)
.expect("Pending block must be set");

let block_env = self.pending_block.get(working_set).unwrap_or_default();
let cfg = self.cfg.get(working_set).unwrap_or_default();
let cfg = self.cfg.get(working_set).expect("Evm config must be set");
let cfg_env = get_cfg_env(&block_env, cfg, None);

let hash = evm_tx_recovered.hash();

let evm_db: EvmDb<'_, C> = self.get_db(working_set);
let result = executor::execute_tx(evm_db, &block_env, &evm_tx_recovered, cfg_env);
let previous_transaction = self.pending_transactions.last(working_set);
let previous_transaction_cumulative_gas_used = previous_transaction
.as_ref()
.map_or(0u64, |tx| tx.receipt.receipt.cumulative_gas_used);
let log_index_start = previous_transaction.as_ref().map_or(0u64, |tx| {
tx.receipt.log_index_start + tx.receipt.receipt.logs.len() as u64
});

// TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/505
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();
let transaction = reth_rpc_types::Transaction::from_recovered(evm_tx_recovered);
let receipt = match result {
Ok(result) => {
let logs: Vec<_> = result.logs().into_iter().map(into_reth_log).collect();
let gas_used = result.gas_used();

self.pending_transactions
.push(&transaction, &mut working_set.accessory_state());
Receipt {
receipt: reth_primitives::Receipt {
tx_type: evm_tx_recovered.tx_type(),
success: result.is_success(),
cumulative_gas_used: previous_transaction_cumulative_gas_used + gas_used,
logs,
},
gas_used,
log_index_start,
error: None,
}
}
Err(err) => Receipt {
receipt: reth_primitives::Receipt {
tx_type: evm_tx_recovered.tx_type(),
success: false,
cumulative_gas_used: previous_transaction_cumulative_gas_used,
logs: vec![],
},
// TODO: Do we want failed transactions to use all gas?
gas_used: 0,
log_index_start,
error: Some(match err {
EVMError::Transaction(err) => EVMError::Transaction(err),
EVMError::PrevrandaoNotSet => EVMError::PrevrandaoNotSet,
EVMError::Database(_) => EVMError::Database(0u8),
}),
},
};

let receipt = reth_rpc_types::TransactionReceipt {
transaction_hash: hash.into(),
// TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/504
transaction_index: Some(reth_primitives::U256::from(0)),
// TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/504
block_hash: Default::default(),
// TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/504
block_number: Some(reth_primitives::U256::from(0)),
from,
to,
// TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/504
gas_used: Default::default(),
// TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/504
cumulative_gas_used: Default::default(),
contract_address: contract_address(result),
// TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/504
logs: Default::default(),
state_root: Some(reth_primitives::U256::from(0).into()),
// TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/504
logs_bloom: Default::default(),
status_code: Some(1u64.into()),
// TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/504
effective_gas_price: Default::default(),
transaction_type: reth_primitives::U8::from(1),
let pending_transaction = PendingTransaction {
transaction: TransactionSignedAndRecovered {
signer: evm_tx_recovered.signer(),
signed_transaction: evm_tx_recovered.into(),
block_number: block_env.number,
},
receipt,
};

self.receipts
.set(&hash.into(), &receipt, &mut working_set.accessory_state());
self.pending_transactions
.push(&pending_transaction, working_set);

Ok(CallResponse::default())
}
Expand Down
64 changes: 14 additions & 50 deletions module-system/module-implementations/sov-evm/src/evm/conversions.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use bytes::Bytes;
use ethereum_types::U64;
use ethers_core::types::{Bytes as EthBytes, OtherFields, Transaction};
use reth_primitives::{
Bytes as RethBytes, TransactionSigned, TransactionSignedEcRecovered, TransactionSignedNoHash,
};
Expand All @@ -12,7 +10,7 @@ use revm::primitives::{
};
use thiserror::Error;

use super::transaction::{BlockEnv, RlpEvmTransaction};
use super::transaction::{BlockEnv, RlpEvmTransaction, TransactionSignedAndRecovered};
use super::AccountInfo;

impl From<AccountInfo> for ReVmAccountInfo {
Expand All @@ -37,15 +35,14 @@ impl From<ReVmAccountInfo> for AccountInfo {
}
}

impl From<BlockEnv> for ReVmBlockEnv {
fn from(block_env: BlockEnv) -> Self {
impl From<&BlockEnv> for ReVmBlockEnv {
fn from(block_env: &BlockEnv) -> Self {
Self {
number: U256::from(block_env.number),
coinbase: block_env.coinbase,
timestamp: block_env.timestamp,
// TODO: handle difficulty
timestamp: U256::from(block_env.timestamp),
difficulty: U256::ZERO,
prevrandao: block_env.prevrandao,
prevrandao: Some(block_env.prevrandao),
basefee: U256::from(block_env.basefee),
gas_limit: U256::from(block_env.gas_limit),
}
Expand Down Expand Up @@ -73,44 +70,6 @@ pub(crate) fn create_tx_env(tx: &TransactionSignedEcRecovered) -> TxEnv {
}
}

impl TryFrom<RlpEvmTransaction> for Transaction {
type Error = RawEvmTxConversionError;
fn try_from(evm_tx: RlpEvmTransaction) -> Result<Self, Self::Error> {
let tx: TransactionSignedEcRecovered = evm_tx.try_into()?;

Ok(Self {
hash: tx.hash().into(),
nonce: tx.nonce().into(),

from: tx.signer().into(),
to: tx.to().map(|addr| addr.into()),
value: tx.value().into(),
gas_price: Some(tx.effective_gas_price(None).into()),

input: EthBytes::from(tx.input().to_vec()),
v: tx.signature().v(tx.chain_id()).into(),
r: tx.signature().r.into(),
s: tx.signature().s.into(),
transaction_type: Some(U64::from(tx.tx_type() as u8)),
// TODO handle access list
access_list: None,
max_priority_fee_per_gas: tx.max_priority_fee_per_gas().map(From::from),
max_fee_per_gas: Some(tx.max_fee_per_gas().into()),
chain_id: tx.chain_id().map(|id| id.into()),
// TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/503
block_hash: Some([0; 32].into()),
// TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/503
block_number: Some(1.into()),
// TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/503
transaction_index: Some(1.into()),
// TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/503
gas: Default::default(),
// TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/503
other: OtherFields::default(),
})
}
}

#[derive(Error, Debug)]
pub enum RawEvmTxConversionError {
#[error("Empty raw transaction data")]
Expand Down Expand Up @@ -162,6 +121,15 @@ impl TryFrom<RlpEvmTransaction> for TransactionSignedEcRecovered {
}
}

impl From<TransactionSignedAndRecovered> for TransactionSignedEcRecovered {
fn from(value: TransactionSignedAndRecovered) -> Self {
TransactionSignedEcRecovered::from_signed_transaction(
value.signed_transaction,
value.signer,
)
}
}

// TODO https://github.com/Sovereign-Labs/sovereign-sdk/issues/576
// https://github.com/paradigmxyz/reth/blob/d8677b4146f77c7c82d659c59b79b38caca78778/crates/rpc/rpc/src/eth/revm_utils.rs#L201
pub fn prepare_call_env(request: CallRequest) -> TxEnv {
Expand All @@ -187,7 +155,3 @@ pub fn prepare_call_env(request: CallRequest) -> TxEnv {
access_list: Default::default(),
}
}

pub fn to_u64(value: U256) -> u64 {
value.try_into().unwrap()
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use super::transaction::BlockEnv;

pub(crate) fn execute_tx<DB: Database<Error = Infallible> + DatabaseCommit>(
db: DB,
block_env: BlockEnv,
block_env: &BlockEnv,
tx: &TransactionSignedEcRecovered,
config_env: CfgEnv,
) -> Result<ExecutionResult, EVMError<Infallible>> {
Expand All @@ -29,7 +29,7 @@ pub(crate) fn execute_tx<DB: Database<Error = Infallible> + DatabaseCommit>(

pub(crate) fn inspect<DB: Database<Error = Infallible> + DatabaseCommit>(
db: DB,
block_env: BlockEnv,
block_env: &BlockEnv,
tx: TxEnv,
config_env: CfgEnv,
) -> Result<ResultAndState, EVMError<Infallible>> {
Expand Down
11 changes: 0 additions & 11 deletions module-system/module-implementations/sov-evm/src/evm/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use reth_primitives::{Address, H256, U256};
use revm::primitives::specification::SpecId;
use revm::primitives::{ExecutionResult, Output, B160};
use serde::{Deserialize, Serialize};
use sov_state::{Prefix, StateMap};

Expand Down Expand Up @@ -59,16 +58,6 @@ impl DbAccount {
}
}

pub(crate) fn contract_address(result: ExecutionResult) -> Option<B160> {
match result {
ExecutionResult::Success {
output: Output::Create(_, Some(addr)),
..
} => Some(addr),
_ => None,
}
}

/// EVM Chain configuration
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct EvmChainConfig {
Expand Down
Loading

0 comments on commit fa8edcd

Please sign in to comment.