Transaction type and some wrap over revm (#19)

* Transaction and wip db binding
* simple revm wrapper
* nits and small sig change
This commit is contained in:
rakita
2022-10-07 13:56:11 +02:00
committed by GitHub
parent a3a7f1317b
commit 95ed994fd1
11 changed files with 366 additions and 34 deletions

View File

@ -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<dyn ExecutorDb>,
pub db: Box<dyn ExecutorDb>,
/// Consensus
consensus: Box<dyn Consensus>,
pub consensus: Box<dyn Consensus>,
}
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)
}
}

View File

@ -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;

View File

@ -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<EmptyDB>;
/// 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<Option<revm::AccountInfo>, 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<revm::Bytecode, Self::Error> {
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<U256, Self::Error> {
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<H256, Self::Error> {
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();
}
}
}

View File

@ -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<Block>;
fn block(&self, _height: BlockNumber) -> Option<Block>;
/// Get storage.
fn get_storage(&self, account: Address, storage_key: StorageKey) -> Option<StorageValue>;
fn storage(&self, account: Address, storage_key: StorageKey) -> Option<StorageValue>;
/// Get basic account information.
fn basic_account(&self, adderss: Address) -> Option<Account>;
/// 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<H256>;
}

View File

@ -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,
}

View File

@ -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<Receipt>,
}
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<TransactionSigned>,
/// Block receipts.
pub receipts: Vec<Receipt>,
}
impl Deref for BlockLocked {
type Target = Header;
fn deref(&self) -> &Self::Target {
self.header.as_ref()
}
}

View File

@ -1,3 +1,5 @@
use std::ops::Deref;
use crate::{BlockNumber, Bytes, H160, H256, U256};
/// Block header
@ -87,6 +89,14 @@ impl AsRef<Header> 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 {

View File

@ -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;

View File

@ -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<H256>,
}
/// AccessList as defined in EIP-2930
pub type AccessList = Vec<AccessListItem>;

View File

@ -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<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 calls recipient or, for a contract creation
/// transaction, ∅, used here to denote the only member of B0 ; formally Tt.
to: Option<Address>,
/// A scalar value equal to the number of Wei to
/// be transferred to the message calls 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 calls recipient or, for a contract creation
/// transaction, ∅, used here to denote the only member of B0 ; formally Tt.
to: Option<Address>,
/// A scalar value equal to the number of Wei to
/// be transferred to the message calls 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 calls recipient or, for a contract creation
/// transaction, ∅, used here to denote the only member of B0 ; formally Tt.
to: Option<Address>,
/// A scalar value equal to the number of Wei to
/// be transferred to the message calls 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<Transaction> 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
}
}

View File

@ -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,
}