From 95ed994fd19f63b2b6ab0c62727cad647e7617e9 Mon Sep 17 00:00:00 2001 From: rakita Date: Fri, 7 Oct 2022 13:56:11 +0200 Subject: [PATCH] Transaction type and some wrap over revm (#19) * Transaction and wip db binding * simple revm wrapper * nits and small sig change --- crates/executor/src/executor.rs | 34 +++-- crates/executor/src/lib.rs | 4 +- crates/executor/src/revm_wrap.rs | 128 +++++++++++++++++ crates/interfaces/src/executor.rs | 17 ++- crates/primitives/src/account.rs | 9 +- crates/primitives/src/block.rs | 28 +++- crates/primitives/src/header.rs | 10 ++ crates/primitives/src/lib.rs | 6 +- .../primitives/src/transaction/access_list.rs | 14 ++ crates/primitives/src/transaction/mod.rs | 134 +++++++++++++++++- .../primitives/src/transaction/signature.rs | 16 ++- 11 files changed, 366 insertions(+), 34 deletions(-) create mode 100644 crates/executor/src/revm_wrap.rs create mode 100644 crates/primitives/src/transaction/access_list.rs diff --git a/crates/executor/src/executor.rs b/crates/executor/src/executor.rs index ef8b19651..7486668a6 100644 --- a/crates/executor/src/executor.rs +++ b/crates/executor/src/executor.rs @@ -1,20 +1,19 @@ -#![allow(missing_debug_implementations)] - -use crate::Config; +use crate::{revm_wrap, Config}; use reth_interfaces::{ consensus::Consensus, executor::{BlockExecutor, Error, ExecutorDb}, }; -use reth_primitives::Block; +use reth_primitives::BlockLocked; +use revm::{db::EmptyDB, AnalysisKind, Env, SpecId}; /// Main block executor pub struct Executor { /// Configuration, Spec and optional flags. - config: Config, + pub config: Config, /// Database - db: Box, + pub db: Box, /// Consensus - consensus: Box, + pub consensus: Box, } impl Executor { @@ -23,13 +22,22 @@ impl Executor { Self { config, db, consensus } } - /// Verify block (TODO) - pub fn verify(&self, block: &Block) -> Result<(), Error> { - // TODO example - let _ = self.consensus.validate_header(&block.header); - if self.config.example { - let _block_exist = self.db.get_block(block.header.number); + /// Verify block. Execute all transaction and compare results. + pub fn verify(&self, block: &BlockLocked) -> Result<(), Error> { + let mut env = Env::default(); + env.cfg.chain_id = 1.into(); + env.cfg.spec_id = SpecId::LATEST; + env.cfg.perf_all_precompiles_have_balance = true; + env.cfg.perf_analyse_created_bytecodes = AnalysisKind::Raw; + + revm_wrap::fill_block_env(&mut env.block, block); + + let _database = revm_wrap::TempStateDb::new(EmptyDB::default()); + + for transaction in block.body.iter() { + revm_wrap::fill_tx_env(&mut env.tx, transaction.as_ref()); } + Err(Error::VerificationFailed) } } diff --git a/crates/executor/src/lib.rs b/crates/executor/src/lib.rs index 5961fac18..2853d2ab6 100644 --- a/crates/executor/src/lib.rs +++ b/crates/executor/src/lib.rs @@ -1,4 +1,4 @@ -#![warn(missing_debug_implementations, missing_docs, unreachable_pub)] +#![warn(missing_docs, unreachable_pub)] #![deny(unused_must_use, rust_2018_idioms)] #![doc(test( no_crate_inject, @@ -10,5 +10,7 @@ mod cfg; /// Executor pub mod executor; +/// Wrapper around revm database and types +pub mod revm_wrap; pub use cfg::Config; diff --git a/crates/executor/src/revm_wrap.rs b/crates/executor/src/revm_wrap.rs new file mode 100644 index 000000000..42484dace --- /dev/null +++ b/crates/executor/src/revm_wrap.rs @@ -0,0 +1,128 @@ +use reth_interfaces::executor::ExecutorDb; +use reth_primitives::{BlockLocked, Transaction, H160, H256, U256}; +use revm::{ + db::{CacheDB, Database, EmptyDB}, + BlockEnv, TransactTo, TxEnv, +}; +use std::convert::Infallible; + +/// Temporary stateDB TODO +pub type TempStateDb = CacheDB; + +/// Wrapper around ExeuctorDb that implements revm database trait +pub struct Wrapper<'a>(&'a dyn ExecutorDb); + +impl<'a> Database for Wrapper<'a> { + type Error = Infallible; + + fn basic(&mut self, address: H160) -> Result, Self::Error> { + Ok(self.0.basic_account(address).map(|account| revm::AccountInfo { + balance: account.balance, + nonce: account.nonce, + code_hash: account.bytecode_hash, + code: None, + })) + } + + fn code_by_hash(&mut self, code_hash: H256) -> Result { + let (bytecode, size) = self.0.bytecode_by_hash(code_hash).unwrap_or_default(); + Ok(unsafe { revm::Bytecode::new_checked(bytecode.0, size, Some(code_hash)) }) + } + + fn storage(&mut self, address: H160, index: U256) -> Result { + let mut h_index = H256::zero(); + index.to_big_endian(h_index.as_bytes_mut()); + + Ok(U256::from_big_endian(self.0.storage(address, h_index).unwrap_or_default().as_ref())) + } + + fn block_hash(&mut self, number: U256) -> Result { + Ok(self.0.block_hash(number).unwrap_or_default()) + } +} + +/// Fill block environment from Block. +pub fn fill_block_env(block_env: &mut BlockEnv, block: &BlockLocked) { + block_env.number = block.header.number.into(); + block_env.coinbase = block.header.beneficiary; + block_env.timestamp = block.header.timestamp.into(); + block_env.difficulty = block.header.difficulty; + block_env.basefee = block.header.base_fee_per_gas.unwrap_or_default().into(); + block_env.gas_limit = block.header.gas_limit.into(); +} + +/// Fill transaction environment from Transaction. +pub fn fill_tx_env(tx_env: &mut TxEnv, transaction: &Transaction) { + match transaction { + Transaction::Legacy { nonce, chain_id, gas_price, gas_limit, to, value, input } => { + tx_env.gas_limit = *gas_limit; + tx_env.gas_price = (*gas_price).into(); + tx_env.gas_priority_fee = None; + tx_env.transact_to = + if let Some(to) = to { TransactTo::Call(*to) } else { TransactTo::create() }; + tx_env.value = *value; + tx_env.data = input.0.clone(); + tx_env.chain_id = *chain_id; + tx_env.nonce = Some(*nonce); + } + Transaction::Eip2930 { + nonce, + chain_id, + gas_price, + gas_limit, + to, + value, + input, + access_list, + } => { + tx_env.gas_limit = *gas_limit; + tx_env.gas_price = (*gas_price).into(); + tx_env.gas_priority_fee = None; + tx_env.transact_to = + if let Some(to) = to { TransactTo::Call(*to) } else { TransactTo::create() }; + tx_env.value = *value; + tx_env.data = input.0.clone(); + tx_env.chain_id = Some(*chain_id); + tx_env.nonce = Some(*nonce); + tx_env.access_list = access_list + .iter() + .map(|l| { + ( + l.address, + l.storage_keys.iter().map(|k| U256::from_big_endian(k.as_ref())).collect(), + ) + }) + .collect(); + } + Transaction::Eip1559 { + nonce, + chain_id, + gas_limit, + max_fee_per_gas, + max_priority_fee_per_gas, + to, + value, + input, + access_list, + } => { + tx_env.gas_limit = *gas_limit; + tx_env.gas_price = (*max_fee_per_gas).into(); + tx_env.gas_priority_fee = Some((*max_priority_fee_per_gas).into()); + tx_env.transact_to = + if let Some(to) = to { TransactTo::Call(*to) } else { TransactTo::create() }; + tx_env.value = *value; + tx_env.data = input.0.clone(); + tx_env.chain_id = Some(*chain_id); + tx_env.nonce = Some(*nonce); + tx_env.access_list = access_list + .iter() + .map(|l| { + ( + l.address, + l.storage_keys.iter().map(|k| U256::from_big_endian(k.as_ref())).collect(), + ) + }) + .collect(); + } + } +} diff --git a/crates/interfaces/src/executor.rs b/crates/interfaces/src/executor.rs index 75ee4726d..99ae10c87 100644 --- a/crates/interfaces/src/executor.rs +++ b/crates/interfaces/src/executor.rs @@ -1,5 +1,7 @@ use async_trait::async_trait; -use reth_primitives::{Address, Block, BlockNumber, StorageKey, StorageValue}; +use reth_primitives::{ + Account, Address, Block, BlockNumber, Bytes, StorageKey, StorageValue, H256, U256, +}; use thiserror::Error; /// Takes block and executes it, returns error @@ -22,8 +24,17 @@ pub enum Error { /// Function needed for executor pub trait ExecutorDb { /// Get Block by BlockNumber. - fn get_block(&self, _height: BlockNumber) -> Option; + fn block(&self, _height: BlockNumber) -> Option; /// Get storage. - fn get_storage(&self, account: Address, storage_key: StorageKey) -> Option; + fn storage(&self, account: Address, storage_key: StorageKey) -> Option; + + /// Get basic account information. + fn basic_account(&self, adderss: Address) -> Option; + + /// Get account code by its hash + fn bytecode_by_hash(&self, code_hash: H256) -> Option<(Bytes, usize)>; + + /// Get block hash by number. + fn block_hash(&self, number: U256) -> Option; } diff --git a/crates/primitives/src/account.rs b/crates/primitives/src/account.rs index fae49f16b..7542a2b6d 100644 --- a/crates/primitives/src/account.rs +++ b/crates/primitives/src/account.rs @@ -1,9 +1,12 @@ +use crate::{H256, U256}; - - +/// Account saved in database #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Account { + /// Nonce. pub nonce: u64, + /// Account balance. pub balance: U256, + /// Hash of the bytecode. pub bytecode_hash: H256, -} \ No newline at end of file +} diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index 395586c4e..3a87796be 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -1,4 +1,5 @@ -use crate::{Header, Receipt, Transaction}; +use crate::{Header, HeaderLocked, Receipt, Transaction, TransactionSigned}; +use std::ops::Deref; /// Ethereum full block. #[derive(Debug, Clone, PartialEq, Eq, Default)] @@ -10,3 +11,28 @@ pub struct Block { /// Block receipts. pub receipts: Vec, } + +impl Deref for Block { + type Target = Header; + fn deref(&self) -> &Self::Target { + &self.header + } +} + +/// Sealed Ethereum full block. +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct BlockLocked { + /// Locked block header. + pub header: HeaderLocked, + /// Transactions with signatures. + pub body: Vec, + /// Block receipts. + pub receipts: Vec, +} + +impl Deref for BlockLocked { + type Target = Header; + fn deref(&self) -> &Self::Target { + self.header.as_ref() + } +} diff --git a/crates/primitives/src/header.rs b/crates/primitives/src/header.rs index 3b9be0cb7..5649e7b8e 100644 --- a/crates/primitives/src/header.rs +++ b/crates/primitives/src/header.rs @@ -1,3 +1,5 @@ +use std::ops::Deref; + use crate::{BlockNumber, Bytes, H160, H256, U256}; /// Block header @@ -87,6 +89,14 @@ impl AsRef
for HeaderLocked { } } +impl Deref for HeaderLocked { + type Target = Header; + + fn deref(&self) -> &Self::Target { + &self.header + } +} + impl HeaderLocked { /// Extract raw header that can be modified. pub fn unlock(self) -> Header { diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index c767103ea..93d8c1d35 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -7,6 +7,7 @@ //! Commonly used types in reth. +mod account; mod block; mod chain; mod header; @@ -14,11 +15,12 @@ mod log; mod receipt; mod transaction; -pub use block::Block; +pub use account::Account; +pub use block::{Block, BlockLocked}; pub use header::{Header, HeaderLocked}; pub use log::Log; pub use receipt::Receipt; -pub use transaction::{Transaction, TransactionSigned, TxType}; +pub use transaction::{AccessList, AccessListItem, Transaction, TransactionSigned, TxType}; /// Block Number is height of chain pub type BlockNumber = u64; diff --git a/crates/primitives/src/transaction/access_list.rs b/crates/primitives/src/transaction/access_list.rs new file mode 100644 index 000000000..cd50a25b5 --- /dev/null +++ b/crates/primitives/src/transaction/access_list.rs @@ -0,0 +1,14 @@ +use crate::{Address, H256}; + +/// A list of addresses and storage keys that the transaction plans to access. +/// Accesses outside the list are possible, but become more expensive. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct AccessListItem { + /// Account addresses that would be loaded at the start of execution + pub address: Address, + /// Keys of storage that would be loaded at the start of execution + pub storage_keys: Vec, +} + +/// AccessList as defined in EIP-2930 +pub type AccessList = Vec; diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 36abd1ce3..0281b7a01 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -1,33 +1,142 @@ +mod access_list; mod signature; mod tx_type; +use crate::{Address, Bytes, TxHash, U256}; +pub use access_list::{AccessList, AccessListItem}; use signature::Signature; +use std::ops::Deref; pub use tx_type::TxType; /// Raw Transaction. +/// Transaction type is introduced in EIP-2718: https://eips.ethereum.org/EIPS/eip-2718 #[derive(Debug, Clone, PartialEq, Eq)] pub enum Transaction { /// Legacy transaciton. Legacy { - /// Nonce. + /// Added as EIP-155: Simple replay attack protection + chain_id: Option, + /// A scalar value equal to the number of transactions sent by the sender; formally Tn. nonce: u64, + /// A scalar value equal to the number of + /// Wei to be paid per unit of gas for all computation + /// costs incurred as a result of the execution of this transaction; formally Tp. + gas_price: u64, + /// A scalar value equal to the maximum + /// amount of gas that should be used in executing + /// this transaction. This is paid up-front, before any + /// computation is done and may not be increased + /// later; formally Tg. + gas_limit: u64, + /// The 160-bit address of the message call’s recipient or, for a contract creation + /// transaction, ∅, used here to denote the only member of B0 ; formally Tt. + to: Option
, + /// A scalar value equal to the number of Wei to + /// be transferred to the message call’s recipient or, + /// in the case of contract creation, as an endowment + /// to the newly created account; formally Tv. + value: U256, + /// Input has two uses depending if transaction is Create or Call (if `to` field is None or + /// Some). init: An unlimited size byte array specifying the + /// EVM-code for the account initialisation procedure CREATE, + /// data: An unlimited size byte array specifying the + /// input data of the message call, formally Td. + input: Bytes, }, - /// Transaction with AccessList. + /// Transaction with AccessList. https://eips.ethereum.org/EIPS/eip-2930 Eip2930 { - /// nonce. + /// Added as EIP-155: Simple replay attack protection + chain_id: u64, + /// A scalar value equal to the number of transactions sent by the sender; formally Tn. nonce: u64, + /// A scalar value equal to the number of + /// Wei to be paid per unit of gas for all computation + /// costs incurred as a result of the execution of this transaction; formally Tp. + gas_price: u64, + /// A scalar value equal to the maximum + /// amount of gas that should be used in executing + /// this transaction. This is paid up-front, before any + /// computation is done and may not be increased + /// later; formally Tg. + gas_limit: u64, + /// The 160-bit address of the message call’s recipient or, for a contract creation + /// transaction, ∅, used here to denote the only member of B0 ; formally Tt. + to: Option
, + /// A scalar value equal to the number of Wei to + /// be transferred to the message call’s recipient or, + /// in the case of contract creation, as an endowment + /// to the newly created account; formally Tv. + value: U256, + /// Input has two uses depending if transaction is Create or Call (if `to` field is None or + /// Some). init: An unlimited size byte array specifying the + /// EVM-code for the account initialisation procedure CREATE, + /// data: An unlimited size byte array specifying the + /// input data of the message call, formally Td. + input: Bytes, + /// The accessList specifies a list of addresses and storage keys; + /// these addresses and storage keys are added into the `accessed_addresses` + /// and `accessed_storage_keys` global sets (introduced in EIP-2929). + /// A gas cost is charged, though at a discount relative to the cost of + /// accessing outside the list. + access_list: AccessList, }, - /// Transaction with priority fee. + /// Transaction with priority fee. https://eips.ethereum.org/EIPS/eip-1559 Eip1559 { - /// Nonce. + /// Added as EIP-155: Simple replay attack protection + chain_id: u64, + /// A scalar value equal to the number of transactions sent by the sender; formally Tn. nonce: u64, + /// A scalar value equal to the maximum + /// amount of gas that should be used in executing + /// this transaction. This is paid up-front, before any + /// computation is done and may not be increased + /// later; formally Tg. + gas_limit: u64, + /// A scalar value equal to the maximum + /// amount of gas that should be used in executing + /// this transaction. This is paid up-front, before any + /// computation is done and may not be increased + /// later; formally Tg. + max_fee_per_gas: u64, + /// Max Priority fee that transaction is paying + max_priority_fee_per_gas: u64, + /// The 160-bit address of the message call’s recipient or, for a contract creation + /// transaction, ∅, used here to denote the only member of B0 ; formally Tt. + to: Option
, + /// A scalar value equal to the number of Wei to + /// be transferred to the message call’s recipient or, + /// in the case of contract creation, as an endowment + /// to the newly created account; formally Tv. + value: U256, + /// Input has two uses depending if transaction is Create or Call (if `to` field is None or + /// Some). init: An unlimited size byte array specifying the + /// EVM-code for the account initialisation procedure CREATE, + /// data: An unlimited size byte array specifying the + /// input data of the message call, formally Td. + input: Bytes, + /// The accessList specifies a list of addresses and storage keys; + /// these addresses and storage keys are added into the `accessed_addresses` + /// and `accessed_storage_keys` global sets (introduced in EIP-2929). + /// A gas cost is charged, though at a discount relative to the cost of + /// accessing outside the list. + access_list: AccessList, }, } +impl Transaction { + /// Heavy operation that return hash over rlp encoded transaction. + /// It is only used for signature signing. + pub fn signature_hash(&self) -> TxHash { + todo!() + } +} + /// Signed transaction. -#[derive(Debug, Clone)] + +#[derive(Debug, Clone, PartialEq, Eq)] pub struct TransactionSigned { transaction: Transaction, + hash: TxHash, signature: Signature, } @@ -37,9 +146,22 @@ impl AsRef for TransactionSigned { } } +impl Deref for TransactionSigned { + type Target = Transaction; + + fn deref(&self) -> &Self::Target { + &self.transaction + } +} + impl TransactionSigned { /// Transaction signature. pub fn signature(&self) -> &Signature { &self.signature } + + /// Transaction hash. Used to identify transaction. + pub fn hash(&self) -> TxHash { + self.hash + } } diff --git a/crates/primitives/src/transaction/signature.rs b/crates/primitives/src/transaction/signature.rs index 6153b11ee..e0fd9d16a 100644 --- a/crates/primitives/src/transaction/signature.rs +++ b/crates/primitives/src/transaction/signature.rs @@ -1,9 +1,15 @@ -use crate::H256; +use crate::U256; -/// Signature still TODO -#[derive(Debug, Clone)] +/// Signature TODO +/// r, s: Values corresponding to the signature of the +/// transaction and used to determine the sender of +/// the transaction; formally Tr and Ts. This is expanded in Appendix F of yellow paper. +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Signature { - pub r: H256, - pub s: H256, + /// The R field of the signature; the point on the curve. + pub r: U256, + /// The S field of the signature; the point on the curve. + pub s: U256, + /// yParity: Signature Y parity; forma Ty pub y_parity: u8, }