Skip to content

Commit

Permalink
refactor: use genesis state instead of genesis accounts (#755)
Browse files Browse the repository at this point in the history
  • Loading branch information
Wodann authored Dec 20, 2024
1 parent 7d71ee6 commit 53aa8bf
Show file tree
Hide file tree
Showing 30 changed files with 601 additions and 322 deletions.
8 changes: 6 additions & 2 deletions crates/edr_eth/src/beacon.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use revm_primitives::hex;

use crate::{address, Address};

/// The address of the beacon roots contract.
pub const BEACON_ROOTS_ADDRESS: &str = "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02";
pub const BEACON_ROOTS_ADDRESS: Address = address!("000F3df6D732807Ef1319fB7B8bB8522d0Beac02");

/// The bytecode of the beacon roots contract.
pub const BEACON_ROOTS_BYTECODE: &str = "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500";
pub const BEACON_ROOTS_BYTECODE: [u8; 97] = hex!("3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500");
13 changes: 5 additions & 8 deletions crates/edr_evm/src/blockchain/forked.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{collections::BTreeMap, fmt::Debug, num::NonZeroU64, str::FromStr, sync::Arc};
use std::{collections::BTreeMap, fmt::Debug, num::NonZeroU64, sync::Arc};

use derive_where::derive_where;
use edr_eth::{
Expand Down Expand Up @@ -201,19 +201,16 @@ impl<ChainSpecT: RuntimeSpec> ForkedBlockchain<ChainSpecT> {

if remote_hardfork.into() < l1::SpecId::CANCUN && hardfork.into() >= l1::SpecId::CANCUN
{
let beacon_roots_address =
Address::from_str(BEACON_ROOTS_ADDRESS).expect("Is valid address");
let beacon_roots_contract = Bytecode::new_raw(
Bytes::from_str(BEACON_ROOTS_BYTECODE).expect("Is valid bytecode"),
);
let beacon_roots_contract =
Bytecode::new_raw(Bytes::from_static(&BEACON_ROOTS_BYTECODE));

let state_root = state_root_generator.lock().next_value();

irregular_state
.state_override_at_block_number(fork_block_number)
.and_modify(|state_override| {
state_override.diff.apply_account_change(
beacon_roots_address,
BEACON_ROOTS_ADDRESS,
AccountInfo {
code_hash: beacon_roots_contract.hash_slow(),
code: Some(beacon_roots_contract.clone()),
Expand All @@ -223,7 +220,7 @@ impl<ChainSpecT: RuntimeSpec> ForkedBlockchain<ChainSpecT> {
})
.or_insert_with(|| {
let accounts: HashMap<Address, Account> = [(
beacon_roots_address,
BEACON_ROOTS_ADDRESS,
Account {
info: AccountInfo {
code_hash: beacon_roots_contract.hash_slow(),
Expand Down
26 changes: 3 additions & 23 deletions crates/edr_evm/src/blockchain/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,16 @@ use std::{
collections::BTreeMap,
fmt::Debug,
num::NonZeroU64,
str::FromStr,
sync::Arc,
time::{SystemTime, UNIX_EPOCH},
};

use derive_where::derive_where;
use edr_eth::{
account::AccountInfo,
beacon::{BEACON_ROOTS_ADDRESS, BEACON_ROOTS_BYTECODE},
block::{BlobGas, BlockOptions, PartialHeader},
l1,
log::FilterLog,
Address, Bytecode, Bytes, HashSet, B256, U256,
Address, Bytes, HashSet, B256, U256,
};

use super::{
Expand Down Expand Up @@ -102,34 +99,17 @@ where
#[cfg_attr(feature = "tracing", tracing::instrument(skip_all))]
#[allow(clippy::too_many_arguments)]
pub fn new(
mut genesis_diff: StateDiff,
genesis_diff: StateDiff,
chain_id: u64,
hardfork: ChainSpecT::Hardfork,
options: GenesisBlockOptions,
) -> Result<Self, CreationError> {
const EXTRA_DATA: &[u8] = b"\x12\x34";

let evm_spec_id = hardfork.into();
if evm_spec_id >= l1::SpecId::CANCUN {
let beacon_roots_address =
Address::from_str(BEACON_ROOTS_ADDRESS).expect("Is valid address");
let beacon_roots_contract = Bytecode::new_raw(
Bytes::from_str(BEACON_ROOTS_BYTECODE).expect("Is valid bytecode"),
);

genesis_diff.apply_account_change(
beacon_roots_address,
AccountInfo {
code_hash: beacon_roots_contract.hash_slow(),
code: Some(beacon_roots_contract),
..AccountInfo::default()
},
);
}

let mut genesis_state = TrieState::default();
genesis_state.commit(genesis_diff.clone().into());

let evm_spec_id = hardfork.into();
if evm_spec_id >= l1::SpecId::MERGE && options.mix_hash.is_none() {
return Err(CreationError::MissingPrevrandao);
}
Expand Down
2 changes: 1 addition & 1 deletion crates/edr_evm/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use edr_rpc_eth::client::RpcClientError;
use revm::DatabaseRef;
pub use revm::{
database_interface::{Database, DatabaseCommit as StateCommit, WrapDatabaseRef},
state::{EvmState, EvmStorageSlot},
state::{EvmState, EvmStorage, EvmStorageSlot},
};

pub use self::{
Expand Down
43 changes: 38 additions & 5 deletions crates/edr_napi/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,31 @@

/* auto-generated by NAPI-RS */

/** An account that needs to be created during the genesis block. */
export interface GenesisAccount {
/** A description of an account's state. */
export interface Account {
/** The account's address */
address: Uint8Array
/** The account's balance */
balance: bigint
/** The account's nonce */
nonce: bigint
/** The account's code */
code?: Uint8Array
/** The account's storage */
storage: Array<StorageSlot>
}
/** A description of a storage slot's state. */
export interface StorageSlot {
/** The storage slot's index */
index: bigint
/** The storage slot's value */
value: bigint
}
/**
* An owned account, for which the secret key is known, and its desired genesis
* balance.
*/
export interface OwnedAccount {
/** Account secret key */
secretKey: string
/** Account balance */
Expand Down Expand Up @@ -66,6 +89,14 @@ export interface CallOverrideResult {
export const GENERIC_CHAIN_TYPE: string
export declare function genericChainProviderFactory(): ProviderFactory
export const L1_CHAIN_TYPE: string
export declare function l1GenesisState(hardfork: SpecId): Array<Account>
/**
* Creates a new instance by matching the provided string.
*
* Defaults to `SpecId::Latest` if the string does not match any known
* hardfork.
*/
export declare function l1HardforkFromString(hardfork: string): SpecId
export declare function l1ProviderFactory(): ProviderFactory
/** Identifier for the Ethereum spec. */
export enum SpecId {
Expand Down Expand Up @@ -106,7 +137,7 @@ export enum SpecId {
/** Cancun */
Cancun = 17,
/** Latest */
Latest = 18
Latest = 2147483647
}
export const FRONTIER: string
export const FRONTIER_THAWING: string
Expand Down Expand Up @@ -207,8 +238,8 @@ export interface ProviderConfig {
* blockchain will be created
*/
fork?: ForkConfig
/** The genesis accounts of the blockchain */
genesisAccounts: Array<GenesisAccount>
/** The genesis state of the blockchain */
genesisState: Array<Account>
/** The hardfork of the blockchain */
hardfork: string
/**
Expand All @@ -231,6 +262,8 @@ export interface ProviderConfig {
mining: MiningConfig
/** The network ID of the blockchain */
networkId: bigint
/** Owned accounts, for which the secret key is known */
ownedAccounts: Array<OwnedAccount>
}
export interface DebugTraceResult {
pass: boolean
Expand Down
4 changes: 3 additions & 1 deletion crates/edr_napi/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,11 +310,13 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}

const { GENERIC_CHAIN_TYPE, genericChainProviderFactory, L1_CHAIN_TYPE, l1ProviderFactory, SpecId, FRONTIER, FRONTIER_THAWING, HOMESTEAD, DAO_FORK, TANGERINE, SPURIOUS_DRAGON, BYZANTIUM, CONSTANTINOPLE, PETERSBURG, ISTANBUL, MUIR_GLACIER, BERLIN, LONDON, ARROW_GLACIER, GRAY_GLACIER, MERGE, SHANGHAI, CANCUN, PRAGUE, PRAGUE_EOF, LATEST, MineOrdering, EdrContext, ProviderFactory, Response, Provider, SuccessReason, ExceptionalHalt, createModelsAndDecodeBytecodes, linkHexStringBytecode, BytecodeWrapper, ContractFunctionType, printMessageTrace, printStackTrace, Exit, ExitCode, ReturnData, StackTraceEntryType, stackTraceEntryTypeToString, FALLBACK_FUNCTION_NAME, RECEIVE_FUNCTION_NAME, CONSTRUCTOR_FUNCTION_NAME, UNRECOGNIZED_FUNCTION_NAME, UNKNOWN_FUNCTION_NAME, PRECOMPILE_FUNCTION_NAME, UNRECOGNIZED_CONTRACT_NAME, SolidityTracer, VmTraceDecoder, initializeVmTraceDecoder, VmTracer, RawTrace } = nativeBinding
const { GENERIC_CHAIN_TYPE, genericChainProviderFactory, L1_CHAIN_TYPE, l1GenesisState, l1HardforkFromString, l1ProviderFactory, SpecId, FRONTIER, FRONTIER_THAWING, HOMESTEAD, DAO_FORK, TANGERINE, SPURIOUS_DRAGON, BYZANTIUM, CONSTANTINOPLE, PETERSBURG, ISTANBUL, MUIR_GLACIER, BERLIN, LONDON, ARROW_GLACIER, GRAY_GLACIER, MERGE, SHANGHAI, CANCUN, PRAGUE, PRAGUE_EOF, LATEST, MineOrdering, EdrContext, ProviderFactory, Response, Provider, SuccessReason, ExceptionalHalt, createModelsAndDecodeBytecodes, linkHexStringBytecode, BytecodeWrapper, ContractFunctionType, printMessageTrace, printStackTrace, Exit, ExitCode, ReturnData, StackTraceEntryType, stackTraceEntryTypeToString, FALLBACK_FUNCTION_NAME, RECEIVE_FUNCTION_NAME, CONSTRUCTOR_FUNCTION_NAME, UNRECOGNIZED_FUNCTION_NAME, UNKNOWN_FUNCTION_NAME, PRECOMPILE_FUNCTION_NAME, UNRECOGNIZED_CONTRACT_NAME, SolidityTracer, VmTraceDecoder, initializeVmTraceDecoder, VmTracer, RawTrace } = nativeBinding

module.exports.GENERIC_CHAIN_TYPE = GENERIC_CHAIN_TYPE
module.exports.genericChainProviderFactory = genericChainProviderFactory
module.exports.L1_CHAIN_TYPE = L1_CHAIN_TYPE
module.exports.l1GenesisState = l1GenesisState
module.exports.l1HardforkFromString = l1HardforkFromString
module.exports.l1ProviderFactory = l1ProviderFactory
module.exports.SpecId = SpecId
module.exports.FRONTIER = FRONTIER
Expand Down
38 changes: 33 additions & 5 deletions crates/edr_napi/src/account.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,50 @@
use edr_eth::signature::secret_key_from_str;
use napi::{bindgen_prelude::BigInt, Status};
use napi::{
bindgen_prelude::{BigInt, Uint8Array},
Status,
};
use napi_derive::napi;

use crate::cast::TryCast as _;

/// An account that needs to be created during the genesis block.
/// A description of an account's state.
#[napi(object)]
pub struct GenesisAccount {
pub struct Account {
/// The account's address
pub address: Uint8Array,
/// The account's balance
pub balance: BigInt,
/// The account's nonce
pub nonce: BigInt,
/// The account's code
pub code: Option<Uint8Array>,
/// The account's storage
pub storage: Vec<StorageSlot>,
}

/// A description of a storage slot's state.
#[napi(object)]
pub struct StorageSlot {
/// The storage slot's index
pub index: BigInt,
/// The storage slot's value
pub value: BigInt,
}

/// An owned account, for which the secret key is known, and its desired genesis
/// balance.
#[napi(object)]
pub struct OwnedAccount {
/// Account secret key
pub secret_key: String,
/// Account balance
pub balance: BigInt,
}

impl TryFrom<GenesisAccount> for edr_provider::AccountConfig {
impl TryFrom<OwnedAccount> for edr_provider::config::OwnedAccount {
type Error = napi::Error;

fn try_from(value: GenesisAccount) -> Result<Self, Self::Error> {
fn try_from(value: OwnedAccount) -> Result<Self, Self::Error> {
let secret_key = secret_key_from_str(&value.secret_key)
.map_err(|e| napi::Error::new(Status::InvalidArg, e.to_string()))?;

Expand Down
32 changes: 30 additions & 2 deletions crates/edr_napi/src/cast.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use edr_eth::{Address, Bytes, B256, B64, U256};
use edr_eth::{Address, Bytecode, Bytes, B256, B64, U256};
use napi::{
bindgen_prelude::{BigInt, Buffer},
bindgen_prelude::{BigInt, Buffer, Uint8Array},
Status,
};

Expand Down Expand Up @@ -29,6 +29,20 @@ impl TryCast<Address> for Buffer {
}
}

impl TryCast<Address> for Uint8Array {
type Error = napi::Error;

fn try_cast(self) -> std::result::Result<Address, Self::Error> {
if self.len() != 20 {
return Err(napi::Error::new(
Status::InvalidArg,
"Uint8Array was expected to be 20 bytes.".to_string(),
));
}
Ok(Address::from_slice(&self))
}
}

impl TryCast<B64> for Buffer {
type Error = napi::Error;

Expand Down Expand Up @@ -57,6 +71,20 @@ impl TryCast<B256> for Buffer {
}
}

impl TryCast<Bytecode> for Uint8Array {
type Error = napi::Error;

fn try_cast(self) -> std::result::Result<Bytecode, Self::Error> {
let bytes = Bytes::copy_from_slice(&self);
Bytecode::new_raw_checked(bytes).map_err(|error| {
napi::Error::new(
Status::InvalidArg,
format!("Uint8Array was not valid bytecode: {error}"),
)
})
}
}

impl TryCast<u64> for BigInt {
type Error = napi::Error;

Expand Down
Loading

0 comments on commit 53aa8bf

Please sign in to comment.