mirror of
https://github.com/pezkuwichain/revive-differential-tests.git
synced 2026-06-12 10:01:07 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c31fdb236 |
Generated
+16
-19
@@ -260,9 +260,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-json-abi"
|
name = "alloy-json-abi"
|
||||||
version = "1.2.1"
|
version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "15516116086325c157c18261d768a20677f0f699348000ed391d4ad0dcb82530"
|
checksum = "3ccaa79753d7bf15f06399ea76922afbfaf8d18bebed9e8fc452984b4a90dcc9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-primitives",
|
"alloy-primitives",
|
||||||
"alloy-sol-type-parser",
|
"alloy-sol-type-parser",
|
||||||
@@ -325,9 +325,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-primitives"
|
name = "alloy-primitives"
|
||||||
version = "1.2.1"
|
version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6177ed26655d4e84e00b65cb494d4e0b8830e7cae7ef5d63087d445a2600fb55"
|
checksum = "18c35fc4b03ace65001676358ffbbaefe2a2b27ee50fe777c345082c7c888be8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-rlp",
|
"alloy-rlp",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -575,9 +575,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-sol-macro"
|
name = "alloy-sol-macro"
|
||||||
version = "1.2.1"
|
version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a14f21d053aea4c6630687c2f4ad614bed4c81e14737a9b904798b24f30ea849"
|
checksum = "8612e0658964d616344f199ab251a49d48113992d81b92dab93ed855faa66383"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-sol-macro-expander",
|
"alloy-sol-macro-expander",
|
||||||
"alloy-sol-macro-input",
|
"alloy-sol-macro-input",
|
||||||
@@ -589,9 +589,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-sol-macro-expander"
|
name = "alloy-sol-macro-expander"
|
||||||
version = "1.2.1"
|
version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34d99282e7c9ef14eb62727981a985a01869e586d1dec729d3bb33679094c100"
|
checksum = "7a384edac7283bc4c010a355fb648082860c04b826bb7a814c45263c8f304c74"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-json-abi",
|
"alloy-json-abi",
|
||||||
"alloy-sol-macro-input",
|
"alloy-sol-macro-input",
|
||||||
@@ -608,9 +608,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-sol-macro-input"
|
name = "alloy-sol-macro-input"
|
||||||
version = "1.2.1"
|
version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eda029f955b78e493360ee1d7bd11e1ab9f2a220a5715449babc79d6d0a01105"
|
checksum = "0dd588c2d516da7deb421b8c166dc60b7ae31bca5beea29ab6621fcfa53d6ca5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-json-abi",
|
"alloy-json-abi",
|
||||||
"const-hex",
|
"const-hex",
|
||||||
@@ -626,9 +626,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-sol-type-parser"
|
name = "alloy-sol-type-parser"
|
||||||
version = "1.2.1"
|
version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "10db1bd7baa35bc8d4a1b07efbf734e73e5ba09f2580fb8cee3483a36087ceb2"
|
checksum = "e86ddeb70792c7ceaad23e57d52250107ebbb86733e52f4a25d8dc1abc931837"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"winnow",
|
"winnow",
|
||||||
@@ -636,9 +636,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-sol-types"
|
name = "alloy-sol-types"
|
||||||
version = "1.2.1"
|
version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "58377025a47d8b8426b3e4846a251f2c1991033b27f517aade368146f6ab1dfe"
|
checksum = "584cb97bfc5746cb9dcc4def77da11694b5d6d7339be91b7480a6a68dc129387"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-json-abi",
|
"alloy-json-abi",
|
||||||
"alloy-primitives",
|
"alloy-primitives",
|
||||||
@@ -4034,7 +4034,6 @@ dependencies = [
|
|||||||
"revive-dt-node-interaction",
|
"revive-dt-node-interaction",
|
||||||
"revive-dt-report",
|
"revive-dt-report",
|
||||||
"revive-solc-json-interface",
|
"revive-solc-json-interface",
|
||||||
"serde_json",
|
|
||||||
"temp-dir",
|
"temp-dir",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4043,8 +4042,6 @@ name = "revive-dt-format"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy",
|
"alloy",
|
||||||
"alloy-primitives",
|
|
||||||
"alloy-sol-types",
|
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"log",
|
"log",
|
||||||
"semver 1.0.26",
|
"semver 1.0.26",
|
||||||
@@ -5146,9 +5143,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn-solidity"
|
name = "syn-solidity"
|
||||||
version = "1.2.1"
|
version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b9ac494e7266fcdd2ad80bf4375d55d27a117ea5c866c26d0e97fe5b3caeeb75"
|
checksum = "1b5d879005cc1b5ba4e18665be9e9501d9da3a9b95f625497c4cb7ee082b532e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"paste",
|
"paste",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ revive-dt-node-pool = { version = "0.1.0", path = "crates/node-pool" }
|
|||||||
revive-dt-report = { version = "0.1.0", path = "crates/report" }
|
revive-dt-report = { version = "0.1.0", path = "crates/report" }
|
||||||
revive-dt-solc-binaries = { version = "0.1.0", path = "crates/solc-binaries" }
|
revive-dt-solc-binaries = { version = "0.1.0", path = "crates/solc-binaries" }
|
||||||
|
|
||||||
alloy-primitives = "1.2.1"
|
|
||||||
alloy-sol-types = "1.2.1"
|
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
env_logger = "0.11.8"
|
env_logger = "0.11.8"
|
||||||
|
|||||||
@@ -27,5 +27,4 @@ log = { workspace = true }
|
|||||||
env_logger = { workspace = true }
|
env_logger = { workspace = true }
|
||||||
rayon = { workspace = true }
|
rayon = { workspace = true }
|
||||||
revive-solc-json-interface = { workspace = true }
|
revive-solc-json-interface = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
|
||||||
temp-dir = { workspace = true }
|
temp-dir = { workspace = true }
|
||||||
|
|||||||
+41
-255
@@ -1,14 +1,11 @@
|
|||||||
//! The test driver handles the compilation and execution of the test cases.
|
//! The test driver handles the compilation and execution of the test cases.
|
||||||
|
|
||||||
use alloy::json_abi::JsonAbi;
|
|
||||||
use alloy::primitives::Bytes;
|
|
||||||
use alloy::rpc::types::trace::geth::GethTrace;
|
|
||||||
use alloy::rpc::types::{TransactionInput, TransactionReceipt};
|
|
||||||
use alloy::{
|
use alloy::{
|
||||||
primitives::{Address, TxKind, map::HashMap},
|
network::TransactionBuilder,
|
||||||
|
primitives::{Address, bytes::Bytes, map::HashMap},
|
||||||
rpc::types::{
|
rpc::types::{
|
||||||
TransactionRequest,
|
TransactionRequest,
|
||||||
trace::geth::{AccountState, DiffMode},
|
trace::geth::{AccountState, DiffMode, GethTrace},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use revive_dt_compiler::{Compiler, CompilerInput, SolidityCompiler};
|
use revive_dt_compiler::{Compiler, CompilerInput, SolidityCompiler};
|
||||||
@@ -17,8 +14,6 @@ use revive_dt_format::{input::Input, metadata::Metadata, mode::SolcMode};
|
|||||||
use revive_dt_node_interaction::EthereumNode;
|
use revive_dt_node_interaction::EthereumNode;
|
||||||
use revive_dt_report::reporter::{CompilationTask, Report, Span};
|
use revive_dt_report::reporter::{CompilationTask, Report, Span};
|
||||||
use revive_solc_json_interface::SolcStandardJsonOutput;
|
use revive_solc_json_interface::SolcStandardJsonOutput;
|
||||||
use serde_json::Value;
|
|
||||||
use std::collections::HashMap as StdHashMap;
|
|
||||||
|
|
||||||
use crate::Platform;
|
use crate::Platform;
|
||||||
|
|
||||||
@@ -31,8 +26,7 @@ pub struct State<'a, T: Platform> {
|
|||||||
config: &'a Arguments,
|
config: &'a Arguments,
|
||||||
span: Span,
|
span: Span,
|
||||||
contracts: Contracts<T>,
|
contracts: Contracts<T>,
|
||||||
deployed_contracts: StdHashMap<String, Address>,
|
deployed_contracts: HashMap<String, Address>,
|
||||||
deployed_abis: StdHashMap<String, JsonAbi>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> State<'a, T>
|
impl<'a, T> State<'a, T>
|
||||||
@@ -45,7 +39,6 @@ where
|
|||||||
span,
|
span,
|
||||||
contracts: Default::default(),
|
contracts: Default::default(),
|
||||||
deployed_contracts: Default::default(),
|
deployed_contracts: Default::default(),
|
||||||
deployed_abis: Default::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,24 +84,10 @@ where
|
|||||||
task.json_output = Some(output.output.clone());
|
task.json_output = Some(output.output.clone());
|
||||||
task.error = output.error;
|
task.error = output.error;
|
||||||
self.contracts.insert(output.input, output.output);
|
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);
|
Report::compilation(span, T::config_id(), task);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
log::error!("Failed to compile contract: {:?}", error.to_string());
|
|
||||||
task.error = Some(error.to_string());
|
task.error = Some(error.to_string());
|
||||||
Err(error)
|
Err(error)
|
||||||
}
|
}
|
||||||
@@ -119,90 +98,34 @@ where
|
|||||||
&mut self,
|
&mut self,
|
||||||
input: &Input,
|
input: &Input,
|
||||||
node: &T::Blockchain,
|
node: &T::Blockchain,
|
||||||
) -> anyhow::Result<(TransactionReceipt, GethTrace, DiffMode)> {
|
) -> anyhow::Result<(GethTrace, DiffMode)> {
|
||||||
log::trace!("Calling execute_input for input: {input:?}");
|
let receipt = node.execute_transaction(input.legacy_transaction(
|
||||||
|
|
||||||
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,
|
self.config.network_id,
|
||||||
nonce,
|
0,
|
||||||
&self.deployed_contracts,
|
&self.deployed_contracts,
|
||||||
&self.deployed_abis,
|
)?)?;
|
||||||
) {
|
|
||||||
Ok(tx) => {
|
|
||||||
log::debug!("Legacy transaction data: {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,
|
|
||||||
);
|
|
||||||
|
|
||||||
|
log::trace!("Transaction receipt: {:?}", receipt);
|
||||||
let trace = node.trace_transaction(receipt.clone())?;
|
let trace = node.trace_transaction(receipt.clone())?;
|
||||||
log::trace!(
|
log::trace!("Trace result: {:?}", trace);
|
||||||
"Trace result for contract: {} - {:?}",
|
|
||||||
&input.instance,
|
|
||||||
trace
|
|
||||||
);
|
|
||||||
|
|
||||||
let diff = node.state_diff(receipt.clone())?;
|
let diff = node.state_diff(receipt)?;
|
||||||
|
|
||||||
Ok((receipt, trace, diff))
|
Ok((trace, diff))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deploy_contracts(&mut self, input: &Input, node: &T::Blockchain) -> anyhow::Result<()> {
|
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() {
|
for output in self.contracts.values() {
|
||||||
let Some(contract_map) = &output.contracts else {
|
let Some(contract_map) = &output.contracts else {
|
||||||
log::debug!(
|
log::debug!("No contracts in output — skipping deployment for this input.");
|
||||||
"No contracts in output — skipping deployment for this input {}",
|
|
||||||
&input.instance
|
|
||||||
);
|
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
for contracts in contract_map.values() {
|
for contracts in contract_map.values() {
|
||||||
for (contract_name, contract) in contracts {
|
for (contract_name, contract) in contracts {
|
||||||
log::debug!(
|
if contract_name != &input.instance {
|
||||||
"Contract name is: {:?} and the input name is: {:?}",
|
continue;
|
||||||
&contract_name,
|
}
|
||||||
&input.instance
|
|
||||||
);
|
|
||||||
|
|
||||||
let bytecode = contract
|
let bytecode = contract
|
||||||
.evm
|
.evm
|
||||||
@@ -211,126 +134,33 @@ where
|
|||||||
.map(|b| b.object.clone());
|
.map(|b| b.object.clone());
|
||||||
|
|
||||||
let Some(code) = bytecode else {
|
let Some(code) = bytecode else {
|
||||||
log::error!("no bytecode for contract {contract_name}");
|
anyhow::bail!("no bytecode for contract `{}`", contract_name);
|
||||||
continue;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let nonce = node.fetch_add_nonce(input.caller)?;
|
let tx = TransactionRequest::default()
|
||||||
|
.with_from(input.caller)
|
||||||
log::debug!(
|
.with_to(Address::ZERO)
|
||||||
"Calculated nonce {}, for contract {}, having address {} on node: {}",
|
.with_input(Bytes::from(code.clone()))
|
||||||
&nonce,
|
.with_gas_price(20_000_000_000)
|
||||||
&input.instance,
|
.with_gas_limit(20_000_000_000)
|
||||||
&input.caller,
|
.with_chain_id(self.config.network_id)
|
||||||
std::any::type_name::<T>()
|
.with_nonce(0);
|
||||||
);
|
|
||||||
|
|
||||||
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 receipt = node.execute_transaction(tx)?;
|
||||||
let Some(address) = receipt.contract_address else {
|
let Some(address) = receipt.contract_address else {
|
||||||
log::error!(
|
anyhow::bail!(
|
||||||
"contract {contract_name} deployment did not return an address"
|
"contract `{}` deployment did not return an address",
|
||||||
|
contract_name
|
||||||
);
|
);
|
||||||
continue;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.deployed_contracts
|
self.deployed_contracts
|
||||||
.insert(contract_name.clone(), address);
|
.insert(contract_name.clone(), address);
|
||||||
log::trace!(
|
log::info!("deployed contract `{}` at {:?}", contract_name, address);
|
||||||
"deployed contract `{}` at {:?}, on node {:?}",
|
|
||||||
contract_name,
|
|
||||||
address,
|
|
||||||
std::any::type_name::<T>()
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(Value::String(metadata_json_str)) = &contract.metadata {
|
|
||||||
log::trace!(
|
|
||||||
"metadata found for contract {contract_name}, {metadata_json_str}"
|
|
||||||
);
|
|
||||||
|
|
||||||
match serde_json::from_str::<serde_json::Value>(metadata_json_str) {
|
|
||||||
Ok(metadata_json) => {
|
|
||||||
if let Some(abi_value) =
|
|
||||||
metadata_json.get("output").and_then(|o| o.get("abi"))
|
|
||||||
{
|
|
||||||
match serde_json::from_value::<JsonAbi>(abi_value.clone()) {
|
|
||||||
Ok(parsed_abi) => {
|
|
||||||
log::trace!(
|
|
||||||
"ABI found in metadata for contract {}",
|
|
||||||
&contract_name
|
|
||||||
);
|
|
||||||
self.deployed_abis
|
|
||||||
.insert(contract_name.clone(), parsed_abi);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
anyhow::bail!(
|
|
||||||
"Failed to parse ABI from metadata for contract {}: {}",
|
|
||||||
contract_name,
|
|
||||||
err
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
anyhow::bail!(
|
|
||||||
"No ABI found in metadata for contract {}",
|
|
||||||
contract_name
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
anyhow::bail!(
|
|
||||||
"Failed to parse metadata JSON string for contract {}: {}",
|
|
||||||
contract_name,
|
|
||||||
err
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
anyhow::bail!("No metadata found for contract {}", contract_name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log::debug!("Available contracts: {:?}", self.deployed_contracts.keys());
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -362,28 +192,28 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn trace_diff_mode(label: &str, diff: &DiffMode) {
|
pub fn trace_diff_mode(label: &str, diff: &DiffMode) {
|
||||||
log::trace!("{label} - PRE STATE:");
|
log::trace!("{} - PRE STATE:", label);
|
||||||
for (addr, state) in &diff.pre {
|
for (addr, state) in &diff.pre {
|
||||||
Self::trace_account_state(" [pre]", addr, state);
|
Self::trace_account_state(" [pre]", addr, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
log::trace!("{label} - POST STATE:");
|
log::trace!("{} - POST STATE:", label);
|
||||||
for (addr, state) in &diff.post {
|
for (addr, state) in &diff.post {
|
||||||
Self::trace_account_state(" [post]", addr, state);
|
Self::trace_account_state(" [post]", addr, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trace_account_state(prefix: &str, addr: &Address, state: &AccountState) {
|
fn trace_account_state(prefix: &str, addr: &Address, state: &AccountState) {
|
||||||
log::trace!("{prefix} 0x{addr:x}");
|
log::trace!("{} 0x{:x}", prefix, addr);
|
||||||
|
|
||||||
if let Some(balance) = &state.balance {
|
if let Some(balance) = &state.balance {
|
||||||
log::trace!("{prefix} balance: {balance}");
|
log::trace!("{} balance: {}", prefix, balance);
|
||||||
}
|
}
|
||||||
if let Some(nonce) = &state.nonce {
|
if let Some(nonce) = &state.nonce {
|
||||||
log::trace!("{prefix} nonce: {nonce}");
|
log::trace!("{} nonce: {}", prefix, nonce);
|
||||||
}
|
}
|
||||||
if let Some(code) = &state.code {
|
if let Some(code) = &state.code {
|
||||||
log::trace!("{prefix} code: {code}");
|
log::trace!("{} code: {}", prefix, code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,42 +227,12 @@ where
|
|||||||
|
|
||||||
for case in &self.metadata.cases {
|
for case in &self.metadata.cases {
|
||||||
for input in &case.inputs {
|
for input in &case.inputs {
|
||||||
log::debug!("Starting deploying contract {}", &input.instance);
|
leader_state.deploy_contracts(input, self.leader_node)?;
|
||||||
if let Err(err) = leader_state.deploy_contracts(input, self.leader_node) {
|
follower_state.deploy_contracts(input, self.follower_node)?;
|
||||||
log::error!("Leader deployment failed for {}: {err}", input.instance);
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
log::debug!("Leader deployment succeeded for {}", &input.instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(err) = follower_state.deploy_contracts(input, self.follower_node) {
|
let (_, leader_diff) = leader_state.execute_input(input, self.leader_node)?;
|
||||||
log::error!("Follower deployment failed for {}: {err}", input.instance);
|
let (_, follower_diff) =
|
||||||
continue;
|
follower_state.execute_input(input, self.follower_node)?;
|
||||||
} else {
|
|
||||||
log::debug!("Follower deployment succeeded for {}", &input.instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
log::debug!("Starting executing contract {}", &input.instance);
|
|
||||||
|
|
||||||
let (leader_receipt, _, leader_diff) = match leader_state
|
|
||||||
.execute_input(input, self.leader_node)
|
|
||||||
{
|
|
||||||
Ok(result) => result,
|
|
||||||
Err(err) => {
|
|
||||||
log::error!("Leader execution failed for {}: {err}", input.instance);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let (follower_receipt, _, follower_diff) = match follower_state
|
|
||||||
.execute_input(input, self.follower_node)
|
|
||||||
{
|
|
||||||
Ok(result) => result,
|
|
||||||
Err(err) => {
|
|
||||||
log::error!("Follower execution failed for {}: {err}", input.instance);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if leader_diff == follower_diff {
|
if leader_diff == follower_diff {
|
||||||
log::debug!("State diffs match between leader and follower.");
|
log::debug!("State diffs match between leader and follower.");
|
||||||
@@ -441,20 +241,6 @@ where
|
|||||||
Self::trace_diff_mode("Leader", &leader_diff);
|
Self::trace_diff_mode("Leader", &leader_diff);
|
||||||
Self::trace_diff_mode("Follower", &follower_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()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ rust-version.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
alloy = { workspace = true }
|
alloy = { workspace = true }
|
||||||
alloy-primitives = { workspace = true }
|
|
||||||
alloy-sol-types = { workspace = true }
|
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
semver = { workspace = true }
|
semver = { workspace = true }
|
||||||
|
|||||||
+12
-334
@@ -1,13 +1,9 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use alloy::{
|
use alloy::{
|
||||||
hex,
|
json_abi::Function, network::TransactionBuilder, primitives::Address,
|
||||||
json_abi::{Function, JsonAbi},
|
rpc::types::TransactionRequest,
|
||||||
primitives::{Address, Bytes, TxKind},
|
|
||||||
rpc::types::{TransactionInput, TransactionRequest},
|
|
||||||
};
|
};
|
||||||
use alloy_primitives::U256;
|
|
||||||
use alloy_sol_types::SolValue;
|
|
||||||
use semver::VersionReq;
|
use semver::VersionReq;
|
||||||
use serde::{Deserialize, de::Deserializer};
|
use serde::{Deserialize, de::Deserializer};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
@@ -47,15 +43,7 @@ pub struct ExpectedOutput {
|
|||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum Calldata {
|
pub enum Calldata {
|
||||||
Single(String),
|
Single(String),
|
||||||
Compound(Vec<CalldataArg>),
|
Compound(Vec<String>),
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
pub enum CalldataArg {
|
|
||||||
Literal(String),
|
|
||||||
/// For example: `Contract.address`
|
|
||||||
AddressRef(String),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specify how the contract is called.
|
/// Specify how the contract is called.
|
||||||
@@ -113,137 +101,25 @@ impl Input {
|
|||||||
.ok_or_else(|| anyhow::anyhow!("instance {instance} not deployed"))
|
.ok_or_else(|| anyhow::anyhow!("instance {instance} not deployed"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encoded_input(
|
|
||||||
&self,
|
|
||||||
deployed_abis: &HashMap<String, JsonAbi>,
|
|
||||||
deployed_contracts: &HashMap<String, Address>,
|
|
||||||
) -> anyhow::Result<Bytes> {
|
|
||||||
let Method::Function(selector) = self.method else {
|
|
||||||
return Ok(Bytes::default()); // fallback or deployer — no input
|
|
||||||
};
|
|
||||||
|
|
||||||
let abi = deployed_abis
|
|
||||||
.get(&self.instance)
|
|
||||||
.ok_or_else(|| anyhow::anyhow!("ABI for instance '{}' not found", &self.instance))?;
|
|
||||||
|
|
||||||
log::trace!("ABI found for instance: {}", &self.instance);
|
|
||||||
|
|
||||||
// Find function by selector
|
|
||||||
let function = abi
|
|
||||||
.functions()
|
|
||||||
.find(|f| f.selector().0 == selector)
|
|
||||||
.ok_or_else(|| {
|
|
||||||
anyhow::anyhow!(
|
|
||||||
"Function with selector {:?} not found in ABI for the instance {:?}",
|
|
||||||
selector,
|
|
||||||
&self.instance
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
log::trace!("Functions found for instance: {}", &self.instance);
|
|
||||||
|
|
||||||
let calldata_args = match &self.calldata {
|
|
||||||
Some(Calldata::Compound(args)) => args,
|
|
||||||
_ => anyhow::bail!("Expected compound calldata for function call"),
|
|
||||||
};
|
|
||||||
|
|
||||||
if calldata_args.len() != function.inputs.len() {
|
|
||||||
anyhow::bail!(
|
|
||||||
"Function expects {} args, but got {}",
|
|
||||||
function.inputs.len(),
|
|
||||||
calldata_args.len()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
log::trace!(
|
|
||||||
"Starting encoding ABI's parameters for instance: {}",
|
|
||||||
&self.instance
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut encoded = selector.to_vec();
|
|
||||||
|
|
||||||
for (i, param) in function.inputs.iter().enumerate() {
|
|
||||||
let arg = calldata_args.get(i).unwrap();
|
|
||||||
let encoded_arg = match arg {
|
|
||||||
CalldataArg::Literal(value) => match param.ty.as_str() {
|
|
||||||
"uint256" | "uint" => {
|
|
||||||
let val: U256 = value.parse()?;
|
|
||||||
val.abi_encode()
|
|
||||||
}
|
|
||||||
"uint24" => {
|
|
||||||
let val: u32 = value.parse()?;
|
|
||||||
(val & 0xFFFFFF).abi_encode()
|
|
||||||
}
|
|
||||||
"bool" => {
|
|
||||||
let val: bool = value.parse()?;
|
|
||||||
val.abi_encode()
|
|
||||||
}
|
|
||||||
"address" => {
|
|
||||||
let addr: Address = value.parse()?;
|
|
||||||
addr.abi_encode()
|
|
||||||
}
|
|
||||||
"string" => value.abi_encode(),
|
|
||||||
"bytes32" => {
|
|
||||||
let val = hex::decode(value.trim_start_matches("0x"))?;
|
|
||||||
let mut fixed = [0u8; 32];
|
|
||||||
fixed[..val.len()].copy_from_slice(&val);
|
|
||||||
fixed.abi_encode()
|
|
||||||
}
|
|
||||||
"uint256[]" | "uint[]" => {
|
|
||||||
let nums: Vec<u64> = serde_json::from_str(value)?;
|
|
||||||
nums.abi_encode()
|
|
||||||
}
|
|
||||||
"bytes" => {
|
|
||||||
let val = hex::decode(value.trim_start_matches("0x"))?;
|
|
||||||
val.abi_encode()
|
|
||||||
}
|
|
||||||
_ => anyhow::bail!("Unsupported type: {}", param.ty),
|
|
||||||
},
|
|
||||||
CalldataArg::AddressRef(name) => {
|
|
||||||
let contract_name = name.trim_end_matches(".address");
|
|
||||||
let addr = deployed_contracts
|
|
||||||
.get(contract_name)
|
|
||||||
.copied()
|
|
||||||
.ok_or_else(|| {
|
|
||||||
anyhow::anyhow!("Address for '{}' not found", contract_name)
|
|
||||||
})?;
|
|
||||||
addr.abi_encode()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
encoded.extend(encoded_arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Bytes::from(encoded))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse this input into a legacy transaction.
|
/// Parse this input into a legacy transaction.
|
||||||
pub fn legacy_transaction(
|
pub fn legacy_transaction(
|
||||||
&self,
|
&self,
|
||||||
chain_id: u64,
|
chain_id: u64,
|
||||||
nonce: u64,
|
nonce: u64,
|
||||||
deployed_contracts: &HashMap<String, Address>,
|
deployed_contracts: &HashMap<String, Address>,
|
||||||
deployed_abis: &HashMap<String, JsonAbi>,
|
|
||||||
) -> anyhow::Result<TransactionRequest> {
|
) -> anyhow::Result<TransactionRequest> {
|
||||||
let to = match self.method {
|
let to = match self.method {
|
||||||
Method::Deployer => Some(TxKind::Create),
|
Method::Deployer => Address::ZERO,
|
||||||
_ => Some(TxKind::Call(
|
_ => self.instance_to_address(&self.instance, deployed_contracts)?,
|
||||||
self.instance_to_address(&self.instance, deployed_contracts)?,
|
|
||||||
)),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let input_data = self.encoded_input(deployed_abis, deployed_contracts)?;
|
Ok(TransactionRequest::default()
|
||||||
|
.with_from(self.caller)
|
||||||
Ok(TransactionRequest {
|
.with_to(to)
|
||||||
from: Some(self.caller),
|
.with_nonce(nonce)
|
||||||
to,
|
.with_chain_id(chain_id)
|
||||||
nonce: Some(nonce),
|
.with_gas_price(20_000_000_000)
|
||||||
chain_id: Some(chain_id),
|
.with_gas_limit(20_000_000_000))
|
||||||
gas_price: Some(5_000_000),
|
|
||||||
gas: Some(5_000_000),
|
|
||||||
input: TransactionInput::new(input_data),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,201 +130,3 @@ fn default_instance() -> String {
|
|||||||
fn default_caller() -> Address {
|
fn default_caller() -> Address {
|
||||||
"90F8bf6A479f320ead074411a4B0e7944Ea8c9C1".parse().unwrap()
|
"90F8bf6A479f320ead074411a4B0e7944Ea8c9C1".parse().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use alloy::json_abi::JsonAbi;
|
|
||||||
use alloy_primitives::{address, keccak256};
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_encoded_input_uint256() {
|
|
||||||
let raw_metadata = r#"
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"inputs": [{"name": "value", "type": "uint256"}],
|
|
||||||
"name": "store",
|
|
||||||
"outputs": [],
|
|
||||||
"stateMutability": "nonpayable",
|
|
||||||
"type": "function"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
"#;
|
|
||||||
|
|
||||||
let parsed_abi: JsonAbi = serde_json::from_str(raw_metadata).unwrap();
|
|
||||||
let selector = keccak256("store(uint256)".as_bytes())[0..4]
|
|
||||||
.try_into()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let input = Input {
|
|
||||||
instance: "Contract".to_string(),
|
|
||||||
method: Method::Function(selector),
|
|
||||||
calldata: Some(Calldata::Compound(vec![CalldataArg::Literal(
|
|
||||||
"42".to_string(),
|
|
||||||
)])),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut deployed_abis = HashMap::new();
|
|
||||||
deployed_abis.insert("Contract".to_string(), parsed_abi);
|
|
||||||
let deployed_contracts = HashMap::new();
|
|
||||||
|
|
||||||
let encoded = input
|
|
||||||
.encoded_input(&deployed_abis, &deployed_contracts)
|
|
||||||
.unwrap();
|
|
||||||
assert!(encoded.0.starts_with(&selector));
|
|
||||||
|
|
||||||
type T = (u64,);
|
|
||||||
let decoded: T = T::abi_decode(&encoded.0[4..]).unwrap();
|
|
||||||
assert_eq!(decoded.0, 42);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_encoded_input_bool() {
|
|
||||||
let raw_abi = r#"[
|
|
||||||
{
|
|
||||||
"inputs": [{"name": "flag", "type": "bool"}],
|
|
||||||
"name": "toggle",
|
|
||||||
"outputs": [],
|
|
||||||
"stateMutability": "nonpayable",
|
|
||||||
"type": "function"
|
|
||||||
}
|
|
||||||
]"#;
|
|
||||||
|
|
||||||
let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap();
|
|
||||||
let selector = keccak256("toggle(bool)".as_bytes())[0..4]
|
|
||||||
.try_into()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let input = Input {
|
|
||||||
instance: "Contract".to_string(),
|
|
||||||
method: Method::Function(selector),
|
|
||||||
calldata: Some(Calldata::Compound(vec![CalldataArg::Literal(
|
|
||||||
"true".to_string(),
|
|
||||||
)])),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut abis = HashMap::new();
|
|
||||||
abis.insert("Contract".to_string(), parsed_abi);
|
|
||||||
let contracts = HashMap::new();
|
|
||||||
|
|
||||||
let encoded = input.encoded_input(&abis, &contracts).unwrap();
|
|
||||||
assert!(encoded.0.starts_with(&selector));
|
|
||||||
|
|
||||||
type T = (bool,);
|
|
||||||
let decoded: T = T::abi_decode(&encoded.0[4..]).unwrap();
|
|
||||||
assert_eq!(decoded.0, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_encoded_input_string() {
|
|
||||||
let raw_abi = r#"[
|
|
||||||
{
|
|
||||||
"inputs": [{"name": "msg", "type": "string"}],
|
|
||||||
"name": "echo",
|
|
||||||
"outputs": [],
|
|
||||||
"stateMutability": "nonpayable",
|
|
||||||
"type": "function"
|
|
||||||
}
|
|
||||||
]"#;
|
|
||||||
|
|
||||||
let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap();
|
|
||||||
let selector = keccak256("echo(string)".as_bytes())[0..4]
|
|
||||||
.try_into()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let input = Input {
|
|
||||||
instance: "Contract".to_string(),
|
|
||||||
method: Method::Function(selector),
|
|
||||||
calldata: Some(Calldata::Compound(vec![CalldataArg::Literal(
|
|
||||||
"hello".to_string(),
|
|
||||||
)])),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut abis = HashMap::new();
|
|
||||||
abis.insert("Contract".to_string(), parsed_abi);
|
|
||||||
let contracts = HashMap::new();
|
|
||||||
|
|
||||||
let encoded = input.encoded_input(&abis, &contracts).unwrap();
|
|
||||||
assert!(encoded.0.starts_with(&selector));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_encoded_input_uint256_array() {
|
|
||||||
let raw_abi = r#"[
|
|
||||||
{
|
|
||||||
"inputs": [{"name": "arr", "type": "uint256[]"}],
|
|
||||||
"name": "sum",
|
|
||||||
"outputs": [],
|
|
||||||
"stateMutability": "nonpayable",
|
|
||||||
"type": "function"
|
|
||||||
}
|
|
||||||
]"#;
|
|
||||||
|
|
||||||
let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap();
|
|
||||||
let selector = keccak256("sum(uint256[])".as_bytes())[0..4]
|
|
||||||
.try_into()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let input = Input {
|
|
||||||
instance: "Contract".to_string(),
|
|
||||||
method: Method::Function(selector),
|
|
||||||
calldata: Some(Calldata::Compound(vec![CalldataArg::Literal(
|
|
||||||
"[1,2,3]".to_string(),
|
|
||||||
)])),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut abis = HashMap::new();
|
|
||||||
abis.insert("Contract".to_string(), parsed_abi);
|
|
||||||
let contracts = HashMap::new();
|
|
||||||
|
|
||||||
let encoded = input.encoded_input(&abis, &contracts).unwrap();
|
|
||||||
assert!(encoded.0.starts_with(&selector));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_encoded_input_address() {
|
|
||||||
let raw_abi = r#"[
|
|
||||||
{
|
|
||||||
"inputs": [{"name": "recipient", "type": "address"}],
|
|
||||||
"name": "send",
|
|
||||||
"outputs": [],
|
|
||||||
"stateMutability": "nonpayable",
|
|
||||||
"type": "function"
|
|
||||||
}
|
|
||||||
]"#;
|
|
||||||
|
|
||||||
let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap();
|
|
||||||
let selector = keccak256("send(address)".as_bytes())[0..4]
|
|
||||||
.try_into()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let input = Input {
|
|
||||||
instance: "Contract".to_string(),
|
|
||||||
method: Method::Function(selector),
|
|
||||||
calldata: Some(Calldata::Compound(vec![CalldataArg::Literal(
|
|
||||||
"0x1000000000000000000000000000000000000001".to_string(),
|
|
||||||
)])),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut abis = HashMap::new();
|
|
||||||
abis.insert("Contract".to_string(), parsed_abi);
|
|
||||||
let contracts = HashMap::new();
|
|
||||||
|
|
||||||
let encoded = input.encoded_input(&abis, &contracts).unwrap();
|
|
||||||
assert!(encoded.0.starts_with(&selector));
|
|
||||||
|
|
||||||
type T = (alloy_primitives::Address,);
|
|
||||||
let decoded: T = T::abi_decode(&encoded.0[4..]).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
decoded.0,
|
|
||||||
address!("0x1000000000000000000000000000000000000001")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
//! This crate implements all node interactions.
|
//! This crate implements all node interactions.
|
||||||
|
|
||||||
use alloy::primitives::Address;
|
|
||||||
use alloy::rpc::types::trace::geth::{DiffMode, GethTrace};
|
use alloy::rpc::types::trace::geth::{DiffMode, GethTrace};
|
||||||
use alloy::rpc::types::{TransactionReceipt, TransactionRequest};
|
use alloy::rpc::types::{TransactionReceipt, TransactionRequest};
|
||||||
use tokio_runtime::TO_TOKIO;
|
use tokio_runtime::TO_TOKIO;
|
||||||
|
|
||||||
pub mod nonce;
|
|
||||||
mod tokio_runtime;
|
mod tokio_runtime;
|
||||||
pub mod trace;
|
pub mod trace;
|
||||||
pub mod transaction;
|
pub mod transaction;
|
||||||
@@ -23,7 +21,4 @@ pub trait EthereumNode {
|
|||||||
|
|
||||||
/// Returns the state diff of the transaction hash in the [TransactionReceipt].
|
/// Returns the state diff of the transaction hash in the [TransactionReceipt].
|
||||||
fn state_diff(&self, transaction: TransactionReceipt) -> anyhow::Result<DiffMode>;
|
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>;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
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,7 +10,6 @@ use tokio::spawn;
|
|||||||
use tokio::sync::{mpsc, oneshot};
|
use tokio::sync::{mpsc, oneshot};
|
||||||
use tokio::task::JoinError;
|
use tokio::task::JoinError;
|
||||||
|
|
||||||
use crate::nonce::Nonce;
|
|
||||||
use crate::trace::Trace;
|
use crate::trace::Trace;
|
||||||
use crate::transaction::Transaction;
|
use crate::transaction::Transaction;
|
||||||
|
|
||||||
@@ -34,7 +33,6 @@ pub(crate) trait AsyncNodeInteraction: Send + 'static {
|
|||||||
pub(crate) struct TokioRuntime {
|
pub(crate) struct TokioRuntime {
|
||||||
pub(crate) transaction_sender: mpsc::Sender<Transaction>,
|
pub(crate) transaction_sender: mpsc::Sender<Transaction>,
|
||||||
pub(crate) trace_sender: mpsc::Sender<Trace>,
|
pub(crate) trace_sender: mpsc::Sender<Trace>,
|
||||||
pub(crate) nonce_sender: mpsc::Sender<Nonce>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokioRuntime {
|
impl TokioRuntime {
|
||||||
@@ -42,13 +40,11 @@ impl TokioRuntime {
|
|||||||
let rt = Runtime::new().expect("should be able to create the tokio runtime");
|
let rt = Runtime::new().expect("should be able to create the tokio runtime");
|
||||||
let (transaction_sender, transaction_receiver) = mpsc::channel::<Transaction>(1024);
|
let (transaction_sender, transaction_receiver) = mpsc::channel::<Transaction>(1024);
|
||||||
let (trace_sender, trace_receiver) = mpsc::channel::<Trace>(1024);
|
let (trace_sender, trace_receiver) = mpsc::channel::<Trace>(1024);
|
||||||
let (nonce_sender, nonce_receiver) = mpsc::channel::<Nonce>(1024);
|
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
rt.block_on(async move {
|
rt.block_on(async move {
|
||||||
let transaction_task = spawn(interaction::<Transaction>(transaction_receiver));
|
let transaction_task = spawn(interaction::<Transaction>(transaction_receiver));
|
||||||
let trace_task = spawn(interaction::<Trace>(trace_receiver));
|
let trace_task = spawn(interaction::<Trace>(trace_receiver));
|
||||||
let nonce_task = spawn(interaction::<Nonce>(nonce_receiver));
|
|
||||||
|
|
||||||
if let Err(error) = transaction_task.await {
|
if let Err(error) = transaction_task.await {
|
||||||
log::error!("tokio transaction task failed: {error}");
|
log::error!("tokio transaction task failed: {error}");
|
||||||
@@ -56,16 +52,12 @@ impl TokioRuntime {
|
|||||||
if let Err(error) = trace_task.await {
|
if let Err(error) = trace_task.await {
|
||||||
log::error!("tokio trace transaction task failed: {error}");
|
log::error!("tokio trace transaction task failed: {error}");
|
||||||
}
|
}
|
||||||
if let Err(error) = nonce_task.await {
|
|
||||||
log::error!("tokio nonce task failed: {error}");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
transaction_sender,
|
transaction_sender,
|
||||||
trace_sender,
|
trace_sender,
|
||||||
nonce_sender,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-25
@@ -1,22 +1,17 @@
|
|||||||
//! The go-ethereum node implementation.
|
//! The go-ethereum node implementation.
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
|
||||||
fs::{File, create_dir_all, remove_dir_all},
|
fs::{File, create_dir_all, remove_dir_all},
|
||||||
io::{BufRead, BufReader, Read, Write},
|
io::{BufRead, BufReader, Read, Write},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
process::{Child, Command, Stdio},
|
process::{Child, Command, Stdio},
|
||||||
sync::{
|
sync::atomic::{AtomicU32, Ordering},
|
||||||
Mutex,
|
|
||||||
atomic::{AtomicU32, Ordering},
|
|
||||||
},
|
|
||||||
thread,
|
thread,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use alloy::{
|
use alloy::{
|
||||||
network::EthereumWallet,
|
network::EthereumWallet,
|
||||||
primitives::Address,
|
|
||||||
providers::{Provider, ProviderBuilder, ext::DebugApi},
|
providers::{Provider, ProviderBuilder, ext::DebugApi},
|
||||||
rpc::types::{
|
rpc::types::{
|
||||||
TransactionReceipt, TransactionRequest,
|
TransactionReceipt, TransactionRequest,
|
||||||
@@ -25,8 +20,7 @@ use alloy::{
|
|||||||
};
|
};
|
||||||
use revive_dt_config::Arguments;
|
use revive_dt_config::Arguments;
|
||||||
use revive_dt_node_interaction::{
|
use revive_dt_node_interaction::{
|
||||||
EthereumNode, nonce::fetch_onchain_nonce, trace::trace_transaction,
|
EthereumNode, trace::trace_transaction, transaction::execute_transaction,
|
||||||
transaction::execute_transaction,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::Node;
|
use crate::Node;
|
||||||
@@ -51,7 +45,6 @@ pub struct Instance {
|
|||||||
network_id: u64,
|
network_id: u64,
|
||||||
start_timeout: u64,
|
start_timeout: u64,
|
||||||
wallet: EthereumWallet,
|
wallet: EthereumWallet,
|
||||||
nonces: Mutex<HashMap<Address, u64>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instance {
|
impl Instance {
|
||||||
@@ -159,8 +152,6 @@ impl EthereumNode for Instance {
|
|||||||
let connection_string = self.connection_string();
|
let connection_string = self.connection_string();
|
||||||
let wallet = self.wallet.clone();
|
let wallet = self.wallet.clone();
|
||||||
|
|
||||||
log::debug!("Submitting transaction: {transaction:#?}");
|
|
||||||
|
|
||||||
execute_transaction(Box::pin(async move {
|
execute_transaction(Box::pin(async move {
|
||||||
Ok(ProviderBuilder::new()
|
Ok(ProviderBuilder::new()
|
||||||
.wallet(wallet)
|
.wallet(wallet)
|
||||||
@@ -207,19 +198,6 @@ impl EthereumNode for Instance {
|
|||||||
_ => anyhow::bail!("expected a diff mode trace"),
|
_ => 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 {
|
impl Node for Instance {
|
||||||
@@ -238,7 +216,6 @@ impl Node for Instance {
|
|||||||
network_id: config.network_id,
|
network_id: config.network_id,
|
||||||
start_timeout: config.geth_start_timeout,
|
start_timeout: config.geth_start_timeout,
|
||||||
wallet: config.wallet(),
|
wallet: config.wallet(),
|
||||||
nonces: Mutex::new(HashMap::new()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,15 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
|
||||||
fs::create_dir_all,
|
fs::create_dir_all,
|
||||||
io::BufRead,
|
io::BufRead,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
process::{Child, Command, Stdio},
|
process::{Child, Command, Stdio},
|
||||||
sync::{
|
sync::atomic::{AtomicU32, Ordering},
|
||||||
Mutex,
|
|
||||||
atomic::{AtomicU32, Ordering},
|
|
||||||
},
|
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use alloy::{
|
use alloy::{
|
||||||
hex,
|
hex,
|
||||||
network::EthereumWallet,
|
network::EthereumWallet,
|
||||||
primitives::Address,
|
|
||||||
providers::{Provider, ProviderBuilder, ext::DebugApi},
|
providers::{Provider, ProviderBuilder, ext::DebugApi},
|
||||||
rpc::types::{
|
rpc::types::{
|
||||||
TransactionReceipt,
|
TransactionReceipt,
|
||||||
@@ -27,8 +22,7 @@ use sp_runtime::AccountId32;
|
|||||||
|
|
||||||
use revive_dt_config::Arguments;
|
use revive_dt_config::Arguments;
|
||||||
use revive_dt_node_interaction::{
|
use revive_dt_node_interaction::{
|
||||||
EthereumNode, nonce::fetch_onchain_nonce, trace::trace_transaction,
|
EthereumNode, trace::trace_transaction, transaction::execute_transaction,
|
||||||
transaction::execute_transaction,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::Node;
|
use crate::Node;
|
||||||
@@ -45,7 +39,6 @@ pub struct KitchensinkNode {
|
|||||||
base_directory: PathBuf,
|
base_directory: PathBuf,
|
||||||
process_substrate: Option<Child>,
|
process_substrate: Option<Child>,
|
||||||
process_proxy: Option<Child>,
|
process_proxy: Option<Child>,
|
||||||
nonces: Mutex<HashMap<Address, u64>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KitchensinkNode {
|
impl KitchensinkNode {
|
||||||
@@ -252,8 +245,6 @@ impl EthereumNode for KitchensinkNode {
|
|||||||
let url = self.rpc_url.clone();
|
let url = self.rpc_url.clone();
|
||||||
let wallet = self.wallet.clone();
|
let wallet = self.wallet.clone();
|
||||||
|
|
||||||
log::debug!("Submitting transaction: {transaction:#?}");
|
|
||||||
|
|
||||||
execute_transaction(Box::pin(async move {
|
execute_transaction(Box::pin(async move {
|
||||||
Ok(ProviderBuilder::new()
|
Ok(ProviderBuilder::new()
|
||||||
.wallet(wallet)
|
.wallet(wallet)
|
||||||
@@ -298,19 +289,6 @@ impl EthereumNode for KitchensinkNode {
|
|||||||
_ => anyhow::bail!("expected a diff mode trace"),
|
_ => 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 {
|
impl Node for KitchensinkNode {
|
||||||
@@ -328,7 +306,6 @@ impl Node for KitchensinkNode {
|
|||||||
base_directory,
|
base_directory,
|
||||||
process_substrate: None,
|
process_substrate: None,
|
||||||
process_proxy: None,
|
process_proxy: None,
|
||||||
nonces: Mutex::new(HashMap::new()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
Submodule polkadot-sdk updated: dc3d0e5ab7...dd97d10c52
Reference in New Issue
Block a user