mirror of
https://github.com/pezkuwichain/revive-differential-tests.git
synced 2026-06-22 05:31:03 +00:00
Generated
+2
@@ -2873,6 +2873,7 @@ dependencies = [
|
||||
name = "revive-dt-core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"anyhow",
|
||||
"clap",
|
||||
"env_logger",
|
||||
@@ -2882,6 +2883,7 @@ dependencies = [
|
||||
"revive-dt-config",
|
||||
"revive-dt-format",
|
||||
"revive-dt-node",
|
||||
"revive-dt-node-interaction",
|
||||
"revive-solc-json-interface",
|
||||
"semver 1.0.26",
|
||||
"serde",
|
||||
|
||||
+49
-10
@@ -3,7 +3,7 @@
|
||||
//! - Polkadot revive resolc compiler
|
||||
//! - Polkadot revive Wasm compiler
|
||||
|
||||
use std::{fs::read_to_string, path::Path};
|
||||
use std::{fs::read_to_string, hash::Hash, path::Path};
|
||||
|
||||
use revive_common::EVMVersion;
|
||||
use revive_solc_json_interface::{
|
||||
@@ -20,21 +20,57 @@ pub mod solc;
|
||||
/// A common interface for all supported Solidity compilers.
|
||||
pub trait SolidityCompiler {
|
||||
/// Extra options specific to the compiler.
|
||||
type Options;
|
||||
type Options: Default + PartialEq + Eq + Hash;
|
||||
|
||||
/// The low-level compiler interface.
|
||||
fn build(
|
||||
&self,
|
||||
input: &SolcStandardJsonInput,
|
||||
extra_options: &Option<Self::Options>,
|
||||
) -> anyhow::Result<SolcStandardJsonOutput>;
|
||||
input: CompilerInput<Self::Options>,
|
||||
) -> anyhow::Result<CompilerOutput<Self::Options>>;
|
||||
|
||||
fn new(solc_version: &Version) -> Self;
|
||||
}
|
||||
|
||||
/// The generic compilation input configuration.
|
||||
#[derive(Debug)]
|
||||
pub struct CompilerInput<T: PartialEq + Eq + Hash> {
|
||||
pub extra_options: T,
|
||||
pub input: SolcStandardJsonInput,
|
||||
}
|
||||
|
||||
/// The generic compilation output configuration.
|
||||
pub struct CompilerOutput<T: PartialEq + Eq + Hash> {
|
||||
pub input: CompilerInput<T>,
|
||||
pub output: SolcStandardJsonOutput,
|
||||
}
|
||||
|
||||
impl<T> PartialEq for CompilerInput<T>
|
||||
where
|
||||
T: PartialEq + Eq + Hash,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let self_input = serde_json::to_vec(&self.input).unwrap_or_default();
|
||||
let other_input = serde_json::to_vec(&self.input).unwrap_or_default();
|
||||
self.extra_options.eq(&other.extra_options) && self_input == other_input
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for CompilerInput<T> where T: PartialEq + Eq + Hash {}
|
||||
|
||||
impl<T> Hash for CompilerInput<T>
|
||||
where
|
||||
T: PartialEq + Eq + Hash,
|
||||
{
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.extra_options.hash(state);
|
||||
state.write(&serde_json::to_vec(&self.input).unwrap_or_default());
|
||||
}
|
||||
}
|
||||
|
||||
/// A generic builder style interface for configuring all compiler options.
|
||||
pub struct Compiler<T: SolidityCompiler> {
|
||||
input: SolcStandardJsonInput,
|
||||
extra_options: Option<T::Options>,
|
||||
extra_options: T::Options,
|
||||
allow_paths: Vec<String>,
|
||||
base_path: Option<String>,
|
||||
}
|
||||
@@ -68,7 +104,7 @@ where
|
||||
None,
|
||||
),
|
||||
},
|
||||
extra_options: None,
|
||||
extra_options: Default::default(),
|
||||
allow_paths: Default::default(),
|
||||
base_path: None,
|
||||
}
|
||||
@@ -92,7 +128,7 @@ where
|
||||
}
|
||||
|
||||
pub fn extra_options(mut self, extra_options: T::Options) -> Self {
|
||||
self.extra_options = Some(extra_options);
|
||||
self.extra_options = extra_options;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -106,7 +142,10 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
pub fn try_build(&self, solc_version: &Version) -> anyhow::Result<SolcStandardJsonOutput> {
|
||||
T::new(solc_version).build(&self.input, &self.extra_options)
|
||||
pub fn try_build(self, solc_version: &Version) -> anyhow::Result<CompilerOutput<T::Options>> {
|
||||
T::new(solc_version).build(CompilerInput {
|
||||
extra_options: self.extra_options,
|
||||
input: self.input,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,9 @@ use std::{
|
||||
process::{Command, Stdio},
|
||||
};
|
||||
|
||||
use revive_solc_json_interface::{SolcStandardJsonInput, SolcStandardJsonOutput};
|
||||
use semver::Version;
|
||||
|
||||
use crate::SolidityCompiler;
|
||||
use crate::{CompilerInput, CompilerOutput, SolidityCompiler};
|
||||
|
||||
pub struct Solc {
|
||||
binary_path: PathBuf,
|
||||
@@ -20,9 +19,8 @@ impl SolidityCompiler for Solc {
|
||||
|
||||
fn build(
|
||||
&self,
|
||||
input: &SolcStandardJsonInput,
|
||||
_extra_options: &Option<Self::Options>,
|
||||
) -> anyhow::Result<SolcStandardJsonOutput> {
|
||||
input: CompilerInput<Self::Options>,
|
||||
) -> anyhow::Result<CompilerOutput<Self::Options>> {
|
||||
let mut child = Command::new(&self.binary_path)
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
@@ -31,10 +29,13 @@ impl SolidityCompiler for Solc {
|
||||
.spawn()?;
|
||||
|
||||
let stdin = child.stdin.as_mut().expect("should be piped");
|
||||
serde_json::to_writer(stdin, input)?;
|
||||
serde_json::to_writer(stdin, &input.input)?;
|
||||
|
||||
let output = child.wait_with_output()?.stdout;
|
||||
Ok(serde_json::from_slice(&output)?)
|
||||
Ok(CompilerOutput {
|
||||
input,
|
||||
output: serde_json::from_slice(&output)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn new(_solc_version: &Version) -> Self {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::Parser;
|
||||
use clap::{Parser, ValueEnum};
|
||||
|
||||
#[derive(Debug, Parser, Clone)]
|
||||
#[command(name = "retester")]
|
||||
@@ -48,6 +48,18 @@ pub struct Arguments {
|
||||
default_value = "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d"
|
||||
)]
|
||||
pub account: String,
|
||||
|
||||
/// The differential testing leader node implementation.
|
||||
#[arg(short, long = "leader", default_value = "geth")]
|
||||
pub leader: TestingPlatform,
|
||||
|
||||
/// The differential testing follower node implementation.
|
||||
#[arg(short, long = "follower", default_value = "kitchensink")]
|
||||
pub follower: TestingPlatform,
|
||||
|
||||
/// Only compile against this testing platform (doesn't execute the tests).
|
||||
#[arg(short, long = "compile-only")]
|
||||
pub compile_only: bool,
|
||||
}
|
||||
|
||||
impl Default for Arguments {
|
||||
@@ -55,3 +67,15 @@ impl Default for Arguments {
|
||||
Arguments::parse_from(["retester"])
|
||||
}
|
||||
}
|
||||
|
||||
/// The Solidity compatible node implementation.
|
||||
///
|
||||
/// This describes the solutions to be tested against on a high level.
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, ValueEnum)]
|
||||
#[clap(rename_all = "lower")]
|
||||
pub enum TestingPlatform {
|
||||
/// The go-ethereum reference full node EVM implementation.
|
||||
Geth,
|
||||
/// The kitchensink runtime provides the PolkaVM (PVM) based node implentation.
|
||||
Kitchensink,
|
||||
}
|
||||
|
||||
@@ -18,7 +18,9 @@ revive-dt-config = { workspace = true }
|
||||
revive-dt-format = { workspace = true }
|
||||
revive-solc-json-interface = { workspace = true }
|
||||
revive-dt-node = { workspace = true }
|
||||
revive-dt-node-interaction = { workspace = true }
|
||||
|
||||
alloy = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
log = { workspace = true }
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use revive_dt_compiler::{Compiler, solc::Solc};
|
||||
use revive_dt_compiler::{Compiler, CompilerInput, SolidityCompiler};
|
||||
use revive_dt_format::{
|
||||
metadata::Metadata,
|
||||
mode::{Mode, SolcMode},
|
||||
@@ -10,15 +10,9 @@ use revive_dt_format::{
|
||||
use revive_solc_json_interface::SolcStandardJsonOutput;
|
||||
use semver::Version;
|
||||
|
||||
#[derive(Hash, Eq, PartialEq)]
|
||||
pub struct SolcSettings {
|
||||
pub optimizer: bool,
|
||||
pub solc_version: Version,
|
||||
}
|
||||
|
||||
pub fn build_evm(
|
||||
pub fn build<T: SolidityCompiler>(
|
||||
metadata: &Metadata,
|
||||
) -> anyhow::Result<HashMap<SolcSettings, SolcStandardJsonOutput>> {
|
||||
) -> anyhow::Result<HashMap<CompilerInput<T::Options>, SolcStandardJsonOutput>> {
|
||||
let sources = metadata.contract_sources()?;
|
||||
let base_path = metadata.directory()?.display().to_string();
|
||||
let modes = metadata
|
||||
@@ -28,7 +22,7 @@ pub fn build_evm(
|
||||
|
||||
let mut result = HashMap::new();
|
||||
for mode in modes {
|
||||
let mut compiler = Compiler::<Solc>::new().base_path(base_path.clone());
|
||||
let mut compiler = Compiler::<T>::new().base_path(base_path.clone());
|
||||
for (file, _contract) in sources.values() {
|
||||
compiler = compiler.with_source(file)?;
|
||||
}
|
||||
@@ -41,15 +35,8 @@ pub fn build_evm(
|
||||
}) => {
|
||||
let optimizer = solc_optimize.unwrap_or(true);
|
||||
let version = Version::new(0, 8, 29);
|
||||
let out = compiler.solc_optimizer(optimizer).try_build(&version)?;
|
||||
|
||||
result.insert(
|
||||
SolcSettings {
|
||||
optimizer: true,
|
||||
solc_version: version,
|
||||
},
|
||||
out,
|
||||
);
|
||||
let output = compiler.solc_optimizer(optimizer).try_build(&version)?;
|
||||
result.insert(output.input, output.output);
|
||||
}
|
||||
Mode::Unknown(mode) => log::debug!("compiler: ignoring unknown mode '{mode}'"),
|
||||
}
|
||||
|
||||
@@ -1,4 +1,60 @@
|
||||
//! The test driver handles the compilation and execution of the test cases.
|
||||
|
||||
use alloy::primitives::map::HashMap;
|
||||
use compiler::build;
|
||||
use revive_dt_compiler::{CompilerInput, SolidityCompiler};
|
||||
use revive_dt_config::Arguments;
|
||||
use revive_dt_format::metadata::Metadata;
|
||||
use revive_dt_node::Node;
|
||||
use revive_solc_json_interface::SolcStandardJsonOutput;
|
||||
|
||||
use crate::Platform;
|
||||
|
||||
pub mod compiler;
|
||||
pub mod input;
|
||||
|
||||
type Contracts<T> = HashMap<
|
||||
CompilerInput<<<T as Platform>::Compiler as SolidityCompiler>::Options>,
|
||||
SolcStandardJsonOutput,
|
||||
>;
|
||||
|
||||
pub struct Driver<'a, Leader: Platform, Follower: Platform> {
|
||||
metadata: &'a Metadata,
|
||||
config: &'a Arguments,
|
||||
|
||||
leader_contracts: Contracts<Leader>,
|
||||
leader_node: <Leader as Platform>::Blockchain,
|
||||
|
||||
follower_contracts: Contracts<Follower>,
|
||||
follower_node: <Follower as Platform>::Blockchain,
|
||||
}
|
||||
|
||||
impl<'a, L, F> Driver<'a, L, F>
|
||||
where
|
||||
L: Platform + Default,
|
||||
F: Platform + Default,
|
||||
{
|
||||
pub fn new(metadata: &'a Metadata, config: &'a Arguments) -> Driver<'a, L, F> {
|
||||
Self {
|
||||
metadata,
|
||||
config,
|
||||
|
||||
leader_node: <<L as Platform>::Blockchain as Node>::new(config),
|
||||
leader_contracts: Default::default(),
|
||||
|
||||
follower_node: <<F as Platform>::Blockchain as Node>::new(config),
|
||||
follower_contracts: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute(&mut self) -> anyhow::Result<()> {
|
||||
self.leader_contracts = build::<L::Compiler>(self.metadata)?;
|
||||
self.follower_contracts = build::<F::Compiler>(self.metadata)?;
|
||||
|
||||
if self.config.compile_only {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
+27
-2
@@ -2,7 +2,32 @@
|
||||
//!
|
||||
//! This crate defines the testing configuration and
|
||||
//! provides a helper utilty to execute tests.
|
||||
//!
|
||||
//!
|
||||
|
||||
use revive_dt_compiler::{SolidityCompiler, solc};
|
||||
use revive_dt_node::{Node, geth};
|
||||
|
||||
pub mod driver;
|
||||
|
||||
/// One platform can be tested differentially against another.
|
||||
///
|
||||
/// For this we need a blockchain node implementation and a compiler.
|
||||
pub trait Platform {
|
||||
type Blockchain: Node;
|
||||
type Compiler: SolidityCompiler;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Geth;
|
||||
|
||||
impl Platform for Geth {
|
||||
type Blockchain = geth::Instance;
|
||||
type Compiler = solc::Solc;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Kitchensink;
|
||||
|
||||
impl Platform for Kitchensink {
|
||||
type Blockchain = geth::Instance;
|
||||
type Compiler = solc::Solc;
|
||||
}
|
||||
|
||||
+12
-6
@@ -4,7 +4,7 @@ use clap::Parser;
|
||||
use rayon::prelude::*;
|
||||
|
||||
use revive_dt_config::*;
|
||||
use revive_dt_core::driver::compiler::build_evm;
|
||||
use revive_dt_core::{Geth, Kitchensink, driver::Driver};
|
||||
use revive_dt_format::corpus::Corpus;
|
||||
use temp_dir::TempDir;
|
||||
|
||||
@@ -27,22 +27,28 @@ fn main() -> anyhow::Result<()> {
|
||||
log::info!("found {} tests", tests.len());
|
||||
|
||||
tests.par_iter().for_each(|metadata| {
|
||||
let _ = match build_evm(metadata) {
|
||||
let mut driver = match (&args.leader, &args.follower) {
|
||||
(TestingPlatform::Geth, TestingPlatform::Kitchensink) => {
|
||||
Driver::<Geth, Kitchensink>::new(metadata, &args)
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
|
||||
match driver.execute() {
|
||||
Ok(build) => {
|
||||
log::info!(
|
||||
"metadata {} compilation success",
|
||||
"metadata {} success",
|
||||
metadata.file_path.as_ref().unwrap().display()
|
||||
);
|
||||
build
|
||||
}
|
||||
Err(error) => {
|
||||
log::warn!(
|
||||
"metadata {} compilation failure: {error:?}",
|
||||
"metadata {} failure: {error:?}",
|
||||
metadata.file_path.as_ref().unwrap().display()
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,12 @@ use serde::{Deserialize, de::Deserializer};
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)]
|
||||
pub struct Input {
|
||||
instance: String,
|
||||
pub instance: String,
|
||||
#[serde(deserialize_with = "deserialize_method")]
|
||||
method: Method,
|
||||
pub method: Method,
|
||||
#[serde(deserialize_with = "deserialize_calldata")]
|
||||
calldata: Vec<u8>,
|
||||
expected: Option<Vec<String>>,
|
||||
pub calldata: Vec<u8>,
|
||||
pub expected: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
/// Specify how the contract is called.
|
||||
|
||||
+22
-25
@@ -52,28 +52,6 @@ impl Instance {
|
||||
const READY_MARKER: &str = "IPC endpoint opened";
|
||||
const ERROR_MARKER: &str = "Fatal:";
|
||||
|
||||
/// Create a new uninitialized instance.
|
||||
pub fn new(config: &Arguments) -> anyhow::Result<Self> {
|
||||
let geth_directory = config
|
||||
.working_directory
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow::anyhow!("config did not provide working directory"))?
|
||||
.join(Self::BASE_DIRECTORY);
|
||||
let id = NODE_COUNT.fetch_add(1, Ordering::SeqCst);
|
||||
let base_directory = geth_directory.join(id.to_string());
|
||||
|
||||
Ok(Self {
|
||||
connection_string: base_directory.join(Self::IPC_FILE).display().to_string(),
|
||||
data_directory: base_directory.join(Self::DATA_DIRECTORY),
|
||||
base_directory,
|
||||
geth: config.geth.clone(),
|
||||
id,
|
||||
handle: None,
|
||||
network_id: config.network_id,
|
||||
start_timeout: config.geth_start_timeout,
|
||||
})
|
||||
}
|
||||
|
||||
/// Create the node directory and call `geth init` to configure the genesis.
|
||||
fn init(&mut self, genesis: String) -> anyhow::Result<&mut Self> {
|
||||
let geth_directory = self.base_directory.parent().expect("the id should be set");
|
||||
@@ -182,6 +160,27 @@ impl EthereumNode for Instance {
|
||||
}
|
||||
|
||||
impl Node for Instance {
|
||||
fn new(config: &Arguments) -> Self {
|
||||
let geth_directory = config
|
||||
.working_directory
|
||||
.as_ref()
|
||||
.expect("config should provide working directory")
|
||||
.join(Self::BASE_DIRECTORY);
|
||||
let id = NODE_COUNT.fetch_add(1, Ordering::SeqCst);
|
||||
let base_directory = geth_directory.join(id.to_string());
|
||||
|
||||
Self {
|
||||
connection_string: base_directory.join(Self::IPC_FILE).display().to_string(),
|
||||
data_directory: base_directory.join(Self::DATA_DIRECTORY),
|
||||
base_directory,
|
||||
geth: config.geth.clone(),
|
||||
id,
|
||||
handle: None,
|
||||
network_id: config.network_id,
|
||||
start_timeout: config.geth_start_timeout,
|
||||
}
|
||||
}
|
||||
|
||||
fn connection_string(&self) -> String {
|
||||
self.connection_string.clone()
|
||||
}
|
||||
@@ -252,7 +251,6 @@ mod tests {
|
||||
#[test]
|
||||
fn init_works() {
|
||||
Instance::new(&test_config().0)
|
||||
.unwrap()
|
||||
.init(GENESIS_JSON.to_string())
|
||||
.unwrap();
|
||||
}
|
||||
@@ -260,14 +258,13 @@ mod tests {
|
||||
#[test]
|
||||
fn spawn_works() {
|
||||
Instance::new(&test_config().0)
|
||||
.unwrap()
|
||||
.spawn(GENESIS_JSON.to_string())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn version_works() {
|
||||
let version = Instance::new(&test_config().0).unwrap().version().unwrap();
|
||||
let version = Instance::new(&test_config().0).version().unwrap();
|
||||
assert!(
|
||||
version.starts_with("geth version"),
|
||||
"expected version string, got: '{version}'"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! This crate implements the testing nodes.
|
||||
|
||||
use alloy::rpc::types::{TransactionReceipt, trace::geth::DiffMode};
|
||||
use revive_dt_config::Arguments;
|
||||
use revive_dt_node_interaction::EthereumNode;
|
||||
|
||||
pub mod geth;
|
||||
@@ -10,6 +11,9 @@ pub const GENESIS_JSON: &str = include_str!("../../../genesis.json");
|
||||
|
||||
/// An abstract interface for testing nodes.
|
||||
pub trait Node: EthereumNode {
|
||||
/// Create a new uninitialized instance.
|
||||
fn new(config: &Arguments) -> Self;
|
||||
|
||||
/// Spawns a node configured according to the genesis json.
|
||||
///
|
||||
/// Blocking until it's ready to accept transactions.
|
||||
|
||||
Reference in New Issue
Block a user