parsing complex tests works modulo the contract addresses in calldata

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
This commit is contained in:
Cyrill Leutwiler
2025-03-19 16:23:04 +01:00
parent d08d6fd66f
commit 67f068ca12
15 changed files with 429 additions and 27 deletions
+1
View File
@@ -2,3 +2,4 @@
.vscode/
.DS_Store
node_modules
/*.json
Generated
+228 -8
View File
@@ -2,6 +2,15 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "alloy-eip2124"
version = "0.1.0"
@@ -231,6 +240,56 @@ dependencies = [
"tracing",
]
[[package]]
name = "anstream"
version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]]
name = "anstyle-parse"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
dependencies = [
"anstyle",
"once_cell",
"windows-sys",
]
[[package]]
name = "anyhow"
version = "1.0.97"
@@ -501,6 +560,52 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.5.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.5.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "clap_lex"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
[[package]]
name = "colorchoice"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]]
name = "const-hex"
version = "1.14.0"
@@ -723,6 +828,29 @@ dependencies = [
"zeroize",
]
[[package]]
name = "env_filter"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
dependencies = [
"log",
"regex",
]
[[package]]
name = "env_logger"
version = "0.11.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3716d7a920fb4fac5d84e9d4bce8ceb321e9414b4409da61b07b75c1e3d0697"
dependencies = [
"anstream",
"anstyle",
"env_filter",
"jiff",
"log",
]
[[package]]
name = "equivalent"
version = "1.0.2"
@@ -929,6 +1057,12 @@ dependencies = [
"serde",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itertools"
version = "0.10.5"
@@ -944,6 +1078,30 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "jiff"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d699bc6dfc879fb1bf9bdff0d4c56f0884fc6f0d0eb0fba397a6d00cd9a6b85e"
dependencies = [
"jiff-static",
"log",
"portable-atomic",
"portable-atomic-util",
"serde",
]
[[package]]
name = "jiff-static"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d16e75759ee0aa64c57a56acbf43916987b20c77373cb7e808979e02b93c9f9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "k256"
version = "0.13.4"
@@ -1000,6 +1158,12 @@ version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413"
[[package]]
name = "log"
version = "0.4.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
[[package]]
name = "macro-string"
version = "0.1.4"
@@ -1136,6 +1300,21 @@ dependencies = [
"spki",
]
[[package]]
name = "portable-atomic"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
[[package]]
name = "portable-atomic-util"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
dependencies = [
"portable-atomic",
]
[[package]]
name = "ppv-lite86"
version = "0.2.21"
@@ -1283,12 +1462,48 @@ dependencies = [
"rand_core",
]
[[package]]
name = "regex"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "revive-differential-testing-core"
version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"env_logger",
"log",
"revive-differential-testing-format",
"serde",
"serde_json",
]
[[package]]
name = "revive-differential-testing-format"
version = "0.1.0"
@@ -1299,19 +1514,12 @@ dependencies = [
"alloy-serde",
"alloy-sol-types",
"anyhow",
"log",
"semver 1.0.26",
"serde",
"serde_json",
]
[[package]]
name = "revive-differential-testing-retester"
version = "0.1.0"
dependencies = [
"revive-differential-testing-format",
"serde_json",
]
[[package]]
name = "rfc6979"
version = "0.4.0"
@@ -1570,6 +1778,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "subtle"
version = "2.6.1"
@@ -1745,6 +1959,12 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "valuable"
version = "0.1.1"
+3
View File
@@ -21,9 +21,12 @@ alloy-genesis = "0.12.6"
alloy-primitives = { version = "0.8.23", features = ["serde"] }
alloy-serde = "0.12.6"
alloy-sol-types = "0.8.23"
env_logger = "0.11.7"
log = "0.4.26"
semver = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["arbitrary_precision"] }
clap = { version = "4", features = ["derive"] }
# revive compiler
revive-llvm-context = { git = "https://github.com/paritytech/revive", rev = "2bbc5d713d1bf8b38c43f6ce9382812561ca3ce1" }
@@ -1,5 +1,5 @@
[package]
name = "revive-differential-testing-retester"
name = "revive-differential-testing-core"
version.workspace = true
authors.workspace = true
license.workspace = true
@@ -9,8 +9,13 @@ rust-version.workspace = true
[[bin]]
name = "retester"
path = "src/main.rs"
path = "src/bin/main.rs"
[dependencies]
anyhow = { workspace = true }
clap = { workspace = true }
log = { workspace = true }
env_logger = { workspace = true }
revive-differential-testing-format = { workspace = true }
serde = { workspace = true, features = [ "derive" ] }
serde_json = { workspace = true }
+17
View File
@@ -0,0 +1,17 @@
use std::path::PathBuf;
use clap::Parser;
#[derive(Debug, Parser)]
#[command(name = "The PolkaVM Solidity compiler", arg_required_else_help = true)]
pub struct Arguments {
/// The path where the `resolc` executable to be tested is found at.
///
/// By default it uses the `resolc` found in `$PATH`
#[arg(long = "resolc")]
pub resolc: Option<PathBuf>,
/// A list of test corpus JSON files to be tested.
#[arg(long = "corpus")]
pub corpus: Vec<PathBuf>,
}
+24
View File
@@ -0,0 +1,24 @@
use std::collections::BTreeSet;
use clap::Parser;
use revive_differential_testing_core::arguments::Arguments;
use revive_differential_testing_format::corpus::Corpus;
fn main() -> anyhow::Result<()> {
env_logger::init();
let args = Arguments::try_parse()?;
for path in args.corpus.iter().collect::<BTreeSet<_>>() {
log::trace!("attempting corpus {path:?}");
let corpus = Corpus::try_from_path(path)?;
log::info!("found corpus: {corpus:?}");
let tests = corpus.enumerate_tests();
log::debug!("{tests:?}");
}
Ok(())
}
+6
View File
@@ -0,0 +1,6 @@
//! The revive differential testing core library.
//!
//! This crate defines the testing configuration and
//! provides a helper utilty to execute tests.
pub mod arguments;
+1
View File
@@ -14,6 +14,7 @@ alloy-serde = { workspace = true }
alloy-primitives = { workspace = true }
alloy-sol-types = { workspace = true }
anyhow = { workspace = true }
log = { workspace = true }
semver = { workspace = true }
serde = { workspace = true, features = [ "derive" ] }
serde_json = { workspace = true }
+1 -2
View File
@@ -1,4 +1,4 @@
use serde::{Deserialize, de::Deserializer};
use serde::Deserialize;
use crate::{input::Input, mode::Mode};
@@ -8,5 +8,4 @@ pub struct Case {
pub comment: Option<String>,
pub modes: Option<Vec<Mode>>,
pub inputs: Vec<Input>,
pub expected: Vec<String>,
}
+67
View File
@@ -0,0 +1,67 @@
use std::{
fs::File,
path::{Path, PathBuf},
};
use serde::Deserialize;
use crate::metadata::Metadata;
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)]
pub struct Corpus {
pub name: String,
pub path: PathBuf,
}
impl Corpus {
/// Try to read and parse the corpus definition file at given `path`.
pub fn try_from_path(path: &Path) -> anyhow::Result<Self> {
let file = File::open(path)?;
Ok(serde_json::from_reader(file)?)
}
/// Scan the corpus base directory and return all tests found.
pub fn enumerate_tests(&self) -> Vec<Metadata> {
let mut tests = Vec::new();
collect_metadata(&self.path, &mut tests);
tests
}
}
/// Recursively walks `path` and parses any JSON or Solidity file into a test
/// definition [Metadata].
///
/// Found tests are inserted into `tests`.
///
/// `path` is expected to be a directory.
pub fn collect_metadata(path: &Path, tests: &mut Vec<Metadata>) {
let dir_entry = match std::fs::read_dir(path) {
Ok(dir_entry) => dir_entry,
Err(error) => {
log::error!("failed to read dir '{}': {error}", path.display());
return;
}
};
for entry in dir_entry {
let entry = match entry {
Ok(entry) => entry,
Err(error) => {
log::error!("error reading dir entry: {error}");
continue;
}
};
let path = entry.path();
if path.is_dir() {
collect_metadata(&path, tests);
continue;
}
if path.is_file() {
if let Some(metadata) = Metadata::try_from_file(&path) {
tests.push(metadata)
}
}
}
}
+2 -2
View File
@@ -41,7 +41,7 @@ where
match calldata_string.parse::<U256>() {
Ok(parsed) => result.extend_from_slice(&parsed.to_be_bytes::<32>()),
Err(error) => {
return Err(serde::de::Error::custom(&format!(
return Err(serde::de::Error::custom(format!(
"parsing U256 {calldata_string} error: {error}"
)));
}
@@ -67,7 +67,7 @@ where
match Function::parse(&signature) {
Ok(function) => Method::Function(function.selector().0),
Err(error) => {
return Err(serde::de::Error::custom(&format!(
return Err(serde::de::Error::custom(format!(
"parsing function signature '{signature}' error: {error}"
)));
}
+1
View File
@@ -1,6 +1,7 @@
//! The revive differential tests case format.
pub mod case;
pub mod corpus;
pub mod input;
pub mod metadata;
pub mod mode;
+68 -1
View File
@@ -1,9 +1,16 @@
use std::collections::BTreeMap;
use std::{
collections::BTreeMap,
fs::File,
path::{Path, PathBuf},
};
use serde::Deserialize;
use crate::{case::Case, mode::Mode};
pub const METADATA_FILE_EXTENSION: &str = "json";
pub const SOLIDITY_CASE_FILE_EXTENSION: &str = "sol";
#[derive(Debug, Default, Deserialize, Clone, Eq, PartialEq)]
pub struct Metadata {
pub cases: Vec<Case>,
@@ -11,4 +18,64 @@ pub struct Metadata {
pub libraries: Option<BTreeMap<String, BTreeMap<String, String>>>,
pub ignore: Option<bool>,
pub modes: Option<Vec<Mode>>,
pub path: Option<PathBuf>,
pub directory: Option<PathBuf>,
}
impl Metadata {
/// Try to parse the test metadata struct from the given file at `path`.
///
/// Returns `None` if `path` didn't contain a test metadata or case definition.
///
/// # Panics
/// Expects the supplied `path` to be a file.
pub fn try_from_file(path: &Path) -> Option<Self> {
assert!(path.is_file(), "not a file: {}", path.display());
let Some(file_extension) = path.extension() else {
log::debug!("skipping corpus file: {}", path.display());
return None;
};
if file_extension == METADATA_FILE_EXTENSION {
return Self::try_from_json(path);
}
if file_extension == SOLIDITY_CASE_FILE_EXTENSION {
return None;
//return Self::try_from_solidity(path);
}
log::debug!("ignoring invalid corpus file: {}", path.display());
None
}
fn try_from_json(path: &Path) -> Option<Self> {
let file = File::open(path)
.inspect_err(|error| {
log::error!(
"opening JSON test metadata file '{}' error: {error}",
path.display()
);
})
.ok()?;
match serde_json::from_reader::<_, Metadata>(file) {
Ok(mut metadata) => {
metadata.directory = Some(path.to_path_buf());
Some(metadata)
}
Err(error) => {
log::error!(
"parsing JSON test metadata file '{}' error: {error}",
path.display()
);
None
}
}
}
fn try_from_solidity(path: &Path) -> Option<Self> {
todo!()
}
}
+3 -3
View File
@@ -38,13 +38,13 @@ impl SolcMode {
_ => return None,
}
while let Some(part) = parts.next() {
for part in parts {
if let Ok(solc_version) = semver::VersionReq::parse(part) {
result.solc_version = Some(solc_version);
continue;
}
if part.starts_with("-O") {
result.llvm_optimizer_settings.push(part[2..].to_string());
if let Some(level) = part.strip_prefix("-O") {
result.llvm_optimizer_settings.push(level.to_string());
continue;
}
panic!("the YUL mode string {mode_string} failed to parse, invalid part: {part}")
-9
View File
@@ -1,9 +0,0 @@
use revive_differential_testing_format::metadata::Metadata;
fn main() {
let example_def = include_str!("../../../test.json");
let metadata: Metadata = serde_json::from_str(example_def).unwrap();
println!("{metadata:?}");
}