From 76552e0fd89621bba32568034816aeef8c3f82df Mon Sep 17 00:00:00 2001 From: mandreyel Date: Mon, 20 Mar 2023 17:03:27 +0400 Subject: [PATCH] sync with upstream for shanghai changes --- ethjson/src/spec/spec.rs | 18 +++++++++++++- ethjson/src/vm.rs | 10 ++++++-- jsontests/res/ethtests | 2 +- jsontests/src/state.rs | 53 +++++++++++++++++++++++++++++++--------- jsontests/src/utils.rs | 6 ++--- jsontests/src/vm.rs | 19 ++++++++++++-- jsontests/tests/state.rs | 6 ++--- 7 files changed, 90 insertions(+), 24 deletions(-) diff --git a/ethjson/src/spec/spec.rs b/ethjson/src/spec/spec.rs index 634c1dd..c882721 100644 --- a/ethjson/src/spec/spec.rs +++ b/ethjson/src/spec/spec.rs @@ -42,8 +42,13 @@ pub enum ForkSpec { Istanbul, /// Berlin (#12,244,000, 2021-04-15) Berlin, - /// London (To be announced) + /// London (#12,965,000, 2021-08-05) London, + /// Paris - The Merge (#15,537,394, 2022-09-15) + Merge, + /// Shanghai (#17,034,870, 2023-04-12) + Shanghai, + /// Byzantium transition test-net EIP158ToByzantiumAt5, /// Homestead transition test-net @@ -58,6 +63,17 @@ pub enum ForkSpec { ConstantinopleFixToIstanbulAt5, } +impl ForkSpec { + /// Returns true if the fork is at or after the merge. + pub fn is_eth2(&self) -> bool { + // NOTE: Include new forks in this match arm. + matches!( + *self, + ForkSpec::London | ForkSpec::Merge | ForkSpec::Shanghai + ) + } +} + /// Spec deserialization. #[derive(Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] diff --git a/ethjson/src/vm.rs b/ethjson/src/vm.rs index 4578145..ca23153 100644 --- a/ethjson/src/vm.rs +++ b/ethjson/src/vm.rs @@ -121,6 +121,10 @@ pub struct Env { #[serde(rename = "currentBaseFee")] #[serde(default)] pub block_base_fee_per_gas: Uint, + /// Pre-seeded random value for testing + #[serde(rename = "currentRandom")] + #[serde(default)] + pub random: Option, } #[cfg(test)] @@ -145,7 +149,8 @@ mod tests { "currentDifficulty" : "0x0100", "currentGasLimit" : "0x0f4240", "currentNumber" : "0x00", - "currentTimestamp" : "0x01" + "currentTimestamp" : "0x01", + "currentRandom" : "0x01" }, "exec" : { "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", @@ -192,7 +197,8 @@ mod tests { gas_limit: Uint(0x0f4240.into()), number: Uint(0.into()), timestamp: Uint(1.into()), - block_base_fee_per_gas: Uint(0.into()) + block_base_fee_per_gas: Uint(0.into()), + random: Some(Uint(1.into())), } ); assert_eq!( diff --git a/jsontests/res/ethtests b/jsontests/res/ethtests index 6f41f7b..b25623d 160000 --- a/jsontests/res/ethtests +++ b/jsontests/res/ethtests @@ -1 +1 @@ -Subproject commit 6f41f7b6171f6da759060db177b440030dadd5b0 +Subproject commit b25623d4d7df10e38498cace7adc7eb413c4b20d diff --git a/jsontests/src/state.rs b/jsontests/src/state.rs index e2d2f5f..3d8a3a4 100644 --- a/jsontests/src/state.rs +++ b/jsontests/src/state.rs @@ -23,15 +23,15 @@ impl Test { } pub fn unwrap_caller(&self) -> H160 { - let hash: H256 = self.0.transaction.secret.clone().unwrap().into(); + let hash: H256 = self.0.transaction.secret.unwrap().into(); let mut secret_key = [0; 32]; - secret_key.copy_from_slice(&hash.as_bytes()[..]); + secret_key.copy_from_slice(hash.as_bytes()); let secret = SecretKey::parse(&secret_key); let public = libsecp256k1::PublicKey::from_secret_key(&secret.unwrap()); let mut res = [0u8; 64]; res.copy_from_slice(&public.serialize()[1..65]); - H160::from(H256::from_slice(Keccak256::digest(&res).as_slice())) + H160::from(H256::from_slice(Keccak256::digest(res).as_slice())) } pub fn unwrap_to_vicinity(&self, spec: &ForkSpec) -> Option { @@ -70,17 +70,36 @@ impl Test { return None; } + let block_randomness = if spec.is_eth2() { + self.0.env.random.map(|r| { + // Convert between U256 and H256. U256 is in little-endian but since H256 is just + // a string-like byte array, it's big endian (MSB is the first element of the array). + // + // Byte order here is important because this opcode has the same value as DIFFICULTY + // (0x44), and so for older forks of Ethereum, the threshold value of 2^64 is used to + // distinguish between the two: if it's below, the value corresponds to the DIFFICULTY + // opcode, otherwise to the PREVRANDAO opcode. + let mut buf = [0u8; 32]; + r.0.to_big_endian(&mut buf); + H256(buf) + }) + } else { + None + }; + + Some(MemoryVicinity { gas_price, origin: self.unwrap_caller(), block_hashes: Vec::new(), - block_number: self.0.env.number.clone().into(), - block_coinbase: self.0.env.author.clone().into(), - block_timestamp: self.0.env.timestamp.clone().into(), - block_difficulty: self.0.env.difficulty.clone().into(), - block_gas_limit: self.0.env.gas_limit.clone().into(), + block_number: self.0.env.number.into(), + block_coinbase: self.0.env.author.into(), + block_timestamp: self.0.env.timestamp.into(), + block_difficulty: self.0.env.difficulty.into(), + block_gas_limit: self.0.env.gas_limit.into(), chain_id: U256::one(), block_base_fee_per_gas, + block_randomness, }) } } @@ -139,6 +158,10 @@ impl JsonPrecompile { } // precompiles for London and Berlin are the same ForkSpec::London => Self::precompile(&ForkSpec::Berlin), + // precompiles for Merge and Berlin are the same + ForkSpec::Merge => Self::precompile(&ForkSpec::Berlin), + // precompiles for Shanghai and Berlin are the same + ForkSpec::Shanghai => Self::precompile(&ForkSpec::Berlin), _ => None, } } @@ -211,8 +234,10 @@ fn test_run(name: &str, test: Test) { ethjson::spec::ForkSpec::Istanbul => (Config::istanbul(), true), ethjson::spec::ForkSpec::Berlin => (Config::berlin(), true), ethjson::spec::ForkSpec::London => (Config::london(), true), + ethjson::spec::ForkSpec::Merge => (Config::merge(), true), + ethjson::spec::ForkSpec::Shanghai => (Config::shanghai(), true), spec => { - println!("Skip spec {:?}", spec); + println!("Skip spec {spec:?}"); continue; } }; @@ -288,8 +313,9 @@ fn test_run(name: &str, test: Test) { } let actual_fee = executor.fee(vicinity.gas_price); - let mniner_reward = if let ForkSpec::London = spec { - // see EIP-1559 + // Forks after London burn miner rewards and thus have different gas fee + // calculation (see EIP-1559) + let miner_reward = if spec.is_eth2() { let max_priority_fee_per_gas = test.0.transaction.max_priority_fee_per_gas(); let max_fee_per_gas = test.0.transaction.max_fee_per_gas(); let base_fee_per_gas = vicinity.block_base_fee_per_gas; @@ -299,11 +325,14 @@ fn test_run(name: &str, test: Test) { } else { actual_fee }; + executor .state_mut() - .deposit(vicinity.block_coinbase, mniner_reward); + .deposit(vicinity.block_coinbase, miner_reward); executor.state_mut().deposit(caller, total_fee - actual_fee); + let (values, logs) = executor.into_state().deconstruct(); + backend.apply(values, logs, delete_empty); } diff --git a/jsontests/src/utils.rs b/jsontests/src/utils.rs index 38b52db..60bbd39 100644 --- a/jsontests/src/utils.rs +++ b/jsontests/src/utils.rs @@ -144,9 +144,9 @@ pub fn assert_valid_hash(h: &H256, b: &BTreeMap) { .collect::>(); let root = ethereum::util::sec_trie_root(tree); - let expect = h.clone().into(); + let expect = h; - if root != expect { + if root != *expect { panic!( "Hash not equal; calculated: {:?}, expect: {:?}\nState: {:#x?}", root, expect, b @@ -157,7 +157,7 @@ pub fn assert_valid_hash(h: &H256, b: &BTreeMap) { pub fn flush() { use std::io::{self, Write}; - io::stdout().flush().ok().expect("Could not flush stdout"); + io::stdout().flush().expect("Could not flush stdout"); } pub mod transaction { diff --git a/jsontests/src/vm.rs b/jsontests/src/vm.rs index 13e85b8..8fe211a 100644 --- a/jsontests/src/vm.rs +++ b/jsontests/src/vm.rs @@ -2,7 +2,7 @@ use crate::utils::*; use evm::backend::{ApplyBackend, MemoryAccount, MemoryBackend, MemoryVicinity}; use evm::executor::stack::{MemoryStackState, StackExecutor, StackSubstateMetadata}; use evm::Config; -use primitive_types::{H160, U256}; +use primitive_types::{H160, H256, U256}; use serde::Deserialize; use std::collections::BTreeMap; use std::rc::Rc; @@ -16,6 +16,19 @@ impl Test { } pub fn unwrap_to_vicinity(&self) -> MemoryVicinity { + let block_randomness = self.0.env.random.map(|r| { + // Convert between U256 and H256. U256 is in little-endian but since H256 is just + // a string-like byte array, it's big endian (MSB is the first element of the array). + // + // Byte order here is important because this opcode has the same value as DIFFICULTY + // (0x44), and so for older forks of Ethereum, the threshold value of 2^64 is used to + // distinguish between the two: if it's below, the value corresponds to the DIFFICULTY + // opcode, otherwise to the PREVRANDAO opcode. + let mut buf = [0u8; 32]; + r.0.to_big_endian(&mut buf); + H256(buf) + }); + MemoryVicinity { gas_price: self.0.transaction.gas_price.clone().into(), origin: self.0.transaction.origin.clone().into(), @@ -27,6 +40,7 @@ impl Test { block_gas_limit: self.0.env.gas_limit.clone().into(), chain_id: U256::zero(), block_base_fee_per_gas: self.0.transaction.gas_price.clone().into(), + block_randomness, } } @@ -75,7 +89,8 @@ pub fn test(name: &str, test: Test) { let code = test.unwrap_to_code(); let data = test.unwrap_to_data(); let context = test.unwrap_to_context(); - let mut runtime = evm::Runtime::new(code, data, context, config.stack_limit, config.memory_limit); + let mut runtime = + evm::Runtime::new(code, data, context, config.stack_limit, config.memory_limit); let reason = executor.execute(&mut runtime); let gas = executor.gas(); diff --git a/jsontests/tests/state.rs b/jsontests/tests/state.rs index e38a1cb..ebefc3b 100644 --- a/jsontests/tests/state.rs +++ b/jsontests/tests/state.rs @@ -13,7 +13,7 @@ pub fn run(dir: &str) { for entry in fs::read_dir(dest).unwrap() { let entry = entry.unwrap(); if let Some(s) = entry.file_name().to_str() { - if s.starts_with(".") { + if s.starts_with('.') { continue; } } @@ -23,8 +23,8 @@ pub fn run(dir: &str) { let file = File::open(path).expect("Open file failed"); let reader = BufReader::new(file); - let coll = serde_json::from_reader::<_, HashMap>(reader) - .expect("Parse test cases failed"); + let coll: HashMap = + serde_json::from_reader(reader).expect("Parse test cases failed"); for (name, test) in coll { statetests::test(&name, test);