Skip to content

Commit 02c402a

Browse files
authored
Pass all Berlin tests (#376)
* Berlin modexp gas cost * Handle create transaction access list * Access list is inside the substack * Fix some missing gas costs in access list * Mark hot for precompiles * Cargo fmt * Do not warm coinbase in Berlin * Fix state touch * Pass all berlin tests
1 parent 8de1ace commit 02c402a

File tree

13 files changed

+256
-150
lines changed

13 files changed

+256
-150
lines changed

fuzz/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ fn fuzz_transact(
223223
usize,
224224
usize,
225225
) {
226-
let mut overlayed_backend = OverlayedBackend::new(backend, Default::default(), &config.runtime);
226+
let mut overlayed_backend = OverlayedBackend::new(backend, &config.runtime);
227227
#[cfg(not(feature = "fuzzing"))]
228228
let now = std::time::Instant::now();
229229

interpreter/src/runtime.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ pub struct RuntimeConfig {
3434
pub eip7610_create_check_storage: bool,
3535
/// EIP-6780: selfdestruct deletet contract only if called in the same tx as creation
3636
pub eip6780_suicide_only_in_same_tx: bool,
37+
/// EIP-3651
38+
pub eip3651_warm_coinbase_address: bool,
3739
}
3840

3941
impl RuntimeConfig {
@@ -43,6 +45,7 @@ impl RuntimeConfig {
4345
eip161_empty_check: true,
4446
eip7610_create_check_storage: true,
4547
eip6780_suicide_only_in_same_tx: false,
48+
eip3651_warm_coinbase_address: false,
4649
}
4750
}
4851

@@ -52,6 +55,7 @@ impl RuntimeConfig {
5255
eip161_empty_check: false,
5356
eip6780_suicide_only_in_same_tx: false,
5457
eip7610_create_check_storage: true,
58+
eip3651_warm_coinbase_address: false,
5559
}
5660
}
5761
}

jsontests/src/run.rs

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use std::{
2-
collections::{BTreeMap, BTreeSet},
2+
collections::BTreeMap,
33
fs::{self, File},
44
io::{BufReader, BufWriter},
55
};
66

77
use evm::{
88
backend::{InMemoryAccount, InMemoryBackend, InMemoryEnvironment, OverlayedBackend},
9-
interpreter::{Capture, runtime::GasState, utils::u256_to_h256},
9+
interpreter::{Capture, runtime::GasState},
1010
standard::{Config, TransactArgs, TransactArgsCallCreate},
1111
};
1212
use evm_mainnet::with_mainnet_invoker;
@@ -186,6 +186,7 @@ pub fn run_test(
186186
Fork::Byzantium => Config::byzantium(),
187187
Fork::ConstantinopleFix => Config::petersburg(),
188188
Fork::Istanbul => Config::istanbul(),
189+
Fork::Berlin => Config::berlin(),
189190
_ => return Err(Error::UnsupportedFork),
190191
};
191192
config_change(&mut config);
@@ -296,23 +297,13 @@ pub fn run_test(
296297
}
297298
};
298299

299-
let initial_accessed = {
300-
let mut hots = BTreeSet::new();
301-
for i in 1..10 {
302-
hots.insert((u256_to_h256(U256::from(i)).into(), None));
303-
}
304-
hots
305-
};
306-
307300
let base_backend = InMemoryBackend {
308301
environment: env,
309302
state,
310303
};
311304

312-
let mut run_backend =
313-
OverlayedBackend::new(&base_backend, initial_accessed.clone(), &config.runtime);
314-
let mut step_backend =
315-
OverlayedBackend::new(&base_backend, initial_accessed.clone(), &config.runtime);
305+
let mut run_backend = OverlayedBackend::new(&base_backend, &config.runtime);
306+
let mut step_backend = OverlayedBackend::new(&base_backend, &config.runtime);
316307

317308
// Run
318309
let run_result = evm_mainnet::transact(args.clone(), &mut run_backend);

precompile/src/lib.rs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,15 @@ use crate::{
2929
Bn128AddByzantium, Bn128AddIstanbul, Bn128MulByzantium, Bn128MulIstanbul,
3030
Bn128PairingByzantium, Bn128PairingIstanbul,
3131
},
32-
modexp::Modexp,
32+
modexp::{ModexpBerlin, ModexpByzantium},
3333
simple::{ECRecover, Identity, Ripemd160, Sha256},
3434
};
3535
use evm::{
3636
GasMutState,
37-
interpreter::{ExitError, ExitException, ExitResult, runtime::RuntimeState},
37+
interpreter::{
38+
ExitError, ExitException, ExitResult,
39+
runtime::{RuntimeBackend, RuntimeState, TouchKind},
40+
},
3841
standard::{Config, PrecompileSet},
3942
};
4043
use primitive_types::H160;
@@ -46,9 +49,33 @@ trait PurePrecompile<G> {
4649
/// The standard precompile set on Ethereum mainnet.
4750
pub struct StandardPrecompileSet;
4851

49-
impl<G: AsRef<RuntimeState> + AsRef<Config> + GasMutState, H> PrecompileSet<G, H>
52+
impl<G: AsRef<RuntimeState> + AsRef<Config> + GasMutState, H: RuntimeBackend> PrecompileSet<G, H>
5053
for StandardPrecompileSet
5154
{
55+
fn on_transaction(&self, state: &mut G, handler: &mut H) {
56+
handler.mark_hot(address(1), TouchKind::Access);
57+
handler.mark_hot(address(2), TouchKind::Access);
58+
handler.mark_hot(address(3), TouchKind::Access);
59+
handler.mark_hot(address(4), TouchKind::Access);
60+
61+
if AsRef::<Config>::as_ref(state).eip198_modexp_precompile {
62+
handler.mark_hot(address(5), TouchKind::Access);
63+
}
64+
65+
if AsRef::<Config>::as_ref(state).eip196_ec_add_mul_precompile {
66+
handler.mark_hot(address(6), TouchKind::Access);
67+
handler.mark_hot(address(7), TouchKind::Access);
68+
}
69+
70+
if AsRef::<Config>::as_ref(state).eip197_ec_pairing_precompile {
71+
handler.mark_hot(address(8), TouchKind::Access);
72+
}
73+
74+
if AsRef::<Config>::as_ref(state).eip152_blake_2f_precompile {
75+
handler.mark_hot(address(9), TouchKind::Access);
76+
}
77+
}
78+
5279
fn execute(
5380
&self,
5481
code_address: H160,
@@ -67,7 +94,11 @@ impl<G: AsRef<RuntimeState> + AsRef<Config> + GasMutState, H> PrecompileSet<G, H
6794
} else if AsRef::<Config>::as_ref(gasometer).eip198_modexp_precompile
6895
&& code_address == address(5)
6996
{
70-
Some(Modexp.execute(input, gasometer))
97+
if AsRef::<Config>::as_ref(gasometer).eip2565_lower_modexp {
98+
Some(ModexpBerlin.execute(input, gasometer))
99+
} else {
100+
Some(ModexpByzantium.execute(input, gasometer))
101+
}
71102
} else if AsRef::<Config>::as_ref(gasometer).eip196_ec_add_mul_precompile
72103
&& code_address == address(6)
73104
{

precompile/src/modexp.rs

Lines changed: 70 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ use primitive_types::U256;
88

99
use crate::PurePrecompile;
1010

11-
pub struct Modexp;
12-
1311
fn modexp(base: &[u8], exponent: &[u8], modulus: &[u8]) -> Vec<u8> {
1412
aurora_engine_modexp::modexp(base, exponent, modulus)
1513
}
@@ -49,33 +47,13 @@ pub fn byzantium_gas_calc(base_len: u64, exp_len: u64, mod_len: u64, exp_highp:
4947

5048
/// Calculate gas cost according to EIP 2565:
5149
/// <https://eips.ethereum.org/EIPS/eip-2565>
52-
#[allow(unused)]
5350
fn berlin_gas_calc(base_len: u64, exp_len: u64, mod_len: u64, exp_highp: &U256) -> u64 {
5451
gas_calc::<200, 8, 3, _>(base_len, exp_len, mod_len, exp_highp, |max_len| -> U256 {
5552
let words = U256::from(max_len.div_ceil(8));
5653
words * words
5754
})
5855
}
5956

60-
/// Calculate gas cost according to EIP-7883:
61-
/// <https://eips.ethereum.org/EIPS/eip-7883>
62-
///
63-
/// There are three changes:
64-
/// 1. Increase minimal price from 200 to 500
65-
/// 2. Increase cost when exponent is larger than 32 bytes
66-
/// 3. Increase cost when base or modulus is larger than 32 bytes
67-
#[allow(unused)]
68-
fn osaka_gas_calc(base_len: u64, exp_len: u64, mod_len: u64, exp_highp: &U256) -> u64 {
69-
gas_calc::<500, 16, 1, _>(base_len, exp_len, mod_len, exp_highp, |max_len| -> U256 {
70-
if max_len <= 32 {
71-
return U256::from(16); // multiplication_complexity = 16
72-
}
73-
74-
let words = U256::from(max_len.div_ceil(8));
75-
words * words * U256::from(2) // multiplication_complexity = 2 * words**2
76-
})
77-
}
78-
7957
/// Calculate gas cost.
8058
fn gas_calc<const MIN_PRICE: u64, const MULTIPLIER: u64, const GAS_DIVISOR: u64, F>(
8159
base_len: u64,
@@ -162,63 +140,78 @@ pub fn left_pad_vec(data: &[u8], len: usize) -> Cow<'_, [u8]> {
162140
}
163141
}
164142

165-
impl<G: GasMutState> PurePrecompile<G> for Modexp {
166-
fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec<u8>) {
167-
// The format of input is:
168-
// <length_of_BASE> <length_of_EXPONENT> <length_of_MODULUS> <BASE> <EXPONENT> <MODULUS>
169-
// Where every length is a 32-byte left-padded integer representing the number of bytes
170-
// to be taken up by the next value.
171-
const HEADER_LENGTH: usize = 96;
172-
173-
// Extract the header
174-
let base_len = U256::from_big_endian(&right_pad_with_offset::<32>(input, 0).into_owned());
175-
let exp_len = U256::from_big_endian(&right_pad_with_offset::<32>(input, 32).into_owned());
176-
let mod_len = U256::from_big_endian(&right_pad_with_offset::<32>(input, 64).into_owned());
177-
178-
// Cast base and modulus to usize, it does not make sense to handle larger values
179-
let base_len = try_some!(usize::try_from(base_len).map_err(|_| ExitException::OutOfGas));
180-
let mod_len = try_some!(usize::try_from(mod_len).map_err(|_| ExitException::OutOfGas));
181-
// cast exp len to the max size, it will fail later in gas calculation if it is too large.
182-
let exp_len = usize::try_from(exp_len).unwrap_or(usize::MAX);
183-
184-
// TODO: Check Osaka size limit.
185-
186-
// Used to extract ADJUSTED_EXPONENT_LENGTH.
187-
let exp_highp_len = min(exp_len, 32);
188-
189-
// Throw away the header data as we already extracted lengths.
190-
let input = input.get(HEADER_LENGTH..).unwrap_or_default();
191-
192-
let exp_highp = {
193-
// Get right padded bytes so if data.len is less then exp_len we will get right padded zeroes.
194-
let right_padded_highp = right_pad_with_offset::<32>(input, base_len);
195-
// If exp_len is less then 32 bytes get only exp_len bytes and do left padding.
196-
let out = left_pad::<32>(&right_padded_highp[..exp_highp_len]);
197-
U256::from_big_endian(&out.into_owned())
198-
};
199-
200-
// Check if we have enough gas.
201-
let gas_cost =
202-
byzantium_gas_calc(base_len as u64, exp_len as u64, mod_len as u64, &exp_highp);
203-
try_some!(gasometer.record_gas(U256::from(gas_cost)));
204-
205-
if base_len == 0 && mod_len == 0 {
206-
return (ExitSucceed::Returned.into(), Vec::new());
207-
}
143+
fn execute<G: GasMutState>(
144+
input: &[u8],
145+
gasometer: &mut G,
146+
gas_calc: fn(u64, u64, u64, &U256) -> u64,
147+
) -> (ExitResult, Vec<u8>) {
148+
// The format of input is:
149+
// <length_of_BASE> <length_of_EXPONENT> <length_of_MODULUS> <BASE> <EXPONENT> <MODULUS>
150+
// Where every length is a 32-byte left-padded integer representing the number of bytes
151+
// to be taken up by the next value.
152+
const HEADER_LENGTH: usize = 96;
153+
154+
// Extract the header
155+
let base_len = U256::from_big_endian(&right_pad_with_offset::<32>(input, 0).into_owned());
156+
let exp_len = U256::from_big_endian(&right_pad_with_offset::<32>(input, 32).into_owned());
157+
let mod_len = U256::from_big_endian(&right_pad_with_offset::<32>(input, 64).into_owned());
158+
159+
// Cast base and modulus to usize, it does not make sense to handle larger values
160+
let base_len = try_some!(usize::try_from(base_len).map_err(|_| ExitException::OutOfGas));
161+
let mod_len = try_some!(usize::try_from(mod_len).map_err(|_| ExitException::OutOfGas));
162+
// cast exp len to the max size, it will fail later in gas calculation if it is too large.
163+
let exp_len = usize::try_from(exp_len).unwrap_or(usize::MAX);
164+
165+
// Used to extract ADJUSTED_EXPONENT_LENGTH.
166+
let exp_highp_len = min(exp_len, 32);
167+
168+
// Throw away the header data as we already extracted lengths.
169+
let input = input.get(HEADER_LENGTH..).unwrap_or_default();
170+
171+
let exp_highp = {
172+
// Get right padded bytes so if data.len is less then exp_len we will get right padded zeroes.
173+
let right_padded_highp = right_pad_with_offset::<32>(input, base_len);
174+
// If exp_len is less then 32 bytes get only exp_len bytes and do left padding.
175+
let out = left_pad::<32>(&right_padded_highp[..exp_highp_len]);
176+
U256::from_big_endian(&out.into_owned())
177+
};
178+
179+
// Check if we have enough gas.
180+
let gas_cost = gas_calc(base_len as u64, exp_len as u64, mod_len as u64, &exp_highp);
181+
try_some!(gasometer.record_gas(U256::from(gas_cost)));
182+
183+
if base_len == 0 && mod_len == 0 {
184+
return (ExitSucceed::Returned.into(), Vec::new());
185+
}
186+
187+
// Padding is needed if the input does not contain all 3 values.
188+
let input_len = base_len.saturating_add(exp_len).saturating_add(mod_len);
189+
let input = right_pad_vec(input, input_len);
190+
let (base, input) = input.split_at(base_len);
191+
let (exponent, modulus) = input.split_at(exp_len);
192+
debug_assert_eq!(modulus.len(), mod_len);
193+
194+
// Call the modexp.
195+
let output = modexp(base, exponent, modulus);
196+
197+
(
198+
ExitSucceed::Returned.into(),
199+
left_pad_vec(&output, mod_len).into_owned(),
200+
)
201+
}
208202

209-
// Padding is needed if the input does not contain all 3 values.
210-
let input_len = base_len.saturating_add(exp_len).saturating_add(mod_len);
211-
let input = right_pad_vec(input, input_len);
212-
let (base, input) = input.split_at(base_len);
213-
let (exponent, modulus) = input.split_at(exp_len);
214-
debug_assert_eq!(modulus.len(), mod_len);
203+
pub struct ModexpByzantium;
215204

216-
// Call the modexp.
217-
let output = modexp(base, exponent, modulus);
205+
impl<G: GasMutState> PurePrecompile<G> for ModexpByzantium {
206+
fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec<u8>) {
207+
execute(input, gasometer, byzantium_gas_calc)
208+
}
209+
}
218210

219-
(
220-
ExitSucceed::Returned.into(),
221-
left_pad_vec(&output, mod_len).into_owned(),
222-
)
211+
pub struct ModexpBerlin;
212+
213+
impl<G: GasMutState> PurePrecompile<G> for ModexpBerlin {
214+
fn execute(&self, input: &[u8], gasometer: &mut G) -> (ExitResult, Vec<u8>) {
215+
execute(input, gasometer, berlin_gas_calc)
223216
}
224217
}

0 commit comments

Comments
 (0)