the node pool

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
This commit is contained in:
Cyrill Leutwiler
2025-03-26 11:58:38 +01:00
parent 34b8879b15
commit 95d2afde05
11 changed files with 304 additions and 112 deletions
+21 -36
View File
@@ -32,7 +32,7 @@ impl<'a, T> State<'a, T>
where
T: Platform,
{
fn new(config: &'a Arguments) -> Self {
pub fn new(config: &'a Arguments) -> Self {
Self {
config,
contracts: Default::default(),
@@ -49,6 +49,7 @@ where
let base_path = metadata.directory()?.display().to_string();
let mut compiler = Compiler::<T::Compiler>::new().base_path(base_path.clone());
for (file, _contract) in sources.values() {
log::debug!("contract source {}", file.display());
compiler = compiler.with_source(file)?;
}
@@ -73,15 +74,15 @@ where
&self.deployed_contracts,
)?)?;
dbg!(&receipt);
Ok(node.trace_transaction(receipt)?)
node.trace_transaction(receipt)
}
}
pub struct Driver<'a, Leader: Platform, Follower: Platform> {
metadata: &'a Metadata,
config: &'a Arguments,
leader: State<'a, Leader>,
follower: State<'a, Follower>,
leader_node: &'a Leader::Blockchain,
follower_node: &'a Follower::Blockchain,
}
impl<'a, L, F> Driver<'a, L, F>
@@ -89,52 +90,36 @@ where
L: Platform,
F: Platform,
{
pub fn new(metadata: &'a Metadata, config: &'a Arguments) -> Driver<'a, L, F> {
pub fn new(
metadata: &'a Metadata,
config: &'a Arguments,
leader_node: &'a L::Blockchain,
follower_node: &'a F::Blockchain,
) -> Driver<'a, L, F> {
Self {
metadata,
config,
leader: State::new(config),
follower: State::new(config),
leader_node,
follower_node,
}
}
pub fn execute(
&mut self,
leader: L::Blockchain,
follower: F::Blockchain,
) -> anyhow::Result<()> {
for mode in self.modes() {
self.leader.build_contracts(&mode, self.metadata)?;
self.follower.build_contracts(&mode, self.metadata)?;
pub fn execute(&mut self) -> anyhow::Result<()> {
for mode in self.metadata.solc_modes() {
let mut leader_state = State::<L>::new(self.config);
leader_state.build_contracts(&mode, self.metadata)?;
if self.config.compile_only {
continue;
}
let mut follower_state = State::<F>::new(self.config);
follower_state.build_contracts(&mode, self.metadata)?;
for case in &self.metadata.cases {
for input in &case.inputs {
let expected = self.leader.execute_input(input, &leader)?;
let expected = leader_state.execute_input(input, self.leader_node)?;
let received = follower_state.execute_input(input, self.follower_node)?;
}
}
*self = Self::new(self.metadata, self.config);
}
Ok(())
}
fn modes(&self) -> Vec<SolcMode> {
self.metadata
.modes()
.iter()
.filter_map(|mode| match mode {
Mode::Solidity(solc_mode) => Some(solc_mode),
Mode::Unknown(mode) => {
log::debug!("compiler: ignoring unknown mode '{mode}'");
None
}
})
.cloned()
.collect()
}
}
+3 -2
View File
@@ -4,7 +4,8 @@
//! provides a helper utilty to execute tests.
use revive_dt_compiler::{SolidityCompiler, solc};
use revive_dt_node::{Node, geth};
use revive_dt_node::geth;
use revive_dt_node_interaction::EthereumNode;
pub mod driver;
@@ -12,7 +13,7 @@ pub mod driver;
///
/// For this we need a blockchain node implementation and a compiler.
pub trait Platform {
type Blockchain: Node;
type Blockchain: EthereumNode;
type Compiler: SolidityCompiler;
}
+92 -40
View File
@@ -1,15 +1,40 @@
use std::collections::BTreeSet;
use std::{collections::HashMap, sync::LazyLock};
use clap::Parser;
use rayon::{ThreadPoolBuilder, prelude::*};
use revive_dt_config::*;
use revive_dt_core::{Geth, Kitchensink, driver::Driver};
use revive_dt_format::corpus::Corpus;
use revive_dt_node::{Node, geth};
use revive_dt_core::{
Geth, Kitchensink,
driver::{Driver, State},
};
use revive_dt_format::{corpus::Corpus, metadata::Metadata};
use revive_dt_node::pool::NodePool;
use temp_dir::TempDir;
static TEMP_DIR: LazyLock<TempDir> = LazyLock::new(|| TempDir::new().unwrap());
fn main() -> anyhow::Result<()> {
let args = init_cli()?;
let corpora = collect_corpora(&args)?;
if let Some(platform) = &args.compile_only {
for tests in corpora.values() {
main_compile_only(&args, tests, platform)?;
}
return Ok(());
}
for tests in corpora.values() {
main_execute_differential(&args, tests)?;
}
Ok(())
}
fn init_cli() -> anyhow::Result<Arguments> {
env_logger::init();
let mut args = Arguments::parse();
@@ -17,7 +42,7 @@ fn main() -> anyhow::Result<()> {
anyhow::bail!("no test corpus specified");
}
if args.working_directory.is_none() {
args.temp_dir = TempDir::new()?.into()
args.temp_dir = Some(&TEMP_DIR);
}
ThreadPoolBuilder::new()
@@ -25,45 +50,72 @@ fn main() -> anyhow::Result<()> {
.build_global()
.unwrap();
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:?}");
Ok(args)
}
fn collect_corpora(args: &Arguments) -> anyhow::Result<HashMap<Corpus, Vec<Metadata>>> {
let mut corpora = HashMap::new();
for path in &args.corpus {
let corpus = Corpus::try_from_path(path)?;
log::info!("found corpus: {}", path.display());
let tests = corpus.enumerate_tests();
log::info!("corpus '{}' contains {} tests", &corpus.name, tests.len());
tests.par_iter().for_each(|metadata| {
let (leader, follower) = match (&args.leader, &args.follower) {
(TestingPlatform::Geth, TestingPlatform::Kitchensink) => {
(geth::Instance::new(&args), geth::Instance::new(&args))
}
_ => unimplemented!(),
};
let mut driver = match (&args.leader, &args.follower) {
(TestingPlatform::Geth, TestingPlatform::Kitchensink) => {
Driver::<Geth, Geth>::new(metadata, &args)
}
_ => unimplemented!(),
};
match Driver::<Geth, Geth>::new(metadata, &args).execute(leader, follower) {
Ok(build) => {
log::info!(
"metadata {} success",
metadata.directory().as_ref().unwrap().display()
);
build
}
Err(error) => {
log::warn!(
"metadata {} failure: {error:?}",
metadata.file_path.as_ref().unwrap().display()
);
}
}
});
corpora.insert(corpus, tests);
}
Ok(corpora)
}
fn main_execute_differential(args: &Arguments, tests: &[Metadata]) -> anyhow::Result<()> {
let leader_nodes = NodePool::new(args)?;
let follower_nodes = NodePool::new(args)?;
tests.par_iter().for_each(|metadata| {
let mut driver = match (&args.leader, &args.follower) {
(TestingPlatform::Geth, TestingPlatform::Kitchensink) => Driver::<Geth, Geth>::new(
metadata,
args,
leader_nodes.round_robbin(),
follower_nodes.round_robbin(),
),
_ => unimplemented!(),
};
match driver.execute() {
Ok(build) => {
log::info!(
"metadata {} success",
metadata.directory().as_ref().unwrap().display()
);
build
}
Err(error) => {
log::warn!(
"metadata {} failure: {error:?}",
metadata.file_path.as_ref().unwrap().display()
);
}
}
});
Ok(())
}
fn main_compile_only(
config: &Arguments,
tests: &[Metadata],
platform: &TestingPlatform,
) -> anyhow::Result<()> {
tests.par_iter().for_each(|metadata| {
for mode in &metadata.solc_modes() {
let mut state = match platform {
TestingPlatform::Geth => State::<Geth>::new(config),
_ => todo!(),
};
let _ = state.build_contracts(mode, metadata);
}
});
Ok(())
}