diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 99739ab6..b21ecb79 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -24,8 +24,8 @@ jobs: run: cargo clippy --no-default-features -- -D clippy::all -D clippy::nursery - name: Clippy with features run: cargo clippy --features tracing,create-fixed -- -D clippy::all -D clippy::nursery - - name: Clippy with features for evm-jsontests - run: cargo clippy -p evm-jsontests --features dump-state -- -D clippy::all -D clippy::nursery + - name: Clippy with features for aurora-evm-jsontests + run: cargo clippy -p aurora-evm-jsontests --features dump-state -- -D clippy::all -D clippy::nursery build: runs-on: ubuntu-latest diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 802b4651..09953f65 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -76,7 +76,7 @@ jobs: - name: Run Ethereum state tests run: | - cargo run -r -p evm-jsontests -F enable-slow-tests -- state -f \ + cargo run -r -p aurora-evm-jsontests -F enable-slow-tests -- state -f \ ethtests/GeneralStateTests/ \ ethereum-spec-tests-static/fixtures/state_tests/ \ ethtests/LegacyTests/Cancun/GeneralStateTests/ \ @@ -85,7 +85,7 @@ jobs: - name: Run Ethereum vm tests run: | - cargo run -r -p evm-jsontests -F enable-slow-tests -- vm -f \ + cargo run -r -p aurora-evm-jsontests -F enable-slow-tests -- vm -f \ ethtests/LegacyTests/Constantinople/VMTests/vmArithmeticTest \ ethtests/LegacyTests/Constantinople/VMTests/vmBitwiseLogicOperation \ ethtests/LegacyTests/Constantinople/VMTests/vmBlockInfoTest \ @@ -138,14 +138,14 @@ jobs: mkdir ethereum-spec-tests-static tar -xzf ethereum-spec-tests-static.tar.gz -C ethereum-spec-tests-static - cargo run -r -p evm-jsontests -F enable-slow-tests -- state -f \ + cargo run -r -p aurora-evm-jsontests -F enable-slow-tests -- state -f \ ethtests/GeneralStateTests/ \ ethereum-spec-tests-static/fixtures/state_tests/ \ ethtests/LegacyTests/Cancun/GeneralStateTests/ \ ethereum-spec-tests/fixtures/state_tests/ \ ethereum-spec-tests2/fixtures/state_tests/ - cargo run -r -p evm-jsontests -F enable-slow-tests -- vm -f \ + cargo run -r -p aurora-evm-jsontests -F enable-slow-tests -- vm -f \ ethtests/LegacyTests/Constantinople/VMTests/vmArithmeticTest \ ethtests/LegacyTests/Constantinople/VMTests/vmBitwiseLogicOperation \ ethtests/LegacyTests/Constantinople/VMTests/vmBlockInfoTest \ diff --git a/.gitignore b/.gitignore index f16e0361..2b47df9b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ target **/result tests.bin -Cargo.lock \ No newline at end of file +Cargo.lock +ethtests/ diff --git a/Cargo.toml b/Cargo.toml index 51426a6e..8932f4d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,13 +2,13 @@ resolver = "2" members = [ "evm", - "evm-tests/jsontests" + "evm-tests" ] [workspace.package] authors = ["Aurora Labs "] edition = "2021" -version = "2.1.3" +version = "2.2.0" description = "Aurora Ethereum Virtual Machine implementation written in pure Rust" categories = ["no-std", "compilers", "cryptography::cryptocurrencies"] keywords = ["aurora-evm", "evm", "ethereum", "blockchain", "no_std"] @@ -22,4 +22,8 @@ auto_impl = "1.0" primitive-types = { version = "0.13", default-features = false } rlp = { version = "0.6", default-features = false, features = ["derive"] } sha3 = { version = "0.10", default-features = false } -serde = { version = "1.0", features = ["derive"] } +serde = { version = "1.0", default-features = false } + +[patch.crates-io] +# Constrain to v0.14.6 to ensure compatibility with sha3 0.10 and prevent unexpected upgrades +generic-array = { git = "https://github.com/fizyk20/generic-array", rev = "effb209dd7e61b8226e194dec2979314e27598d1" } diff --git a/evm-tests/Cargo.toml b/evm-tests/Cargo.toml new file mode 100644 index 00000000..c9117f4b --- /dev/null +++ b/evm-tests/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "aurora-evm-jsontests" +version.workspace = true +license = "MIT" +authors.workspace = true +description = "Aurora EVM json tests" +repository.workspace = true +keywords.workspace = true +edition.workspace = true +publish = false + +[lints.clippy] +all = { level = "deny", priority = -1 } +pedantic = { level = "deny", priority = -1 } +nursery = { level = "deny", priority = -1 } + +[dependencies] +aurora-engine-modexp = { git = "https://github.com/aurora-is-near/aurora-engine.git", tag = "3.10.0-rc.1" } +aurora-engine-precompiles = { git = "https://github.com/aurora-is-near/aurora-engine.git", tag = "3.10.0-rc.1" } +aurora-evm.workspace = true +bytecount = "0.6" +clap = { version = "4.5", features = ["cargo"] } +c-kzg = "1.0" +derive_more = { version = "2", features = ["full"] } +ethereum = "0.18" +hex = "0.4" +hex-literal = "1.1" +libsecp256k1 = "0.7" +primitive-types = { workspace = true, features = ["serde"] } +rlp.workspace = true +sha2 = { version = "0.10.0", default-features = false } +sha3.workspace = true +serde.workspace = true +serde_json = "1.0" + +[features] +enable-slow-tests = [] +print-debug = ["aurora-evm/print-debug"] +dump-state = ["aurora-evm/with-serde"] diff --git a/evm-tests/EIP-152/Cargo.toml b/evm-tests/EIP-152/Cargo.toml deleted file mode 100644 index bafe6573..00000000 --- a/evm-tests/EIP-152/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "eip-152" -version = "0.1.0" -authors = ["Parity Technologies "] -repository = "https://github.com/openethereum/openethereum" -documentation = "https://docs.rs/eip-152" -readme = "README.md" -description = "eip-512 blake2 F compression function" -keywords = ["eip-152", "eip152", "eip"] -license = "GPL-3.0" -edition = "2018" - -[dependencies] -arrayref = "0.3.5" - -[dev-dependencies] -criterion = "0.3" -rustc-hex = "2.1.0" - -[[bench]] -name = "bench" -harness = false diff --git a/evm-tests/EIP-152/LICENSE b/evm-tests/EIP-152/LICENSE deleted file mode 100644 index 09df722d..00000000 --- a/evm-tests/EIP-152/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -This program is copyright 2020 Parity Technologies Limited and its licensors. - - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - -Some portions of the program (“the Software”) are Copyright (c) 2018 Jack O'Connor -and the following relates solely to such portions: - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/evm-tests/EIP-152/benches/bench.rs b/evm-tests/EIP-152/benches/bench.rs deleted file mode 100644 index ccdd8c6c..00000000 --- a/evm-tests/EIP-152/benches/bench.rs +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; -use eip_152::portable; - -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -mod avx { - use criterion::{black_box, BenchmarkId, Criterion, Throughput}; - use std::mem; - use std::sync::atomic::{AtomicPtr, Ordering}; - - use eip_152::{avx2, portable}; - - type FnRaw = *mut (); - type Blake2bF = fn(&mut [u64; 8], [u64; 16], [u64; 2], bool, usize); - - static FN: AtomicPtr<()> = AtomicPtr::new(detect as FnRaw); - - fn detect(state: &mut [u64; 8], message: [u64; 16], count: [u64; 2], f: bool, rounds: usize) { - let fun = if is_x86_feature_detected!("avx2") { - avx2::compress as FnRaw - } else { - portable::compress as FnRaw - }; - FN.store(fun as FnRaw, Ordering::Relaxed); - unsafe { mem::transmute::(fun)(state, message, count, f, rounds) } - } - - pub fn avx_ifunc_benchmark(c: &mut Criterion) { - let mut group = c.benchmark_group("avx2_ifunc"); - - for rounds in [12, 50, 100].iter() { - group.throughput(Throughput::Elements(*rounds as u64)); - group.bench_with_input(BenchmarkId::new("rounds", rounds), &rounds, |b, rounds| { - let mut state = [ - 0x6a09e667f2bdc948_u64, - 0xbb67ae8584caa73b_u64, - 0x3c6ef372fe94f82b_u64, - 0xa54ff53a5f1d36f1_u64, - 0x510e527fade682d1_u64, - 0x9b05688c2b3e6c1f_u64, - 0x1f83d9abfb41bd6b_u64, - 0x5be0cd19137e2179_u64, - ]; - - let message = [ - 0x0000000000636261_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - ]; - let count = [3, 0]; - let f = true; - - b.iter(|| unsafe { - let fun = FN.load(Ordering::Relaxed); - mem::transmute::(fun)( - black_box(&mut state), - black_box(message), - black_box(count), - black_box(f), - black_box(**rounds as usize), - ); - }); - }); - } - - group.finish(); - } - - pub fn avx_benchmark(c: &mut Criterion) { - let mut group = c.benchmark_group("avx2"); - - for rounds in [12, 50, 100].iter() { - group.throughput(Throughput::Elements(*rounds as u64)); - group.bench_with_input(BenchmarkId::new("rounds", rounds), &rounds, |b, rounds| { - let mut state = [ - 0x6a09e667f2bdc948_u64, - 0xbb67ae8584caa73b_u64, - 0x3c6ef372fe94f82b_u64, - 0xa54ff53a5f1d36f1_u64, - 0x510e527fade682d1_u64, - 0x9b05688c2b3e6c1f_u64, - 0x1f83d9abfb41bd6b_u64, - 0x5be0cd19137e2179_u64, - ]; - - let message = [ - 0x0000000000636261_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - ]; - let count = [3, 0]; - let f = true; - - b.iter(|| unsafe { - avx2::compress( - black_box(&mut state), - black_box(message), - black_box(count), - black_box(f), - black_box(**rounds as usize), - ); - }); - }); - } - - group.finish(); - } -} - -pub fn portable_benchmark(c: &mut Criterion) { - let mut group = c.benchmark_group("portable_impl"); - - for rounds in [12, 50, 100].iter() { - group.throughput(Throughput::Elements(*rounds as u64)); - group.bench_with_input(BenchmarkId::new("rounds", rounds), &rounds, |b, rounds| { - let mut state = [ - 0x6a09e667f2bdc948_u64, - 0xbb67ae8584caa73b_u64, - 0x3c6ef372fe94f82b_u64, - 0xa54ff53a5f1d36f1_u64, - 0x510e527fade682d1_u64, - 0x9b05688c2b3e6c1f_u64, - 0x1f83d9abfb41bd6b_u64, - 0x5be0cd19137e2179_u64, - ]; - - let message = [ - 0x0000000000636261_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - ]; - let count = [3, 0]; - let f = true; - - b.iter(|| { - portable::compress( - black_box(&mut state), - black_box(message), - black_box(count), - black_box(f), - black_box(**rounds as usize), - ); - }); - }); - } - - group.finish(); -} - -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -criterion_group!( - benches, - avx::avx_benchmark, - avx::avx_ifunc_benchmark, - portable_benchmark -); -#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] -criterion_group!(benches, portable_benchmark); - -criterion_main!(benches); diff --git a/evm-tests/EIP-152/src/avx2.rs b/evm-tests/EIP-152/src/avx2.rs deleted file mode 100644 index 7dc2d648..00000000 --- a/evm-tests/EIP-152/src/avx2.rs +++ /dev/null @@ -1,467 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! AVX2 implementation of the blake2b compression function. -use crate::IV; - -use arrayref::{array_refs, mut_array_refs}; -#[cfg(target_arch = "x86")] -use core::arch::x86::*; -#[cfg(target_arch = "x86_64")] -use core::arch::x86_64::*; - -// Adapted from https://github.com/rust-lang-nursery/stdsimd/pull/479. -macro_rules! _MM_SHUFFLE { - ($z:expr, $y:expr, $x:expr, $w:expr) => { - ($z << 6) | ($y << 4) | ($x << 2) | $w - }; -} - -/// The Blake2b compression function F. See https://tools.ietf.org/html/rfc7693#section-3.2 -/// Takes as an argument the state vector `state`, message block vector `message`, offset counter, final -/// block indicator flag `f`, and number of rounds `rounds`. The state vector provided as the first -/// parameter is modified by the function. -/// -/// `g1` only operates on `x` from the original g function. -/// ``` -/// fn portable_g1(v: &mut [u64], a: usize, b: usize, c: usize, d: usize, x: u64) { -/// v[a] = v[a].wrapping_add(v[b]).wrapping_add(x); -/// v[d] = (v[d] ^ v[a]).rotate_right(32); -/// v[c] = v[c].wrapping_add(v[d]); -/// v[b] = (v[b] ^ v[c]).rotate_right(24); -/// } -/// ``` -/// -/// `g2` only operates on `y` from the original g function. -/// ``` -/// fn portable_g2(v: &mut [u64], a: usize, b: usize, c: usize, d: usize, y: u64) { -/// v[a] = v[a].wrapping_add(v[b]).wrapping_add(y); -/// v[d] = (v[d] ^ v[a]).rotate_right(16); -/// v[c] = v[c].wrapping_add(v[d]); -/// v[b] = (v[b] ^ v[c]).rotate_right(63); -/// } -/// ``` -/// -/// Message mixing is done based on sigma values, for a given round. -/// -/// # Example -/// -/// `SIGMA` for round 1 i.e `SIGMA[0]` = `[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]`; -/// ``` -/// let s = &SIGMA[0 % 10]; -/// // a, b, c, d, x -/// g(&mut v, 0, 4, 8 , 12, m[s[0]]); -/// g(&mut v, 1, 5, 9 , 13, m[s[2]]); -/// g(&mut v, 2, 6, 10, 14, m[s[4]]); -/// g(&mut v, 3, 7, 11, 15, m[s[6]]); -/// -/// let a = v[..4]; -/// let b = v[4..8]; -/// let c = v[8..12]; -/// let d = v[12..16]; -/// let mut b0 = [m[0], m[2], m[4], m[6]]; -/// -/// g1(&mut a, &mut b, &mut c, &mut d, &b0); -/// // ... then construct b0 for `g2` etc. -/// ``` -/// -#[target_feature(enable = "avx2")] -#[allow(clippy::missing_safety_doc)] -pub unsafe fn compress( - state: &mut [u64; 8], - message: [u64; 16], - count: [u64; 2], - f: bool, - rounds: usize, -) { - // get a mutable reference to state[0..4], state[4..] - let (state_low, state_high) = mut_array_refs!(state, 4, 4); - // get a reference to IV[0..4], IV[4..] - let (iv_low, iv_high) = array_refs!(&IV, 4, 4); - - // loads them into an __m256i - let mut a = loadu(state_low); - let mut b = loadu(state_high); - let mut c = loadu(iv_low); - - // !a = xor(a, xor(a, !a)) - let inverse = if f { iv_high[3] ^ !iv_high[3] } else { 0 }; - - let flags = set4(count[0], count[1], inverse, 0); - - let mut d = xor(loadu(iv_high), flags); - - // get a reference to message[(0..2)+,] - let msg_chunks = array_refs!(&message, 2, 2, 2, 2, 2, 2, 2, 2); - // load each message [u64; 2] into an __m128i, broadcast it into both lanes of an __m256i. - - // m0 = __m256i([message[0], message[1], message[0], message[1]]) - let m0 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.0)); - // m1 = __m256i([message[2], message[3], message[2], message[3]]) - let m1 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.1)); - // m2 = __m256i([message[4], message[5], message[4], message[5]]) - let m2 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.2)); - // m3 = __m256i([message[6], message[7], message[6], message[7]]) - let m3 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.3)); - // m4 = __m256i([message[8], message[9], message[8], message[9]]) - let m4 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.4)); - // m5 = __m256i([message[10], message[11], message[10], message[11]]) - let m5 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.5)); - // m6 = __m256i([message[12], message[13], message[12], message[13]]) - let m6 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.6)); - // m7 = __m256i([message[14], message[15], message[14], message[15]]) - let m7 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.7)); - - let iv0 = a; - let iv1 = b; - - let mut t0; - let mut t1; - let mut b0; - - for i in 0..rounds { - match i % 10 { - 0 => { - t0 = _mm256_unpacklo_epi64(m0, m1); // ([0, 1, 0, 1], [2, 3, 2, 3]) = [0, 2, 0, 2] - t1 = _mm256_unpacklo_epi64(m2, m3); // ([4, 5, 4, 5], [6, 7, 6, 7]) = [4, 6, 4, 6] - b0 = _mm256_blend_epi32(t0, t1, 0xF0); // ([0, 2, 0, 2], [4, 6, 4, 6]) = [0, 2, 4, 6] - g1(&mut a, &mut b, &mut c, &mut d, &b0); - t0 = _mm256_unpackhi_epi64(m0, m1); // ([0, 1, 0, 1], [2, 3, 2, 3]) = [1, 3, 1, 3] - t1 = _mm256_unpackhi_epi64(m2, m3); // ([4, 5, 4, 5], [6, 7, 6, 7]) = [5, 7, 5, 7] - b0 = _mm256_blend_epi32(t0, t1, 0xF0); // ([1, 3, 1, 3], [5, 7, 5, 7]) = [1, 3, 5, 7] - g2(&mut a, &mut b, &mut c, &mut d, &b0); - diagonalize(&mut a, &mut b, &mut c, &mut d); - t0 = _mm256_unpacklo_epi64(m7, m4); // ([14, 15, 14, 15], [8, 9, 8, 9]) = [14, 8, 14, 8] - t1 = _mm256_unpacklo_epi64(m5, m6); // ([10, 11, 10, 11], [12, 13, 12, 13]) = [10, 12, 10, 12] - b0 = _mm256_blend_epi32(t0, t1, 0xF0); // ([14, 8, 14, 8], [10, 12, 10, 12]) = [14, 8, 10, 12] - g1(&mut a, &mut b, &mut c, &mut d, &b0); - t0 = _mm256_unpackhi_epi64(m7, m4); // ([14, 15, 14, 15], [8, 9, 8, 9]) = [15, 9, 15, 9] - t1 = _mm256_unpackhi_epi64(m5, m6); // ([10, 11, 10, 11], [12, 13, 12, 13]) = [11, 13, 11, 13] - b0 = _mm256_blend_epi32(t0, t1, 0xF0); // ([15, 9, 15, 9], [11, 13, 11, 13]) = [15, 9, 11, 13] - g2(&mut a, &mut b, &mut c, &mut d, &b0); - undiagonalize(&mut a, &mut b, &mut c, &mut d); - } - 1 => { - t0 = _mm256_unpacklo_epi64(m7, m2); - t1 = _mm256_unpackhi_epi64(m4, m6); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g1(&mut a, &mut b, &mut c, &mut d, &b0); - t0 = _mm256_unpacklo_epi64(m5, m4); - t1 = _mm256_alignr_epi8(m3, m7, 8); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g2(&mut a, &mut b, &mut c, &mut d, &b0); - diagonalize(&mut a, &mut b, &mut c, &mut d); - t0 = _mm256_unpackhi_epi64(m2, m0); - t1 = _mm256_blend_epi32(m5, m0, 0x33); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g1(&mut a, &mut b, &mut c, &mut d, &b0); - t0 = _mm256_alignr_epi8(m6, m1, 8); - t1 = _mm256_blend_epi32(m3, m1, 0x33); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g2(&mut a, &mut b, &mut c, &mut d, &b0); - undiagonalize(&mut a, &mut b, &mut c, &mut d); - } - 2 => { - // round 3 - t0 = _mm256_alignr_epi8(m6, m5, 8); - t1 = _mm256_unpackhi_epi64(m2, m7); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g1(&mut a, &mut b, &mut c, &mut d, &b0); - t0 = _mm256_unpacklo_epi64(m4, m0); - t1 = _mm256_blend_epi32(m6, m1, 0x33); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g2(&mut a, &mut b, &mut c, &mut d, &b0); - diagonalize(&mut a, &mut b, &mut c, &mut d); - t0 = _mm256_alignr_epi8(m5, m4, 8); - t1 = _mm256_unpackhi_epi64(m1, m3); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g1(&mut a, &mut b, &mut c, &mut d, &b0); - t0 = _mm256_unpacklo_epi64(m2, m7); - t1 = _mm256_blend_epi32(m0, m3, 0x33); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g2(&mut a, &mut b, &mut c, &mut d, &b0); - undiagonalize(&mut a, &mut b, &mut c, &mut d); - } - 3 => { - // round 4 - t0 = _mm256_unpackhi_epi64(m3, m1); - t1 = _mm256_unpackhi_epi64(m6, m5); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g1(&mut a, &mut b, &mut c, &mut d, &b0); - t0 = _mm256_unpackhi_epi64(m4, m0); - t1 = _mm256_unpacklo_epi64(m6, m7); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g2(&mut a, &mut b, &mut c, &mut d, &b0); - diagonalize(&mut a, &mut b, &mut c, &mut d); - t0 = _mm256_alignr_epi8(m1, m7, 8); - t1 = _mm256_shuffle_epi32(m2, _MM_SHUFFLE!(1, 0, 3, 2)); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g1(&mut a, &mut b, &mut c, &mut d, &b0); - t0 = _mm256_unpacklo_epi64(m4, m3); - t1 = _mm256_unpacklo_epi64(m5, m0); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g2(&mut a, &mut b, &mut c, &mut d, &b0); - undiagonalize(&mut a, &mut b, &mut c, &mut d); - } - 4 => { - // round 5 - t0 = _mm256_unpackhi_epi64(m4, m2); - t1 = _mm256_unpacklo_epi64(m1, m5); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g1(&mut a, &mut b, &mut c, &mut d, &b0); - t0 = _mm256_blend_epi32(m3, m0, 0x33); - t1 = _mm256_blend_epi32(m7, m2, 0x33); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g2(&mut a, &mut b, &mut c, &mut d, &b0); - diagonalize(&mut a, &mut b, &mut c, &mut d); - t0 = _mm256_alignr_epi8(m7, m1, 8); - t1 = _mm256_alignr_epi8(m3, m5, 8); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g1(&mut a, &mut b, &mut c, &mut d, &b0); - t0 = _mm256_unpackhi_epi64(m6, m0); - t1 = _mm256_unpacklo_epi64(m6, m4); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g2(&mut a, &mut b, &mut c, &mut d, &b0); - undiagonalize(&mut a, &mut b, &mut c, &mut d); - } - 5 => { - // round 6 - t0 = _mm256_unpacklo_epi64(m1, m3); - t1 = _mm256_unpacklo_epi64(m0, m4); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g1(&mut a, &mut b, &mut c, &mut d, &b0); - t0 = _mm256_unpacklo_epi64(m6, m5); - t1 = _mm256_unpackhi_epi64(m5, m1); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g2(&mut a, &mut b, &mut c, &mut d, &b0); - diagonalize(&mut a, &mut b, &mut c, &mut d); - t0 = _mm256_alignr_epi8(m2, m0, 8); - t1 = _mm256_unpackhi_epi64(m3, m7); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g1(&mut a, &mut b, &mut c, &mut d, &b0); - t0 = _mm256_unpackhi_epi64(m4, m6); - t1 = _mm256_alignr_epi8(m7, m2, 8); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g2(&mut a, &mut b, &mut c, &mut d, &b0); - undiagonalize(&mut a, &mut b, &mut c, &mut d); - } - 6 => { - // round 7 - t0 = _mm256_blend_epi32(m0, m6, 0x33); - t1 = _mm256_unpacklo_epi64(m7, m2); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g1(&mut a, &mut b, &mut c, &mut d, &b0); - t0 = _mm256_unpackhi_epi64(m2, m7); - t1 = _mm256_alignr_epi8(m5, m6, 8); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g2(&mut a, &mut b, &mut c, &mut d, &b0); - diagonalize(&mut a, &mut b, &mut c, &mut d); - t0 = _mm256_unpacklo_epi64(m4, m0); - t1 = _mm256_blend_epi32(m4, m3, 0x33); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g1(&mut a, &mut b, &mut c, &mut d, &b0); - t0 = _mm256_unpackhi_epi64(m5, m3); - t1 = _mm256_shuffle_epi32(m1, _MM_SHUFFLE!(1, 0, 3, 2)); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g2(&mut a, &mut b, &mut c, &mut d, &b0); - undiagonalize(&mut a, &mut b, &mut c, &mut d); - } - 7 => { - // round 8 - t0 = _mm256_unpackhi_epi64(m6, m3); - t1 = _mm256_blend_epi32(m1, m6, 0x33); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g1(&mut a, &mut b, &mut c, &mut d, &b0); - t0 = _mm256_alignr_epi8(m7, m5, 8); - t1 = _mm256_unpackhi_epi64(m0, m4); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g2(&mut a, &mut b, &mut c, &mut d, &b0); - diagonalize(&mut a, &mut b, &mut c, &mut d); - t0 = _mm256_blend_epi32(m2, m1, 0x33); - t1 = _mm256_alignr_epi8(m4, m7, 8); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g1(&mut a, &mut b, &mut c, &mut d, &b0); - t0 = _mm256_unpacklo_epi64(m5, m0); - t1 = _mm256_unpacklo_epi64(m2, m3); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g2(&mut a, &mut b, &mut c, &mut d, &b0); - undiagonalize(&mut a, &mut b, &mut c, &mut d); - } - 8 => { - // round 9 - t0 = _mm256_unpacklo_epi64(m3, m7); - t1 = _mm256_alignr_epi8(m0, m5, 8); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g1(&mut a, &mut b, &mut c, &mut d, &b0); - t0 = _mm256_unpackhi_epi64(m7, m4); - t1 = _mm256_alignr_epi8(m4, m1, 8); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g2(&mut a, &mut b, &mut c, &mut d, &b0); - diagonalize(&mut a, &mut b, &mut c, &mut d); - t0 = _mm256_unpacklo_epi64(m5, m6); - t1 = _mm256_unpackhi_epi64(m6, m0); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g1(&mut a, &mut b, &mut c, &mut d, &b0); - t0 = _mm256_alignr_epi8(m1, m2, 8); - t1 = _mm256_alignr_epi8(m2, m3, 8); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g2(&mut a, &mut b, &mut c, &mut d, &b0); - undiagonalize(&mut a, &mut b, &mut c, &mut d); - } - _ => { - // round 10 - t0 = _mm256_unpacklo_epi64(m5, m4); - t1 = _mm256_unpackhi_epi64(m3, m0); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g1(&mut a, &mut b, &mut c, &mut d, &b0); - t0 = _mm256_unpacklo_epi64(m1, m2); - t1 = _mm256_blend_epi32(m2, m3, 0x33); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g2(&mut a, &mut b, &mut c, &mut d, &b0); - diagonalize(&mut a, &mut b, &mut c, &mut d); - t0 = _mm256_unpackhi_epi64(m6, m7); - t1 = _mm256_unpackhi_epi64(m4, m1); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g1(&mut a, &mut b, &mut c, &mut d, &b0); - t0 = _mm256_blend_epi32(m5, m0, 0x33); - t1 = _mm256_unpacklo_epi64(m7, m6); - b0 = _mm256_blend_epi32(t0, t1, 0xF0); - g2(&mut a, &mut b, &mut c, &mut d, &b0); - undiagonalize(&mut a, &mut b, &mut c, &mut d); - } - } - } - - a = xor(a, c); - b = xor(b, d); - a = xor(a, iv0); - b = xor(b, iv1); - - storeu(a, state_low); - storeu(b, state_high); -} - -#[inline(always)] -unsafe fn loadu(src: *const [u64; 4]) -> __m256i { - // This is an unaligned load, so the pointer cast is allowed. - _mm256_loadu_si256(src as *const __m256i) -} - -#[inline(always)] -unsafe fn storeu(src: __m256i, dest: *mut [u64; 4]) { - // This is an unaligned store, so the pointer cast is allowed. - _mm256_storeu_si256(dest as *mut __m256i, src) -} - -#[inline(always)] -unsafe fn loadu_128(mem_addr: &[u64; 2]) -> __m128i { - _mm_loadu_si128(mem_addr.as_ptr() as *const __m128i) -} - -#[inline(always)] -unsafe fn add(a: __m256i, b: __m256i) -> __m256i { - _mm256_add_epi64(a, b) -} - -#[inline(always)] -unsafe fn xor(a: __m256i, b: __m256i) -> __m256i { - _mm256_xor_si256(a, b) -} - -#[inline(always)] -unsafe fn set4(a: u64, b: u64, c: u64, d: u64) -> __m256i { - _mm256_setr_epi64x(a as i64, b as i64, c as i64, d as i64) -} - -#[inline(always)] -unsafe fn rotate_right_32(x: __m256i) -> __m256i { - _mm256_shuffle_epi32(x, _MM_SHUFFLE!(2, 3, 0, 1)) -} - -#[inline(always)] -unsafe fn rotate_right_24(x: __m256i) -> __m256i { - let rotate24 = _mm256_setr_epi8( - 3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10, 3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, - 14, 15, 8, 9, 10, - ); - _mm256_shuffle_epi8(x, rotate24) -} - -#[inline(always)] -unsafe fn rotate_right_16(x: __m256i) -> __m256i { - let rotate16 = _mm256_setr_epi8( - 2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9, 2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, - 13, 14, 15, 8, 9, - ); - _mm256_shuffle_epi8(x, rotate16) -} - -#[inline(always)] -unsafe fn rotate_right_63(x: __m256i) -> __m256i { - _mm256_or_si256(_mm256_srli_epi64(x, 63), add(x, x)) -} - -#[inline(always)] -unsafe fn g1(a: &mut __m256i, b: &mut __m256i, c: &mut __m256i, d: &mut __m256i, m: &__m256i) { - *a = add(*a, *m); - *a = add(*a, *b); - *d = xor(*d, *a); - *d = rotate_right_32(*d); - *c = add(*c, *d); - *b = xor(*b, *c); - *b = rotate_right_24(*b); -} - -#[inline(always)] -unsafe fn g2(a: &mut __m256i, b: &mut __m256i, c: &mut __m256i, d: &mut __m256i, m: &__m256i) { - *a = add(*a, *m); - *a = add(*a, *b); - *d = xor(*d, *a); - *d = rotate_right_16(*d); - *c = add(*c, *d); - *b = xor(*b, *c); - *b = rotate_right_63(*b); -} - -// Note the optimization here of leaving b as the unrotated row, rather than a. -// All the message loads below are adjusted to compensate for this. See -// discussion at https://github.com/sneves/blake2-avx2/pull/4 -#[inline(always)] -unsafe fn diagonalize(a: &mut __m256i, _b: &mut __m256i, c: &mut __m256i, d: &mut __m256i) { - *a = _mm256_permute4x64_epi64(*a, _MM_SHUFFLE!(2, 1, 0, 3)); - *d = _mm256_permute4x64_epi64(*d, _MM_SHUFFLE!(1, 0, 3, 2)); - *c = _mm256_permute4x64_epi64(*c, _MM_SHUFFLE!(0, 3, 2, 1)); -} - -// Note the optimization here of leaving b as the unrotated row, rather than a. -// All the message loads below are adjusted to compensate for this. See -// discussion at https://github.com/sneves/blake2-avx2/pull/4 -#[inline(always)] -unsafe fn undiagonalize(a: &mut __m256i, _b: &mut __m256i, c: &mut __m256i, d: &mut __m256i) { - *a = _mm256_permute4x64_epi64(*a, _MM_SHUFFLE!(0, 3, 2, 1)); - *d = _mm256_permute4x64_epi64(*d, _MM_SHUFFLE!(1, 0, 3, 2)); - *c = _mm256_permute4x64_epi64(*c, _MM_SHUFFLE!(2, 1, 0, 3)); -} - -#[cfg(test)] -mod tests { - #[test] - fn test_mm_shuffle() { - assert_eq!(_MM_SHUFFLE!(0, 1, 1, 3), 0b00_01_01_11); - assert_eq!(_MM_SHUFFLE!(3, 1, 1, 0), 0b11_01_01_00); - assert_eq!(_MM_SHUFFLE!(1, 2, 2, 1), 0b01_10_10_01); - } -} diff --git a/evm-tests/EIP-152/src/lib.rs b/evm-tests/EIP-152/src/lib.rs deleted file mode 100644 index ca0ec54a..00000000 --- a/evm-tests/EIP-152/src/lib.rs +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -#![allow(clippy::too_long_first_doc_paragraph)] - -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub mod avx2; -pub mod portable; - -/// The precomputed values for BLAKE2b [from the spec](https://tools.ietf.org/html/rfc7693#section-2.7) -/// There are 10 16-byte arrays - one for each round -/// the entries are calculated from the sigma constants. -const SIGMA: [[usize; 16]; 10] = [ - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], - [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], - [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], - [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], - [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], - [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], - [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], - [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], - [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], - [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], -]; - -/// IV is the initialization vector for BLAKE2b. See https://tools.ietf.org/html/rfc7693#section-2.6 -/// for details. -const IV: [u64; 8] = [ - 0x6a09_e667_f3bc_c908, - 0xbb67_ae85_84ca_a73b, - 0x3c6e_f372_fe94_f82b, - 0xa54f_f53a_5f1d_36f1, - 0x510e_527f_ade6_82d1, - 0x9b05_688c_2b3e_6c1f, - 0x1f83_d9ab_fb41_bd6b, - 0x5be0_cd19_137e_2179, -]; - -/// blake2b compression function -pub fn compress(state: &mut [u64; 8], message: [u64; 16], count: [u64; 2], f: bool, rounds: usize) { - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - { - if is_x86_feature_detected!("avx2") { - unsafe { avx2::compress(state, message, count, f, rounds) } - } else { - portable::compress(state, message, count, f, rounds); - }; - } - - #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] - portable::compress(state, message, count, f, rounds); -} - -#[cfg(test)] -mod tests { - use crate::portable; - - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - use crate::avx2; - use rustc_hex::FromHex; - - #[test] - fn test_blake2_f() { - // test from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#example-usage-in-solidity - let mut h_in = [ - 0x6a09_e667_f2bd_c948_u64, - 0xbb67_ae85_84ca_a73b_u64, - 0x3c6e_f372_fe94_f82b_u64, - 0xa54f_f53a_5f1d_36f1_u64, - 0x510e_527f_ade6_82d1_u64, - 0x9b05_688c_2b3e_6c1f_u64, - 0x1f83_d9ab_fb41_bd6b_u64, - 0x5be0_cd19_137e_2179_u64, - ]; - - let m = [ - 0x0000000000636261_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - 0x0000000000000000_u64, - ]; - let c = [3, 0]; - let f = true; - let rounds = 12; - let h_out: [u64; 8] = [ - 0x0D4D1C983FA580BA_u64, - 0xE9F6129FB697276A_u64, - 0xB7C45A68142F214C_u64, - 0xD1A2FFDB6FBB124B_u64, - 0x2D79AB2A39C5877D_u64, - 0x95CC3345DED552C2_u64, - 0x5A92F1DBA88AD318_u64, - 0x239900D4ED8623B9_u64, - ]; - - // portable - portable::compress(&mut h_in, m, c, f, rounds); - assert_eq!(h_in, h_out); - - // avx - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - { - let mut h_in = [ - 0x6a09e667f2bdc948_u64, - 0xbb67ae8584caa73b_u64, - 0x3c6ef372fe94f82b_u64, - 0xa54ff53a5f1d36f1_u64, - 0x510e527fade682d1_u64, - 0x9b05688c2b3e6c1f_u64, - 0x1f83d9abfb41bd6b_u64, - 0x5be0cd19137e2179_u64, - ]; - - if is_x86_feature_detected!("avx2") { - unsafe { - avx2::compress(&mut h_in, m, c, f, rounds); - assert_eq!(h_in, h_out); - } - } - } - } - - fn to_u64_slice(vec: &[u8], slice: &mut [u64]) { - vec.chunks(8).enumerate().for_each(|(index, val)| { - slice[index] = u64::from_le_bytes([ - val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7], - ]) - }) - } - - #[test] - fn test_vectors_from_eip() { - let vec = vec![ - ( - // Test vector 4 - "0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", - "08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b", - ), - ( // test vector 5 - "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", - "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923", - ), - ( - // Test vector 6 - "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000", - "75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735", - ), - ( - // Test vector 7 - "0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", - "b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421", - ), - // Test vector 8 – u32::MAX rounds – too slow to run -// ( -// "ffffffff48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", -// "fc59093aafa9ab43daae0e914c57635c5402d8e3d2130eb9b3cc181de7f0ecf9b22bf99a7815ce16419e200e01846e6b5df8cc7703041bbceb571de6631d2615", -// ), - ]; - for (hex, output) in vec { - let bytes: Vec = hex.from_hex().unwrap(); - - assert_eq!(bytes.len(), 213); - - let mut h = [0u64; 8]; - let mut m = [0u64; 16]; - let mut t = [0u64; 2]; - - let rounds = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]); - let f = match bytes[212] { - 1 => true, - 0 => false, - _ => unreachable!(), - }; - - to_u64_slice(&bytes[4..68], &mut h); - to_u64_slice(&bytes[68..196], &mut m); - to_u64_slice(&bytes[196..212], &mut t); - let output: Vec = output.from_hex().unwrap(); - let mut out = [0u64; 8]; - to_u64_slice(&output[..], &mut out); - - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - { - // avx - if is_x86_feature_detected!("avx2") { - unsafe { - avx2::compress(&mut h, m, t, f, rounds as usize); - assert_eq!(out, h); - } - } - } - - { - // portable - to_u64_slice(&bytes[4..68], &mut h); - portable::compress(&mut h, m, t, f, rounds as usize); - assert_eq!(out, h); - } - } - } -} diff --git a/evm-tests/EIP-152/src/portable.rs b/evm-tests/EIP-152/src/portable.rs deleted file mode 100644 index 7dff0166..00000000 --- a/evm-tests/EIP-152/src/portable.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Portable implementation of the blake2b compress function - -use crate::{IV, SIGMA}; - -/// The G mixing function. See https://tools.ietf.org/html/rfc7693#section-3.1 -#[inline(always)] -fn g(v: &mut [u64], a: usize, b: usize, c: usize, d: usize, x: u64, y: u64) { - v[a] = v[a].wrapping_add(v[b]).wrapping_add(x); - v[d] = (v[d] ^ v[a]).rotate_right(32); - v[c] = v[c].wrapping_add(v[d]); - v[b] = (v[b] ^ v[c]).rotate_right(24); - - v[a] = v[a].wrapping_add(v[b]).wrapping_add(y); - v[d] = (v[d] ^ v[a]).rotate_right(16); - v[c] = v[c].wrapping_add(v[d]); - v[b] = (v[b] ^ v[c]).rotate_right(63); -} - -/// The Blake2b compression function F. See https://tools.ietf.org/html/rfc7693#section-3.2 -/// Takes as an argument the state vector `h`, message block vector `m`, offset counter `t`, final -/// block indicator flag `f`, and number of rounds `rounds`. The state vector provided as the first -/// parameter is modified by the function. -pub fn compress(h: &mut [u64; 8], m: [u64; 16], t: [u64; 2], f: bool, rounds: usize) { - let mut v = [0u64; 16]; - v[..8].copy_from_slice(h); // First half from state. - v[8..].copy_from_slice(&IV); // Second half from IV. - - v[12] ^= t[0]; - v[13] ^= t[1]; - - if f { - v[14] = !v[14]; // Invert all bits if the last-block-flag is set. - } - - for i in 0..rounds { - // Message word selection permutation for this round. - let s = &SIGMA[i % 10]; - g(&mut v, 0, 4, 8, 12, m[s[0]], m[s[1]]); - g(&mut v, 1, 5, 9, 13, m[s[2]], m[s[3]]); - g(&mut v, 2, 6, 10, 14, m[s[4]], m[s[5]]); - g(&mut v, 3, 7, 11, 15, m[s[6]], m[s[7]]); - - g(&mut v, 0, 5, 10, 15, m[s[8]], m[s[9]]); - g(&mut v, 1, 6, 11, 12, m[s[10]], m[s[11]]); - g(&mut v, 2, 7, 8, 13, m[s[12]], m[s[13]]); - g(&mut v, 3, 4, 9, 14, m[s[14]], m[s[15]]); - } - - for i in 0..8 { - h[i] ^= v[i] ^ v[i + 8]; - } -} diff --git a/evm-tests/ethcore-builtin/Cargo.toml b/evm-tests/ethcore-builtin/Cargo.toml deleted file mode 100644 index 58c1a8d2..00000000 --- a/evm-tests/ethcore-builtin/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -description = "ethereum vm builtin" -name = "ethcore-builtin" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" - -[dependencies] -bn = { git = "https://github.com/paritytech/bn", rev = "b048fe1", default-features = false } -byteorder = "1.3.2" -eip-152 = { path = "../EIP-152" } -ethereum-types = "0.15" -ethjson = { path = "../ethjson" } -keccak-hash = "0.10" -log = "0.4" -num = "0.2" -parity-bytes = "0.1" -libsecp256k1 = "0.7" -ripemd = { version = "0.1", default-features = false } -sha2 = { version = "0.10.0", default-features = false } -eth_pairings = { git = "https://github.com/matter-labs/eip1962.git", default-features = false, features = ["eip_2537"], rev = "ece6cbabc41948db4200e41f0bfdab7ab94c7af8" } -hex-literal = "0.4" -c-kzg = "1.0" -derive_more = "0.99" -hex = "0.4.3" -blst = "0.3.13" - -[dev-dependencies] -maplit = "1.0.2" diff --git a/evm-tests/ethcore-builtin/src/bls.rs b/evm-tests/ethcore-builtin/src/bls.rs deleted file mode 100644 index 62c207d8..00000000 --- a/evm-tests/ethcore-builtin/src/bls.rs +++ /dev/null @@ -1,801 +0,0 @@ -use blst::{ - blst_bendian_from_fp, blst_fp, blst_fp_from_bendian, blst_p1_affine, blst_p1_affine_in_g1, - blst_p1_affine_on_curve, blst_scalar, blst_scalar_from_bendian, -}; -use std::cmp::Ordering; -use std::convert::TryInto; - -/// Number of bits used in the BLS12-381 curve finite field elements. -const NBITS: usize = 256; -/// Finite field element input length. -const FP_LENGTH: usize = 48; -/// Finite field element padded input length. -const PADDED_FP_LENGTH: usize = 64; -/// Quadratic extension of finite field element input length. -const PADDED_FP2_LENGTH: usize = 128; -/// Input elements padding length. -const PADDING_LENGTH: usize = 16; -/// Scalar length. -const SCALAR_LENGTH: usize = 32; -// Big-endian non-Montgomery form. -const MODULUS_REPR: [u8; 48] = [ - 0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, 0xac, 0xd7, - 0x64, 0x77, 0x4b, 0x84, 0xf3, 0x85, 0x12, 0xbf, 0x67, 0x30, 0xd2, 0xa0, 0xf6, 0xb0, 0xf6, 0x24, - 0x1e, 0xab, 0xff, 0xfe, 0xb1, 0x53, 0xff, 0xff, 0xb9, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xab, -]; -/// Amount used to calculate the multi-scalar-multiplication discount. -const MSM_MULTIPLIER: u64 = 1000; - -/// BLS Encodes a single finite field element into byte slice with padding. -fn fp_to_bytes(out: &mut [u8], input: *const blst_fp) { - if out.len() != PADDED_FP_LENGTH { - return; - } - let (padding, rest) = out.split_at_mut(PADDING_LENGTH); - padding.fill(0); - unsafe { blst_bendian_from_fp(rest.as_mut_ptr(), input) }; -} - -/// Checks if the input is a valid big-endian representation of a field element. -fn is_valid_be(input: &[u8; 48]) -> bool { - for (i, modul) in input.iter().zip(MODULUS_REPR.iter()) { - match i.cmp(modul) { - Ordering::Greater => return false, - Ordering::Less => return true, - Ordering::Equal => continue, - } - } - // false if matching the modulus - false -} - -/// Checks whether or not the input represents a canonical field element, returning the field -/// element if successful. -fn fp_from_bendian(input: &[u8; 48]) -> Result { - if !is_valid_be(input) { - return Err("non-canonical fp value"); - } - let mut fp = blst_fp::default(); - // SAFETY: input has fixed length, and fp is a blst value. - unsafe { - // This performs the check for canonical field elements - blst_fp_from_bendian(&mut fp, input.as_ptr()); - } - - Ok(fp) -} - -/// Removes zeros with which the precompile inputs are left padded to 64 bytes. -fn remove_padding(input: &[u8]) -> Result<&[u8; FP_LENGTH], &'static str> { - if input.len() != PADDED_FP_LENGTH { - return Err(Box::leak( - format!( - "Padded input should be {PADDED_FP_LENGTH} bytes, was {}", - input.len() - ) - .into_boxed_str(), - )); - } - let (padding, unpadded) = input.split_at(PADDING_LENGTH); - if !padding.iter().all(|&x| x == 0) { - return Err(Box::leak( - format!("{PADDING_LENGTH} top bytes of input are not zero").into_boxed_str(), - )); - } - Ok(unpadded.try_into().unwrap()) -} - -/// Extracts a scalar from a 32 byte slice representation, decoding the input as a big endian -/// unsigned integer. If the input is not exactly 32 bytes long, an error is returned. -/// -/// From [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537): -/// * A scalar for the multiplication operation is encoded as 32 bytes by performing BigEndian -/// encoding of the corresponding (unsigned) integer. -/// -/// We do not check that the scalar is a canonical Fr element, because the EIP specifies: -/// * The corresponding integer is not required to be less than or equal than main subgroup order -/// `q`. -fn extract_scalar_input(input: &[u8]) -> Result { - if input.len() != SCALAR_LENGTH { - return Err(Box::leak( - format!("Input should be {SCALAR_LENGTH} bytes, was {}", input.len()).into_boxed_str(), - )); - } - - let mut out = blst_scalar::default(); - // SAFETY: input length is checked previously, out is a blst value. - unsafe { - // NOTE: we do not use `blst_scalar_fr_check` here because, from EIP-2537: - // - // * The corresponding integer is not required to be less than or equal than main subgroup - // order `q`. - blst_scalar_from_bendian(&mut out, input.as_ptr()) - }; - - Ok(out) -} - -/// Implements the gas schedule for G1/G2 Multiscalar-multiplication assuming 30 -/// MGas/second, see also: -fn msm_required_gas(k: usize, discount_table: &[u16], multiplication_cost: u64) -> u64 { - if k == 0 { - return 0; - } - - let index = core::cmp::min(k - 1, discount_table.len() - 1); - let discount = discount_table[index] as u64; - - (k as u64 * discount * multiplication_cost) / MSM_MULTIPLIER -} - -mod g1 { - use super::*; - - /// Length of each of the elements in a g1 operation input. - pub const G1_INPUT_ITEM_LENGTH: usize = 128; - - /// Output length of a g1 operation. - const G1_OUTPUT_LENGTH: usize = 128; - - /// Encodes a G1 point in affine format into byte slice with padded elements. - pub fn encode_g1_point(input: *const blst_p1_affine) -> Vec { - let mut out = vec![0u8; G1_OUTPUT_LENGTH]; - // SAFETY: out comes from fixed length array, input is a blst value. - unsafe { - fp_to_bytes(&mut out[..PADDED_FP_LENGTH], &(*input).x); - fp_to_bytes(&mut out[PADDED_FP_LENGTH..], &(*input).y); - } - out - } - - /// Returns a `blst_p1_affine` from the provided byte slices, which represent the x and y - /// affine coordinates of the point. - /// - /// If the x or y coordinate do not represent a canonical field element, an error is returned. - /// - /// See [fp_from_bendian] for more information. - pub fn decode_and_check_g1( - p0_x: &[u8; 48], - p0_y: &[u8; 48], - ) -> Result { - let out = blst_p1_affine { - x: fp_from_bendian(p0_x)?, - y: fp_from_bendian(p0_y)?, - }; - - Ok(out) - } - - /// Extracts a G1 point in Affine format from a 128 byte slice representation. - /// - /// NOTE: This function will perform a G1 subgroup check if `subgroup_check` is set to `true`. - pub fn extract_g1_input( - input: &[u8], - subgroup_check: bool, - ) -> Result { - if input.len() != G1_INPUT_ITEM_LENGTH { - return Err(Box::leak( - format!( - "Input should be {G1_INPUT_ITEM_LENGTH} bytes, was {}", - input.len() - ) - .into_boxed_str(), - )); - } - - let input_p0_x = remove_padding(&input[..PADDED_FP_LENGTH])?; - let input_p0_y = remove_padding(&input[PADDED_FP_LENGTH..G1_INPUT_ITEM_LENGTH])?; - let out = decode_and_check_g1(input_p0_x, input_p0_y)?; - - if subgroup_check { - // NB: Subgroup checks - // - // Scalar multiplications, MSMs and pairings MUST perform a subgroup check. - // - // Implementations SHOULD use the optimized subgroup check method: - // - // https://eips.ethereum.org/assets/eip-2537/fast_subgroup_checks - // - // On any input that fail the subgroup check, the precompile MUST return an error. - // - // As endomorphism acceleration requires input on the correct subgroup, implementers MAY - // use endomorphism acceleration. - if unsafe { !blst_p1_affine_in_g1(&out) } { - return Err("Element not in G1"); - } - } else { - // From EIP-2537: - // - // Error cases: - // - // * An input is neither a point on the G1 elliptic curve nor the infinity point - // - // NB: There is no subgroup check for the G1 addition precompile. - // - // We use blst_p1_affine_on_curve instead of blst_p1_affine_in_g1 because the latter performs - // the subgroup check. - // - // SAFETY: out is a blst value. - if unsafe { !blst_p1_affine_on_curve(&out) } { - return Err("Element not on G1 curve"); - } - } - - Ok(out) - } -} - -mod g2 { - use super::*; - use blst::{blst_fp2, blst_p2_affine, blst_p2_affine_in_g2, blst_p2_affine_on_curve}; - - /// Length of each of the elements in a g2 operation input. - pub const G2_INPUT_ITEM_LENGTH: usize = 256; - - /// Output length of a g2 operation. - const G2_OUTPUT_LENGTH: usize = 256; - - /// Encodes a G2 point in affine format into byte slice with padded elements. - pub fn encode_g2_point(input: &blst_p2_affine) -> Vec { - let mut out = vec![0u8; G2_OUTPUT_LENGTH]; - fp_to_bytes(&mut out[..PADDED_FP_LENGTH], &input.x.fp[0]); - fp_to_bytes( - &mut out[PADDED_FP_LENGTH..2 * PADDED_FP_LENGTH], - &input.x.fp[1], - ); - fp_to_bytes( - &mut out[2 * PADDED_FP_LENGTH..3 * PADDED_FP_LENGTH], - &input.y.fp[0], - ); - fp_to_bytes( - &mut out[3 * PADDED_FP_LENGTH..4 * PADDED_FP_LENGTH], - &input.y.fp[1], - ); - out - } - - /// Convert the following field elements from byte slices into a `blst_p2_affine` point. - pub fn decode_and_check_g2( - x1: &[u8; 48], - x2: &[u8; 48], - y1: &[u8; 48], - y2: &[u8; 48], - ) -> Result { - Ok(blst_p2_affine { - x: check_canonical_fp2(x1, x2)?, - y: check_canonical_fp2(y1, y2)?, - }) - } - - /// Checks whether or not the input represents a canonical fp2 field element, returning the field - /// element if successful. - pub fn check_canonical_fp2( - input_1: &[u8; 48], - input_2: &[u8; 48], - ) -> Result { - let fp_1 = fp_from_bendian(input_1)?; - let fp_2 = fp_from_bendian(input_2)?; - - let fp2 = blst_fp2 { fp: [fp_1, fp_2] }; - - Ok(fp2) - } - - /// Extracts a G2 point in Affine format from a 256 byte slice representation. - /// - /// NOTE: This function will perform a G2 subgroup check if `subgroup_check` is set to `true`. - pub fn extract_g2_input( - input: &[u8], - subgroup_check: bool, - ) -> Result { - if input.len() != G2_INPUT_ITEM_LENGTH { - return Err(Box::leak( - format!( - "Input should be {G2_INPUT_ITEM_LENGTH} bytes, was {}", - input.len() - ) - .into_boxed_str(), - )); - } - - let mut input_fps = [&[0; FP_LENGTH]; 4]; - for i in 0..4 { - input_fps[i] = - remove_padding(&input[i * PADDED_FP_LENGTH..(i + 1) * PADDED_FP_LENGTH])?; - } - - let out = decode_and_check_g2(input_fps[0], input_fps[1], input_fps[2], input_fps[3])?; - - if subgroup_check { - // NB: Subgroup checks - // - // Scalar multiplications, MSMs and pairings MUST perform a subgroup check. - // - // Implementations SHOULD use the optimized subgroup check method: - // - // https://eips.ethereum.org/assets/eip-2537/fast_subgroup_checks - // - // On any input that fail the subgroup check, the precompile MUST return an error. - // - // As endomorphism acceleration requires input on the correct subgroup, implementers MAY - // use endomorphism acceleration. - if unsafe { !blst_p2_affine_in_g2(&out) } { - return Err("Element not in G2"); - } - } else { - // From EIP-2537: - // - // Error cases: - // - // * An input is neither a point on the G2 elliptic curve nor the infinity point - // - // NB: There is no subgroup check for the G2 addition precompile. - // - // We use blst_p2_affine_on_curve instead of blst_p2_affine_in_g2 because the latter performs - // the subgroup check. - // - // SAFETY: out is a blst value. - if unsafe { !blst_p2_affine_on_curve(&out) } { - return Err("Element not on G2 curve"); - } - } - - Ok(out) - } -} - -pub mod g1_add { - use super::*; - use blst::{blst_p1, blst_p1_add_or_double_affine, blst_p1_from_affine, blst_p1_to_affine}; - - /// Input length of g1_add operation. - const INPUT_LENGTH: usize = 256; - - /// G1 addition call expects `256` bytes as an input that is interpreted as byte - /// concatenation of two G1 points (`128` bytes each). - /// Output is an encoding of addition operation result - single G1 point (`128` - /// bytes). - /// See also: - pub fn g1_add(input: &[u8]) -> Result, &'static str> { - if input.len() != INPUT_LENGTH { - return Err(Box::leak( - format!( - "G1ADD input should be {INPUT_LENGTH} bytes got {}", - input.len() - ) - .into_boxed_str(), - )); - } - - // NB: There is no subgroup check for the G1 addition precompile. - // - // We set the subgroup checks here to `false` - let a_aff = &g1::extract_g1_input(&input[..g1::G1_INPUT_ITEM_LENGTH], false)?; - let b_aff = &g1::extract_g1_input(&input[g1::G1_INPUT_ITEM_LENGTH..], false)?; - - let mut b = blst_p1::default(); - // SAFETY: b and b_aff are blst values. - unsafe { blst_p1_from_affine(&mut b, b_aff) }; - - let mut p = blst_p1::default(); - // SAFETY: p, b and a_aff are blst values. - unsafe { blst_p1_add_or_double_affine(&mut p, &b, a_aff) }; - - let mut p_aff = blst_p1_affine::default(); - // SAFETY: p_aff and p are blst values. - unsafe { blst_p1_to_affine(&mut p_aff, &p) }; - - Ok(g1::encode_g1_point(&p_aff)) - } -} - -pub mod g1_mul { - use super::*; - use blst::{blst_p1, blst_p1_from_affine, blst_p1_to_affine, p1_affines}; - - /// Input length of g1_mul operation. - const INPUT_LENGTH: usize = 160; - - /// Base gas fee for BLS12-381 g1_mul operation. - pub const BASE_GAS_FEE: u64 = 12000; - - /// Discounts table for G1 MSM as a vector of pairs `[k, discount]`. - const DISCOUNT_TABLE: [u16; 128] = [ - 1000, 949, 848, 797, 764, 750, 738, 728, 719, 712, 705, 698, 692, 687, 682, 677, 673, 669, - 665, 661, 658, 654, 651, 648, 645, 642, 640, 637, 635, 632, 630, 627, 625, 623, 621, 619, - 617, 615, 613, 611, 609, 608, 606, 604, 603, 601, 599, 598, 596, 595, 593, 592, 591, 589, - 588, 586, 585, 584, 582, 581, 580, 579, 577, 576, 575, 574, 573, 572, 570, 569, 568, 567, - 566, 565, 564, 563, 562, 561, 560, 559, 558, 557, 556, 555, 554, 553, 552, 551, 550, 549, - 548, 547, 547, 546, 545, 544, 543, 542, 541, 540, 540, 539, 538, 537, 536, 536, 535, 534, - 533, 532, 532, 531, 530, 529, 528, 528, 527, 526, 525, 525, 524, 523, 522, 522, 521, 520, - 520, 519, - ]; - - /// Implements EIP-2537 G1MSM precompile. - /// G1 multi-scalar-multiplication call expects `160*k` bytes as an input that is interpreted - /// as byte concatenation of `k` slices each of them being a byte concatenation - /// of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` - /// bytes). - /// Output is an encoding of multi-scalar-multiplication operation result - single G1 - /// point (`128` bytes). - /// See also: - pub fn g1_mul(input: &[u8]) -> Result, &'static str> { - if input.is_empty() || input.len() % INPUT_LENGTH != 0 { - return Err(Box::leak( - format!( - "G1MSM input length should be multiple of {INPUT_LENGTH}, was {}", - input.len() - ) - .into_boxed_str(), - )); - } - - let k = input.len() / INPUT_LENGTH; - let mut g1_points: Vec = Vec::with_capacity(k); - let mut scalars: Vec = Vec::with_capacity(k * SCALAR_LENGTH); - for i in 0..k { - let slice = &input[i * INPUT_LENGTH..i * INPUT_LENGTH + g1::G1_INPUT_ITEM_LENGTH]; - - // BLST batch API for p1_affines blows up when you pass it a point at infinity, so we must - // filter points at infinity (and their corresponding scalars) from the input. - if slice.iter().all(|i| *i == 0) { - continue; - } - - // NB: Scalar multiplications, MSMs and pairings MUST perform a subgroup check. - // - // So we set the subgroup_check flag to `true` - let p0_aff = &g1::extract_g1_input(slice, true)?; - - let mut p0 = blst_p1::default(); - // SAFETY: p0 and p0_aff are blst values. - unsafe { blst_p1_from_affine(&mut p0, p0_aff) }; - g1_points.push(p0); - - scalars.extend_from_slice( - &extract_scalar_input( - &input[i * INPUT_LENGTH + g1::G1_INPUT_ITEM_LENGTH - ..i * INPUT_LENGTH + g1::G1_INPUT_ITEM_LENGTH + SCALAR_LENGTH], - )? - .b, - ); - } - - // return infinity point if all points are infinity - if g1_points.is_empty() { - return Ok([0; 128].into()); - } - - let points = p1_affines::from(&g1_points); - let multiexp = points.mult(&scalars, NBITS); - - let mut multiexp_aff = blst_p1_affine::default(); - // SAFETY: multiexp_aff and multiexp are blst values. - unsafe { blst_p1_to_affine(&mut multiexp_aff, &multiexp) }; - - Ok(g1::encode_g1_point(&multiexp_aff)) - } - - /// G1MSM required gas - pub fn required_gas(input: &[u8]) -> u64 { - let k = input.len() / INPUT_LENGTH; - msm_required_gas(k, &DISCOUNT_TABLE, BASE_GAS_FEE) - } -} - -pub mod g2_add { - use super::*; - use blst::{ - blst_p2, blst_p2_add_or_double_affine, blst_p2_affine, blst_p2_from_affine, - blst_p2_to_affine, - }; - - /// Input length of g2_add operation. - const INPUT_LENGTH: usize = 512; - - /// G2 addition call expects `512` bytes as an input that is interpreted as byte - /// concatenation of two G2 points (`256` bytes each). - /// - /// Output is an encoding of addition operation result - single G2 point (`256` - /// bytes). - /// See also - pub fn g2_add(input: &[u8]) -> Result, &'static str> { - if input.len() != INPUT_LENGTH { - return Err(Box::leak( - format!( - "G2ADD input should be {INPUT_LENGTH} bytes, was {}", - input.len() - ) - .into_boxed_str(), - )); - } - - // NB: There is no subgroup check for the G2 addition precompile. - // - // So we set the subgroup checks here to `false` - let a_aff = &g2::extract_g2_input(&input[..g2::G2_INPUT_ITEM_LENGTH], false)?; - let b_aff = &g2::extract_g2_input(&input[g2::G2_INPUT_ITEM_LENGTH..], false)?; - - let mut b = blst_p2::default(); - // SAFETY: b and b_aff are blst values. - unsafe { blst_p2_from_affine(&mut b, b_aff) }; - - let mut p = blst_p2::default(); - // SAFETY: p, b and a_aff are blst values. - unsafe { blst_p2_add_or_double_affine(&mut p, &b, a_aff) }; - - let mut p_aff = blst_p2_affine::default(); - // SAFETY: p_aff and p are blst values. - unsafe { blst_p2_to_affine(&mut p_aff, &p) }; - - Ok(g2::encode_g2_point(&p_aff)) - } -} - -pub mod g2_mul { - use super::*; - use blst::{blst_p2, blst_p2_affine, blst_p2_from_affine, blst_p2_to_affine, p2_affines}; - - /// Base gas fee for BLS12-381 g2_mul operation. - pub const BASE_GAS_FEE: u64 = 22500; - - /// Input length of g2_mul operation. - pub const INPUT_LENGTH: usize = 288; - - // Discounts table for G2 MSM as a vector of pairs `[k, discount]`: - pub static DISCOUNT_TABLE: [u16; 128] = [ - 1000, 1000, 923, 884, 855, 832, 812, 796, 782, 770, 759, 749, 740, 732, 724, 717, 711, 704, - 699, 693, 688, 683, 679, 674, 670, 666, 663, 659, 655, 652, 649, 646, 643, 640, 637, 634, - 632, 629, 627, 624, 622, 620, 618, 615, 613, 611, 609, 607, 606, 604, 602, 600, 598, 597, - 595, 593, 592, 590, 589, 587, 586, 584, 583, 582, 580, 579, 578, 576, 575, 574, 573, 571, - 570, 569, 568, 567, 566, 565, 563, 562, 561, 560, 559, 558, 557, 556, 555, 554, 553, 552, - 552, 551, 550, 549, 548, 547, 546, 545, 545, 544, 543, 542, 541, 541, 540, 539, 538, 537, - 537, 536, 535, 535, 534, 533, 532, 532, 531, 530, 530, 529, 528, 528, 527, 526, 526, 525, - 524, 524, - ]; - - /// Implements EIP-2537 G2MSM precompile. - /// G2 multi-scalar-multiplication call expects `288*k` bytes as an input that is interpreted - /// as byte concatenation of `k` slices each of them being a byte concatenation - /// of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` - /// bytes). - /// Output is an encoding of multi-scalar-multiplication operation result - single G2 - /// point (`256` bytes). - /// See also: - pub fn g2_mul(input: &[u8]) -> Result, &'static str> { - let input_len = input.len(); - if input_len == 0 || input_len % INPUT_LENGTH != 0 { - return Err(Box::leak( - format!( - "G2MSM input length should be multiple of {}, was {}", - INPUT_LENGTH, input_len - ) - .into_boxed_str(), - )); - } - - let k = input_len / INPUT_LENGTH; - let mut g2_points: Vec = Vec::with_capacity(k); - let mut scalars: Vec = Vec::with_capacity(k * SCALAR_LENGTH); - for i in 0..k { - let slice = &input[i * INPUT_LENGTH..i * INPUT_LENGTH + g2::G2_INPUT_ITEM_LENGTH]; - // BLST batch API for p2_affines blows up when you pass it a point at infinity, so we must - // filter points at infinity (and their corresponding scalars) from the input. - if slice.iter().all(|i| *i == 0) { - continue; - } - - // NB: Scalar multiplications, MSMs and pairings MUST perform a subgroup check. - // - // So we set the subgroup_check flag to `true` - let p0_aff = &g2::extract_g2_input(slice, true)?; - - let mut p0 = blst_p2::default(); - // SAFETY: p0 and p0_aff are blst values. - unsafe { blst_p2_from_affine(&mut p0, p0_aff) }; - - g2_points.push(p0); - - scalars.extend_from_slice( - &extract_scalar_input( - &input[i * INPUT_LENGTH + g2::G2_INPUT_ITEM_LENGTH - ..i * INPUT_LENGTH + g2::G2_INPUT_ITEM_LENGTH + SCALAR_LENGTH], - )? - .b, - ); - } - - // return infinity point if all points are infinity - if g2_points.is_empty() { - return Ok([0; 256].into()); - } - - let points = p2_affines::from(&g2_points); - let multiexp = points.mult(&scalars, NBITS); - - let mut multiexp_aff = blst_p2_affine::default(); - // SAFETY: multiexp_aff and multiexp are blst values. - unsafe { blst_p2_to_affine(&mut multiexp_aff, &multiexp) }; - - Ok(g2::encode_g2_point(&multiexp_aff)) - } - - /// G2MSM required gas - pub fn required_gas(input: &[u8]) -> u64 { - let k = input.len() / INPUT_LENGTH; - msm_required_gas(k, &DISCOUNT_TABLE, BASE_GAS_FEE) - } -} - -pub mod pairing { - use super::*; - use blst::{blst_final_exp, blst_fp12, blst_fp12_is_one, blst_fp12_mul, blst_miller_loop}; - - /// Multiplier gas fee for BLS12-381 pairing operation. - const PAIRING_MULTIPLIER_BASE: u64 = 32600; - /// Offset gas fee for BLS12-381 pairing operation. - const PAIRING_OFFSET_BASE: u64 = 37700; - /// Input length of pairing operation. - const INPUT_LENGTH: usize = 384; - - /// Pairing call expects 384*k (k being a positive integer) bytes as an inputs - /// that is interpreted as byte concatenation of k slices. Each slice has the - /// following structure: - /// * 128 bytes of G1 point encoding - /// * 256 bytes of G2 point encoding - /// - /// Each point is expected to be in the subgroup of order q. - /// Output is 32 bytes where first 31 bytes are equal to 0x00 and the last byte - /// is 0x01 if pairing result is equal to the multiplicative identity in a pairing - /// target field and 0x00 otherwise. - /// - /// See also: - pub fn pairing(input: &[u8]) -> Result, &'static str> { - let input_len = input.len(); - if input_len == 0 || input_len % INPUT_LENGTH != 0 { - return Err(Box::leak( - format!( - "Pairing input length should be multiple of {INPUT_LENGTH}, was {input_len}" - ) - .into_boxed_str(), - )); - } - - let k = input_len / INPUT_LENGTH; - // Accumulator for the fp12 multiplications of the miller loops. - let mut acc = blst_fp12::default(); - for i in 0..k { - // NB: Scalar multiplications, MSMs and pairings MUST perform a subgroup check. - // - // So we set the subgroup_check flag to `true` - let p1_aff = &g1::extract_g1_input( - &input[i * INPUT_LENGTH..i * INPUT_LENGTH + g1::G1_INPUT_ITEM_LENGTH], - true, - )?; - - // NB: Scalar multiplications, MSMs and pairings MUST perform a subgroup check. - // - // So we set the subgroup_check flag to `true` - let p2_aff = &g2::extract_g2_input( - &input[i * INPUT_LENGTH + g1::G1_INPUT_ITEM_LENGTH - ..i * INPUT_LENGTH + g1::G1_INPUT_ITEM_LENGTH + g2::G2_INPUT_ITEM_LENGTH], - true, - )?; - - if i > 0 { - // After the first slice (i>0) we use cur_ml to store the current - // miller loop and accumulate with the previous results using a fp12 - // multiplication. - let mut cur_ml = blst_fp12::default(); - let mut res = blst_fp12::default(); - // SAFETY: res, acc, cur_ml, p1_aff and p2_aff are blst values. - unsafe { - blst_miller_loop(&mut cur_ml, p2_aff, p1_aff); - blst_fp12_mul(&mut res, &acc, &cur_ml); - } - acc = res; - } else { - // On the first slice (i==0) there is no previous results and no need - // to accumulate. - // SAFETY: acc, p1_aff and p2_aff are blst values. - unsafe { - blst_miller_loop(&mut acc, p2_aff, p1_aff); - } - } - } - - // SAFETY: ret and acc are blst values. - let mut ret = blst_fp12::default(); - unsafe { - blst_final_exp(&mut ret, &acc); - } - - let mut result: u8 = 0; - // SAFETY: ret is a blst value. - unsafe { - if blst_fp12_is_one(&ret) { - result = 1; - } - } - let mut out = [0u8; 32]; - out[31] = result; - Ok(out.into()) - } - - /// Pairing required gas - pub const fn required_gas(input: &[u8]) -> u64 { - let k = input.len() / INPUT_LENGTH; - PAIRING_MULTIPLIER_BASE * k as u64 + PAIRING_OFFSET_BASE - } -} - -pub mod map_fp_to_g1 { - use super::*; - use blst::{blst_map_to_g1, blst_p1, blst_p1_to_affine}; - - /// Field-to-curve call expects 64 bytes as an input that is interpreted as an - /// element of Fp. Output of this call is 128 bytes and is an encoded G1 point. - /// See also: - pub fn map_fp_to_g1(input: &[u8]) -> Result, &'static str> { - if input.len() != PADDED_FP_LENGTH { - return Err(Box::leak( - format!( - "MAP_FP_TO_G1 input should be {PADDED_FP_LENGTH} bytes, was {}", - input.len() - ) - .into_boxed_str(), - )); - } - - let input_p0 = remove_padding(input)?; - let fp = fp_from_bendian(input_p0)?; - - let mut p = blst_p1::default(); - // SAFETY: p and fp are blst values. - // third argument is unused if null. - unsafe { blst_map_to_g1(&mut p, &fp, core::ptr::null()) }; - - let mut p_aff = blst_p1_affine::default(); - // SAFETY: p_aff and p are blst values. - unsafe { blst_p1_to_affine(&mut p_aff, &p) }; - - Ok(g1::encode_g1_point(&p_aff)) - } -} - -pub mod map_fp2_to_g2 { - use super::*; - use crate::bls::g2::check_canonical_fp2; - use blst::{blst_map_to_g2, blst_p2, blst_p2_affine, blst_p2_to_affine}; - - /// Field-to-curve call expects 128 bytes as an input that is interpreted as - /// an element of Fp2. Output of this call is 256 bytes and is an encoded G2 - /// point. - /// See also: - pub fn map_fp2_to_g2(input: &[u8]) -> Result, &'static str> { - if input.len() != PADDED_FP2_LENGTH { - return Err(Box::leak( - format!( - "MAP_FP2_TO_G2 input should be {PADDED_FP2_LENGTH} bytes, was {}", - input.len() - ) - .into_boxed_str(), - )); - } - - let input_p0_x = remove_padding(&input[..PADDED_FP_LENGTH])?; - let input_p0_y = remove_padding(&input[PADDED_FP_LENGTH..PADDED_FP2_LENGTH])?; - let fp2 = check_canonical_fp2(input_p0_x, input_p0_y)?; - - let mut p = blst_p2::default(); - // SAFETY: p and fp2 are blst values. - // third argument is unused if null. - unsafe { blst_map_to_g2(&mut p, &fp2, core::ptr::null()) }; - - let mut p_aff = blst_p2_affine::default(); - // SAFETY: p_aff and p are blst values. - unsafe { blst_p2_to_affine(&mut p_aff, &p) }; - - Ok(g2::encode_g2_point(&p_aff)) - } -} diff --git a/evm-tests/ethcore-builtin/src/kzg.rs b/evm-tests/ethcore-builtin/src/kzg.rs deleted file mode 100644 index a8b39213..00000000 --- a/evm-tests/ethcore-builtin/src/kzg.rs +++ /dev/null @@ -1,229 +0,0 @@ -use c_kzg::{Bytes32, Bytes48, KzgProof, KzgSettings, BYTES_PER_G1_POINT, BYTES_PER_G2_POINT}; -use core::convert::TryInto; -use core::hash::{Hash, Hasher}; -use derive_more::{AsMut, AsRef, Deref, DerefMut}; -use hex_literal::hex; -use sha2::Digest; -use std::convert::TryFrom; -use std::rc::Rc; - -pub const RETURN_VALUE: &[u8; 64] = &hex!( - "0000000000000000000000000000000000000000000000000000000000001000" - "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001" -); - -/// Number of G1 Points. -const NUM_G1_POINTS: usize = 4096; - -/// Number of G2 Points. -const NUM_G2_POINTS: usize = 65; - -/// A newtype over list of G1 point from kzg trusted setup. -#[derive(Debug, Clone, PartialEq, AsRef, AsMut, Deref, DerefMut)] -#[repr(transparent)] -struct G1Points(pub [[u8; BYTES_PER_G1_POINT]; NUM_G1_POINTS]); - -impl Default for G1Points { - fn default() -> Self { - Self([[0; BYTES_PER_G1_POINT]; NUM_G1_POINTS]) - } -} - -/// A newtype over list of G2 point from kzg trusted setup. -#[derive(Debug, Clone, Eq, PartialEq, AsRef, AsMut, Deref, DerefMut)] -#[repr(transparent)] -struct G2Points(pub [[u8; BYTES_PER_G2_POINT]; NUM_G2_POINTS]); - -impl Default for G2Points { - fn default() -> Self { - Self([[0; BYTES_PER_G2_POINT]; NUM_G2_POINTS]) - } -} - -/// Default G1 points. -const G1_POINTS: &G1Points = { - const BYTES: &[u8] = include_bytes!("../assets/g1_points.bin"); - assert!(BYTES.len() == core::mem::size_of::()); - unsafe { &*BYTES.as_ptr().cast::() } -}; - -/// Default G2 points. -const G2_POINTS: &G2Points = { - const BYTES: &[u8] = include_bytes!("../assets/g2_points.bin"); - assert!(BYTES.len() == core::mem::size_of::()); - unsafe { &*BYTES.as_ptr().cast::() } -}; - -/// Parses the contents of a KZG trusted setup file into a list of G1 and G2 points. -/// -/// These can then be used to create a KZG settings object with -/// [`KzgSettings::load_trusted_setup`](c_kzg::KzgSettings::load_trusted_setup). -#[allow(dead_code)] -fn parse_kzg_trusted_setup( - trusted_setup: &str, -) -> Result<(Box, Box), &'static str> { - let mut lines = trusted_setup.lines(); - - // load number of points - let n_g1 = lines - .next() - .ok_or("KzgFileFormatError")? - .parse::() - .map_err(|_| "KzgParseError")?; - let n_g2 = lines - .next() - .ok_or("KzgFileFormatError")? - .parse::() - .map_err(|_| "KzgParseError")?; - - if n_g1 != NUM_G1_POINTS { - return Err("KzgMismatchedNumberOfPoints"); - } - - if n_g2 != NUM_G2_POINTS { - return Err("KzgMismatchedNumberOfPoints"); - } - - // load g1 points - let mut g1_points = Box::::default(); - for bytes in &mut g1_points.0 { - let line = lines.next().ok_or("KzgFileFormatError")?; - hex::decode_to_slice(line, bytes).map_err(|_| "KzgParseError")?; - } - - // load g2 points - let mut g2_points = Box::::default(); - for bytes in &mut g2_points.0 { - let line = lines.next().ok_or("KzgFileFormatError")?; - hex::decode_to_slice(line, bytes).map_err(|_| "KzgParseError")?; - } - - if lines.next().is_some() { - return Err("KzgFileFormatError"); - } - - Ok((g1_points, g2_points)) -} - -/// KZG Settings that allow us to specify a custom trusted setup. -/// or use hardcoded default settings. -#[allow(dead_code)] -#[derive(Debug, Clone, Default)] -pub enum EnvKzgSettings { - /// Default mainnet trusted setup - #[default] - Default, - /// Custom trusted setup. - Custom(Rc), -} - -// Implement PartialEq and Hash manually because `c_kzg::KzgSettings` does not implement them -impl PartialEq for EnvKzgSettings { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (Self::Default, Self::Default) => true, - (Self::Custom(a), Self::Custom(b)) => Rc::ptr_eq(a, b), - _ => false, - } - } -} - -impl Hash for EnvKzgSettings { - fn hash(&self, state: &mut H) { - core::mem::discriminant(self).hash(state); - match self { - Self::Default => {} - Self::Custom(settings) => Rc::as_ptr(settings).hash(state), - } - } -} - -impl EnvKzgSettings { - /// Return set KZG settings. - /// - /// In will initialize the default settings if it is not already loaded. - pub fn get(&self) -> Rc { - match self { - Self::Default => { - let res = KzgSettings::load_trusted_setup(G1_POINTS.as_ref(), G2_POINTS.as_ref()) - .expect("failed to load default trusted setup"); - Rc::new(res) - } - Self::Custom(settings) => settings.clone(), - } - } -} - -/// `VERSIONED_HASH_VERSION_KZG ++ sha256(commitment)[1..]` -#[inline] -pub fn kzg_to_versioned_hash(commitment: &[u8]) -> [u8; 32] { - const VERSIONED_HASH_VERSION_KZG: u8 = 0x01; - let mut hash: [u8; 32] = sha2::Sha256::digest(commitment).into(); - hash[0] = VERSIONED_HASH_VERSION_KZG; - hash -} - -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub struct KzgInput { - commitment: Bytes48, - z: Bytes32, - y: Bytes32, - proof: Bytes48, -} - -impl KzgInput { - #[inline] - pub fn verify_kzg_proof(&self, kzg_settings: &KzgSettings) -> bool { - KzgProof::verify_kzg_proof( - &self.commitment, - &self.z, - &self.y, - &self.proof, - kzg_settings, - ) - .unwrap_or(false) - } -} - -impl TryFrom<&[u8]> for KzgInput { - type Error = &'static str; - - fn try_from(input: &[u8]) -> Result { - if input.len() != 192 { - return Err("BlobInvalidInputLength"); - } - // Verify commitment matches versioned_hash - let versioned_hash = &input[..32]; - let commitment = &input[96..144]; - if kzg_to_versioned_hash(commitment) != versioned_hash { - return Err("BlobMismatchedVersion"); - } - let commitment = *as_bytes48(commitment); - let z = *as_bytes32(&input[32..64]); - let y = *as_bytes32(&input[64..96]); - let proof = *as_bytes48(&input[144..192]); - Ok(Self { - commitment, - z, - y, - proof, - }) - } -} - -#[inline] -fn as_array(bytes: &[u8]) -> &[u8; N] { - bytes.try_into().expect("slice with incorrect length") -} - -#[inline] -fn as_bytes32(bytes: &[u8]) -> &Bytes32 { - // SAFETY: `#[repr(C)] Bytes32([u8; 32])` - unsafe { &*as_array::<32>(bytes).as_ptr().cast() } -} - -#[inline] -fn as_bytes48(bytes: &[u8]) -> &Bytes48 { - // SAFETY: `#[repr(C)] Bytes48([u8; 48])` - unsafe { &*as_array::<48>(bytes).as_ptr().cast() } -} diff --git a/evm-tests/ethcore-builtin/src/lib.rs b/evm-tests/ethcore-builtin/src/lib.rs deleted file mode 100644 index c80bf463..00000000 --- a/evm-tests/ethcore-builtin/src/lib.rs +++ /dev/null @@ -1,2213 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Standard built-in contracts. - -#![warn(missing_docs)] - -mod bls; -mod kzg; - -use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; -use eip_152::compress; -use ethereum_types::{H256, U256}; -use keccak_hash::keccak; -use log::{trace, warn}; -use num::{BigUint, One, Zero}; -use parity_bytes::BytesRef; -use sha2::Digest; -use std::{ - cmp::{max, min}, - collections::BTreeMap, - convert::{TryFrom, TryInto}, - io::{self, Cursor, Read}, - str::FromStr, -}; - -/// Native implementation of a built-in contract. -pub trait Implementation: Send + Sync { - /// execute this built-in on the given input, writing to the given output. - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str>; -} - -/// A gas pricing scheme for built-in contracts. -trait Pricer: Send + Sync { - /// The gas cost of running this built-in for the given input data at block number `at` - fn cost(&self, input: &[u8]) -> U256; -} - -/// Pricing for the Blake2 compression function (aka "F"). -/// Computes the price as a fixed cost per round where the number of rounds is part of the input -/// byte slice. -pub type Blake2FPricer = u64; - -impl Pricer for Blake2FPricer { - fn cost(&self, input: &[u8]) -> U256 { - const FOUR: usize = size_of::(); - // Returning zero if the conversion fails is fine because `execute()` will check the length - // and bail with the appropriate error. - if input.len() < FOUR { - return U256::zero(); - } - let (rounds_bytes, _) = input.split_at(FOUR); - let rounds = u32::from_be_bytes(rounds_bytes.try_into().unwrap_or([0u8; FOUR])); - U256::from(*self as u128 * rounds as u128) - } -} - -/// Pricing model -#[derive(Debug)] -enum Pricing { - AltBn128Pairing(AltBn128PairingPricer), - AltBn128ConstOperations(AltBn128ConstOperations), - Blake2F(Blake2FPricer), - Linear(Linear), - Modexp(ModexpPricer), - Bls12G1Mul(Bls12G1MulPricer), - Bls12G2Mul(Bls12G2MulPricer), - Bls12Pairing(Bls12PairingPricer), -} - -impl Pricer for Pricing { - fn cost(&self, input: &[u8]) -> U256 { - match self { - Self::AltBn128Pairing(inner) => inner.cost(input), - Self::AltBn128ConstOperations(inner) => inner.cost(input), - Self::Blake2F(inner) => inner.cost(input), - Self::Linear(inner) => inner.cost(input), - Self::Modexp(inner) => inner.cost(input), - Self::Bls12G1Mul(inner) => inner.cost(input), - Self::Bls12G2Mul(inner) => inner.cost(input), - Self::Bls12Pairing(inner) => inner.cost(input), - } - } -} - -/// A linear pricing model. This computes a price using a base cost and a cost per-word. -#[derive(Debug)] -struct Linear { - base: u64, - word: u64, -} - -/// A special pricing model for modular exponentiation. -#[derive(Debug)] -struct ModexpPricer { - divisor: u64, - is_eip_2565: bool, -} - -impl Pricer for Linear { - fn cost(&self, input: &[u8]) -> U256 { - U256::from(self.base) + U256::from(self.word) * U256::from(input.len().div_ceil(32)) - } -} - -/// alt_bn128 pairing price -#[derive(Debug, Copy, Clone)] -struct AltBn128PairingPrice { - base: u64, - pair: u64, -} - -/// alt_bn128_pairing pricing model. This computes a price using a base cost and a cost per pair. -#[derive(Debug)] -struct AltBn128PairingPricer { - price: AltBn128PairingPrice, -} - -/// Pricing for constant alt_bn128 operations (ECADD and ECMUL) -#[derive(Debug, Copy, Clone)] -pub struct AltBn128ConstOperations { - /// Fixed price. - pub price: u64, -} - -impl Pricer for AltBn128ConstOperations { - fn cost(&self, _input: &[u8]) -> U256 { - self.price.into() - } -} - -impl Pricer for AltBn128PairingPricer { - fn cost(&self, input: &[u8]) -> U256 { - U256::from(self.price.base) + U256::from(self.price.pair) * U256::from(input.len() / 192) - } -} - -impl Pricer for ModexpPricer { - fn cost(&self, input: &[u8]) -> U256 { - if self.is_eip_2565 { - let mut buf = [0; 32]; - let (base_len, exp_len, mod_len) = Self::read_lengths(input, &mut buf); - let exp = Self::read_exp(input, base_len, exp_len, &mut buf); - Self::eip_2565_cost(self.divisor.into(), base_len, mod_len, exp_len, exp) - } else { - Self::cost(self.divisor, input) - } - } -} - -impl ModexpPricer { - fn adjusted_exp_len(len: u64, exp_low: U256) -> u64 { - let bit_index = if exp_low.is_zero() { - 0 - } else { - (255 - exp_low.leading_zeros()) as u64 - }; - if len <= 32 { - bit_index - } else { - 8 * (len - 32) + bit_index - } - } - - const fn mult_complexity(x: u64) -> u64 { - match x { - x if x <= 64 => x * x, - x if x <= 1024 => (x * x) / 4 + 96 * x - 3072, - x => (x * x) / 16 + 480 * x - 199_680, - } - } - - fn read_lengths(input: &[u8], buf: &mut [u8; 32]) -> (U256, U256, U256) { - let mut reader = input.chain(io::repeat(0)); - let mut read_len = || { - reader - .read_exact(&mut buf[..]) - .expect("reading from zero-extended memory cannot fail; qed"); - U256::from_big_endian(&buf[..]) - }; - let base_len = read_len(); - let exp_len = read_len(); - let mod_len = read_len(); - (base_len, exp_len, mod_len) - } - - fn read_exp(input: &[u8], base_len: U256, exp_len: U256, buf: &mut [u8; 32]) -> U256 { - let base_len = if base_len > U256::from(u32::MAX) { - return U256::zero(); - } else { - base_len.low_u64() - }; - if base_len + 96 >= input.len() as u64 { - U256::zero() - } else { - buf.iter_mut().for_each(|b| *b = 0); - let mut reader = input[(96 + base_len as usize)..].chain(io::repeat(0)); - let len = if exp_len < U256::from(32) { - exp_len.low_u64() as usize - } else { - 32 - }; - reader - .read_exact(&mut buf[(32 - len)..]) - .expect("reading from zero-extended memory cannot fail; qed"); - U256::from_big_endian(&buf[..]) - } - } - - fn cost(divisor: u64, input: &[u8]) -> U256 { - let mut buf = [0; 32]; - - // read lengths as U256 here for accurate gas calculation. - let (base_len, exp_len, mod_len) = Self::read_lengths(input, &mut buf); - - if mod_len.is_zero() && base_len.is_zero() { - return U256::zero(); - } - - let max_len = U256::from(u32::MAX / 2); - if base_len > max_len || mod_len > max_len || exp_len > max_len { - return U256::max_value(); - } - - // read fist 32-byte word of the exponent. - let exp_low = Self::read_exp(input, base_len, exp_len, &mut buf); - - let (base_len, exp_len, mod_len) = - (base_len.low_u64(), exp_len.low_u64(), mod_len.low_u64()); - - let m = max(mod_len, base_len); - - let adjusted_exp_len = Self::adjusted_exp_len(exp_len, exp_low); - - let (gas, overflow) = Self::mult_complexity(m).overflowing_mul(max(adjusted_exp_len, 1)); - if overflow { - return U256::max_value(); - } - (gas / divisor).into() - } - - fn eip_2565_mul_complexity(base_length: U256, modulus_length: U256) -> U256 { - let max_length = max(base_length, modulus_length); - let words = { - // div_ceil(max_length, 8); - let tmp = max_length / 8; - if (max_length % 8).is_zero() { - tmp - } else { - tmp + 1 - } - }; - words.saturating_mul(words) - } - - fn eip_2565_iter_count(exponent_length: U256, exponent: U256) -> U256 { - let thirty_two = U256::from(32); - let it = if exponent_length <= thirty_two && exponent.is_zero() { - U256::zero() - } else if exponent_length <= thirty_two { - U256::from(exponent.bits()) - U256::from(1) - } else { - // else > 32 - U256::from(8) - .saturating_mul(exponent_length - thirty_two) - .saturating_add(U256::from(exponent.bits()).saturating_sub(U256::from(1))) - }; - max(it, U256::one()) - } - - fn eip_2565_cost( - divisor: U256, - base_length: U256, - modulus_length: U256, - exponent_length: U256, - exponent: U256, - ) -> U256 { - let multiplication_complexity = Self::eip_2565_mul_complexity(base_length, modulus_length); - let iteration_count = Self::eip_2565_iter_count(exponent_length, exponent); - max( - U256::from(200), - multiplication_complexity.saturating_mul(iteration_count) / divisor, - ) - } -} - -/// MSM pricer in G1 -#[derive(Debug, Copy, Clone)] -pub struct Bls12G1MulPricer; - -/// MSM pricer in G2 -#[derive(Debug, Copy, Clone)] -pub struct Bls12G2MulPricer; - -/// Pairing pricer -#[derive(Debug, Copy, Clone)] -pub struct Bls12PairingPricer; - -impl Pricer for Bls12G1MulPricer { - fn cost(&self, input: &[u8]) -> U256 { - U256::from(bls::g1_mul::required_gas(input)) - } -} - -impl Pricer for Bls12G2MulPricer { - fn cost(&self, input: &[u8]) -> U256 { - U256::from(bls::g2_mul::required_gas(input)) - } -} - -impl Pricer for Bls12PairingPricer { - fn cost(&self, input: &[u8]) -> U256 { - U256::from(bls::pairing::required_gas(input)) - } -} - -/// Pricing scheme, execution definition, and activation block for a built-in contract. -/// -/// Call `cost` to compute cost for the given input, `execute` to execute the contract -/// on the given input, and `is_active` to determine whether the contract is active. -#[derive(Debug)] -pub struct Builtin { - pricer: BTreeMap, - native: EthereumBuiltin, -} - -impl Builtin { - /// Simple forwarder for cost. - /// - /// Return the cost of the most recently activated pricer at the current block number. - /// - /// If no pricer is actived `zero` is returned - /// - /// If multiple `activation_at` has the same block number the last one is used - /// (follows `BTreeMap` semantics). - #[inline] - pub fn cost(&self, input: &[u8], at: u64) -> U256 { - if let Some((_, pricer)) = self.pricer.range(0..=at).last() { - pricer.cost(input) - } else { - U256::zero() - } - } - - /// Simple forwarder for execute. - #[inline] - pub fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { - self.native.execute(input, output) - } - - /// Whether the builtin is activated at the given block number. - #[inline] - pub fn is_active(&self, at: u64) -> bool { - self.pricer.range(0..=at).last().is_some() - } -} - -impl TryFrom for Builtin { - type Error = (); - - fn try_from(b: ethjson::spec::builtin::Builtin) -> Result { - let native = EthereumBuiltin::from_str(&b.name)?; - let mut pricer = BTreeMap::new(); - - for (activate_at, p) in b.pricing { - pricer.insert(activate_at, p.price.into()); - } - - Ok(Self { pricer, native }) - } -} - -impl From for Pricing { - fn from(pricing: ethjson::spec::builtin::Pricing) -> Self { - match pricing { - ethjson::spec::builtin::Pricing::Blake2F { gas_per_round } => { - Self::Blake2F(gas_per_round) - } - ethjson::spec::builtin::Pricing::Linear(linear) => Self::Linear(Linear { - base: linear.base, - word: linear.word, - }), - ethjson::spec::builtin::Pricing::Modexp(exp) => Self::Modexp(ModexpPricer { - divisor: if exp.divisor == 0 { - warn!(target: "builtin", "Zero modexp divisor specified. Falling back to default: 10."); - 10 - } else { - exp.divisor - }, - is_eip_2565: exp.is_eip_2565, - }), - ethjson::spec::builtin::Pricing::AltBn128Pairing(pricer) => { - Self::AltBn128Pairing(AltBn128PairingPricer { - price: AltBn128PairingPrice { - base: pricer.base, - pair: pricer.pair, - }, - }) - } - ethjson::spec::builtin::Pricing::AltBn128ConstOperations(pricer) => { - Self::AltBn128ConstOperations(AltBn128ConstOperations { - price: pricer.price, - }) - } - ethjson::spec::builtin::Pricing::Bls12G1Mul => Self::Bls12G1Mul(Bls12G1MulPricer), - ethjson::spec::builtin::Pricing::Bls12G2Mul => Self::Bls12G2Mul(Bls12G2MulPricer), - ethjson::spec::builtin::Pricing::Bls12Pairing => Self::Bls12Pairing(Bls12PairingPricer), - } - } -} - -#[derive(Debug)] -/// Ethereum builtins: -enum EthereumBuiltin { - /// The identity function - Identity(Identity), - /// ec recovery - EcRecover(EcRecover), - /// sha256 - Sha256(Sha256), - /// ripemd160 - Ripemd160(Ripemd160), - /// modexp (EIP 198) - Modexp(Modexp), - /// alt_bn128_add - Bn128Add(Bn128Add), - /// alt_bn128_mul - Bn128Mul(Bn128Mul), - /// alt_bn128_pairing - Bn128Pairing(Bn128Pairing), - /// blake2_f (The Blake2 compression function F, EIP-152) - Blake2F(Blake2F), - Kgz(Kzg), - /// bls12_381 addition in g1 - Bls12G1Add(Bls12G1Add), - /// bls12_381 multiplication in g1 - Bls12G1Mul(Bls12G1Mul), - /// bls12_381 addition in g2 - Bls12G2Add(Bls12G2Add), - /// bls12_381 multiplication in g2 - Bls12G2Mul(Bls12G2Mul), - /// bls12_381 pairing - Bls12Pairing(Bls12Pairing), - /// bls12_381 fp to g1 mapping - Bls12MapFpToG1(Bls12MapFpToG1), - /// bls12_381 fp2 to g2 mapping - Bls12MapFp2ToG2(Bls12MapFp2ToG2), -} - -impl FromStr for EthereumBuiltin { - type Err = (); - - fn from_str(name: &str) -> Result { - match name { - "identity" => Ok(Self::Identity(Identity)), - "ecrecover" => Ok(Self::EcRecover(EcRecover)), - "sha256" => Ok(Self::Sha256(Sha256)), - "ripemd160" => Ok(Self::Ripemd160(Ripemd160)), - "modexp" => Ok(Self::Modexp(Modexp)), - "alt_bn128_add" => Ok(Self::Bn128Add(Bn128Add)), - "alt_bn128_mul" => Ok(Self::Bn128Mul(Bn128Mul)), - "alt_bn128_pairing" => Ok(Self::Bn128Pairing(Bn128Pairing)), - "blake2_f" => Ok(Self::Blake2F(Blake2F)), - "kzg" => Ok(Self::Kgz(Kzg)), - "bls12_381_g1_add" => Ok(Self::Bls12G1Add(Bls12G1Add)), - "bls12_381_g1_mul" => Ok(Self::Bls12G1Mul(Bls12G1Mul)), - "bls12_381_g2_add" => Ok(Self::Bls12G2Add(Bls12G2Add)), - "bls12_381_g2_mul" => Ok(Self::Bls12G2Mul(Bls12G2Mul)), - "bls12_381_pairing" => Ok(Self::Bls12Pairing(Bls12Pairing)), - "bls12_381_fp_to_g1" => Ok(Self::Bls12MapFpToG1(Bls12MapFpToG1)), - "bls12_381_fp2_to_g2" => Ok(Self::Bls12MapFp2ToG2(Bls12MapFp2ToG2)), - _ => Err(()), - } - } -} - -impl Implementation for EthereumBuiltin { - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { - match self { - Self::Identity(inner) => inner.execute(input, output), - Self::EcRecover(inner) => inner.execute(input, output), - Self::Sha256(inner) => inner.execute(input, output), - Self::Ripemd160(inner) => inner.execute(input, output), - Self::Modexp(inner) => inner.execute(input, output), - Self::Bn128Add(inner) => inner.execute(input, output), - Self::Bn128Mul(inner) => inner.execute(input, output), - Self::Bn128Pairing(inner) => inner.execute(input, output), - Self::Blake2F(inner) => inner.execute(input, output), - Self::Kgz(inner) => inner.execute(input, output), - Self::Bls12G1Add(inner) => inner.execute(input, output), - Self::Bls12G1Mul(inner) => inner.execute(input, output), - Self::Bls12G2Add(inner) => inner.execute(input, output), - Self::Bls12G2Mul(inner) => inner.execute(input, output), - Self::Bls12Pairing(inner) => inner.execute(input, output), - Self::Bls12MapFpToG1(inner) => inner.execute(input, output), - Self::Bls12MapFp2ToG2(inner) => inner.execute(input, output), - } - } -} - -#[derive(Debug)] -/// The identity builtin -pub struct Identity; - -#[derive(Debug)] -/// The EC Recover builtin -pub struct EcRecover; - -#[derive(Debug)] -/// The Sha256 builtin -pub struct Sha256; - -#[derive(Debug)] -/// The Ripemd160 builtin -pub struct Ripemd160; - -#[derive(Debug)] -/// The Modexp builtin -pub struct Modexp; - -#[derive(Debug)] -/// The Bn128Add builtin -pub struct Bn128Add; - -#[derive(Debug)] -/// The Bn128Mul builtin -pub struct Bn128Mul; - -#[derive(Debug)] -/// The Bn128Pairing builtin -pub struct Bn128Pairing; - -#[derive(Debug)] -/// The Blake2F builtin -pub struct Blake2F; - -#[derive(Debug)] -/// The Kzg builtin -pub struct Kzg; - -#[derive(Debug)] -/// The Bls12G1Add builtin. -pub struct Bls12G1Add; - -#[derive(Debug)] -/// The Bls12G1Mul builtin. -pub struct Bls12G1Mul; - -#[derive(Debug)] -/// The Bls12G2Add builtin. -pub struct Bls12G2Add; - -#[derive(Debug)] -/// The Bls12G2Mul builtin. -pub struct Bls12G2Mul; - -#[derive(Debug)] -/// The Bls12Pairing builtin. -pub struct Bls12Pairing; - -#[derive(Debug)] -/// The Bls12MapFpToG1 builtin. -pub struct Bls12MapFpToG1; - -#[derive(Debug)] -/// The Bls12MapFp2ToG2 builtin. -pub struct Bls12MapFp2ToG2; - -impl Implementation for Identity { - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { - output.write(0, input); - Ok(()) - } -} - -impl Implementation for EcRecover { - fn execute(&self, i: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { - let len = min(i.len(), 128); - - let mut input = [0; 128]; - input[..len].copy_from_slice(&i[..len]); - - let mut hash = [0; 32]; - hash.copy_from_slice(&input[0..32]); - - let v = H256::from_slice(&input[32..64]); - let bit = match v[31] { - 27 | 28 if v.0[..31] == [0; 31] => v[31] - 27, - _ => { - return Ok(()); - } - }; - let mut signature = [0; 64]; - signature[..64].copy_from_slice(&input[64..128]); - let signature = libsecp256k1::Signature::parse_standard(&signature); - if bit <= 1 { - if let Ok(signature) = signature { - // The builtin allows/requires all-zero messages to be valid to - // recover the public key. Use of such messages is disallowed in - // `rust-secp256k1` and this is a workaround for that. It is not an - // openethereum-level error to fail here; instead we return all - // zeroes and let the caller interpret that outcome. - let message = libsecp256k1::Message::parse(&hash); - let recovery_id = libsecp256k1::RecoveryId::parse(bit); - if let Ok(recovery_id) = recovery_id { - if let Ok(p) = libsecp256k1::recover(&message, &signature, &recovery_id) { - let r = keccak(&p.serialize()[1..65]); - output.write(0, &[0; 12]); - output.write(12, &r.as_bytes()[12..]); - } - } - } - } - - Ok(()) - } -} - -impl Implementation for Sha256 { - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { - let mut hasher = sha2::Sha256::new(); - hasher.update(input); - output.write(0, &hasher.finalize()); - Ok(()) - } -} - -impl Implementation for Blake2F { - /// Format of `input`: - /// [4 bytes for rounds][64 bytes for h][128 bytes for m][8 bytes for t_0][8 bytes for t_1][1 byte for f] - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { - const BLAKE2_F_ARG_LEN: usize = 213; - const PROOF: &str = "Checked the length of the input above; qed"; - - if input.len() != BLAKE2_F_ARG_LEN { - trace!(target: "builtin", "input length for Blake2 F precompile should be exactly 213 bytes, was {}", input.len()); - return Err("input length for Blake2 F precompile should be exactly 213 bytes"); - } - - let mut cursor = Cursor::new(input); - let rounds = cursor.read_u32::().expect(PROOF); - - // state vector, h - let mut h = [0u64; 8]; - for state_word in &mut h { - *state_word = cursor.read_u64::().expect(PROOF); - } - - // message block vector, m - let mut m = [0u64; 16]; - for msg_word in &mut m { - *msg_word = cursor.read_u64::().expect(PROOF); - } - - // 2w-bit offset counter, t - let t = [ - cursor.read_u64::().expect(PROOF), - cursor.read_u64::().expect(PROOF), - ]; - - // final block indicator flag, "f" - let f = match input.last() { - Some(1) => true, - Some(0) => false, - _ => { - trace!(target: "builtin", "incorrect final block indicator flag, was: {:?}", input.last()); - return Err("incorrect final block indicator flag"); - } - }; - - compress(&mut h, m, t, f, rounds as usize); - - let mut output_buf = [0u8; u64::BITS as usize]; - for (i, state_word) in h.iter().enumerate() { - output_buf[i * 8..(i + 1) * 8].copy_from_slice(&state_word.to_le_bytes()); - } - output.write(0, &output_buf[..]); - Ok(()) - } -} - -impl Implementation for Ripemd160 { - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { - output.write(0, &[0; 12][..]); - output.write(12, &ripemd::Ripemd160::digest(input)); - Ok(()) - } -} - -impl Implementation for Modexp { - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { - let mut reader = input.chain(io::repeat(0)); - let mut buf = [0; 32]; - - // read lengths as usize. - // ignoring the first 24 bytes might technically lead us to fall out of consensus, - // but so would running out of addressable memory! - let mut read_len = |reader: &mut io::Chain<&[u8], io::Repeat>| { - reader - .read_exact(&mut buf[..]) - .expect("reading from zero-extended memory cannot fail; qed"); - let mut len_bytes = [0u8; 8]; - len_bytes.copy_from_slice(&buf[24..]); - u64::from_be_bytes(len_bytes) as usize - }; - - let base_len = read_len(&mut reader); - let exp_len = read_len(&mut reader); - let mod_len = read_len(&mut reader); - - // Gas formula allows arbitrary large exp_len when base and modulus are empty, so we need to handle empty base first. - let r = if base_len == 0 && mod_len == 0 { - BigUint::zero() - } else { - // read the numbers themselves. - let mut buf = vec![0; max(mod_len, max(base_len, exp_len))]; - let mut read_num = |reader: &mut io::Chain<&[u8], io::Repeat>, len: usize| { - reader - .read_exact(&mut buf[..len]) - .expect("reading from zero-extended memory cannot fail; qed"); - BigUint::from_bytes_be(&buf[..len]) - }; - - let base = read_num(&mut reader, base_len); - let exponent = read_num(&mut reader, exp_len); - let modulus = read_num(&mut reader, mod_len); - - if modulus.is_zero() || modulus.is_one() { - BigUint::zero() - } else { - base.modpow(&exponent, &modulus) - } - }; - - // write output to given memory, left padded and same length as the modulus. - let bytes = r.to_bytes_be(); - - // always true except in the case of zero-length modulus, which leads to - // output of length and value 1. - if bytes.len() <= mod_len { - let res_start = mod_len - bytes.len(); - output.write(res_start, &bytes); - } - - Ok(()) - } -} - -fn read_fr(reader: &mut io::Chain<&[u8], io::Repeat>) -> Result { - let mut buf = [0u8; 32]; - - reader - .read_exact(&mut buf[..]) - .expect("reading from zero-extended memory cannot fail; qed"); - bn::Fr::from_slice(&buf[0..32]).map_err(|_| "Invalid field element") -} - -fn read_point(reader: &mut io::Chain<&[u8], io::Repeat>) -> Result { - use bn::{AffineG1, Fq, Group, G1}; - - let mut buf = [0u8; 32]; - - reader - .read_exact(&mut buf[..]) - .expect("reading from zero-extended memory cannot fail; qed"); - let px = Fq::from_slice(&buf[0..32]).map_err(|_| "Invalid point x coordinate")?; - - reader - .read_exact(&mut buf[..]) - .expect("reading from zero-extended memory cannot fail; qed"); - let py = Fq::from_slice(&buf[0..32]).map_err(|_| "Invalid point y coordinate")?; - Ok(if px == Fq::zero() && py == Fq::zero() { - G1::zero() - } else { - AffineG1::new(px, py) - .map_err(|_| "Invalid curve point")? - .into() - }) -} - -impl Implementation for Bn128Add { - // Can fail if any of the 2 points does not belong the bn128 curve - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { - use bn::AffineG1; - - let mut padded_input = input.chain(io::repeat(0)); - let p1 = read_point(&mut padded_input)?; - let p2 = read_point(&mut padded_input)?; - - let mut write_buf = [0u8; 64]; - if let Some(sum) = AffineG1::from_jacobian(p1 + p2) { - // point not at infinity - sum.x() - .to_big_endian(&mut write_buf[0..32]) - .expect("Cannot fail since 0..32 is 32-byte length"); - sum.y() - .to_big_endian(&mut write_buf[32..64]) - .expect("Cannot fail since 32..64 is 32-byte length"); - } - output.write(0, &write_buf); - - Ok(()) - } -} - -impl Implementation for Bn128Mul { - // Can fail if first paramter (bn128 curve point) does not actually belong to the curve - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { - use bn::AffineG1; - - let mut padded_input = input.chain(io::repeat(0)); - let p = read_point(&mut padded_input)?; - let fr = read_fr(&mut padded_input)?; - - let mut write_buf = [0u8; 64]; - if let Some(sum) = AffineG1::from_jacobian(p * fr) { - // point not at infinity - sum.x() - .to_big_endian(&mut write_buf[0..32]) - .expect("Cannot fail since 0..32 is 32-byte length"); - sum.y() - .to_big_endian(&mut write_buf[32..64]) - .expect("Cannot fail since 32..64 is 32-byte length"); - } - output.write(0, &write_buf); - Ok(()) - } -} - -impl Bn128Pairing { - fn execute_with_error(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { - use bn::{pairing_batch, AffineG1, AffineG2, Fq, Fq2, Group, Gt, G1, G2}; - - let ret_val = if input.is_empty() { - U256::one() - } else { - // (a, b_a, b_b - each 64-byte affine coordinates) - let elements = input.len() / 192; - let mut vals = Vec::new(); - for idx in 0..elements { - let a_x = Fq::from_slice(&input[idx * 192..idx * 192 + 32]) - .map_err(|_| "Invalid a argument x coordinate")?; - - let a_y = Fq::from_slice(&input[idx * 192 + 32..idx * 192 + 64]) - .map_err(|_| "Invalid a argument y coordinate")?; - - let b_a_y = Fq::from_slice(&input[idx * 192 + 64..idx * 192 + 96]) - .map_err(|_| "Invalid b argument imaginary coeff x coordinate")?; - - let b_a_x = Fq::from_slice(&input[idx * 192 + 96..idx * 192 + 128]) - .map_err(|_| "Invalid b argument imaginary coeff y coordinate")?; - - let b_b_y = Fq::from_slice(&input[idx * 192 + 128..idx * 192 + 160]) - .map_err(|_| "Invalid b argument real coeff x coordinate")?; - - let b_b_x = Fq::from_slice(&input[idx * 192 + 160..idx * 192 + 192]) - .map_err(|_| "Invalid b argument real coeff y coordinate")?; - - let b_a = Fq2::new(b_a_x, b_a_y); - let b_b = Fq2::new(b_b_x, b_b_y); - let b = if b_a.is_zero() && b_b.is_zero() { - G2::zero() - } else { - G2::from( - AffineG2::new(b_a, b_b).map_err(|_| "Invalid b argument - not on curve")?, - ) - }; - let a = if a_x.is_zero() && a_y.is_zero() { - G1::zero() - } else { - G1::from( - AffineG1::new(a_x, a_y).map_err(|_| "Invalid a argument - not on curve")?, - ) - }; - vals.push((a, b)); - } - - let mul = pairing_batch(&vals); - - if mul == Gt::one() { - U256::one() - } else { - U256::zero() - } - }; - - let buf = ret_val.to_big_endian(); - output.write(0, &buf); - - Ok(()) - } -} - -impl Implementation for Bn128Pairing { - /// Can fail if: - /// - input length is not a multiple of 192 - /// - any of odd points does not belong to bn128 curve - /// - any of even points does not belong to the twisted bn128 curve over the field F_p^2 = F_p[i] / (i^2 + 1) - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { - if input.len() % 192 != 0 { - return Err("Invalid input length, must be multiple of 192 (3 * (32*2))"); - } - - if let Err(err) = self.execute_with_error(input, output) { - trace!(target: "builtin", "Pairing error: {:?}", err); - return Err(err); - } - Ok(()) - } -} - -impl Implementation for Kzg { - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { - // Get and verify KZG input. - let kzg_input: kzg::KzgInput = input.try_into()?; - let kzg_settings = kzg::EnvKzgSettings::Default; - if !kzg_input.verify_kzg_proof(&kzg_settings.get()) { - return Err("BlobVerifyKzgProofFailed"); - } - output.write(0, kzg::RETURN_VALUE.as_slice()); - Ok(()) - } -} - -impl Implementation for Bls12G1Add { - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { - let out = bls::g1_add::g1_add(input)?; - output.write(0, &out); - Ok(()) - } -} - -impl Implementation for Bls12G1Mul { - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { - let out = bls::g1_mul::g1_mul(input)?; - output.write(0, out.as_slice()); - Ok(()) - } -} - -impl Implementation for Bls12G2Add { - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { - let out = bls::g2_add::g2_add(input)?; - output.write(0, &out); - Ok(()) - } -} - -impl Implementation for Bls12G2Mul { - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { - let out = bls::g2_mul::g2_mul(input)?; - output.write(0, out.as_slice()); - Ok(()) - } -} - -impl Implementation for Bls12Pairing { - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { - let out = bls::pairing::pairing(input)?; - output.write(0, out.as_slice()); - Ok(()) - } -} - -impl Implementation for Bls12MapFpToG1 { - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { - let out = bls::map_fp_to_g1::map_fp_to_g1(input)?; - output.write(0, out.as_slice()); - Ok(()) - } -} - -impl Implementation for Bls12MapFp2ToG2 { - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { - let out = bls::map_fp2_to_g2::map_fp2_to_g2(input)?; - output.write(0, out.as_slice()); - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::{ - Bls12G1MulPricer, Bls12G2MulPricer, Bls12PairingPricer, Builtin, EthereumBuiltin, FromStr, - Implementation, Linear, ModexpPricer, Pricing, - }; - use ethereum_types::U256; - use ethjson::spec::builtin::{ - AltBn128Pairing as JsonAltBn128PairingPricing, Builtin as JsonBuiltin, - Linear as JsonLinearPricing, Pricing as JsonPricing, PricingAt, - }; - use hex_literal::hex; - use maplit::btreemap; - use parity_bytes::BytesRef; - use std::convert::TryFrom; - - #[test] - fn blake2f_cost() { - let f = Builtin { - pricer: btreemap![0 => Pricing::Blake2F(123)], - native: EthereumBuiltin::from_str("blake2_f").unwrap(), - }; - // 5 rounds - let input = hex!("0000000548c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); - let mut output = [0u8; 64]; - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) - .unwrap(); - - assert_eq!(f.cost(&input[..], 0), U256::from(123 * 5)); - } - - #[test] - fn blake2f_cost_on_invalid_length() { - let f = Builtin { - pricer: btreemap![0 => Pricing::Blake2F(123)], - native: EthereumBuiltin::from_str("blake2_f").expect("known builtin"), - }; - // invalid input (too short) - let input = hex!("00"); - - assert_eq!(f.cost(&input[..], 0), U256::from(0)); - } - - #[test] - fn blake2_f_is_err_on_invalid_length() { - let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); - // Test vector 1 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-1 - let input = hex!("00000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); - let mut out = [0u8; 64]; - - let result = blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..])); - assert!(result.is_err()); - assert_eq!( - result.unwrap_err(), - "input length for Blake2 F precompile should be exactly 213 bytes" - ); - } - - #[test] - fn blake2_f_is_err_on_invalid_length_2() { - let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); - // Test vector 2 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-2 - let input = hex!("000000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); - let mut out = [0u8; 64]; - - let result = blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..])); - assert!(result.is_err()); - assert_eq!( - result.unwrap_err(), - "input length for Blake2 F precompile should be exactly 213 bytes" - ); - } - - #[test] - fn blake2_f_is_err_on_bad_finalization_flag() { - let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); - // Test vector 3 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-3 - let input = hex!("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000002"); - let mut out = [0u8; 64]; - - let result = blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..])); - assert!(result.is_err()); - assert_eq!(result.unwrap_err(), "incorrect final block indicator flag"); - } - - #[test] - fn blake2_f_zero_rounds_is_ok_test_vector_4() { - let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); - // Test vector 4 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-4 - let input = hex!("0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); - let expected = hex!("08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b"); - let mut output = [0u8; 64]; - blake2 - .execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) - .unwrap(); - assert_eq!(&output[..], &expected[..]); - } - - #[test] - fn blake2_f_test_vector_5() { - let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); - // Test vector 5 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-5 - let input = hex!("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); - let expected = hex!("ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"); - let mut out = [0u8; 64]; - blake2 - .execute(&input[..], &mut BytesRef::Fixed(&mut out[..])) - .unwrap(); - assert_eq!(&out[..], &expected[..]); - } - - #[test] - fn blake2_f_test_vector_6() { - let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); - // Test vector 6 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-6 - let input = hex!("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000"); - let expected = hex!("75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735"); - let mut out = [0u8; 64]; - blake2 - .execute(&input[..], &mut BytesRef::Fixed(&mut out[..])) - .unwrap(); - assert_eq!(&out[..], &expected[..]); - } - - #[test] - fn blake2_f_test_vector_7() { - let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); - // Test vector 7 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-7 - let input = hex!("0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); - let expected = hex!("b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421"); - let mut out = [0u8; 64]; - blake2 - .execute(&input[..], &mut BytesRef::Fixed(&mut out[..])) - .unwrap(); - assert_eq!(&out[..], &expected[..]); - } - - #[ignore] - #[test] - fn blake2_f_test_vector_8() { - let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); - // Test vector 8 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-8 - // Note this test is slow, 4294967295/0xffffffff rounds take a while. - let input = hex!("ffffffff48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); - let expected = hex!("fc59093aafa9ab43daae0e914c57635c5402d8e3d2130eb9b3cc181de7f0ecf9b22bf99a7815ce16419e200e01846e6b5df8cc7703041bbceb571de6631d2615"); - let mut out = [0u8; 64]; - blake2 - .execute(&input[..], &mut BytesRef::Fixed(&mut out[..])) - .unwrap(); - assert_eq!(&out[..], &expected[..]); - } - - #[test] - fn identity() { - let f = EthereumBuiltin::from_str("identity").unwrap(); - let i = [0u8, 1, 2, 3]; - - let mut o2 = [255u8; 2]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o2[..])) - .expect("Builtin should not fail"); - assert_eq!(i[0..2], o2); - - let mut o4 = [255u8; 4]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o4[..])) - .expect("Builtin should not fail"); - assert_eq!(i, o4); - - let mut o8 = [255u8; 8]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])) - .expect("Builtin should not fail"); - assert_eq!(i, o8[..4]); - assert_eq!([255u8; 4], o8[4..]); - } - - #[test] - fn sha256() { - let f = EthereumBuiltin::from_str("sha256").unwrap(); - let i = [0u8; 0]; - - let mut o = [255u8; 32]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])) - .expect("Builtin should not fail"); - assert_eq!( - &o[..], - hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") - ); - - let mut o8 = [255u8; 8]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])) - .expect("Builtin should not fail"); - assert_eq!(&o8[..], hex!("e3b0c44298fc1c14")); - - let mut o34 = [255u8; 34]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..])) - .expect("Builtin should not fail"); - assert_eq!( - &o34[..], - &hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855ffff")[..] - ); - - let mut ov = vec![]; - f.execute(&i[..], &mut BytesRef::Flexible(&mut ov)) - .expect("Builtin should not fail"); - assert_eq!( - &ov[..], - &hex!("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")[..] - ); - } - - #[test] - fn ripemd160() { - let f = EthereumBuiltin::from_str("ripemd160").unwrap(); - let i = [0u8; 0]; - - let mut o = [255u8; 32]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])) - .expect("Builtin should not fail"); - assert_eq!( - &o[..], - &hex!("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31")[..] - ); - - let mut o8 = [255u8; 8]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])) - .expect("Builtin should not fail"); - assert_eq!(&o8[..], &hex!("0000000000000000")[..]); - - let mut o34 = [255u8; 34]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..])) - .expect("Builtin should not fail"); - assert_eq!( - &o34[..], - &hex!("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31ffff")[..] - ); - } - - #[test] - fn ecrecover() { - let f = EthereumBuiltin::from_str("ecrecover").unwrap(); - - let i = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03"); - - let mut o = [255u8; 32]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])) - .expect("Builtin should not fail"); - assert_eq!( - &o[..], - &hex!("000000000000000000000000c08b5542d177ac6686946920409741463a15dddb")[..] - ); - - let mut o8 = [255u8; 8]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o8[..])) - .expect("Builtin should not fail"); - assert_eq!(&o8[..], &hex!("0000000000000000")[..]); - - let mut o34 = [255u8; 34]; - f.execute(&i[..], &mut BytesRef::Fixed(&mut o34[..])) - .expect("Builtin should not fail"); - assert_eq!( - &o34[..], - &hex!("000000000000000000000000c08b5542d177ac6686946920409741463a15dddbffff")[..] - ); - - let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001a650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03"); - let mut o = [255u8; 32]; - f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])) - .expect("Builtin should not fail"); - assert_eq!( - &o[..], - &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..] - ); - - let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000"); - let mut o = [255u8; 32]; - f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])) - .expect("Builtin should not fail"); - assert_eq!( - &o[..], - &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..] - ); - - let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b"); - let mut o = [255u8; 32]; - f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])) - .expect("Builtin should not fail"); - assert_eq!( - &o[..], - &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..] - ); - - let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000001b"); - let mut o = [255u8; 32]; - f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])) - .expect("Builtin should not fail"); - assert_eq!( - &o[..], - &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..] - ); - - let i_bad = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - let mut o = [255u8; 32]; - f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])) - .expect("Builtin should not fail"); - assert_eq!( - &o[..], - &hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..] - ); - - // TODO: Should this (corrupted version of the above) fail rather than returning some address? - /* let i_bad = FromHex::from_hex("48173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap(); - let mut o = [255u8; 32]; - f.execute(&i_bad[..], &mut BytesRef::Fixed(&mut o[..])); - assert_eq!(&o[..], &(FromHex::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap())[..]);*/ - } - - #[test] - fn modexp() { - let f = Builtin { - pricer: btreemap![0 => Pricing::Modexp(ModexpPricer { divisor: 20, is_eip_2565: false })], - native: EthereumBuiltin::from_str("modexp").unwrap(), - }; - - // test for potential gas cost multiplication overflow - { - let input = hex!("0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000003b27bafd00000000000000000000000000000000000000000000000000000000503c8ac3"); - let expected_cost = U256::max_value(); - assert_eq!(f.cost(&input[..], 0), expected_cost); - } - - // test for potential exp len overflow - { - let input = hex!( - " - 00000000000000000000000000000000000000000000000000000000000000ff - 2a1e530000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000" - ); - - let mut output = vec![0u8; 32]; - let expected = hex!("0000000000000000000000000000000000000000000000000000000000000000"); - let expected_cost = U256::max_value(); - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) - .expect("Builtin should fail"); - assert_eq!(output, expected); - assert_eq!(f.cost(&input[..], 0), expected_cost); - } - - // fermat's little theorem example. - { - let input = hex!( - " - 0000000000000000000000000000000000000000000000000000000000000001 - 0000000000000000000000000000000000000000000000000000000000000020 - 0000000000000000000000000000000000000000000000000000000000000020 - 03 - fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e - fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" - ); - - let mut output = vec![0u8; 32]; - let expected = hex!("0000000000000000000000000000000000000000000000000000000000000001"); - let expected_cost = 13056; - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) - .expect("Builtin should not fail"); - assert_eq!(output, expected); - assert_eq!(f.cost(&input[..], 0), expected_cost.into()); - } - - // second example from EIP: zero base. - { - let input = hex!( - " - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000020 - 0000000000000000000000000000000000000000000000000000000000000020 - fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e - fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" - ); - - let mut output = vec![0u8; 32]; - let expected = hex!("0000000000000000000000000000000000000000000000000000000000000000"); - let expected_cost = 13056; - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) - .expect("Builtin should not fail"); - assert_eq!(output, expected); - assert_eq!(f.cost(&input[..], 0), expected_cost.into()); - } - - // another example from EIP: zero-padding - { - let input = hex!( - " - 0000000000000000000000000000000000000000000000000000000000000001 - 0000000000000000000000000000000000000000000000000000000000000002 - 0000000000000000000000000000000000000000000000000000000000000020 - 03 - ffff - 80" - ); - - let mut output = vec![0u8; 32]; - let expected = hex!("3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab"); - let expected_cost = 768; - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) - .expect("Builtin should not fail"); - assert_eq!(output, expected); - assert_eq!(f.cost(&input[..], 0), expected_cost.into()); - } - - // zero-length modulus. - { - let input = hex!( - " - 0000000000000000000000000000000000000000000000000000000000000001 - 0000000000000000000000000000000000000000000000000000000000000002 - 0000000000000000000000000000000000000000000000000000000000000000 - 03 - ffff" - ); - - let mut output = vec![]; - let expected_cost = 0; - - f.execute(&input[..], &mut BytesRef::Flexible(&mut output)) - .expect("Builtin should not fail"); - assert_eq!(output.len(), 0); // shouldn't have written any output. - assert_eq!(f.cost(&input[..], 0), expected_cost.into()); - } - } - - #[test] - fn bn128_add() { - let f = Builtin { - pricer: btreemap![0 => Pricing::Linear(Linear { base: 0, word: 0 })], - native: EthereumBuiltin::from_str("alt_bn128_add").unwrap(), - }; - - // zero-points additions - { - let input = hex!( - " - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000" - ); - - let mut output = vec![0u8; 64]; - let expected = hex!( - " - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000" - ); - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) - .expect("Builtin should not fail"); - assert_eq!(output, &expected[..]); - } - - // no input, should not fail - { - let mut empty = [0u8; 0]; - let input = BytesRef::Fixed(&mut empty); - - let mut output = vec![0u8; 64]; - let expected = hex!( - " - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000" - ); - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) - .expect("Builtin should not fail"); - assert_eq!(output, &expected[..]); - } - - // should fail - point not on curve - { - let input = hex!( - " - 1111111111111111111111111111111111111111111111111111111111111111 - 1111111111111111111111111111111111111111111111111111111111111111 - 1111111111111111111111111111111111111111111111111111111111111111 - 1111111111111111111111111111111111111111111111111111111111111111" - ); - - let mut output = [0u8; 64]; - - let res = f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])); - assert!(res.is_err(), "There should be built-in error here"); - } - } - - #[test] - fn bn128_mul() { - let f = Builtin { - pricer: btreemap![0 => Pricing::Linear(Linear { base: 0, word: 0 })], - native: EthereumBuiltin::from_str("alt_bn128_mul").unwrap(), - }; - - // zero-point multiplication - { - let input = hex!( - " - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000 - 0200000000000000000000000000000000000000000000000000000000000000" - ); - - let mut output = vec![0u8; 64]; - let expected = hex!( - " - 0000000000000000000000000000000000000000000000000000000000000000 - 0000000000000000000000000000000000000000000000000000000000000000" - ); - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) - .expect("Builtin should not fail"); - assert_eq!(output, &expected[..]); - } - - // should fail - point not on curve - { - let input = hex!( - " - 1111111111111111111111111111111111111111111111111111111111111111 - 1111111111111111111111111111111111111111111111111111111111111111 - 0f00000000000000000000000000000000000000000000000000000000000000" - ); - - let mut output = [0u8; 64]; - - let res = f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])); - assert!(res.is_err(), "There should be built-in error here"); - } - } - - fn builtin_pairing() -> Builtin { - Builtin { - pricer: btreemap![0 => Pricing::Linear(Linear { base: 0, word: 0 })], - native: EthereumBuiltin::from_str("alt_bn128_pairing").unwrap(), - } - } - - fn empty_test(f: Builtin, expected: Vec) { - let mut empty = [0u8; 0]; - let input = BytesRef::Fixed(&mut empty); - - let mut output = vec![0u8; expected.len()]; - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) - .expect("Builtin should not fail"); - assert_eq!(output, expected); - } - - fn error_test(f: Builtin, input: &[u8], msg_contains: Option<&str>) { - let mut output = [0u8; 64]; - let res = f.execute(input, &mut BytesRef::Fixed(&mut output[..])); - msg_contains.map_or_else( - || { - assert!(res.is_err(), "There should be built-in error here"); - }, - |msg| { - if let Err(e) = res { - if !e.contains(msg) { - panic!( - "There should be error containing '{}' here, but got: '{}'", - msg, e - ); - } - } - }, - ) - } - - #[test] - fn bn128_pairing_empty() { - // should not fail, because empty input is a valid input of 0 elements - empty_test( - builtin_pairing(), - hex!("0000000000000000000000000000000000000000000000000000000000000001").to_vec(), - ); - } - - #[test] - fn bn128_pairing_notcurve() { - // should fail - point not on curve - error_test( - builtin_pairing(), - &hex!( - " - 1111111111111111111111111111111111111111111111111111111111111111 - 1111111111111111111111111111111111111111111111111111111111111111 - 1111111111111111111111111111111111111111111111111111111111111111 - 1111111111111111111111111111111111111111111111111111111111111111 - 1111111111111111111111111111111111111111111111111111111111111111 - 1111111111111111111111111111111111111111111111111111111111111111" - ), - Some("not on curve"), - ); - } - - #[test] - fn bn128_pairing_fragmented() { - // should fail - input length is invalid - error_test( - builtin_pairing(), - &hex!( - " - 1111111111111111111111111111111111111111111111111111111111111111 - 1111111111111111111111111111111111111111111111111111111111111111 - 111111111111111111111111111111" - ), - Some("Invalid input length"), - ); - } - - #[test] - #[should_panic] - fn from_unknown_linear() { - let _ = EthereumBuiltin::from_str("foo").unwrap(); - } - - #[test] - fn is_active() { - let pricer = Pricing::Linear(Linear { base: 10, word: 20 }); - let b = Builtin { - pricer: btreemap![100_000 => pricer], - native: EthereumBuiltin::from_str("identity").unwrap(), - }; - - assert!(!b.is_active(99_999)); - assert!(b.is_active(100_000)); - assert!(b.is_active(100_001)); - } - - #[test] - fn from_named_linear() { - let pricer = Pricing::Linear(Linear { base: 10, word: 20 }); - let b = Builtin { - pricer: btreemap![0 => pricer], - native: EthereumBuiltin::from_str("identity").unwrap(), - }; - - assert_eq!(b.cost(&[0; 0], 0), U256::from(10)); - assert_eq!(b.cost(&[0; 1], 0), U256::from(30)); - assert_eq!(b.cost(&[0; 32], 0), U256::from(30)); - assert_eq!(b.cost(&[0; 33], 0), U256::from(50)); - - let i = [0u8, 1, 2, 3]; - let mut o = [255u8; 4]; - b.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])) - .expect("Builtin should not fail"); - assert_eq!(i, o); - } - - #[test] - fn from_json() { - let b = Builtin::try_from(ethjson::spec::Builtin { - name: "identity".to_owned(), - pricing: btreemap![ - 0 => PricingAt { - info: None, - price: JsonPricing::Linear(JsonLinearPricing { base: 10, word: 20 }) - } - ], - }) - .expect("known builtin"); - - assert_eq!(b.cost(&[0; 0], 0), U256::from(10)); - assert_eq!(b.cost(&[0; 1], 0), U256::from(30)); - assert_eq!(b.cost(&[0; 32], 0), U256::from(30)); - assert_eq!(b.cost(&[0; 33], 0), U256::from(50)); - - let i = [0u8, 1, 2, 3]; - let mut o = [255u8; 4]; - b.execute(&i[..], &mut BytesRef::Fixed(&mut o[..])) - .expect("Builtin should not fail"); - assert_eq!(i, o); - } - - #[test] - fn bn128_pairing_eip1108_transition() { - let b = Builtin::try_from(JsonBuiltin { - name: "alt_bn128_pairing".to_owned(), - pricing: btreemap![ - 10 => PricingAt { - info: None, - price: JsonPricing::AltBn128Pairing(JsonAltBn128PairingPricing { - base: 100_000, - pair: 80_000, - }), - }, - 20 => PricingAt { - info: None, - price: JsonPricing::AltBn128Pairing(JsonAltBn128PairingPricing { - base: 45_000, - pair: 34_000, - }), - } - ], - }) - .unwrap(); - - assert_eq!( - b.cost(&[0; 192 * 3], 10), - U256::from(340_000), - "80 000 * 3 + 100 000 == 340 000" - ); - assert_eq!( - b.cost(&[0; 192 * 7], 20), - U256::from(283_000), - "34 000 * 7 + 45 000 == 283 000" - ); - } - - #[test] - fn bn128_add_eip1108_transition() { - let b = Builtin::try_from(JsonBuiltin { - name: "alt_bn128_add".to_owned(), - pricing: btreemap![ - 10 => PricingAt { - info: None, - price: JsonPricing::Linear(JsonLinearPricing { - base: 500, - word: 0, - }), - }, - 20 => PricingAt { - info: None, - price: JsonPricing::Linear(JsonLinearPricing { - base: 150, - word: 0, - }), - } - ], - }) - .unwrap(); - - assert_eq!(b.cost(&[0; 192], 10), U256::from(500)); - assert_eq!( - b.cost(&[0; 10], 20), - U256::from(150), - "after istanbul hardfork gas cost for add should be 150" - ); - } - - #[test] - fn bn128_mul_eip1108_transition() { - let b = Builtin::try_from(JsonBuiltin { - name: "alt_bn128_mul".to_owned(), - pricing: btreemap![ - 10 => PricingAt { - info: None, - price: JsonPricing::Linear(JsonLinearPricing { - base: 40_000, - word: 0, - }), - }, - 20 => PricingAt { - info: None, - price: JsonPricing::Linear(JsonLinearPricing { - base: 6_000, - word: 0, - }), - } - ], - }) - .unwrap(); - - assert_eq!(b.cost(&[0; 192], 10), U256::from(40_000)); - assert_eq!( - b.cost(&[0; 10], 20), - U256::from(6_000), - "after istanbul hardfork gas cost for mul should be 6 000" - ); - } - - #[test] - fn multimap_use_most_recent_on_activate() { - let b = Builtin::try_from(JsonBuiltin { - name: "alt_bn128_mul".to_owned(), - pricing: btreemap![ - 10 => PricingAt { - info: None, - price: JsonPricing::Linear(JsonLinearPricing { - base: 40_000, - word: 0, - }), - }, - 20 => PricingAt { - info: None, - price: JsonPricing::Linear(JsonLinearPricing { - base: 6_000, - word: 0, - }) - }, - 100 => PricingAt { - info: None, - price: JsonPricing::Linear(JsonLinearPricing { - base: 1_337, - word: 0, - }) - } - ], - }) - .unwrap(); - - assert_eq!( - b.cost(&[0; 2], 0), - U256::zero(), - "not activated yet; should be zero" - ); - assert_eq!(b.cost(&[0; 3], 10), U256::from(40_000), "use price #1"); - assert_eq!(b.cost(&[0; 4], 20), U256::from(6_000), "use price #2"); - assert_eq!(b.cost(&[0; 1], 99), U256::from(6_000), "use price #2"); - assert_eq!(b.cost(&[0; 1], 100), U256::from(1_337), "use price #3"); - assert_eq!( - b.cost(&[0; 1], u64::MAX), - U256::from(1_337), - "use price #3 indefinitely" - ); - } - - #[test] - fn multimap_use_last_with_same_activate_at() { - let b = Builtin::try_from(JsonBuiltin { - name: "alt_bn128_mul".to_owned(), - pricing: btreemap![ - 1 => PricingAt { - info: None, - price: JsonPricing::Linear(JsonLinearPricing { - base: 40_000, - word: 0, - }), - }, - 1 => PricingAt { - info: None, - price: JsonPricing::Linear(JsonLinearPricing { - base: 6_000, - word: 0, - }), - }, - 1 => PricingAt { - info: None, - price: JsonPricing::Linear(JsonLinearPricing { - base: 1_337, - word: 0, - }), - } - ], - }) - .unwrap(); - - assert_eq!(b.cost(&[0; 1], 0), U256::from(0), "not activated yet"); - assert_eq!(b.cost(&[0; 1], 1), U256::from(1_337), "use price #3"); - } - #[test] - fn bls12_381_g1_add() { - let f = Builtin { - pricer: btreemap![0 => Pricing::Linear(Linear { base: 375, word: 0 })], - native: EthereumBuiltin::from_str("bls12_381_g1_add").unwrap(), - }; - - let input = hex!(" - 00000000000000000000000000000000117dbe419018f67844f6a5e1b78a1e597283ad7b8ee7ac5e58846f5a5fd68d0da99ce235a91db3ec1cf340fe6b7afcdb - 0000000000000000000000000000000013316f23de032d25e912ae8dc9b54c8dba1be7cecdbb9d2228d7e8f652011d46be79089dd0a6080a73c82256ce5e4ed2 - 000000000000000000000000000000000441e7f7f96198e4c23bd5eb16f1a7f045dbc8c53219ab2bcea91d3a027e2dfe659feac64905f8b9add7e4bfc91bec2b - 0000000000000000000000000000000005fc51bb1b40c87cd4292d4b66f8ca5ce4ef9abd2b69d4464b4879064203bda7c9fc3f896a3844ebc713f7bb20951d95 - "); - let expected = hex!(" - 0000000000000000000000000000000016b8ab56b45a9294466809b8e858c1ad15ad0d52cfcb62f8f5753dc94cee1de6efaaebce10701e3ec2ecaa9551024ea - 600000000000000000000000000000000124571eec37c0b1361023188d66ec17c1ec230d31b515e0e81e599ec19e40c8a7c8cdea9735bc3d8b4e37ca7e5dd71f6 - "); - - let mut output = [0u8; 128]; - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) - .expect("Builtin should not fail"); - assert_eq!(&output[..], &expected[..]); - } - - #[test] - fn bls12_381_g1_mul() { - let f = Builtin { - pricer: btreemap![0 => Pricing::Bls12G1Mul(Bls12G1MulPricer)], - native: EthereumBuiltin::from_str("bls12_381_g1_mul").unwrap(), - }; - - let input = hex!(" - 000000000000000000000000000000000b3a1dfe2d1b62538ed49648cb2a8a1d66bdc4f7a492eee59942ab810a306876a7d49e5ac4c6bb1613866c158ded993e - 000000000000000000000000000000001300956110f47ca8e2aacb30c948dfd046bf33f69bf54007d76373c5a66019454da45e3cf14ce2b9d53a50c9b4366aa3 - ac23d04ee3acc757aae6795532ce4c9f34534e506a4d843a26b052a040c79659 - "); - let expected = hex!(" - 000000000000000000000000000000001227b7021e9d3dc8bcbf5b346fc503f7f8576965769c5e22bb70056eef03c84b8c80290ae9ce20345770290c55549bce - 00000000000000000000000000000000188ddbbfb4ad2d34a8d3dc0ec92b70b63caa73ad7dea0cc9740bac2309b4bb11107912bd086379746e9a9bcd26d4db58 - "); - - let mut output = [0u8; 128]; - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) - .expect("Builtin should not fail"); - assert_eq!(&output[..], &expected[..]); - } - - #[test] - fn bls12_381_g1_mul_extend() { - let f = Builtin { - pricer: btreemap![0 => Pricing::Bls12G1Mul(Bls12G1MulPricer)], - native: EthereumBuiltin::from_str("bls12_381_g1_mul").unwrap(), - }; - let input = hex!(" - 0000000000000000000000000000000012196c5a43d69224d8713389285f26b98f86ee910ab3dd668e413738282003cc5b7357af9a7af54bb713d62255e80f56 - 0000000000000000000000000000000006ba8102bfbeea4416b710c73e8cce3032c31c6269c44906f8ac4f7874ce99fb17559992486528963884ce429a992fee - b3c940fe79b6966489b527955de7599194a9ac69a6ff58b8d99e7b1084f0464e - 00000000000000000000000000000000117dbe419018f67844f6a5e1b78a1e597283ad7b8ee7ac5e58846f5a5fd68d0da99ce235a91db3ec1cf340fe6b7afcdb - 0000000000000000000000000000000013316f23de032d25e912ae8dc9b54c8dba1be7cecdbb9d2228d7e8f652011d46be79089dd0a6080a73c82256ce5e4ed2 - 4d0e25bf3f6fc9f4da25d21fdc71773f1947b7a8a775b8177f7eca990b05b71d - 0000000000000000000000000000000008ab7b556c672db7883ec47efa6d98bb08cec7902ebb421aac1c31506b177ac444ffa2d9b400a6f1cbdc6240c607ee11 - 0000000000000000000000000000000016b7fa9adf4addc2192271ce7ad3c8d8f902d061c43b7d2e8e26922009b777855bffabe7ed1a09155819eabfa87f276f - 973f40c12c92b703d7b7848ef8b4466d40823aad3943a312b57432b91ff68be1 - 0000000000000000000000000000000015ff9a232d9b5a8020a85d5fe08a1dcfb73ece434258fe0e2fddf10ddef0906c42dcb5f5d62fc97f934ba900f17beb33 - 0000000000000000000000000000000009cfe4ee2241d9413c616462d7bac035a6766aeaab69c81e094d75b840df45d7e0dfac0265608b93efefb9a8728b98e4 - 4c51f97bcdda93904ae26991b471e9ea942e2b5b8ed26055da11c58bc7b5002a - 0000000000000000000000000000000017a17b82e3bfadf3250210d8ef572c02c3610d65ab4d7366e0b748768a28ee6a1b51f77ed686a64f087f36f641e7dca9 - 00000000000000000000000000000000077ea73d233ccea51dc4d5acecf6d9332bf17ae51598f4b394a5f62fb387e9c9aa1d6823b64a074f5873422ca57545d3 - 8964d5867927bc3e35a0b4c457482373969bff5edff8a781d65573e07fd87b89 - 000000000000000000000000000000000c1243478f4fbdc21ea9b241655947a28accd058d0cdb4f9f0576d32f09dddaf0850464550ff07cab5927b3e4c863ce9 - 0000000000000000000000000000000015fb54db10ffac0b6cd374eb7168a8cb3df0a7d5f872d8e98c1f623deb66df5dd08ff4c3658f2905ec8bd02598bd4f90 - 787c38b944eadbd03fd3187f450571740f6cd00e5b2e560165846eb800e5c944 - 000000000000000000000000000000000328f09584b6d6c98a709fc22e184123994613aca95a28ac53df8523b92273eb6f4e2d9b2a7dcebb474604d54a210719 - 000000000000000000000000000000001220ebde579911fe2e707446aaad8d3789fae96ae2e23670a4fd856ed82daaab704779eb4224027c1ed9460f39951a1b - aaee7ae2a237e8e53560c79e7baa9adf9c00a0ea4d6f514e7a6832eb15cef1e1 - 0000000000000000000000000000000002ebfa98aa92c32a29ebe17fcb1819ba82e686abd9371fcee8ea793b4c72b6464085044f818f1f5902396df0122830cb - 00000000000000000000000000000000001184715b8432ed190b459113977289a890f68f6085ea111466af15103c9c02467da33e01d6bff87fd57db6ccba442a - dac6ed3ef45c1d7d3028f0f89e5458797996d3294b95bebe049b76c7d0db317c - 0000000000000000000000000000000009d6424e002439998e91cd509f85751ad25e574830c564e7568347d19e3f38add0cab067c0b4b0801785a78bcbeaf246 - 000000000000000000000000000000000ef6d7db03ee654503b46ff0dbc3297536a422e963bda9871a8da8f4eeb98dedebd6071c4880b4636198f4c2375dc795 - bb30985756c3ca075114c92f231575d6befafe4084517f1166a47376867bd108 - 0000000000000000000000000000000002d1cdb93191d1f9f0308c2c55d0208a071f5520faca7c52ab0311dbc9ba563bd33b5dd6baa77bf45ac2c3269e945f48 - 00000000000000000000000000000000072a52106e6d7b92c594c4dacd20ef5fab7141e45c231457cd7e71463b2254ee6e72689e516fa6a8f29f2a173ce0a190 - fb730105809f64ea522983d6bbb62f7e2e8cbf702685e9be10e2ef71f8187672 - 0000000000000000000000000000000000641642f6801d39a09a536f506056f72a619c50d043673d6d39aa4af11d8e3ded38b9c3bbc970dbc1bd55d68f94b50d - 0000000000000000000000000000000009ab050de356a24aea90007c6b319614ba2f2ed67223b972767117769e3c8e31ee4056494628fb2892d3d37afb6ac943 - b6a9408625b0ca8fcbfb21d34eec2d8e24e9a30d2d3b32d7a37d110b13afbfea - 000000000000000000000000000000000fd4893addbd58fb1bf30b8e62bef068da386edbab9541d198e8719b2de5beb9223d87387af82e8b55bd521ff3e47e2d - 000000000000000000000000000000000f3a923b76473d5b5a53501790cb02597bb778bdacb3805a9002b152d22241ad131d0f0d6a260739cbab2c2fe602870e - 3b77283d0a7bb9e17a27e66851792fdd605cc0a339028b8985390fd024374c76 - 0000000000000000000000000000000002cb4b24c8aa799fd7cb1e4ab1aab1372113200343d8526ea7bc64dfaf926baf5d90756a40e35617854a2079cd07fba4 - 0000000000000000000000000000000003327ca22bd64ebd673cc6d5b02b2a8804d5353c9d251637c4273ad08d581cc0d58da9bea27c37a0b3f4961dbafd276b - dd994eae929aee7428fdda2e44f8cb12b10b91c83b22abc8bbb561310b62257c - 00000000000000000000000000000000024ad70f2b2105ca37112858e84c6f5e3ffd4a8b064522faae1ecba38fabd52a6274cb46b00075deb87472f11f2e67d9 - 0000000000000000000000000000000010a502c8b2a68aa30d2cb719273550b9a3c283c35b2e18a01b0b765344ffaaa5cb30a1e3e6ecd3a53ab67658a5787681 - 7010b134989c8368c7f831f9dd9f9a890e2c1435681107414f2e8637153bbf6a - 0000000000000000000000000000000000704cc57c8e0944326ddc7c747d9e7347a7f6918977132eea269f161461eb64066f773352f293a3ac458dc3ccd5026a - 000000000000000000000000000000001099d3c2bb2d082f2fdcbed013f7ac69e8624f4fcf6dfab3ee9dcf7fbbdb8c49ee79de40e887c0b6828d2496e3a6f768 - 94c68bc8d91ac8c489ee87dbfc4b94c93c8bbd5fc04c27db8b02303f3a659054 - 00000000000000000000000000000000130535a29392c77f045ac90e47f2e7b3cffff94494fe605aad345b41043f6663ada8e2e7ecd3d06f3b8854ef92212f42 - 000000000000000000000000000000001699a3cc1f10cd2ed0dc68eb916b4402e4f12bf4746893bf70e26e209e605ea89e3d53e7ac52bd07713d3c8fc671931d - b3682accc3939283b870357cf83683350baf73aa0d3d68bda82a0f6ae7e51746 - "); - let expected = hex!(" - 000000000000000000000000000000000b370fc4ca67fb0c3c270b1b4c4816ef953cd9f7cf6ad20e88099c40aace9c4bb3f4cd215e5796f65080c69c9f4d2a0f - 0000000000000000000000000000000007203220935ddc0190e2d7a99ec3f9231da550768373f9a5933dffd366f48146f8ea5fe5dee6539d925288083bb5a8f1 - "); - - let mut output = [0u8; 128]; - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) - .expect("Builtin should not fail"); - assert_eq!(&output[..], &expected[..]); - } - - #[test] - fn bls12_381_g2_add() { - let f = Builtin { - pricer: btreemap![0 => Pricing::Linear(Linear { base: 600, word: 0 })], - native: EthereumBuiltin::from_str("bls12_381_g2_add").unwrap(), - }; - let input = hex!(" - 00000000000000000000000000000000161c595d151a765c7dee03c9210414cdffab84b9078b4b98f9df09be5ec299b8f6322c692214f00ede97958f235c352b - 00000000000000000000000000000000106883e0937cb869e579b513bde8f61020fcf26be38f8b98eae3885cedec2e028970415fc653cf10e64727b7f6232e06 - 000000000000000000000000000000000f351a82b733af31af453904874b7ca6252957a1ab51ec7f7b6fff85bbf3331f870a7e72a81594a9930859237e7a154d - 0000000000000000000000000000000012fcf20d1750901f2cfed64fd362f010ee64fafe9ddab406cc352b65829b929881a50514d53247d1cca7d6995d0bc9b2 - 00000000000000000000000000000000148b7dfc21521d79ff817c7a0305f1048851e283be13c07d5c04d28b571d48172838399ba539529e8d037ffd1f729558 - 0000000000000000000000000000000003015abea326c15098f5205a8b2d3cd74d72dac59d60671ca6ef8c9c714ea61ffdacd46d1024b5b4f7e6b3b569fabaf2 - 0000000000000000000000000000000011f0c512fe7dc2dd8abdc1d22c2ecd2e7d1b84f8950ab90fc93bf54badf7bb9a9bad8c355d52a5efb110dca891e4cc3d - 0000000000000000000000000000000019774010814d1d94caf3ecda3ef4f5c5986e966eaf187c32a8a5a4a59452af0849690cf71338193f2d8435819160bcfb - "); - let expected = hex!(" - 000000000000000000000000000000000383ab7a17cc57e239e874af3f1aaabba0e64625b848676712f05f56132dbbd1cadfabeb3fe1f461daba3f1720057ddd - 00000000000000000000000000000000096967e9b3747f1b8e344535eaa0c51e70bc77412bfaa2a7ce76f11f570c9febb8f4227316866a416a50436d098e6f9a - 000000000000000000000000000000001079452b7519a7b090d668d54c266335b1cdd1080ed867dd17a2476b11c2617da829bf740e51cb7dfd60d73ed02c0c67 - 00000000000000000000000000000000015fc3a972e05cbd9014882cfe6f2f16d0291c403bf28b05056ac625e4f71dfb1295c85d73145ef554614e6eb2d5bf02 - "); - - let mut output = [0u8; 256]; - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) - .expect("Builtin should not fail"); - assert_eq!(&output[..], &expected[..]); - } - - #[test] - fn bls12_381_g2_mul() { - let f = Builtin { - pricer: btreemap![0 => Pricing::Bls12G2Mul(Bls12G2MulPricer)], - native: EthereumBuiltin::from_str("bls12_381_g2_mul").unwrap(), - }; - - let input = hex!(" - 00000000000000000000000000000000159da74f15e4c614b418997f81a1b8a3d9eb8dd80d94b5bad664bff271bb0f2d8f3c4ceb947dc6300d5003a2f7d7a829 - 000000000000000000000000000000000cdd4d1d4666f385dd54052cf5c1966328403251bebb29f0d553a9a96b5ade350c8493270e9b5282d8a06f9fa8d7b1d9 - 00000000000000000000000000000000189f8d3c94fdaa72cc67a7f93d35f91e22206ff9e97eed9601196c28d45b69c802ae92bcbf582754717b0355e08d37c0 - 00000000000000000000000000000000054b0a282610f108fc7f6736b8c22c8778d082bf4b0d0abca5a228198eba6a868910dd5c5c440036968e977955054196 - b6a9408625b0ca8fcbfb21d34eec2d8e24e9a30d2d3b32d7a37d110b13afbfea - "); - let expected = hex!(" - 000000000000000000000000000000000b24adeb2ca184c9646cb39f45e0cf8711e10bf308ddae06519562b0af3b43be44c2fcb90622726f7446ed690551d30e - 00000000000000000000000000000000069467c3edc19416067f572c51740ba8e0e7380121ade98e38ce26d907a2bf3a4e82af2bd195b6c3b7c9b29218880531 - 000000000000000000000000000000000eb8c90d0727511be53ffcb6f3b144c07983ed4b76d31ab003e45b37c7bc1066910f5e29f5adad5757af979dd0d8351d - 0000000000000000000000000000000004760f8d814189dcd893949797a3c4f56f2b60964bba3a4fc741e7ead05eb886787b2502fc64b20363eeba44e65d0ca0 - "); - - let mut output = [0u8; 256]; - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) - .expect("Builtin should not fail"); - assert_eq!(&output[..], &expected[..]); - } - - #[test] - fn bls12_381_g2_mul_extend() { - let f = Builtin { - pricer: btreemap![0 => Pricing::Bls12G2Mul(Bls12G2MulPricer)], - native: EthereumBuiltin::from_str("bls12_381_g2_mul").unwrap(), - }; - - let input = hex!(" - 00000000000000000000000000000000039b10ccd664da6f273ea134bb55ee48f09ba585a7e2bb95b5aec610631ac49810d5d616f67ba0147e6d1be476ea220e - 0000000000000000000000000000000000fbcdff4e48e07d1f73ec42fe7eb026f5c30407cfd2f22bbbfe5b2a09e8a7bb4884178cb6afd1c95f80e646929d3004 - 0000000000000000000000000000000001ed3b0e71acb0adbf44643374edbf4405af87cfc0507db7e8978889c6c3afbe9754d1182e98ac3060d64994d31ef576 - 000000000000000000000000000000001681a2bf65b83be5a2ca50430949b6e2a099977482e9405b593f34d2ed877a3f0d1bddc37d0cec4d59d7df74b2b8f2df - b3c940fe79b6966489b527955de7599194a9ac69a6ff58b8d99e7b1084f0464e - 0000000000000000000000000000000018c0ada6351b70661f053365deae56910798bd2ace6e2bf6ba4192d1a229967f6af6ca1c9a8a11ebc0a232344ee0f6d6 - 000000000000000000000000000000000cc70a587f4652039d8117b6103858adcd9728f6aebe230578389a62da0042b7623b1c0436734f463cfdd187d2090324 - 0000000000000000000000000000000009f50bd7beedb23328818f9ffdafdb6da6a4dd80c5a9048ab8b154df3cad938ccede829f1156f769d9e149791e8e0cd9 - 00000000000000000000000000000000079ba50d2511631b20b6d6f3841e616e9d11b68ec3368cd60129d9d4787ab56c4e9145a38927e51c9cd6271d493d9388 - 4d0e25bf3f6fc9f4da25d21fdc71773f1947b7a8a775b8177f7eca990b05b71d - 0000000000000000000000000000000003632695b09dbf86163909d2bb25995b36ad1d137cf252860fd4bb6c95749e19eb0c1383e9d2f93f2791cb0cf6c8ed9d - 000000000000000000000000000000001688a855609b0bbff4452d146396558ff18777f329fd4f76a96859dabfc6a6f6977c2496280dbe3b1f8923990c1d6407 - 000000000000000000000000000000000c8567fee05d05af279adc67179468a29d7520b067dbb348ee315a99504f70a206538b81a457cce855f4851ad48b7e80 - 000000000000000000000000000000001238dcdfa80ea46e1500026ea5feadb421de4409f4992ffbf5ae59fa67fd82f38452642a50261b849e74b4a33eed70cc - 973f40c12c92b703d7b7848ef8b4466d40823aad3943a312b57432b91ff68be1 - 000000000000000000000000000000000149704960cccf9d5ea414c73871e896b1d4cf0a946b0db72f5f2c5df98d2ec4f3adbbc14c78047961bc9620cb6cfb59 - 00000000000000000000000000000000140c5d25e534fb1bfdc19ba4cecaabe619f6e0cd3d60b0f17dafd7bcd27b286d4f4477d00c5e1af22ee1a0c67fbf177c - 00000000000000000000000000000000029a1727041590b8459890de736df15c00d80ab007c3aee692ddcdf75790c9806d198e9f4502bec2f0a623491c3f877d - 0000000000000000000000000000000008a94c98baa9409151030d4fae2bd4a64c6f11ea3c99b9661fdaed226b9a7c2a7d609be34afda5d18b8911b6e015bf49 - 4c51f97bcdda93904ae26991b471e9ea942e2b5b8ed26055da11c58bc7b5002a - 000000000000000000000000000000001156d478661337478ab0cbc877a99d9e4d9824a2b3f605d41404d6b557b3ffabbf42635b0bbcb854cf9ed8b8637561a8 - 000000000000000000000000000000001147ed317d5642e699787a7b47e6795c9a8943a34a694007e44f8654ba96390cf19f010dcf695e22c21874022c6ce291 - 000000000000000000000000000000000c6dccdf920fd5e7fae284115511952633744c6ad94120d9cae6acda8a7c23c48bd912cba6c38de5159587e1e6cad519 - 000000000000000000000000000000001944227d462bc2e5dcc6f6db0f83dad411ba8895262836f975b2b91e06fd0e2138862162acc04e9e65050b34ccbd1a4e - 8964d5867927bc3e35a0b4c457482373969bff5edff8a781d65573e07fd87b89 - 0000000000000000000000000000000019c31e3ab8cc9c920aa8f56371f133b6cb8d7b0b74b23c0c7201aca79e5ae69dc01f1f74d2492dcb081895b17d106b4e - 000000000000000000000000000000001789b0d371bd63077ccde3dbbebf3531368feb775bced187fb31cc6821481664600978e323ff21085b8c08e0f21daf72 - 000000000000000000000000000000000009eacfe8f4a2a9bae6573424d07f42bd6af8a9d55f71476a7e3c7a4b2b898550c1e72ec13afd4eff22421a03af1d31 - 000000000000000000000000000000000410bd4ea74dcfa33f2976aa1b571c67cbb596ab10f76a8aaf4548f1097e55b3373bff02683f806cb84e1e0e877819e2 - 787c38b944eadbd03fd3187f450571740f6cd00e5b2e560165846eb800e5c944 - 00000000000000000000000000000000147f09986691f2e57073378e8bfd58804241eed7934f6adfe6d0a6bac4da0b738495778a303e52113e1c80e698476d50 - 000000000000000000000000000000000762348b84c92a8ca6de319cf1f8f11db296a71b90fe13e1e4bcd25903829c00a5d2ad4b1c8d98c37eaad7e042ab023d - 0000000000000000000000000000000011d1d94530d4a2daf0e902a5c3382cd135938557f94b04bccea5e16ea089c5e020e13524c854a316662bd68784fe31f3 - 00000000000000000000000000000000070828522bec75b6a492fd9bca7b54dac6fbbf4f0bc3179d312bb65c647439e3868e4d5b21af5a64c93aeee8a9b7e46e - aaee7ae2a237e8e53560c79e7baa9adf9c00a0ea4d6f514e7a6832eb15cef1e1 - 000000000000000000000000000000000690a0869204c8dced5ba0ce13554b2703a3f18afb8fa8fa1c457d79c58fdc25471ae85bafad52e506fc1917fc3becff - 0000000000000000000000000000000010f7dbb16f8571ede1cec79e3f9ea03ae6468d7285984713f19607f5cab902b9a6b7cbcfd900be5c2e407cc093ea0e67 - 00000000000000000000000000000000151caf87968433cb1f85fc1854c57049be22c26497a86bfbd66a2b3af121d894dba8004a17c6ff96a5843c2719fa32d1 - 0000000000000000000000000000000011f0270f2b039409f70392879bcc2c67c836c100cf9883d3dc48d7adbcd52037d270539e863a951acd47ecaa1ca4db12 - dac6ed3ef45c1d7d3028f0f89e5458797996d3294b95bebe049b76c7d0db317c - 0000000000000000000000000000000017fae043c8fd4c520a90d4a6bd95f5b0484acc279b899e7b1d8f7f7831cc6ba37cd5965c4dc674768f5805842d433af3 - 0000000000000000000000000000000008ddd7b41b8fa4d29fb931830f29b46f4015ec202d51cb969d7c832aafc0995c875cd45eff4a083e2d5ecb5ad185b64f - 0000000000000000000000000000000015d384ab7e52420b83a69827257cb52b00f0199ed2240a142812b46cf67e92b99942ac59fb9f9efd7dd822f5a36c799f - 00000000000000000000000000000000074b3a16a9cc4be9da0ac8e2e7003d9c1ec89244d2c33441b31af76716cce439f805843a9a44701203231efdca551d5b - bb30985756c3ca075114c92f231575d6befafe4084517f1166a47376867bd108 - 000000000000000000000000000000000e25365988664e8b6ade2e5a40da49c11ff1e084cc0f8dca51f0d0578555d39e3617c8cadb2abc2633b28c5895ab0a9e - 00000000000000000000000000000000169f5fd768152169c403475dee475576fd2cc3788179453b0039ff3cb1b7a5a0fff8f82d03f56e65cad579218486c3b6 - 00000000000000000000000000000000087ccd7f92032febc1f75c7115111ede4acbb2e429cbccf3959524d0b79c449d431ff65485e1aecb442b53fec80ecb40 - 00000000000000000000000000000000135d63f264360003b2eb28f126c6621a40088c6eb15acc4aea89d6068e9d5a47f842aa4b4300f5cda5cc5831edb81596 - fb730105809f64ea522983d6bbb62f7e2e8cbf702685e9be10e2ef71f8187672 - 00000000000000000000000000000000159da74f15e4c614b418997f81a1b8a3d9eb8dd80d94b5bad664bff271bb0f2d8f3c4ceb947dc6300d5003a2f7d7a829 - 000000000000000000000000000000000cdd4d1d4666f385dd54052cf5c1966328403251bebb29f0d553a9a96b5ade350c8493270e9b5282d8a06f9fa8d7b1d9 - 00000000000000000000000000000000189f8d3c94fdaa72cc67a7f93d35f91e22206ff9e97eed9601196c28d45b69c802ae92bcbf582754717b0355e08d37c0 - 00000000000000000000000000000000054b0a282610f108fc7f6736b8c22c8778d082bf4b0d0abca5a228198eba6a868910dd5c5c440036968e977955054196 - b6a9408625b0ca8fcbfb21d34eec2d8e24e9a30d2d3b32d7a37d110b13afbfea - 000000000000000000000000000000000f29b0d2b6e3466668e1328048e8dbc782c1111ab8cbe718c85d58ded992d97ca8ba20b9d048feb6ed0aa1b4139d02d3 - 000000000000000000000000000000000d1f0dae940b99fbfc6e4a58480cac8c4e6b2fe33ce6f39c7ac1671046ce94d9e16cba2bb62c6749ef73d45bea21501a - 000000000000000000000000000000001902ccece1c0c763fd06934a76d1f2f056563ae6d8592bafd589cfebd6f057726fd908614ccd6518a21c66ecc2f78b66 - 0000000000000000000000000000000017f6b113f8872c3187d20b0c765d73b850b54244a719cf461fb318796c0b8f310b5490959f9d9187f99c8ed3e25e42a9 - 3b77283d0a7bb9e17a27e66851792fdd605cc0a339028b8985390fd024374c76 - 000000000000000000000000000000000576b8cf1e69efdc277465c344cadf7f8cceffacbeca83821f3ff81717308b97f4ac046f1926e7c2eb42677d7afc257c - 000000000000000000000000000000000cc1524531e96f3c00e4250dd351aedb5a4c3184aff52ec8c13d470068f5967f3674fe173ee239933e67501a9decc668 - 0000000000000000000000000000000001610cfcaea414c241b44cf6f3cc319dcb51d6b8de29c8a6869ff7c1ebb7b747d881e922b42e8fab96bde7cf23e8e4cd - 0000000000000000000000000000000017d4444dc8b6893b681cf10dac8169054f9d2f61d3dd5fd785ae7afa49d18ebbde9ce8dde5641adc6b38173173459836 - dd994eae929aee7428fdda2e44f8cb12b10b91c83b22abc8bbb561310b62257c - 000000000000000000000000000000000ca8f961f86ee6c46fc88fbbf721ba760186f13cd4cce743f19dc60a89fd985cb3feee34dcc4656735a326f515a729e4 - 00000000000000000000000000000000174baf466b809b1155d524050f7ee58c7c5cf728c674e0ce549f5551047a4479ca15bdf69b403b03fa74eb1b26bbff6c - 0000000000000000000000000000000000e8c8b587c171b1b292779abfef57202ed29e7fe94ade9634ec5a2b3b4692a4f3c15468e3f6418b144674be70780d5b - 000000000000000000000000000000001865e99cf97d88bdf56dae32314eb32295c39a1e755cd7d1478bea8520b9ff21c39b683b92ae15568420c390c42b123b - 7010b134989c8368c7f831f9dd9f9a890e2c1435681107414f2e8637153bbf6a - 0000000000000000000000000000000017eccd446f10018219a1bd111b8786cf9febd49f9e7e754e82dd155ead59b819f0f20e42f4635d5044ec5d550d847623 - 000000000000000000000000000000000403969d2b8f914ff2ea3bf902782642e2c6157bd2a343acf60ff9125b48b558d990a74c6d4d6398e7a3cc2a16037346 - 000000000000000000000000000000000bd45f61f142bd78619fb520715320eb5e6ebafa8b078ce796ba62fe1a549d5fb9df57e92d8d2795988eb6ae18cf9d93 - 00000000000000000000000000000000097db1314e064b8e670ec286958f17065bce644cf240ab1b1b220504560d36a0b43fc18453ff3a2bb315e219965f5bd3 - 94c68bc8d91ac8c489ee87dbfc4b94c93c8bbd5fc04c27db8b02303f3a659054 - 00000000000000000000000000000000018244ab39a716e252cbfb986c7958b371e29ea9190010d1f5e1cfdb6ce4822d4055c37cd411fc9a0c46d728f2c13ecf - 0000000000000000000000000000000001985d3c667c8d68c9adb92bdc7a8af959c17146544997d97116120a0f55366bd7ad7ffa28d93ee51222ff9222779675 - 000000000000000000000000000000000c70fd4e3c8f2a451f83fb6c046431b38251b7bae44cf8d36df69a03e2d3ce6137498523fcf0bcf29b5d69e8f265e24d - 00000000000000000000000000000000047b9163a218f7654a72e0d7c651a2cf7fd95e9784a59e0bf119d081de6c0465d374a55fbc1eff9828c9fd29abf4c4bd - b3682accc3939283b870357cf83683350baf73aa0d3d68bda82a0f6ae7e51746 - "); - let expected = hex!(" - 00000000000000000000000000000000083ad744b34f6393bc983222b004657494232c5d9fbc978d76e2377a28a34c4528da5d91cbc0977dc953397a6d21eca2 - 0000000000000000000000000000000015aec6526e151cf5b8403353517dfb9a162087a698b71f32b266d3c5c936a83975d5567c25b3a5994042ec1379c8e526 - 000000000000000000000000000000000e3647185d1a20efad19f975729908840dc33909a583600f7915025f906aef9c022fd34e618170b11178aaa824ae36b3 - 00000000000000000000000000000000159576d1d53f6cd12c39d651697e11798321f17cd287118d7ebeabf68281bc03109ee103ee8ef2ef93c71dd1dcbaf1e0 - "); - - let mut output = [0u8; 256]; - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) - .expect("Builtin should not fail"); - assert_eq!(&output[..], &expected[..]); - } - - #[test] - fn bls12_381_pairing() { - let f = Builtin { - pricer: btreemap![0 => Pricing::Bls12Pairing(Bls12PairingPricer)], - native: EthereumBuiltin::from_str("bls12_381_pairing").unwrap(), - }; - - let input = hex!(" - 000000000000000000000000000000001830f52d9bff64a623c6f5259e2cd2c2a08ea17a8797aaf83174ea1e8c3bd3955c2af1d39bfa474815bfe60714b7cd80 - 000000000000000000000000000000000874389c02d4cf1c61bc54c4c24def11dfbe7880bc998a95e70063009451ee8226fec4b278aade3a7cea55659459f1d5 - 00000000000000000000000000000000197737f831d4dc7e708475f4ca7ca15284db2f3751fcaac0c17f517f1ddab35e1a37907d7b99b39d6c8d9001cd50e79e - 000000000000000000000000000000000af1a3f6396f0c983e7c2d42d489a3ae5a3ff0a553d93154f73ac770cd0af7467aa0cef79f10bbd34621b3ec9583a834 - 000000000000000000000000000000001918cb6e448ed69fb906145de3f11455ee0359d030e90d673ce050a360d796de33ccd6a941c49a1414aca1c26f9e699e - 0000000000000000000000000000000019a915154a13249d784093facc44520e7f3a18410ab2a3093e0b12657788e9419eec25729944f7945e732104939e7a9e - 000000000000000000000000000000001830f52d9bff64a623c6f5259e2cd2c2a08ea17a8797aaf83174ea1e8c3bd3955c2af1d39bfa474815bfe60714b7cd80 - 00000000000000000000000000000000118cd94e36ab177de95f52f180fdbdc584b8d30436eb882980306fa0625f07a1f7ad3b4c38a921c53d14aa9a6ba5b8d6 - 00000000000000000000000000000000197737f831d4dc7e708475f4ca7ca15284db2f3751fcaac0c17f517f1ddab35e1a37907d7b99b39d6c8d9001cd50e79e - 000000000000000000000000000000000af1a3f6396f0c983e7c2d42d489a3ae5a3ff0a553d93154f73ac770cd0af7467aa0cef79f10bbd34621b3ec9583a834 - 000000000000000000000000000000001918cb6e448ed69fb906145de3f11455ee0359d030e90d673ce050a360d796de33ccd6a941c49a1414aca1c26f9e699e - 0000000000000000000000000000000019a915154a13249d784093facc44520e7f3a18410ab2a3093e0b12657788e9419eec25729944f7945e732104939e7a9e - "); - let expected = hex!( - " - 0000000000000000000000000000000000000000000000000000000000000001 - " - ); - - let mut output = [0u8; 32]; - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) - .expect("Builtin should not fail"); - assert_eq!(&output[..], &expected[..]); - } - - #[test] - fn bls12_381_fp_to_g1() { - let f = Builtin { - pricer: btreemap![0 => Pricing::Linear(Linear { base: 5_500, word: 0 })], - native: EthereumBuiltin::from_str("bls12_381_fp_to_g1").unwrap(), - }; - - let input = hex!(" - 0000000000000000000000000000000017f66b472b36717ee0902d685c808bb5f190bbcb2c51d067f1cbec64669f10199a5868d7181dcec0498fcc71f5acaf79 - "); - let expected = hex!(" - 00000000000000000000000000000000188dc9e5ddf48977f33aeb6e505518269bf67fb624fa86b79741d842e75a6fa1be0911c2caa9e55571b6e55a3c0c0b9e - 00000000000000000000000000000000193e8b7c7e78daf104a59d7b39401a65355fa874bd34e91688580941e99a863367efc68fe871e38e07423090e93919c9 - "); - - let mut output = [0u8; 128]; - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) - .expect("Builtin should not fail"); - assert_eq!(&output[..], &expected[..]); - } - - #[test] - fn bls12_381_fp2_to_g2() { - let f = Builtin { - pricer: btreemap![0 => Pricing::Linear(Linear { base: 23_800, word: 0 })], - native: EthereumBuiltin::from_str("bls12_381_fp2_to_g2").unwrap(), - }; - - let input = hex!(" - 000000000000000000000000000000000f470603a402bc134db1b389fd187460f9eb2dd001a2e99f730af386508c62f0e911d831a2562da84bce11d39f2ff13f - 000000000000000000000000000000000d8c45f4ab20642d0cba9764126e0818b7d731a6ba29ed234d9d6309a5e8ddfbd85193f1fa8b7cfeed3d31b23b904ee9 - "); - let expected = hex!(" - 0000000000000000000000000000000012e74d5a0c005a86ca148e9eff8e34a00bfa8b6e6aadf633d65cd09bb29917e0ceb0d5c9d9650c162d7fe4aa27452685 - 0000000000000000000000000000000005f09101a2088712619f9c096403b66855a12f9016c55aef6047372fba933f02d9d59db1a86df7be57978021e2457821 - 00000000000000000000000000000000136975b37fe400d1d217a2b496c1552b39be4e9e71dd7ad482f5f0836d271d02959fdb698dda3d0530587fb86e0db1dd - 0000000000000000000000000000000000bad0aabd9309e92e2dd752f4dd73be07c0de2c5ddd57916b9ffa065d7440d03d44e7c042075cda694414a9fb639bb7 - "); - - let mut output = [0u8; 256]; - - f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])) - .expect("Builtin should not fail"); - assert_eq!(&output[..], &expected[..]); - } -} diff --git a/evm-tests/ethjson/Cargo.toml b/evm-tests/ethjson/Cargo.toml deleted file mode 100644 index 5e0cf0ea..00000000 --- a/evm-tests/ethjson/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -description = "OpenEthereum JSON Deserialization" -name = "ethjson" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" - -[dependencies] -ethereum-types = "0.15" -rustc-hex = "2.1.0" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" - -[dev-dependencies] -maplit = "1.0.2" - -[features] -test-helpers = [] diff --git a/evm-tests/ethjson/src/bytes.rs b/evm-tests/ethjson/src/bytes.rs deleted file mode 100644 index 922747e5..00000000 --- a/evm-tests/ethjson/src/bytes.rs +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Lenient bytes json deserialization for test json files. - -use rustc_hex::FromHex; -use serde::de::{Error, Visitor}; -use serde::{Deserialize, Deserializer}; -use std::fmt; -use std::ops::Deref; -use std::str::FromStr; - -/// Lenient bytes json deserialization for test json files. -#[derive(Default, Debug, PartialEq, Eq, Clone, PartialOrd, Ord)] -pub struct Bytes(Vec); - -impl Bytes { - /// Creates bytes struct. - pub const fn new(v: Vec) -> Self { - Self(v) - } -} - -impl From for Vec { - fn from(bytes: Bytes) -> Self { - bytes.0 - } -} - -impl From> for Bytes { - fn from(bytes: Vec) -> Self { - Self(bytes) - } -} - -impl Deref for Bytes { - type Target = [u8]; - - fn deref(&self) -> &[u8] { - &self.0 - } -} - -impl FromStr for Bytes { - type Err = String; - - fn from_str(value: &str) -> Result { - let v = match value.len() { - 0 => vec![], - 2 if value.starts_with("0x") => vec![], - _ if value.starts_with("0x") && value.len() % 2 == 1 => { - let v = "0".to_owned() + &value[2..]; - FromHex::from_hex(v.as_str()).unwrap_or_default() - } - _ if value.starts_with("0x") => FromHex::from_hex(&value[2..]).unwrap_or_default(), - _ => FromHex::from_hex(value).unwrap_or_default(), - }; - - Ok(Self(v)) - } -} - -impl<'a> Deserialize<'a> for Bytes { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'a>, - { - deserializer.deserialize_any(BytesVisitor) - } -} - -struct BytesVisitor; - -impl Visitor<'_> for BytesVisitor { - type Value = Bytes; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a hex encoded string of bytes") - } - - fn visit_str(self, value: &str) -> Result - where - E: Error, - { - Bytes::from_str(value).map_err(Error::custom) - } - - fn visit_string(self, value: String) -> Result - where - E: Error, - { - self.visit_str(value.as_ref()) - } -} - -#[cfg(test)] -mod test { - use super::Bytes; - - #[test] - fn bytes_deserialization() { - let s = r#"["", "0x", "0x12", "1234", "0x001"]"#; - let deserialized: Vec = serde_json::from_str(s).unwrap(); - assert_eq!( - deserialized, - vec![ - Bytes(vec![]), - Bytes(vec![]), - Bytes(vec![0x12]), - Bytes(vec![0x12, 0x34]), - Bytes(vec![0, 1]) - ] - ); - } - - #[test] - fn bytes_into() { - let v: Vec = Bytes(vec![0xff, 0x11]).into(); - assert_eq!(vec![0xff, 0x11], v); - } -} diff --git a/evm-tests/ethjson/src/hash.rs b/evm-tests/ethjson/src/hash.rs deleted file mode 100644 index f579c4db..00000000 --- a/evm-tests/ethjson/src/hash.rs +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Lenient hash json deserialization for test json files. - -use ethereum_types::{ - Bloom as Hash2048, H160 as Hash160, H256 as Hash256, H520 as Hash520, H64 as Hash64, -}; -use serde::de::{Error, Visitor}; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::fmt; -use std::str::FromStr; - -macro_rules! impl_hash { - ($name: ident, $inner: ident) => { - /// Lenient hash json deserialization for test json files. - #[derive(Default, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone, Copy)] - pub struct $name(pub $inner); - - impl From<$name> for $inner { - fn from(other: $name) -> $inner { - other.0 - } - } - - impl From<$inner> for $name { - fn from(i: $inner) -> Self { - $name(i) - } - } - - impl<'a> Deserialize<'a> for $name { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'a>, - { - struct HashVisitor; - - impl<'b> Visitor<'b> for HashVisitor { - type Value = $name; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a 0x-prefixed hex-encoded hash") - } - - fn visit_str(self, value: &str) -> Result - where - E: Error, - { - let value = match value.len() { - 0 => $inner::from_low_u64_be(0), - 2 if value == "0x" => $inner::from_low_u64_be(0), - _ if value.starts_with("0x") => { - $inner::from_str(&value[2..]).map_err(|e| { - Error::custom( - format!("Invalid hex value {}: {}", value, e).as_str(), - ) - })? - } - _ => $inner::from_str(value).map_err(|e| { - Error::custom( - format!("Invalid hex value {}: {}", value, e).as_str(), - ) - })?, - }; - - Ok($name(value)) - } - - fn visit_string(self, value: String) -> Result - where - E: Error, - { - self.visit_str(value.as_ref()) - } - } - - deserializer.deserialize_any(HashVisitor) - } - } - - impl Serialize for $name { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&format!("{:#x}", self.0)) - } - } - }; -} - -impl_hash!(H64, Hash64); -impl_hash!(Address, Hash160); -impl_hash!(H256, Hash256); -impl_hash!(H520, Hash520); -impl_hash!(Bloom, Hash2048); - -#[cfg(test)] -mod test { - use super::H256; - use std::str::FromStr; - - #[test] - fn hash_deserialization() { - let s = r#"["", "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae"]"#; - let deserialized: Vec = serde_json::from_str(s).unwrap(); - assert_eq!( - deserialized, - vec![ - H256(ethereum_types::H256::zero()), - H256( - ethereum_types::H256::from_str( - "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae" - ) - .unwrap() - ) - ] - ); - } - - #[test] - fn hash_into() { - assert_eq!( - ethereum_types::H256::zero(), - H256(ethereum_types::H256::zero()).into() - ); - } -} diff --git a/evm-tests/ethjson/src/lib.rs b/evm-tests/ethjson/src/lib.rs deleted file mode 100644 index 7ac15ad7..00000000 --- a/evm-tests/ethjson/src/lib.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! JSON deserialization library - -#![warn(missing_docs)] -#![allow(clippy::too_long_first_doc_paragraph)] - -pub mod bytes; -pub mod hash; -pub mod maybe; -pub mod spec; -pub mod state; -pub mod transaction; -pub mod uint; -pub mod vm; - -#[cfg(any(test, feature = "test-helpers"))] -pub mod test_helpers; diff --git a/evm-tests/ethjson/src/maybe.rs b/evm-tests/ethjson/src/maybe.rs deleted file mode 100644 index d9708e9e..00000000 --- a/evm-tests/ethjson/src/maybe.rs +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Deserializer of empty string values into optionals. - -use std::fmt; -use std::marker::PhantomData; - -use ethereum_types::U256; -use serde::de::{Error, IntoDeserializer, Visitor}; -use serde::{Deserialize, Deserializer}; - -use crate::uint::Uint; - -/// Deserializer of empty string values into optionals. -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum MaybeEmpty { - /// Some. - Some(T), - /// None. - None, -} - -impl<'a, T> Deserialize<'a> for MaybeEmpty -where - T: Deserialize<'a>, -{ - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'a>, - { - deserializer.deserialize_any(MaybeEmptyVisitor::new()) - } -} - -struct MaybeEmptyVisitor { - _phantom: PhantomData, -} - -impl MaybeEmptyVisitor { - const fn new() -> Self { - Self { - _phantom: PhantomData, - } - } -} - -impl<'a, T> Visitor<'a> for MaybeEmptyVisitor -where - T: Deserialize<'a>, -{ - type Value = MaybeEmpty; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "an empty string or string-encoded type") - } - - fn visit_str(self, value: &str) -> Result - where - E: Error, - { - self.visit_string(value.to_owned()) - } - - fn visit_string(self, value: String) -> Result - where - E: Error, - { - if value.is_empty() { - Ok(MaybeEmpty::None) - } else { - T::deserialize(value.into_deserializer()).map(MaybeEmpty::Some) - } - } -} - -impl From> for Option { - fn from(val: MaybeEmpty) -> Self { - match val { - MaybeEmpty::Some(s) => Some(s), - MaybeEmpty::None => None, - } - } -} - -#[cfg(test)] -impl From for MaybeEmpty { - fn from(uint: Uint) -> Self { - Self::Some(uint) - } -} - -impl From> for U256 { - fn from(maybe: MaybeEmpty) -> Self { - match maybe { - MaybeEmpty::Some(v) => v.0, - MaybeEmpty::None => Self::zero(), - } - } -} - -impl From> for u64 { - fn from(maybe: MaybeEmpty) -> Self { - match maybe { - MaybeEmpty::Some(v) => v.0.low_u64(), - MaybeEmpty::None => 0u64, - } - } -} - -impl Default for MaybeEmpty { - fn default() -> Self { - Self::Some(Uint::default()) - } -} - -#[cfg(test)] -mod tests { - use super::MaybeEmpty; - use crate::hash::H256; - use std::str::FromStr; - - #[test] - fn maybe_deserialization() { - let s = r#"["", "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae"]"#; - let deserialized: Vec> = serde_json::from_str(s).unwrap(); - assert_eq!( - deserialized, - vec![ - MaybeEmpty::None, - MaybeEmpty::Some(H256( - ethereum_types::H256::from_str( - "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae" - ) - .unwrap() - )) - ] - ); - } -} diff --git a/evm-tests/ethjson/src/spec/account.rs b/evm-tests/ethjson/src/spec/account.rs deleted file mode 100644 index 833f12f3..00000000 --- a/evm-tests/ethjson/src/spec/account.rs +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Spec account deserialization. - -use std::collections::BTreeMap; - -use crate::{bytes::Bytes, spec::builtin::BuiltinCompat, uint::Uint}; -use serde::Deserialize; - -/// Spec account. -#[cfg_attr(any(test, feature = "test-helpers"), derive(Clone))] -#[derive(Debug, PartialEq, Deserialize)] -#[serde(deny_unknown_fields)] -#[serde(rename_all = "camelCase")] -pub struct Account { - /// Builtin contract. - pub builtin: Option, - /// Balance. - pub balance: Option, - /// Nonce. - pub nonce: Option, - /// Code. - pub code: Option, - /// Version. - pub version: Option, - /// Storage. - pub storage: Option>, - /// Constructor. - pub constructor: Option, -} - -impl Account { - /// Returns true if account does not have nonce, balance, code and storage. - pub const fn is_empty(&self) -> bool { - self.balance.is_none() - && self.nonce.is_none() - && self.code.is_none() - && self.storage.is_none() - } -} - -#[cfg(test)] -mod tests { - use super::{Account, BTreeMap, Bytes, Uint}; - use ethereum_types::U256; - - #[test] - fn account_balance_missing_not_empty() { - let s = r#"{ - "nonce": "0", - "code": "1234", - "storage": { "0x7fffffffffffffff7fffffffffffffff": "0x1" } - }"#; - let deserialized: Account = serde_json::from_str(s).unwrap(); - assert!(!deserialized.is_empty()); - } - - #[test] - fn account_nonce_missing_not_empty() { - let s = r#"{ - "balance": "1", - "code": "1234", - "storage": { "0x7fffffffffffffff7fffffffffffffff": "0x1" } - }"#; - let deserialized: Account = serde_json::from_str(s).unwrap(); - assert!(!deserialized.is_empty()); - } - - #[test] - fn account_code_missing_not_empty() { - let s = r#"{ - "balance": "1", - "nonce": "0", - "storage": { "0x7fffffffffffffff7fffffffffffffff": "0x1" } - }"#; - let deserialized: Account = serde_json::from_str(s).unwrap(); - assert!(!deserialized.is_empty()); - } - - #[test] - fn account_storage_missing_not_empty() { - let s = r#"{ - "balance": "1", - "nonce": "0", - "code": "1234" - }"#; - let deserialized: Account = serde_json::from_str(s).unwrap(); - assert!(!deserialized.is_empty()); - } - - #[test] - fn account_empty() { - let s = r#"{ - "builtin": { - "name": "ecrecover", - "pricing": { - "linear": { - "base": 3000, - "word": 0 - } - } - } - }"#; - let deserialized: Account = serde_json::from_str(s).unwrap(); - assert!(deserialized.is_empty()); - } - - #[test] - fn account_deserialization() { - let s = r#"{ - "balance": "1", - "nonce": "0", - "code": "1234", - "builtin": { - "name": "ecrecover", - "pricing": { - "linear": { - "base": 3000, - "word": 0 - } - } - } - }"#; - let deserialized: Account = serde_json::from_str(s).unwrap(); - assert!(!deserialized.is_empty()); - assert_eq!(deserialized.balance.unwrap(), Uint(U256::from(1))); - assert_eq!(deserialized.nonce.unwrap(), Uint(U256::from(0))); - assert_eq!(deserialized.code.unwrap(), Bytes::new(vec![0x12, 0x34])); - assert!(deserialized.builtin.is_some()); // Further tested in builtin.rs - } - - #[test] - fn account_storage_deserialization() { - let s = r#"{ - "balance": "1", - "nonce": "0", - "code": "1234", - "storage": { "0x7fffffffffffffff7fffffffffffffff": "0x1" } - }"#; - let deserialized: Account = serde_json::from_str(s).unwrap(); - assert!(!deserialized.is_empty()); - assert_eq!(deserialized.balance.unwrap(), Uint(U256::from(1))); - assert_eq!(deserialized.nonce.unwrap(), Uint(U256::from(0))); - assert_eq!(deserialized.code.unwrap(), Bytes::new(vec![0x12, 0x34])); - let mut storage = BTreeMap::new(); - storage.insert( - Uint(U256::from("7fffffffffffffff7fffffffffffffff")), - Uint(U256::from(1)), - ); - assert_eq!(deserialized.storage.unwrap(), storage); - } -} diff --git a/evm-tests/ethjson/src/spec/authority_round.rs b/evm-tests/ethjson/src/spec/authority_round.rs deleted file mode 100644 index 6f73c655..00000000 --- a/evm-tests/ethjson/src/spec/authority_round.rs +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Authority Round parameter deserialization. -//! -//! Here is an example of input parameters where the step duration is constant at 5 seconds, the set -//! of validators is decided by the contract at address `0x10..01` starting from block 0, and where -//! the address of the contract that computes block rewards is set to `0x20..02` for blocks 0 -//! through 41 and to `0x30.03` for all blocks starting from block 42. -//! -//! ```ignore -//! "params": { -//! "stepDuration": "5", -//! "validators": { -//! "multi": { -//! "0": { -//! "contract": "0x1000000000000000000000000000000000000001" -//! } -//! } -//! }, -//! "blockRewardContractTransitions": { -//! "0": "0x2000000000000000000000000000000000000002", -//! "42": "0x3000000000000000000000000000000000000003" -//! } -//! } -//! ``` - -use super::{StepDuration, ValidatorSet}; -use crate::{bytes::Bytes, hash::Address, uint::Uint}; -use serde::Deserialize; -use std::collections::BTreeMap; - -/// Authority params deserialization. -#[derive(Debug, PartialEq, Deserialize)] -#[serde(deny_unknown_fields)] -#[serde(rename_all = "camelCase")] -pub struct AuthorityRoundParams { - /// Block duration, in seconds. - pub step_duration: StepDuration, - /// Valid authorities - pub validators: ValidatorSet, - /// Starting step. Determined automatically if not specified. - /// To be used for testing only. - pub start_step: Option, - /// Block at which score validation should start. - pub validate_score_transition: Option, - /// Block from which monotonic steps start. - pub validate_step_transition: Option, - /// Whether transitions should be immediate. - pub immediate_transitions: Option, - /// Reward per block in wei. - pub block_reward: Option, - /// Block at which the block reward contract should start being used. This option allows one to - /// add a single block reward contract transition and is compatible with the multiple address - /// option `block_reward_contract_transitions` below. - pub block_reward_contract_transition: Option, - /// Block reward contract address which overrides the `block_reward` setting. This option allows - /// one to add a single block reward contract address and is compatible with the multiple - /// address option `block_reward_contract_transitions` below. - pub block_reward_contract_address: Option
, - /// Block reward contract addresses with their associated starting block numbers. - /// - /// Setting the block reward contract overrides `block_reward`. If the single block reward - /// contract address is also present then it is added into the map at the block number stored in - /// `block_reward_contract_transition` or 0 if that block number is not provided. Therefore both - /// a single block reward contract transition and a map of reward contract transitions can be - /// used simulataneously in the same configuration. In such a case the code requires that the - /// block number of the single transition is strictly less than any of the block numbers in the - /// map. - pub block_reward_contract_transitions: Option>, - /// Block reward code. This overrides the block reward contract address. - pub block_reward_contract_code: Option, - /// Block at which maximum uncle count should be considered. - pub maximum_uncle_count_transition: Option, - /// Maximum number of accepted uncles. - pub maximum_uncle_count: Option, - /// Block at which empty step messages should start. - pub empty_steps_transition: Option, - /// Maximum number of accepted empty steps. - pub maximum_empty_steps: Option, - /// Strict validation of empty steps transition block. - pub strict_empty_steps_transition: Option, - /// First block for which a 2/3 quorum (instead of 1/2) is required. - pub two_thirds_majority_transition: Option, - /// The random number contract's address, or a map of contract transitions. - pub randomness_contract_address: Option>, - /// The addresses of contracts that determine the block gas limit starting from the block number - /// associated with each of those contracts. - pub block_gas_limit_contract_transitions: Option>, - /// The block number at which the consensus engine switches from AuRa to AuRa with POSDAO - /// modifications. - pub posdao_transition: Option, -} - -/// Authority engine deserialization. -#[derive(Debug, PartialEq, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct AuthorityRound { - /// Authority Round parameters. - pub params: AuthorityRoundParams, -} - -#[cfg(test)] -mod tests { - use std::str::FromStr; - - use ethereum_types::{H160, U256}; - - use super::{Address, StepDuration, Uint}; - use crate::spec::{authority_round::AuthorityRound, validator_set::ValidatorSet}; - - #[test] - fn authority_round_deserialization() { - let s = r#"{ - "params": { - "stepDuration": "0x02", - "validators": { - "list" : ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"] - }, - "startStep" : 24, - "validateStepTransition": 150, - "blockReward": 5000000, - "maximumUncleCountTransition": 10000000, - "maximumUncleCount": 5, - "randomnessContractAddress": { - "10": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "20": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" - }, - "blockGasLimitContractTransitions": { - "10": "0x1000000000000000000000000000000000000001", - "20": "0x2000000000000000000000000000000000000002" - } - } - }"#; - - let deserialized: AuthorityRound = serde_json::from_str(s).unwrap(); - assert_eq!( - deserialized.params.step_duration, - StepDuration::Single(Uint(U256::from(2))) - ); - assert_eq!( - deserialized.params.validators, - ValidatorSet::List(vec![Address( - H160::from_str("c6d9d2cd449a754c494264e1809c50e34d64562b").unwrap() - )]), - ); - assert_eq!(deserialized.params.start_step, Some(Uint(U256::from(24)))); - assert_eq!(deserialized.params.immediate_transitions, None); - assert_eq!( - deserialized.params.maximum_uncle_count_transition, - Some(Uint(10_000_000.into())) - ); - assert_eq!( - deserialized.params.maximum_uncle_count, - Some(Uint(5.into())) - ); - assert_eq!( - deserialized.params.randomness_contract_address.unwrap(), - vec![ - ( - Uint(10.into()), - Address(H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap()) - ), - ( - Uint(20.into()), - Address(H160::from_str("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb").unwrap()) - ), - ] - .into_iter() - .collect() - ); - let expected_bglc = [ - ( - Uint(10.into()), - Address(H160::from_str("1000000000000000000000000000000000000001").unwrap()), - ), - ( - Uint(20.into()), - Address(H160::from_str("2000000000000000000000000000000000000002").unwrap()), - ), - ]; - assert_eq!( - deserialized.params.block_gas_limit_contract_transitions, - Some(expected_bglc.iter().copied().collect()) - ); - } -} diff --git a/evm-tests/ethjson/src/spec/basic_authority.rs b/evm-tests/ethjson/src/spec/basic_authority.rs deleted file mode 100644 index e7929e07..00000000 --- a/evm-tests/ethjson/src/spec/basic_authority.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Authority params deserialization. - -use super::ValidatorSet; -use crate::uint::Uint; -use serde::Deserialize; - -/// Authority params deserialization. -#[derive(Debug, PartialEq, Deserialize)] -#[serde(deny_unknown_fields)] -#[serde(rename_all = "camelCase")] -pub struct BasicAuthorityParams { - /// Block duration. - pub duration_limit: Uint, - /// Valid authorities - pub validators: ValidatorSet, -} - -/// Authority engine deserialization. -#[derive(Debug, PartialEq, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct BasicAuthority { - /// Ethash params. - pub params: BasicAuthorityParams, -} - -#[cfg(test)] -mod tests { - use super::{BasicAuthority, Uint}; - use crate::{hash::Address, spec::validator_set::ValidatorSet}; - use ethereum_types::{H160, U256}; - use std::str::FromStr; - - #[test] - fn basic_authority_deserialization() { - let s = r#"{ - "params": { - "durationLimit": "0x0d", - "validators" : { - "list": ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"] - } - } - }"#; - - let deserialized: BasicAuthority = serde_json::from_str(s).unwrap(); - - assert_eq!(deserialized.params.duration_limit, Uint(U256::from(0x0d))); - let vs = ValidatorSet::List(vec![Address( - H160::from_str("c6d9d2cd449a754c494264e1809c50e34d64562b").unwrap(), - )]); - assert_eq!(deserialized.params.validators, vs); - } -} diff --git a/evm-tests/ethjson/src/spec/builtin.rs b/evm-tests/ethjson/src/spec/builtin.rs deleted file mode 100644 index 93675170..00000000 --- a/evm-tests/ethjson/src/spec/builtin.rs +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Spec builtin deserialization. - -use std::collections::BTreeMap; - -use crate::uint::Uint; -use serde::Deserialize; - -/// Linear pricing. -#[derive(Debug, PartialEq, Eq, Deserialize, Clone)] -#[serde(deny_unknown_fields)] -pub struct Linear { - /// Base price. - pub base: u64, - /// Price for word. - pub word: u64, -} - -/// Pricing for modular exponentiation. -#[derive(Debug, PartialEq, Eq, Deserialize, Clone)] -#[serde(deny_unknown_fields)] -pub struct Modexp { - /// Price divisor. - pub divisor: u64, - /// Use price scheme from EIP-2565 - pub is_eip_2565: bool, -} - -/// Pricing for constant alt_bn128 operations (ECADD and ECMUL) -#[derive(Debug, PartialEq, Eq, Deserialize, Clone)] -#[serde(deny_unknown_fields)] -pub struct AltBn128ConstOperations { - /// price - pub price: u64, -} - -/// Pricing for alt_bn128_pairing. -#[derive(Debug, PartialEq, Eq, Deserialize, Clone)] -#[serde(deny_unknown_fields)] -pub struct AltBn128Pairing { - /// Base price. - pub base: u64, - /// Price per point pair. - pub pair: u64, -} - -/// Bls12 pairing price -#[derive(Debug, PartialEq, Eq, Deserialize, Clone)] -#[serde(deny_unknown_fields)] -pub struct Bls12Pairing { - /// Price per final exp - pub base: u64, - /// Price per pair (Miller loop) - pub pair: u64, -} - -/// Pricing for constant Bls12 operations (ADD and MUL in G1 and G2, as well as mappings) -#[derive(Debug, PartialEq, Eq, Deserialize, Clone)] -#[serde(deny_unknown_fields)] -pub struct Bls12ConstOperations { - /// Fixed price. - pub price: u64, -} - -/// Pricing variants. -#[derive(Debug, PartialEq, Eq, Deserialize, Clone)] -#[serde(deny_unknown_fields)] -#[serde(rename_all = "snake_case")] -pub enum Pricing { - /// Pricing for Blake2 compression function: each call costs the same amount per round. - Blake2F { - /// Price per round of Blake2 compression function. - gas_per_round: u64, - }, - /// Linear pricing. - Linear(Linear), - /// Pricing for modular exponentiation. - Modexp(Modexp), - /// Pricing for alt_bn128_pairing exponentiation. - AltBn128Pairing(AltBn128Pairing), - /// Pricing for constant alt_bn128 operations - AltBn128ConstOperations(AltBn128ConstOperations), - /// Pricing of bls12_381 MSM operations in G1 - Bls12G1Mul, - /// Pricing of bls12_381 MSM operations in G2 - Bls12G2Mul, - /// Pricing of pairing bls12_381 operation - Bls12Pairing, -} - -/// Builtin compatibility layer -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Debug, PartialEq, Deserialize, Clone)] -#[serde(deny_unknown_fields)] -pub struct BuiltinCompat { - /// Builtin name. - pub name: String, - /// Builtin pricing. - pub pricing: PricingCompat, - /// Activation block. - pub activate_at: Option, -} - -/// Spec builtin. -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct Builtin { - /// Builtin name. - pub name: String, - /// Builtin pricing. - pub pricing: BTreeMap, -} - -impl From for Builtin { - fn from(legacy: BuiltinCompat) -> Self { - let pricing = match legacy.pricing { - PricingCompat::Single(pricing) => { - let mut map = BTreeMap::new(); - let activate_at: u64 = legacy.activate_at.map_or(0, Into::into); - map.insert( - activate_at, - PricingAt { - info: None, - price: pricing, - }, - ); - map - } - PricingCompat::Multi(pricings) => { - pricings.into_iter().map(|(a, p)| (a.into(), p)).collect() - } - PricingCompat::Empty => BTreeMap::new(), - }; - Self { - name: legacy.name, - pricing, - } - } -} - -/// Compability layer for different pricings -#[derive(Debug, Eq, PartialEq, Deserialize, Clone)] -#[serde(rename_all = "snake_case")] -#[serde(deny_unknown_fields)] -#[serde(untagged)] -pub enum PricingCompat { - /// Single builtin - Single(Pricing), - /// Multiple builtins - Multi(BTreeMap), - /// Not provided pricing info - Empty, -} - -/// Price for a builtin, with the block number to activate it on -#[derive(Debug, PartialEq, Eq, Deserialize, Clone)] -#[serde(deny_unknown_fields)] -pub struct PricingAt { - /// Description of the activation, e.g. "PunyPony HF, March 12, 2025". - pub info: Option, - /// Builtin pricing. - pub price: Pricing, -} - -#[cfg(test)] -mod tests { - use super::{ - AltBn128ConstOperations, Builtin, BuiltinCompat, Linear, Modexp, Pricing, PricingAt, - }; - use maplit::btreemap; - - #[test] - fn builtin_deserialization() { - let s = r#"{ - "name": "ecrecover", - "pricing": { "linear": { "base": 3000, "word": 0 } } - }"#; - let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); - assert_eq!(builtin.name, "ecrecover"); - assert_eq!( - builtin.pricing, - btreemap![ - 0 => PricingAt { - info: None, - price: Pricing::Linear(Linear { base: 3000, word: 0 }) - } - ] - ); - } - - #[test] - fn deserialize_multiple_pricings() { - let s = r#"{ - "name": "ecrecover", - "pricing": { - "0": { - "price": {"linear": { "base": 3000, "word": 0 }} - }, - "500": { - "info": "enable fake EIP at block 500", - "price": {"linear": { "base": 10, "word": 0 }} - } - } - }"#; - let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); - assert_eq!(builtin.name, "ecrecover"); - assert_eq!( - builtin.pricing, - btreemap![ - 0 => PricingAt { - info: None, - price: Pricing::Linear(Linear { base: 3000, word: 0 }) - }, - 500 => PricingAt { - info: Some(String::from("enable fake EIP at block 500")), - price: Pricing::Linear(Linear { base: 10, word: 0 }) - } - ] - ); - } - - #[test] - fn deserialization_blake2_f_builtin() { - let s = r#"{ - "name": "blake2_f", - "activate_at": "0xffffff", - "pricing": { "blake2_f": { "gas_per_round": 123 } } - }"#; - let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); - assert_eq!(builtin.name, "blake2_f"); - assert_eq!( - builtin.pricing, - btreemap![ - 0xffffff => PricingAt { - info: None, - price: Pricing::Blake2F { gas_per_round: 123 } - } - ] - ); - } - - #[test] - fn deserialization_alt_bn128_const_operations() { - let s = r#"{ - "name": "alt_bn128_mul", - "pricing": { - "100500": { - "price": { "alt_bn128_const_operations": { "price": 123 }} - } - } - }"#; - let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); - assert_eq!(builtin.name, "alt_bn128_mul"); - assert_eq!( - builtin.pricing, - btreemap![ - 100500 => PricingAt { - info: None, - price: Pricing::AltBn128ConstOperations(AltBn128ConstOperations { - price: 123, - }), - } - ] - ); - } - - #[test] - fn activate_at() { - let s = r#"{ - "name": "late_start", - "activate_at": 100000, - "pricing": { "modexp": { "divisor": 5, "is_eip_2565": false } } - }"#; - - let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); - assert_eq!(builtin.name, "late_start"); - assert_eq!( - builtin.pricing, - btreemap![ - 100_000 => PricingAt { - info: None, - price: Pricing::Modexp(Modexp { divisor: 5, is_eip_2565: false }) - } - ] - ); - } -} diff --git a/evm-tests/ethjson/src/spec/clique.rs b/evm-tests/ethjson/src/spec/clique.rs deleted file mode 100644 index 01515985..00000000 --- a/evm-tests/ethjson/src/spec/clique.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Clique params deserialization. - -use serde::Deserialize; -use std::num::NonZeroU64; - -/// Clique params deserialization. -#[derive(Debug, PartialEq, Eq, Deserialize)] -pub struct CliqueParams { - /// period as defined in EIP 225 - pub period: Option, - /// epoch length as defined in EIP 225 - pub epoch: Option, -} - -/// Clique engine deserialization. -#[derive(Debug, PartialEq, Eq, Deserialize)] -pub struct Clique { - /// CliqueEngine params - pub params: CliqueParams, -} - -#[cfg(test)] -mod tests { - use super::{Clique, NonZeroU64}; - - #[test] - fn clique_deserialization() { - let s = r#"{ - "params": { - "period": 5, - "epoch": 30000 - } - }"#; - - let deserialized: Clique = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized.params.period, Some(5u64)); - assert_eq!(deserialized.params.epoch, NonZeroU64::new(30000)); - } -} diff --git a/evm-tests/ethjson/src/spec/engine.rs b/evm-tests/ethjson/src/spec/engine.rs deleted file mode 100644 index edffbb80..00000000 --- a/evm-tests/ethjson/src/spec/engine.rs +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Engine deserialization. - -use super::{AuthorityRound, BasicAuthority, Clique, Ethash, InstantSeal, NullEngine}; -use serde::Deserialize; - -/// Engine deserialization. -#[derive(Debug, PartialEq, Deserialize)] -#[serde(deny_unknown_fields)] -#[serde(rename_all = "camelCase")] -pub enum Engine { - /// Null engine. - Null(NullEngine), - /// Instantly sealing engine. - InstantSeal(Option), - /// Ethash engine. - #[serde(rename = "Ethash")] - Ethash(Ethash), - /// BasicAuthority engine. - BasicAuthority(BasicAuthority), - /// AuthorityRound engine. - AuthorityRound(AuthorityRound), - /// Clique engine. - Clique(Clique), -} - -#[cfg(test)] -mod tests { - use super::Engine; - - #[test] - fn engine_deserialization() { - let s = r#"{ - "null": { - "params": { - "blockReward": "0x0d" - } - } - }"#; - - let deserialized: Engine = serde_json::from_str(s).unwrap(); - match deserialized { - Engine::Null(_) => {} // unit test in its own file. - _ => panic!(), - } - - let s = r#"{ - "instantSeal": {"params": {}} - }"#; - - let deserialized: Engine = serde_json::from_str(s).unwrap(); - match deserialized { - Engine::InstantSeal(_) => {} // instant seal is unit tested in its own file. - _ => panic!(), - }; - - let s = r#"{ - "instantSeal": null - }"#; - - let deserialized: Engine = serde_json::from_str(s).unwrap(); - match deserialized { - Engine::InstantSeal(_) => {} // instant seal is unit tested in its own file. - _ => panic!(), - }; - - let s = r#"{ - "Ethash": { - "params": { - "minimumDifficulty": "0x020000", - "difficultyBoundDivisor": "0x0800", - "durationLimit": "0x0d", - "homesteadTransition" : "0x", - "daoHardforkTransition": "0xffffffffffffffff", - "daoHardforkBeneficiary": "0x0000000000000000000000000000000000000000", - "daoHardforkAccounts": [] - } - } - }"#; - - let deserialized: Engine = serde_json::from_str(s).unwrap(); - match deserialized { - Engine::Ethash(_) => {} // ethash is unit tested in its own file. - _ => panic!(), - }; - - let s = r#"{ - "basicAuthority": { - "params": { - "durationLimit": "0x0d", - "validators" : { - "list": ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"] - } - } - } - }"#; - let deserialized: Engine = serde_json::from_str(s).unwrap(); - match deserialized { - Engine::BasicAuthority(_) => {} // basicAuthority is unit tested in its own file. - _ => panic!(), - }; - - let s = r#"{ - "authorityRound": { - "params": { - "stepDuration": "0x02", - "validators": { - "list" : ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"] - }, - "startStep" : 24, - "validateStepTransition": 150 - } - } - }"#; - let deserialized: Engine = serde_json::from_str(s).unwrap(); - match deserialized { - Engine::AuthorityRound(_) => {} // AuthorityRound is unit tested in its own file. - _ => panic!(), - }; - - let s = r#"{ - "clique": { - "params": { - "period": 15, - "epoch": 30000 - } - } - }"#; - let deserialized: Engine = serde_json::from_str(s).unwrap(); - match deserialized { - Engine::Clique(_) => {} // Clique is unit tested in its own file. - _ => panic!(), - }; - } -} diff --git a/evm-tests/ethjson/src/spec/ethash.rs b/evm-tests/ethjson/src/spec/ethash.rs deleted file mode 100644 index 8f3a0254..00000000 --- a/evm-tests/ethjson/src/spec/ethash.rs +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Ethash params deserialization. - -use crate::{ - bytes::Bytes, - hash::Address, - uint::{self, Uint}, -}; -use serde::Deserialize; -use std::collections::BTreeMap; - -/// Deserializable doppelganger of block rewards for EthashParams -#[derive(Clone, Debug, PartialEq, Eq, Deserialize)] -#[serde(deny_unknown_fields)] -#[serde(untagged)] -pub enum BlockReward { - /// Single block reward - Single(Uint), - /// Several block rewards - Multi(BTreeMap), -} - -/// Deserializable doppelganger of EthashParams. -#[derive(Clone, Debug, PartialEq, Eq, Deserialize)] -#[serde(deny_unknown_fields)] -#[serde(rename_all = "camelCase")] -pub struct EthashParams { - /// See main EthashParams docs. - #[serde(deserialize_with = "uint::validate_non_zero")] - pub minimum_difficulty: Uint, - /// See main EthashParams docs. - #[serde(deserialize_with = "uint::validate_non_zero")] - pub difficulty_bound_divisor: Uint, - /// See main EthashParams docs. - #[serde(default, deserialize_with = "uint::validate_optional_non_zero")] - pub difficulty_increment_divisor: Option, - /// See main EthashParams docs. - #[serde(default, deserialize_with = "uint::validate_optional_non_zero")] - pub metropolis_difficulty_increment_divisor: Option, - /// See main EthashParams docs. - pub duration_limit: Option, - - /// See main EthashParams docs. - pub homestead_transition: Option, - /// Reward per block in wei. - pub block_reward: Option, - /// Block at which the block reward contract should start being used. - pub block_reward_contract_transition: Option, - /// Block reward contract address (setting the block reward contract - /// overrides all other block reward parameters). - pub block_reward_contract_address: Option
, - /// Block reward code. This overrides the block reward contract address. - pub block_reward_contract_code: Option, - - /// See main EthashParams docs. - pub dao_hardfork_transition: Option, - /// See main EthashParams docs. - pub dao_hardfork_beneficiary: Option
, - /// See main EthashParams docs. - pub dao_hardfork_accounts: Option>, - - /// See main EthashParams docs. - pub difficulty_hardfork_transition: Option, - /// See main EthashParams docs. - #[serde(default, deserialize_with = "uint::validate_optional_non_zero")] - pub difficulty_hardfork_bound_divisor: Option, - /// See main EthashParams docs. - pub bomb_defuse_transition: Option, - - /// See main EthashParams docs. - pub eip100b_transition: Option, - - /// See main EthashParams docs. - #[serde(default, deserialize_with = "uint::validate_optional_non_zero")] - pub ecip1017_era_rounds: Option, - - /// Delays of difficulty bombs. - pub difficulty_bomb_delays: Option>, - - /// EXPIP-2 block height - pub expip2_transition: Option, - /// EXPIP-2 duration limit - pub expip2_duration_limit: Option, - /// Block to transition to progpow - pub progpow_transition: Option, -} - -/// Ethash engine deserialization. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct Ethash { - /// Ethash params. - pub params: EthashParams, -} - -#[cfg(test)] -mod tests { - use super::{Address, BlockReward, Ethash, EthashParams, Uint}; - use ethereum_types::{H160, U256}; - use std::str::FromStr; - - #[test] - fn ethash_deserialization() { - let s = r#"{ - "params": { - "minimumDifficulty": "0x020000", - "difficultyBoundDivisor": "0x0800", - "durationLimit": "0x0d", - "homesteadTransition": "0x42", - "blockReward": "0x100", - "daoHardforkTransition": "0x08", - "daoHardforkBeneficiary": "0xabcabcabcabcabcabcabcabcabcabcabcabcabca", - "daoHardforkAccounts": [ - "0x304a554a310c7e546dfe434669c62820b7d83490", - "0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", - "0xfe24cdd8648121a43a7c86d289be4dd2951ed49f", - "0x17802f43a0137c506ba92291391a8a8f207f487d", - "0xb136707642a4ea12fb4bae820f03d2562ebff487", - "0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940", - "0xf14c14075d6c4ed84b86798af0956deef67365b5", - "0xca544e5c4687d109611d0f8f928b53a25af72448", - "0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c", - "0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7", - "0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6", - "0x2b3455ec7fedf16e646268bf88846bd7a2319bb2", - "0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a", - "0xd343b217de44030afaa275f54d31a9317c7f441e", - "0x84ef4b2357079cd7a7c69fd7a37cd0609a679106", - "0xda2fef9e4a3230988ff17df2165440f37e8b1708", - "0xf4c64518ea10f995918a454158c6b61407ea345c", - "0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97", - "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", - "0x807640a13483f8ac783c557fcdf27be11ea4ac7a" - ], - "difficultyHardforkTransition": "0x59d9", - "difficultyHardforkBoundDivisor": "0x0200", - "bombDefuseTransition": "0x41", - "eip100bTransition": "0x42" - } - }"#; - - let deserialized: Ethash = serde_json::from_str(s).unwrap(); - - assert_eq!( - deserialized, - Ethash { - params: EthashParams { - minimum_difficulty: Uint(U256::from(0x020000)), - difficulty_bound_divisor: Uint(U256::from(0x0800)), - difficulty_increment_divisor: None, - metropolis_difficulty_increment_divisor: None, - duration_limit: Some(Uint(U256::from(0x0d))), - homestead_transition: Some(Uint(U256::from(0x42))), - block_reward: Some(BlockReward::Single(Uint(U256::from(0x100)))), - block_reward_contract_address: None, - block_reward_contract_code: None, - block_reward_contract_transition: None, - dao_hardfork_transition: Some(Uint(U256::from(0x08))), - dao_hardfork_beneficiary: Some(Address( - H160::from_str("abcabcabcabcabcabcabcabcabcabcabcabcabca").unwrap() - )), - dao_hardfork_accounts: Some(vec![ - Address( - H160::from_str("304a554a310c7e546dfe434669c62820b7d83490").unwrap() - ), - Address( - H160::from_str("914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79").unwrap() - ), - Address( - H160::from_str("fe24cdd8648121a43a7c86d289be4dd2951ed49f").unwrap() - ), - Address( - H160::from_str("17802f43a0137c506ba92291391a8a8f207f487d").unwrap() - ), - Address( - H160::from_str("b136707642a4ea12fb4bae820f03d2562ebff487").unwrap() - ), - Address( - H160::from_str("dbe9b615a3ae8709af8b93336ce9b477e4ac0940").unwrap() - ), - Address( - H160::from_str("f14c14075d6c4ed84b86798af0956deef67365b5").unwrap() - ), - Address( - H160::from_str("ca544e5c4687d109611d0f8f928b53a25af72448").unwrap() - ), - Address( - H160::from_str("aeeb8ff27288bdabc0fa5ebb731b6f409507516c").unwrap() - ), - Address( - H160::from_str("cbb9d3703e651b0d496cdefb8b92c25aeb2171f7").unwrap() - ), - Address( - H160::from_str("accc230e8a6e5be9160b8cdf2864dd2a001c28b6").unwrap() - ), - Address( - H160::from_str("2b3455ec7fedf16e646268bf88846bd7a2319bb2").unwrap() - ), - Address( - H160::from_str("4613f3bca5c44ea06337a9e439fbc6d42e501d0a").unwrap() - ), - Address( - H160::from_str("d343b217de44030afaa275f54d31a9317c7f441e").unwrap() - ), - Address( - H160::from_str("84ef4b2357079cd7a7c69fd7a37cd0609a679106").unwrap() - ), - Address( - H160::from_str("da2fef9e4a3230988ff17df2165440f37e8b1708").unwrap() - ), - Address( - H160::from_str("f4c64518ea10f995918a454158c6b61407ea345c").unwrap() - ), - Address( - H160::from_str("7602b46df5390e432ef1c307d4f2c9ff6d65cc97").unwrap() - ), - Address( - H160::from_str("bb9bc244d798123fde783fcc1c72d3bb8c189413").unwrap() - ), - Address( - H160::from_str("807640a13483f8ac783c557fcdf27be11ea4ac7a").unwrap() - ), - ]), - difficulty_hardfork_transition: Some(Uint(U256::from(0x59d9))), - difficulty_hardfork_bound_divisor: Some(Uint(U256::from(0x0200))), - bomb_defuse_transition: Some(Uint(U256::from(0x41))), - eip100b_transition: Some(Uint(U256::from(0x42))), - ecip1017_era_rounds: None, - expip2_transition: None, - expip2_duration_limit: None, - progpow_transition: None, - difficulty_bomb_delays: None, - } - } - ); - } - - #[test] - fn ethash_deserialization_missing_optionals() { - let s = r#"{ - "params": { - "difficultyBoundDivisor": "0x0800", - "minimumDifficulty": "0x020000" - } - }"#; - - let deserialized: Ethash = serde_json::from_str(s).unwrap(); - assert_eq!( - deserialized, - Ethash { - params: EthashParams { - minimum_difficulty: Uint(U256::from(0x020000)), - difficulty_bound_divisor: Uint(U256::from(0x0800)), - difficulty_increment_divisor: None, - metropolis_difficulty_increment_divisor: None, - duration_limit: None, - homestead_transition: None, - block_reward: None, - block_reward_contract_address: None, - block_reward_contract_code: None, - block_reward_contract_transition: None, - dao_hardfork_transition: None, - dao_hardfork_beneficiary: None, - dao_hardfork_accounts: None, - difficulty_hardfork_transition: None, - difficulty_hardfork_bound_divisor: None, - bomb_defuse_transition: None, - eip100b_transition: None, - ecip1017_era_rounds: None, - expip2_transition: None, - expip2_duration_limit: None, - progpow_transition: None, - difficulty_bomb_delays: None, - } - } - ); - } - - #[test] - #[should_panic(expected = "a non-zero value")] - fn test_zero_value_divisor() { - let s = r#"{ - "params": { - "difficultyBoundDivisor": "0x0", - "minimumDifficulty": "0x020000" - } - }"#; - - let _deserialized: Ethash = serde_json::from_str(s).unwrap(); - } -} diff --git a/evm-tests/ethjson/src/spec/genesis.rs b/evm-tests/ethjson/src/spec/genesis.rs deleted file mode 100644 index 21ab5a13..00000000 --- a/evm-tests/ethjson/src/spec/genesis.rs +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Spec genesis deserialization. - -use crate::{ - bytes::Bytes, - hash::{Address, H256}, - spec::Seal, - uint::{self, Uint}, -}; -use serde::Deserialize; - -/// Spec genesis. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(deny_unknown_fields)] -#[serde(rename_all = "camelCase")] -pub struct Genesis { - /// Seal. - pub seal: Seal, - /// Difficulty. - pub difficulty: Uint, - /// Block author, defaults to 0. - pub author: Option
, - /// Block timestamp, defaults to 0. - pub timestamp: Option, - /// Parent hash, defaults to 0. - pub parent_hash: Option, - /// Gas limit. - #[serde(deserialize_with = "uint::validate_non_zero")] - pub gas_limit: Uint, - /// Transactions root. - pub transactions_root: Option, - /// Receipts root. - pub receipts_root: Option, - /// State root. - pub state_root: Option, - /// Gas used. - pub gas_used: Option, - /// Extra data. - pub extra_data: Option, -} - -#[cfg(test)] -mod tests { - use super::{Address, Bytes, Genesis, Uint, H256}; - use crate::{ - hash::H64, - spec::{Ethereum, Seal}, - }; - use ethereum_types::{H160, H256 as Eth256, H64 as Eth64, U256}; - use std::str::FromStr; - - #[test] - fn genesis_deserialization() { - let s = r#"{ - "difficulty": "0x400000000", - "seal": { - "ethereum": { - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "nonce": "0x00006d6f7264656e" - } - }, - "author": "0x1000000000000000000000000000000000000001", - "timestamp": "0x07", - "parentHash": "0x9000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", - "gasLimit": "0x1388", - "stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544" - }"#; - let deserialized: Genesis = serde_json::from_str(s).unwrap(); - assert_eq!( - deserialized, - Genesis { - seal: Seal::Ethereum(Ethereum { - nonce: H64(Eth64::from_str("00006d6f7264656e").unwrap()), - mix_hash: H256( - Eth256::from_str( - "0000000000000000000000000000000000000000000000000000000000000000" - ) - .unwrap() - ) - }), - difficulty: Uint(U256::from(0x400000000u64)), - author: Some(Address( - H160::from_str("1000000000000000000000000000000000000001").unwrap() - )), - timestamp: Some(Uint(U256::from(0x07))), - parent_hash: Some(H256( - Eth256::from_str( - "9000000000000000000000000000000000000000000000000000000000000000" - ) - .unwrap() - )), - gas_limit: Uint(U256::from(0x1388)), - transactions_root: None, - receipts_root: None, - state_root: Some(H256( - Eth256::from_str( - "d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544" - ) - .unwrap() - )), - gas_used: None, - extra_data: Some( - Bytes::from_str( - "11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa" - ) - .unwrap() - ), - } - ); - } -} diff --git a/evm-tests/ethjson/src/spec/hardcoded_sync.rs b/evm-tests/ethjson/src/spec/hardcoded_sync.rs deleted file mode 100644 index 4a71f527..00000000 --- a/evm-tests/ethjson/src/spec/hardcoded_sync.rs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Spec hardcoded synchronization deserialization for the light client. - -use crate::{bytes::Bytes, hash::H256, uint::Uint}; -use serde::Deserialize; - -/// Spec hardcoded sync. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(deny_unknown_fields)] -#[serde(rename_all = "camelCase")] -pub struct HardcodedSync { - /// Hexadecimal of the RLP encoding of the header of the block to start synchronization from. - pub header: Bytes, - /// Total difficulty including the block of `header`. - pub total_difficulty: Uint, - /// Ordered trie roots of blocks before and including `header`. - #[serde(rename = "CHTs")] - pub chts: Vec, -} - -#[cfg(test)] -mod tests { - use super::{HardcodedSync, Uint, H256}; - use ethereum_types::{H256 as Eth256, U256}; - use std::str::FromStr; - - #[test] - fn hardcoded_sync_deserialization() { - let s = r#"{ - "header": "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23", - "totalDifficulty": "0x400000000", - "CHTs": [ - "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", - "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544" - ] - }"#; - let deserialized: HardcodedSync = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, HardcodedSync { - header: "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".parse().unwrap(), - total_difficulty: Uint(U256::from(0x400000000u64)), - chts: vec![ - H256(Eth256::from_str("11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa").unwrap()), - H256(Eth256::from_str("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544").unwrap()), - ] - }); - } -} diff --git a/evm-tests/ethjson/src/spec/instant_seal.rs b/evm-tests/ethjson/src/spec/instant_seal.rs deleted file mode 100644 index 10e32b3e..00000000 --- a/evm-tests/ethjson/src/spec/instant_seal.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Instant seal engine params deserialization. - -use serde::Deserialize; - -/// Instant seal engine params deserialization. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(deny_unknown_fields)] -#[serde(rename_all = "camelCase")] -pub struct InstantSealParams { - /// Whether to enable millisecond timestamp. - #[serde(default)] - pub millisecond_timestamp: bool, -} - -/// Instant seal engine descriptor. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct InstantSeal { - /// Instant seal parameters. - pub params: InstantSealParams, -} diff --git a/evm-tests/ethjson/src/spec/mod.rs b/evm-tests/ethjson/src/spec/mod.rs deleted file mode 100644 index 17ff23dc..00000000 --- a/evm-tests/ethjson/src/spec/mod.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Spec deserialization. - -pub mod account; -pub mod authority_round; -pub mod basic_authority; -pub mod builtin; -pub mod clique; -pub mod engine; -pub mod ethash; -pub mod genesis; -pub mod hardcoded_sync; -pub mod instant_seal; -pub mod null_engine; -pub mod params; -pub mod seal; -#[allow(clippy::module_inception)] -pub mod spec; -pub mod state; -pub mod step_duration; -pub mod validator_set; - -pub use self::account::Account; -pub use self::authority_round::{AuthorityRound, AuthorityRoundParams}; -pub use self::basic_authority::{BasicAuthority, BasicAuthorityParams}; -pub use self::builtin::{Builtin, Linear, Pricing}; -pub use self::clique::{Clique, CliqueParams}; -pub use self::engine::Engine; -pub use self::ethash::{BlockReward, Ethash, EthashParams}; -pub use self::genesis::Genesis; -pub use self::hardcoded_sync::HardcodedSync; -pub use self::instant_seal::{InstantSeal, InstantSealParams}; -pub use self::null_engine::{NullEngine, NullEngineParams}; -pub use self::params::Params; -pub use self::seal::{AuthorityRoundSeal, Ethereum, Seal, TendermintSeal}; -pub use self::spec::{ForkSpec, Spec}; -pub use self::state::{HashOrMap, State}; -pub use self::step_duration::StepDuration; -pub use self::validator_set::ValidatorSet; diff --git a/evm-tests/ethjson/src/spec/null_engine.rs b/evm-tests/ethjson/src/spec/null_engine.rs deleted file mode 100644 index bad17333..00000000 --- a/evm-tests/ethjson/src/spec/null_engine.rs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Null engine params deserialization. - -use crate::uint::Uint; -use serde::Deserialize; - -/// Authority params deserialization. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(deny_unknown_fields)] -#[serde(rename_all = "camelCase")] -pub struct NullEngineParams { - /// Block reward. - pub block_reward: Option, - /// Immediate finalization. - pub immediate_finalization: Option, -} - -/// Null engine descriptor -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct NullEngine { - /// Ethash params. - pub params: NullEngineParams, -} - -#[cfg(test)] -mod tests { - use super::{NullEngine, Uint}; - use ethereum_types::U256; - - #[test] - fn null_engine_deserialization() { - let s = r#"{ - "params": { - "blockReward": "0x0d" - } - }"#; - - let deserialized: NullEngine = serde_json::from_str(s).unwrap(); - assert_eq!( - deserialized.params.block_reward, - Some(Uint(U256::from(0x0d))) - ); - } -} diff --git a/evm-tests/ethjson/src/spec/params.rs b/evm-tests/ethjson/src/spec/params.rs deleted file mode 100644 index 7a6b7838..00000000 --- a/evm-tests/ethjson/src/spec/params.rs +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Spec params deserialization. - -use crate::{ - bytes::Bytes, - hash::{Address, H256}, - uint::{self, Uint}, -}; -use serde::Deserialize; - -/// Spec params. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(deny_unknown_fields)] -#[serde(rename_all = "camelCase")] -pub struct Params { - /// Account start nonce, defaults to 0. - pub account_start_nonce: Option, - /// Maximum size of extra data. - pub maximum_extra_data_size: Uint, - /// Minimum gas limit. - pub min_gas_limit: Uint, - - /// Network id. - #[serde(rename = "networkID")] - pub network_id: Uint, - /// Chain id. - #[serde(rename = "chainID")] - pub chain_id: Option, - - /// Name of the main ("eth") subprotocol. - pub subprotocol_name: Option, - - /// Option fork block number to check. - pub fork_block: Option, - /// Expected fork block hash. - #[serde(rename = "forkCanonHash")] - pub fork_hash: Option, - - /// See main EthashParams docs. - pub eip150_transition: Option, - - /// See main EthashParams docs. - pub eip160_transition: Option, - - /// See main EthashParams docs. - pub eip161abc_transition: Option, - /// See main EthashParams docs. - pub eip161d_transition: Option, - - /// See `CommonParams` docs. - pub eip98_transition: Option, - /// See `CommonParams` docs. - pub eip155_transition: Option, - /// See `CommonParams` docs. - pub validate_chain_id_transition: Option, - /// See `CommonParams` docs. - pub validate_receipts_transition: Option, - /// See `CommonParams` docs. - pub eip140_transition: Option, - /// See `CommonParams` docs. - pub eip210_transition: Option, - /// See `CommonParams` docs. - pub eip210_contract_address: Option
, - /// See `CommonParams` docs. - pub eip210_contract_code: Option, - /// See `CommonParams` docs. - pub eip210_contract_gas: Option, - /// See `CommonParams` docs. - pub eip211_transition: Option, - /// See `CommonParams` docs. - pub eip145_transition: Option, - /// See `CommonParams` docs. - pub eip214_transition: Option, - /// See `CommonParams` docs. - pub eip658_transition: Option, - /// See `CommonParams` docs. - pub eip1052_transition: Option, - /// See `CommonParams` docs. - pub eip1283_transition: Option, - /// See `CommonParams` docs. - pub eip1283_disable_transition: Option, - /// See `CommonParams` docs. - pub eip1283_reenable_transition: Option, - /// See `CommonParams` docs. - pub eip1014_transition: Option, - /// See `CommonParams` docs. - pub eip1706_transition: Option, - /// See `CommonParams` docs. - pub eip1344_transition: Option, - /// See `CommonParams` docs. - pub eip1884_transition: Option, - /// See `CommonParams` docs. - pub eip2028_transition: Option, - /// See `CommonParams` docs. - pub eip2046_transition: Option, - /// See `CommonParams` docs. - pub eip2200_advance_transition: Option, - /// See `CommonParams` docs. - pub eip2315_transition: Option, - /// See `CommonParams` docs. - pub dust_protection_transition: Option, - /// See `CommonParams` docs. - pub nonce_cap_increment: Option, - /// See `CommonParams` docs. - pub remove_dust_contracts: Option, - /// See `CommonParams` docs. - #[serde(deserialize_with = "uint::validate_non_zero")] - pub gas_limit_bound_divisor: Uint, - /// See `CommonParams` docs. - pub registrar: Option
, - /// Apply reward flag - pub apply_reward: Option, - /// Node permission contract address. - pub node_permission_contract: Option
, - /// See main EthashParams docs. - pub max_code_size: Option, - /// Maximum size of transaction RLP payload. - pub max_transaction_size: Option, - /// See main EthashParams docs. - pub max_code_size_transition: Option, - /// Transaction permission contract address. - pub transaction_permission_contract: Option
, - /// Block at which the transaction permission contract should start being used. - pub transaction_permission_contract_transition: Option, - /// Wasm activation block height, if not activated from start. - pub wasm_activation_transition: Option, - /// Define a separate wasm version instead of using the prefix. - pub wasm_version: Option, - /// KIP4 activiation block height. - pub kip4_transition: Option, - /// KIP6 activiation block height. - pub kip6_transition: Option, -} - -#[cfg(test)] -mod tests { - use super::{Params, Uint}; - use ethereum_types::U256; - - #[test] - fn params_deserialization() { - let s = r#"{ - "maximumExtraDataSize": "0x20", - "networkID": "0x1", - "chainID": "0x15", - "subprotocolName": "exp", - "minGasLimit": "0x1388", - "accountStartNonce": "0x01", - "gasLimitBoundDivisor": "0x20", - "maxCodeSize": "0x1000", - "wasmActivationTransition": "0x1010" - }"#; - - let deserialized: Params = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized.maximum_extra_data_size, Uint(U256::from(0x20))); - assert_eq!(deserialized.network_id, Uint(U256::from(0x1))); - assert_eq!(deserialized.chain_id, Some(Uint(U256::from(0x15)))); - assert_eq!(deserialized.subprotocol_name, Some("exp".to_owned())); - assert_eq!(deserialized.min_gas_limit, Uint(U256::from(0x1388))); - assert_eq!( - deserialized.account_start_nonce, - Some(Uint(U256::from(0x01))) - ); - assert_eq!(deserialized.gas_limit_bound_divisor, Uint(U256::from(0x20))); - assert_eq!(deserialized.max_code_size, Some(Uint(U256::from(0x1000)))); - assert_eq!( - deserialized.wasm_activation_transition, - Some(Uint(U256::from(0x1010))) - ); - } - - #[test] - #[should_panic(expected = "a non-zero value")] - fn test_zero_value_divisor() { - let s = r#"{ - "maximumExtraDataSize": "0x20", - "networkID" : "0x1", - "chainID" : "0x15", - "subprotocolName" : "exp", - "minGasLimit": "0x1388", - "accountStartNonce": "0x01", - "gasLimitBoundDivisor": "0x0", - "maxCodeSize": "0x1000" - }"#; - - let _deserialized: Params = serde_json::from_str(s).unwrap(); - } -} diff --git a/evm-tests/ethjson/src/spec/seal.rs b/evm-tests/ethjson/src/spec/seal.rs deleted file mode 100644 index d77246ee..00000000 --- a/evm-tests/ethjson/src/spec/seal.rs +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Spec seal deserialization. - -use crate::{ - bytes::Bytes, - hash::{H256, H520, H64}, - uint::Uint, -}; -use serde::Deserialize; - -/// Ethereum seal. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(deny_unknown_fields)] -#[serde(rename_all = "camelCase")] -pub struct Ethereum { - /// Seal nonce. - pub nonce: H64, - /// Seal mix hash. - pub mix_hash: H256, -} - -/// AuthorityRound seal. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct AuthorityRoundSeal { - /// Seal step. - pub step: Uint, - /// Seal signature. - pub signature: H520, -} - -/// Tendermint seal. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct TendermintSeal { - /// Seal round. - pub round: Uint, - /// Proposal seal signature. - pub proposal: H520, - /// Proposal seal signature. - pub precommits: Vec, -} - -/// Seal variants. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(deny_unknown_fields)] -#[serde(rename_all = "camelCase")] -pub enum Seal { - /// Ethereum seal. - Ethereum(Ethereum), - /// AuthorityRound seal. - AuthorityRound(AuthorityRoundSeal), - /// Tendermint seal. - Tendermint(TendermintSeal), - /// Generic seal. - Generic(Bytes), -} - -#[cfg(test)] -mod tests { - use super::{AuthorityRoundSeal, Bytes, Ethereum, Seal, TendermintSeal, Uint, H256, H520, H64}; - use ethereum_types::{H256 as Eth256, H520 as Eth520, H64 as Eth64, U256}; - use std::str::FromStr; - - #[test] - fn seal_deserialization() { - let s = r#"[{ - "ethereum": { - "nonce": "0x0000000000000042", - "mixHash": "0x1000000000000000000000000000000000000000000000000000000000000001" - } - },{ - "generic": "0xe011bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa" - },{ - "authorityRound": { - "step": "0x0", - "signature": "0x2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002" - } - },{ - "tendermint": { - "round": "0x3", - "proposal": "0x3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003", - "precommits": [ - "0x4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004" - ] - } - }]"#; - - let deserialized: Vec = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized.len(), 4); - - // [0] - assert_eq!( - deserialized[0], - Seal::Ethereum(Ethereum { - nonce: H64(Eth64::from_str("0000000000000042").unwrap()), - mix_hash: H256( - Eth256::from_str( - "1000000000000000000000000000000000000000000000000000000000000001" - ) - .unwrap() - ) - }) - ); - - // [1] - assert_eq!( - deserialized[1], - Seal::Generic(Bytes::new(vec![ - 0xe0, 0x11, 0xbb, 0xe8, 0xdb, 0x4e, 0x34, 0x7b, 0x4e, 0x8c, 0x93, 0x7c, 0x1c, 0x83, - 0x70, 0xe4, 0xb5, 0xed, 0x33, 0xad, 0xb3, 0xdb, 0x69, 0xcb, 0xdb, 0x7a, 0x38, 0xe1, - 0xe5, 0x0b, 0x1b, 0x82, 0xfa - ])) - ); - - // [2] - assert_eq!(deserialized[2], Seal::AuthorityRound(AuthorityRoundSeal { - step: Uint(U256::from(0x0)), - signature: H520(Eth520::from_str("2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002").unwrap()) - })); - - // [3] - assert_eq!(deserialized[3], Seal::Tendermint(TendermintSeal { - round: Uint(U256::from(0x3)), - proposal: H520(Eth520::from_str("3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003").unwrap()), - precommits: vec![H520(Eth520::from_str("4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004").unwrap())] - })); - } -} diff --git a/evm-tests/ethjson/src/spec/spec.rs b/evm-tests/ethjson/src/spec/spec.rs deleted file mode 100644 index e2ed716b..00000000 --- a/evm-tests/ethjson/src/spec/spec.rs +++ /dev/null @@ -1,341 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Spec deserialization. - -use crate::spec::{Engine, Genesis, HardcodedSync, Params, State}; -use serde::Deserialize; -use serde_json::Error; -use std::convert::TryFrom; -use std::io::Read; - -/// Fork spec definition -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize)] -pub enum ForkSpec { - /// Byzantium transition test-net - EIP158ToByzantiumAt5, - /// Homestead transition test-net - FrontierToHomesteadAt5, - /// Homestead transition test-net - HomesteadToDaoAt5, - /// EIP158/EIP161 transition test-net - HomesteadToEIP150At5, - /// ConstantinopleFix transition test-net - ByzantiumToConstantinopleFixAt5, - /// Istanbul transition test-net - ConstantinopleFixToIstanbulAt5, - /// EIP 150 Tangerine Whistle: Gas cost changes for IO-heavy operations (#2,463,000, 2016-10-18) - EIP150, - /// EIP 158/EIP 161 Spurious Dragon: State trie clearing (#2,675,000, 2016-11-22) - EIP158, - /// Frontier (#1, 2015-07-30) - Frontier, - /// Homestead (#1,150,000, 2016-03-14) - Homestead, - /// Byzantium Metropolis phase 1 (#4,370,000, 2017-10-16) - Byzantium, - /// Constantinople Metropolis phase 2 (#7,280,000, 2019-02-28) - Constantinople, - /// Constantinople transition test-net - ConstantinopleFix, - /// Istanbul (#9,069,000, 2019-12-08) - Istanbul, - /// Berlin (#12,244,000, 2021-04-15) - Berlin, - - /// London (#12,965,000, 2021-08-05) - London, - /// Paris - The Merge (#15,537,394, 2022-09-15) - Merge, - /// Paris - The Merge (#15,537,394, 2022-09-15) - Paris, - /// Shanghai (#17,034,870, 2023-04-12) - Shanghai, - /// Cancun (28,750,000, 2024-03-13) - Cancun, - /// Prague (future) - Prague, -} - -impl ForkSpec { - /// Returns true if the fork is at or after the merge. - pub const fn is_eth2(&self) -> bool { - !matches!( - *self, - Self::EIP158ToByzantiumAt5 - | Self::FrontierToHomesteadAt5 - | Self::HomesteadToDaoAt5 - | Self::HomesteadToEIP150At5 - | Self::ByzantiumToConstantinopleFixAt5 - | Self::ConstantinopleFixToIstanbulAt5 - | Self::EIP150 - | Self::EIP158 - | Self::Frontier - | Self::Homestead - | Self::Byzantium - | Self::Constantinople - | Self::ConstantinopleFix - | Self::Istanbul - | Self::Berlin - ) - } -} - -impl TryFrom for ForkSpec { - type Error = String; - fn try_from(value: String) -> Result { - let res = match value.to_lowercase().as_str() { - "eip158tobyzantiumat5" => Self::EIP158ToByzantiumAt5, - "frontiertohomesteadat5" => Self::FrontierToHomesteadAt5, - "homesteadtodaoat5" => Self::HomesteadToDaoAt5, - "homesteadtoeip150at5" => Self::HomesteadToEIP150At5, - "byzantiumtoconstantinoplefixat5" => Self::ByzantiumToConstantinopleFixAt5, - "constantinoplefixtoistanbulat5" => Self::ConstantinopleFixToIstanbulAt5, - "eip150" => Self::EIP150, - "eip158" => Self::EIP158, - "frontier" => Self::Frontier, - "homestead" => Self::Homestead, - "byzantium" => Self::Byzantium, - "constantinople" => Self::Constantinople, - "constantinoplefix" => Self::ConstantinopleFix, - "istanbul" => Self::Istanbul, - "berlin" => Self::Berlin, - "london" => Self::London, - "merge" => Self::Merge, - "paris" => Self::Paris, - "shanghai" => Self::Shanghai, - "cancun" => Self::Cancun, - "prague" => Self::Prague, - other => return Err(format!("Unknown hard fork spec {other}")), - }; - Ok(res) - } -} - -/// Spec deserialization. -#[derive(Debug, PartialEq, Deserialize)] -#[serde(deny_unknown_fields)] -#[serde(rename_all = "camelCase")] -pub struct Spec { - /// Spec name. - pub name: String, - /// Special fork name. - pub data_dir: Option, - /// Engine. - pub engine: Engine, - /// Spec params. - pub params: Params, - /// Genesis header. - pub genesis: Genesis, - /// Genesis state. - pub accounts: State, - /// Boot nodes. - pub nodes: Option>, - /// Hardcoded synchronization for the light client. - pub hardcoded_sync: Option, -} - -impl Spec { - /// Loads test from json. - pub fn load(reader: R) -> Result - where - R: Read, - { - serde_json::from_reader(reader) - } -} - -#[cfg(test)] -mod tests { - use super::Spec; - - #[test] - fn should_error_on_unknown_fields() { - let s = r#"{ - "name": "Null Morden", - "dataDir": "morden", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x020000", - "difficultyBoundDivisor": "0x0800", - "durationLimit": "0x0d", - "homesteadTransition" : "0x", - "daoHardforkTransition": "0xffffffffffffffff", - "daoHardforkBeneficiary": "0x0000000000000000000000000000000000000000", - "daoHardforkAccounts": [] - } - } - }, - "params": { - "accountStartNonce": "0x0100000", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x2", - "forkBlock": "0xffffffffffffffff", - "forkCanonHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "gasLimitBoundDivisor": "0x20", - "unknownField": "0x0" - }, - "genesis": { - "seal": { - "ethereum": { - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "nonce": "0x00006d6f7264656e" - } - }, - "difficulty": "0x20000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x", - "gasLimit": "0x2fefd8" - }, - "nodes": [ - "enode://b1217cbaa440e35ed471157123fe468e19e8b5ad5bedb4b1fdbcbdab6fb2f5ed3e95dd9c24a22a79fdb2352204cea207df27d92bfd21bfd41545e8b16f637499@104.44.138.37:30303" - ], - "accounts": { - "0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, - "0000000000000000000000000000000000000002": { "balance": "1", "nonce": "1048576", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, - "0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, - "0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, - "102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" } - }, - "hardcodedSync": { - "header": "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23", - "totalDifficulty": "0x400000000", - "CHTs": [ - "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", - "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544" - ] - } - }"#; - let result: Result = serde_json::from_str(s); - assert!(result.is_err()); - } - - #[test] - fn spec_deserialization() { - let s = r#"{ - "name": "Null Morden", - "dataDir": "morden", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x020000", - "difficultyBoundDivisor": "0x0800", - "durationLimit": "0x0d", - "homesteadTransition" : "0x", - "daoHardforkTransition": "0xffffffffffffffff", - "daoHardforkBeneficiary": "0x0000000000000000000000000000000000000000", - "daoHardforkAccounts": [] - } - } - }, - "params": { - "accountStartNonce": "0x0100000", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x2", - "forkBlock": "0xffffffffffffffff", - "forkCanonHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "gasLimitBoundDivisor": "0x20" - }, - "genesis": { - "seal": { - "ethereum": { - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "nonce": "0x00006d6f7264656e" - } - }, - "difficulty": "0x20000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x", - "gasLimit": "0x2fefd8" - }, - "nodes": [ - "enode://b1217cbaa440e35ed471157123fe468e19e8b5ad5bedb4b1fdbcbdab6fb2f5ed3e95dd9c24a22a79fdb2352204cea207df27d92bfd21bfd41545e8b16f637499@104.44.138.37:30303" - ], - "accounts": { - "0000000000000000000000000000000000000001": { - "balance": "1", - "nonce": "1048576", - "builtin": { - "name": "ecrecover", - "pricing": { - "linear": { - "base": 3000, - "word": 0 - } - } - } - }, - "0000000000000000000000000000000000000002": { - "balance": "1", - "nonce": "1048576", - "builtin": { - "name": "sha256", - "pricing": { - "linear": { - "base": 60, - "word": 12 - } - } - } - }, - "0000000000000000000000000000000000000003": { - "balance": "1", - "nonce": "1048576", - "builtin": { - "name": "ripemd160", - "pricing": { - "linear": { - "base": 600, - "word": 120 - } - } - } - }, - "0000000000000000000000000000000000000004": { - "balance": "1", - "nonce": "1048576", - "builtin": { - "name": "identity", - "pricing": { - "linear": { - "base": 15, - "word": 3 - } - } - } - }, - "102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" } - }, - "hardcodedSync": { - "header": "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23", - "totalDifficulty": "0x400000000", - "CHTs": [ - "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", - "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544" - ] - } - }"#; - let _deserialized: Spec = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } -} diff --git a/evm-tests/ethjson/src/spec/state.rs b/evm-tests/ethjson/src/spec/state.rs deleted file mode 100644 index 7e14c31d..00000000 --- a/evm-tests/ethjson/src/spec/state.rs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Blockchain state deserializer. - -use crate::{ - bytes::Bytes, - hash::{Address, H256}, - spec::{Account, Builtin}, -}; -use serde::Deserialize; -use std::collections::BTreeMap; - -/// Recent JSON tests can be either a map or a hash (represented by a string). -/// See https://github.com/ethereum/tests/issues/637 -#[cfg_attr(any(test, feature = "test-helpers"), derive(Clone))] -#[derive(Debug, PartialEq, Deserialize)] -#[serde(untagged)] -pub enum HashOrMap { - /// When the `postState` is large, tests sometimes just include the state root of the last - /// successful block here. - Hash(H256), - /// The expected `postState` of a test - Map(BTreeMap), -} - -/// Blockchain state deserializer. -#[cfg_attr(any(test, feature = "test-helpers"), derive(Clone))] -#[derive(Debug, PartialEq, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct State(pub HashOrMap); - -impl State { - /// Returns all builtins. - pub fn builtins(&self) -> BTreeMap { - match &self.0 { - HashOrMap::Hash(_) => BTreeMap::default(), - HashOrMap::Map(map) => map - .iter() - .filter_map(|(add, acc)| acc.builtin.clone().map(|b| (*add, b.into()))) - .collect(), - } - } - - /// Returns all constructors. - pub fn constructors(&self) -> BTreeMap { - match &self.0 { - HashOrMap::Hash(_) => BTreeMap::default(), - HashOrMap::Map(map) => map - .iter() - .filter_map(|(add, acc)| acc.constructor.clone().map(|b| (*add, b))) - .collect(), - } - } -} - -impl IntoIterator for State { - type Item = as IntoIterator>::Item; - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - if let HashOrMap::Map(m) = self.0 { - m.into_iter() - } else { - BTreeMap::default().into_iter() - } - } -} diff --git a/evm-tests/ethjson/src/spec/step_duration.rs b/evm-tests/ethjson/src/spec/step_duration.rs deleted file mode 100644 index 3827ea6f..00000000 --- a/evm-tests/ethjson/src/spec/step_duration.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Step duration configuration parameter - -use std::collections::BTreeMap; - -use serde::Deserialize; - -use crate::uint::Uint; - -/// Step duration can be specified either as a `Uint` (in seconds), in which case it will be -/// constant, or as a list of pairs consisting of a timestamp of type `Uint` and a duration, in -/// which case the duration of a step will be determined by a mapping arising from that list. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(deny_unknown_fields)] -#[serde(untagged)] -pub enum StepDuration { - /// Duration of all steps. - Single(Uint), - /// Step duration transitions: a mapping of timestamp to step durations. - Transitions(BTreeMap), -} diff --git a/evm-tests/ethjson/src/spec/validator_set.rs b/evm-tests/ethjson/src/spec/validator_set.rs deleted file mode 100644 index e2823ef0..00000000 --- a/evm-tests/ethjson/src/spec/validator_set.rs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Validator set deserialization. - -use crate::{hash::Address, uint::Uint}; -use serde::Deserialize; -use std::collections::BTreeMap; - -/// Different ways of specifying validators. -#[derive(Debug, PartialEq, Deserialize)] -#[serde(deny_unknown_fields)] -#[serde(rename_all = "camelCase")] -pub enum ValidatorSet { - /// A simple list of authorities. - List(Vec
), - /// Address of a contract that indicates the list of authorities. - SafeContract(Address), - /// Address of a contract that indicates the list of authorities and enables reporting of theor misbehaviour using transactions. - Contract(Address), - /// A map of starting blocks for each validator set. - Multi(BTreeMap), -} - -#[cfg(test)] -mod tests { - use super::{Address, Uint, ValidatorSet}; - use ethereum_types::{H160, U256}; - use std::str::FromStr; - - #[test] - fn validator_set_deserialization() { - let s = r#"[{ - "list": ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"] - }, { - "safeContract": "0xc6d9d2cd449a754c494264e1809c50e34d64562b" - }, { - "contract": "0xc6d9d2cd449a754c494264e1809c50e34d64562b" - }, { - "multi": { - "0": { "list": ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"] }, - "10": { "list": ["0xd6d9d2cd449a754c494264e1809c50e34d64562b"] }, - "20": { "contract": "0xc6d9d2cd449a754c494264e1809c50e34d64562b" } - } - }]"#; - - let deserialized: Vec = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized.len(), 4); - - assert_eq!( - deserialized[0], - ValidatorSet::List(vec![Address( - H160::from_str("c6d9d2cd449a754c494264e1809c50e34d64562b").unwrap() - )]) - ); - assert_eq!( - deserialized[1], - ValidatorSet::SafeContract(Address( - H160::from_str("c6d9d2cd449a754c494264e1809c50e34d64562b").unwrap() - )) - ); - assert_eq!( - deserialized[2], - ValidatorSet::Contract(Address( - H160::from_str("c6d9d2cd449a754c494264e1809c50e34d64562b").unwrap() - )) - ); - match deserialized[3] { - ValidatorSet::Multi(ref map) => { - assert_eq!(map.len(), 3); - assert!(map.contains_key(&Uint(U256::from(0)))); - assert!(map.contains_key(&Uint(U256::from(10)))); - assert!(map.contains_key(&Uint(U256::from(20)))); - } - _ => panic!(), - } - } -} diff --git a/evm-tests/ethjson/src/state.rs b/evm-tests/ethjson/src/state.rs deleted file mode 100644 index bd75cf8f..00000000 --- a/evm-tests/ethjson/src/state.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! State deserialization types - -use crate::{ - bytes::Bytes, - hash::{Address, Bloom, H256}, -}; -use serde::Deserialize; - -/// State log deserialization. -#[derive(Debug, PartialEq, Eq, Deserialize)] -pub struct Log { - /// Address. - pub address: Address, - /// Topics. - pub topics: Vec, - /// Data. - pub data: Bytes, - /// Bloom. - pub bloom: Bloom, -} - -#[cfg(test)] -mod tests { - use super::Log; - - #[test] - fn log_deserialization() { - let s = r#"{ - "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", - "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008800000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000", - "data" : "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "topics" : [ - "0000000000000000000000000000000000000000000000000000000000000000" - ] - }"#; - let _deserialized: Log = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } -} diff --git a/evm-tests/ethjson/src/test_helpers/blockchain/block.rs b/evm-tests/ethjson/src/test_helpers/blockchain/block.rs deleted file mode 100644 index e45f08d8..00000000 --- a/evm-tests/ethjson/src/test_helpers/blockchain/block.rs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Blockchain test block deserializer. - -use super::header::Header; -use crate::{bytes::Bytes, transaction::Transaction}; -use serde::Deserialize; - -/// Blockchain test block deserializer. -#[derive(Debug, PartialEq, Eq, Deserialize)] -pub struct Block { - #[serde(rename = "blockHeader")] - header: Option
, - rlp: Bytes, - transactions: Option>, - #[serde(rename = "uncleHeaders")] - uncles: Option>, -} - -impl Block { - /// Returns block rlp. - pub fn rlp(&self) -> Vec { - self.rlp.clone().into() - } -} - -#[cfg(test)] -mod tests { - use super::Block; - - #[test] - fn block_deserialization() { - let s = r#"{ - "blockHeader" : { - "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1", - "difficulty" : "0x020000", - "extraData" : "0x", - "gasLimit" : "0x2fefba", - "gasUsed" : "0x00", - "hash" : "65ebf1b97fb89b14680267e0723d69267ec4bf9a96d4a60ffcb356ae0e81c18f", - "mixHash" : "13735ab4156c9b36327224d92e1692fab8fc362f8e0f868c94d421848ef7cd06", - "nonce" : "931dcc53e5edc514", - "number" : "0x01", - "parentHash" : "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae", - "receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "stateRoot" : "c5c83ff43741f573a0c9b31d0e56fdd745f4e37d193c4e78544f302777aafcf3", - "timestamp" : "0x56850b7b", - "transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" - }, - "blocknumber" : "1", - "rlp" : "0xf901fcf901f7a05a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5aea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0c5c83ff43741f573a0c9b31d0e56fdd745f4e37d193c4e78544f302777aafcf3a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefba808456850b7b80a013735ab4156c9b36327224d92e1692fab8fc362f8e0f868c94d421848ef7cd0688931dcc53e5edc514c0c0", - "transaction" : [], - "uncleHeaders" : [] - }"#; - let _deserialized: Block = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } -} diff --git a/evm-tests/ethjson/src/test_helpers/blockchain/header.rs b/evm-tests/ethjson/src/test_helpers/blockchain/header.rs deleted file mode 100644 index 86b5dfaa..00000000 --- a/evm-tests/ethjson/src/test_helpers/blockchain/header.rs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Blockchain test header deserializer. - -use crate::{ - bytes::Bytes, - hash::{Address, Bloom, H256, H64}, - uint::Uint, -}; -use serde::Deserialize; - -/// Blockchain test header deserializer. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Header { - /// Blocks bloom. - pub bloom: Bloom, - /// Blocks author. - #[serde(rename = "coinbase")] - pub author: Address, - /// Difficulty. - pub difficulty: Uint, - /// Extra data. - pub extra_data: Bytes, - /// Gas limit. - pub gas_limit: Uint, - /// Gas used. - pub gas_used: Uint, - /// Hash. - pub hash: H256, - /// Mix hash. - pub mix_hash: H256, - /// Seal nonce. - pub nonce: H64, - /// Block number. - pub number: Uint, - /// Parent hash. - pub parent_hash: H256, - /// Receipt root. - #[serde(rename = "receiptTrie")] - pub receipts_root: H256, - /// State root. - pub state_root: H256, - /// Timestamp. - pub timestamp: Uint, - /// Transactions root. - #[serde(rename = "transactionsTrie")] - pub transactions_root: H256, - /// Uncles hash. - #[serde(rename = "uncleHash")] - pub uncles_hash: H256, -} - -#[cfg(test)] -mod tests { - use super::Header; - - #[test] - fn header_deserialization() { - let s = r#"{ - "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1", - "difficulty" : "0x020000", - "extraData" : "0x", - "gasLimit" : "0x2fefba", - "gasUsed" : "0x00", - "hash" : "65ebf1b97fb89b14680267e0723d69267ec4bf9a96d4a60ffcb356ae0e81c18f", - "mixHash" : "13735ab4156c9b36327224d92e1692fab8fc362f8e0f868c94d421848ef7cd06", - "nonce" : "931dcc53e5edc514", - "number" : "0x01", - "parentHash" : "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae", - "receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "stateRoot" : "c5c83ff43741f573a0c9b31d0e56fdd745f4e37d193c4e78544f302777aafcf3", - "timestamp" : "0x56850b7b", - "transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" - }"#; - let _deserialized: Header = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } -} diff --git a/evm-tests/ethjson/src/test_helpers/blockchain/mod.rs b/evm-tests/ethjson/src/test_helpers/blockchain/mod.rs deleted file mode 100644 index 85a93c82..00000000 --- a/evm-tests/ethjson/src/test_helpers/blockchain/mod.rs +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Blockchain test deserialization. - -use crate::{ - bytes::Bytes, - hash::H256, - spec::{Ethereum, ForkSpec, Genesis, Seal, State}, -}; -use serde::Deserialize; - -pub mod block; -pub mod header; - -pub use self::block::Block; -pub use self::header::Header; - -/// Type for running `Blockchain` tests -pub type Test = super::tester::GenericTester; - -/// Json Block test possible engine kind. -#[derive(Debug, Default, PartialEq, Eq, Deserialize)] -pub enum Engine { - /// Default (old) behaviour. - #[default] - Ethash, - /// No check of block's difficulty and nonce for tests. - NoProof, -} - -/// Blockchain deserialization. -#[derive(Debug, PartialEq, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct BlockChain { - /// Genesis block header. - #[serde(rename = "genesisBlockHeader")] - pub genesis_block: Header, - /// Genesis block rlp. - #[serde(rename = "genesisRLP")] - pub genesis_rlp: Option, - /// Blocks. - pub blocks: Vec, - /// Post state. - pub post_state: Option, - /// Pre state. - #[serde(rename = "pre")] - pub pre_state: State, - /// Hash of best block. - #[serde(rename = "lastblockhash")] - pub best_block: H256, - /// Network. - pub network: ForkSpec, - #[serde(default)] - #[serde(rename = "sealEngine")] - /// Engine - pub engine: Engine, -} - -impl BlockChain { - /// Returns blocks rlp. - pub fn blocks_rlp(&self) -> Vec> { - self.blocks.iter().map(|block| block.rlp()).collect() - } - - /// Returns spec compatible genesis struct. - pub fn genesis(&self) -> Genesis { - Genesis { - seal: Seal::Ethereum(Ethereum { - nonce: self.genesis_block.nonce, - mix_hash: self.genesis_block.mix_hash, - }), - difficulty: self.genesis_block.difficulty, - author: Some(self.genesis_block.author), - timestamp: Some(self.genesis_block.timestamp), - parent_hash: Some(self.genesis_block.parent_hash), - gas_limit: self.genesis_block.gas_limit, - transactions_root: Some(self.genesis_block.transactions_root), - receipts_root: Some(self.genesis_block.receipts_root), - state_root: Some(self.genesis_block.state_root), - gas_used: Some(self.genesis_block.gas_used), - extra_data: Some(self.genesis_block.extra_data.clone()), - } - } -} - -#[cfg(test)] -mod tests { - use super::BlockChain; - - #[test] - fn blockchain_deserialization() { - let s = r#"{ - "blocks" : [{ - "blockHeader" : { - "bloom" : "00000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000040000000000000000000000000000000000000000000000000000000", - "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1", - "difficulty" : "0x020000", - "extraData" : "0x0102030405060708091011121314151617181920212223242526272829303132", - "gasLimit" : "0x2fefba", - "gasUsed" : "0x560b", - "hash" : "06b5b1742bde29468510c92641f36b719c61b3fc3e9a21c92a23978f4f7faa2a", - "mixHash" : "5266ca43e81d25925a9ba573c3e4f9180bc076d316d90e63c6f8708b272f5ce2", - "nonce" : "59ba4daed1898e21", - "number" : "0x01", - "parentHash" : "f052d217bd5275a5177a3c3b7debdfe2670f1c8394b2965ccd5c1883cc1a524d", - "receiptTrie" : "c7778a7376099ee2e5c455791c1885b5c361b95713fddcbe32d97fd01334d296", - "stateRoot" : "bac6177a79e910c98d86ec31a09ae37ac2de15b754fd7bed1ba52362c49416bf", - "timestamp" : "0x56850c2c", - "transactionsTrie" : "498785da562aa0c5dd5937cf15f22139b0b1bcf3b4fc48986e1bb1dae9292796", - "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" - }, - "rlp" : "0xf90285f90219a0f052d217bd5275a5177a3c3b7debdfe2670f1c8394b2965ccd5c1883cc1a524da01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0bac6177a79e910c98d86ec31a09ae37ac2de15b754fd7bed1ba52362c49416bfa0498785da562aa0c5dd5937cf15f22139b0b1bcf3b4fc48986e1bb1dae9292796a0c7778a7376099ee2e5c455791c1885b5c361b95713fddcbe32d97fd01334d296b90100000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000400000000000000000000000000000000000000000000000000000008302000001832fefba82560b8456850c2ca00102030405060708091011121314151617181920212223242526272829303132a05266ca43e81d25925a9ba573c3e4f9180bc076d316d90e63c6f8708b272f5ce28859ba4daed1898e21f866f864800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d8785012a05f200801ca0ee0b9ec878fbd4258a9473199d8ecc32996a20c323c004e79e0cda20e0418ce3a04e6bc63927d1510bab54f37e46fa036faf4b2c465d271920d9afea1fadf7bd21c0", - "transactions" : [ - { - "data" : "0x00", - "gasLimit" : "0xc350", - "gasPrice" : "0x0a", - "nonce" : "0x00", - "r" : "0xee0b9ec878fbd4258a9473199d8ecc32996a20c323c004e79e0cda20e0418ce3", - "s" : "0x4e6bc63927d1510bab54f37e46fa036faf4b2c465d271920d9afea1fadf7bd21", - "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", - "v" : "0x1c", - "value" : "0x012a05f200" - } - ], - "uncleHeaders" : [ - ] - }], - "network" : "Frontier", - "genesisBlockHeader" : { - "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1", - "difficulty" : "0x020000", - "extraData" : "0x42", - "gasLimit" : "0x2fefd8", - "gasUsed" : "0x00", - "hash" : "f052d217bd5275a5177a3c3b7debdfe2670f1c8394b2965ccd5c1883cc1a524d", - "mixHash" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "nonce" : "0102030405060708", - "number" : "0x00", - "parentHash" : "0000000000000000000000000000000000000000000000000000000000000000", - "receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "stateRoot" : "925002c3260b44e44c3edebad1cc442142b03020209df1ab8bb86752edbd2cd7", - "timestamp" : "0x54c98c81", - "transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" - }, - "genesisRLP" : "0xf901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0925002c3260b44e44c3edebad1cc442142b03020209df1ab8bb86752edbd2cd7a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080832fefd8808454c98c8142a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421880102030405060708c0c0", - "lastblockhash" : "06b5b1742bde29468510c92641f36b719c61b3fc3e9a21c92a23978f4f7faa2a", - "postState" : { - "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { - "balance" : "0x012a05f264", - "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600052600060206000a1", - "nonce" : "0x00", - "storage" : { - } - }, - "8888f1f195afa192cfee860698584c030f4c9db1" : { - "balance" : "0x4563918244f75c6e", - "code" : "0x", - "nonce" : "0x00", - "storage" : { - } - }, - "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "0x012a029592", - "code" : "0x", - "nonce" : "0x01", - "storage" : { - } - } - }, - "pre" : { - "095e7baea6a6c7c4c2dfeb977efac326af552d87" : { - "balance" : "0x64", - "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600052600060206000a1", - "nonce" : "0x00", - "storage" : { - } - }, - "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "0x02540be400", - "code" : "0x", - "nonce" : "0x00", - "storage" : { - } - } - } - }"#; - let _deserialized: BlockChain = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } -} diff --git a/evm-tests/ethjson/src/test_helpers/difficulty.rs b/evm-tests/ethjson/src/test_helpers/difficulty.rs deleted file mode 100644 index b8fff0ac..00000000 --- a/evm-tests/ethjson/src/test_helpers/difficulty.rs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -use crate::{hash::H256, uint::Uint}; -use serde::Deserialize; - -/// Blockchain test header deserializer. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct DifficultyTestCase { - /// Parent timestamp. - pub parent_timestamp: Uint, - /// Parent difficulty. - pub parent_difficulty: Uint, - /// Parent uncle hash. - pub parent_uncles: H256, - /// Current timestamp. - pub current_timestamp: Uint, - /// Current difficulty. - pub current_difficulty: Uint, - /// Current block number. - pub current_block_number: Uint, -} - -/// Type for running `Difficulty` tests -pub type DifficultyTest = super::tester::GenericTester; diff --git a/evm-tests/ethjson/src/test_helpers/mod.rs b/evm-tests/ethjson/src/test_helpers/mod.rs deleted file mode 100644 index f25a7e87..00000000 --- a/evm-tests/ethjson/src/test_helpers/mod.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Test structures for JSON deserialization. - -/// Blockchain test helpers -pub mod blockchain; -/// Difficulty test helpers -pub mod difficulty; -/// Tests to skip helpers -pub mod skip; -/// State test helpers -pub mod state; -/// Test primitives -pub mod tester; -/// Transaction test helpers -pub mod transaction; -/// Trie test helpers -pub mod trie; -/// Vm test helpers -pub mod vm { - /// Type for running `vm` tests - pub type Test = super::tester::GenericTester; -} diff --git a/evm-tests/ethjson/src/test_helpers/skip.rs b/evm-tests/ethjson/src/test_helpers/skip.rs deleted file mode 100644 index 1090e7c7..00000000 --- a/evm-tests/ethjson/src/test_helpers/skip.rs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -use serde::Deserialize; -use std::collections::BTreeMap; - -/// Test to skip (only if issue ongoing) -#[derive(Debug, PartialEq, Eq, Deserialize)] -pub struct SkipTests { - /// Block tests - pub block: Vec, - /// State tests - pub state: Vec, - /// Legacy block tests - pub legacy_block: Vec, - /// Legacy state tests - pub legacy_state: Vec, -} - -/// Block test to skip. -#[derive(Debug, PartialEq, Eq, Deserialize)] -pub struct SkipBlockchainTest { - /// Issue reference. - pub reference: String, - /// Test failing name. - pub failing: String, - /// Items failing for the test. - pub subtests: Vec, -} - -/// State test to skip. -#[derive(Debug, PartialEq, Eq, Deserialize)] -pub struct SkipStateTest { - /// Issue reference. - pub reference: String, - /// Test failing name. - pub failing: String, - /// Items failing for the test. - pub subtests: BTreeMap, -} - -/// State subtest to skip. -#[derive(Debug, PartialEq, Eq, Deserialize)] -pub struct StateSkipSubStates { - /// State test number of this item. Or '*' for all state. - pub subnumbers: Vec, - /// Chain for this items. - pub chain: String, -} - -impl SkipTests { - /// Empty skip states. - pub const fn empty() -> Self { - Self { - block: Vec::new(), - state: Vec::new(), - legacy_block: Vec::new(), - legacy_state: Vec::new(), - } - } - - /// Loads test from json. - pub fn load(reader: R) -> Result - where - R: std::io::Read, - { - serde_json::from_reader(reader) - } -} diff --git a/evm-tests/ethjson/src/test_helpers/state.rs b/evm-tests/ethjson/src/test_helpers/state.rs deleted file mode 100644 index 246dd411..00000000 --- a/evm-tests/ethjson/src/test_helpers/state.rs +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! State test deserialization. - -/// Type for running `State` tests -pub type Test = super::tester::GenericTester; - -use crate::{ - bytes::Bytes, - hash::{Address, H256}, - maybe::MaybeEmpty, - spec::{ForkSpec, State as AccountState}, - transaction::Transaction, - uint::Uint, - vm::Env, -}; -use ethereum_types::U256; -use serde::Deserialize; -use std::collections::BTreeMap; - -/// State test deserialization. -#[derive(Debug, PartialEq, Deserialize)] -pub struct State { - /// Environment. - pub env: Env, - /// Pre state. - #[serde(rename = "pre")] - pub pre_state: AccountState, - /// Post state. - #[serde(rename = "post")] - pub post_states: BTreeMap>, - /// Transaction. - pub transaction: MultiTransaction, -} - -/// State test transaction deserialization. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct MultiTransaction { - /// Transaction data set. - pub data: Vec, - /// Access lists (see EIP-2930) - #[serde(default)] - pub access_lists: Vec>, - /// Gas limit set. - pub gas_limit: Vec, - /// Gas price. - pub gas_price: Option, - /// for details on `maxFeePerGas` see EIP-1559 - pub max_fee_per_gas: Option, - /// for details on `maxPriorityFeePerGas` see EIP-1559 - pub max_priority_fee_per_gas: Option, - /// Nonce. - pub nonce: Uint, - /// Secret key. - #[serde(rename = "secretKey")] - pub secret: Option, - /// To. - pub to: MaybeEmpty
, - /// Value set. - pub value: Vec, - - /// EIP-4844 - #[serde(default)] - pub blob_versioned_hashes: Vec, - /// EIP-4844 - pub max_fee_per_blob_gas: Option, - - /// EIP-7702 - #[serde(default)] - pub authorization_list: AuthorizationList, -} - -impl MultiTransaction { - /// Build transaction with given indexes. - pub fn select(&self, indexes: &PostStateIndexes) -> Transaction { - let data_index = indexes.data as usize; - let access_list = if data_index < self.access_lists.len() { - self.access_lists - .get(data_index) - .unwrap() - .as_ref() - .cloned() - .unwrap_or_default() - .into_iter() - .map(|a| (a.address, a.storage_keys)) - .collect() - } else { - Vec::new() - }; - - Transaction { - data: self.data[data_index].clone(), - gas_limit: self.gas_limit[indexes.gas as usize], - to: self.to.clone(), - value: self.value[indexes.value as usize], - r: Default::default(), - s: Default::default(), - v: Default::default(), - secret: self.secret, - access_list, - authorization_list: self.authorization_list.clone(), - } - } -} - -/// Type alias for access lists (see EIP-2930) -pub type AccessList = Vec; - -/// Access list tuple (see https://eips.ethereum.org/EIPS/eip-2930). -/// Example test spec: https://github.com/ethereum/tests/blob/5490db3ff58d371c0c74826280256ba016b0bd5c/GeneralStateTests/stExample/accessListExample.json -#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct AccessListTuple { - /// Address to access - pub address: Address, - /// Keys (slots) to access at that address - pub storage_keys: Vec, -} - -/// EIP-7702 Authorization List -pub type AuthorizationList = Vec; -/// EIP-7702 Authorization item -#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct AuthorizationItem { - /// Chain ID - pub chain_id: Uint, - /// Address to access - pub address: Address, - /// Keys (slots) to access at that address - pub nonce: Uint, - /// r signature - pub r: Uint, - /// s signature - pub s: Uint, - /// Parity - pub v: Uint, - /// Signer address - pub signer: Option
, -} - -/// State test indexes deserialization. -#[derive(Debug, PartialEq, Eq, Deserialize)] -pub struct PostStateIndexes { - /// Index into transaction data set. - pub data: u64, - /// Index into transaction gas limit set. - pub gas: u64, - /// Index into transaction value set. - pub value: u64, -} - -/// State test indexed state result deserialization. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct PostStateResult { - /// Post state hash - pub hash: H256, - /// Indexes - pub indexes: PostStateIndexes, - /// Expected error if the test is meant to fail - pub expect_exception: Option, - /// Transaction bytes - pub txbytes: Bytes, -} - -#[cfg(test)] -mod tests { - use super::{MultiTransaction, State}; - - #[test] - fn multi_transaction_deserialization() { - let s = r#"{ - "data": [ "" ], - "gasLimit": [ "0x2dc6c0", "0x222222" ], - "gasPrice": "0x01", - "nonce": "0x00", - "secretKey": "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", - "to": "1000000000000000000000000000000000000000", - "value": [ "0x00", "0x01", "0x02" ] - }"#; - let _deserialized: MultiTransaction = serde_json::from_str(s).unwrap(); - } - - #[test] - fn state_deserialization() { - let s = r#"{ - "env": { - "currentCoinbase": "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", - "currentDifficulty": "0x0100", - "currentGasLimit": "0x01c9c380", - "currentNumber": "0x00", - "currentTimestamp": "0x01", - "previousHash": "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" - }, - "post": { - "EIP150": [ - { - "hash": "3e6dacc1575c6a8c76422255eca03529bbf4c0dda75dfc110b22d6dc4152396f", - "txbytes" : "0xf861800a84042c1d8094b94f5374fce5edbc8e2a8697c15331677e6ebf0b80801ca0f141d67812db948c9a4ea43c27d695248205c121ae8d924d23517ab09e38f369a03fe3cfedb4c9a7e61340b6fec87917690e92082f752ad820ad5006c7d49185ed", - "indexes": { "data": 0, "gas": 0, "value": 0 } - }, - { - "hash": "99a450d8ce5b987a71346d8a0a1203711f770745c7ef326912e46761f14cd764", - "txbytes" : "0xf861800a84042c1d8094b94f5374fce5edbc8e2a8697c15331677e6ebf0b80801ca0f141d67812db948c9a4ea43c27d695248205c121ae8d924d23517ab09e38f369a03fe3cfedb4c9a7e61340b6fec87917690e92082f752ad820ad5006c7d49185ed", - "indexes": { "data": 0, "gas": 0, "value": 1 } - } - ], - "EIP158": [ - { - "hash": "3e6dacc1575c6a8c76422255eca03529bbf4c0dda75dfc110b22d6dc4152396f", - "txbytes" : "0xf861800a84042c1d8094b94f5374fce5edbc8e2a8697c15331677e6ebf0b80801ca0f141d67812db948c9a4ea43c27d695248205c121ae8d924d23517ab09e38f369a03fe3cfedb4c9a7e61340b6fec87917690e92082f752ad820ad5006c7d49185ed", - "indexes": { "data": 0, "gas": 0, "value": 0 } - }, - { - "hash": "99a450d8ce5b987a71346d8a0a1203711f770745c7ef326912e46761f14cd764", - "txbytes" : "0xf861800a84042c1d8094b94f5374fce5edbc8e2a8697c15331677e6ebf0b80801ca0f141d67812db948c9a4ea43c27d695248205c121ae8d924d23517ab09e38f369a03fe3cfedb4c9a7e61340b6fec87917690e92082f752ad820ad5006c7d49185ed", - "indexes": { "data": 0, "gas": 0, "value": 1 } - } - ] - }, - "pre": { - "1000000000000000000000000000000000000000": { - "balance": "0x0de0b6b3a7640000", - "code": "0x6040600060406000600173100000000000000000000000000000000000000162055730f1600055", - "nonce": "0x00", - "storage": { - } - }, - "1000000000000000000000000000000000000001": { - "balance": "0x0de0b6b3a7640000", - "code": "0x604060006040600060027310000000000000000000000000000000000000026203d090f1600155", - "nonce": "0x00", - "storage": { - } - }, - "1000000000000000000000000000000000000002": { - "balance": "0x00", - "code": "0x600160025533600455346007553060e6553260e8553660ec553860ee553a60f055", - "nonce": "0x00", - "storage": { - } - }, - "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { - "balance": "0x0de0b6b3a7640000", - "code": "0x", - "nonce": "0x00", - "storage": { - } - } - }, - "transaction": { - "data": [ "" ], - "gasLimit": [ "285000", "100000", "6000" ], - "gasPrice": "0x01", - "nonce": "0x00", - "secretKey": "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", - "to": "095e7baea6a6c7c4c2dfeb977efac326af552d87", - "value": [ "10", "0" ] - } - }"#; - let _deserialized: State = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } -} diff --git a/evm-tests/ethjson/src/test_helpers/tester.rs b/evm-tests/ethjson/src/test_helpers/tester.rs deleted file mode 100644 index a59d7f32..00000000 --- a/evm-tests/ethjson/src/test_helpers/tester.rs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -use serde::de::DeserializeOwned; -use serde::Deserialize; -use std::collections::BTreeMap; - -/// A genric wrapper over a `BTreeMap` for tests -#[derive(Deserialize)] -pub struct GenericTester(BTreeMap); - -impl IntoIterator for GenericTester { - type Item = as IntoIterator>::Item; - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl GenericTester -where - T: DeserializeOwned + Ord, - U: DeserializeOwned, -{ - /// Loads test from json. - pub fn load(reader: R) -> Result - where - R: std::io::Read, - { - serde_json::from_reader(reader) - } -} diff --git a/evm-tests/ethjson/src/test_helpers/transaction.rs b/evm-tests/ethjson/src/test_helpers/transaction.rs deleted file mode 100644 index 115819f0..00000000 --- a/evm-tests/ethjson/src/test_helpers/transaction.rs +++ /dev/null @@ -1,92 +0,0 @@ -#![allow(clippy::doc_markdown, clippy::module_name_repetitions)] -#![allow(clippy::pub_underscore_fields)] -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Transaction test deserialization. - -use crate::{ - bytes::Bytes, - hash::{Address, H256}, - spec::ForkSpec, -}; -use serde::Deserialize; -use std::collections::BTreeMap; - -/// Type for running `Transaction` tests -pub type Test = super::tester::GenericTester; - -/// Transaction test deserialization. -#[derive(Debug, Deserialize)] -pub struct TransactionTest { - /// RLP of the transaction - pub rlp: Bytes, - #[allow(missing_docs)] - pub _info: serde::de::IgnoredAny, - /// State of the transaction after the test runs - #[serde(flatten)] - pub post_state: BTreeMap, -} - -/// TransactionTest post state. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct PostState { - /// Transaction sender. - pub sender: Option
, - /// Transaction hash. - pub hash: Option, -} - -#[cfg(test)] -mod tests { - use super::TransactionTest; - - #[test] - fn transaction_deserialization() { - let s = r#"{ - "Byzantium" : { - "hash" : "4782cb5edcaeda1f0aef204b161214f124cefade9e146245183abbb9ca01bca5", - "sender" : "2ea991808ba979ba103147edfd72304ebd95c028" - }, - "Constantinople" : { - "hash" : "4782cb5edcaeda1f0aef204b161214f124cefade9e146245183abbb9ca01bca5", - "sender" : "2ea991808ba979ba103147edfd72304ebd95c028" - }, - "EIP150" : { - }, - "EIP158" : { - "hash" : "4782cb5edcaeda1f0aef204b161214f124cefade9e146245183abbb9ca01bca5", - "sender" : "2ea991808ba979ba103147edfd72304ebd95c028" - }, - "Frontier" : { - }, - "Homestead" : { - }, - "_info" : { - "comment" : "", - "filledwith" : "cpp-1.3.0+commit.1829957d.Linux.g++", - "lllcversion" : "Version: 0.4.18-develop.2017.10.11+commit.81f9f86c.Linux.g++", - "source" : "src/TransactionTestsFiller/ttVValue/V_equals37Filler.json", - "sourceHash" : "89ef69312d4c0b4e3742da501263d23d2a64f180258ac93940997ac6a17b9b19" - }, - "rlp" : "0xf865808698852840a46f82d6d894095e7baea6a6c7c4c2dfeb977efac326af552d87808025a098ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4aa01887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3" - }"#; - - let _deserialized: TransactionTest = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } -} diff --git a/evm-tests/ethjson/src/test_helpers/trie/input.rs b/evm-tests/ethjson/src/test_helpers/trie/input.rs deleted file mode 100644 index ef7e0e33..00000000 --- a/evm-tests/ethjson/src/test_helpers/trie/input.rs +++ /dev/null @@ -1,177 +0,0 @@ -#![allow(clippy::manual_let_else)] -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Trie test input deserialization. - -use crate::bytes::Bytes; -use serde::de::{Error as ErrorTrait, MapAccess, SeqAccess, Visitor}; -use serde::{Deserialize, Deserializer}; -use std::collections::BTreeMap; -use std::fmt; -use std::str::FromStr; - -/// Trie test input. -#[derive(Debug, PartialEq, Eq)] -pub struct Input { - /// Input params. - pub data: BTreeMap>, -} - -impl<'a> Deserialize<'a> for Input { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'a>, - { - deserializer.deserialize_any(InputVisitor) - } -} - -struct InputVisitor; - -impl<'a> Visitor<'a> for InputVisitor { - type Value = Input; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a map of bytes into bytes") - } - - fn visit_map(self, mut visitor: V) -> Result - where - V: MapAccess<'a>, - { - let mut result = BTreeMap::new(); - - loop { - let key_str: Option = visitor.next_key()?; - let key = match key_str { - Some(ref k) if k.starts_with("0x") => { - Bytes::from_str(k).map_err(V::Error::custom)? - } - Some(k) => Bytes::new(k.into_bytes()), - None => { - break; - } - }; - - let val_str: Option = visitor.next_value()?; - let val = match val_str { - Some(ref v) if v.starts_with("0x") => { - Some(Bytes::from_str(v).map_err(V::Error::custom)?) - } - Some(v) => Some(Bytes::new(v.into_bytes())), - None => None, - }; - - result.insert(key, val); - } - - let input = Input { data: result }; - - Ok(input) - } - - fn visit_seq(self, mut visitor: V) -> Result - where - V: SeqAccess<'a>, - { - let mut result = BTreeMap::new(); - - loop { - let keyval: Option>> = visitor.next_element()?; - let keyval = match keyval { - Some(k) => k, - _ => { - break; - } - }; - - if keyval.len() != 2 { - return Err(V::Error::custom("Invalid key value pair.")); - } - - let key_str = &keyval[0]; - let val_str = &keyval[1]; - - let key = match *key_str { - Some(ref k) if k.starts_with("0x") => { - Bytes::from_str(k).map_err(V::Error::custom)? - } - Some(ref k) => Bytes::new(k.clone().into_bytes()), - None => { - break; - } - }; - - let val = match *val_str { - Some(ref v) if v.starts_with("0x") => { - Some(Bytes::from_str(v).map_err(V::Error::custom)?) - } - Some(ref v) => Some(Bytes::new(v.clone().into_bytes())), - None => None, - }; - - result.insert(key, val); - } - - let input = Input { data: result }; - - Ok(input) - } -} - -#[cfg(test)] -mod tests { - use super::{BTreeMap, Bytes, Input}; - - #[test] - fn input_deserialization_from_map() { - let s = r#"{ - "0x0045" : "0x0123456789", - "be" : "e", - "0x0a" : null - }"#; - - let input: Input = serde_json::from_str(s).unwrap(); - let mut map = BTreeMap::new(); - map.insert( - Bytes::new(vec![0, 0x45]), - Some(Bytes::new(vec![0x01, 0x23, 0x45, 0x67, 0x89])), - ); - map.insert(Bytes::new(vec![0x62, 0x65]), Some(Bytes::new(vec![0x65]))); - map.insert(Bytes::new(vec![0x0a]), None); - assert_eq!(input.data, map); - } - - #[test] - fn input_deserialization_from_array() { - let s = r#"[ - ["0x0045", "0x0123456789"], - ["be", "e"], - ["0x0a", null] - ]"#; - - let input: Input = serde_json::from_str(s).unwrap(); - let mut map = BTreeMap::new(); - map.insert( - Bytes::new(vec![0, 0x45]), - Some(Bytes::new(vec![0x01, 0x23, 0x45, 0x67, 0x89])), - ); - map.insert(Bytes::new(vec![0x62, 0x65]), Some(Bytes::new(vec![0x65]))); - map.insert(Bytes::new(vec![0x0a]), None); - assert_eq!(input.data, map); - } -} diff --git a/evm-tests/ethjson/src/test_helpers/trie/mod.rs b/evm-tests/ethjson/src/test_helpers/trie/mod.rs deleted file mode 100644 index 1c733fae..00000000 --- a/evm-tests/ethjson/src/test_helpers/trie/mod.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Trie test deserialization. - -mod input; - -pub use self::input::Input; - -/// Type used by `trie` tests -pub type Test = super::tester::GenericTester; - -use crate::hash::H256; -use serde::Deserialize; - -/// Trie test deserialization. -#[derive(Debug, Deserialize, Eq, PartialEq)] -pub struct Trie { - /// Trie test input. - #[serde(rename = "in")] - pub input: Input, - /// Trie root hash. - pub root: H256, -} diff --git a/evm-tests/ethjson/src/transaction.rs b/evm-tests/ethjson/src/transaction.rs deleted file mode 100644 index 902f3aaa..00000000 --- a/evm-tests/ethjson/src/transaction.rs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Transaction deserialization. - -use crate::test_helpers::state::AuthorizationList; -use crate::{ - bytes::Bytes, - hash::{Address, H256}, - maybe::MaybeEmpty, - uint::Uint, -}; -use serde::Deserialize; - -/// Unsigned transaction with signing information deserialization. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Transaction { - /// Transaction data. - pub data: Bytes, - /// Transaction access list (see EIP-2930). - #[serde(default)] - pub access_list: Vec<(Address, Vec)>, - /// Transaction authorization list (see EIP-7702`s). - #[serde(default)] - pub authorization_list: AuthorizationList, - /// Gas limit. - pub gas_limit: Uint, - /// To. - pub to: MaybeEmpty
, - /// Value. - pub value: Uint, - /// R. - #[serde(default)] - pub r: MaybeEmpty, - /// S. - #[serde(default)] - pub s: MaybeEmpty, - /// V. - #[serde(default)] - pub v: MaybeEmpty, - /// Secret - #[serde(rename = "secretKey")] - pub secret: Option, -} - -#[cfg(test)] -mod tests { - use super::{Bytes, MaybeEmpty, Transaction, Uint, H256}; - use ethereum_types::{H256 as Eth256, U256}; - - #[test] - fn transaction_deserialization() { - let s = r#"{ - "data" : "0x", - "gasLimit" : "0xf388", - "gasPrice" : "0x09184e72a000", - "nonce" : "0x00", - "to" : "", - "value" : "0x00", - "r": "0", - "s": "1", - "v": "2", - "secretKey": "0x0000000000000000000000000000000000000000000000000000000000000000" - }"#; - let tx: Transaction = serde_json::from_str(s).expect("JSON string is valid"); - assert_eq!(tx.data, Bytes::new(Vec::new())); - assert_eq!(tx.gas_limit, Uint(U256::from(0xf388))); - assert_eq!(tx.to, MaybeEmpty::None); - assert_eq!(tx.r, Uint(U256::zero()).into()); - assert_eq!(tx.s, Uint(U256::one()).into()); - assert_eq!(tx.v, Uint(U256::from(2)).into()); - assert_eq!(tx.secret, Some(H256(Eth256::zero()))); - } -} diff --git a/evm-tests/ethjson/src/uint.rs b/evm-tests/ethjson/src/uint.rs deleted file mode 100644 index cd833ae5..00000000 --- a/evm-tests/ethjson/src/uint.rs +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Lenient uint json deserialization for test json files. - -use ethereum_types::U256; -use serde::de::{Error, Unexpected, Visitor}; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::fmt; -use std::str::FromStr; - -/// Lenient uint json deserialization for test json files. -#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] -pub struct Uint(pub U256); - -impl From for U256 { - fn from(val: Uint) -> Self { - val.0 - } -} - -impl From for u64 { - fn from(val: Uint) -> Self { - val.0.low_u64() - } -} - -impl From for usize { - fn from(val: Uint) -> Self { - val.0.low_u64() as Self - } -} - -impl Serialize for Uint { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - self.0.to_string().serialize(serializer) - } -} - -impl<'a> Deserialize<'a> for Uint { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'a>, - { - deserializer.deserialize_any(UintVisitor) - } -} - -struct UintVisitor; - -impl Visitor<'_> for UintVisitor { - type Value = Uint; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a hex encoded or decimal uint") - } - - fn visit_u64(self, value: u64) -> Result - where - E: Error, - { - Ok(Uint(U256::from(value))) - } - - fn visit_str(self, value: &str) -> Result - where - E: Error, - { - let parse = |value: &str| { - if value.len() > 64 { - return Err(Error::custom( - format!( - "Invalid hex value 0x{value}: value too big (length={})", - value.len() - ) - .as_str(), - )); - } - U256::from_str(value) - .map_err(|e| Error::custom(format!("Invalid hex value 0x{value}: {e}").as_str())) - }; - - let value = match value.len() { - 0 => U256::from(0), - 2 if value.starts_with("0x") => U256::from(0), - _ if value.starts_with("0x:bigint 0x") => parse(&value[12..])?, - _ if value.starts_with("0x") => parse(&value[2..])?, - _ => U256::from_dec_str(value).map_err(|e| { - Error::custom(format!("Invalid decimal value {}: {:?}", value, e).as_str()) - })?, - }; - - Ok(Uint(value)) - } - - fn visit_string(self, value: String) -> Result - where - E: Error, - { - self.visit_str(value.as_ref()) - } -} - -/// Deserialize and validate that the value is non-zero -pub fn validate_non_zero<'de, D>(d: D) -> Result -where - D: Deserializer<'de>, -{ - let value = Uint::deserialize(d)?; - - if value == Uint(U256::from(0)) { - return Err(Error::invalid_value( - Unexpected::Unsigned(0), - &"a non-zero value", - )); - } - - Ok(value) -} - -/// Deserialize and validate that the value is non-zero -pub fn validate_optional_non_zero<'de, D>(d: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - let value: Option = Option::deserialize(d)?; - - if let Some(value) = value { - if value == Uint(U256::from(0)) { - return Err(Error::invalid_value( - Unexpected::Unsigned(0), - &"a non-zero value", - )); - } - } - - Ok(value) -} - -#[cfg(test)] -mod test { - use super::Uint; - use ethereum_types::U256; - - #[test] - fn uint_deserialization() { - let s = r#"["0xa", "10", "", "0x", 0]"#; - let deserialized: Vec = serde_json::from_str(s).unwrap(); - assert_eq!( - deserialized, - vec![ - Uint(U256::from(10)), - Uint(U256::from(10)), - Uint(U256::from(0)), - Uint(U256::from(0)), - Uint(U256::from(0)) - ] - ); - } - - #[test] - fn uint_deserialization_error_for_hex_too_large() { - let hex = format!("0x{}", "1".repeat(65)); - let result: Result = serde_json::from_str(&format!(r#""{}""#, hex)); - let err = result.unwrap_err(); - assert!(err.is_data()); - assert_eq!( - err.to_string(), - format!( - "Invalid hex value {}: value too big (length=65) at line 1 column 69", - hex - ) - ); - } - - #[test] - fn uint_into() { - assert_eq!(U256::from(10), Uint(U256::from(10)).into()); - } -} diff --git a/evm-tests/ethjson/src/vm.rs b/evm-tests/ethjson/src/vm.rs deleted file mode 100644 index 463016bb..00000000 --- a/evm-tests/ethjson/src/vm.rs +++ /dev/null @@ -1,323 +0,0 @@ -#![allow(clippy::too_many_lines)] -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Open Ethereum. - -// Open Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Open Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Open Ethereum. If not, see . - -//! Vm json deserialization - -use crate::{ - bytes::Bytes, - hash::{Address, H256}, - maybe::MaybeEmpty, - spec::State, - uint::Uint, -}; -use serde::Deserialize; - -/// Represents vm execution environment before and after execution of transaction. -#[derive(Debug, PartialEq, Deserialize)] -pub struct Vm { - /// Contract calls made internaly by executed transaction. - #[serde(rename = "callcreates")] - pub calls: Option>, - /// Env info. - pub env: Env, - /// Executed transaction - #[serde(rename = "exec")] - pub transaction: Transaction, - /// Gas left after transaction execution. - #[serde(rename = "gas")] - pub gas_left: Option, - /// Hash of logs created during execution of transaction. - pub logs: Option, - /// Transaction output. - #[serde(rename = "out")] - pub output: Option, - /// Post execution vm state. - #[serde(rename = "post")] - pub post_state: Option, - /// Pre execution vm state. - #[serde(rename = "pre")] - pub pre_state: State, -} - -impl Vm { - /// Returns true if transaction execution run out of gas. - pub const fn out_of_gas(&self) -> bool { - self.calls.is_none() - } -} - -/// Call deserialization. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Call { - /// Call data. - pub data: Bytes, - /// Call destination. - pub destination: MaybeEmpty
, - /// Gas limit. - pub gas_limit: Uint, - /// Call value. - pub value: Uint, -} - -/// Executed transaction. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Transaction { - /// Contract address. - pub address: Address, - /// Transaction sender. - #[serde(rename = "caller")] - pub sender: Address, - /// Contract code. - pub code: Bytes, - /// Input data. - pub data: Bytes, - /// Gas. - pub gas: Uint, - /// Gas price. - pub gas_price: Uint, - /// Transaction origin. - pub origin: Address, - /// Sent value. - pub value: Uint, - /// Contract code version. - #[serde(default)] - pub code_version: Uint, -} - -/// Environment. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Env { - /// Address. - #[serde(rename = "currentCoinbase")] - pub author: Address, - /// Difficulty - #[serde(rename = "currentDifficulty")] - pub difficulty: Uint, - /// Gas limit. - #[serde(rename = "currentGasLimit")] - pub gas_limit: Uint, - /// Number. - #[serde(rename = "currentNumber")] - pub number: Uint, - /// Timestamp. - #[serde(rename = "currentTimestamp")] - pub timestamp: Uint, - /// Block base fee (see EIP-1559) - #[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, - - /// EIP-4844 - pub parent_blob_gas_used: Option, - /// EIP-4844 - pub parent_excess_blob_gas: Option, - /// EIP-4844 - pub current_excess_blob_gas: Option, -} - -#[cfg(test)] -mod tests { - use super::{Address, Bytes, Call, Env, MaybeEmpty, State, Transaction, Uint, Vm, H256}; - use std::str::FromStr; - - use crate::spec::{Account, HashOrMap}; - use ethereum_types::{H160 as Hash160, H256 as Hash256, U256}; - use maplit::btreemap; - use rustc_hex::FromHex; - - const TEST_CODE: &str = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055"; - - #[test] - fn vm_deserialization() { - let s = r#"{ - "callcreates" : [ - ], - "env" : { - "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", - "currentDifficulty" : "0x0100", - "currentGasLimit" : "0x0f4240", - "currentNumber" : "0x00", - "currentTimestamp" : "0x01", - "currentRandom" : "0x01" - }, - "exec" : { - "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", - "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", - "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055", - "data" : "0x", - "gas" : "0x0186a0", - "gasPrice" : "0x5af3107a4000", - "origin" : "cd1722f2947def4cf144679da39c4c32bdc35681", - "value" : "0x0de0b6b3a7640000" - }, - "gas" : "0x013874", - "logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "out" : "0x", - "post" : { - "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { - "balance" : "0x0de0b6b3a7640000", - "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055", - "nonce" : "0x00", - "storage" : { - "0x00" : "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe" - } - } - }, - "pre" : { - "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { - "balance" : "0x0de0b6b3a7640000", - "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055", - "nonce" : "0x00", - "storage" : { - } - } - } - }"#; - let vm: Vm = serde_json::from_str(s).expect("JSON is valid"); - assert_eq!(vm.calls, Some(Vec::new())); - assert_eq!( - vm.env, - Env { - author: Address( - Hash160::from_str("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba").unwrap() - ), - difficulty: Uint(0x0100.into()), - gas_limit: Uint(0x0f4240.into()), - number: Uint(0.into()), - timestamp: Uint(1.into()), - block_base_fee_per_gas: Uint(0.into()), - random: Some(Uint(1.into())), - parent_excess_blob_gas: None, - parent_blob_gas_used: None, - current_excess_blob_gas: None - } - ); - assert_eq!( - vm.transaction, - Transaction { - address: Address( - Hash160::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap() - ), - sender: Address( - Hash160::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap() - ), - code: Bytes::new(TEST_CODE.from_hex().unwrap()), - code_version: Uint(0.into()), - data: Bytes::new(Vec::new()), - gas: Uint(0x0186a0.into()), - gas_price: Uint(0x5af3107a4000_u64.into()), - origin: Address( - Hash160::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap() - ), - value: Uint(0x0de0b6b3a7640000_u64.into()) - } - ); - assert_eq!(vm.gas_left, Some(Uint(0x013874.into()))); - assert_eq!( - vm.logs, - Some(H256( - Hash256::from_str( - "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" - ) - .unwrap() - )) - ); - assert_eq!(vm.output, Some(Bytes::new(Vec::new()))); - assert_eq!( - vm.pre_state, - State(HashOrMap::Map(btreemap![ - Address(Hash160::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap()) => Account { - builtin: None, - balance: Some(Uint(0x0de0b6b3a7640000_u64.into())), - code: Some(Bytes::new(TEST_CODE.from_hex().unwrap())), - constructor: None, - nonce: Some(Uint(0.into())), - storage: Some(btreemap![]), - version: None, - } - ])) - ); - assert_eq!( - vm.post_state, - Some(State(HashOrMap::Map(btreemap![ - Address(Hash160::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap()) => Account { - builtin: None, - balance: Some(Uint(0x0de0b6b3a7640000_u64.into())), - code: Some(Bytes::new(TEST_CODE.from_hex().unwrap())), - constructor: None, - nonce: Some(Uint(0.into())), - storage: Some(btreemap![ - Uint(0.into()) => Uint(U256::from_str("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe").unwrap()) - ]), - version: None, - }]))) - ); - } - - #[test] - fn call_deserialization_empty_dest() { - let s = r#"{ - "data" : "0x1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff", - "destination" : "", - "gasLimit" : "0x1748766aa5", - "value" : "0x00" - }"#; - let call: Call = serde_json::from_str(s).unwrap(); - - assert_eq!( - &call.data[..], - &[ - 0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66, 0x66, 0x77, 0x77, - 0x88, 0x88, 0x99, 0x99, 0x00, 0x00, 0xaa, 0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, - 0xee, 0xee, 0xff, 0xff - ] - ); - - assert_eq!(call.destination, MaybeEmpty::None); - assert_eq!(call.gas_limit, Uint(U256::from(0x1748766aa5u64))); - assert_eq!(call.value, Uint(U256::from(0))); - } - - #[test] - fn call_deserialization_full_dest() { - let s = r#"{ - "data" : "0x1234", - "destination" : "5a39ed1020c04d4d84539975b893a4e7c53eab6c", - "gasLimit" : "0x1748766aa5", - "value" : "0x00" - }"#; - - let call: Call = serde_json::from_str(s).unwrap(); - - assert_eq!(&call.data[..], &[0x12, 0x34]); - assert_eq!( - call.destination, - MaybeEmpty::Some(Address( - Hash160::from_str("5a39ed1020c04d4d84539975b893a4e7c53eab6c").unwrap() - )) - ); - assert_eq!(call.gas_limit, Uint(U256::from(0x1748766aa5u64))); - assert_eq!(call.value, Uint(U256::from(0))); - } -} diff --git a/evm-tests/jsontests/Cargo.toml b/evm-tests/jsontests/Cargo.toml deleted file mode 100644 index fabe9e1d..00000000 --- a/evm-tests/jsontests/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -name = "evm-jsontests" -version.workspace = true -license = "GPL-3.0" -authors.workspace = true -description = "EVM json tests" -repository.workspace = true -keywords.workspace = true -edition.workspace = true -publish = false - -[dependencies] -aurora-evm.workspace = true -primitive-types.workspace = true -rlp.workspace = true -sha3.workspace = true -serde.workspace = true - -ethereum = "0.15.0" -serde_json = "1.0" -hex = "0.4" -clap = { version = "4.5", features = ["cargo"] } -ethjson = { path = "../ethjson", features = ["test-helpers"] } -libsecp256k1 = "0.7" -ethcore-builtin = { path = "../ethcore-builtin" } -parity-bytes = "0.1" -hex-literal = "0.4" - -[features] -enable-slow-tests = [] -print-debug = ["aurora-evm/print-debug"] -dump-state = ["aurora-evm/with-serde"] diff --git a/evm-tests/jsontests/src/lib.rs b/evm-tests/jsontests/src/lib.rs deleted file mode 100644 index 8018d2c5..00000000 --- a/evm-tests/jsontests/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod utils; - -pub mod state; -pub mod vm; diff --git a/evm-tests/jsontests/src/state.rs b/evm-tests/jsontests/src/state.rs deleted file mode 100644 index 470a08f7..00000000 --- a/evm-tests/jsontests/src/state.rs +++ /dev/null @@ -1,1550 +0,0 @@ -use crate::utils::eip_4844; -use crate::utils::transaction::InvalidTxReason; -use aurora_evm::backend::{ApplyBackend, MemoryAccount, MemoryBackend, MemoryVicinity}; -use aurora_evm::executor::stack::{ - Authorization, MemoryStackState, PrecompileFailure, PrecompileFn, PrecompileOutput, - StackExecutor, StackSubstateMetadata, -}; -use aurora_evm::utils::U64_MAX; -use aurora_evm::{Config, Context, ExitError, ExitReason, ExitSucceed}; -use ethjson::hash::Address; -use ethjson::spec::builtin::{AltBn128ConstOperations, AltBn128Pairing, PricingAt}; -use ethjson::spec::{ForkSpec, Pricing}; -use ethjson::test_helpers::state::PostStateResult; -use ethjson::uint::Uint; -use libsecp256k1::SecretKey; -use primitive_types::{H160, H256, U256}; -use serde::Deserialize; -use sha3::{Digest, Keccak256}; -use std::collections::BTreeMap; -use std::path::PathBuf; -use std::str::FromStr; -use std::sync::{Arc, LazyLock}; - -#[derive(Default, Debug, Clone)] -pub struct VerboseOutput { - pub verbose: bool, - pub verbose_failed: bool, - pub very_verbose: bool, - pub print_state: bool, -} - -#[derive(Default, Debug, Clone)] -#[cfg_attr(feature = "dump-state", derive(serde::Serialize, serde::Deserialize))] -pub struct StateTestsDump { - pub state: BTreeMap, - pub caller: H160, - pub gas_price: U256, - pub effective_gas_price: U256, - pub caller_secret_key: H256, - pub used_gas: u64, - pub state_hash: H256, - pub result_state: BTreeMap, - pub to: H160, - pub value: U256, - pub data: Vec, - pub gas_limit: u64, - pub access_list: Vec<(H160, Vec)>, -} - -trait StateTestsDumper { - fn set_state(&mut self, _state: &BTreeMap) {} - fn set_used_gas(&mut self, _used_gas: u64) {} - fn set_vicinity(&mut self, _vicinity: &MemoryVicinity) {} - fn set_tx_data( - &mut self, - _to: H160, - _value: U256, - _data: Vec, - _gas_limit: u64, - _access_list: Vec<(H160, Vec)>, - ) { - } - fn set_caller_secret_key(&mut self, _caller_secret_key: H256) {} - fn set_state_hash(&mut self, _state_hash: H256) {} - fn set_result_state(&mut self, _state: &BTreeMap) {} - fn dump_to_file(&self, _spec: &ForkSpec) {} -} - -#[cfg(not(feature = "dump-state"))] -impl StateTestsDumper for StateTestsDump {} - -#[cfg(feature = "dump-state")] -impl StateTestsDumper for StateTestsDump { - fn set_state(&mut self, state: &BTreeMap) { - self.state = state.clone(); - } - - fn set_used_gas(&mut self, used_gas: u64) { - self.used_gas = used_gas; - } - - fn set_vicinity(&mut self, vicinity: &MemoryVicinity) { - self.caller = vicinity.origin; - self.gas_price = vicinity.gas_price; - self.effective_gas_price = vicinity.effective_gas_price; - } - - fn set_tx_data( - &mut self, - to: H160, - value: U256, - data: Vec, - gas_limit: u64, - access_list: Vec<(H160, Vec)>, - ) { - self.to = to; - self.value = value; - self.data = data; - self.gas_limit = gas_limit; - self.access_list = access_list; - } - - fn set_caller_secret_key(&mut self, caller_secret_key: H256) { - self.caller_secret_key = caller_secret_key; - } - - fn set_state_hash(&mut self, state_hash: H256) { - self.state_hash = state_hash; - } - - fn set_result_state(&mut self, state: &BTreeMap) { - self.result_state = state.clone(); - } - - fn dump_to_file(&self, spec: &ForkSpec) { - use std::time::{SystemTime, UNIX_EPOCH}; - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_micros(); - let path = format!("{spec:?}_BLS12-382_{now}.json"); - let json = serde_json::to_string(&self).unwrap(); - std::fs::write(path, json).unwrap(); - } -} - -#[derive(Clone, Debug)] -pub struct FailedTestDetails { - pub name: String, - pub spec: ForkSpec, - pub index: usize, - pub expected_hash: H256, - pub actual_hash: H256, - pub state: BTreeMap, -} - -#[derive(Clone, Debug)] -pub struct TestExecutionResult { - pub total: u64, - pub failed: u64, - pub failed_tests: Vec, -} - -impl TestExecutionResult { - #[allow(clippy::new_without_default)] - pub const fn new() -> Self { - Self { - total: 0, - failed: 0, - failed_tests: Vec::new(), - } - } - - pub fn merge(&mut self, src: Self) { - self.failed_tests.extend(src.failed_tests); - self.total += src.total; - self.failed += src.failed; - } -} - -#[derive(Deserialize, Debug)] -pub struct Test(ethjson::test_helpers::state::State); - -impl Test { - pub fn unwrap_to_pre_state(&self) -> BTreeMap { - crate::utils::unwrap_to_state(&self.0.pre_state) - } - - pub fn unwrap_caller_secret_key(&self) -> H256 { - self.0.transaction.secret.unwrap().into() - } - - pub fn unwrap_caller(&self) -> H160 { - let hash: H256 = self.0.transaction.secret.unwrap().into(); - let mut secret_key = [0; 32]; - 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())) - } - - pub fn unwrap_to_vicinity( - &self, - spec: &ForkSpec, - blob_gas_price: Option, - ) -> Result { - let block_base_fee_per_gas = self.0.env.block_base_fee_per_gas.0; - let tx = &self.0.transaction; - // Validation for EIP-1559 that was introduced in London hard fork - let gas_price = if *spec >= ForkSpec::London { - tx.gas_price.or(tx.max_fee_per_gas).unwrap_or_default().0 - } else { - if tx.max_fee_per_gas.is_some() { - return Err(InvalidTxReason::GasPriseEip1559); - } - tx.gas_price.expect("expect gas price").0 - }; - - // EIP-1559: priority fee must be lower than gas_price - if let Some(max_priority_fee_per_gas) = tx.max_priority_fee_per_gas { - if max_priority_fee_per_gas.0 > gas_price { - return Err(InvalidTxReason::PriorityFeeTooLarge); - } - } - - let effective_gas_price = self.0.transaction.max_priority_fee_per_gas.map_or( - gas_price, - |max_priority_fee_per_gas| { - gas_price.min(max_priority_fee_per_gas.0 + block_base_fee_per_gas) - }, - ); - - // gas price cannot be lower than base fee - if gas_price < block_base_fee_per_gas { - return Err(InvalidTxReason::GasPriceLessThenBlockBaseFee); - } - - 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. - crate::utils::u256_to_h256(r.0) - }) - } else { - None - }; - let blob_hashes = tx.blob_versioned_hashes.clone(); - - Ok(MemoryVicinity { - gas_price, - effective_gas_price, - origin: self.unwrap_caller(), - block_hashes: Vec::new(), - 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, - blob_gas_price, - blob_hashes, - }) - } -} - -type LazyPrecompiles = LazyLock>; -static ISTANBUL_BUILTINS: LazyPrecompiles = LazyLock::new(istanbul_builtins); -static BERLIN_BUILTINS: LazyPrecompiles = LazyLock::new(berlin_builtins); -static CANCUN_BUILTINS: LazyPrecompiles = LazyLock::new(cancun_builtins); -static PRAGUE_BUILTINS: LazyPrecompiles = LazyLock::new(prague_builtins); - -macro_rules! precompile_entry { - ($map:expr, $builtins:expr, $index:expr) => { - let x: PrecompileFn = - |input: &[u8], gas_limit: Option, _context: &Context, _is_static: bool| { - let builtin = $builtins.get(&H160::from_low_u64_be($index)).unwrap(); - Self::exec_as_precompile(builtin, input, gas_limit) - }; - $map.insert(H160::from_low_u64_be($index), x); - }; -} - -pub struct JsonPrecompile; - -impl JsonPrecompile { - pub fn precompile(spec: &ForkSpec) -> Option> { - match spec { - ForkSpec::Istanbul => { - let mut map = BTreeMap::new(); - precompile_entry!(map, ISTANBUL_BUILTINS, 1); - precompile_entry!(map, ISTANBUL_BUILTINS, 2); - precompile_entry!(map, ISTANBUL_BUILTINS, 3); - precompile_entry!(map, ISTANBUL_BUILTINS, 4); - precompile_entry!(map, ISTANBUL_BUILTINS, 5); - precompile_entry!(map, ISTANBUL_BUILTINS, 6); - precompile_entry!(map, ISTANBUL_BUILTINS, 7); - precompile_entry!(map, ISTANBUL_BUILTINS, 8); - precompile_entry!(map, ISTANBUL_BUILTINS, 9); - Some(map) - } - ForkSpec::Berlin => { - let mut map = BTreeMap::new(); - precompile_entry!(map, BERLIN_BUILTINS, 1); - precompile_entry!(map, BERLIN_BUILTINS, 2); - precompile_entry!(map, BERLIN_BUILTINS, 3); - precompile_entry!(map, BERLIN_BUILTINS, 4); - precompile_entry!(map, BERLIN_BUILTINS, 5); - precompile_entry!(map, BERLIN_BUILTINS, 6); - precompile_entry!(map, BERLIN_BUILTINS, 7); - precompile_entry!(map, BERLIN_BUILTINS, 8); - precompile_entry!(map, BERLIN_BUILTINS, 9); - Some(map) - } - // 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 Paris and Berlin are the same - ForkSpec::Paris => Self::precompile(&ForkSpec::Berlin), - // precompiles for Shanghai and Berlin are the same - ForkSpec::Shanghai => Self::precompile(&ForkSpec::Berlin), - ForkSpec::Cancun => { - let mut map = BTreeMap::new(); - precompile_entry!(map, CANCUN_BUILTINS, 1); - precompile_entry!(map, CANCUN_BUILTINS, 2); - precompile_entry!(map, CANCUN_BUILTINS, 3); - precompile_entry!(map, CANCUN_BUILTINS, 4); - precompile_entry!(map, CANCUN_BUILTINS, 5); - precompile_entry!(map, CANCUN_BUILTINS, 6); - precompile_entry!(map, CANCUN_BUILTINS, 7); - precompile_entry!(map, CANCUN_BUILTINS, 8); - precompile_entry!(map, CANCUN_BUILTINS, 9); - precompile_entry!(map, CANCUN_BUILTINS, 0xA); - Some(map) - } - ForkSpec::Prague => { - let mut map = BTreeMap::new(); - precompile_entry!(map, PRAGUE_BUILTINS, 1); - precompile_entry!(map, PRAGUE_BUILTINS, 2); - precompile_entry!(map, PRAGUE_BUILTINS, 3); - precompile_entry!(map, PRAGUE_BUILTINS, 4); - precompile_entry!(map, PRAGUE_BUILTINS, 5); - precompile_entry!(map, PRAGUE_BUILTINS, 6); - precompile_entry!(map, PRAGUE_BUILTINS, 7); - precompile_entry!(map, PRAGUE_BUILTINS, 8); - precompile_entry!(map, PRAGUE_BUILTINS, 9); - precompile_entry!(map, PRAGUE_BUILTINS, 0x0A); - precompile_entry!(map, PRAGUE_BUILTINS, 0x0B); - precompile_entry!(map, PRAGUE_BUILTINS, 0x0C); - precompile_entry!(map, PRAGUE_BUILTINS, 0x0D); - precompile_entry!(map, PRAGUE_BUILTINS, 0x0E); - precompile_entry!(map, PRAGUE_BUILTINS, 0x0F); - precompile_entry!(map, PRAGUE_BUILTINS, 0x10); - precompile_entry!(map, PRAGUE_BUILTINS, 0x11); - Some(map) - } - _ => None, - } - } - - fn exec_as_precompile( - builtin: ðcore_builtin::Builtin, - input: &[u8], - gas_limit: Option, - ) -> Result<(PrecompileOutput, u64), PrecompileFailure> { - let cost = builtin.cost(input, 0); - - if let Some(target_gas) = gas_limit { - if cost > U64_MAX || target_gas < cost.as_u64() { - return Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }); - } - } - - let mut output = Vec::new(); - match builtin.execute(input, &mut parity_bytes::BytesRef::Flexible(&mut output)) { - Ok(()) => Ok(( - PrecompileOutput { - exit_status: ExitSucceed::Stopped, - output, - }, - cost.as_u64(), - )), - Err(e) => Err(PrecompileFailure::Error { - exit_status: ExitError::Other(e.into()), - }), - } - } -} - -fn istanbul_builtins() -> BTreeMap { - use ethjson::spec::builtin::{BuiltinCompat, Linear, Modexp, PricingCompat}; - - let builtins: BTreeMap = BTreeMap::from([ - ( - Address(H160::from_low_u64_be(1)), - BuiltinCompat { - name: "ecrecover".to_string(), - pricing: PricingCompat::Single(Pricing::Linear(Linear { - base: 3000, - word: 0, - })), - activate_at: None, - }, - ), - ( - Address(H160::from_low_u64_be(2)), - BuiltinCompat { - name: "sha256".to_string(), - pricing: PricingCompat::Single(Pricing::Linear(Linear { base: 60, word: 12 })), - activate_at: None, - }, - ), - ( - Address(H160::from_low_u64_be(3)), - BuiltinCompat { - name: "ripemd160".to_string(), - pricing: PricingCompat::Single(Pricing::Linear(Linear { - base: 600, - word: 120, - })), - activate_at: None, - }, - ), - ( - Address(H160::from_low_u64_be(4)), - BuiltinCompat { - name: "identity".to_string(), - pricing: PricingCompat::Single(Pricing::Linear(Linear { base: 15, word: 3 })), - activate_at: None, - }, - ), - ( - Address(H160::from_low_u64_be(5)), - BuiltinCompat { - name: "modexp".to_string(), - pricing: PricingCompat::Single(Pricing::Modexp(Modexp { - divisor: 20, - is_eip_2565: false, - })), - activate_at: Some(Uint(U256::zero())), - }, - ), - ( - Address(H160::from_low_u64_be(6)), - BuiltinCompat { - name: "alt_bn128_add".to_string(), - pricing: PricingCompat::Multi(BTreeMap::from([( - Uint(U256::zero()), - PricingAt { - info: Some("EIP 1108 transition".to_string()), - price: Pricing::AltBn128ConstOperations(AltBn128ConstOperations { - price: 150, - }), - }, - )])), - activate_at: None, - }, - ), - ( - Address(H160::from_low_u64_be(7)), - BuiltinCompat { - name: "alt_bn128_mul".to_string(), - pricing: PricingCompat::Multi(BTreeMap::from([( - Uint(U256::zero()), - PricingAt { - info: Some("EIP 1108 transition".to_string()), - price: Pricing::AltBn128ConstOperations(AltBn128ConstOperations { - price: 6000, - }), - }, - )])), - activate_at: None, - }, - ), - ( - Address(H160::from_low_u64_be(8)), - BuiltinCompat { - name: "alt_bn128_pairing".to_string(), - pricing: PricingCompat::Multi(BTreeMap::from([( - Uint(U256::zero()), - PricingAt { - info: Some("EIP 1108 transition".to_string()), - price: Pricing::AltBn128Pairing(AltBn128Pairing { - base: 45000, - pair: 34000, - }), - }, - )])), - activate_at: None, - }, - ), - ( - Address(H160::from_low_u64_be(9)), - BuiltinCompat { - name: "blake2_f".to_string(), - pricing: PricingCompat::Single(Pricing::Blake2F { gas_per_round: 1 }), - activate_at: Some(Uint(U256::zero())), - }, - ), - ]); - builtins - .into_iter() - .map(|(address, builtin)| { - ( - address.into(), - ethjson::spec::Builtin::from(builtin).try_into().unwrap(), - ) - }) - .collect() -} - -fn berlin_builtins() -> BTreeMap { - use ethjson::spec::builtin::{BuiltinCompat, Linear, Modexp, PricingCompat}; - - let builtins: BTreeMap = BTreeMap::from([ - ( - Address(H160::from_low_u64_be(1)), - BuiltinCompat { - name: "ecrecover".to_string(), - pricing: PricingCompat::Single(Pricing::Linear(Linear { - base: 3000, - word: 0, - })), - activate_at: None, - }, - ), - ( - Address(H160::from_low_u64_be(2)), - BuiltinCompat { - name: "sha256".to_string(), - pricing: PricingCompat::Single(Pricing::Linear(Linear { base: 60, word: 12 })), - activate_at: None, - }, - ), - ( - Address(H160::from_low_u64_be(3)), - BuiltinCompat { - name: "ripemd160".to_string(), - pricing: PricingCompat::Single(Pricing::Linear(Linear { - base: 600, - word: 120, - })), - activate_at: None, - }, - ), - ( - Address(H160::from_low_u64_be(4)), - BuiltinCompat { - name: "identity".to_string(), - pricing: PricingCompat::Single(Pricing::Linear(Linear { base: 15, word: 3 })), - activate_at: None, - }, - ), - ( - Address(H160::from_low_u64_be(5)), - BuiltinCompat { - name: "modexp".to_string(), - pricing: PricingCompat::Single(Pricing::Modexp(Modexp { - divisor: 3, - is_eip_2565: true, - })), - activate_at: Some(Uint(U256::zero())), - }, - ), - ( - Address(H160::from_low_u64_be(6)), - BuiltinCompat { - name: "alt_bn128_add".to_string(), - pricing: PricingCompat::Multi(BTreeMap::from([( - Uint(U256::zero()), - PricingAt { - info: Some("EIP 1108 transition".to_string()), - price: Pricing::AltBn128ConstOperations(AltBn128ConstOperations { - price: 150, - }), - }, - )])), - activate_at: None, - }, - ), - ( - Address(H160::from_low_u64_be(7)), - BuiltinCompat { - name: "alt_bn128_mul".to_string(), - pricing: PricingCompat::Multi(BTreeMap::from([( - Uint(U256::zero()), - PricingAt { - info: Some("EIP 1108 transition".to_string()), - price: Pricing::AltBn128ConstOperations(AltBn128ConstOperations { - price: 6000, - }), - }, - )])), - activate_at: None, - }, - ), - ( - Address(H160::from_low_u64_be(8)), - BuiltinCompat { - name: "alt_bn128_pairing".to_string(), - pricing: PricingCompat::Multi(BTreeMap::from([( - Uint(U256::zero()), - PricingAt { - info: Some("EIP 1108 transition".to_string()), - price: Pricing::AltBn128Pairing(AltBn128Pairing { - base: 45000, - pair: 34000, - }), - }, - )])), - activate_at: None, - }, - ), - ( - Address(H160::from_low_u64_be(9)), - BuiltinCompat { - name: "blake2_f".to_string(), - pricing: PricingCompat::Single(Pricing::Blake2F { gas_per_round: 1 }), - activate_at: Some(Uint(U256::zero())), - }, - ), - ]); - builtins - .into_iter() - .map(|(address, builtin)| { - ( - address.into(), - ethjson::spec::Builtin::from(builtin).try_into().unwrap(), - ) - }) - .collect() -} - -fn cancun_builtins() -> BTreeMap { - use ethjson::spec::builtin::{BuiltinCompat, Linear, PricingCompat}; - - let mut builtins = berlin_builtins(); - builtins.insert( - Address(H160::from_low_u64_be(0xA)).into(), - ethjson::spec::Builtin::from(BuiltinCompat { - name: "kzg".to_string(), - pricing: PricingCompat::Single(Pricing::Linear(Linear { - base: 50_000, - word: 0, - })), - activate_at: None, - }) - .try_into() - .unwrap(), - ); - builtins -} - -fn prague_builtins() -> BTreeMap { - use ethjson::spec::builtin::{BuiltinCompat, Linear, PricingCompat}; - - let mut builtins = cancun_builtins(); - builtins.insert( - Address(H160::from_low_u64_be(0xB)).into(), - ethjson::spec::Builtin::from(BuiltinCompat { - name: "bls12_381_g1_add".to_string(), - pricing: PricingCompat::Single(Pricing::Linear(Linear { base: 375, word: 0 })), - activate_at: None, - }) - .try_into() - .unwrap(), - ); - builtins.insert( - Address(H160::from_low_u64_be(0xC)).into(), - ethjson::spec::Builtin::from(BuiltinCompat { - name: "bls12_381_g1_mul".to_string(), - pricing: PricingCompat::Single(Pricing::Bls12G1Mul), - activate_at: None, - }) - .try_into() - .unwrap(), - ); - builtins.insert( - Address(H160::from_low_u64_be(0xD)).into(), - ethjson::spec::Builtin::from(BuiltinCompat { - name: "bls12_381_g2_add".to_string(), - pricing: PricingCompat::Single(Pricing::Linear(Linear { base: 600, word: 0 })), - activate_at: None, - }) - .try_into() - .unwrap(), - ); - builtins.insert( - Address(H160::from_low_u64_be(0xE)).into(), - ethjson::spec::Builtin::from(BuiltinCompat { - name: "bls12_381_g2_mul".to_string(), - pricing: PricingCompat::Single(Pricing::Bls12G2Mul), - activate_at: None, - }) - .try_into() - .unwrap(), - ); - builtins.insert( - Address(H160::from_low_u64_be(0xF)).into(), - ethjson::spec::Builtin::from(BuiltinCompat { - name: "bls12_381_pairing".to_string(), - pricing: PricingCompat::Single(Pricing::Bls12Pairing), - activate_at: None, - }) - .try_into() - .unwrap(), - ); - builtins.insert( - Address(H160::from_low_u64_be(0x10)).into(), - ethjson::spec::Builtin::from(BuiltinCompat { - name: "bls12_381_fp_to_g1".to_string(), - pricing: PricingCompat::Single(Pricing::Linear(Linear { - base: 5_500, - word: 0, - })), - activate_at: None, - }) - .try_into() - .unwrap(), - ); - builtins.insert( - Address(H160::from_low_u64_be(0x11)).into(), - ethjson::spec::Builtin::from(BuiltinCompat { - name: "bls12_381_fp2_to_g2".to_string(), - pricing: PricingCompat::Single(Pricing::Linear(Linear { - base: 23_800, - word: 0, - })), - activate_at: None, - }) - .try_into() - .unwrap(), - ); - - builtins -} - -pub fn test( - verbose_output: VerboseOutput, - name: &str, - test: Test, - specific_spec: Option, - file_name: Arc, -) -> TestExecutionResult { - use std::thread; - - const STACK_SIZE: usize = 16 * 1024 * 1024; - - let name = name.to_string(); - // Spawn thread with explicit stack size - - let child = thread::Builder::new() - .stack_size(STACK_SIZE) - .spawn(move || { - test_run( - &verbose_output, - &name, - test, - specific_spec, - file_name.clone(), - ) - }) - .unwrap(); - - // Wait for thread to join - child.join().unwrap() -} - -/// Validate EIP-3607 - empty create caller -fn assert_empty_create_caller(expect_exception: &Option, name: &str) { - let exception = expect_exception - .as_deref() - .expect("expected evm-json-test exception"); - let check_exception = - exception == "SenderNotEOA" || exception == "TransactionException.SENDER_NOT_EOA"; - assert!( - check_exception, - "expected EmptyCaller exception for test: {name}: {expect_exception:?}" - ); -} - -/// Check call expected exception -fn assert_call_exit_exception(expect_exception: &Option, name: &str) { - assert!( - expect_exception.is_none(), - "unexpected call exception: {expect_exception:?} for test: {name}" - ); -} - -/// Check Exit Reason of EVM execution -fn check_create_exit_reason( - reason: &ExitReason, - expect_exception: &Option, - name: &str, -) -> bool { - match reason { - ExitReason::Error(err) => { - if let Some(exception) = expect_exception.as_deref() { - match err { - ExitError::CreateContractLimit => { - let check_result = exception == "TR_InitCodeLimitExceeded" - || exception == "TransactionException.INITCODE_SIZE_EXCEEDED"; - assert!( - check_result, - "unexpected exception {exception:?} for CreateContractLimit error for test: {name}" - ); - return true; - } - ExitError::MaxNonce => { - let check_result = exception == "TR_NonceHasMaxValue" - || exception == "TransactionException.NONCE_IS_MAX"; - assert!(check_result, - "unexpected exception {exception:?} for MaxNonce error for test: {name}" - ); - return true; - } - ExitError::OutOfGas => { - let check_result = - exception == "TransactionException.INTRINSIC_GAS_TOO_LOW"; - assert!(check_result, - "unexpected exception {exception:?} for OutOfGas error for test: {name}" - ); - return true; - } - _ => { - panic!( - "unexpected error: {err:?} for exception: {exception} for test: {name}" - ) - } - } - } else { - return false; - } - } - ExitReason::Fatal(err) => { - panic!("Unexpected error: {err:?}") - } - _ => { - assert!( - expect_exception.is_none(), - "Unexpected json-test error: {expect_exception:?}" - ); - } - } - false -} - -/// Assert vicinity validation to ensure that test os expected validation error -#[allow(clippy::cognitive_complexity)] -fn assert_vicinity_validation( - reason: &InvalidTxReason, - states: &[PostStateResult], - spec: &ForkSpec, - name: &str, - file_name: Arc, -) { - match *spec { - ForkSpec::Istanbul | ForkSpec::Berlin => match reason { - InvalidTxReason::GasPriseEip1559 => { - for (i, state) in states.iter().enumerate() { - let expected = state.expect_exception.as_deref().unwrap_or_else(|| { - panic!( - "expected error message for test: [{spec:?}] {name}:{i}\n{file_name:?}" - ) - }); - - let is_checked = - expected == "TR_TypeNotSupported" || expected == "TR_TypeNotSupportedBlob"; - assert!( - is_checked, - "unexpected error message {expected:?} for: [{spec:?}] {name}:{i}\n{file_name:?}", - ); - } - } - _ => panic!("Unexpected validation reason: {reason:?} [{name}]\n{file_name:?}"), - }, - ForkSpec::London => match reason { - InvalidTxReason::PriorityFeeTooLarge => { - for (i, state) in states.iter().enumerate() { - let expected = state.expect_exception.as_deref().unwrap_or_else(|| { - panic!("expected error message for test: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}") - }); - let is_checked = expected == "tipTooHigh" || expected == "TR_TipGtFeeCap"; - assert!( - is_checked, - "unexpected error message {expected:?} for: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}", - ); - } - } - InvalidTxReason::GasPriceLessThenBlockBaseFee => { - for (i, state) in states.iter().enumerate() { - let expected = state.expect_exception.as_deref().unwrap_or_else(|| { - panic!("expected error message for test: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}") - }); - let is_checked = - expected == "lowFeeCap" || expected == "TR_FeeCapLessThanBlocks"; - assert!( - is_checked, - "unexpected error message {expected:?} for: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}", - ); - } - } - _ => { - panic!("Unexpected validation reason: {reason:?} [{spec:?}] {name}\n{file_name:?}") - } - }, - ForkSpec::Paris => match reason { - InvalidTxReason::PriorityFeeTooLarge => { - for (i, state) in states.iter().enumerate() { - let expected = state.expect_exception.as_deref().unwrap_or_else(|| { - panic!("expected error message for test: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}") - }); - let is_checked = expected == "TR_TipGtFeeCap"; - assert!( - is_checked, - "unexpected error message {expected:?} for: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}", - ); - } - } - InvalidTxReason::GasPriceLessThenBlockBaseFee => { - for (i, state) in states.iter().enumerate() { - let expected = state.expect_exception.as_deref().unwrap_or_else(|| { - panic!("expected error message for test: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}") - }); - let is_checked = expected == "TR_FeeCapLessThanBlocks"; - assert!( - is_checked, - "unexpected error message {expected:?} for: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}", - ); - } - } - _ => { - panic!("Unexpected validation reason: {reason:?} [{spec:?}] {name}\n{file_name:?}") - } - }, - ForkSpec::Shanghai => match reason { - InvalidTxReason::PriorityFeeTooLarge => { - for (i, state) in states.iter().enumerate() { - let expected = state.expect_exception.as_deref().unwrap_or_else(|| { - panic!("expected error message for test: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}") - }); - let is_checked = expected == "TR_TipGtFeeCap"; - assert!( - is_checked, - "unexpected error message {expected:?} for: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}\n{file_name:?}", - ); - } - } - InvalidTxReason::GasPriceLessThenBlockBaseFee => { - for (i, state) in states.iter().enumerate() { - let expected = state.expect_exception.as_deref().unwrap_or_else(|| { - panic!("expected error message for test: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}") - }); - - let is_checked = expected == "TR_FeeCapLessThanBlocks"; - assert!( - is_checked, - "unexpected error message {expected:?} for: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}", - ); - } - } - _ => { - panic!("Unexpected validation reason: {reason:?} [{spec:?}] {name}\n{file_name:?}") - } - }, - ForkSpec::Cancun => match reason { - InvalidTxReason::PriorityFeeTooLarge => { - for (i, state) in states.iter().enumerate() { - let expected = state.expect_exception.as_deref().unwrap_or_else(|| { - panic!("expected error message for test: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}") - }); - - let is_checked = expected == "TR_TipGtFeeCap" - || expected == "TransactionException.PRIORITY_GREATER_THAN_MAX_FEE_PER_GAS"; - assert!( - is_checked, - "unexpected error message {expected:?} for: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}", - ); - } - } - InvalidTxReason::GasPriceLessThenBlockBaseFee => { - for (i, state) in states.iter().enumerate() { - let expected = state.expect_exception.as_deref().unwrap_or_else(|| { - panic!("expected error message for test: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}") - }); - - let is_checked = expected == "TR_FeeCapLessThanBlocks" - || expected == "TransactionException.INSUFFICIENT_MAX_FEE_PER_GAS"; - assert!( - is_checked, - "unexpected error message {expected:?} for: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}", - ); - } - } - _ => { - panic!("Unexpected validation reason: {reason:?} [{spec:?}] {name}\n{file_name:?}") - } - }, - ForkSpec::Prague => match reason { - InvalidTxReason::PriorityFeeTooLarge => { - for (i, state) in states.iter().enumerate() { - let expected = state.expect_exception.as_deref().unwrap_or_else(|| { - panic!("expected error message for test: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}") - }); - - let is_checked = - expected == "TransactionException.PRIORITY_GREATER_THAN_MAX_FEE_PER_GAS"; - assert!( - is_checked, - "unexpected error message {expected:?} for: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}", - ); - } - } - - InvalidTxReason::GasPriceLessThenBlockBaseFee => { - for (i, state) in states.iter().enumerate() { - let expected = state.expect_exception.as_deref().unwrap_or_else(|| { - panic!("expected error message for test: {reason:?} [{spec:?}] {name}:{i})\n{file_name:?}") - }); - let is_checked = expected == "TR_FeeCapLessThanBlocks" - || expected == "TransactionException.INSUFFICIENT_MAX_FEE_PER_GAS"; - assert!( - is_checked, - "unexpected error message {expected:?} for: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}", - ); - } - } - _ => { - panic!("Unexpected validation reason: {reason:?} [{spec:?}] {name}\n{file_name:?}") - } - }, - _ => panic!("Unexpected validation reason: {reason:?} [{spec:?}] {name}\n{file_name:?}"), - } -} - -/// Check Exit Reason of EVM execution -fn check_validate_exit_reason( - reason: &InvalidTxReason, - expect_exception: &Option, - name: &str, - spec: &ForkSpec, -) -> bool { - expect_exception.as_deref().map_or_else( - || { - panic!("unexpected validation error reason: {reason:?} {name}"); - }, - |exception| { - match reason { - InvalidTxReason::OutOfFund => { - let check_result = exception - == "TransactionException.INSUFFICIENT_ACCOUNT_FUNDS" - || exception == "TR_NoFunds" - || exception == "TR_NoFundsX" - || exception == "TransactionException.INSUFFICIENT_MAX_FEE_PER_BLOB_GAS" - || exception == "TransactionException.INSUFFICIENT_ACCOUNT_FUNDS|TransactionException.GASLIMIT_PRICE_PRODUCT_OVERFLOW"; - assert!( - check_result, - "unexpected exception {exception:?} for OutOfFund for test: [{spec:?}] {name}" - ); - } - InvalidTxReason::GasLimitReached => { - let check_result = exception == "TR_GasLimitReached" - || exception == "TransactionException.GAS_ALLOWANCE_EXCEEDED"; - assert!( - check_result, - "unexpected exception {exception:?} for GasLimitReached for test: [{spec:?}] {name}" - ); - } - InvalidTxReason::IntrinsicGas => { - let check_result = exception == "TR_NoFundsOrGas" - || exception == "TR_IntrinsicGas" - || exception == "TransactionException.INTRINSIC_GAS_TOO_LOW" - || exception == "IntrinsicGas" - || exception == "TransactionException.INSUFFICIENT_ACCOUNT_FUNDS|TransactionException.INTRINSIC_GAS_TOO_LOW"; - assert!( - check_result, - "unexpected exception {exception:?} for IntrinsicGas for test: [{spec:?}] {name}" - ); - } - InvalidTxReason::BlobVersionNotSupported => { - let check_result = exception - == "TransactionException.TYPE_3_TX_INVALID_BLOB_VERSIONED_HASH" - || exception == "TR_BLOBVERSION_INVALID"; - assert!( - check_result, - "unexpected exception {exception:?} for BlobVersionNotSupported for test: [{spec:?}] {name}" - ); - } - InvalidTxReason::BlobCreateTransaction => { - let check_result = exception == "TR_BLOBCREATE" - || exception == "TransactionException.TYPE_3_TX_CONTRACT_CREATION"; - assert!( - check_result, - "unexpected exception {exception:?} for BlobCreateTransaction for test: [{spec:?}] {name}" - ); - } - InvalidTxReason::BlobGasPriceGreaterThanMax => { - let check_result = - exception == "TransactionException.INSUFFICIENT_MAX_FEE_PER_BLOB_GAS"; - assert!( - check_result, - "unexpected exception {exception:?} for BlobGasPriceGreaterThanMax for test: [{spec:?}] {name}" - ); - } - InvalidTxReason::TooManyBlobs => { - let check_result = exception == "TR_BLOBLIST_OVERSIZE" - || exception == "TransactionException.TYPE_3_TX_BLOB_COUNT_EXCEEDED" - || exception == "TransactionException.TYPE_3_TX_MAX_BLOB_GAS_ALLOWANCE_EXCEEDED|TransactionException.TYPE_3_TX_BLOB_COUNT_EXCEEDED"; - assert!( - check_result, - "unexpected exception {exception:?} for TooManyBlobs for test: [{spec:?}] {name}" - ); - } - InvalidTxReason::EmptyBlobs => { - let check_result = exception == "TransactionException.TYPE_3_TX_ZERO_BLOBS" - || exception == "TR_EMPTYBLOB"; - assert!( - check_result, - "unexpected exception {exception:?} for EmptyBlobs for test: [{spec:?}] {name}" - ); - } - InvalidTxReason::MaxFeePerBlobGasNotSupported => { - let check_result = - exception == "TransactionException.TYPE_3_TX_PRE_FORK|TransactionException.TYPE_3_TX_ZERO_BLOBS"; - assert!( - check_result, - "unexpected exception {exception:?} for MaxFeePerBlobGasNotSupported for test: [{spec:?}] {name}" - ); - } - InvalidTxReason::BlobVersionedHashesNotSupported => { - let check_result = exception == "TransactionException.TYPE_3_TX_PRE_FORK" - || exception == "TR_TypeNotSupportedBlob"; - assert!( - check_result, - "unexpected exception {exception:?} for BlobVersionedHashesNotSupported for test: [{spec:?}] {name}" - ); - } - InvalidTxReason::InvalidAuthorizationChain => { - let check_result = exception == "TransactionException.TYPE_4_INVALID_AUTHORIZATION_FORMAT"; - assert!( - check_result, - "unexpected exception {exception:?} for InvalidAuthorizationChain for test: [{spec:?}] {name}" - ); - } - InvalidTxReason::InvalidAuthorizationSignature => { - let check_result = exception == "TransactionException.TYPE_4_INVALID_AUTHORITY_SIGNATURE"; - assert!( - check_result, - "unexpected exception {exception:?} for InvalidAuthorizationSignature for test: [{spec:?}] {name}" - ); - } - InvalidTxReason::AuthorizationListNotExist => { - let check_result = exception == "TransactionException.TYPE_4_EMPTY_AUTHORIZATION_LIST" || exception == "TransactionException.TYPE_4_TX_CONTRACT_CREATION"; - assert!( - check_result, - "unexpected exception {exception:?} for AuthorizationListNotExist for test: [{spec:?}] {name}" - ); - } - InvalidTxReason::CreateTransaction => { - let check_result = exception == "TransactionException.TYPE_4_TX_CONTRACT_CREATION"; - assert!( - check_result, - "unexpected exception {exception:?} for CreateTransaction for test: [{spec:?}] {name}" - ); - } - InvalidTxReason::GasFloorMoreThanGasLimit => { - let check_result = exception == "TransactionException.INTRINSIC_GAS_TOO_LOW"; - assert!( - check_result, - "unexpected exception {exception:?} for GasFloorMoreThanGasLimit for test: [{spec:?}] {name}" - ); - } - _ => { - panic!( - "unexpected exception {exception:?} for reason {reason:?} for test: [{spec:?}] {name}" - ); - } - } - true - }, - ) -} - -#[allow(clippy::cognitive_complexity)] -fn test_run( - verbose_output: &VerboseOutput, - name: &str, - test: Test, - specific_spec: Option, - file_name: Arc, -) -> TestExecutionResult { - let mut tests_result = TestExecutionResult::new(); - let test_tx = &test.0.transaction; - for (spec, states) in &test.0.post_states { - // Run tests for specific SPEC (Hard fork) - if let Some(s) = specific_spec.as_ref() { - if s != spec { - continue; - } - } - - let (gasometer_config, delete_empty) = match spec { - ForkSpec::Istanbul => (Config::istanbul(), true), - ForkSpec::Berlin => (Config::berlin(), true), - ForkSpec::London => (Config::london(), true), - ForkSpec::Merge => (Config::merge(), true), - ForkSpec::Paris => (Config::merge(), true), - ForkSpec::Shanghai => (Config::shanghai(), true), - ForkSpec::Cancun => (Config::cancun(), true), - ForkSpec::Prague => (Config::prague(), true), - _ => { - continue; - } - }; - - // EIP-4844 - let blob_gas_price = - if let Some(current_excess_blob_gas) = test.0.env.current_excess_blob_gas { - Some(eip_4844::calc_blob_gas_price( - current_excess_blob_gas.0.as_u64(), - )) - } else if let (Some(parent_blob_gas_used), Some(parent_excess_blob_gas)) = ( - test.0.env.parent_blob_gas_used, - test.0.env.parent_excess_blob_gas, - ) { - let excess_blob_gas = eip_4844::calc_excess_blob_gas( - parent_blob_gas_used.0.as_u64(), - parent_excess_blob_gas.0.as_u64(), - ); - Some(eip_4844::calc_blob_gas_price(excess_blob_gas)) - } else { - None - }; - // EIP-4844 - let data_max_fee = if gasometer_config.has_shard_blob_transactions { - let max_fee_per_blob_gas = test_tx.max_fee_per_blob_gas.unwrap_or_default().0; - Some(eip_4844::calc_max_data_fee( - max_fee_per_blob_gas, - test_tx.blob_versioned_hashes.len(), - )) - } else { - None - }; - let data_fee = if gasometer_config.has_shard_blob_transactions { - Some(eip_4844::calc_data_fee( - blob_gas_price.expect("expect blob_gas_price"), - test_tx.blob_versioned_hashes.len(), - )) - } else { - None - }; - - let original_state = test.unwrap_to_pre_state(); - let vicinity = test.unwrap_to_vicinity(spec, blob_gas_price); - if let Err(tx_err) = vicinity { - tests_result.total += states.len() as u64; - let h = states.first().unwrap().hash.0; - // if vicinity could not be computed then the transaction was invalid so we simply - // check the original state and move on - let (is_valid_hash, actual_hash) = crate::utils::check_valid_hash(&h, &original_state); - if !is_valid_hash { - tests_result.failed_tests.push(FailedTestDetails { - expected_hash: h, - actual_hash, - index: 0, - name: String::from_str(name).unwrap(), - spec: spec.clone(), - state: original_state, - }); - if verbose_output.verbose_failed { - println!(" [{spec:?}] {name}: {tx_err:?} ... validation failed\t<----"); - } - tests_result.failed += 1; - continue; - } - assert_vicinity_validation(&tx_err, states, spec, name, file_name.clone()); - // As it's expected validation error - skip the test run - continue; - } - let vicinity = vicinity.unwrap(); - let caller = test.unwrap_caller(); - let caller_balance = original_state - .get(&caller) - .map_or_else(U256::zero, |acc| acc.balance); - // EIP-3607 - let caller_code = original_state - .get(&caller) - .map_or_else(Vec::new, |acc| acc.code.clone()); - // EIP-7702 - check if it's delegated designation. If it's delegation designation then - // even if `caller_code` is non-empty transaction should be executed. - let is_delegated = original_state - .get(&caller) - .is_some_and(|c| Authorization::is_delegated(&c.code)); - - for (i, state) in states.iter().enumerate() { - let transaction = test_tx.select(&state.indexes); - let mut backend = MemoryBackend::new(&vicinity, original_state.clone()); - tests_result.total += 1; - // Test case may be expected to fail with an unsupported tx type if the current fork is - // older than Berlin (see EIP-2718). However, this is not implemented in sputnik itself and rather - // in the code hosting sputnik. https://github.com/rust-blockchain/evm/pull/40 - let expect_tx_type_not_supported = matches!( - spec, - ForkSpec::EIP150 - | ForkSpec::EIP158 - | ForkSpec::Frontier - | ForkSpec::Homestead - | ForkSpec::Byzantium - | ForkSpec::Constantinople - | ForkSpec::ConstantinopleFix - | ForkSpec::Istanbul - | ForkSpec::Berlin - ) && TxType::from_txbytes(&state.txbytes) - != TxType::Legacy - && state.expect_exception.as_deref() == Some("TR_TypeNotSupported"); - if expect_tx_type_not_supported { - continue; - } - - let gas_limit: u64 = transaction.gas_limit.into(); - let data: Vec = transaction.data.clone().into(); - let valid_tx = crate::utils::transaction::validate( - &transaction, - test.0.env.gas_limit.0, - caller_balance, - &gasometer_config, - test_tx, - &vicinity, - blob_gas_price, - data_max_fee, - spec, - state, - ); - // Only execute valid transactions - if let Err(err) = &valid_tx { - if check_validate_exit_reason(err, &state.expect_exception, name, spec) { - continue; - } - } - let authorization_list = valid_tx.unwrap(); - - // We do not check overflow after TX validation - let total_fee = if let Some(data_fee) = data_fee { - vicinity.effective_gas_price * gas_limit + data_fee - } else { - vicinity.effective_gas_price * gas_limit - }; - - // Dump state transaction data - let mut state_tests_dump = StateTestsDump::default(); - state_tests_dump.set_state(&original_state); - state_tests_dump.set_caller_secret_key(test.unwrap_caller_secret_key()); - state_tests_dump.set_vicinity(&vicinity); - - let metadata = - StackSubstateMetadata::new(transaction.gas_limit.into(), &gasometer_config); - let executor_state = MemoryStackState::new(metadata, &backend); - let precompile = JsonPrecompile::precompile(spec).unwrap(); - let mut executor = - StackExecutor::new_with_precompiles(executor_state, &gasometer_config, &precompile); - executor.state_mut().withdraw(caller, total_fee).unwrap(); - - let access_list: Vec<(H160, Vec)> = transaction - .access_list - .into_iter() - .map(|(address, keys)| (address.0, keys.into_iter().map(|k| k.0).collect())) - .collect(); - - // EIP-3607: Reject transactions from senders with deployed code - // EIP-7702: Accept transaction even if caller has code. - if caller_code.is_empty() || is_delegated { - match transaction.to { - ethjson::maybe::MaybeEmpty::Some(to) => { - let value = transaction.value.into(); - - state_tests_dump.set_tx_data( - to.0, - value, - data.clone(), - gas_limit, - access_list.clone(), - ); - - // Exit reason for Call do not analyzed as it mostly do not expect exceptions - let _reason = executor.transact_call( - caller, - to.into(), - value, - data, - gas_limit, - access_list, - authorization_list, - ); - assert_call_exit_exception(&state.expect_exception, name); - } - ethjson::maybe::MaybeEmpty::None => { - let code = data; - let value = transaction.value.into(); - - let reason = - executor.transact_create(caller, value, code, gas_limit, access_list); - if check_create_exit_reason( - &reason.0, - &state.expect_exception, - &format!("{spec:?}-{name}-{i}"), - ) { - continue; - } - } - } - } else { - // According to EIP7702 - https://eips.ethereum.org/EIPS/eip-7702#transaction-origination: - // allow EOAs whose code is a valid delegation designation, i.e. `0xef0100 || address`, - // to continue to originate transactions. - #[allow(clippy::collapsible_if)] - if !(*spec >= ForkSpec::Prague - && TxType::from_txbytes(&state.txbytes) == TxType::EOAAccountCode) - { - assert_empty_create_caller(&state.expect_exception, name); - } - } - - let used_gas = executor.used_gas(); - if verbose_output.print_state { - println!("gas_limit: {gas_limit}\nused_gas: {used_gas}"); - } - - let actual_fee = executor.fee(vicinity.effective_gas_price); - // Forks after London burn miner rewards and thus have different gas fee - // calculation (see EIP-1559) - let miner_reward = if spec.is_eth2() { - let coinbase_gas_price = vicinity - .effective_gas_price - .saturating_sub(vicinity.block_base_fee_per_gas); - executor.fee(coinbase_gas_price) - } else { - actual_fee - }; - - executor - .state_mut() - .deposit(vicinity.block_coinbase, miner_reward); - - let amount_to_return_for_caller = data_fee.map_or_else( - || total_fee - actual_fee, - |data_fee| total_fee - actual_fee - data_fee, - ); - executor - .state_mut() - .deposit(caller, amount_to_return_for_caller); - - let (values, logs) = executor.into_state().deconstruct(); - - backend.apply(values, logs, delete_empty); - // It's special case for hard forks: London or before London - // According to EIP-160 empty account should be removed. But in that particular test - original test state - // contains account 0x03 (it's precompile), and when precompile 0x03 was called it exit with - // OutOfGas result. And after exit of substate account not marked as touched, as exit reason - // is not success. And it mean, that it don't appeared in Apply::Modify, then as untouched it - // can't be removed by backend.apply event. In that particular case we should manage it manually. - // NOTE: it's not realistic situation for real life flow. - if *spec <= ForkSpec::London && delete_empty && name == "failed_tx_xcf416c53" { - let state = backend.state_mut(); - state.retain(|addr, account| { - // Check is account empty for precompile 0x03 - !(addr == &H160::from_low_u64_be(3) - && account.balance == U256::zero() - && account.nonce == U256::zero() - && account.code.is_empty()) - }); - } - - let (is_valid_hash, actual_hash) = - crate::utils::check_valid_hash(&state.hash.0, backend.state()); - - if !is_valid_hash { - let failed_res = FailedTestDetails { - expected_hash: state.hash.0, - actual_hash, - index: i, - name: String::from_str(name).unwrap(), - spec: spec.clone(), - state: backend.state().clone(), - }; - tests_result.failed_tests.push(failed_res); - tests_result.failed += 1; - - if verbose_output.verbose_failed { - println!("\n[{spec:?}] {name}:{i} ... failed\t<----"); - } - - if verbose_output.print_state { - // Print detailed state data - println!( - "expected_hash:\t{:?}\nactual_hash:\t{actual_hash:?}", - state.hash.0, - ); - for (addr, acc) in backend.state().clone() { - // Decode balance - let balance = acc.balance.to_string(); - - println!( - "{addr:?}: {{\n balance: {balance}\n code: {:?}\n nonce: {}\n storage: {:#?}\n}}", - hex::encode(acc.code), - acc.nonce, - acc.storage - ); - } - if let Some(e) = state.expect_exception.as_ref() { - println!("-> expect_exception: {e}"); - } - } - } else if verbose_output.very_verbose && !verbose_output.verbose_failed { - println!(" [{spec:?}] {name}:{i} ... passed"); - } - - state_tests_dump.set_used_gas(used_gas); - state_tests_dump.set_state_hash(actual_hash); - state_tests_dump.set_result_state(backend.state()); - state_tests_dump.dump_to_file(spec); - } - } - tests_result -} - -/// Denotes the type of transaction. -#[derive(Debug, PartialEq, Eq)] -pub enum TxType { - /// All transactions before EIP-2718 are legacy. - Legacy, - /// https://eips.ethereum.org/EIPS/eip-2718 - AccessList, - /// https://eips.ethereum.org/EIPS/eip-1559 - DynamicFee, - /// https://eips.ethereum.org/EIPS/eip-4844 - ShardBlob, - /// https://eips.ethereum.org/EIPS/eip-7702 - EOAAccountCode, -} - -impl TxType { - /// Whether this is a legacy, access list, dynamic fee, etc transaction - // Taken from geth's core/types/transaction.go/UnmarshalBinary, but we only detect the transaction - // type rather than unmarshal the entire payload. - pub const fn from_txbytes(txbytes: &[u8]) -> Self { - match txbytes[0] { - b if b > 0x7f => Self::Legacy, - 1 => Self::AccessList, - 2 => Self::DynamicFee, - 3 => Self::ShardBlob, - 4 => Self::EOAAccountCode, - _ => panic!( - "Unknown tx type. \ -You may need to update the TxType enum if Ethereum introduced new enveloped transaction types." - ), - } - } -} diff --git a/evm-tests/jsontests/src/utils.rs b/evm-tests/jsontests/src/utils.rs deleted file mode 100644 index 5bb28d29..00000000 --- a/evm-tests/jsontests/src/utils.rs +++ /dev/null @@ -1,659 +0,0 @@ -use aurora_evm::backend::MemoryAccount; -use aurora_evm::ExitError; -use primitive_types::{H160, H256, U256}; -use sha3::{Digest, Keccak256}; -use std::borrow::Cow; -use std::collections::BTreeMap; - -pub fn u256_to_h256(u: U256) -> H256 { - H256(u.to_big_endian()) -} - -pub fn unwrap_to_account(s: ðjson::spec::Account) -> MemoryAccount { - MemoryAccount { - balance: s.balance.unwrap().into(), - nonce: s.nonce.unwrap().0, - code: s.code.clone().unwrap().into(), - storage: s - .storage - .as_ref() - .unwrap() - .iter() - .filter_map(|(k, v)| { - if v.0.is_zero() { - // If value is zero then the key is not really there - None - } else { - Some((u256_to_h256((*k).into()), u256_to_h256((*v).into()))) - } - }) - .collect(), - } -} - -pub fn unwrap_to_state(a: ðjson::spec::State) -> BTreeMap { - match &a.0 { - ethjson::spec::HashOrMap::Map(m) => m - .iter() - .map(|(k, v)| ((*k).into(), unwrap_to_account(v))) - .collect(), - ethjson::spec::HashOrMap::Hash(_) => panic!("Hash can not be converted."), - } -} - -/// Basic account type. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct TrieAccount { - /// Nonce of the account. - pub nonce: U256, - /// Balance of the account. - pub balance: U256, - /// Storage root of the account. - pub storage_root: H256, - /// Code hash of the account. - pub code_hash: H256, - /// Code version of the account. - pub code_version: U256, -} - -impl rlp::Encodable for TrieAccount { - fn rlp_append(&self, stream: &mut rlp::RlpStream) { - let use_short_version = self.code_version == U256::zero(); - - match use_short_version { - true => { - stream.begin_list(4); - } - false => { - stream.begin_list(5); - } - } - - stream.append(&self.nonce); - stream.append(&self.balance); - stream.append(&self.storage_root); - stream.append(&self.code_hash); - - if !use_short_version { - stream.append(&self.code_version); - } - } -} - -impl rlp::Decodable for TrieAccount { - fn decode(rlp: &rlp::Rlp) -> Result { - let use_short_version = match rlp.item_count()? { - 4 => true, - 5 => false, - _ => return Err(rlp::DecoderError::RlpIncorrectListLen), - }; - - Ok(Self { - nonce: rlp.val_at(0)?, - balance: rlp.val_at(1)?, - storage_root: rlp.val_at(2)?, - code_hash: rlp.val_at(3)?, - code_version: if use_short_version { - U256::zero() - } else { - rlp.val_at(4)? - }, - }) - } -} - -pub fn check_valid_hash(h: &H256, b: &BTreeMap) -> (bool, H256) { - let tree = b - .iter() - .map(|(address, account)| { - let storage_root = H256( - ethereum::util::sec_trie_root( - account - .storage - .iter() - .map(|(k, v)| (k, rlp::encode(&U256::from_big_endian(&v[..])))), - ) - .0, - ); - let code_hash = H256::from_slice(Keccak256::digest(&account.code).as_slice()); - - let account = TrieAccount { - nonce: account.nonce, - balance: account.balance, - storage_root, - code_hash, - code_version: U256::zero(), - }; - (address, rlp::encode(&account)) - }) - .collect::>(); - - let root = H256(ethereum::util::sec_trie_root(tree).0); - let expect = h; - (root == *expect, root) -} - -pub fn flush() { - use std::io::{self, Write}; - - io::stdout().flush().expect("Could not flush stdout"); -} - -/// EIP-7623 form Prague hard fork -pub mod eip7623 { - /// The standard cost of calldata token. - pub const STANDARD_TOKEN_COST: usize = 4; - /// The cost of a non-zero byte in calldata adjusted by [EIP-2028](https://eips.ethereum.org/EIPS/eip-2028). - pub const NON_ZERO_BYTE_DATA_COST: usize = 16; - /// The multiplier for a non zero byte in calldata adjusted by [EIP-2028](https://eips.ethereum.org/EIPS/eip-2028). - pub const NON_ZERO_BYTE_MULTIPLIER: usize = NON_ZERO_BYTE_DATA_COST / STANDARD_TOKEN_COST; - // The cost floor per token - pub const TOTAL_COST_FLOOR_PER_TOKEN: u64 = 10; - - /// Retrieve the total number of tokens in calldata. - #[must_use] - pub fn get_tokens_in_calldata(input: &[u8]) -> u64 { - let zero_data_len = input.iter().filter(|v| **v == 0).count(); - let non_zero_data_len = input.len() - zero_data_len; - u64::try_from(zero_data_len + non_zero_data_len * NON_ZERO_BYTE_MULTIPLIER).unwrap() - } - - /// Calculate the transaction cost floor as specified in EIP-7623. - #[must_use] - pub const fn calc_tx_floor_cost(tokens_in_calldata: u64) -> u64 { - tokens_in_calldata * TOTAL_COST_FLOOR_PER_TOKEN + 21_000 - } -} - -/// EIP-7702 -pub mod eip7702 { - use super::{Digest, Keccak256, H160, H256, U256}; - use aurora_evm::ExitError; - use rlp::RlpStream; - - pub const MAGIC: u8 = 0x5; - /// The order of the secp256k1 curve, divided by two. Signatures that should be checked according - /// to EIP-2 should have an S value less than or equal to this. - /// - /// `57896044618658097711785492504343953926418782139537452191302581570759080747168` - pub const SECP256K1N_HALF: U256 = U256([ - 0xDFE92F46681B20A0, - 0x5D576E7357A4501D, - 0xFFFFFFFFFFFFFFFF, - 0x7FFFFFFFFFFFFFFF, - ]); - - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Authorization { - pub chain_id: U256, - pub address: H160, - pub nonce: u64, - } - - impl Authorization { - #[must_use] - pub const fn new(chain_id: U256, address: H160, nonce: u64) -> Self { - Self { - chain_id, - address, - nonce, - } - } - - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(3); - s.append(&self.chain_id); - s.append(&self.address); - s.append(&self.nonce); - } - - pub fn signature_hash(&self) -> H256 { - let mut rlp_stream = RlpStream::new(); - rlp_stream.append(&MAGIC); - self.rlp_append(&mut rlp_stream); - H256::from_slice(Keccak256::digest(rlp_stream.as_raw()).as_slice()) - } - } - - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct SignedAuthorization { - chain_id: U256, - address: H160, - nonce: u64, - v: bool, - r: U256, - s: U256, - } - - impl SignedAuthorization { - #[must_use] - pub const fn new( - chain_id: U256, - address: H160, - nonce: u64, - r: U256, - s: U256, - v: bool, - ) -> Self { - Self { - chain_id, - address, - nonce, - s, - r, - v, - } - } - - pub fn recover_address(&self) -> Result { - let auth = Authorization::new(self.chain_id, self.address, self.nonce).signature_hash(); - super::ecrecover(auth, &super::vrs_to_arr(self.v, self.r, self.s)) - } - } -} - -/// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 -pub mod eip_4844 { - use super::U256; - - /// EIP-4844 constants - /// Gas consumption of a single data blob (== blob byte size). - pub const GAS_PER_BLOB: u64 = 1 << 17; - /// Max number of blobs per block: EIP-7691 - pub const MAX_BLOBS_PER_BLOCK_ELECTRA: u64 = 9; - pub const MAX_BLOBS_PER_BLOCK_CANCUN: u64 = 6; - /// Target consumable blob gas for data blobs per block: EIP-7691 - pub const TARGET_BLOB_GAS_PER_BLOCK: u64 = 786432; - /// Minimum gas price for data blobs. - pub const MIN_BLOB_GASPRICE: u64 = 1; - /// Controls the maximum rate of change for blob gas price. - pub const BLOB_GASPRICE_UPDATE_FRACTION: u64 = 3338477; - /// First version of the blob. - pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01; - - /// Calculates the `excess_blob_gas` from the parent header's `blob_gas_used` and `excess_blob_gas`. - /// - /// See also [the EIP-4844 helpers] - /// (`calc_excess_blob_gas`). - #[inline] - pub const fn calc_excess_blob_gas( - parent_excess_blob_gas: u64, - parent_blob_gas_used: u64, - ) -> u64 { - (parent_excess_blob_gas + parent_blob_gas_used).saturating_sub(TARGET_BLOB_GAS_PER_BLOCK) - } - - /// Calculates the blob gas price from the header's excess blob gas field. - /// - /// See also [the EIP-4844 helpers](https://eips.ethereum.org/EIPS/eip-4844#helpers) - /// (`get_blob_gasprice`). - #[inline] - pub fn calc_blob_gas_price(excess_blob_gas: u64) -> u128 { - fake_exponential( - MIN_BLOB_GASPRICE, - excess_blob_gas, - BLOB_GASPRICE_UPDATE_FRACTION, - ) - } - - /// See [EIP-4844], [`calc_max_data_fee`] - /// - /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 - #[inline] - pub const fn get_total_blob_gas(blob_hashes_len: usize) -> u64 { - GAS_PER_BLOB * blob_hashes_len as u64 - } - - /// Calculates the [EIP-4844] `data_fee` of the transaction. - /// - /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 - #[inline] - pub fn calc_max_data_fee(max_fee_per_blob_gas: U256, blob_hashes_len: usize) -> U256 { - max_fee_per_blob_gas.saturating_mul(U256::from(get_total_blob_gas(blob_hashes_len))) - } - - /// Calculates the [EIP-4844] `data_fee` of the transaction. - /// - /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 - #[inline] - pub fn calc_data_fee(blob_gas_price: u128, blob_hashes_len: usize) -> U256 { - U256::from(blob_gas_price).saturating_mul(U256::from(get_total_blob_gas(blob_hashes_len))) - } - - /// Approximates `factor * e ** (numerator / denominator)` using Taylor expansion. - /// - /// This is used to calculate the blob price. - /// - /// See also [the EIP-4844 helpers](https://eips.ethereum.org/EIPS/eip-4844#helpers) - /// (`fake_exponential`). - /// - /// # Panics - /// - /// This function panics if `denominator` is zero. - /// - /// # NOTES - /// PLEASE DO NOT USE IN PRODUCTION as not checked overflow. For tests only. - #[inline] - pub fn fake_exponential(factor: u64, numerator: u64, denominator: u64) -> u128 { - assert_ne!(denominator, 0, "attempt to divide by zero"); - let factor = factor as u128; - let numerator = numerator as u128; - let denominator = denominator as u128; - - let mut i = 1; - let mut output = 0; - let mut numerator_accum = factor * denominator; - while numerator_accum > 0 { - output += numerator_accum; - - // Denominator is asserted as not zero at the start of the function. - numerator_accum = (numerator_accum * numerator) / (denominator * i); - i += 1; - } - output / denominator - } -} - -pub mod transaction { - use crate::state::TxType; - use crate::utils::{eip7623, eip7702}; - use aurora_evm::backend::MemoryVicinity; - use aurora_evm::executor::stack::Authorization; - use aurora_evm::gasometer::{self, Gasometer}; - use ethjson::hash::Address; - use ethjson::maybe::MaybeEmpty; - use ethjson::spec::ForkSpec; - use ethjson::test_helpers::state::{MultiTransaction, PostStateResult}; - use ethjson::transaction::Transaction; - use ethjson::uint::Uint; - use primitive_types::{H160, H256, U256}; - - // TODO: it will be refactored as old solution inefficient, also will be removed clippy-allow flag - #[allow(clippy::too_many_arguments)] - pub fn validate( - tx: &Transaction, - block_gas_limit: U256, - caller_balance: U256, - config: &aurora_evm::Config, - test_tx: &MultiTransaction, - vicinity: &MemoryVicinity, - blob_gas_price: Option, - data_fee: Option, - spec: &ForkSpec, - tx_state: &PostStateResult, - ) -> Result, InvalidTxReason> { - let mut authorization_list: Vec = vec![]; - match intrinsic_gas(tx, config) { - None => return Err(InvalidTxReason::IntrinsicGas), - Some(required_gas) => { - if tx.gas_limit < Uint(U256::from(required_gas)) { - return Err(InvalidTxReason::IntrinsicGas); - } - } - } - - if block_gas_limit < tx.gas_limit.0 { - return Err(InvalidTxReason::GasLimitReached); - } - - let required_funds = tx - .gas_limit - .0 - .checked_mul(vicinity.gas_price) - .ok_or(InvalidTxReason::OutOfFund)? - .checked_add(tx.value.0) - .ok_or(InvalidTxReason::OutOfFund)?; - - let required_funds = if let Some(data_fee) = data_fee { - required_funds - .checked_add(data_fee) - .ok_or(InvalidTxReason::OutOfFund)? - } else { - required_funds - }; - if caller_balance < required_funds { - return Err(InvalidTxReason::OutOfFund); - } - - // CANCUN tx validation - // Presence of max_fee_per_blob_gas means that this is blob transaction. - if *spec >= ForkSpec::Cancun { - if let Some(max) = test_tx.max_fee_per_blob_gas { - // ensure that the user was willing to at least pay the current blob gasprice - if U256::from(blob_gas_price.expect("expect blob_gas_price")) > max.0 { - return Err(InvalidTxReason::BlobGasPriceGreaterThanMax); - } - - // there must be at least one blob - if test_tx.blob_versioned_hashes.is_empty() { - return Err(InvalidTxReason::EmptyBlobs); - } - - // The field `to` deviates slightly from the semantics with the exception - // that it MUST NOT be nil and therefore must always represent - // a 20-byte address. This means that blob transactions cannot - // have the form of a create transaction. - let to_address: Option
= test_tx.to.clone().into(); - if to_address.is_none() { - return Err(InvalidTxReason::BlobCreateTransaction); - } - - // all versioned blob hashes must start with VERSIONED_HASH_VERSION_KZG - for blob in test_tx.blob_versioned_hashes.iter() { - let blob_hash = H256(blob.to_big_endian()); - if blob_hash[0] != super::eip_4844::VERSIONED_HASH_VERSION_KZG { - return Err(InvalidTxReason::BlobVersionNotSupported); - } - } - - // ensure the total blob gas spent is at most equal to the limit - // assert blob_gas_used <= MAX_BLOB_GAS_PER_BLOCK - // EIP-7691 - let max_blob_len = if *spec == ForkSpec::Cancun { - super::eip_4844::MAX_BLOBS_PER_BLOCK_CANCUN - } else { - super::eip_4844::MAX_BLOBS_PER_BLOCK_ELECTRA - }; - if test_tx.blob_versioned_hashes.len() > max_blob_len as usize { - return Err(InvalidTxReason::TooManyBlobs); - } - } - } else { - if !test_tx.blob_versioned_hashes.is_empty() { - return Err(InvalidTxReason::BlobVersionedHashesNotSupported); - } - if test_tx.max_fee_per_blob_gas.is_some() { - return Err(InvalidTxReason::MaxFeePerBlobGasNotSupported); - } - } - - if *spec >= ForkSpec::Prague { - // EIP-7623 validation - let floor_gas = eip7623::calc_tx_floor_cost(eip7623::get_tokens_in_calldata(&tx.data)); - if floor_gas > tx.gas_limit.into() { - return Err(InvalidTxReason::GasFloorMoreThanGasLimit); - } - - // EIP-7702 - if transaction type is EOAAccountCode then - // `authorization_list` must be present - if TxType::from_txbytes(&tx_state.txbytes) == TxType::EOAAccountCode - && test_tx.authorization_list.is_empty() - { - return Err(InvalidTxReason::AuthorizationListNotExist); - } - - // Check EIP-7702 Spec validation steps: 1 and 2 - // Other validation step inside EVM transact logic. - for auth in test_tx.authorization_list.iter() { - // 1. Verify the chain id is either 0 or the chain’s current ID. - let mut is_valid = auth.chain_id.0 <= U256::from(u64::MAX) - && (auth.chain_id.0 == U256::from(0) || auth.chain_id.0 == vicinity.chain_id); - - // 3. `authority = ecrecover(keccak(MAGIC || rlp([chain_id, address, nonce])), y_parity, r, s]` - // Validate the signature, as in tests it is possible to have invalid signatures values. - // Value `v` shouldn't be greater then 1 - let v = auth.v.0; - if v > U256::from(1) { - is_valid = false; - } - - // EIP-2 validation - if auth.s.0 > eip7702::SECP256K1N_HALF { - is_valid = false; - } - - let auth_address = eip7702::SignedAuthorization::new( - auth.chain_id.0, - auth.address.0, - auth.nonce.0.as_u64(), - auth.r.0, - auth.s.0, - auth.v.0.as_u32() > 0, - ) - .recover_address(); - let auth_address = auth_address.unwrap_or_else(|_| { - is_valid = false; - H160::zero() - }); - - authorization_list.push(Authorization { - authority: auth_address, - address: auth.address.0, - nonce: auth.nonce.0.as_u64(), - is_valid, - }); - } - } else if !test_tx.authorization_list.is_empty() { - return Err(InvalidTxReason::AuthorizationListNotSupported); - } - Ok(authorization_list) - } - - fn intrinsic_gas(tx: &Transaction, config: &aurora_evm::Config) -> Option { - let is_contract_creation = match tx.to { - MaybeEmpty::None => true, - MaybeEmpty::Some(_) => false, - }; - let data = &tx.data; - let access_list: Vec<(H160, Vec)> = tx - .access_list - .iter() - .map(|(a, s)| (a.0, s.iter().map(|h| h.0).collect())) - .collect(); - - // EIP-7702 - let authorization_list_len = tx.authorization_list.len(); - - let cost = if is_contract_creation { - gasometer::create_transaction_cost(data, &access_list) - } else { - gasometer::call_transaction_cost(data, &access_list, authorization_list_len) - }; - - let mut g = Gasometer::new(u64::MAX, config); - g.record_transaction(cost).ok()?; - - Some(g.total_used_gas()) - } - - #[derive(Debug)] - pub enum InvalidTxReason { - IntrinsicGas, - OutOfFund, - GasLimitReached, - PriorityFeeTooLarge, - GasPriceLessThenBlockBaseFee, - BlobCreateTransaction, - BlobVersionNotSupported, - TooManyBlobs, - EmptyBlobs, - BlobGasPriceGreaterThanMax, - BlobVersionedHashesNotSupported, - MaxFeePerBlobGasNotSupported, - GasPriseEip1559, - AuthorizationListNotExist, - AuthorizationListNotSupported, - InvalidAuthorizationChain, - InvalidAuthorizationSignature, - CreateTransaction, - GasFloorMoreThanGasLimit, - } -} - -fn ecrecover(hash: H256, signature: &[u8]) -> Result { - let hash = libsecp256k1::Message::parse_slice(hash.as_bytes()) - .map_err(|e| ExitError::Other(Cow::from(e.to_string())))?; - let v = signature[64]; - let signature = libsecp256k1::Signature::parse_standard_slice(&signature[0..64]) - .map_err(|e| ExitError::Other(Cow::from(e.to_string())))?; - let bit = match v { - 0..=26 => v, - _ => v - 27, - }; - - if let Ok(recovery_id) = libsecp256k1::RecoveryId::parse(bit) { - if let Ok(public_key) = libsecp256k1::recover(&hash, &signature, &recovery_id) { - // recover returns a 65-byte key, but addresses come from the raw 64-byte key - let r = sha3::Keccak256::digest(&public_key.serialize()[1..]); - return Ok(H160::from_slice(&r[12..])); - } - } - - Err(ExitError::Other(Cow::from("ECRecoverErr unknown error"))) -} - -/// v, r, s signature values to array -fn vrs_to_arr(v: bool, r: U256, s: U256) -> [u8; 65] { - let mut result = [0u8; 65]; // (r, s, v), typed (uint256, uint256, uint8) - result[..32].copy_from_slice(&r.to_big_endian()); - result[32..64].copy_from_slice(&s.to_big_endian()); - result[64] = u8::from(v); - result -} - -#[cfg(test)] -mod tests { - use super::*; - use hex_literal::hex; - use primitive_types::H160; - - #[test] - fn test_ecrecover_success() { - let hash = H256::from_slice(&hex!( - "47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad" - )); - let signature = hex!("650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e031b"); - let expected_address = H160::from_slice(&hex!("c08b5542d177ac6686946920409741463a15dddb")); - - let result = ecrecover(hash, &signature).expect("ecrecover should succeed"); - assert_eq!(result, expected_address); - } - - #[test] - fn test_ecrecover_invalid_signature() { - let hash = H256::from_slice(&hex!( - "47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad" - )); - let signature = hex!("00650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e031c"); - - let result = ecrecover(hash, &signature); - assert_eq!( - result, - Err(ExitError::Other(Cow::from("ECRecoverErr unknown error"))) - ); - } - - #[test] - fn test_ecrecover_invalid_recovery_id() { - let hash = H256::from_slice(&hex!( - "47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad" - )); - let signature = hex!("650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e0327"); - - let result = ecrecover(hash, &signature); - assert_eq!( - result, - Err(ExitError::Other(Cow::from("ECRecoverErr unknown error"))) - ); - } -} diff --git a/evm-tests/jsontests/src/vm.rs b/evm-tests/jsontests/src/vm.rs deleted file mode 100644 index 51a4bb9a..00000000 --- a/evm-tests/jsontests/src/vm.rs +++ /dev/null @@ -1,180 +0,0 @@ -use crate::state::{TestExecutionResult, VerboseOutput}; -use aurora_evm::backend::{ApplyBackend, MemoryAccount, MemoryBackend, MemoryVicinity}; -use aurora_evm::executor::stack::{MemoryStackState, StackExecutor, StackSubstateMetadata}; -use aurora_evm::Config; -use primitive_types::{H160, H256, U256}; -use serde::Deserialize; -use std::collections::BTreeMap; -use std::rc::Rc; - -#[derive(Deserialize, Debug)] -pub struct Test(ethjson::vm::Vm); - -impl Test { - fn unwrap_to_pre_state(&self) -> BTreeMap { - crate::utils::unwrap_to_state(&self.0.pre_state) - } - - 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. - H256(r.0.to_big_endian()) - }); - - MemoryVicinity { - gas_price: self.0.transaction.gas_price.into(), - effective_gas_price: self.0.transaction.gas_price.into(), - origin: self.0.transaction.origin.into(), - block_hashes: Vec::new(), - 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::zero(), - block_base_fee_per_gas: self.0.transaction.gas_price.into(), - block_randomness, - blob_gas_price: None, - blob_hashes: Vec::new(), - } - } - - fn unwrap_to_code(&self) -> Rc> { - Rc::new(self.0.transaction.code.clone().into()) - } - - fn unwrap_to_data(&self) -> Rc> { - Rc::new(self.0.transaction.data.clone().into()) - } - - fn unwrap_to_context(&self) -> aurora_evm::Context { - aurora_evm::Context { - address: self.0.transaction.address.into(), - caller: self.0.transaction.sender.into(), - apparent_value: self.0.transaction.value.into(), - } - } - - fn unwrap_to_return_value(&self) -> Vec { - self.0.output.clone().unwrap().into() - } - - fn unwrap_to_gas_limit(&self) -> u64 { - self.0.transaction.gas.into() - } - - fn unwrap_to_post_gas(&self) -> u64 { - self.0.gas_left.unwrap().into() - } - - fn check_valid_state(&self, b: &BTreeMap) -> bool { - let post_state = self.0.post_state.as_ref().unwrap(); - match &post_state.0 { - ethjson::spec::HashOrMap::Map(m) => { - &m.iter() - .map(|(k, v)| ((*k).into(), crate::utils::unwrap_to_account(v))) - .collect::>() - == b - } - ethjson::spec::HashOrMap::Hash(h) => { - let x = crate::utils::check_valid_hash(&(*h).into(), b); - !x.0 - } - } - } -} - -pub fn test(verbose_output: &VerboseOutput, name: &str, test: Test) -> TestExecutionResult { - let mut result = TestExecutionResult::new(); - let mut failed = false; - result.total = 1; - if verbose_output.verbose { - print!("Running test {} ... ", name); - crate::utils::flush(); - } - - let original_state = test.unwrap_to_pre_state(); - let vicinity = test.unwrap_to_vicinity(); - let config = Config::frontier(); - let mut backend = MemoryBackend::new(&vicinity, original_state); - let metadata = StackSubstateMetadata::new(test.unwrap_to_gas_limit(), &config); - let state = MemoryStackState::new(metadata, &backend); - let precompile = BTreeMap::new(); - let mut executor = StackExecutor::new_with_precompiles(state, &config, &precompile); - - let code = test.unwrap_to_code(); - let data = test.unwrap_to_data(); - let context = test.unwrap_to_context(); - let mut runtime = - aurora_evm::Runtime::new(code, data, context, config.stack_limit, config.memory_limit); - - let reason = executor.execute(&mut runtime); - let gas = executor.gas(); - let (values, logs) = executor.into_state().deconstruct(); - backend.apply(values, logs, false); - - if test.0.output.is_none() { - if verbose_output.verbose { - print!("{:?} ", reason); - } - - if reason.is_succeed() { - failed = true; - if verbose_output.verbose_failed { - print!("[Failed: succeed for empty output: {:?}] ", reason); - } - } - if !(test.0.post_state.is_none() && test.0.gas_left.is_none()) { - failed = true; - if verbose_output.verbose_failed { - print!( - "[Failed: not empty state and left gas for empty output: {:?}] ", - reason - ); - } - } - } else { - let expected_post_gas = test.unwrap_to_post_gas(); - if verbose_output.verbose { - print!("{:?} ", reason); - } - - if runtime.machine().return_value() != test.unwrap_to_return_value() { - failed = true; - if verbose_output.verbose_failed { - print!( - "[Failed: wrong return value: {:?}] ", - runtime.machine().return_value() - ); - } - } - if !test.check_valid_state(backend.state()) { - failed = true; - if verbose_output.verbose_failed { - print!("[Failed: invalid state] "); - } - } - if gas != expected_post_gas { - failed = true; - if verbose_output.verbose_failed { - print!("[Failed: unexpected gas: {:?}] ", gas); - } - } - } - - if failed { - result.failed += 1; - if verbose_output.verbose || verbose_output.verbose_failed { - println!("failed <-------"); - } - } else if verbose_output.verbose { - println!("succeed"); - } - result -} diff --git a/evm-tests/src/assertions.rs b/evm-tests/src/assertions.rs new file mode 100644 index 00000000..76c66601 --- /dev/null +++ b/evm-tests/src/assertions.rs @@ -0,0 +1,419 @@ +use crate::config::TestConfig; +use crate::types::Spec; +use crate::types::{InvalidTxReason, PostState}; +use aurora_evm::{ExitError, ExitReason}; + +/// Assert vicinity validation to ensure that the test expected validation error +#[allow(clippy::cognitive_complexity, clippy::too_many_lines)] +pub fn assert_vicinity_validation( + reason: &InvalidTxReason, + states: &[PostState], + spec: &Spec, + test_config: &TestConfig, +) { + let name = &test_config.name; + let file_name = &test_config.file_name; + match *spec { + Spec::Istanbul | Spec::Berlin => match reason { + InvalidTxReason::GasPriceEip1559 => { + for (i, state) in states.iter().enumerate() { + let expected = state.expect_exception.as_deref().unwrap_or_else(|| { + panic!( + "expected error message for test: [{spec:?}] {name}:{i}\n{file_name:?}" + ) + }); + + let is_checked = + expected == "TR_TypeNotSupported" || expected == "TR_TypeNotSupportedBlob"; + assert!( + is_checked, + "unexpected error message {expected:?} for: [{spec:?}] {name}:{i}\n{file_name:?}", + ); + } + } + _ => panic!("Unexpected validation reason: {reason:?} [{name}]\n{file_name:?}"), + }, + Spec::London => match reason { + InvalidTxReason::PriorityFeeTooLarge => { + for (i, state) in states.iter().enumerate() { + let expected = state.expect_exception.as_deref().unwrap_or_else(|| { + panic!("expected error message for test: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}") + }); + let is_checked = expected == "tipTooHigh" || expected == "TR_TipGtFeeCap"; + assert!( + is_checked, + "unexpected error message {expected:?} for: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}", + ); + } + } + InvalidTxReason::GasPriceLessThanBlockBaseFee => { + for (i, state) in states.iter().enumerate() { + let expected = state.expect_exception.as_deref().unwrap_or_else(|| { + panic!("expected error message for test: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}") + }); + let is_checked = + expected == "lowFeeCap" || expected == "TR_FeeCapLessThanBlocks"; + assert!( + is_checked, + "unexpected error message {expected:?} for: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}", + ); + } + } + _ => { + panic!("Unexpected validation reason: {reason:?} [{spec:?}] {name}\n{file_name:?}") + } + }, + Spec::Merge => match reason { + InvalidTxReason::PriorityFeeTooLarge => { + for (i, state) in states.iter().enumerate() { + let expected = state.expect_exception.as_deref().unwrap_or_else(|| { + panic!("expected error message for test: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}") + }); + let is_checked = expected == "TR_TipGtFeeCap"; + assert!( + is_checked, + "unexpected error message {expected:?} for: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}", + ); + } + } + InvalidTxReason::GasPriceLessThanBlockBaseFee => { + for (i, state) in states.iter().enumerate() { + let expected = state.expect_exception.as_deref().unwrap_or_else(|| { + panic!("expected error message for test: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}") + }); + let is_checked = expected == "TR_FeeCapLessThanBlocks"; + assert!( + is_checked, + "unexpected error message {expected:?} for: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}", + ); + } + } + _ => { + panic!("Unexpected validation reason: {reason:?} [{spec:?}] {name}\n{file_name:?}") + } + }, + Spec::Shanghai => match reason { + InvalidTxReason::PriorityFeeTooLarge => { + for (i, state) in states.iter().enumerate() { + let expected = state.expect_exception.as_deref().unwrap_or_else(|| { + panic!("expected error message for test: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}") + }); + let is_checked = expected == "TR_TipGtFeeCap"; + assert!( + is_checked, + "unexpected error message {expected:?} for: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}", + ); + } + } + InvalidTxReason::GasPriceLessThanBlockBaseFee => { + for (i, state) in states.iter().enumerate() { + let expected = state.expect_exception.as_deref().unwrap_or_else(|| { + panic!("expected error message for test: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}") + }); + + let is_checked = expected == "TR_FeeCapLessThanBlocks"; + assert!( + is_checked, + "unexpected error message {expected:?} for: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}", + ); + } + } + _ => { + panic!("Unexpected validation reason: {reason:?} [{spec:?}] {name}\n{file_name:?}") + } + }, + Spec::Cancun => match reason { + InvalidTxReason::PriorityFeeTooLarge => { + for (i, state) in states.iter().enumerate() { + let expected = state.expect_exception.as_deref().unwrap_or_else(|| { + panic!("expected error message for test: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}") + }); + + let is_checked = expected == "TR_TipGtFeeCap" + || expected == "TransactionException.PRIORITY_GREATER_THAN_MAX_FEE_PER_GAS"; + assert!( + is_checked, + "unexpected error message {expected:?} for: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}", + ); + } + } + InvalidTxReason::GasPriceLessThanBlockBaseFee => { + for (i, state) in states.iter().enumerate() { + let expected = state.expect_exception.as_deref().unwrap_or_else(|| { + panic!("expected error message for test: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}") + }); + + let is_checked = expected == "TR_FeeCapLessThanBlocks" + || expected == "TransactionException.INSUFFICIENT_MAX_FEE_PER_GAS"; + assert!( + is_checked, + "unexpected error message {expected:?} for: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}", + ); + } + } + _ => { + panic!("Unexpected validation reason: {reason:?} [{spec:?}] {name}\n{file_name:?}") + } + }, + Spec::Prague => match reason { + InvalidTxReason::PriorityFeeTooLarge => { + for (i, state) in states.iter().enumerate() { + let expected = state.expect_exception.as_deref().unwrap_or_else(|| { + panic!("expected error message for test: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}") + }); + + let is_checked = + expected == "TransactionException.PRIORITY_GREATER_THAN_MAX_FEE_PER_GAS"; + assert!( + is_checked, + "unexpected error message {expected:?} for: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}", + ); + } + } + + InvalidTxReason::GasPriceLessThanBlockBaseFee => { + for (i, state) in states.iter().enumerate() { + let expected = state.expect_exception.as_deref().unwrap_or_else(|| { + panic!("expected error message for test: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}") + }); + let is_checked = expected == "TR_FeeCapLessThanBlocks" + || expected == "TransactionException.INSUFFICIENT_MAX_FEE_PER_GAS"; + assert!( + is_checked, + "unexpected error message {expected:?} for: {reason:?} [{spec:?}] {name}:{i}\n{file_name:?}", + ); + } + } + _ => { + panic!("Unexpected validation reason: {reason:?} [{spec:?}] {name}\n{file_name:?}") + } + }, + _ => panic!("Unexpected validation reason: {reason:?} [{spec:?}] {name}\n{file_name:?}"), + } +} + +/// Check Exit Reason of EVM execution +#[allow(clippy::too_many_lines)] +pub fn check_validate_exit_reason( + reason: &InvalidTxReason, + expect_exception: Option<&String>, + name: &str, + spec: &Spec, +) -> bool { + expect_exception.map_or_else( + || { + panic!("unexpected validation error reason: {reason:?} {name}"); + }, + |exception| { + match reason { + InvalidTxReason::OutOfFund => { + let check_result = exception + == "TransactionException.INSUFFICIENT_ACCOUNT_FUNDS" + || exception == "TR_NoFunds" + || exception == "TR_NoFundsX" + || exception == "TransactionException.INSUFFICIENT_MAX_FEE_PER_BLOB_GAS" + || exception == "TransactionException.INSUFFICIENT_ACCOUNT_FUNDS|TransactionException.GASLIMIT_PRICE_PRODUCT_OVERFLOW"; + assert!( + check_result, + "unexpected exception {exception:?} for OutOfFund for test: [{spec:?}] {name}" + ); + } + InvalidTxReason::GasLimitReached => { + let check_result = exception == "TR_GasLimitReached" + || exception == "TransactionException.GAS_ALLOWANCE_EXCEEDED"; + assert!( + check_result, + "unexpected exception {exception:?} for GasLimitReached for test: [{spec:?}] {name}" + ); + } + InvalidTxReason::IntrinsicGas => { + let check_result = exception == "TR_NoFundsOrGas" + || exception == "TR_IntrinsicGas" + || exception == "TransactionException.INTRINSIC_GAS_TOO_LOW" + || exception == "IntrinsicGas" + || exception == "TransactionException.INSUFFICIENT_ACCOUNT_FUNDS|TransactionException.INTRINSIC_GAS_TOO_LOW"; + assert!( + check_result, + "unexpected exception {exception:?} for IntrinsicGas for test: [{spec:?}] {name}" + ); + } + InvalidTxReason::BlobVersionNotSupported => { + let check_result = exception + == "TransactionException.TYPE_3_TX_INVALID_BLOB_VERSIONED_HASH" + || exception == "TR_BLOBVERSION_INVALID"; + assert!( + check_result, + "unexpected exception {exception:?} for BlobVersionNotSupported for test: [{spec:?}] {name}" + ); + } + InvalidTxReason::BlobCreateTransaction => { + let check_result = exception == "TR_BLOBCREATE" + || exception == "TransactionException.TYPE_3_TX_CONTRACT_CREATION"; + assert!( + check_result, + "unexpected exception {exception:?} for BlobCreateTransaction for test: [{spec:?}] {name}" + ); + } + InvalidTxReason::BlobGasPriceGreaterThanMax => { + let check_result = + exception == "TransactionException.INSUFFICIENT_MAX_FEE_PER_BLOB_GAS"; + assert!( + check_result, + "unexpected exception {exception:?} for BlobGasPriceGreaterThanMax for test: [{spec:?}] {name}" + ); + } + InvalidTxReason::TooManyBlobs => { + let check_result = exception == "TR_BLOBLIST_OVERSIZE" + || exception == "TransactionException.TYPE_3_TX_BLOB_COUNT_EXCEEDED" + || exception == "TransactionException.TYPE_3_TX_MAX_BLOB_GAS_ALLOWANCE_EXCEEDED|TransactionException.TYPE_3_TX_BLOB_COUNT_EXCEEDED"; + assert!( + check_result, + "unexpected exception {exception:?} for TooManyBlobs for test: [{spec:?}] {name}" + ); + } + InvalidTxReason::EmptyBlobs => { + let check_result = exception == "TransactionException.TYPE_3_TX_ZERO_BLOBS" + || exception == "TR_EMPTYBLOB"; + assert!( + check_result, + "unexpected exception {exception:?} for EmptyBlobs for test: [{spec:?}] {name}" + ); + } + InvalidTxReason::MaxFeePerBlobGasNotSupported => { + let check_result = + exception == "TransactionException.TYPE_3_TX_PRE_FORK|TransactionException.TYPE_3_TX_ZERO_BLOBS"; + assert!( + check_result, + "unexpected exception {exception:?} for MaxFeePerBlobGasNotSupported for test: [{spec:?}] {name}" + ); + } + InvalidTxReason::BlobVersionedHashesNotSupported => { + let check_result = exception == "TransactionException.TYPE_3_TX_PRE_FORK" + || exception == "TR_TypeNotSupportedBlob"; + assert!( + check_result, + "unexpected exception {exception:?} for BlobVersionedHashesNotSupported for test: [{spec:?}] {name}" + ); + } + InvalidTxReason::InvalidAuthorizationChain => { + let check_result = exception == "TransactionException.TYPE_4_INVALID_AUTHORIZATION_FORMAT"; + assert!( + check_result, + "unexpected exception {exception:?} for InvalidAuthorizationChain for test: [{spec:?}] {name}" + ); + } + InvalidTxReason::InvalidAuthorizationSignature => { + let check_result = exception == "TransactionException.TYPE_4_INVALID_AUTHORITY_SIGNATURE"; + assert!( + check_result, + "unexpected exception {exception:?} for InvalidAuthorizationSignature for test: [{spec:?}] {name}" + ); + } + InvalidTxReason::AuthorizationListNotExist => { + let check_result = exception == "TransactionException.TYPE_4_EMPTY_AUTHORIZATION_LIST" || exception == "TransactionException.TYPE_4_TX_CONTRACT_CREATION"; + assert!( + check_result, + "unexpected exception {exception:?} for AuthorizationListNotExist for test: [{spec:?}] {name}" + ); + } + InvalidTxReason::CreateTransaction => { + let check_result = exception == "TransactionException.TYPE_4_TX_CONTRACT_CREATION"; + assert!( + check_result, + "unexpected exception {exception:?} for CreateTransaction for test: [{spec:?}] {name}" + ); + } + InvalidTxReason::GasFloorMoreThanGasLimit => { + let check_result = exception == "TransactionException.INTRINSIC_GAS_TOO_LOW"; + assert!( + check_result, + "unexpected exception {exception:?} for GasFloorMoreThanGasLimit for test: [{spec:?}] {name}" + ); + } + _ => { + panic!( + "unexpected exception {exception:?} for reason {reason:?} for test: [{spec:?}] {name}" + ); + } + } + true + }, + ) +} + +/// Validate EIP-3607 - empty create caller +pub fn assert_empty_create_caller(expect_exception: Option<&String>, name: &str) { + let exception = expect_exception.expect("expected evm-json-test exception"); + let check_exception = + exception == "SenderNotEOA" || exception == "TransactionException.SENDER_NOT_EOA"; + assert!( + check_exception, + "expected EmptyCaller exception for test: {name}: {expect_exception:?}" + ); +} + +/// Check call expected exception +pub fn assert_call_exit_exception(expect_exception: Option<&String>, name: &str) { + assert!( + expect_exception.is_none(), + "unexpected call exception: {expect_exception:?} for test: {name}" + ); +} + +/// Check Exit Reason of EVM execution +pub fn check_create_exit_reason( + reason: &ExitReason, + expect_exception: Option<&String>, + name: &str, +) -> bool { + match reason { + ExitReason::Error(err) => { + if let Some(exception) = expect_exception { + match err { + ExitError::CreateContractLimit => { + let check_result = exception == "TR_InitCodeLimitExceeded" + || exception == "TransactionException.INITCODE_SIZE_EXCEEDED"; + assert!( + check_result, + "unexpected exception {exception:?} for CreateContractLimit error for test: {name}" + ); + return true; + } + ExitError::MaxNonce => { + let check_result = exception == "TR_NonceHasMaxValue" + || exception == "TransactionException.NONCE_IS_MAX"; + assert!(check_result, + "unexpected exception {exception:?} for MaxNonce error for test: {name}" + ); + return true; + } + ExitError::OutOfGas => { + let check_result = + exception == "TransactionException.INTRINSIC_GAS_TOO_LOW"; + assert!(check_result, + "unexpected exception {exception:?} for OutOfGas error for test: {name}" + ); + return true; + } + _ => { + panic!( + "unexpected error: {err:?} for exception: {exception} for test: {name}" + ) + } + } + } else { + return false; + } + } + ExitReason::Fatal(err) => { + panic!("Unexpected error: {err:?}") + } + _ => { + assert!( + expect_exception.is_none(), + "Unexpected json-test error: {expect_exception:?}" + ); + } + } + false +} diff --git a/evm-tests/src/config.rs b/evm-tests/src/config.rs new file mode 100644 index 00000000..de228ac8 --- /dev/null +++ b/evm-tests/src/config.rs @@ -0,0 +1,19 @@ +use crate::types::Spec; +use std::path::PathBuf; + +#[derive(Default, Debug, Clone)] +#[allow(clippy::struct_excessive_bools)] +pub struct VerboseOutput { + pub verbose: bool, + pub verbose_failed: bool, + pub very_verbose: bool, + pub print_state: bool, +} + +#[derive(Default, Debug, Clone)] +pub struct TestConfig { + pub verbose_output: VerboseOutput, + pub spec: Option, + pub file_name: PathBuf, + pub name: String, +} diff --git a/evm-tests/src/execution_results.rs b/evm-tests/src/execution_results.rs new file mode 100644 index 00000000..49aa2cd0 --- /dev/null +++ b/evm-tests/src/execution_results.rs @@ -0,0 +1,39 @@ +use crate::types::Spec; +use aurora_evm::backend::MemoryAccount; +use primitive_types::{H160, H256}; +use std::collections::BTreeMap; + +#[derive(Clone, Debug)] +pub struct FailedTestDetails { + pub name: String, + pub spec: Spec, + pub index: usize, + pub expected_hash: H256, + pub actual_hash: H256, + pub state: BTreeMap, +} + +#[derive(Clone, Debug)] +pub struct TestExecutionResult { + pub total: u64, + pub failed: u64, + pub failed_tests: Vec, +} + +impl TestExecutionResult { + #[allow(clippy::new_without_default)] + #[must_use] + pub const fn new() -> Self { + Self { + total: 0, + failed: 0, + failed_tests: Vec::new(), + } + } + + pub fn merge(&mut self, src: Self) { + self.failed_tests.extend(src.failed_tests); + self.total += src.total; + self.failed += src.failed; + } +} diff --git a/evm-tests/jsontests/src/main.rs b/evm-tests/src/main.rs similarity index 78% rename from evm-tests/jsontests/src/main.rs rename to evm-tests/src/main.rs index ae065f92..08f064a2 100644 --- a/evm-tests/jsontests/src/main.rs +++ b/evm-tests/src/main.rs @@ -1,16 +1,29 @@ +#![allow(clippy::too_long_first_doc_paragraph, clippy::missing_panics_doc)] + +use crate::config::{TestConfig, VerboseOutput}; +use crate::execution_results::TestExecutionResult; +use crate::types::Spec; +use crate::types::StateTestCase; +use crate::types::VmTestCase; use clap::{arg, command, value_parser, ArgAction, Command}; -use ethjson::spec::ForkSpec; -use evm_jsontests::state as statetests; -use evm_jsontests::state::{TestExecutionResult, VerboseOutput}; -use evm_jsontests::vm as vmtests; use std::collections::HashMap; use std::fs; use std::fs::File; use std::io::BufReader; use std::path::{Path, PathBuf}; -use std::sync::Arc; +use std::str::FromStr; + +pub mod state; +pub mod types; +pub mod vm; + +mod assertions; +mod config; +mod execution_results; +mod precompiles; +mod state_dump; -#[allow(clippy::cognitive_complexity)] +#[allow(clippy::cognitive_complexity, clippy::too_many_lines)] fn main() -> Result<(), String> { let matches = command!() .version(env!("CARGO_PKG_VERSION")) @@ -19,7 +32,7 @@ fn main() -> Result<(), String> { Command::new("vm") .about("vm tests runner") .arg( - arg!([PATH] "json file or directory for tests run") + arg!([PATH] "JSON file or directory for tests run") .action(ArgAction::Append) .required(true) .value_parser(value_parser!(PathBuf)), @@ -39,11 +52,16 @@ fn main() -> Result<(), String> { Command::new("state") .about("state tests runner") .arg( - arg!([PATH] "json file or directory for tests run") + arg!([PATH] "JSON file or directory for tests run") .action(ArgAction::Append) .required(true) .value_parser(value_parser!(PathBuf)), ) + .arg( + arg!(-n --"test-name" "filer for the test name, for ex: \"test/name\")") + .required(false) + .value_parser(value_parser!(String)) + ) .arg(arg!(-s --spec "Ethereum hard fork")) .arg( arg!(-v --verbose "Verbose output") @@ -61,7 +79,7 @@ fn main() -> Result<(), String> { .action(ArgAction::SetTrue), ) .arg( - arg!(-p --print_state "When test failed print state") + arg!(-p --print_state "Print state when the test fails") .default_value("false") .action(ArgAction::SetTrue), ), @@ -78,7 +96,7 @@ fn main() -> Result<(), String> { let mut tests_result = TestExecutionResult::new(); for src_name in matches.get_many::("PATH").unwrap() { let path = Path::new(src_name); - assert!(path.exists(), "data source is not exist"); + assert!(path.exists(), "data source does not exist"); if path.is_file() { run_vm_test_for_file(&verbose_output, path, &mut tests_result); } else if path.is_dir() { @@ -93,9 +111,11 @@ fn main() -> Result<(), String> { } if let Some(matches) = matches.subcommand_matches("state") { - let spec: Option = matches + let spec: Option = matches .get_one::("spec") - .and_then(|spec| spec.clone().try_into().ok()); + .and_then(|spec| Spec::from_str(spec).ok()); + + let test_name: Option<&String> = matches.get_one::("test-name"); let verbose_output = VerboseOutput { verbose: matches.get_flag("verbose"), @@ -106,11 +126,28 @@ fn main() -> Result<(), String> { let mut tests_result = TestExecutionResult::new(); for src_name in matches.get_many::("PATH").unwrap() { let path = Path::new(src_name); - assert!(path.exists(), "data source is not exist: {path:?}"); + + assert!( + path.exists(), + "data source does not exist: {}", + path.display() + ); if path.is_file() { - run_test_for_file(&spec, &verbose_output, path, &mut tests_result); + run_test_for_file( + spec.as_ref(), + &verbose_output, + path, + &mut tests_result, + test_name, + ); } else if path.is_dir() { - run_test_for_dir(&spec, &verbose_output, path, &mut tests_result); + run_test_for_dir( + spec.as_ref(), + &verbose_output, + path, + &mut tests_result, + test_name, + ); } } println!("\nTOTAL: {}", tests_result.total); @@ -154,14 +191,14 @@ fn run_vm_test_for_file( short_test_file_name(file_name.to_str().unwrap()) ); } - let file = File::open(file_name).expect("Open file failed"); + let file = File::open(file_name).expect("Open file failed"); let reader = BufReader::new(file); - let test_suite = serde_json::from_reader::<_, HashMap>(reader) + let test_suite = serde_json::from_reader::<_, HashMap>(reader) .expect("Parse test cases failed"); for (name, test) in test_suite { - let test_res = vmtests::test(verbose_output, &name, test); + let test_res = vm::test(verbose_output, &name, &test); if test_res.failed > 0 { if verbose_output.verbose { @@ -192,13 +229,14 @@ fn run_vm_test_for_file( } fn run_test_for_dir( - spec: &Option, + spec: Option<&Spec>, verbose_output: &VerboseOutput, dir_name: &Path, tests_result: &mut TestExecutionResult, + test_name: Option<&String>, ) { if should_skip(dir_name) { - println!("Skipping test case {:?}", dir_name); + println!("Skipping the test case {}", dir_name.display()); return; } for entry in fs::read_dir(dir_name).unwrap() { @@ -210,22 +248,35 @@ fn run_test_for_dir( } let path = entry.path(); if path.is_dir() { - run_test_for_dir(spec, verbose_output, path.as_path(), tests_result); + run_test_for_dir( + spec, + verbose_output, + path.as_path(), + tests_result, + test_name, + ); } else { - run_test_for_file(spec, verbose_output, path.as_path(), tests_result); + run_test_for_file( + spec, + verbose_output, + path.as_path(), + tests_result, + test_name, + ); } } } fn run_test_for_file( - spec: &Option, + spec: Option<&Spec>, verbose_output: &VerboseOutput, file_name: &Path, tests_result: &mut TestExecutionResult, + test_name: Option<&String>, ) { if should_skip(file_name) { if verbose_output.verbose { - println!("Skipping test case {:?}", file_name); + println!("Skipping the test case {}", file_name.display()); } return; } @@ -237,18 +288,24 @@ fn run_test_for_file( } let file = File::open(file_name).expect("Open file failed"); let reader = BufReader::new(file); - let test_suite = serde_json::from_reader::<_, HashMap>(reader) + + let test_suite = serde_json::from_reader::<_, HashMap>(reader) .expect("Parse test cases failed"); - let file_name = Arc::new(file_name.to_path_buf()); for (name, test) in test_suite { - let test_res = statetests::test( - verbose_output.clone(), - &name, - test, - spec.clone(), - file_name.clone(), - ); + if let Some(t) = test_name { + if !name.contains(t) { + continue; + } + } + + let test_config = TestConfig { + verbose_output: verbose_output.clone(), + spec: spec.cloned(), + file_name: file_name.to_path_buf(), + name, + }; + let test_res = state::test(test_config, test); if test_res.failed > 0 { if verbose_output.verbose { @@ -315,7 +372,7 @@ const SKIPPED_CASES: &[&str] = &[ // NOTE: this tests related to hard forks: London and before London "stRevertTest/RevertPrecompiledTouch", "stRevertTest/RevertPrecompiledTouch_storage", - // These tests are passing, but they take a lot of time to execute so can going to skip them. + // These tests pass, but they take a long time to execute, so they are skipped by default. "stTimeConsuming/static_Call50000_sha256", "vmPerformance/loopMul", "stTimeConsuming/CALLBlake2f_MaxRounds", @@ -329,8 +386,8 @@ const SKIPPED_CASES: &[&str] = &[ /// Check if a path should be skipped. /// It checks: -/// - path/and_file_stem - check path and file name (without extention) -/// - path/with/sub/path - recursively check path +/// - `path/and_file_stem` - check path and file name (without extension) +/// - `path/with/sub/path` - recursively check a path fn should_skip(path: &Path) -> bool { let matches = |case: &str| { let case_path = Path::new(case); @@ -339,7 +396,7 @@ fn should_skip(path: &Path) -> bool { let case_path_len = case_path_components.len(); let path_len = path_components.len(); - // Check path length without file name + // Check path length without a file name if case_path_len > path_len { return false; } @@ -348,7 +405,7 @@ fn should_skip(path: &Path) -> bool { (path.file_stem(), case_path.file_stem()) { if file_path_stem == case_file_path_stem { - // If case path contains only file name + // If a case path contains only a file name if case_path_len == 1 { return true; } @@ -362,7 +419,7 @@ fn should_skip(path: &Path) -> bool { } } } - // Check recursively path from the end without file name + // Check recursively path from the end without a file name if case_path_len < path_len && path_len > 1 { for i in 1..=path_len - case_path_len { if case_path_components diff --git a/evm-tests/src/precompiles.rs b/evm-tests/src/precompiles.rs new file mode 100644 index 00000000..75bdbfe6 --- /dev/null +++ b/evm-tests/src/precompiles.rs @@ -0,0 +1,276 @@ +mod kzg; + +use crate::precompiles::kzg::Kzg; +use crate::types::Spec; +use aurora_engine_modexp::AuroraModExp; +use aurora_engine_precompiles::{ + alt_bn256::{Bn256Add, Bn256Mul, Bn256Pair}, + blake2::Blake2F, + bls12_381::{ + BlsG1Add, BlsG1Msm, BlsG2Add, BlsG2Msm, BlsMapFp2ToG2, BlsMapFpToG1, BlsPairingCheck, + }, + hash::{RIPEMD160, SHA256}, + identity::Identity, + modexp::ModExp, + secp256k1::ECRecover, + Berlin, Byzantium, EthGas, Istanbul, Precompile, +}; +use aurora_evm::executor::stack::{ + PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileSet, +}; +use aurora_evm::{ExitError, ExitSucceed, Opcode}; +use primitive_types::H160; +use std::collections::BTreeMap; + +pub struct Precompiles(BTreeMap>); + +impl PrecompileSet for Precompiles { + fn execute( + &self, + handle: &mut impl PrecompileHandle, + ) -> Option> { + let p = self.0.get(&handle.code_address())?; + let result = process_precompile(p.as_ref(), handle); + Some(result.and_then(|output| post_process(output, handle))) + } + + fn is_precompile(&self, address: H160) -> bool { + self.0.contains_key(&address) + } +} + +impl Precompiles { + pub fn new(spec: &Spec) -> Self { + match *spec { + Spec::Frontier + | Spec::Homestead + | Spec::Tangerine + | Spec::SpuriousDragon + | Spec::Byzantium + | Spec::Constantinople + | Spec::Petersburg + | Spec::Istanbul => Self::new_istanbul(), + Spec::Berlin | Spec::London | Spec::Merge | Spec::Shanghai => Self::new_berlin(), + Spec::Cancun => Self::new_cancun(), + Spec::Prague | Spec::Osaka => Self::new_prague(), + } + } + + pub fn new_istanbul() -> Self { + let mut map = BTreeMap::new(); + map.insert( + ECRecover::ADDRESS.raw(), + Box::new(ECRecover) as Box, + ); + map.insert(SHA256::ADDRESS.raw(), Box::new(SHA256)); + map.insert(RIPEMD160::ADDRESS.raw(), Box::new(RIPEMD160)); + map.insert(Identity::ADDRESS.raw(), Box::new(Identity)); + map.insert( + ModExp::::ADDRESS.raw(), + Box::new(ModExp::::new()), + ); + map.insert( + Bn256Add::::ADDRESS.raw(), + Box::new(Bn256Add::::new()), + ); + map.insert( + Bn256Mul::::ADDRESS.raw(), + Box::new(Bn256Mul::::new()), + ); + map.insert( + Bn256Pair::::ADDRESS.raw(), + Box::new(Bn256Pair::::new()), + ); + map.insert(Blake2F::ADDRESS.raw(), Box::new(Blake2F)); + Self(map) + } + + pub fn new_berlin() -> Self { + let mut map = BTreeMap::new(); + map.insert( + ECRecover::ADDRESS.raw(), + Box::new(ECRecover) as Box, + ); + map.insert(SHA256::ADDRESS.raw(), Box::new(SHA256)); + map.insert(RIPEMD160::ADDRESS.raw(), Box::new(RIPEMD160)); + map.insert(Identity::ADDRESS.raw(), Box::new(Identity)); + map.insert( + ModExp::::ADDRESS.raw(), + Box::new(ModExp::::new()), + ); + map.insert( + Bn256Add::::ADDRESS.raw(), + Box::new(Bn256Add::::new()), + ); + map.insert( + Bn256Mul::::ADDRESS.raw(), + Box::new(Bn256Mul::::new()), + ); + map.insert( + Bn256Pair::::ADDRESS.raw(), + Box::new(Bn256Pair::::new()), + ); + map.insert(Blake2F::ADDRESS.raw(), Box::new(Blake2F)); + Self(map) + } + + pub fn new_cancun() -> Self { + let mut map = Self::new_berlin().0; + map.insert(Kzg::ADDRESS, Box::new(Kzg)); + Self(map) + } + + pub fn new_prague() -> Self { + let mut map = Self::new_cancun().0; + map.insert(BlsG1Add::ADDRESS.raw(), Box::new(BlsG1Add)); + map.insert(BlsG1Msm::ADDRESS.raw(), Box::new(BlsG1Msm)); + map.insert(BlsG2Add::ADDRESS.raw(), Box::new(BlsG2Add)); + map.insert(BlsG2Msm::ADDRESS.raw(), Box::new(BlsG2Msm)); + map.insert(BlsPairingCheck::ADDRESS.raw(), Box::new(BlsPairingCheck)); + map.insert(BlsMapFpToG1::ADDRESS.raw(), Box::new(BlsMapFpToG1)); + map.insert(BlsMapFp2ToG2::ADDRESS.raw(), Box::new(BlsMapFp2ToG2)); + Self(map) + } +} + +/// Precompile input and output data struct +#[cfg(feature = "dump-state")] +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct PrecompileStandaloneData { + pub input: String, + pub output: String, +} + +/// Standalone data for the precompile tests. +/// It contains input data for precompile and expected +/// output after the precompile execution. +#[cfg(feature = "dump-state")] +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct PrecompileStandalone { + pub precompile_data: Vec, +} + +fn process_precompile( + p: &dyn Precompile, + handle: &impl PrecompileHandle, +) -> Result { + let input = handle.input(); + let gas_limit = handle.gas_limit(); + let evm_context = handle.context(); + let context = aurora_engine_precompiles::Context { + address: evm_context.address, + caller: evm_context.caller, + apparent_value: evm_context.apparent_value, + }; + let is_static = handle.is_static(); + + let output = p + .run(input, gas_limit.map(EthGas::new), &context, is_static) + .map_err(|err| PrecompileFailure::Error { + exit_status: get_exit_error(err), + }); + #[cfg(feature = "dump-state")] + if let Ok(_out) = &output { + /* EXAMPLE: + dump_precompile_state( + "bn256_pairing_all.json", + input, + &out.output, + evm_context.address, + Bn256Pair::::ADDRESS.raw(), + ); + */ + } + + output +} + +fn post_process( + output: aurora_engine_precompiles::PrecompileOutput, + handle: &mut impl PrecompileHandle, +) -> Result { + handle.record_cost(output.cost.as_u64())?; + Ok(PrecompileOutput { + exit_status: ExitSucceed::Stopped, + output: output.output, + }) +} + +fn get_exit_error(exit_error: aurora_engine_precompiles::ExitError) -> ExitError { + match exit_error { + aurora_engine_precompiles::ExitError::StackUnderflow => ExitError::StackUnderflow, + aurora_engine_precompiles::ExitError::StackOverflow => ExitError::StackOverflow, + aurora_engine_precompiles::ExitError::InvalidJump => ExitError::InvalidJump, + aurora_engine_precompiles::ExitError::InvalidRange => ExitError::InvalidRange, + aurora_engine_precompiles::ExitError::DesignatedInvalid => ExitError::DesignatedInvalid, + aurora_engine_precompiles::ExitError::CallTooDeep => ExitError::CallTooDeep, + aurora_engine_precompiles::ExitError::CreateCollision => ExitError::CreateCollision, + aurora_engine_precompiles::ExitError::CreateContractLimit => ExitError::CreateContractLimit, + aurora_engine_precompiles::ExitError::InvalidCode(op) => { + ExitError::InvalidCode(Opcode(op.0)) + } + aurora_engine_precompiles::ExitError::OutOfOffset => ExitError::OutOfOffset, + aurora_engine_precompiles::ExitError::OutOfGas => ExitError::OutOfGas, + aurora_engine_precompiles::ExitError::OutOfFund => ExitError::OutOfFund, + aurora_engine_precompiles::ExitError::PCUnderflow => ExitError::PCUnderflow, + aurora_engine_precompiles::ExitError::CreateEmpty => ExitError::CreateEmpty, + aurora_engine_precompiles::ExitError::Other(msg) => ExitError::Other(msg), + aurora_engine_precompiles::ExitError::MaxNonce => ExitError::MaxNonce, + aurora_engine_precompiles::ExitError::UsizeOverflow => ExitError::UsizeOverflow, + aurora_engine_precompiles::ExitError::CreateContractStartingWithEF => { + ExitError::CreateContractStartingWithEF + } + } +} + +/// Dumps precompile input and output data to a JSON file for test case generation. +/// +/// It can be used for debugging and creating new test cases for precompiles. +#[cfg(feature = "dump-state")] +#[allow(dead_code)] +fn dump_precompile_state( + file_name: &str, + input: &[u8], + output: &[u8], + evm_context_address: H160, + precompile_address: H160, +) { + use std::fs; + + if input.is_empty() || evm_context_address != precompile_address { + return; + } + + let mut data = fs::read_to_string(file_name) + .ok() + .and_then(|content| { + if content.trim().is_empty() { + None + } else { + serde_json::from_str::(&content).ok() + } + }) + .unwrap_or_else(|| PrecompileStandalone { + precompile_data: Vec::new(), + }); + + let hex_input = hex::encode(input); + let hex_output = hex::encode(output); + + if !data + .precompile_data + .iter() + .any(|entry| entry.input == hex_input) + { + data.precompile_data.push(PrecompileStandaloneData { + input: hex_input, + output: hex_output, + }); + + if let Ok(serialized) = serde_json::to_string(&data) { + fs::write(file_name, serialized).expect("Unable to write the file"); + } else { + panic!("Unable to parse the file"); + } + } +} diff --git a/evm-tests/ethcore-builtin/assets/g1_points.bin b/evm-tests/src/precompiles/assets/g1_points.bin similarity index 100% rename from evm-tests/ethcore-builtin/assets/g1_points.bin rename to evm-tests/src/precompiles/assets/g1_points.bin diff --git a/evm-tests/ethcore-builtin/assets/g2_points.bin b/evm-tests/src/precompiles/assets/g2_points.bin similarity index 100% rename from evm-tests/ethcore-builtin/assets/g2_points.bin rename to evm-tests/src/precompiles/assets/g2_points.bin diff --git a/evm-tests/ethcore-builtin/assets/trusted_setup.txt b/evm-tests/src/precompiles/assets/trusted_setup.txt similarity index 100% rename from evm-tests/ethcore-builtin/assets/trusted_setup.txt rename to evm-tests/src/precompiles/assets/trusted_setup.txt diff --git a/evm-tests/src/precompiles/kzg.rs b/evm-tests/src/precompiles/kzg.rs new file mode 100644 index 00000000..3fb0b69f --- /dev/null +++ b/evm-tests/src/precompiles/kzg.rs @@ -0,0 +1,294 @@ +#![allow(clippy::module_inception, clippy::large_stack_arrays)] + +use aurora_engine_precompiles::{ + Context, EthGas, EvmPrecompileResult, ExitError, Precompile, PrecompileOutput, +}; +use primitive_types::H160; +use std::borrow::Cow::Borrowed; + +mod kzg { + use c_kzg::{Bytes32, Bytes48, KzgProof, KzgSettings, BYTES_PER_G1_POINT, BYTES_PER_G2_POINT}; + use core::convert::TryInto; + use core::hash::{Hash, Hasher}; + use derive_more::{AsMut, AsRef, Deref, DerefMut}; + use hex_literal::hex; + use sha2::Digest; + use std::convert::TryFrom; + use std::rc::Rc; + use std::str::Lines; + + pub const RETURN_VALUE: &[u8; 64] = &hex!( + "0000000000000000000000000000000000000000000000000000000000001000" + "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001" + ); + + /// Number of G1 Points. + const NUM_G1_POINTS: usize = 4096; + + /// Number of G2 Points. + const NUM_G2_POINTS: usize = 65; + + /// A new type over the list of G1 point from kzg trusted setup. + #[derive(Debug, Clone, PartialEq, AsRef, AsMut, Deref, DerefMut)] + #[repr(transparent)] + struct G1Points(pub [[u8; BYTES_PER_G1_POINT]; NUM_G1_POINTS]); + + impl Default for G1Points { + fn default() -> Self { + Self([[0; BYTES_PER_G1_POINT]; NUM_G1_POINTS]) + } + } + + /// A new type over the list of G2 point from kzg trusted setup. + #[derive(Debug, Clone, Eq, PartialEq, AsRef, AsMut, Deref, DerefMut)] + #[repr(transparent)] + struct G2Points(pub [[u8; BYTES_PER_G2_POINT]; NUM_G2_POINTS]); + + impl Default for G2Points { + fn default() -> Self { + Self([[0; BYTES_PER_G2_POINT]; NUM_G2_POINTS]) + } + } + + /// Default G1 points. + const G1_POINTS: &G1Points = { + const BYTES: &[u8] = include_bytes!("assets/g1_points.bin"); + assert!(BYTES.len() == size_of::()); + unsafe { &*BYTES.as_ptr().cast::() } + }; + + /// Default G2 points. + const G2_POINTS: &G2Points = { + const BYTES: &[u8] = include_bytes!("assets/g2_points.bin"); + assert!(BYTES.len() == size_of::()); + unsafe { &*BYTES.as_ptr().cast::() } + }; + + /// Parse contents of a KZG trusted setup file into a list of G1 and G2 points. + /// + /// These can then be used to create a KZG settings object with + /// [`KzgSettings::load_trusted_setup`](c_kzg::KzgSettings::load_trusted_setup). + #[allow(dead_code)] + fn parse_kzg_trusted_setup( + trusted_setup: &str, + ) -> Result<(Box, Box), &'static str> { + let mut lines = trusted_setup.lines(); + + // load number of points + let n_g1 = lines + .next() + .ok_or("KzgFileFormatError")? + .parse::() + .map_err(|_| "KzgParseError")?; + let n_g2 = lines + .next() + .ok_or("KzgFileFormatError")? + .parse::() + .map_err(|_| "KzgParseError")?; + + if n_g1 != NUM_G1_POINTS { + return Err("KzgMismatchedNumberOfPoints"); + } + + if n_g2 != NUM_G2_POINTS { + return Err("KzgMismatchedNumberOfPoints"); + } + + // load g1 points + let mut g1_points = Box::::default(); + decode_points(&mut g1_points.0, &mut lines)?; + + // load g2 points + let mut g2_points = Box::::default(); + decode_points(&mut g2_points.0, &mut lines)?; + + if lines.next().is_some() { + return Err("KzgFileFormatError"); + } + + Ok((g1_points, g2_points)) + } + + /// KZG Settings that allow us to specify a custom trusted setup. + /// or use hardcoded default settings. + #[allow(dead_code)] + #[derive(Debug, Clone, Default)] + pub enum EnvKzgSettings { + /// Default mainnet trusted setup + #[default] + Default, + /// Custom trusted setup. + Custom(Rc), + } + + // Implement PartialEq and Hash manually because `c_kzg::KzgSettings` does not implement them + impl PartialEq for EnvKzgSettings { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Default, Self::Default) => true, + (Self::Custom(a), Self::Custom(b)) => Rc::ptr_eq(a, b), + _ => false, + } + } + } + + impl Hash for EnvKzgSettings { + fn hash(&self, state: &mut H) { + core::mem::discriminant(self).hash(state); + match self { + Self::Default => {} + Self::Custom(settings) => settings.hash(state), + } + } + } + + impl EnvKzgSettings { + /// Return set KZG settings. + /// + /// In will initialize the default settings if it is not already loaded. + pub fn get(&self) -> Rc { + match self { + Self::Default => { + let res = + KzgSettings::load_trusted_setup(G1_POINTS.as_ref(), G2_POINTS.as_ref()) + .expect("failed to load default trusted setup"); + Rc::new(res) + } + Self::Custom(settings) => settings.clone(), + } + } + } + + /// `VERSIONED_HASH_VERSION_KZG ++ sha256(commitment)[1..]` + #[inline] + pub fn kzg_to_versioned_hash(commitment: &[u8]) -> [u8; 32] { + const VERSIONED_HASH_VERSION_KZG: u8 = 0x01; + let mut hash: [u8; 32] = sha2::Sha256::digest(commitment).into(); + hash[0] = VERSIONED_HASH_VERSION_KZG; + hash + } + + #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] + pub struct KzgInput { + commitment: Bytes48, + z: Bytes32, + y: Bytes32, + proof: Bytes48, + } + + impl KzgInput { + #[inline] + pub fn verify_kzg_proof(&self, kzg_settings: &KzgSettings) -> bool { + KzgProof::verify_kzg_proof( + &self.commitment, + &self.z, + &self.y, + &self.proof, + kzg_settings, + ) + .unwrap_or(false) + } + } + + impl TryFrom<&[u8]> for KzgInput { + type Error = &'static str; + + fn try_from(input: &[u8]) -> Result { + if input.len() != 192 { + return Err("BlobInvalidInputLength"); + } + // Verify commitment matches versioned_hash + let versioned_hash = &input[..32]; + let commitment = &input[96..144]; + if kzg_to_versioned_hash(commitment) != versioned_hash { + return Err("BlobMismatchedVersion"); + } + let commitment = *as_bytes48(commitment); + let z = *as_bytes32(&input[32..64]); + let y = *as_bytes32(&input[64..96]); + let proof = *as_bytes48(&input[144..192]); + Ok(Self { + commitment, + z, + y, + proof, + }) + } + } + + #[inline] + fn as_array(bytes: &[u8]) -> &[u8; N] { + bytes.try_into().expect("slice with incorrect length") + } + + #[inline] + fn as_bytes32(bytes: &[u8]) -> &Bytes32 { + // SAFETY: `#[repr(C)] Bytes32([u8; 32])` + unsafe { &*as_array::<32>(bytes).as_ptr().cast() } + } + + #[inline] + fn as_bytes48(bytes: &[u8]) -> &Bytes48 { + // SAFETY: `#[repr(C)] Bytes48([u8; 48])` + unsafe { &*as_array::<48>(bytes).as_ptr().cast() } + } + + #[inline] + fn decode_points( + points: &mut [[u8; LEN]], + lines: &mut Lines<'_>, + ) -> Result<(), &'static str> { + for bytes in points { + let line = lines.next().ok_or("KzgFileFormatError")?; + hex::decode_to_slice(line, bytes).map_err(|_| "KzgParseError")?; + } + Ok(()) + } +} + +const KZG_BASE_GAS_FEE: u64 = 50_000; + +pub struct Kzg; + +impl Kzg { + pub const ADDRESS: H160 = H160([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0A, + ]); + + fn execute(input: &[u8]) -> Result, ExitError> { + // Get and verify KZG input. + let kzg_input: kzg::KzgInput = input + .try_into() + .map_err(|e| ExitError::Other(Borrowed(e)))?; + let kzg_settings = kzg::EnvKzgSettings::Default; + if !kzg_input.verify_kzg_proof(&kzg_settings.get()) { + return Err(ExitError::Other(Borrowed("BlobVerifyKzgProofFailed"))); + } + Ok(kzg::RETURN_VALUE.to_vec()) + } +} + +impl Precompile for Kzg { + fn required_gas(_input: &[u8]) -> Result { + Ok(EthGas::new(KZG_BASE_GAS_FEE)) + } + + fn run( + &self, + input: &[u8], + target_gas: Option, + _context: &Context, + _is_static: bool, + ) -> EvmPrecompileResult { + let cost = Self::required_gas(input)?; + if let Some(target_gas) = target_gas { + if cost > target_gas { + return Err(ExitError::OutOfGas); + } + } + + let output = Self::execute(input)?; + Ok(PrecompileOutput::without_logs(cost, output)) + } +} diff --git a/evm-tests/src/state.rs b/evm-tests/src/state.rs new file mode 100644 index 00000000..16fcfcc8 --- /dev/null +++ b/evm-tests/src/state.rs @@ -0,0 +1,324 @@ +use crate::assertions::{ + self, assert_call_exit_exception, assert_empty_create_caller, assert_vicinity_validation, + check_create_exit_reason, +}; +use crate::config::TestConfig; +use crate::execution_results::{FailedTestDetails, TestExecutionResult}; +use crate::precompiles::Precompiles; +use crate::state_dump::{StateTestsDump, StateTestsDumper}; +use crate::types::account_state::MemoryAccountsState; +use crate::types::blob::{calc_data_fee, calc_max_data_fee, BlobExcessGasAndPrice}; +use crate::types::transaction::TxType; +use crate::types::{Spec, StateTestCase}; +use aurora_evm::backend::{ApplyBackend, MemoryBackend}; +use aurora_evm::executor::stack::{MemoryStackState, StackExecutor, StackSubstateMetadata}; +use aurora_evm::utils::U256_ZERO; +use primitive_types::H160; +use std::str::FromStr; + +/// Runs a test in a separate thread with a specified stack size. +/// +/// # Panics +/// This function will panic if thread spawning or joining fails. +#[must_use] +pub fn test(test_config: TestConfig, test: StateTestCase) -> TestExecutionResult { + use std::thread; + + const STACK_SIZE: usize = 16 * 1024 * 1024; + + // Spawn thread with explicit stack size + let child = thread::Builder::new() + .stack_size(STACK_SIZE) + .spawn(move || test_run(&test_config, &test)) + .unwrap(); + + // Wait for the thread to join + child.join().unwrap() +} + +#[allow(clippy::cognitive_complexity, clippy::too_many_lines)] +fn test_run(test_config: &TestConfig, test: &StateTestCase) -> TestExecutionResult { + let mut tests_result = TestExecutionResult::new(); + for (spec, states) in &test.post_states { + // Run tests for the specific EVM hard fork (Spec) + if let Some(s) = test_config.spec.as_ref() { + if s != spec { + continue; + } + } + + // Geet gasometer config for the current spec + let Some(gasometer_config) = spec.get_gasometer_config() else { + // If the spec is not supported, skip the test + continue; + }; + + // EIP-4844 + let blob_gas_price = BlobExcessGasAndPrice::from_env(&test.env); + // EIP-4844 + let data_max_fee = calc_max_data_fee(&gasometer_config, &test.transaction); + let data_fee = calc_data_fee( + &gasometer_config, + &test.transaction, + blob_gas_price.as_ref(), + ); + + let original_state = test.pre_state.as_ref().to_memory_accounts_state(); + let vicinity = test.get_memory_vicinity(spec, blob_gas_price); + + if let Err(tx_err) = vicinity { + tests_result.total += states.len() as u64; + let h = states.first().unwrap().hash; + // if vicinity could not be computed, then the transaction was invalid, so we simply + // check the original state and move on + let (is_valid_hash, actual_hash) = original_state.check_valid_hash(&h); + if !is_valid_hash { + tests_result.failed_tests.push(FailedTestDetails { + expected_hash: h, + actual_hash, + index: 0, + name: String::from_str(&test_config.name).unwrap(), + spec: spec.clone(), + state: original_state.0, + }); + if test_config.verbose_output.verbose_failed { + println!( + " [{spec:?}] {}: {tx_err:?} ... validation failed\t<----", + test_config.name + ); + } + tests_result.failed += 1; + continue; + } + assert_vicinity_validation(&tx_err, states, spec, test_config); + // As it's an expected validation error-skip the test run + continue; + } + + let vicinity = vicinity.unwrap(); + let caller = test.transaction.get_caller_from_secret_key(); + + let caller_balance = original_state.caller_balance(caller); + // EIP-3607 + let caller_code = original_state.caller_code(caller); + // EIP-7702 - check if it's delegated designation. If it's a delegation designation, then, + // even if `caller_code` is non-empty, the transaction should be executed. + let is_delegated = original_state.is_delegated(caller); + + for (i, state) in states.iter().enumerate() { + let mut backend = MemoryBackend::new(&vicinity, original_state.0.clone()); + tests_result.total += 1; + + // Test case may be expected to fail with an unsupported tx type if the current fork is + // older than Berlin (see EIP-2718). However, this is not implemented in sputnik itself and rather + // in the code hosting sputnik. https://github.com/rust-blockchain/evm/pull/40 + if spec.is_filtered_spec_for_skip() + && TxType::from_tx_bytes(&state.tx_bytes) != TxType::Legacy + && state.expect_exception.as_deref() == Some("TR_TypeNotSupported") + { + continue; + } + + let gas_limit: u64 = test.transaction.get_gas_limit(state).as_u64(); + let data: Vec = test.transaction.get_data(state); + + let valid_tx = test.transaction.validate( + test.env.block_gas_limit, + caller_balance, + &gasometer_config, + &vicinity, + blob_gas_price, + data_max_fee, + spec, + state, + ); + // Only execute valid transactions + let authorization_list = match valid_tx { + Ok(list) => list, + Err(err) + if assertions::check_validate_exit_reason( + &err, + state.expect_exception.as_ref(), + test_config.name.as_str(), + spec, + ) => + { + continue + } + Err(err) => panic!("transaction validation error: {err:?}"), + }; + + // We do not check overflow after TX validation + let total_fee = if let Some(data_fee) = data_fee { + vicinity.effective_gas_price * gas_limit + data_fee + } else { + vicinity.effective_gas_price * gas_limit + }; + + // Dump state transaction data + let mut state_tests_dump = StateTestsDump::default(); + state_tests_dump.set_state(&original_state.0); + state_tests_dump.set_vicinity(&vicinity); + + let metadata = StackSubstateMetadata::new(gas_limit, &gasometer_config); + let executor_state = MemoryStackState::new(metadata, &backend); + // let precompile = JsonPrecompile::precompile(spec).unwrap(); + let precompile = Precompiles::new(spec); + let mut executor = + StackExecutor::new_with_precompiles(executor_state, &gasometer_config, &precompile); + executor.state_mut().withdraw(caller, total_fee).unwrap(); + + let access_list = test.transaction.get_access_list(state); + + // EIP-3607: Reject transactions from senders with deployed code + // EIP-7702: Accept transaction even if the caller has code. + if caller_code.is_empty() || is_delegated { + let value = test.transaction.get_value(state); + if let Some(to) = test.transaction.to { + state_tests_dump.set_tx_data( + to, + value, + data.clone(), + gas_limit, + access_list.clone(), + ); + + // Exit reason for the call is not analyzed as it mostly does not expect exceptions + let _reason = executor.transact_call( + caller, + to, + value, + data, + gas_limit, + access_list.clone(), + authorization_list.clone(), + ); + assert_call_exit_exception(state.expect_exception.as_ref(), &test_config.name); + } else { + let code = data; + + let reason = + executor.transact_create(caller, value, code, gas_limit, access_list); + if check_create_exit_reason( + &reason.0, + state.expect_exception.as_ref(), + &format!("{spec:?}-{}-{i}", test_config.name), + ) { + continue; + } + } + } else { + // According to EIP7702 - https://eips.ethereum.org/EIPS/eip-7702#transaction-origination: + // allow EOAs whose code is a valid delegation designation, i.e. `0xef0100 || address`, + // to continue to originate transactions. + #[allow(clippy::collapsible_if)] + if !(*spec >= Spec::Prague + && TxType::from_tx_bytes(&state.tx_bytes) == TxType::EOAAccountCode) + { + assert_empty_create_caller(state.expect_exception.as_ref(), &test_config.name); + } + } + + let used_gas = executor.used_gas(); + if test_config.verbose_output.print_state { + println!("gas_limit: {gas_limit}\nused_gas: {used_gas}"); + } + + let actual_fee = executor.fee(vicinity.effective_gas_price); + // Forks after London burn miner rewards and thus have different gas fee + // calculation (see EIP-1559) + let miner_reward = if *spec > Spec::Berlin { + let coinbase_gas_price = vicinity + .effective_gas_price + .saturating_sub(vicinity.block_base_fee_per_gas); + executor.fee(coinbase_gas_price) + } else { + actual_fee + }; + + executor + .state_mut() + .deposit(vicinity.block_coinbase, miner_reward); + + let amount_to_return_for_caller = data_fee.map_or_else( + || total_fee - actual_fee, + |data_fee| total_fee - actual_fee - data_fee, + ); + executor + .state_mut() + .deposit(caller, amount_to_return_for_caller); + + let (values, logs) = executor.into_state().deconstruct(); + + backend.apply(values, logs, true); + // It's a special case for hard forks: London and before, + // According to EIP-160, an empty account should be removed. But in that particular test - original test state + // contains account 0x03 (it's a precompile), and when precompile 0x03 was called it exit with + // OutOfGas result. And after exit of the substate, the account is not marked as touched, as exit reason + // is not a success. And it means that it doesn't appear in Apply::Modify, then as untouched it + // can't be removed by the backend.apply event. In that particular case we should manage it manually. + // NOTE: it's not realistic situation for real life flow. + if *spec <= Spec::London && test_config.name == "failed_tx_xcf416c53" { + let state = backend.state_mut(); + state.retain(|addr, account| { + // Check if the account is empty for the precompile `0x03` + !(addr == &H160::from_low_u64_be(3) + && account.balance == U256_ZERO + && account.nonce == U256_ZERO + && account.code.is_empty()) + }); + } + + let backend_state = MemoryAccountsState(backend.state().clone()); + let (is_valid_hash, actual_hash) = backend_state.check_valid_hash(&state.hash); + if !is_valid_hash { + let failed_res = FailedTestDetails { + expected_hash: state.hash, + actual_hash, + index: i, + name: test_config.name.clone(), + spec: spec.clone(), + state: backend.state().clone(), + }; + tests_result.failed_tests.push(failed_res); + tests_result.failed += 1; + + if test_config.verbose_output.verbose_failed { + println!("\n[{spec:?}] {}:{i} ... failed\t<----", test_config.name); + } + + if test_config.verbose_output.print_state { + // Print detailed state data + println!( + "expected_hash:\t{:?}\nactual_hash:\t{actual_hash:?}", + state.hash.0, + ); + for (addr, acc) in backend.state().clone() { + // Decode balance + let balance = acc.balance.to_string(); + + println!( + "{addr:?}: {{\n balance: {balance}\n code: {:?}\n nonce: {}\n storage: {:#?}\n}}", + hex::encode(acc.code), + acc.nonce, + acc.storage + ); + } + if let Some(e) = state.expect_exception.as_ref() { + println!("-> expect_exception: {e}"); + } + } + } else if test_config.verbose_output.very_verbose + && !test_config.verbose_output.verbose_failed + { + println!(" [{spec:?}] {}:{i} ... passed", test_config.name); + } + + state_tests_dump.set_used_gas(used_gas); + state_tests_dump.set_state_hash(actual_hash); + state_tests_dump.set_result_state(backend.state()); + state_tests_dump.dump_to_file(spec); + } + } + tests_result +} diff --git a/evm-tests/src/state_dump.rs b/evm-tests/src/state_dump.rs new file mode 100644 index 00000000..48472549 --- /dev/null +++ b/evm-tests/src/state_dump.rs @@ -0,0 +1,94 @@ +use crate::types::Spec; +use aurora_evm::backend::{MemoryAccount, MemoryVicinity}; +use primitive_types::{H160, H256, U256}; +use std::collections::BTreeMap; + +#[allow(dead_code)] +#[derive(Default, Debug, Clone)] +#[cfg_attr(feature = "dump-state", derive(serde::Serialize, serde::Deserialize))] +pub struct StateTestsDump { + pub state: BTreeMap, + pub caller: H160, + pub gas_price: U256, + pub effective_gas_price: U256, + pub used_gas: u64, + pub state_hash: H256, + pub result_state: BTreeMap, + pub to: H160, + pub value: U256, + pub data: Vec, + pub gas_limit: u64, + pub access_list: Vec<(H160, Vec)>, +} + +pub trait StateTestsDumper { + fn set_state(&mut self, _state: &BTreeMap) {} + fn set_used_gas(&mut self, _used_gas: u64) {} + fn set_vicinity(&mut self, _vicinity: &MemoryVicinity) {} + fn set_tx_data( + &mut self, + _to: H160, + _value: U256, + _data: Vec, + _gas_limit: u64, + _access_list: Vec<(H160, Vec)>, + ) { + } + fn set_state_hash(&mut self, _state_hash: H256) {} + fn set_result_state(&mut self, _state: &BTreeMap) {} + fn dump_to_file(&self, _spec: &Spec) {} +} + +#[cfg(not(feature = "dump-state"))] +impl StateTestsDumper for StateTestsDump {} + +#[cfg(feature = "dump-state")] +impl StateTestsDumper for StateTestsDump { + fn set_state(&mut self, state: &BTreeMap) { + self.state = state.clone(); + } + + fn set_used_gas(&mut self, used_gas: u64) { + self.used_gas = used_gas; + } + + fn set_vicinity(&mut self, vicinity: &MemoryVicinity) { + self.caller = vicinity.origin; + self.gas_price = vicinity.gas_price; + self.effective_gas_price = vicinity.effective_gas_price; + } + + fn set_tx_data( + &mut self, + to: H160, + value: U256, + data: Vec, + gas_limit: u64, + access_list: Vec<(H160, Vec)>, + ) { + self.to = to; + self.value = value; + self.data = data; + self.gas_limit = gas_limit; + self.access_list = access_list; + } + + fn set_state_hash(&mut self, state_hash: H256) { + self.state_hash = state_hash; + } + + fn set_result_state(&mut self, state: &BTreeMap) { + self.result_state = state.clone(); + } + + fn dump_to_file(&self, spec: &Spec) { + use std::time::{SystemTime, UNIX_EPOCH}; + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_micros(); + let path = format!("state_test_{spec:?}_{now}.json"); + let json = serde_json::to_string(&self).unwrap(); + std::fs::write(path, json).unwrap(); + } +} diff --git a/evm-tests/src/types/account_state.rs b/evm-tests/src/types/account_state.rs new file mode 100644 index 00000000..d8711e7c --- /dev/null +++ b/evm-tests/src/types/account_state.rs @@ -0,0 +1,198 @@ +use super::json_utils::{ + btree_h256_h256_from_str, deserialize_bytes_from_str_opt, deserialize_u256_from_str, + h160_from_hex_str, strip_0x_prefix, +}; +use aurora_evm::backend::MemoryAccount; +use aurora_evm::executor::stack::Authorization; +use primitive_types::{H160, H256, U256}; +use serde::{Deserialize, Deserializer}; +use sha3::{Digest, Keccak256}; +use std::collections::BTreeMap; + +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct StateAccount { + /// Account Nonce. + #[serde(deserialize_with = "deserialize_u256_from_str")] + pub nonce: U256, + /// Account Balance. + #[serde(deserialize_with = "deserialize_u256_from_str")] + pub balance: U256, + /// Account Code. + #[serde(default, deserialize_with = "deserialize_bytes_from_str_opt")] + pub code: Option>, + /// Account Storage. + #[serde(default, deserialize_with = "btree_h256_h256_from_str")] + pub storage: BTreeMap, +} + +impl From for MemoryAccount { + fn from(account: StateAccount) -> Self { + Self { + nonce: account.nonce, + balance: account.balance, + storage: account + .storage + .iter() + .filter_map(|(k, v)| { + if v.is_zero() { + // If value is zero then the key is not really there + None + } else { + Some((*k, *v)) + } + }) + .collect(), + code: account.code.unwrap_or_default(), + } + } +} + +/// `AccountsState` represents a sorted mapping from Ethereum account addresses (`H160`) to their +/// corresponding state (`StateAccount`). +/// It uses a `BTreeMap` to ensure a consistent order for serialization. +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)] +pub struct AccountsState(BTreeMap); + +impl AccountsState { + /// Converts the `AccountsState` into a `BTreeMap` of `H160` addresses to `MemoryAccount`. + #[must_use] + pub fn to_memory_accounts_state(&self) -> MemoryAccountsState { + MemoryAccountsState( + self.0 + .iter() + .map(|(&address, state_account)| { + (address, MemoryAccount::from(state_account.clone())) + }) + .collect(), + ) + } +} + +impl<'de> Deserialize<'de> for AccountsState { + fn deserialize>(deserializer: D) -> Result { + let map: BTreeMap = Deserialize::deserialize(deserializer)?; + let mut inner = BTreeMap::new(); + for (k, v) in map { + let address = h160_from_hex_str::(strip_0x_prefix(&k))?; + inner.insert(address, v); + } + Ok(Self(inner)) + } +} + +/// Basic account type. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TrieAccount { + /// Nonce of the account. + pub nonce: U256, + /// Balance of the account. + pub balance: U256, + /// Storage root of the account. + pub storage_root: H256, + /// Code hash of the account. + pub code_hash: H256, + /// Code version of the account. + pub code_version: U256, +} + +impl rlp::Encodable for TrieAccount { + fn rlp_append(&self, stream: &mut rlp::RlpStream) { + let use_short_version = self.code_version == U256::zero(); + + if use_short_version { + stream.begin_list(4); + } else { + stream.begin_list(5); + } + + stream.append(&self.nonce); + stream.append(&self.balance); + stream.append(&self.storage_root); + stream.append(&self.code_hash); + + if !use_short_version { + stream.append(&self.code_version); + } + } +} + +impl rlp::Decodable for TrieAccount { + fn decode(rlp: &rlp::Rlp) -> Result { + let use_short_version = match rlp.item_count()? { + 4 => true, + 5 => false, + _ => return Err(rlp::DecoderError::RlpIncorrectListLen), + }; + + Ok(Self { + nonce: rlp.val_at(0)?, + balance: rlp.val_at(1)?, + storage_root: rlp.val_at(2)?, + code_hash: rlp.val_at(3)?, + code_version: if use_short_version { + U256::zero() + } else { + rlp.val_at(4)? + }, + }) + } +} + +#[derive(Default, Clone, Debug, Eq, PartialEq)] +pub struct MemoryAccountsState(pub BTreeMap); + +impl MemoryAccountsState { + #[must_use] + pub fn check_valid_hash(&self, h: &H256) -> (bool, H256) { + let tree = self + .0 + .iter() + .map(|(address, account)| { + let storage_root = H256( + ethereum::util::sec_trie_root( + account + .storage + .iter() + .map(|(k, v)| (k, rlp::encode(&U256::from_big_endian(&v[..])))), + ) + .0, + ); + let code_hash = + H256::from_slice(<[u8; 32]>::from(Keccak256::digest(&account.code)).as_slice()); + + let account = TrieAccount { + nonce: account.nonce, + balance: account.balance, + storage_root, + code_hash, + code_version: U256::zero(), + }; + (address, rlp::encode(&account)) + }) + .collect::>(); + + let root = H256(ethereum::util::sec_trie_root(tree).0); + let expect = h; + (root == *expect, root) + } + + pub fn caller_balance(&self, caller: H160) -> U256 { + self.0 + .get(&caller) + .map_or_else(U256::zero, |acc| acc.balance) + } + + pub fn caller_code(&self, caller: H160) -> Vec { + self.0 + .get(&caller) + .map_or_else(Vec::new, |acc| acc.code.clone()) + } + + #[must_use] + pub fn is_delegated(&self, caller: H160) -> bool { + self.0 + .get(&caller) + .is_some_and(|c| Authorization::is_delegated(&c.code)) + } +} diff --git a/evm-tests/src/types/blob.rs b/evm-tests/src/types/blob.rs new file mode 100644 index 00000000..51717708 --- /dev/null +++ b/evm-tests/src/types/blob.rs @@ -0,0 +1,197 @@ +//! EIP-4844 constants and helpers for blob gas pricing. + +use crate::types::transaction::Transaction; +use crate::types::StateEnv; +use aurora_evm::Config; +use primitive_types::U256; +use serde::Deserialize; + +/// Controls the maximum rate of change for blob gas price +pub const BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN: u64 = 3_338_477; + +/// Controls the maximum rate of change for blob gas price +pub const BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE: u64 = 5_007_716; + +/// First version of the blob +pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01; + +/// Gas consumption of a single data blob (== blob byte size) +pub const GAS_PER_BLOB: u64 = 1 << 17; + +/// Min blob gas price +pub const MIN_BLOB_GASPRICE: u64 = 1; + +/// Target number of the blob per block +pub const TARGET_BLOB_NUMBER_PER_BLOCK_CANCUN: u64 = 3; + +/// Max number of blobs per block +pub const MAX_BLOB_NUMBER_PER_BLOCK_CANCUN: u64 = 2 * TARGET_BLOB_NUMBER_PER_BLOCK_CANCUN; + +/// Maximum consumable blob gas for data blobs per block +pub const MAX_BLOB_GAS_PER_BLOCK_CANCUN: u64 = MAX_BLOB_NUMBER_PER_BLOCK_CANCUN * GAS_PER_BLOB; + +/// Target consumable blob gas for data blobs per block: EIP-7691 +pub const TARGET_BLOB_GAS_PER_BLOCK: u64 = 786_432; + +/// Target number of the blob per block +pub const TARGET_BLOB_NUMBER_PER_BLOCK_PRAGUE: u64 = 6; + +/// Max number of blobs per block +pub const MAX_BLOB_NUMBER_PER_BLOCK_PRAGUE: u64 = 9; + +/// Maximum consumable blob gas for data blobs per block +pub const MAX_BLOB_GAS_PER_BLOCK_PRAGUE: u64 = MAX_BLOB_NUMBER_PER_BLOCK_PRAGUE * GAS_PER_BLOB; + +/// Target consumable blob gas for data blobs per block (for 1559-like pricing) +pub const TARGET_BLOB_GAS_PER_BLOCK_PRAGUE: u64 = + TARGET_BLOB_NUMBER_PER_BLOCK_PRAGUE * GAS_PER_BLOB; + +/// Structure holding block blob excess gas and it calculates blob fee +/// +/// Incorporated as part of the Cancun upgrade via [EIP-4844]. +/// +/// [EIP-4844]: +#[derive(Copy, Clone, Debug, Ord, PartialOrd, PartialEq, Eq, Deserialize)] +pub struct BlobExcessGasAndPrice { + /// The excess blob gas of the block + pub excess_blob_gas: u64, + /// The calculated blob gas price based on the `excess_blob_gas` + /// + /// See [`calc_blob_gas_price`] + pub blob_gas_price: u128, +} + +impl BlobExcessGasAndPrice { + /// Creates a new instance by calculating the blob gas price with [`calc_blob_gasprice`]. + #[must_use] + pub fn new(excess_blob_gas: u64) -> Self { + let blob_gas_price = calc_blob_gas_price(excess_blob_gas); + Self { + excess_blob_gas, + blob_gas_price, + } + } + + /// Calculate this block excess gas and price from the parent excess gas and gas used + /// and the target blob gas per block. + /// + /// These fields will be used to calculate `excess_blob_gas` with [`calc_excess_blob_gas`] func. + #[must_use] + pub fn from_parent(parent_excess_blob_gas: u64, parent_blob_gas_used: u64) -> Self { + Self::new(calc_excess_blob_gas( + parent_excess_blob_gas, + parent_blob_gas_used, + )) + } + + /// Initializes the ``BlobExcessGasAndPrice`` from the environment state. + #[must_use] + pub fn from_env(env: &StateEnv) -> Option { + env.current_excess_blob_gas.map(Self::new).or_else(|| { + env.parent_blob_gas_used + .zip(env.parent_excess_blob_gas) + .map(|(parent_blob_gas_used, parent_excess_blob_gas)| { + Self::from_parent(parent_excess_blob_gas, parent_blob_gas_used) + }) + }) + } +} + +/// Calculates the `excess_blob_gas` from the parent header's `blob_gas_used` and `excess_blob_gas`. +/// +/// See also [the EIP-4844 helpers] +#[inline] +#[must_use] +pub const fn calc_excess_blob_gas(parent_excess_blob_gas: u64, parent_blob_gas_used: u64) -> u64 { + (parent_excess_blob_gas + parent_blob_gas_used).saturating_sub(TARGET_BLOB_GAS_PER_BLOCK) +} + +/// Calculates the blob gas price from the header's excess blob gas field. +/// +/// See also [the EIP-4844 helpers](https://eips.ethereum.org/EIPS/eip-4844#helpers) +#[inline] +#[must_use] +pub fn calc_blob_gas_price(excess_blob_gas: u64) -> u128 { + fake_exponential( + MIN_BLOB_GASPRICE, + excess_blob_gas, + BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN, + ) +} + +/// Approximates `factor * e ** (numerator / denominator)` using Taylor expansion. +/// +/// This is used to calculate the blob price. +/// +/// See also [the EIP-4844 helpers](https://eips.ethereum.org/EIPS/eip-4844#helpers) +/// (`fake_exponential`). +/// +/// # Panics +/// +/// This function panics if `denominator` is zero. +#[inline] +#[must_use] +pub fn fake_exponential(factor: u64, numerator: u64, denominator: u64) -> u128 { + assert_ne!(denominator, 0, "attempt to divide by zero"); + let factor = u128::from(factor); + let numerator = u128::from(numerator); + let denominator = u128::from(denominator); + + let mut i = 1; + let mut output = 0; + let mut numerator_accum = factor * denominator; + while numerator_accum > 0 { + output += numerator_accum; + + // Denominator is asserted as not zero at the start of the function. + numerator_accum = (numerator_accum * numerator) / (denominator * i); + i += 1; + } + output / denominator +} + +/// Calculates the [EIP-4844] `data_fee` of the transaction. +/// +/// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 +#[inline] +#[must_use] +pub fn calc_max_data_fee(config: &Config, tx: &Transaction) -> Option { + config.has_shard_blob_transactions.then(|| { + tx.max_fee_per_blob_gas + .unwrap_or_default() + .saturating_mul(U256::from(get_total_blob_gas( + tx.blob_versioned_hashes.len(), + ))) + }) +} + +/// Calculates the [EIP-4844] `data_fee` of the transaction. +/// +/// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 +#[inline] +#[must_use] +pub fn calc_data_fee( + config: &Config, + tx: &Transaction, + blob_gas_price: Option<&BlobExcessGasAndPrice>, +) -> Option { + config.has_shard_blob_transactions.then(|| { + U256::from( + blob_gas_price + .expect("expect blob_gas_price") + .blob_gas_price, + ) + .saturating_mul(U256::from(get_total_blob_gas( + tx.blob_versioned_hashes.len(), + ))) + }) +} + +/// See [EIP-4844], [`calc_max_data_fee`] +/// +/// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 +#[inline] +#[must_use] +pub const fn get_total_blob_gas(blob_hashes_len: usize) -> u64 { + GAS_PER_BLOB * blob_hashes_len as u64 +} diff --git a/evm-tests/src/types/eip_4844.rs b/evm-tests/src/types/eip_4844.rs new file mode 100644 index 00000000..3ecdc955 --- /dev/null +++ b/evm-tests/src/types/eip_4844.rs @@ -0,0 +1,102 @@ +//! [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 +use primitive_types::U256; + +/// EIP-4844 constants +/// Gas consumption of a single data blob (== blob byte size). +pub const GAS_PER_BLOB: u64 = 1 << 17; +/// Max number of blobs per block: EIP-7691 +pub const MAX_BLOBS_PER_BLOCK_ELECTRA: u64 = 9; +pub const MAX_BLOBS_PER_BLOCK_CANCUN: u64 = 6; +/// Target consumable blob gas for data blobs per block: EIP-7691 +pub const TARGET_BLOB_GAS_PER_BLOCK: u64 = 786_432; +/// Minimum gas price for data blobs. +pub const MIN_BLOB_GASPRICE: u64 = 1; +/// Controls the maximum rate of change for blob gas price. +pub const BLOB_GASPRICE_UPDATE_FRACTION: u64 = 3_338_477; +/// First version of the blob. +pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01; + +/// Calculates the `excess_blob_gas` from the parent header's `blob_gas_used` and `excess_blob_gas`. +/// +/// See also [the EIP-4844 helpers] +/// (`calc_excess_blob_gas`). +#[inline] +#[must_use] +pub const fn calc_excess_blob_gas(parent_excess_blob_gas: u64, parent_blob_gas_used: u64) -> u64 { + (parent_excess_blob_gas + parent_blob_gas_used).saturating_sub(TARGET_BLOB_GAS_PER_BLOCK) +} + +/// Calculates the blob gas price from the header's excess blob gas field. +/// +/// See also [the EIP-4844 helpers](https://eips.ethereum.org/EIPS/eip-4844#helpers) +/// (`get_blob_gasprice`). +#[inline] +#[must_use] +pub fn calc_blob_gas_price(excess_blob_gas: u64) -> u128 { + fake_exponential( + MIN_BLOB_GASPRICE, + excess_blob_gas, + BLOB_GASPRICE_UPDATE_FRACTION, + ) +} + +/// See [EIP-4844], [`calc_max_data_fee`] +/// +/// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 +#[inline] +#[must_use] +pub const fn get_total_blob_gas(blob_hashes_len: usize) -> u64 { + GAS_PER_BLOB * blob_hashes_len as u64 +} + +/// Calculates the [EIP-4844] `data_fee` of the transaction. +/// +/// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 +#[inline] +#[must_use] +pub fn calc_max_data_fee(max_fee_per_blob_gas: U256, blob_hashes_len: usize) -> U256 { + max_fee_per_blob_gas.saturating_mul(U256::from(get_total_blob_gas(blob_hashes_len))) +} + +/// Calculates the [EIP-4844] `data_fee` of the transaction. +/// +/// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 +#[inline] +#[must_use] +pub fn calc_data_fee(blob_gas_price: u128, blob_hashes_len: usize) -> U256 { + U256::from(blob_gas_price).saturating_mul(U256::from(get_total_blob_gas(blob_hashes_len))) +} + +/// Approximates `factor * e ** (numerator / denominator)` using Taylor expansion. +/// +/// This is used to calculate the blob price. +/// +/// See also [the EIP-4844 helpers](https://eips.ethereum.org/EIPS/eip-4844#helpers) +/// (`fake_exponential`). +/// +/// # Panics +/// +/// This function panics if `denominator` is zero. +/// +/// # NOTES +/// PLEASE DO NOT USE IN PRODUCTION as not checked overflow. For tests only. +#[inline] +#[must_use] +pub fn fake_exponential(factor: u64, numerator: u64, denominator: u64) -> u128 { + assert_ne!(denominator, 0, "attempt to divide by zero"); + let factor = u128::from(factor); + let numerator = u128::from(numerator); + let denominator = u128::from(denominator); + + let mut i = 1; + let mut output = 0; + let mut numerator_accum = factor * denominator; + while numerator_accum > 0 { + output += numerator_accum; + + // Denominator is asserted as not zero at the start of the function. + numerator_accum = (numerator_accum * numerator) / (denominator * i); + i += 1; + } + output / denominator +} diff --git a/evm-tests/src/types/eip_7623.rs b/evm-tests/src/types/eip_7623.rs new file mode 100644 index 00000000..489246ea --- /dev/null +++ b/evm-tests/src/types/eip_7623.rs @@ -0,0 +1,24 @@ +//! EIP-7623 from Prague hard fork + +/// The standard cost of calldata token. +pub const STANDARD_TOKEN_COST: usize = 4; +/// The cost of a non-zero byte in calldata adjusted by [EIP-2028](https://eips.ethereum.org/EIPS/eip-2028). +pub const NON_ZERO_BYTE_DATA_COST: usize = 16; +/// The multiplier for a non zero byte in calldata adjusted by [EIP-2028](https://eips.ethereum.org/EIPS/eip-2028). +pub const NON_ZERO_BYTE_MULTIPLIER: usize = NON_ZERO_BYTE_DATA_COST / STANDARD_TOKEN_COST; +/// The cost floor per token +pub const TOTAL_COST_FLOOR_PER_TOKEN: u64 = 10; + +/// Retrieve the total number of tokens in calldata. +#[must_use] +pub fn get_tokens_in_calldata(input: &[u8]) -> u64 { + let zero_data_len = bytecount::count(input, 0); + let non_zero_data_len = input.len() - zero_data_len; + u64::try_from(zero_data_len + non_zero_data_len * NON_ZERO_BYTE_MULTIPLIER).unwrap() +} + +/// Calculate the transaction cost floor as specified in EIP-7623. +#[must_use] +pub const fn calc_tx_floor_cost(tokens_in_calldata: u64) -> u64 { + tokens_in_calldata * TOTAL_COST_FLOOR_PER_TOKEN + 21_000 +} diff --git a/evm-tests/src/types/eip_7702.rs b/evm-tests/src/types/eip_7702.rs new file mode 100644 index 00000000..4e95291c --- /dev/null +++ b/evm-tests/src/types/eip_7702.rs @@ -0,0 +1,91 @@ +//! EIP-7702 - Prague hard fork +#![allow(clippy::missing_errors_doc)] + +use aurora_engine_precompiles::secp256k1::ecrecover; +use aurora_engine_precompiles::ExitError; +use primitive_types::{H160, H256, U256}; +use rlp::RlpStream; +use sha3::{Digest, Keccak256}; + +pub const MAGIC: u8 = 0x5; +/// The order of the secp256k1 curve, divided by two. Signatures that should be checked according +/// to EIP-2 should have an S value less than or equal to this. +/// +/// `57896044618658097711785492504343953926418782139537452191302581570759080747168` +pub const SECP256K1N_HALF: U256 = U256([ + 0xDFE9_2F46_681B_20A0, + 0x5D57_6E73_57A4_501D, + 0xFFFF_FFFF_FFFF_FFFF, + 0x7FFF_FFFF_FFFF_FFFF, +]); + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Authorization { + pub chain_id: U256, + pub address: H160, + pub nonce: u64, +} + +impl Authorization { + #[must_use] + pub const fn new(chain_id: U256, address: H160, nonce: u64) -> Self { + Self { + chain_id, + address, + nonce, + } + } + + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(3); + s.append(&self.chain_id); + s.append(&self.address); + s.append(&self.nonce); + } + + #[must_use] + pub fn signature_hash(&self) -> H256 { + let mut rlp_stream = RlpStream::new(); + rlp_stream.append(&MAGIC); + self.rlp_append(&mut rlp_stream); + H256::from_slice(<[u8; 32]>::from(Keccak256::digest(rlp_stream.as_raw())).as_slice()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SignedAuthorization { + chain_id: U256, + address: H160, + nonce: u64, + v: bool, + r: U256, + s: U256, +} + +impl SignedAuthorization { + #[must_use] + pub const fn new(chain_id: U256, address: H160, nonce: u64, r: U256, s: U256, v: bool) -> Self { + Self { + chain_id, + address, + nonce, + v, + r, + s, + } + } + + pub fn recover_address(&self) -> Result { + let auth = Authorization::new(self.chain_id, self.address, self.nonce).signature_hash(); + ecrecover(auth, &vrs_to_arr(self.v, self.r, self.s)).map(|a| a.raw()) + } +} + +/// v, r, s signature values to array +fn vrs_to_arr(v: bool, r: U256, s: U256) -> [u8; 65] { + let mut result = [0u8; 65]; // (r, s, v), typed (uint256, uint256, uint8) + result[..32].copy_from_slice(&r.to_big_endian()); + result[32..64].copy_from_slice(&s.to_big_endian()); + result[64] = u8::from(v); + result +} diff --git a/evm-tests/src/types/info.rs b/evm-tests/src/types/info.rs new file mode 100644 index 00000000..66edc730 --- /dev/null +++ b/evm-tests/src/types/info.rs @@ -0,0 +1,41 @@ +use serde::Deserialize; +use std::collections::BTreeMap; + +#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Info { + pub comment: String, + #[serde(default, rename = "filling-rpc-server")] + pub filling_rpc_server: Option, + #[serde(rename = "filling-tool-version")] + pub filling_tool_version: Option, + #[serde(rename = "fixture-format", alias = "fixture_format")] + pub fixture_format: Option, + #[serde(rename = "generatedTestHash")] + pub generated_test_hash: Option, + pub lllcversion: Option, + pub solidity: Option, + pub source: Option, + #[serde(rename = "sourceHash")] + pub source_hash: Option, + pub labels: Option>, + #[serde(rename = "filling-transition-tool")] + pub filling_transition_tool: Option, + pub hash: Option, + pub description: Option, + pub url: Option, + #[serde(rename = "reference-spec")] + pub reference_spec: Option, + #[serde(rename = "reference-spec-version")] + pub reference_spec_version: Option, + #[serde(rename = "eels-resolution")] + pub eels_resolution: Option, +} + +#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq, Deserialize)] +pub struct EelsResolution { + #[serde(rename = "git-url")] + pub git_url: String, + pub branch: String, + pub commit: String, +} diff --git a/evm-tests/src/types/json_utils.rs b/evm-tests/src/types/json_utils.rs new file mode 100644 index 00000000..9dee63f6 --- /dev/null +++ b/evm-tests/src/types/json_utils.rs @@ -0,0 +1,265 @@ +use primitive_types::{H160, H256, U256}; +use serde::de::Error; +use serde::{Deserialize, Deserializer}; +use std::collections::BTreeMap; +use std::fmt::Display; +use std::str::FromStr; + +/// Removes the "0x" prefix from a string if it exists. +pub fn strip_0x_prefix(s: &str) -> &str { + s.strip_prefix("0x").unwrap_or(s) +} + +/// Converts a hexadecimal string into a `u64` value. +/// Returns an error if the string length is greater than 16 or parsing fails. +pub fn u64_from_hex_str<'de, D: Deserializer<'de>>(value: &str) -> Result { + if value.len() > 16 { + return Err(Error::custom(format!( + "u64 value too big (length={})", + value.len() + ))); + } + + u64::from_str_radix(value, 16).map_err(|e| convert_error::(e, "u64")) +} + +/// Converts a hexadecimal string into a `u8` value. +/// Returns an error if the string length is greater than 16 or parsing fails. +pub fn u8_from_hex_str<'de, D: Deserializer<'de>>(value: &str) -> Result { + if value.len() > 16 { + return Err(Error::custom(format!( + "u8 value too big (length={})", + value.len() + ))); + } + + u8::from_str_radix(value, 16).map_err(|e| convert_error::(e, "u8")) +} + +/// Converts a hexadecimal string into a `U256` value. +/// Returns an error if the string length is greater than 64 or parsing fails. +pub fn u256_from_hex_str<'de, D: Deserializer<'de>>(value: &str) -> Result { + if value.len() > 64 { + return Err(Error::custom(format!( + "U256 value too big (length={})", + value.len() + ))); + } + + U256::from_str_radix(value, 16).map_err(|e| convert_error::(e, "U256")) +} + +/// Converts a hexadecimal string into a `H160` value. +/// Returns an error if the string length is greater than 40 or parsing fails. +pub fn h160_from_hex_str<'de, D: Deserializer<'de>>(value: &str) -> Result { + if value.len() > 40 { + return Err(Error::custom(format!( + "H160 value too big (length={})", + value.len() + ))); + } + H160::from_str(value).map_err(|e| convert_error::(e, "H160")) +} + +fn convert_error<'de, D: Deserializer<'de>>(error: impl Display, primitive: &str) -> D::Error { + Error::custom(format!("Invalid {primitive} value: {error}")) +} + +/// Converts a `BTreeMap` with hexadecimal string keys and values into a `BTreeMap` with `U256` keys and values. +/// The hexadecimal strings may optionally start with the "0x" prefix. +/// Returns an error if any key or value cannot be parsed into a U256. +pub fn btree_u256_u256_from_hex_str<'de, D: Deserializer<'de>>( + map_str: BTreeMap, +) -> Result, D::Error> { + let mut map = BTreeMap::new(); + for (k, v) in map_str { + let key = u256_from_hex_str::(strip_0x_prefix(&k))?; + let value = u256_from_hex_str::(strip_0x_prefix(&v))?; + map.insert(key, value); + } + Ok(map) +} + +/// Converts a `BTreeMap` with hexadecimal string keys and values into a `BTreeMap` with keys and values of type H256. +/// The hexadecimal strings may start with the "0x" prefix, which will be removed. +/// Returns an error if any key or value cannot be converted. +pub fn btree_h256_h256_from_str<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let map_str: BTreeMap = BTreeMap::deserialize(deserializer)?; + + let mut map = BTreeMap::new(); + for (k, v) in map_str { + let key_u256 = u256_from_hex_str::(strip_0x_prefix(&k))?; + let value_u256 = u256_from_hex_str::(strip_0x_prefix(&v))?; + let key = H256::from(key_u256.to_big_endian()); + let value = H256::from(value_u256.to_big_endian()); + map.insert(key, value); + } + + Ok(map) +} + +/// Deserializes a hexadecimal string into an H160 address. +/// Returns an error if the string length is greater than 40 or parsing fails. +pub fn deserialize_h160_from_str<'de, D: Deserializer<'de>>( + deserializer: D, +) -> Result { + h160_from_hex_str::(strip_0x_prefix(&String::deserialize(deserializer)?)) +} + +/// Deserializes a hexadecimal string into an `H256` hash by converting it to `U256` first. +/// Returns an error if parsing fails. +pub fn deserialize_h256_from_u256_str<'de, D: Deserializer<'de>>( + deserializer: D, +) -> Result { + let v = u256_from_hex_str::(strip_0x_prefix(&String::deserialize(deserializer)?))?; + Ok(H256::from(v.to_big_endian())) +} + +/// Deserializes a hexadecimal string into a `U256` value. +/// Returns an error if parsing fails. +pub fn deserialize_u256_from_str<'de, D: Deserializer<'de>>( + deserializer: D, +) -> Result { + u256_from_hex_str::(strip_0x_prefix(&String::deserialize(deserializer)?)) +} + +/// Deserializes a hexadecimal string into a `u64` value. +/// Returns an error if parsing fails. +#[allow(dead_code)] +pub fn deserialize_u64_from_str<'de, D: Deserializer<'de>>( + deserializer: D, +) -> Result { + u64_from_hex_str::(strip_0x_prefix(&String::deserialize(deserializer)?)) +} + +/// Deserializes an optional hexadecimal string into an optional `H256` hash. +/// Returns `None` if the value is missing. +pub fn deserialize_h256_from_u256_str_opt<'de, D: Deserializer<'de>>( + deserializer: D, +) -> Result, D::Error> { + Option::::deserialize(deserializer)? + .map(|s| u256_from_hex_str::(strip_0x_prefix(&s)).map(|v| H256::from(v.to_big_endian()))) + .transpose() +} + +/// Deserializes an optional hexadecimal string into an optional `U256` value. +/// Returns `None` if the value is missing. +#[allow(dead_code)] +pub fn deserialize_u256_from_str_opt<'de, D: Deserializer<'de>>( + deserializer: D, +) -> Result, D::Error> { + Option::::deserialize(deserializer)? + .map(|s| u256_from_hex_str::(strip_0x_prefix(&s))) + .transpose() +} + +/// Deserializes an optional hexadecimal string into an optional u64 value. +/// Returns `None` if the value is missing. +pub fn deserialize_u64_from_str_opt<'de, D: Deserializer<'de>>( + deserializer: D, +) -> Result, D::Error> { + Option::::deserialize(deserializer)? + .map(|s| u64_from_hex_str::(strip_0x_prefix(&s))) + .transpose() +} + +/// Deserializes an optional hexadecimal string into an optional u64 value. +/// Returns `None` if the value is missing. +pub fn deserialize_u8_from_str_opt<'de, D: Deserializer<'de>>( + deserializer: D, +) -> Result, D::Error> { + Option::::deserialize(deserializer)? + .map(|s| u8_from_hex_str::(strip_0x_prefix(&s))) + .transpose() +} + +/// Deserializes hexadecimal string into vector of bytes. +/// The hexadecimal string may start with the "0x" prefix. +pub fn deserialize_bytes_from_str<'de, D: Deserializer<'de>>( + deserializer: D, +) -> Result, D::Error> { + let s = String::deserialize(deserializer)?; + hex::decode(strip_0x_prefix(&s)).map_err(|e: hex::FromHexError| Error::custom(e.to_string())) +} + +/// Deserializes a vector of hexadecimal strings into a vector of byte vectors. +/// Each hexadecimal string may start with a "0x" prefix, which will be removed. +/// Returns an error if any string contains invalid hexadecimal characters. +pub fn deserialize_vec_of_hex<'de, D>(deserializer: D) -> Result>, D::Error> +where + D: Deserializer<'de>, +{ + let values: Vec = Vec::deserialize(deserializer)?; + values + .into_iter() + .map(|s| { + hex::decode(strip_0x_prefix(&s)) + .map_err(|e: hex::FromHexError| Error::custom(e.to_string())) + }) + .collect() +} + +/// Deserializes an optional hexadecimal string into an optional vector of bytes. +/// The hexadecimal string may start with the "0x" prefix. +/// Returns `None` if the value is missing. +pub fn deserialize_bytes_from_str_opt<'de, D: Deserializer<'de>>( + deserializer: D, +) -> Result>, D::Error> { + Option::::deserialize(deserializer)? + .map(|s| { + hex::decode(strip_0x_prefix(&s)) + .map_err(|e: hex::FromHexError| Error::custom(e.to_string())) + }) + .transpose() +} + +/// Deserializes an optional `JSON` object with hexadecimal string keys and values into an optional +/// `BTreeMap` with `U256` keys and values. +/// The hexadecimal strings may start with the "0x" prefix. +/// Returns `None` if the value is missing. +#[allow(dead_code)] +pub fn deserialize_btree_u256_u256_from_str_opt<'de, D: Deserializer<'de>>( + deserializer: D, +) -> Result>, D::Error> { + Option::>::deserialize(deserializer)? + .map(btree_u256_u256_from_hex_str::) + .transpose() +} + +/// Deserializes strings to `Vec`. +pub fn deserialize_vec_u256_from_str<'de, D: Deserializer<'de>>( + deserializer: D, +) -> Result, D::Error> { + let vec = Vec::::deserialize(deserializer)?; + vec.into_iter() + .map(|s| u256_from_hex_str::(strip_0x_prefix(&s))) + .collect() +} + +/// Deserializes strings to `Vec`. +#[allow(dead_code)] +pub fn deserialize_vec_h256_from_str<'de, D: Deserializer<'de>>( + deserializer: D, +) -> Result, D::Error> { + let vec = Vec::::deserialize(deserializer)?; + vec.into_iter() + .map(|s| { + let v = u256_from_hex_str::(strip_0x_prefix(&s))?; + Ok(H256::from(v.to_big_endian())) + }) + .collect() +} + +/// Deserializes an optional hexadecimal string into an optional `H160` address. +/// Returns `None` if the value is missing. +pub fn deserialize_h160_from_str_opt<'de, D: Deserializer<'de>>( + deserializer: D, +) -> Result, D::Error> { + Option::::deserialize(deserializer)? + .filter(|s| !s.is_empty()) + .map(|s| h160_from_hex_str::(strip_0x_prefix(&s))) + .transpose() +} diff --git a/evm-tests/src/types/mod.rs b/evm-tests/src/types/mod.rs new file mode 100644 index 00000000..87fd2257 --- /dev/null +++ b/evm-tests/src/types/mod.rs @@ -0,0 +1,276 @@ +use self::account_state::AccountsState; +use self::transaction::Transaction; +use crate::types::blob::BlobExcessGasAndPrice; +use crate::types::json_utils::{ + deserialize_bytes_from_str, deserialize_bytes_from_str_opt, deserialize_h160_from_str, + deserialize_h256_from_u256_str, deserialize_h256_from_u256_str_opt, deserialize_u256_from_str, + deserialize_u64_from_str_opt, +}; +use aurora_evm::backend::MemoryVicinity; +use primitive_types::{H160, H256, U256}; +use serde::Deserialize; +use std::collections::BTreeMap; + +pub mod account_state; +pub mod blob; +pub mod eip_4844; +pub mod eip_7623; +pub mod eip_7702; +mod info; +mod json_utils; +pub mod spec; +pub mod transaction; +mod vm; + +pub use spec::Spec; +pub use vm::VmTestCase; + +/// Represents a test case for the Ethereum state transitions. +/// It includes the environment setup, pre-state, transaction details, +/// expected post-state results for different forks, configuration, and metadata. +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Deserialize)] +pub struct StateTestCase { + /// The environment parameters for the state test execution. + /// This includes block-specific information like coinbase address, difficulty, gas limit, etc. + pub env: StateEnv, + + /// The initial state of accounts before the transaction is executed. + #[serde(rename = "pre")] + pub pre_state: PreState, + + /// The expected state of accounts after the transaction execution for various forks. + /// Maps fork specifications to a list of possible outcomes (results). + /// + /// NOTE: field `config` skipped as it is not used in the current context. + #[serde(rename = "post")] + pub post_states: BTreeMap>, + + /// The transaction(s) to be executed in the test case. + /// Can represent different transaction types across forks. + pub transaction: Transaction, + + #[serde(default, deserialize_with = "deserialize_bytes_from_str_opt")] + pub out: Option>, + + /// Additional information or metadata about the state test. + #[serde(rename = "_info")] + pub info: info::Info, +} + +impl StateTestCase { + /// Get the memory vicinity for the transaction, which includesState test data. + /// + /// # Errors + /// Invalid transaction error status. + pub fn get_memory_vicinity( + &self, + spec: &Spec, + blob_gas_price: Option, + ) -> Result { + let block_base_fee_per_gas = self.env.block_base_fee_per_gas; + let tx = &self.transaction; + // Validation for EIP-1559 that was introduced in London hard fork + let gas_price = if *spec >= Spec::London { + tx.gas_price.or(tx.max_fee_per_gas).unwrap_or_default() + } else { + if tx.max_fee_per_gas.is_some() { + return Err(InvalidTxReason::GasPriceEip1559); + } + tx.gas_price.expect("expect gas price") + }; + + // EIP-1559: priority fee must be lower than gas_price + if let Some(max_priority_fee_per_gas) = tx.max_priority_fee_per_gas { + if max_priority_fee_per_gas > gas_price { + return Err(InvalidTxReason::PriorityFeeTooLarge); + } + } + + let effective_gas_price = self.transaction.max_priority_fee_per_gas.map_or( + gas_price, + |max_priority_fee_per_gas| { + gas_price.min(max_priority_fee_per_gas + block_base_fee_per_gas) + }, + ); + + // the gas price cannot be lower than the base fee + if gas_price < block_base_fee_per_gas { + return Err(InvalidTxReason::GasPriceLessThanBlockBaseFee); + } + + let blob_hashes = tx.blob_versioned_hashes.clone(); + + Ok(MemoryVicinity { + gas_price, + effective_gas_price, + origin: self.transaction.get_caller_from_secret_key(), + block_hashes: Vec::new(), + block_number: self.env.block_number, + block_coinbase: self.env.block_coinbase, + block_timestamp: self.env.block_timestamp, + block_difficulty: self.env.block_difficulty, + block_gas_limit: self.env.block_gas_limit, + chain_id: U256::one(), + block_base_fee_per_gas, + block_randomness: self.env.random, + blob_gas_price: blob_gas_price.map(|bgp| bgp.blob_gas_price), + blob_hashes, + }) + } +} + +/// Represents the environment parameters under which a state test is executed. +/// These parameters typically correspond to the fields of a block header. +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct StateEnv { + /// The difficulty of the current block. + #[serde( + rename = "currentDifficulty", + deserialize_with = "deserialize_u256_from_str" + )] + pub block_difficulty: U256, + /// The address of the beneficiary account (miner) to whom the block rewards are transferred. + #[serde( + rename = "currentCoinbase", + deserialize_with = "deserialize_h160_from_str" + )] + pub block_coinbase: H160, + /// The gas limit for the current block, setting the maximum amount of gas that can be + /// consumed by transactions in the block. + #[serde( + rename = "currentGasLimit", + deserialize_with = "deserialize_u256_from_str" + )] + pub block_gas_limit: U256, + /// The number of the current block in the blockchain. + #[serde( + rename = "currentNumber", + deserialize_with = "deserialize_u256_from_str" + )] + pub block_number: U256, + /// The timestamp of the current block, typically representing the time when the block was mined. + #[serde( + rename = "currentTimestamp", + deserialize_with = "deserialize_u256_from_str" + )] + pub block_timestamp: U256, + /// The base fee per gas for the current block, as introduced in EIP-1559. + /// This value adjusts based on network congestion. + #[serde( + default, + rename = "currentBaseFee", + deserialize_with = "deserialize_u256_from_str" + )] + pub block_base_fee_per_gas: U256, + /// A pre-seeded random value (mix hash) used for testing purposes, particularly relevant + /// before the Merge (transition to Proof-of-Stake). + #[serde( + default, + rename = "currentRandom", + deserialize_with = "deserialize_h256_from_u256_str_opt" + )] + pub random: Option, + + /// The amount of blob gas used by the parent block. Relevant for EIP-4844. + #[serde(default, deserialize_with = "deserialize_u64_from_str_opt")] + pub parent_blob_gas_used: Option, + /// The excess blob gas of the parent block. Relevant for EIP-4844. + #[serde(default, deserialize_with = "deserialize_u64_from_str_opt")] + pub parent_excess_blob_gas: Option, + /// The excess blob gas for the current block being processed. Relevant for EIP-4844. + #[serde(default, deserialize_with = "deserialize_u64_from_str_opt")] + pub current_excess_blob_gas: Option, +} + +impl From for MemoryVicinity { + fn from(env: StateEnv) -> Self { + Self { + gas_price: U256::zero(), // Gas price is not used in state tests + effective_gas_price: U256::zero(), // Effective gas price is not used in state tests + origin: H160::default(), // Origin is not used in state tests + block_hashes: Vec::new(), // Block hashes are not used in state tests + block_number: env.block_number, + block_coinbase: env.block_coinbase, + block_timestamp: env.block_timestamp, + block_difficulty: env.block_difficulty, + block_gas_limit: env.block_gas_limit, + chain_id: U256::zero(), // Chain ID is not used in state tests + block_base_fee_per_gas: env.block_base_fee_per_gas, + block_randomness: env.random, + blob_gas_price: None, // Blob gas price is not used in state tests + blob_hashes: Vec::new(), // Blob hashes are not used in state tests + } + } +} + +/// `PreState` represents a sorted mapping from Ethereum account addresses (`H160`) to their +/// corresponding state (`StateAccount`). +/// Represents vis `AccountsState`. +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Deserialize)] +pub struct PreState(AccountsState); + +impl AsRef for PreState { + fn as_ref(&self) -> &AccountsState { + &self.0 + } +} + +#[derive(Debug, Eq, Ord, PartialOrd, PartialEq, Clone, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct PostState { + /// Post state hash + #[serde(deserialize_with = "deserialize_h256_from_u256_str")] + pub hash: H256, + /// Post state logs + #[serde(deserialize_with = "deserialize_h256_from_u256_str")] + pub logs: H256, + /// Indexes + pub indexes: PostStateIndexes, + /// Expected error if the test is meant to fail + #[serde(default)] + pub expect_exception: Option, + /// Transaction bytes + #[serde(rename = "txbytes", deserialize_with = "deserialize_bytes_from_str")] + pub tx_bytes: Vec, + /// Output Accounts state + #[serde(default)] + pub state: Option, + /// Post Accounts state + #[serde(default)] + pub post_state: Option, +} + +/// Post State indexes. +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Deserialize)] +pub struct PostStateIndexes { + /// Index into transaction data set. + pub data: usize, + /// Index into transaction gas limit set. + pub gas: usize, + /// Index into transaction value set. + pub value: usize, +} + +#[derive(Debug)] +pub enum InvalidTxReason { + IntrinsicGas, + OutOfFund, + GasLimitReached, + PriorityFeeTooLarge, + GasPriceLessThanBlockBaseFee, + BlobCreateTransaction, + BlobVersionNotSupported, + TooManyBlobs, + EmptyBlobs, + BlobGasPriceGreaterThanMax, + BlobVersionedHashesNotSupported, + MaxFeePerBlobGasNotSupported, + GasPriceEip1559, + AuthorizationListNotExist, + AuthorizationListNotSupported, + InvalidAuthorizationChain, + InvalidAuthorizationSignature, + CreateTransaction, + GasFloorMoreThanGasLimit, +} diff --git a/evm-tests/src/types/spec.rs b/evm-tests/src/types/spec.rs new file mode 100644 index 00000000..22777393 --- /dev/null +++ b/evm-tests/src/types/spec.rs @@ -0,0 +1,139 @@ +use aurora_evm::Config; +use serde::de::{self, Visitor}; +use serde::{Deserialize, Deserializer}; +use std::fmt; +use std::str::FromStr; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum Spec { + /// Frontier hard fork + /// Activated at block 0 + Frontier, + /// Homestead hard fork + /// Activated at block 1150000 + Homestead, + /// Tangerine Whistle hard fork + /// Activated at block 2463000 + Tangerine, + /// Spurious Dragon hard fork + /// Activated at block 2675000 + SpuriousDragon, + /// Byzantium hard fork + /// Activated at block 4370000 + Byzantium, + /// Constantinople hard fork + /// Activated at block 7280000 is overwritten with PETERSBURG + Constantinople, + /// Petersburg hard fork + /// Activated at block 7280000 + Petersburg, + /// Istanbul hard fork + /// Activated at block 9069000 + Istanbul, + /// Berlin hard fork + /// Activated at block 12244000 + Berlin, + /// London hard fork + /// Activated at block 12965000 + London, + /// Paris/Merge hard fork + /// Activated at block 15537394 (TTD: 58750000000000000000000) + Merge, + /// Shanghai hard fork + /// Activated at block 17034870 (Timestamp: 1681338455) + Shanghai, + /// Cancun hard fork + /// Activated at block 19426587 (Timestamp: 1710338135) + Cancun, + /// Prague hard fork + /// Activated at block 22431084 (Timestamp: 1746612311) + Prague, + /// Osaka hard fork + /// Activated at block TBD + Osaka, +} + +impl Spec { + #[must_use] + pub const fn is_filtered_spec_for_skip(&self) -> bool { + matches!( + self, + Self::Tangerine + | Self::SpuriousDragon + | Self::Frontier + | Self::Homestead + | Self::Byzantium + | Self::Constantinople + | Self::Istanbul + | Self::Berlin + ) + } + + #[must_use] + pub(crate) const fn get_gasometer_config(&self) -> Option { + match self { + Self::Istanbul => Some(Config::istanbul()), + Self::Berlin => Some(Config::berlin()), + Self::London => Some(Config::london()), + Self::Merge => Some(Config::merge()), + Self::Shanghai => Some(Config::shanghai()), + Self::Cancun => Some(Config::cancun()), + Self::Prague => Some(Config::prague()), + _ => None, + } + } +} + +impl FromStr for Spec { + type Err = String; + + fn from_str(value: &str) -> Result { + match value { + "Frontier" => Ok(Self::Frontier), + "Homestead" | "FrontierToHomesteadAt5" => Ok(Self::Homestead), + "EIP150" | "HomesteadToDaoAt5" | "HomesteadToEIP150At5" => Ok(Self::Tangerine), + "EIP158" => Ok(Self::SpuriousDragon), + "Byzantium" | "EIP158ToByzantiumAt5" => Ok(Self::Byzantium), + "Constantinople" + | "ConstantinopleFix" + | "ByzantiumToConstantinopleAt5" + | "ByzantiumToConstantinopleFixAt5" => Ok(Self::Constantinople), + "Petersburg" => Ok(Self::Petersburg), + "Istanbul" => Ok(Self::Istanbul), + "Berlin" => Ok(Self::Berlin), + "London" | "BerlinToLondonAt5" => Ok(Self::London), + "Merge" | "Paris" => Ok(Self::Merge), + "Shanghai" => Ok(Self::Shanghai), + "Cancun" => Ok(Self::Cancun), + "Prague" => Ok(Self::Prague), + "Osaka" => Ok(Self::Osaka), + _ => Err(format!("Unknown Spec value: {value}")), + } + } +} +impl<'de> Deserialize<'de> for Spec { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct SpecVisitor; + + impl Visitor<'_> for SpecVisitor { + type Value = Spec; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("Ethereum hard fork name") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + Spec::from_str(value) + .map_err(|_| de::Error::invalid_value(de::Unexpected::Str(value), &self)) + } + } + + deserializer.deserialize_str(SpecVisitor) + } +} diff --git a/evm-tests/src/types/transaction.rs b/evm-tests/src/types/transaction.rs new file mode 100644 index 00000000..7d1a64f2 --- /dev/null +++ b/evm-tests/src/types/transaction.rs @@ -0,0 +1,391 @@ +use crate::types::blob::BlobExcessGasAndPrice; +use crate::types::json_utils::{ + deserialize_bytes_from_str_opt, deserialize_h160_from_str, deserialize_h160_from_str_opt, + deserialize_h256_from_u256_str_opt, deserialize_u256_from_str, deserialize_u256_from_str_opt, + deserialize_u8_from_str_opt, deserialize_vec_of_hex, deserialize_vec_u256_from_str, +}; +use crate::types::{eip_4844, eip_7623, eip_7702, InvalidTxReason, PostState, Spec}; +use aurora_evm::backend::MemoryVicinity; +use aurora_evm::executor::stack::Authorization; +use aurora_evm::gasometer; +use aurora_evm::gasometer::Gasometer; +use primitive_types::{H160, H256, U256}; +use serde::Deserialize; +use sha3::Digest; + +/// Transaction data. +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Transaction { + #[serde( + default, + rename = "type", + deserialize_with = "deserialize_u8_from_str_opt" + )] + pub tx_type: Option, + #[serde(deserialize_with = "deserialize_vec_of_hex")] + pub data: Vec>, + #[serde(deserialize_with = "deserialize_vec_u256_from_str")] + pub gas_limit: Vec, + #[serde(default, deserialize_with = "deserialize_u256_from_str_opt")] + pub gas_price: Option, + #[serde(deserialize_with = "deserialize_u256_from_str")] + pub nonce: U256, + #[serde(default, deserialize_with = "deserialize_h256_from_u256_str_opt")] + pub secret_key: Option, + #[serde(default, deserialize_with = "deserialize_h160_from_str_opt")] + pub sender: Option, + #[serde(default, deserialize_with = "deserialize_h160_from_str_opt")] + pub to: Option, + #[serde(deserialize_with = "deserialize_vec_u256_from_str")] + pub value: Vec, + /// for details on `maxFeePerGas` see EIP-1559 + #[serde(default, deserialize_with = "deserialize_u256_from_str_opt")] + pub max_fee_per_gas: Option, + /// for details on `maxPriorityFeePerGas` see EIP-1559 + #[serde(default, deserialize_with = "deserialize_u256_from_str_opt")] + pub max_priority_fee_per_gas: Option, + #[serde( + default, + rename = "initcodes", + deserialize_with = "deserialize_bytes_from_str_opt" + )] + pub init_codes: Option>, + + /// EIP-2930 + #[serde(default)] + pub access_lists: Vec>, + + /// EIP-4844 + #[serde(default, deserialize_with = "deserialize_vec_u256_from_str")] + pub blob_versioned_hashes: Vec, + /// EIP-4844 + #[serde(default, deserialize_with = "deserialize_u256_from_str_opt")] + pub max_fee_per_blob_gas: Option, + /// EIP-7702 + #[serde(default)] + pub authorization_list: Option, +} + +impl Transaction { + /// Get `data` from with state data + #[must_use] + pub fn get_data(&self, state: &PostState) -> Vec { + self.data[state.indexes.data].clone() + } + + /// Get `gas_limit` from with state data + #[must_use] + pub fn get_gas_limit(&self, state: &PostState) -> U256 { + self.gas_limit[state.indexes.gas] + } + + /// Get `value` from with state data + #[must_use] + pub fn get_value(&self, state: &PostState) -> U256 { + self.value[state.indexes.value] + } + + /// Get `access_list` from with state data + #[must_use] + pub fn get_access_list(&self, state: &PostState) -> Vec<(H160, Vec)> { + if state.indexes.data < self.access_lists.len() { + self.access_lists + .get(state.indexes.data) + .unwrap() + .clone() + .unwrap_or_default() + .into_iter() + .map(|a| (a.address, a.storage_keys)) + .collect() + } else { + Vec::new() + } + } + + /// Get caller from transaction's secret key. + /// + /// # Panics + /// If the transaction secret is missing or if parsing the secret key fails. + #[must_use] + pub fn get_caller_from_secret_key(&self) -> H160 { + let hash = self.secret_key.unwrap(); + let mut secret_key = [0; 32]; + secret_key.copy_from_slice(hash.as_bytes()); + let secret = libsecp256k1::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( + <[u8; 32]>::from(sha3::Keccak256::digest(res)).as_slice(), + )) + } + + fn intrinsic_gas(&self, config: &aurora_evm::Config, state: &PostState) -> Option { + let is_contract_creation = self.to.is_none(); + let data = &self.get_data(state); + let access_list = self.get_access_list(state); + + // EIP-7702 + let authorization_list_len = self.authorization_list.as_ref().map_or(0, Vec::len); + + let cost = if is_contract_creation { + gasometer::create_transaction_cost(data, &access_list) + } else { + gasometer::call_transaction_cost(data, &access_list, authorization_list_len) + }; + + let mut g = Gasometer::new(u64::MAX, config); + g.record_transaction(cost).ok()?; + + Some(g.total_used_gas()) + } + + /// Validate the transaction against block, payment, and EIP constraints. + /// + /// # Errors + /// Returns `InvalidTxReason` if validation fails. + #[allow(clippy::too_many_lines, clippy::too_many_arguments)] + pub fn validate( + &self, + block_gas_limit: U256, + caller_balance: U256, + config: &aurora_evm::Config, + vicinity: &MemoryVicinity, + blob_gas_price: Option, + data_fee: Option, + spec: &Spec, + state: &PostState, + ) -> Result, InvalidTxReason> { + let gas_limit = self.get_gas_limit(state); + let mut authorization_list: Vec = vec![]; + match self.intrinsic_gas(config, state) { + None => return Err(InvalidTxReason::IntrinsicGas), + Some(required_gas) => { + if gas_limit < U256::from(required_gas) { + return Err(InvalidTxReason::IntrinsicGas); + } + } + } + + if block_gas_limit < gas_limit { + return Err(InvalidTxReason::GasLimitReached); + } + + let required_funds = gas_limit + .checked_mul(vicinity.gas_price) + .ok_or(InvalidTxReason::OutOfFund)? + .checked_add(self.get_value(state)) + .ok_or(InvalidTxReason::OutOfFund)?; + + let required_funds = if let Some(data_fee) = data_fee { + required_funds + .checked_add(data_fee) + .ok_or(InvalidTxReason::OutOfFund)? + } else { + required_funds + }; + if caller_balance < required_funds { + return Err(InvalidTxReason::OutOfFund); + } + + // CANCUN tx validation + // Presence of max_fee_per_blob_gas means that this is a blob transaction. + if *spec >= Spec::Cancun { + if let Some(max) = self.max_fee_per_blob_gas { + // ensure that the user was willing to at least pay the current blob gasprice + if U256::from( + blob_gas_price + .expect("expect blob_gas_price") + .blob_gas_price, + ) > max + { + return Err(InvalidTxReason::BlobGasPriceGreaterThanMax); + } + + // there must be at least one blob + if self.blob_versioned_hashes.is_empty() { + return Err(InvalidTxReason::EmptyBlobs); + } + + // The field `to` deviates slightly from the semantics with the exception + // that it MUST NOT be nil and therefore must always represent + // a 20-byte address. This means that blob transactions cannot + // have the form of a `create` transaction. + if self.to.is_none() { + return Err(InvalidTxReason::BlobCreateTransaction); + } + + // all versioned blob hashes must start with VERSIONED_HASH_VERSION_KZG + for blob in &self.blob_versioned_hashes { + let blob_hash = H256(blob.to_big_endian()); + if blob_hash[0] != eip_4844::VERSIONED_HASH_VERSION_KZG { + return Err(InvalidTxReason::BlobVersionNotSupported); + } + } + + // ensure the total blob gas spent is at most equal to the limit + // assert blob_gas_used <= MAX_BLOB_GAS_PER_BLOCK + // EIP-7691 + let max_blob_len = if *spec == Spec::Cancun { + eip_4844::MAX_BLOBS_PER_BLOCK_CANCUN + } else { + eip_4844::MAX_BLOBS_PER_BLOCK_ELECTRA + }; + if self.blob_versioned_hashes.len() > usize::try_from(max_blob_len).unwrap() { + return Err(InvalidTxReason::TooManyBlobs); + } + } + } else { + if !self.blob_versioned_hashes.is_empty() { + return Err(InvalidTxReason::BlobVersionedHashesNotSupported); + } + if self.max_fee_per_blob_gas.is_some() { + return Err(InvalidTxReason::MaxFeePerBlobGasNotSupported); + } + } + + if *spec >= Spec::Prague { + // EIP-7623 validation + let floor_gas = eip_7623::calc_tx_floor_cost(eip_7623::get_tokens_in_calldata( + &self.get_data(state), + )); + if floor_gas > gas_limit.as_u64() { + return Err(InvalidTxReason::GasFloorMoreThanGasLimit); + } + + let tx_authorization_list = self.authorization_list.clone().unwrap_or_default(); + + // EIP-7702 - if transaction type is EOAAccountCode then + // `authorization_list` must be present + if TxType::from_tx_bytes(&state.tx_bytes) == TxType::EOAAccountCode + && tx_authorization_list.is_empty() + { + return Err(InvalidTxReason::AuthorizationListNotExist); + } + + // Check EIP-7702 Spec validation steps: 1 and 2 + // Other validation step inside EVM transact logic. + for auth in &tx_authorization_list { + // 1. Verify the chain id is either 0 or the chain’s current ID. + let mut is_valid = auth.chain_id <= U256::from(u64::MAX) + && (auth.chain_id == U256::from(0) || auth.chain_id == vicinity.chain_id); + + // 3. `authority = ecrecover(keccak(MAGIC || rlp([chain_id, address, nonce])), y_parity, r, s]` + // Validate the signature, as in tests it is possible to have invalid signatures values. + // Value `v` shouldn't be greater then 1 + let v = auth.v; + if v > U256::from(1) { + is_valid = false; + } + + // EIP-2 validation + if auth.s > eip_7702::SECP256K1N_HALF { + is_valid = false; + } + + let auth_address = eip_7702::SignedAuthorization::new( + auth.chain_id, + auth.address, + auth.nonce.as_u64(), + auth.r, + auth.s, + auth.v.as_u32() > 0, + ) + .recover_address(); + let auth_address = auth_address.unwrap_or_else(|_| { + is_valid = false; + H160::zero() + }); + + authorization_list.push(Authorization { + authority: auth_address, + address: auth.address, + nonce: auth.nonce.as_u64(), + is_valid, + }); + } + } else if self.authorization_list.is_some() { + return Err(InvalidTxReason::AuthorizationListNotSupported); + } + Ok(authorization_list) + } +} + +/// Type alias for access lists (see EIP-2930) +pub type AccessList = Vec; + +/// Access list tuple (see ). +#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AccessListTuple { + /// Address to access + #[serde(deserialize_with = "deserialize_h160_from_str")] + pub address: H160, + /// Keys (slots) to access at that address + pub storage_keys: Vec, +} + +/// EIP-7702 Authorization List +pub type AuthorizationList = Vec; +/// EIP-7702 Authorization item +#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AuthorizationItem { + /// Chain ID + #[serde(deserialize_with = "deserialize_u256_from_str")] + pub chain_id: U256, + /// Address to access + #[serde(deserialize_with = "deserialize_h160_from_str")] + pub address: H160, + /// Keys (slots) to access at that address + #[serde(deserialize_with = "deserialize_u256_from_str")] + pub nonce: U256, + /// r signature + #[serde(deserialize_with = "deserialize_u256_from_str")] + pub r: U256, + /// s signature + #[serde(deserialize_with = "deserialize_u256_from_str")] + pub s: U256, + /// Parity + #[serde(deserialize_with = "deserialize_u256_from_str")] + pub v: U256, + /// Signer address + #[serde(default, deserialize_with = "deserialize_h160_from_str_opt")] + pub signer: Option, +} + +/// Denotes the type of transaction. +#[derive(Debug, PartialEq, Eq)] +pub enum TxType { + /// All transactions before EIP-2718 are legacy. + Legacy, + /// + AccessList, + /// + DynamicFee, + /// + ShardBlob, + /// + EOAAccountCode, +} + +impl TxType { + /// Whether this is a legacy, access list, dynamic fee, etc. transaction + /// Taken from geth's core/types/transaction.go/UnmarshalBinary, but we only detect the transaction + /// type rather than unmarshal the entire payload. + #[must_use] + pub const fn from_tx_bytes(tx_bytes: &[u8]) -> Self { + match tx_bytes[0] { + b if b > 0x7f => Self::Legacy, + 1 => Self::AccessList, + 2 => Self::DynamicFee, + 3 => Self::ShardBlob, + 4 => Self::EOAAccountCode, + _ => panic!( + "Unknown tx type. You may need to update the TxType enum if Ethereum introduced new enveloped transaction types." + ), + } + } +} diff --git a/evm-tests/src/types/vm.rs b/evm-tests/src/types/vm.rs new file mode 100644 index 00000000..354e21c0 --- /dev/null +++ b/evm-tests/src/types/vm.rs @@ -0,0 +1,156 @@ +use super::{AccountsState, StateEnv}; +use crate::types::json_utils::{ + deserialize_bytes_from_str, deserialize_bytes_from_str_opt, deserialize_h160_from_str, + deserialize_h160_from_str_opt, deserialize_h256_from_u256_str_opt, deserialize_u256_from_str, + deserialize_u256_from_str_opt, +}; +use aurora_evm::backend::{MemoryAccount, MemoryVicinity}; +use primitive_types::{H160, H256, U256}; +use serde::Deserialize; +use std::collections::BTreeMap; + +/// Represents vm execution environment before and after execution of transaction. +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Deserialize)] +pub struct VmTestCase { + /// Contract calls made internally by executed transaction. + #[serde(rename = "callcreates")] + pub calls: Option>, + /// Env info. + pub env: StateEnv, + /// Executed transaction + #[serde(rename = "exec")] + pub transaction: ExecutionTransaction, + /// Gas left after transaction execution. + #[serde( + rename = "gas", + default, + deserialize_with = "deserialize_u256_from_str_opt" + )] + pub gas_left: Option, + /// Hash of logs created during execution of the transaction. + #[serde(default, deserialize_with = "deserialize_h256_from_u256_str_opt")] + pub logs: Option, + /// Transaction output. + #[serde( + default, + rename = "out", + deserialize_with = "deserialize_bytes_from_str_opt" + )] + pub output: Option>, + /// Post execution vm state. + #[serde(rename = "post")] + pub post_state: Option, + /// Pre execution vm state. + #[serde(rename = "pre")] + pub pre_state: AccountsState, +} + +impl VmTestCase { + #[must_use] + pub fn get_output(&self) -> Vec { + self.output.clone().unwrap() + } + + #[must_use] + pub fn get_gas_left(&self) -> u64 { + self.gas_left.unwrap().as_u64() + } + + #[must_use] + pub fn get_gas_limit(&self) -> u64 { + self.transaction.gas.as_u64() + } + + #[must_use] + pub fn validate_state(&self, state: &BTreeMap) -> bool { + &self + .post_state + .clone() + .unwrap() + .to_memory_accounts_state() + .0 + == state + } + + #[must_use] + pub const fn get_memory_vicinity(&self) -> MemoryVicinity { + MemoryVicinity { + gas_price: self.transaction.gas_price, + effective_gas_price: self.transaction.gas_price, + origin: self.transaction.origin, + block_hashes: Vec::new(), + block_number: self.env.block_number, + block_coinbase: self.env.block_coinbase, + block_timestamp: self.env.block_timestamp, + block_difficulty: self.env.block_difficulty, + block_gas_limit: self.env.block_gas_limit, + chain_id: U256::zero(), + block_base_fee_per_gas: self.transaction.gas_price, + block_randomness: self.env.random, + blob_gas_price: None, + blob_hashes: Vec::new(), + } + } +} + +/// Call deserialization. +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Call { + /// Call data. + #[serde(deserialize_with = "deserialize_bytes_from_str")] + pub data: Vec, + /// Call destination. + #[serde(default, deserialize_with = "deserialize_h160_from_str_opt")] + pub destination: Option, + /// Gas limit. + #[serde(deserialize_with = "deserialize_u256_from_str")] + pub gas_limit: U256, + /// Call value. + #[serde(deserialize_with = "deserialize_u256_from_str")] + pub value: U256, +} + +/// Executed transaction. +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct ExecutionTransaction { + /// Contract address. + #[serde(deserialize_with = "deserialize_h160_from_str")] + pub address: H160, + /// Transaction sender. + #[serde(rename = "caller", deserialize_with = "deserialize_h160_from_str")] + pub sender: H160, + /// Contract code. + #[serde(deserialize_with = "deserialize_bytes_from_str")] + pub code: Vec, + /// Input data. + #[serde(deserialize_with = "deserialize_bytes_from_str")] + pub data: Vec, + /// Gas. + #[serde(deserialize_with = "deserialize_u256_from_str")] + pub gas: U256, + /// Gas price. + #[serde(deserialize_with = "deserialize_u256_from_str")] + pub gas_price: U256, + /// Transaction origin. + #[serde(deserialize_with = "deserialize_h160_from_str")] + pub origin: H160, + /// Sent value. + #[serde(deserialize_with = "deserialize_u256_from_str")] + pub value: U256, + /// Contract code version. + #[serde(default, deserialize_with = "deserialize_u256_from_str")] + pub code_version: U256, +} + +impl ExecutionTransaction { + #[must_use] + pub const fn get_context(&self) -> aurora_evm::Context { + aurora_evm::Context { + address: self.address, + caller: self.sender, + apparent_value: self.value, + } + } +} diff --git a/evm-tests/src/vm.rs b/evm-tests/src/vm.rs new file mode 100644 index 00000000..bbaf04fa --- /dev/null +++ b/evm-tests/src/vm.rs @@ -0,0 +1,99 @@ +use crate::config::VerboseOutput; +use crate::execution_results::TestExecutionResult; +use crate::types::VmTestCase; +use aurora_evm::backend::{ApplyBackend, MemoryBackend}; +use aurora_evm::executor::stack::{MemoryStackState, StackExecutor, StackSubstateMetadata}; +use aurora_evm::Config; +use std::collections::BTreeMap; +use std::io::{self, Write}; +use std::rc::Rc; + +#[must_use] +pub fn test(verbose_output: &VerboseOutput, name: &str, test: &VmTestCase) -> TestExecutionResult { + let mut result = TestExecutionResult::new(); + let mut failed = false; + result.total = 1; + if verbose_output.verbose { + print!("Running test {name} ... "); + io::stdout().flush().expect("Could not flush stdout"); + } + + let original_state = test.pre_state.to_memory_accounts_state(); + let vicinity = test.get_memory_vicinity(); + let config = Config::frontier(); + let mut backend = MemoryBackend::new(&vicinity, original_state.0); + let metadata = StackSubstateMetadata::new(test.get_gas_limit(), &config); + let state = MemoryStackState::new(metadata, &backend); + let precompile = BTreeMap::new(); + let mut executor = StackExecutor::new_with_precompiles(state, &config, &precompile); + + let mut runtime = aurora_evm::Runtime::new( + Rc::new(test.transaction.code.clone()), + Rc::new(test.transaction.data.clone()), + test.transaction.get_context(), + config.stack_limit, + config.memory_limit, + ); + + let reason = executor.execute(&mut runtime); + let gas = executor.gas(); + let (values, logs) = executor.into_state().deconstruct(); + backend.apply(values, logs, false); + + if test.output.is_none() { + if verbose_output.verbose { + print!("{reason:?} "); + } + + if reason.is_succeed() { + failed = true; + if verbose_output.verbose_failed { + print!("[Failed: succeed for empty output: {reason:?}] "); + } + } + if !(test.post_state.is_none() && test.gas_left.is_none()) { + failed = true; + if verbose_output.verbose_failed { + print!("[Failed: not empty state and left gas for empty output: {reason:?}] ",); + } + } + } else { + let expected_post_gas = test.get_gas_left(); + if verbose_output.verbose { + print!("{reason:?} "); + } + + if runtime.machine().return_value() != test.get_output() { + failed = true; + if verbose_output.verbose_failed { + print!( + "[Failed: wrong return value: {:?}] ", + runtime.machine().return_value() + ); + } + } + + if !test.validate_state(backend.state()) { + failed = true; + if verbose_output.verbose_failed { + print!("[Failed: invalid state] "); + } + } + if gas != expected_post_gas { + failed = true; + if verbose_output.verbose_failed { + print!("[Failed: unexpected gas: {gas:?}] "); + } + } + } + + if failed { + result.failed += 1; + if verbose_output.verbose || verbose_output.verbose_failed { + println!("failed <-------"); + } + } else if verbose_output.verbose { + println!("succeed"); + } + result +} diff --git a/evm/src/backend/memory.rs b/evm/src/backend/memory.rs index 931b0bbf..d59ab5c6 100644 --- a/evm/src/backend/memory.rs +++ b/evm/src/backend/memory.rs @@ -92,7 +92,7 @@ impl<'vicinity> MemoryBackend<'vicinity> { } /// Get a mutable reference to the underlying `BTreeMap` storing the state. - pub fn state_mut(&mut self) -> &mut BTreeMap { + pub const fn state_mut(&mut self) -> &mut BTreeMap { &mut self.state } } @@ -173,7 +173,7 @@ impl Backend for MemoryBackend<'_> { fn is_empty_storage(&self, address: H160) -> bool { self.state .get(&address) - .map_or(true, |v| v.storage.is_empty()) + .is_none_or(|v| v.storage.is_empty()) } fn original_storage(&self, address: H160, index: H256) -> Option { diff --git a/evm/src/core/mod.rs b/evm/src/core/mod.rs index 9b1145ee..ee065be6 100644 --- a/evm/src/core/mod.rs +++ b/evm/src/core/mod.rs @@ -74,7 +74,7 @@ impl Machine { &self.stack } /// Mutable reference of machine stack. - pub fn stack_mut(&mut self) -> &mut Stack { + pub const fn stack_mut(&mut self) -> &mut Stack { &mut self.stack } /// Reference of machine memory. @@ -83,7 +83,7 @@ impl Machine { &self.memory } /// Mutable reference of machine memory. - pub fn memory_mut(&mut self) -> &mut Memory { + pub const fn memory_mut(&mut self) -> &mut Memory { &mut self.memory } /// Return a reference of the program counter. diff --git a/evm/src/executor/stack/executor.rs b/evm/src/executor/stack/executor.rs index b4c3bb15..8453b507 100644 --- a/evm/src/executor/stack/executor.rs +++ b/evm/src/executor/stack/executor.rs @@ -247,7 +247,7 @@ impl<'config> StackSubstateMetadata<'config> { &self.gasometer } - pub fn gasometer_mut(&mut self) -> &mut Gasometer<'config> { + pub const fn gasometer_mut(&mut self) -> &mut Gasometer<'config> { &mut self.gasometer } @@ -445,7 +445,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> &self.state } - pub fn state_mut(&mut self) -> &mut S { + pub const fn state_mut(&mut self) -> &mut S { &mut self.state } @@ -713,7 +713,8 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> } } - let code_hash = H256::from_slice(Keccak256::digest(&init_code).as_slice()); + let code_hash = + H256::from_slice(<[u8; 32]>::from(Keccak256::digest(&init_code)).as_slice()); let address = self.create_address(CreateScheme::Create2 { caller, code_hash, @@ -887,14 +888,15 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> hasher.update(&caller[..]); hasher.update(&salt[..]); hasher.update(&code_hash[..]); - H256::from_slice(hasher.finalize().as_slice()).into() + H256::from_slice(<[u8; 32]>::from(hasher.finalize()).as_slice()).into() } CreateScheme::Legacy { caller } => { let nonce = self.nonce(caller); let mut stream = rlp::RlpStream::new_list(2); stream.append(&caller); stream.append(&nonce); - H256::from_slice(Keccak256::digest(stream.out()).as_slice()).into() + H256::from_slice(<[u8; 32]>::from(Keccak256::digest(stream.out())).as_slice()) + .into() } CreateScheme::Fixed(address) => address, } @@ -1492,7 +1494,7 @@ impl<'config, S: StackState<'config>, P: PrecompileSet> Handler return H256::default(); } let code = self.code(address); - H256::from_slice(Keccak256::digest(code).as_slice()) + H256::from_slice(<[u8; 32]>::from(Keccak256::digest(code)).as_slice()) } /// Get account code diff --git a/evm/src/executor/stack/memory.rs b/evm/src/executor/stack/memory.rs index a0e4e249..b03d5781 100644 --- a/evm/src/executor/stack/memory.rs +++ b/evm/src/executor/stack/memory.rs @@ -48,7 +48,7 @@ impl<'config> MemoryStackSubstate<'config> { &self.logs } - pub fn logs_mut(&mut self) -> &mut Vec { + pub const fn logs_mut(&mut self) -> &mut Vec { &mut self.logs } @@ -57,7 +57,7 @@ impl<'config> MemoryStackSubstate<'config> { &self.metadata } - pub fn metadata_mut(&mut self) -> &mut StackSubstateMetadata<'config> { + pub const fn metadata_mut(&mut self) -> &mut StackSubstateMetadata<'config> { &mut self.metadata } @@ -316,9 +316,7 @@ impl<'config> MemoryStackSubstate<'config> { if local_is_accessed { false } else { - self.parent - .as_ref() - .map_or(true, |p| p.recursive_is_cold(f)) + self.parent.as_ref().is_none_or(|p| p.recursive_is_cold(f)) } } diff --git a/evm/src/runtime/eval/system.rs b/evm/src/runtime/eval/system.rs index e303e207..2bb39e8e 100644 --- a/evm/src/runtime/eval/system.rs +++ b/evm/src/runtime/eval/system.rs @@ -27,7 +27,7 @@ pub fn sha3(runtime: &mut Runtime) -> Control { }; let ret = Keccak256::digest(data.as_slice()); - push_h256!(runtime, H256::from_slice(ret.as_slice())); + push_h256!(runtime, H256::from_slice(<[u8; 32]>::from(ret).as_slice())); Control::Continue } @@ -204,7 +204,7 @@ pub fn returndatacopy(runtime: &mut Runtime) -> Control { .resize_offset(memory_offset, len)); if data_offset .checked_add(len.into()) - .map_or(true, |l| l > U256::from(runtime.return_data_buffer.len())) + .is_none_or(|l| l > U256::from(runtime.return_data_buffer.len())) { return Control::Exit(ExitError::OutOfOffset.into()); } @@ -432,7 +432,7 @@ pub fn create(runtime: &mut Runtime, is_create2: bool, handler: &mut let scheme = if is_create2 { pop_h256!(runtime, salt); - let code_hash = H256::from_slice(Keccak256::digest(&code).as_slice()); + let code_hash = H256::from_slice(<[u8; 32]>::from(Keccak256::digest(&code)).as_slice()); CreateScheme::Create2 { caller: runtime.context.address, salt, diff --git a/evm/src/runtime/interrupt.rs b/evm/src/runtime/interrupt.rs index 442255d1..c5b82452 100644 --- a/evm/src/runtime/interrupt.rs +++ b/evm/src/runtime/interrupt.rs @@ -14,7 +14,7 @@ pub struct ResolveCreate<'a> { } impl<'a> ResolveCreate<'a> { - pub(crate) fn new(runtime: &'a mut Runtime) -> Self { + pub(crate) const fn new(runtime: &'a mut Runtime) -> Self { Self { _runtime: runtime } } } @@ -25,7 +25,7 @@ pub struct ResolveCall<'a> { } impl<'a> ResolveCall<'a> { - pub(crate) fn new(runtime: &'a mut Runtime) -> Self { + pub(crate) const fn new(runtime: &'a mut Runtime) -> Self { Self { _runtime: runtime } } } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f19c7df4..d0929449 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.81.0" +channel = "1.86.0" components = ["rustfmt", "clippy"]