mirror of
https://github.com/pezkuwichain/revive-differential-tests.git
synced 2026-06-14 09:51:08 +00:00
make node interactions generic
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
This commit is contained in:
@@ -6,8 +6,6 @@ use std::{
|
|||||||
process::{Command, Stdio},
|
process::{Command, Stdio},
|
||||||
};
|
};
|
||||||
|
|
||||||
use semver::Version;
|
|
||||||
|
|
||||||
use crate::{CompilerInput, CompilerOutput, SolidityCompiler};
|
use crate::{CompilerInput, CompilerOutput, SolidityCompiler};
|
||||||
|
|
||||||
pub struct Solc {
|
pub struct Solc {
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
//! The global configuration used accross all revive differential testing crates.
|
//! The global configuration used accross all revive differential testing crates.
|
||||||
|
|
||||||
use std::{
|
use std::path::{Path, PathBuf};
|
||||||
env,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
use clap::{Arg, Parser, ValueEnum};
|
use clap::{Parser, ValueEnum};
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
use temp_dir::TempDir;
|
use temp_dir::TempDir;
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,7 @@ use alloy::{
|
|||||||
};
|
};
|
||||||
use revive_dt_compiler::{Compiler, CompilerInput, SolidityCompiler};
|
use revive_dt_compiler::{Compiler, CompilerInput, SolidityCompiler};
|
||||||
use revive_dt_config::Arguments;
|
use revive_dt_config::Arguments;
|
||||||
use revive_dt_format::{
|
use revive_dt_format::{input::Input, metadata::Metadata, mode::SolcMode};
|
||||||
input::Input,
|
|
||||||
metadata::Metadata,
|
|
||||||
mode::{Mode, SolcMode},
|
|
||||||
};
|
|
||||||
use revive_dt_node_interaction::EthereumNode;
|
use revive_dt_node_interaction::EthereumNode;
|
||||||
use revive_dt_solc_binaries::download_solc;
|
use revive_dt_solc_binaries::download_solc;
|
||||||
use revive_solc_json_interface::SolcStandardJsonOutput;
|
use revive_solc_json_interface::SolcStandardJsonOutput;
|
||||||
@@ -74,7 +70,8 @@ where
|
|||||||
&self.deployed_contracts,
|
&self.deployed_contracts,
|
||||||
)?)?;
|
)?)?;
|
||||||
dbg!(&receipt);
|
dbg!(&receipt);
|
||||||
node.trace_transaction(receipt)
|
//node.trace_transaction(receipt)
|
||||||
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,8 +111,8 @@ where
|
|||||||
|
|
||||||
for case in &self.metadata.cases {
|
for case in &self.metadata.cases {
|
||||||
for input in &case.inputs {
|
for input in &case.inputs {
|
||||||
let expected = leader_state.execute_input(input, self.leader_node)?;
|
let _ = leader_state.execute_input(input, self.leader_node)?;
|
||||||
let received = follower_state.execute_input(input, self.follower_node)?;
|
let _ = follower_state.execute_input(input, self.follower_node)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use rayon::{ThreadPoolBuilder, prelude::*};
|
|||||||
|
|
||||||
use revive_dt_config::*;
|
use revive_dt_config::*;
|
||||||
use revive_dt_core::{
|
use revive_dt_core::{
|
||||||
Geth, Kitchensink,
|
Geth,
|
||||||
driver::{Driver, State},
|
driver::{Driver, State},
|
||||||
};
|
};
|
||||||
use revive_dt_format::{corpus::Corpus, metadata::Metadata};
|
use revive_dt_format::{corpus::Corpus, metadata::Metadata};
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ impl Input {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Ok(TransactionRequest::default()
|
Ok(TransactionRequest::default()
|
||||||
.with_from(self.caller.clone())
|
.with_from(self.caller)
|
||||||
.with_to(to)
|
.with_to(to)
|
||||||
.with_nonce(nonce)
|
.with_nonce(nonce)
|
||||||
.with_chain_id(chain_id)
|
.with_chain_id(chain_id)
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
//! The alloy crate is convenient but requires a tokio runtime.
|
//! The alloy crate __requires__ a tokio runtime.
|
||||||
//! We contain any async rust right here.
|
//! We contain any async rust right here.
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
use std::pin::Pin;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
use tokio::spawn;
|
use tokio::spawn;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::{mpsc, oneshot};
|
||||||
use tokio::task::JoinError;
|
use tokio::task::JoinError;
|
||||||
|
|
||||||
use crate::trace::Trace;
|
use crate::trace::Trace;
|
||||||
@@ -15,15 +16,18 @@ use crate::transaction::Transaction;
|
|||||||
pub(crate) static TO_TOKIO: Lazy<Mutex<TokioRuntime>> =
|
pub(crate) static TO_TOKIO: Lazy<Mutex<TokioRuntime>> =
|
||||||
Lazy::new(|| Mutex::new(TokioRuntime::spawn()));
|
Lazy::new(|| Mutex::new(TokioRuntime::spawn()));
|
||||||
|
|
||||||
// Common interface for executing async node interactions from a non-async context.
|
/// Common interface for executing async node interactions from a non-async context.
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
pub(crate) trait AsyncNodeInteraction: Send + 'static {
|
pub(crate) trait AsyncNodeInteraction: Send + 'static {
|
||||||
type Output: Send + 'static;
|
type Output: Send;
|
||||||
|
|
||||||
/// Any async calls the task needs to perform go here.
|
//// Returns the task and the output sender.
|
||||||
fn execute_async(self) -> impl std::future::Future<Output = Self::Output> + Send;
|
fn split(
|
||||||
|
self,
|
||||||
/// Returns the interactions output sender.
|
) -> (
|
||||||
fn output_sender(&self) -> mpsc::Sender<Self::Output>;
|
Pin<Box<dyn Future<Output = Self::Output> + Send>>,
|
||||||
|
oneshot::Sender<Self::Output>,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct TokioRuntime {
|
pub(crate) struct TokioRuntime {
|
||||||
@@ -58,18 +62,17 @@ impl TokioRuntime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn interaction<T: AsyncNodeInteraction>(
|
async fn interaction<T>(mut receiver: mpsc::Receiver<T>) -> Result<(), JoinError>
|
||||||
mut receiver: mpsc::Receiver<T>,
|
where
|
||||||
) -> Result<(), JoinError> {
|
T: AsyncNodeInteraction,
|
||||||
|
{
|
||||||
while let Some(task) = receiver.recv().await {
|
while let Some(task) = receiver.recv().await {
|
||||||
spawn(async move {
|
spawn(async move {
|
||||||
let sender = task.output_sender();
|
let (task, sender) = task.split();
|
||||||
let result = task.execute_async().await;
|
sender
|
||||||
if let Err(error) = sender.send(result).await {
|
.send(task.await)
|
||||||
log::error!("failed to send task output: {error}");
|
.unwrap_or_else(|_| panic!("failed to send task output"));
|
||||||
}
|
});
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,57 +1,43 @@
|
|||||||
//! Trace transactions in a sync context.
|
//! Trace transactions in a sync context.
|
||||||
|
|
||||||
use alloy::primitives::TxHash;
|
use std::pin::Pin;
|
||||||
use alloy::providers::ProviderBuilder;
|
|
||||||
use alloy::providers::ext::DebugApi;
|
use alloy::rpc::types::trace::geth::GethTrace;
|
||||||
use alloy::rpc::types::TransactionReceipt;
|
use tokio::sync::oneshot;
|
||||||
use alloy::rpc::types::trace::geth::{GethDebugTracingOptions, GethTrace};
|
|
||||||
use tokio::sync::mpsc;
|
|
||||||
|
|
||||||
use crate::TO_TOKIO;
|
use crate::TO_TOKIO;
|
||||||
use crate::tokio_runtime::AsyncNodeInteraction;
|
use crate::tokio_runtime::AsyncNodeInteraction;
|
||||||
|
|
||||||
|
pub type Task = Pin<Box<dyn Future<Output = anyhow::Result<GethTrace>> + Send>>;
|
||||||
|
|
||||||
pub(crate) struct Trace {
|
pub(crate) struct Trace {
|
||||||
transaction_hash: TxHash,
|
sender: oneshot::Sender<anyhow::Result<GethTrace>>,
|
||||||
options: GethDebugTracingOptions,
|
task: Task,
|
||||||
geth_trace_sender: mpsc::Sender<anyhow::Result<GethTrace>>,
|
|
||||||
connection_string: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsyncNodeInteraction for Trace {
|
impl AsyncNodeInteraction for Trace {
|
||||||
type Output = anyhow::Result<GethTrace>;
|
type Output = anyhow::Result<GethTrace>;
|
||||||
|
|
||||||
async fn execute_async(self) -> Self::Output {
|
fn split(
|
||||||
let provider = ProviderBuilder::new()
|
self,
|
||||||
.connect(&self.connection_string)
|
) -> (
|
||||||
.await?;
|
std::pin::Pin<Box<dyn Future<Output = Self::Output> + Send>>,
|
||||||
Ok(provider
|
oneshot::Sender<Self::Output>,
|
||||||
.debug_trace_transaction(self.transaction_hash, self.options)
|
) {
|
||||||
.await?)
|
(self.task, self.sender)
|
||||||
}
|
|
||||||
|
|
||||||
fn output_sender(&self) -> mpsc::Sender<Self::Output> {
|
|
||||||
self.geth_trace_sender.clone()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trace the transaction in [TransactionReceipt] against the `node`,
|
/// Execute some [Task] that return a [GethTrace] result.
|
||||||
/// using the provided [GethDebugTracingOptions].
|
pub fn trace_transaction(task: Task) -> anyhow::Result<GethTrace> {
|
||||||
pub fn trace_transaction(
|
let task_sender = TO_TOKIO.lock().unwrap().trace_sender.clone();
|
||||||
transaction_receipt: TransactionReceipt,
|
let (sender, receiver) = oneshot::channel();
|
||||||
options: GethDebugTracingOptions,
|
|
||||||
connection_string: String,
|
|
||||||
) -> anyhow::Result<GethTrace> {
|
|
||||||
let trace_sender = TO_TOKIO.lock().unwrap().trace_sender.clone();
|
|
||||||
let (geth_trace_sender, mut geth_trace_receiver) = mpsc::channel(1);
|
|
||||||
|
|
||||||
trace_sender.blocking_send(Trace {
|
task_sender
|
||||||
transaction_hash: transaction_receipt.transaction_hash,
|
.blocking_send(Trace { task, sender })
|
||||||
options,
|
.expect("we are not calling this from an async context");
|
||||||
geth_trace_sender,
|
|
||||||
connection_string,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
geth_trace_receiver
|
receiver
|
||||||
.blocking_recv()
|
.blocking_recv()
|
||||||
.unwrap_or_else(|| anyhow::bail!("no receipt received"))
|
.unwrap_or_else(|error| anyhow::bail!("no trace received: {error}"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,52 +1,46 @@
|
|||||||
//! Execute transactions in a sync context.
|
//! Execute transactions in a sync context.
|
||||||
|
|
||||||
use alloy::providers::{Provider, ProviderBuilder};
|
use std::pin::Pin;
|
||||||
use alloy::rpc::types::{TransactionReceipt, TransactionRequest};
|
|
||||||
use tokio::sync::mpsc;
|
use alloy::rpc::types::TransactionReceipt;
|
||||||
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
use crate::TO_TOKIO;
|
use crate::TO_TOKIO;
|
||||||
use crate::tokio_runtime::AsyncNodeInteraction;
|
use crate::tokio_runtime::AsyncNodeInteraction;
|
||||||
|
|
||||||
|
pub type Task = Pin<Box<dyn Future<Output = anyhow::Result<TransactionReceipt>> + Send>>;
|
||||||
|
|
||||||
pub(crate) struct Transaction {
|
pub(crate) struct Transaction {
|
||||||
transaction_request: TransactionRequest,
|
receipt_sender: oneshot::Sender<anyhow::Result<TransactionReceipt>>,
|
||||||
receipt_sender: mpsc::Sender<anyhow::Result<TransactionReceipt>>,
|
task: Task,
|
||||||
connection_string: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsyncNodeInteraction for Transaction {
|
impl AsyncNodeInteraction for Transaction {
|
||||||
type Output = anyhow::Result<TransactionReceipt>;
|
type Output = anyhow::Result<TransactionReceipt>;
|
||||||
|
|
||||||
async fn execute_async(self) -> Self::Output {
|
fn split(
|
||||||
let provider = ProviderBuilder::new()
|
self,
|
||||||
.connect(&self.connection_string)
|
) -> (
|
||||||
.await?;
|
Pin<Box<dyn Future<Output = Self::Output> + Send>>,
|
||||||
Ok(provider
|
oneshot::Sender<Self::Output>,
|
||||||
.send_transaction(self.transaction_request)
|
) {
|
||||||
.await?
|
(self.task, self.receipt_sender)
|
||||||
.get_receipt()
|
|
||||||
.await?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn output_sender(&self) -> mpsc::Sender<Self::Output> {
|
|
||||||
self.receipt_sender.clone()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute the [TransactionRequest] against the `node`.
|
/// Execute some [Task] that returns a [TransactionReceipt].
|
||||||
pub fn execute_transaction(
|
pub fn execute_transaction(task: Task) -> anyhow::Result<TransactionReceipt> {
|
||||||
transaction_request: TransactionRequest,
|
|
||||||
connection_string: String,
|
|
||||||
) -> anyhow::Result<TransactionReceipt> {
|
|
||||||
let request_sender = TO_TOKIO.lock().unwrap().transaction_sender.clone();
|
let request_sender = TO_TOKIO.lock().unwrap().transaction_sender.clone();
|
||||||
let (receipt_sender, mut receipt_receiver) = mpsc::channel(1);
|
let (receipt_sender, receipt_receiver) = oneshot::channel();
|
||||||
|
|
||||||
request_sender.blocking_send(Transaction {
|
request_sender
|
||||||
transaction_request,
|
.blocking_send(Transaction {
|
||||||
receipt_sender,
|
receipt_sender,
|
||||||
connection_string,
|
task,
|
||||||
})?;
|
})
|
||||||
|
.expect("we are not calling this from an async context");
|
||||||
|
|
||||||
receipt_receiver
|
receipt_receiver
|
||||||
.blocking_recv()
|
.blocking_recv()
|
||||||
.unwrap_or_else(|| anyhow::bail!("no receipt received"))
|
.unwrap_or_else(|error| anyhow::bail!("no receipt received: {error}"))
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-5
@@ -10,9 +10,12 @@ use std::{
|
|||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use alloy::rpc::types::{
|
use alloy::{
|
||||||
TransactionReceipt, TransactionRequest,
|
providers::{Provider, ProviderBuilder, ext::DebugApi},
|
||||||
trace::geth::{DiffMode, PreStateFrame},
|
rpc::types::{
|
||||||
|
TransactionReceipt, TransactionRequest,
|
||||||
|
trace::geth::{DiffMode, GethDebugTracingOptions, PreStateConfig, PreStateFrame},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use revive_dt_config::Arguments;
|
use revive_dt_config::Arguments;
|
||||||
use revive_dt_node_interaction::{
|
use revive_dt_node_interaction::{
|
||||||
@@ -144,14 +147,37 @@ impl EthereumNode for Instance {
|
|||||||
&self,
|
&self,
|
||||||
transaction: TransactionRequest,
|
transaction: TransactionRequest,
|
||||||
) -> anyhow::Result<alloy::rpc::types::TransactionReceipt> {
|
) -> anyhow::Result<alloy::rpc::types::TransactionReceipt> {
|
||||||
execute_transaction(transaction, self.connection_string())
|
let connection_string = self.connection_string();
|
||||||
|
|
||||||
|
execute_transaction(Box::pin(async move {
|
||||||
|
Ok(ProviderBuilder::new()
|
||||||
|
.connect(&connection_string)
|
||||||
|
.await?
|
||||||
|
.send_transaction(transaction)
|
||||||
|
.await?
|
||||||
|
.get_receipt()
|
||||||
|
.await?)
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trace_transaction(
|
fn trace_transaction(
|
||||||
&self,
|
&self,
|
||||||
transaction: TransactionReceipt,
|
transaction: TransactionReceipt,
|
||||||
) -> anyhow::Result<alloy::rpc::types::trace::geth::GethTrace> {
|
) -> anyhow::Result<alloy::rpc::types::trace::geth::GethTrace> {
|
||||||
trace_transaction(transaction, Default::default(), self.connection_string())
|
let connection_string = self.connection_string();
|
||||||
|
let trace_options = GethDebugTracingOptions::prestate_tracer(PreStateConfig {
|
||||||
|
diff_mode: Some(true),
|
||||||
|
disable_code: None,
|
||||||
|
disable_storage: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
trace_transaction(Box::pin(async move {
|
||||||
|
Ok(ProviderBuilder::new()
|
||||||
|
.connect(&connection_string)
|
||||||
|
.await?
|
||||||
|
.debug_trace_transaction(transaction.transaction_hash, trace_options)
|
||||||
|
.await?)
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_node<T: Node + Send>(args: &Arguments, genesis: String) -> anyhow::Result<T> {
|
fn spawn_node<T: Node + Send>(args: &Arguments, genesis: String) -> anyhow::Result<T> {
|
||||||
let mut node = T::new(&args);
|
let mut node = T::new(args);
|
||||||
log::info!("starting node: {}", node.connection_string());
|
log::info!("starting node: {}", node.connection_string());
|
||||||
node.spawn(genesis)?;
|
node.spawn(genesis)?;
|
||||||
Ok(node)
|
Ok(node)
|
||||||
|
|||||||
Reference in New Issue
Block a user