mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
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:
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
128
crates/executor/src/revm_wrap.rs
Normal file
128
crates/executor/src/revm_wrap.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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>;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
|
||||
14
crates/primitives/src/transaction/access_list.rs
Normal file
14
crates/primitives/src/transaction/access_list.rs
Normal 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>;
|
||||
@ -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 call’s 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 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<Address>,
|
||||
/// 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<Address>,
|
||||
/// 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<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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user