Skip to content

Commit a6864ff

Browse files
authored
feat: add local predeploys for soltests (#969)
1 parent 2af8310 commit a6864ff

File tree

22 files changed

+322
-73
lines changed

22 files changed

+322
-73
lines changed

.changeset/fluffy-bags-tickle.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@nomicfoundation/edr": patch
3+
---
4+
5+
Added support for local pre-deploys for Solidity tests.

.github/workflows/edr-ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ jobs:
200200
run: pnpm install --frozen-lockfile --prefer-offline
201201

202202
- name: Build edr_napi
203-
run: cd crates/edr_napi && pnpm build:dev
203+
run: cd crates/edr_napi && pnpm build:typingFile
204204

205205
- name: Check that there are no uncommitted changes
206206
run: git diff --exit-code

crates/edr_napi/index.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,14 @@ export interface SolidityTestRunnerConfigArgs {
590590
* Defaults to 33_554_432 (2^25 = 32MiB).
591591
*/
592592
memoryLimit?: bigint
593+
/**
594+
* The predeploys applied in local mode. Defaults to no predeploys.
595+
* These should match the predeploys of the network in fork mode, so they
596+
* aren't set in fork mode.
597+
* The code must be set and non-empty. The nonce and the balance default to
598+
* zero and storage defaults to empty.
599+
*/
600+
localPredeploys?: Array<AccountOverride>
593601
/**
594602
* If set, all tests are run in fork mode using this url or remote name.
595603
* Defaults to none.

crates/edr_napi/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,18 +54,18 @@
5454
"scripts": {
5555
"artifacts": "napi artifacts",
5656
"build": "pnpm run build:publish",
57-
"build:debug": "napi build --platform --no-const-enum",
58-
"build:dev": "napi build --platform --release --no-const-enum",
59-
"build:op": "napi build --platform --release --no-const-enum --features op",
57+
"build:debug": "napi build --platform --no-const-enum --features op",
58+
"build:dev": "napi build --platform --release --no-const-enum --features op",
6059
"build:publish": "napi build --platform --profile napi-publish --no-const-enum ",
6160
"build:scenarios": "napi build --platform --release --no-const-enum --features scenarios",
6261
"build:tracing": "napi build --platform --release --no-const-enum --features tracing",
62+
"build:typingFile": "napi build --platform --no-const-enum",
6363
"clean": "rm -rf @nomicfoundation/edr.node",
6464
"eslint": "eslint 'test/**/*.ts'",
6565
"lint": "pnpm run prettier && pnpm run eslint",
6666
"lint:fix": "pnpm run prettier --write",
6767
"prepublishOnly": "bash ../../scripts/prepublish.sh",
68-
"pretest": "pnpm build:op",
68+
"pretest": "pnpm build:dev",
6969
"prettier": "prettier --check \"test/**.ts\"",
7070
"test": "pnpm tsc && node --max-old-space-size=8192 node_modules/mocha/bin/_mocha --recursive \"test/**/*.ts\"",
7171
"testNoBuild": "pnpm tsc && node --max-old-space-size=8192 node_modules/mocha/bin/_mocha --recursive \"test/**/{,!(op)}.ts\"",

crates/edr_napi/src/account.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,120 @@
1+
use edr_eth::{HashMap, U256};
2+
use edr_solidity_tests::{backend::Predeploy, revm::state::AccountInfo};
13
use napi::bindgen_prelude::{BigInt, Uint8Array};
24
use napi_derive::napi;
35

6+
use crate::{
7+
cast::TryCast,
8+
serde::{
9+
serialize_bigint_as_struct, serialize_optional_bigint_as_struct,
10+
serialize_optional_uint8array_as_hex, serialize_uint8array_as_hex,
11+
},
12+
};
13+
414
/// Specification of overrides for an account and its storage.
515
#[napi(object)]
16+
#[derive(Clone, serde::Serialize)]
617
pub struct AccountOverride {
718
/// The account's address
19+
#[serde(serialize_with = "serialize_uint8array_as_hex")]
820
pub address: Uint8Array,
921
/// If present, the overwriting balance.
22+
#[serde(serialize_with = "serialize_optional_bigint_as_struct")]
1023
pub balance: Option<BigInt>,
1124
/// If present, the overwriting nonce.
25+
#[serde(serialize_with = "serialize_optional_bigint_as_struct")]
1226
pub nonce: Option<BigInt>,
1327
/// If present, the overwriting code.
28+
#[serde(serialize_with = "serialize_optional_uint8array_as_hex")]
1429
pub code: Option<Uint8Array>,
1530
/// BEWARE: This field is not supported yet. See <https://github.com/NomicFoundation/edr/issues/911>
1631
///
1732
/// If present, the overwriting storage.
1833
pub storage: Option<Vec<StorageSlot>>,
1934
}
2035

36+
impl TryFrom<AccountOverride> for (edr_eth::Address, edr_provider::AccountOverride) {
37+
type Error = napi::Error;
38+
39+
fn try_from(value: AccountOverride) -> Result<Self, Self::Error> {
40+
let AccountOverride {
41+
address,
42+
balance,
43+
nonce,
44+
code,
45+
storage,
46+
} = value;
47+
let storage = storage
48+
.map(|storage| {
49+
storage
50+
.into_iter()
51+
.map(|StorageSlot { index, value }| {
52+
let value = value.try_cast()?;
53+
let slot = edr_evm::state::EvmStorageSlot::new(value);
54+
55+
let index: edr_eth::U256 = index.try_cast()?;
56+
Ok((index, slot))
57+
})
58+
.collect::<napi::Result<_>>()
59+
})
60+
.transpose()?;
61+
62+
let account_override = edr_provider::AccountOverride {
63+
balance: balance.map(TryCast::try_cast).transpose()?,
64+
nonce: nonce.map(TryCast::try_cast).transpose()?,
65+
code: code.map(TryCast::try_cast).transpose()?,
66+
storage,
67+
};
68+
69+
let address: edr_eth::Address = address.try_cast()?;
70+
71+
Ok((address, account_override))
72+
}
73+
}
74+
75+
impl TryFrom<AccountOverride> for Predeploy {
76+
type Error = napi::Error;
77+
78+
fn try_from(value: AccountOverride) -> Result<Self, Self::Error> {
79+
let (address, account_override) = value.try_into()?;
80+
81+
let storage = account_override.storage.unwrap_or_else(HashMap::new);
82+
let balance = account_override.balance.unwrap_or(U256::ZERO);
83+
let nonce = account_override.nonce.unwrap_or(0);
84+
let code = account_override.code.ok_or_else(|| {
85+
napi::Error::from_reason(format!("Predeploy with address '{address}' must have code"))
86+
})?;
87+
88+
if code.is_empty() {
89+
return Err(napi::Error::from_reason(
90+
"Predeploy with address '{address}' must have non-empty code",
91+
));
92+
}
93+
let code_hash = code.hash_slow();
94+
95+
let account_info = AccountInfo {
96+
balance,
97+
nonce,
98+
code_hash,
99+
code: Some(code),
100+
};
101+
102+
Ok(Self {
103+
address,
104+
account_info,
105+
storage,
106+
})
107+
}
108+
}
109+
21110
/// A description of a storage slot's state.
22111
#[napi(object)]
112+
#[derive(Clone, serde::Serialize)]
23113
pub struct StorageSlot {
24114
/// The storage slot's index
115+
#[serde(serialize_with = "serialize_bigint_as_struct")]
25116
pub index: BigInt,
26117
/// The storage slot's value
118+
#[serde(serialize_with = "serialize_bigint_as_struct")]
27119
pub value: BigInt,
28120
}

crates/edr_napi/src/config.rs

Lines changed: 4 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::{
77

88
use edr_eth::{
99
signature::{secret_key_from_str, SecretKey},
10-
Bytes, HashSet,
10+
Bytes, HashMap, HashSet,
1111
};
1212
use edr_provider::coverage::SyncOnCollectedCoverageCallback;
1313
use napi::{
@@ -19,12 +19,7 @@ use napi::{
1919
};
2020
use napi_derive::napi;
2121

22-
use crate::{
23-
account::{AccountOverride, StorageSlot},
24-
block::BlobGas,
25-
cast::TryCast,
26-
precompile::Precompile,
27-
};
22+
use crate::{account::AccountOverride, block::BlobGas, cast::TryCast, precompile::Precompile};
2823

2924
/// Specification of a chain with possible overrides.
3025
#[napi(object)]
@@ -437,42 +432,8 @@ impl ProviderConfig {
437432
let genesis_state = self
438433
.genesis_state
439434
.into_iter()
440-
.map(
441-
|AccountOverride {
442-
address,
443-
balance,
444-
nonce,
445-
code,
446-
storage,
447-
}| {
448-
let storage = storage
449-
.map(|storage| {
450-
storage
451-
.into_iter()
452-
.map(|StorageSlot { index, value }| {
453-
let value = value.try_cast()?;
454-
let slot = edr_evm::state::EvmStorageSlot::new(value);
455-
456-
let index: edr_eth::U256 = index.try_cast()?;
457-
Ok((index, slot))
458-
})
459-
.collect::<napi::Result<_>>()
460-
})
461-
.transpose()?;
462-
463-
let account_override = edr_provider::AccountOverride {
464-
balance: balance.map(TryCast::try_cast).transpose()?,
465-
nonce: nonce.map(TryCast::try_cast).transpose()?,
466-
code: code.map(TryCast::try_cast).transpose()?,
467-
storage,
468-
};
469-
470-
let address: edr_eth::Address = address.try_cast()?;
471-
472-
Ok((address, account_override))
473-
},
474-
)
475-
.collect::<napi::Result<_>>()?;
435+
.map(TryInto::try_into)
436+
.collect::<napi::Result<HashMap<edr_eth::Address, edr_provider::AccountOverride>>>()?;
476437

477438
let precompile_overrides = self
478439
.precompile_overrides

crates/edr_napi/src/solidity_tests/config.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use napi::{
1515
use napi_derive::napi;
1616

1717
use crate::{
18+
account::AccountOverride,
1819
cast::TryCast,
1920
serde::{
2021
serialize_optional_bigint_as_struct, serialize_optional_uint8array_as_hex,
@@ -102,6 +103,12 @@ pub struct SolidityTestRunnerConfigArgs {
102103
/// Defaults to 33_554_432 (2^25 = 32MiB).
103104
#[serde(serialize_with = "serialize_optional_bigint_as_struct")]
104105
pub memory_limit: Option<BigInt>,
106+
/// The predeploys applied in local mode. Defaults to no predeploys.
107+
/// These should match the predeploys of the network in fork mode, so they
108+
/// aren't set in fork mode.
109+
/// The code must be set and non-empty. The nonce and the balance default to
110+
/// zero and storage defaults to empty.
111+
pub local_predeploys: Option<Vec<AccountOverride>>,
105112
/// If set, all tests are run in fork mode using this url or remote name.
106113
/// Defaults to none.
107114
pub eth_rpc_url: Option<String>,
@@ -194,6 +201,7 @@ impl TryFrom<SolidityTestRunnerConfigArgs> for edr_napi_core::solidity::config::
194201
block_gas_limit,
195202
disable_block_gas_limit,
196203
memory_limit,
204+
local_predeploys,
197205
eth_rpc_url,
198206
rpc_cache_path,
199207
fork_block_number,
@@ -216,6 +224,15 @@ impl TryFrom<SolidityTestRunnerConfigArgs> for edr_napi_core::solidity::config::
216224
.transpose()?,
217225
};
218226

227+
let local_predeploys = local_predeploys
228+
.map(|local_predeploys| {
229+
local_predeploys
230+
.into_iter()
231+
.map(TryInto::try_into)
232+
.collect::<Result<Vec<_>, _>>()
233+
})
234+
.transpose()?;
235+
219236
let invariant: InvariantConfig = fuzz
220237
.as_ref()
221238
.map(|f| invariant.clone().unwrap_or_default().defaults_from_fuzz(f))
@@ -278,6 +295,7 @@ impl TryFrom<SolidityTestRunnerConfigArgs> for edr_napi_core::solidity::config::
278295
block_gas_limit: block_gas_limit.map(TryCast::try_cast).transpose()?,
279296
disable_block_gas_limit,
280297
memory_limit: memory_limit.map(TryCast::try_cast).transpose()?,
298+
local_predeploys,
281299
fork_url: eth_rpc_url,
282300
fork_block_number: fork_block_number.map(TryCast::try_cast).transpose()?,
283301
cheatcode,

crates/edr_napi/src/solidity_tests/l1.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ impl SyncTestRunnerFactory for L1TestRunnerFactory {
4242
edr_eth::l1::InvalidTransaction,
4343
TxEnv,
4444
>::new(
45-
config.into(),
45+
config.try_into()?,
4646
contracts,
4747
known_contracts,
4848
libs_to_deploy,

crates/edr_napi/src/solidity_tests/op.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ impl SyncTestRunnerFactory for OpTestRunnerFactory {
4343
edr_op::transaction::InvalidTransaction,
4444
edr_op::transaction::OpTxEnv<edr_eth::l1::TxEnv>,
4545
>::new(
46-
config.into(),
46+
config.try_into()?,
4747
contracts,
4848
known_contracts,
4949
libs_to_deploy,

crates/edr_napi_core/src/solidity/config.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::path::PathBuf;
22

33
use edr_eth::{Address, U256};
44
use edr_solidity_tests::{
5+
backend::Predeploy,
56
evm_context::HardforkTr,
67
fuzz::{invariant::InvariantConfig, FuzzConfig},
78
inspectors::cheatcodes::CheatsConfigOptions,
@@ -144,6 +145,10 @@ pub struct TestRunnerConfig {
144145
/// The memory limit of the EVM in bytes.
145146
/// Defaults to `33_554_432` (2^25 = 32MiB).
146147
pub memory_limit: Option<u64>,
148+
/// The predeploys applied in local mode. Defaults to no predeploys.
149+
/// These should match the predeploys of the network in fork mode, so they
150+
/// aren't set in fork mode.
151+
pub local_predeploys: Option<Vec<Predeploy>>,
147152
/// If set, all tests are run in fork mode using this url or remote name.
148153
/// Defaults to none.
149154
pub fork_url: Option<String>,
@@ -165,8 +170,10 @@ pub struct TestRunnerConfig {
165170
pub test_pattern: TestFilterConfig,
166171
}
167172

168-
impl<HardforkT: HardforkTr> From<TestRunnerConfig> for SolidityTestRunnerConfig<HardforkT> {
169-
fn from(value: TestRunnerConfig) -> Self {
173+
impl<HardforkT: HardforkTr> TryFrom<TestRunnerConfig> for SolidityTestRunnerConfig<HardforkT> {
174+
type Error = napi::Error;
175+
176+
fn try_from(value: TestRunnerConfig) -> Result<Self, Self::Error> {
170177
let TestRunnerConfig {
171178
project_root,
172179
test_fail,
@@ -186,6 +193,7 @@ impl<HardforkT: HardforkTr> From<TestRunnerConfig> for SolidityTestRunnerConfig<
186193
block_gas_limit,
187194
disable_block_gas_limit,
188195
memory_limit,
196+
local_predeploys,
189197
fork_url,
190198
fork_block_number,
191199
cheatcode: cheats_config_options,
@@ -259,18 +267,21 @@ impl<HardforkT: HardforkTr> From<TestRunnerConfig> for SolidityTestRunnerConfig<
259267
evm_opts.disable_block_gas_limit = disable_block_gas_limit;
260268
}
261269

262-
SolidityTestRunnerConfig {
270+
let local_predeploys = local_predeploys.unwrap_or_default();
271+
272+
Ok(SolidityTestRunnerConfig {
263273
project_root,
264274
include_traces,
265275
test_fail,
266276
// TODO
267277
coverage: false,
268278
cheats_config_options,
269279
evm_opts,
280+
local_predeploys,
270281
fuzz,
271282
invariant,
272283
// Solidity fuzz fixtures are not supported by the JS backend
273284
solidity_fuzz_fixtures: false,
274-
}
285+
})
275286
}
276287
}

0 commit comments

Comments
 (0)