mirror of
https://github.com/pezkuwichain/revive-differential-tests.git
synced 2026-04-22 17:17:58 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0ba6d5a653 | |||
| 3a537c2812 | |||
| 4ab79ed97e | |||
| ee97b62e70 | |||
| e9b5a06aec | |||
| 534170db6f | |||
| 090b56c46a | |||
| 547563e718 |
@@ -8,7 +8,7 @@ clippy:
|
||||
|
||||
machete:
|
||||
cargo install cargo-machete
|
||||
cargo machete
|
||||
cargo machete crates
|
||||
|
||||
test: format clippy machete
|
||||
cargo test --workspace -- --nocapture
|
||||
|
||||
@@ -10,6 +10,24 @@ The revive differential testing framework allows to define smart contract tests
|
||||
|
||||
For now, the format used to write tests is the [matter-labs era compiler format](https://github.com/matter-labs/era-compiler-tests?tab=readme-ov-file#matter-labs-simplecomplex-format). This allows us to re-use many tests from their corpora.
|
||||
|
||||
# Dependencies
|
||||
|
||||
The following is needed to execute `geth` vs. substrate node differential tests:
|
||||
- [`geth` node](https://github.com/ethereum/go-ethereum/)
|
||||
- `kitchensink` node and revive ETH RPC binary
|
||||
- [`solc` compiler](https://github.com/ethereum/solidity)
|
||||
- [`resolc` compiler](https://github.com/paritytech/revive)
|
||||
|
||||
For the compilers and `geth` node please check the project help on how to obtain them.
|
||||
|
||||
To install the `kitchensink` node and the ETH RPC binary:
|
||||
```
|
||||
git submodule update --init --recursive
|
||||
cd polkadot-sdk
|
||||
cargo install --locked --force --profile=production --path substrate/bin/node/cli --bin substrate-node --features cli
|
||||
cargo install --path substrate/frame/revive/rpc --bin eth-rpc
|
||||
```
|
||||
|
||||
# The `retester` utility
|
||||
|
||||
The `retester` helper utilty is used to run the tests. To get an idea of what `retester` can do, please consults its command line help:
|
||||
|
||||
+242
-13
@@ -1,8 +1,13 @@
|
||||
//! The test driver handles the compilation and execution of the test cases.
|
||||
|
||||
use alloy::primitives::Bytes;
|
||||
use alloy::rpc::types::TransactionInput;
|
||||
use alloy::{
|
||||
primitives::{Address, map::HashMap},
|
||||
rpc::types::trace::geth::GethTrace,
|
||||
primitives::{Address, TxKind, map::HashMap},
|
||||
rpc::types::{
|
||||
TransactionReceipt, TransactionRequest,
|
||||
trace::geth::{AccountState, DiffMode, GethTrace},
|
||||
},
|
||||
};
|
||||
use revive_dt_compiler::{Compiler, CompilerInput, SolidityCompiler};
|
||||
use revive_dt_config::Arguments;
|
||||
@@ -80,10 +85,24 @@ where
|
||||
task.json_output = Some(output.output.clone());
|
||||
task.error = output.error;
|
||||
self.contracts.insert(output.input, output.output);
|
||||
|
||||
if let Some(last_output) = self.contracts.values().last() {
|
||||
if let Some(contracts) = &last_output.contracts {
|
||||
for (file, contracts_map) in contracts {
|
||||
for contract_name in contracts_map.keys() {
|
||||
log::debug!("Compiled contract: {contract_name} from file: {file}");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::warn!("Compiled contracts field is None");
|
||||
}
|
||||
}
|
||||
|
||||
Report::compilation(span, T::config_id(), task);
|
||||
Ok(())
|
||||
}
|
||||
Err(error) => {
|
||||
log::error!("Failed to compile contract: {:?}", error.to_string());
|
||||
task.error = Some(error.to_string());
|
||||
Err(error)
|
||||
}
|
||||
@@ -94,15 +113,170 @@ where
|
||||
&mut self,
|
||||
input: &Input,
|
||||
node: &T::Blockchain,
|
||||
) -> anyhow::Result<GethTrace> {
|
||||
let receipt = node.execute_transaction(input.legacy_transaction(
|
||||
self.config.network_id,
|
||||
0,
|
||||
&self.deployed_contracts,
|
||||
)?)?;
|
||||
dbg!(&receipt);
|
||||
//node.trace_transaction(receipt)
|
||||
todo!()
|
||||
) -> anyhow::Result<(TransactionReceipt, GethTrace, DiffMode)> {
|
||||
log::trace!("Calling execute_input for input: {input:?}");
|
||||
|
||||
let nonce = node.fetch_add_nonce(input.caller)?;
|
||||
|
||||
log::debug!(
|
||||
"Nonce calculated on the execute contract, calculated nonce {}, for contract {}, having address {} on node: {}",
|
||||
&nonce,
|
||||
&input.instance,
|
||||
&input.caller,
|
||||
std::any::type_name::<T>()
|
||||
);
|
||||
|
||||
let tx =
|
||||
match input.legacy_transaction(self.config.network_id, nonce, &self.deployed_contracts)
|
||||
{
|
||||
Ok(tx) => tx,
|
||||
Err(err) => {
|
||||
log::error!("Failed to construct legacy transaction: {err:?}");
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
|
||||
log::trace!("Executing transaction for input: {input:?}");
|
||||
|
||||
let receipt = match node.execute_transaction(tx) {
|
||||
Ok(receipt) => receipt,
|
||||
Err(err) => {
|
||||
log::error!(
|
||||
"Failed to execute transaction when executing the contract: {}, {:?}",
|
||||
&input.instance,
|
||||
err
|
||||
);
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
|
||||
log::trace!(
|
||||
"Transaction receipt for executed contract: {} - {:?}",
|
||||
&input.instance,
|
||||
receipt,
|
||||
);
|
||||
|
||||
let trace = node.trace_transaction(receipt.clone())?;
|
||||
log::trace!(
|
||||
"Trace result for contract: {} - {:?}",
|
||||
&input.instance,
|
||||
trace
|
||||
);
|
||||
|
||||
let diff = node.state_diff(receipt.clone())?;
|
||||
|
||||
Ok((receipt, trace, diff))
|
||||
}
|
||||
|
||||
pub fn deploy_contracts(&mut self, input: &Input, node: &T::Blockchain) -> anyhow::Result<()> {
|
||||
log::debug!(
|
||||
"Deploying contracts {}, having address {} on node: {}",
|
||||
&input.instance,
|
||||
&input.caller,
|
||||
std::any::type_name::<T>()
|
||||
);
|
||||
for output in self.contracts.values() {
|
||||
let Some(contract_map) = &output.contracts else {
|
||||
log::debug!(
|
||||
"No contracts in output — skipping deployment for this input {}",
|
||||
&input.instance
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
for contracts in contract_map.values() {
|
||||
for (contract_name, contract) in contracts {
|
||||
log::debug!(
|
||||
"Contract name is: {:?} and the input name is: {:?}",
|
||||
&contract_name,
|
||||
&input.instance
|
||||
);
|
||||
if contract_name != &input.instance {
|
||||
continue;
|
||||
}
|
||||
|
||||
let bytecode = contract
|
||||
.evm
|
||||
.as_ref()
|
||||
.and_then(|evm| evm.bytecode.as_ref())
|
||||
.map(|b| b.object.clone());
|
||||
|
||||
let Some(code) = bytecode else {
|
||||
log::error!("no bytecode for contract {contract_name}");
|
||||
continue;
|
||||
};
|
||||
|
||||
let nonce = node.fetch_add_nonce(input.caller)?;
|
||||
|
||||
log::debug!(
|
||||
"Calculated nonce {}, for contract {}, having address {} on node: {}",
|
||||
&nonce,
|
||||
&input.instance,
|
||||
&input.caller,
|
||||
std::any::type_name::<T>()
|
||||
);
|
||||
|
||||
let tx = TransactionRequest {
|
||||
from: Some(input.caller),
|
||||
to: Some(TxKind::Create),
|
||||
gas_price: Some(5_000_000),
|
||||
gas: Some(5_000_000),
|
||||
chain_id: Some(self.config.network_id),
|
||||
nonce: Some(nonce),
|
||||
input: TransactionInput::new(Bytes::from(code.into_bytes())),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let receipt = match node.execute_transaction(tx) {
|
||||
Ok(receipt) => receipt,
|
||||
Err(err) => {
|
||||
log::error!(
|
||||
"Failed to execute transaction when deploying the contract on node : {:?}, {:?}, {:?}",
|
||||
std::any::type_name::<T>(),
|
||||
&contract_name,
|
||||
err
|
||||
);
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
|
||||
log::debug!(
|
||||
"Deployment tx sent for {} with nonce {} → tx hash: {:?}, on node: {:?}",
|
||||
contract_name,
|
||||
nonce,
|
||||
receipt.transaction_hash,
|
||||
std::any::type_name::<T>(),
|
||||
);
|
||||
|
||||
log::trace!(
|
||||
"Deployed transaction receipt for contract: {} - {:?}, on node: {:?}",
|
||||
&contract_name,
|
||||
receipt,
|
||||
std::any::type_name::<T>(),
|
||||
);
|
||||
|
||||
let Some(address) = receipt.contract_address else {
|
||||
log::error!(
|
||||
"contract {contract_name} deployment did not return an address"
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
self.deployed_contracts
|
||||
.insert(contract_name.clone(), address);
|
||||
log::trace!(
|
||||
"deployed contract `{}` at {:?}, on node {:?}",
|
||||
contract_name,
|
||||
address,
|
||||
std::any::type_name::<T>()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log::debug!("Available contracts: {:?}", self.deployed_contracts.keys());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,6 +306,32 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trace_diff_mode(label: &str, diff: &DiffMode) {
|
||||
log::trace!("{label} - PRE STATE:");
|
||||
for (addr, state) in &diff.pre {
|
||||
Self::trace_account_state(" [pre]", addr, state);
|
||||
}
|
||||
|
||||
log::trace!("{label} - POST STATE:");
|
||||
for (addr, state) in &diff.post {
|
||||
Self::trace_account_state(" [post]", addr, state);
|
||||
}
|
||||
}
|
||||
|
||||
fn trace_account_state(prefix: &str, addr: &Address, state: &AccountState) {
|
||||
log::trace!("{prefix} 0x{addr:x}");
|
||||
|
||||
if let Some(balance) = &state.balance {
|
||||
log::trace!("{prefix} balance: {balance}");
|
||||
}
|
||||
if let Some(nonce) = &state.nonce {
|
||||
log::trace!("{prefix} nonce: {nonce}");
|
||||
}
|
||||
if let Some(code) = &state.code {
|
||||
log::trace!("{prefix} code: {code}");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute(&mut self, span: Span) -> anyhow::Result<()> {
|
||||
for mode in self.metadata.solc_modes() {
|
||||
let mut leader_state = State::<L>::new(self.config, span);
|
||||
@@ -142,8 +342,37 @@ where
|
||||
|
||||
for case in &self.metadata.cases {
|
||||
for input in &case.inputs {
|
||||
let _ = leader_state.execute_input(input, self.leader_node)?;
|
||||
let _ = follower_state.execute_input(input, self.follower_node)?;
|
||||
log::debug!("Starting deploying contract {}", &input.instance);
|
||||
leader_state.deploy_contracts(input, self.leader_node)?;
|
||||
follower_state.deploy_contracts(input, self.follower_node)?;
|
||||
|
||||
log::debug!("Starting executing contract {}", &input.instance);
|
||||
let (leader_receipt, _, leader_diff) =
|
||||
leader_state.execute_input(input, self.leader_node)?;
|
||||
let (follower_receipt, _, follower_diff) =
|
||||
follower_state.execute_input(input, self.follower_node)?;
|
||||
|
||||
if leader_diff == follower_diff {
|
||||
log::debug!("State diffs match between leader and follower.");
|
||||
} else {
|
||||
log::debug!("State diffs mismatch between leader and follower.");
|
||||
Self::trace_diff_mode("Leader", &leader_diff);
|
||||
Self::trace_diff_mode("Follower", &follower_diff);
|
||||
}
|
||||
|
||||
if leader_receipt.logs() != follower_receipt.logs() {
|
||||
log::debug!("Log/event mismatch between leader and follower.");
|
||||
log::trace!("Leader logs: {:?}", leader_receipt.logs());
|
||||
log::trace!("Follower logs: {:?}", follower_receipt.logs());
|
||||
}
|
||||
|
||||
if leader_receipt.status() != follower_receipt.status() {
|
||||
log::debug!(
|
||||
"Mismatch in status: leader = {}, follower = {}",
|
||||
leader_receipt.status(),
|
||||
follower_receipt.status()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
use revive_dt_compiler::{SolidityCompiler, revive_resolc, solc};
|
||||
use revive_dt_config::TestingPlatform;
|
||||
use revive_dt_node::geth;
|
||||
use revive_dt_node::{geth, kitchensink::KitchensinkNode};
|
||||
use revive_dt_node_interaction::EthereumNode;
|
||||
|
||||
pub mod driver;
|
||||
@@ -37,7 +37,7 @@ impl Platform for Geth {
|
||||
pub struct Kitchensink;
|
||||
|
||||
impl Platform for Kitchensink {
|
||||
type Blockchain = geth::Instance;
|
||||
type Blockchain = KitchensinkNode;
|
||||
type Compiler = revive_resolc::Resolc;
|
||||
|
||||
fn config_id() -> TestingPlatform {
|
||||
|
||||
+31
-15
@@ -5,7 +5,7 @@ use rayon::{ThreadPoolBuilder, prelude::*};
|
||||
|
||||
use revive_dt_config::*;
|
||||
use revive_dt_core::{
|
||||
Geth, Kitchensink,
|
||||
Geth, Kitchensink, Platform,
|
||||
driver::{Driver, State},
|
||||
};
|
||||
use revive_dt_format::{corpus::Corpus, metadata::Metadata};
|
||||
@@ -74,28 +74,30 @@ fn collect_corpora(args: &Arguments) -> anyhow::Result<HashMap<Corpus, Vec<Metad
|
||||
Ok(corpora)
|
||||
}
|
||||
|
||||
fn execute_corpus(args: &Arguments, tests: &[Metadata], span: Span) -> anyhow::Result<()> {
|
||||
let leader_nodes = NodePool::new(args)?;
|
||||
let follower_nodes = NodePool::new(args)?;
|
||||
fn run_driver<L, F>(args: &Arguments, tests: &[Metadata], span: Span) -> anyhow::Result<()>
|
||||
where
|
||||
L: Platform,
|
||||
F: Platform,
|
||||
L::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
|
||||
F::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
|
||||
{
|
||||
let leader_nodes = NodePool::<L::Blockchain>::new(args)?;
|
||||
let follower_nodes = NodePool::<F::Blockchain>::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!(),
|
||||
};
|
||||
let mut driver = Driver::<L, F>::new(
|
||||
metadata,
|
||||
args,
|
||||
leader_nodes.round_robbin(),
|
||||
follower_nodes.round_robbin(),
|
||||
);
|
||||
|
||||
match driver.execute(span) {
|
||||
Ok(build) => {
|
||||
Ok(_) => {
|
||||
log::info!(
|
||||
"metadata {} success",
|
||||
metadata.directory().as_ref().unwrap().display()
|
||||
);
|
||||
build
|
||||
}
|
||||
Err(error) => {
|
||||
log::warn!(
|
||||
@@ -109,6 +111,20 @@ fn execute_corpus(args: &Arguments, tests: &[Metadata], span: Span) -> anyhow::R
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn execute_corpus(args: &Arguments, tests: &[Metadata], span: Span) -> anyhow::Result<()> {
|
||||
match (&args.leader, &args.follower) {
|
||||
(TestingPlatform::Geth, TestingPlatform::Kitchensink) => {
|
||||
run_driver::<Geth, Kitchensink>(args, tests, span)?
|
||||
}
|
||||
(TestingPlatform::Geth, TestingPlatform::Geth) => {
|
||||
run_driver::<Geth, Geth>(args, tests, span)?
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compile_corpus(config: &Arguments, tests: &[Metadata], platform: &TestingPlatform, span: Span) {
|
||||
tests.par_iter().for_each(|metadata| {
|
||||
for mode in &metadata.solc_modes() {
|
||||
|
||||
+15
-10
@@ -1,7 +1,8 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use alloy::{
|
||||
json_abi::Function, network::TransactionBuilder, primitives::Address,
|
||||
json_abi::Function,
|
||||
primitives::{Address, TxKind},
|
||||
rpc::types::TransactionRequest,
|
||||
};
|
||||
use semver::VersionReq;
|
||||
@@ -109,17 +110,21 @@ impl Input {
|
||||
deployed_contracts: &HashMap<String, Address>,
|
||||
) -> anyhow::Result<TransactionRequest> {
|
||||
let to = match self.method {
|
||||
Method::Deployer => Address::ZERO,
|
||||
_ => self.instance_to_address(&self.instance, deployed_contracts)?,
|
||||
Method::Deployer => Some(TxKind::Create),
|
||||
_ => Some(TxKind::Call(
|
||||
self.instance_to_address(&self.instance, deployed_contracts)?,
|
||||
)),
|
||||
};
|
||||
|
||||
Ok(TransactionRequest::default()
|
||||
.with_from(self.caller)
|
||||
.with_to(to)
|
||||
.with_nonce(nonce)
|
||||
.with_chain_id(chain_id)
|
||||
.with_gas_price(20_000_000_000)
|
||||
.with_gas_limit(20_000_000_000))
|
||||
Ok(TransactionRequest {
|
||||
from: Some(self.caller),
|
||||
to,
|
||||
nonce: Some(nonce),
|
||||
chain_id: Some(chain_id),
|
||||
gas_price: Some(5_000_000),
|
||||
gas: Some(5_000_000),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ impl Metadata {
|
||||
}
|
||||
|
||||
fn try_from_solidity(path: &Path) -> Option<Self> {
|
||||
let buf = read_to_string(path)
|
||||
let spec = read_to_string(path)
|
||||
.inspect_err(|error| {
|
||||
log::error!(
|
||||
"opening JSON test metadata file '{}' error: {error}",
|
||||
@@ -147,18 +147,24 @@ impl Metadata {
|
||||
buf
|
||||
});
|
||||
|
||||
if buf.is_empty() {
|
||||
if spec.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
match serde_json::from_str::<Self>(&buf) {
|
||||
match serde_json::from_str::<Self>(&spec) {
|
||||
Ok(mut metadata) => {
|
||||
metadata.file_path = Some(path.to_path_buf());
|
||||
let name = path
|
||||
.file_name()
|
||||
.expect("this should be the path to a Solidity file")
|
||||
.to_str()
|
||||
.expect("the file name should be valid UTF-8k");
|
||||
metadata.contracts = Some([(String::from("Test"), format!("{name}:Test"))].into());
|
||||
Some(metadata)
|
||||
}
|
||||
Err(error) => {
|
||||
log::error!(
|
||||
"parsing Solidity test metadata file '{}' error: {error}",
|
||||
"parsing Solidity test metadata file '{}' error: '{error}' from data: {spec}",
|
||||
path.display()
|
||||
);
|
||||
None
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
//! This crate implements all node interactions.
|
||||
|
||||
use alloy::primitives::Address;
|
||||
use alloy::rpc::types::trace::geth::{DiffMode, GethTrace};
|
||||
use alloy::rpc::types::{TransactionReceipt, TransactionRequest};
|
||||
use tokio_runtime::TO_TOKIO;
|
||||
|
||||
pub mod nonce;
|
||||
mod tokio_runtime;
|
||||
pub mod trace;
|
||||
pub mod transaction;
|
||||
@@ -21,4 +23,7 @@ pub trait EthereumNode {
|
||||
|
||||
/// Returns the state diff of the transaction hash in the [TransactionReceipt].
|
||||
fn state_diff(&self, transaction: TransactionReceipt) -> anyhow::Result<DiffMode>;
|
||||
|
||||
/// Returns the next available nonce for the given [Address].
|
||||
fn fetch_add_nonce(&self, address: Address) -> anyhow::Result<u64>;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
use std::pin::Pin;
|
||||
|
||||
use alloy::{
|
||||
primitives::Address,
|
||||
providers::{Provider, ProviderBuilder},
|
||||
};
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
use crate::{TO_TOKIO, tokio_runtime::AsyncNodeInteraction};
|
||||
|
||||
pub type Task = Pin<Box<dyn Future<Output = anyhow::Result<u64>> + Send>>;
|
||||
|
||||
pub(crate) struct Nonce {
|
||||
sender: oneshot::Sender<anyhow::Result<u64>>,
|
||||
task: Task,
|
||||
}
|
||||
|
||||
impl AsyncNodeInteraction for Nonce {
|
||||
type Output = anyhow::Result<u64>;
|
||||
|
||||
fn split(
|
||||
self,
|
||||
) -> (
|
||||
std::pin::Pin<Box<dyn Future<Output = Self::Output> + Send>>,
|
||||
oneshot::Sender<Self::Output>,
|
||||
) {
|
||||
(self.task, self.sender)
|
||||
}
|
||||
}
|
||||
|
||||
/// This is like `trace_transaction`, just for nonces.
|
||||
pub fn fetch_onchain_nonce(
|
||||
connection: String,
|
||||
wallet: alloy::network::EthereumWallet,
|
||||
address: Address,
|
||||
) -> anyhow::Result<u64> {
|
||||
let sender = TO_TOKIO.lock().unwrap().nonce_sender.clone();
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let task: Task = Box::pin(async move {
|
||||
let provider = ProviderBuilder::new()
|
||||
.wallet(wallet)
|
||||
.connect(&connection)
|
||||
.await?;
|
||||
let onchain = provider.get_transaction_count(address).await?;
|
||||
Ok(onchain)
|
||||
});
|
||||
|
||||
sender
|
||||
.blocking_send(Nonce { task, sender: tx })
|
||||
.expect("not in async context");
|
||||
|
||||
rx.blocking_recv()
|
||||
.unwrap_or_else(|err| anyhow::bail!("nonce fetch failed: {err}"))
|
||||
}
|
||||
@@ -10,6 +10,7 @@ use tokio::spawn;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
use tokio::task::JoinError;
|
||||
|
||||
use crate::nonce::Nonce;
|
||||
use crate::trace::Trace;
|
||||
use crate::transaction::Transaction;
|
||||
|
||||
@@ -33,6 +34,7 @@ pub(crate) trait AsyncNodeInteraction: Send + 'static {
|
||||
pub(crate) struct TokioRuntime {
|
||||
pub(crate) transaction_sender: mpsc::Sender<Transaction>,
|
||||
pub(crate) trace_sender: mpsc::Sender<Trace>,
|
||||
pub(crate) nonce_sender: mpsc::Sender<Nonce>,
|
||||
}
|
||||
|
||||
impl TokioRuntime {
|
||||
@@ -40,11 +42,13 @@ impl TokioRuntime {
|
||||
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);
|
||||
let (nonce_sender, nonce_receiver) = mpsc::channel::<Nonce>(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));
|
||||
let nonce_task = spawn(interaction::<Nonce>(nonce_receiver));
|
||||
|
||||
if let Err(error) = transaction_task.await {
|
||||
log::error!("tokio transaction task failed: {error}");
|
||||
@@ -52,12 +56,16 @@ impl TokioRuntime {
|
||||
if let Err(error) = trace_task.await {
|
||||
log::error!("tokio trace transaction task failed: {error}");
|
||||
}
|
||||
if let Err(error) = nonce_task.await {
|
||||
log::error!("tokio nonce task failed: {error}");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Self {
|
||||
transaction_sender,
|
||||
trace_sender,
|
||||
nonce_sender,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+22
-2
@@ -5,13 +5,17 @@ use std::{
|
||||
io::{BufRead, BufReader, Read, Write},
|
||||
path::PathBuf,
|
||||
process::{Child, Command, Stdio},
|
||||
sync::atomic::{AtomicU32, Ordering},
|
||||
sync::{
|
||||
Mutex,
|
||||
atomic::{AtomicU32, Ordering},
|
||||
},
|
||||
thread,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use alloy::{
|
||||
network::EthereumWallet,
|
||||
primitives::{Address, map::HashMap},
|
||||
providers::{Provider, ProviderBuilder, ext::DebugApi},
|
||||
rpc::types::{
|
||||
TransactionReceipt, TransactionRequest,
|
||||
@@ -20,7 +24,8 @@ use alloy::{
|
||||
};
|
||||
use revive_dt_config::Arguments;
|
||||
use revive_dt_node_interaction::{
|
||||
EthereumNode, trace::trace_transaction, transaction::execute_transaction,
|
||||
EthereumNode, nonce::fetch_onchain_nonce, trace::trace_transaction,
|
||||
transaction::execute_transaction,
|
||||
};
|
||||
|
||||
use crate::Node;
|
||||
@@ -45,6 +50,7 @@ pub struct Instance {
|
||||
network_id: u64,
|
||||
start_timeout: u64,
|
||||
wallet: EthereumWallet,
|
||||
nonces: Mutex<HashMap<Address, u64>>,
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
@@ -198,6 +204,19 @@ impl EthereumNode for Instance {
|
||||
_ => anyhow::bail!("expected a diff mode trace"),
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_add_nonce(&self, address: Address) -> anyhow::Result<u64> {
|
||||
let connection_string = self.connection_string.clone();
|
||||
let wallet = self.wallet.clone();
|
||||
|
||||
let onchain_nonce = fetch_onchain_nonce(connection_string, wallet, address)?;
|
||||
|
||||
let mut nonces = self.nonces.lock().unwrap();
|
||||
let current = nonces.entry(address).or_insert(onchain_nonce);
|
||||
let value = *current;
|
||||
*current += 1;
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for Instance {
|
||||
@@ -216,6 +235,7 @@ impl Node for Instance {
|
||||
network_id: config.network_id,
|
||||
start_timeout: config.geth_start_timeout,
|
||||
wallet: config.wallet(),
|
||||
nonces: Mutex::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,13 +3,17 @@ use std::{
|
||||
io::BufRead,
|
||||
path::PathBuf,
|
||||
process::{Child, Command, Stdio},
|
||||
sync::atomic::{AtomicU32, Ordering},
|
||||
sync::{
|
||||
Mutex,
|
||||
atomic::{AtomicU32, Ordering},
|
||||
},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use alloy::{
|
||||
hex,
|
||||
network::EthereumWallet,
|
||||
primitives::{Address, map::HashMap},
|
||||
providers::{Provider, ProviderBuilder, ext::DebugApi},
|
||||
rpc::types::{
|
||||
TransactionReceipt,
|
||||
@@ -22,7 +26,8 @@ use sp_runtime::AccountId32;
|
||||
|
||||
use revive_dt_config::Arguments;
|
||||
use revive_dt_node_interaction::{
|
||||
EthereumNode, trace::trace_transaction, transaction::execute_transaction,
|
||||
EthereumNode, nonce::fetch_onchain_nonce, trace::trace_transaction,
|
||||
transaction::execute_transaction,
|
||||
};
|
||||
|
||||
use crate::Node;
|
||||
@@ -39,6 +44,7 @@ pub struct KitchensinkNode {
|
||||
base_directory: PathBuf,
|
||||
process_substrate: Option<Child>,
|
||||
process_proxy: Option<Child>,
|
||||
nonces: Mutex<HashMap<Address, u64>>,
|
||||
}
|
||||
|
||||
impl KitchensinkNode {
|
||||
@@ -289,6 +295,19 @@ impl EthereumNode for KitchensinkNode {
|
||||
_ => anyhow::bail!("expected a diff mode trace"),
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_add_nonce(&self, address: Address) -> anyhow::Result<u64> {
|
||||
let url = self.rpc_url.clone();
|
||||
let wallet = self.wallet.clone();
|
||||
|
||||
let onchain_nonce = fetch_onchain_nonce(url, wallet, address)?;
|
||||
|
||||
let mut nonces = self.nonces.lock().unwrap();
|
||||
let current = nonces.entry(address).or_insert(onchain_nonce);
|
||||
let value = *current;
|
||||
*current += 1;
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for KitchensinkNode {
|
||||
@@ -306,6 +325,7 @@ impl Node for KitchensinkNode {
|
||||
base_directory,
|
||||
process_substrate: None,
|
||||
process_proxy: None,
|
||||
nonces: Mutex::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
Submodule polkadot-sdk updated: dd97d10c52...dc3d0e5ab7
Reference in New Issue
Block a user