mirror of
https://github.com/pezkuwichain/revive-differential-tests.git
synced 2026-06-12 08:51:02 +00:00
the node interaction interface
Signed-off-by: xermicus <bigcyrill@hotmail.com>
This commit is contained in:
Generated
+40
-4
@@ -305,7 +305,9 @@ dependencies = [
|
||||
"alloy-network-primitives",
|
||||
"alloy-primitives",
|
||||
"alloy-rpc-client",
|
||||
"alloy-rpc-types-debug",
|
||||
"alloy-rpc-types-eth",
|
||||
"alloy-rpc-types-trace",
|
||||
"alloy-sol-types",
|
||||
"alloy-transport",
|
||||
"alloy-transport-http",
|
||||
@@ -383,6 +385,7 @@ checksum = "9157deaec6ba2ad7854f16146e4cd60280e76593eed79fdcb06e0fa8b6c60f77"
|
||||
dependencies = [
|
||||
"alloy-primitives",
|
||||
"alloy-rpc-types-eth",
|
||||
"alloy-rpc-types-trace",
|
||||
"alloy-serde",
|
||||
"serde",
|
||||
]
|
||||
@@ -398,6 +401,16 @@ dependencies = [
|
||||
"alloy-serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alloy-rpc-types-debug"
|
||||
version = "0.12.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08b113a0087d226291b9768ed331818fa0b0744cc1207ae7c150687cf3fde1bd"
|
||||
dependencies = [
|
||||
"alloy-primitives",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alloy-rpc-types-eth"
|
||||
version = "0.12.6"
|
||||
@@ -418,6 +431,20 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alloy-rpc-types-trace"
|
||||
version = "0.12.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4747763aee39c1b0f5face79bde9be8932be05b2db7d8bdcebb93490f32c889c"
|
||||
dependencies = [
|
||||
"alloy-primitives",
|
||||
"alloy-rpc-types-eth",
|
||||
"alloy-serde",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "alloy-serde"
|
||||
version = "0.12.6"
|
||||
@@ -1414,7 +1441,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2864,6 +2891,14 @@ dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "revive-dt-node"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"anyhow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "revive-dt-node-interaction"
|
||||
version = "0.1.0"
|
||||
@@ -2873,6 +2908,7 @@ dependencies = [
|
||||
"hex",
|
||||
"log",
|
||||
"once_cell",
|
||||
"revive-dt-node",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
]
|
||||
@@ -3015,7 +3051,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3356,7 +3392,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"psm",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3490,7 +3526,7 @@ dependencies = [
|
||||
"getrandom 0.3.2",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+16
-1
@@ -16,10 +16,11 @@ rust-version = "1.85.0"
|
||||
revive-dt-compiler = { version = "0.1.0", path = "crates/compiler" }
|
||||
revive-dt-core = { version = "0.1.0", path = "crates/core" }
|
||||
revive-dt-format = { version = "0.1.0", path = "crates/format" }
|
||||
revive-dt-node = { version = "0.1.0", path = "crates/node" }
|
||||
revive-dt-node-interaction = { version = "0.1.0", path = "crates/node-interaction" }
|
||||
revive-dt-node-pool = { version = "0.1.0", path = "crates/node-pool" }
|
||||
|
||||
anyhow = "1.0"
|
||||
alloy = { version = "0.12.6", default-features = false, features = [ "providers", "rpc-types", "json-abi", "reqwest", "std" ] }
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
env_logger = "0.11.7"
|
||||
hex = "0.4.3"
|
||||
@@ -36,6 +37,20 @@ tokio = { version = "1", default-features = false, features = ["rt-multi-thread"
|
||||
# revive compiler
|
||||
revive-solc-json-interface = { git = "https://github.com/paritytech/revive", rev = "497dae2494dabe12d1af32d6d687122903cb2ada" }
|
||||
revive-common = { git = "https://github.com/paritytech/revive", rev = "497dae2494dabe12d1af32d6d687122903cb2ada" }
|
||||
revive-differential = { git = "https://github.com/paritytech/revive", rev = "497dae2494dabe12d1af32d6d687122903cb2ada" }
|
||||
|
||||
[workspace.dependencies.alloy]
|
||||
version = "0.12.6"
|
||||
default-features = false
|
||||
features = [
|
||||
"providers",
|
||||
"rpc-types",
|
||||
"json-abi",
|
||||
"reqwest",
|
||||
"std",
|
||||
"genesis",
|
||||
"provider-debug-api"
|
||||
]
|
||||
|
||||
[profile.bench]
|
||||
inherits = "release"
|
||||
|
||||
@@ -34,7 +34,6 @@ impl SolidityCompiler for Solc {
|
||||
serde_json::to_writer(stdin, input)?;
|
||||
|
||||
let output = child.wait_with_output()?.stdout;
|
||||
|
||||
Ok(serde_json::from_slice(&output)?)
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ pub fn build_evm(
|
||||
let mut result = HashMap::new();
|
||||
for mode in modes {
|
||||
let mut compiler = Compiler::<Solc>::new().base_path(directory.display().to_string());
|
||||
for (_, file) in &sources {
|
||||
for file in sources.values() {
|
||||
compiler = compiler.with_source(file)?;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ fn main() -> anyhow::Result<()> {
|
||||
|
||||
tests
|
||||
.par_iter()
|
||||
.for_each(|metadata| match build_evm(&metadata) {
|
||||
.for_each(|metadata| match build_evm(metadata) {
|
||||
Ok(_) => log::info!(
|
||||
"metadata {} compilation success",
|
||||
metadata.path.as_ref().unwrap().display()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "revive-dt-node-interaction"
|
||||
description = "send and trace transactions to EVM and PVM nodes"
|
||||
description = "send and trace transactions to nodes"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
@@ -16,3 +16,5 @@ log = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
|
||||
revive-dt-node = { workspace = true }
|
||||
|
||||
@@ -1,45 +1,25 @@
|
||||
//! Implements helpers for node interactions using transactions.
|
||||
//!
|
||||
//! The alloy crate is convenient but requires running in a tokio runtime.
|
||||
//! We contain any async rust right here.
|
||||
//! This crate implements all node interactions.
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::Mutex;
|
||||
use std::thread;
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio::sync::mpsc;
|
||||
use transaction::Transaction;
|
||||
use alloy::rpc::types::trace::geth::GethTrace;
|
||||
use alloy::rpc::types::{TransactionReceipt, TransactionRequest};
|
||||
use revive_dt_node::Node;
|
||||
use tokio_runtime::TO_TOKIO;
|
||||
|
||||
mod tokio_runtime;
|
||||
pub mod trace;
|
||||
pub mod transaction;
|
||||
|
||||
pub(crate) static TO_TOKIO: Lazy<Mutex<TokioRuntime>> =
|
||||
Lazy::new(|| Mutex::new(TokioRuntime::spawn()));
|
||||
/// An interface for all node interactions.
|
||||
pub trait NodeInteraction: Node {
|
||||
/// Execute the [TransactionRequest] and return a [TransactionReceipt].
|
||||
fn execute_transaction(
|
||||
&self,
|
||||
transaction_request: TransactionRequest,
|
||||
) -> anyhow::Result<TransactionReceipt>;
|
||||
|
||||
pub struct TokioRuntime {
|
||||
pub transaction_sender: mpsc::Sender<Transaction>,
|
||||
}
|
||||
|
||||
impl TokioRuntime {
|
||||
pub fn spawn() -> Self {
|
||||
let rt = Runtime::new().expect("should be able to create the tokio runtime");
|
||||
let (transaction_sender, mut transaction_receiver) = mpsc::channel::<Transaction>(1024);
|
||||
|
||||
thread::spawn(move || {
|
||||
rt.block_on(async move {
|
||||
while let Some(transaction) = transaction_receiver.recv().await {
|
||||
tokio::task::spawn(async move {
|
||||
let sender = transaction.receipt_sender.clone();
|
||||
let result = transaction.execute().await;
|
||||
if let Err(error) = sender.send(result).await {
|
||||
log::error!("failed to send transaction receipt: {error}");
|
||||
}
|
||||
})
|
||||
.await
|
||||
.expect("should alaways be able to spawn the tokio tasks");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Self { transaction_sender }
|
||||
}
|
||||
/// Trace the transaction in the [TransactionReceipt] and return a [GethTrace].
|
||||
fn trace_transaction(
|
||||
&self,
|
||||
transaction_receipt: TransactionReceipt,
|
||||
) -> anyhow::Result<GethTrace>;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
//! The alloy crate is convenient but requires a tokio runtime.
|
||||
//! We contain any async rust right here.
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::Mutex;
|
||||
use std::thread;
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio::spawn;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::task::JoinError;
|
||||
|
||||
use crate::trace::Trace;
|
||||
use crate::transaction::Transaction;
|
||||
|
||||
pub(crate) static TO_TOKIO: Lazy<Mutex<TokioRuntime>> =
|
||||
Lazy::new(|| Mutex::new(TokioRuntime::spawn()));
|
||||
|
||||
// Common interface for executing async node interactions from a non-async context.
|
||||
pub(crate) trait AsyncNodeInteraction: Send + 'static {
|
||||
type Output: Send + 'static;
|
||||
|
||||
/// Any async calls the task needs to perform go here.
|
||||
fn execute_async(self) -> impl std::future::Future<Output = Self::Output> + Send;
|
||||
|
||||
/// Returns the interactions output sender.
|
||||
fn output_sender(&self) -> mpsc::Sender<Self::Output>;
|
||||
}
|
||||
|
||||
pub(crate) struct TokioRuntime {
|
||||
pub(crate) transaction_sender: mpsc::Sender<Transaction>,
|
||||
pub(crate) trace_sender: mpsc::Sender<Trace>,
|
||||
}
|
||||
|
||||
impl TokioRuntime {
|
||||
fn spawn() -> Self {
|
||||
let rt = Runtime::new().expect("should be able to create the tokio runtime");
|
||||
let (transaction_sender, transaction_receiver) = mpsc::channel::<Transaction>(1024);
|
||||
let (trace_sender, trace_receiver) = mpsc::channel::<Trace>(1024);
|
||||
|
||||
thread::spawn(move || {
|
||||
rt.block_on(async move {
|
||||
let transaction_task = spawn(interaction::<Transaction>(transaction_receiver));
|
||||
let trace_task = spawn(interaction::<Trace>(trace_receiver));
|
||||
|
||||
if let Err(error) = transaction_task.await {
|
||||
log::error!("tokio transaction task failed: {error}");
|
||||
}
|
||||
if let Err(error) = trace_task.await {
|
||||
log::error!("tokio trace transaction task failed: {error}");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Self {
|
||||
transaction_sender,
|
||||
trace_sender,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn interaction<T: AsyncNodeInteraction>(
|
||||
mut receiver: mpsc::Receiver<T>,
|
||||
) -> Result<(), JoinError> {
|
||||
while let Some(task) = receiver.recv().await {
|
||||
spawn(async move {
|
||||
let sender = task.output_sender();
|
||||
let result = task.execute_async().await;
|
||||
if let Err(error) = sender.send(result).await {
|
||||
log::error!("failed to send task output: {error}");
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
//! Trace transactions in a sync context.
|
||||
|
||||
use alloy::primitives::TxHash;
|
||||
use alloy::providers::ProviderBuilder;
|
||||
use alloy::providers::ext::DebugApi;
|
||||
use alloy::rpc::types::TransactionReceipt;
|
||||
use alloy::rpc::types::trace::geth::{GethDebugTracingOptions, GethTrace};
|
||||
use revive_dt_node::Node;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::TO_TOKIO;
|
||||
use crate::tokio_runtime::AsyncNodeInteraction;
|
||||
|
||||
pub(crate) struct Trace {
|
||||
transaction_hash: TxHash,
|
||||
options: GethDebugTracingOptions,
|
||||
geth_trace_sender: mpsc::Sender<anyhow::Result<GethTrace>>,
|
||||
connection_string: String,
|
||||
}
|
||||
|
||||
impl AsyncNodeInteraction for Trace {
|
||||
type Output = anyhow::Result<GethTrace>;
|
||||
|
||||
async fn execute_async(self) -> Self::Output {
|
||||
let provider = ProviderBuilder::new()
|
||||
.connect(&self.connection_string)
|
||||
.await?;
|
||||
Ok(provider
|
||||
.debug_trace_transaction(self.transaction_hash, self.options)
|
||||
.await?)
|
||||
}
|
||||
|
||||
fn output_sender(&self) -> mpsc::Sender<Self::Output> {
|
||||
self.geth_trace_sender.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Trace the transaction in [TransactionReceipt] against the `node`,
|
||||
/// using the provided [GethDebugTracingOptions].
|
||||
pub fn trace_transaction<T: Node>(
|
||||
transaction_receipt: TransactionReceipt,
|
||||
options: GethDebugTracingOptions,
|
||||
node: &T,
|
||||
) -> 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 {
|
||||
transaction_hash: transaction_receipt.transaction_hash,
|
||||
options,
|
||||
geth_trace_sender,
|
||||
connection_string: node.connection_string(),
|
||||
})?;
|
||||
|
||||
geth_trace_receiver
|
||||
.blocking_recv()
|
||||
.unwrap_or_else(|| anyhow::bail!("no receipt received"))
|
||||
}
|
||||
@@ -1,17 +1,23 @@
|
||||
//! Execute transactions in a sync context.
|
||||
|
||||
use alloy::providers::{Provider, ProviderBuilder};
|
||||
use alloy::rpc::types::{TransactionReceipt, TransactionRequest};
|
||||
use revive_dt_node::Node;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::TO_TOKIO;
|
||||
use crate::tokio_runtime::AsyncNodeInteraction;
|
||||
|
||||
pub struct Transaction {
|
||||
pub transaction_request: TransactionRequest,
|
||||
pub receipt_sender: mpsc::Sender<anyhow::Result<TransactionReceipt>>,
|
||||
pub connection_string: String,
|
||||
pub(crate) struct Transaction {
|
||||
transaction_request: TransactionRequest,
|
||||
receipt_sender: mpsc::Sender<anyhow::Result<TransactionReceipt>>,
|
||||
connection_string: String,
|
||||
}
|
||||
|
||||
impl Transaction {
|
||||
pub async fn execute(self) -> anyhow::Result<TransactionReceipt> {
|
||||
impl AsyncNodeInteraction for Transaction {
|
||||
type Output = anyhow::Result<TransactionReceipt>;
|
||||
|
||||
async fn execute_async(self) -> Self::Output {
|
||||
let provider = ProviderBuilder::new()
|
||||
.connect(&self.connection_string)
|
||||
.await?;
|
||||
@@ -21,11 +27,16 @@ impl Transaction {
|
||||
.get_receipt()
|
||||
.await?)
|
||||
}
|
||||
|
||||
fn output_sender(&self) -> mpsc::Sender<Self::Output> {
|
||||
self.receipt_sender.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute_transaction(
|
||||
/// Execute the [TransactionRequest] against the `node`.
|
||||
pub fn execute_transaction<T: Node>(
|
||||
transaction_request: TransactionRequest,
|
||||
connection_string: String,
|
||||
node: &T,
|
||||
) -> anyhow::Result<TransactionReceipt> {
|
||||
let request_sender = TO_TOKIO.lock().unwrap().transaction_sender.clone();
|
||||
let (receipt_sender, mut receipt_receiver) = mpsc::channel(1);
|
||||
@@ -33,7 +44,7 @@ pub fn execute_transaction(
|
||||
request_sender.blocking_send(Transaction {
|
||||
transaction_request,
|
||||
receipt_sender,
|
||||
connection_string,
|
||||
connection_string: node.connection_string(),
|
||||
})?;
|
||||
|
||||
receipt_receiver
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "revive-dt-node"
|
||||
description = "abstraction over blockchain nodes"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
alloy = { workspace = true }
|
||||
@@ -0,0 +1,29 @@
|
||||
//! An abstract interface for testing nodes.
|
||||
|
||||
use alloy::{
|
||||
genesis::Genesis,
|
||||
rpc::types::{TransactionReceipt, TransactionRequest, trace::parity::StateDiff},
|
||||
};
|
||||
|
||||
pub trait Node {
|
||||
/// Configures the node with the given genesis configuration.
|
||||
fn with_genesis(&mut self, genesis: &Genesis) -> anyhow::Result<&mut Self>;
|
||||
|
||||
/// Spawns the node, blocking until it's ready to accept transactions.
|
||||
fn spawn(&mut self) -> anyhow::Result<&mut Self>;
|
||||
|
||||
/// Prune the node, blocking until it's completely stopped.
|
||||
fn shutdown(self) -> anyhow::Result<()>;
|
||||
|
||||
/// Returns the nodes connection string.
|
||||
fn connection_string(&self) -> String;
|
||||
|
||||
/// Execute the [TransactionRequest], blocking until the transaction is mined.
|
||||
fn execute_transaction(
|
||||
&self,
|
||||
transaction: &TransactionRequest,
|
||||
) -> anyhow::Result<TransactionReceipt>;
|
||||
|
||||
/// Returns the state diff of the transaction hash in the [TransactionReceipt].
|
||||
fn state_diff(&self, transaction: &TransactionReceipt) -> anyhow::Result<StateDiff>;
|
||||
}
|
||||
Reference in New Issue
Block a user