Compare commits

...

3 Commits

Author SHA1 Message Date
Omar Abdulla 772c5f79fc Implement a reverse polish calculator for calldata arithmetic 2025-07-24 17:25:36 +03:00
Omar Abdulla aabcd06254 Re-order the input file.
This commit reorders the input file such that we have a definitions
section and an implementations section and such that the the order of
the items in both sections is the same.
2025-07-24 16:07:06 +03:00
Omar 90fb89adc0 Add a common crate (#75)
* Add a barebones common crate

* Refactor some code into the common crate

* Add a `ResolverApi` interface.

This commit adds a `ResolverApi` trait to the `format` crate that can be
implemented by any type that can act as a resolver. A resolver is able
to provide information on the chain state. This chain state could be
fresh or it could be cached (which is something that we will do in a
future PR).

This cleans up our crate graph so that `format` is not depending on the
node interactions crate for the `EthereumNode` trait.

* Cleanup the blocking executor
2025-07-24 12:42:45 +00:00
24 changed files with 690 additions and 378 deletions
Generated
+15 -5
View File
@@ -3948,6 +3948,17 @@ dependencies = [
"serde_stacker",
]
[[package]]
name = "revive-dt-common"
version = "0.1.0"
dependencies = [
"anyhow",
"futures",
"once_cell",
"tokio",
"tracing",
]
[[package]]
name = "revive-dt-compiler"
version = "0.1.0"
@@ -3982,6 +3993,7 @@ dependencies = [
"clap",
"indexmap 2.10.0",
"rayon",
"revive-dt-common",
"revive-dt-compiler",
"revive-dt-config",
"revive-dt-format",
@@ -4003,7 +4015,7 @@ dependencies = [
"alloy-primitives",
"alloy-sol-types",
"anyhow",
"revive-dt-node-interaction",
"revive-dt-common",
"semver 1.0.26",
"serde",
"serde_json",
@@ -4016,7 +4028,9 @@ version = "0.1.0"
dependencies = [
"alloy",
"anyhow",
"revive-dt-common",
"revive-dt-config",
"revive-dt-format",
"revive-dt-node-interaction",
"serde",
"serde_json",
@@ -4033,10 +4047,6 @@ version = "0.1.0"
dependencies = [
"alloy",
"anyhow",
"futures",
"once_cell",
"tokio",
"tracing",
]
[[package]]
+1
View File
@@ -11,6 +11,7 @@ repository = "https://github.com/paritytech/revive-differential-testing.git"
rust-version = "1.85.0"
[workspace.dependencies]
revive-dt-common = { version = "0.1.0", path = "crates/common" }
revive-dt-compiler = { version = "0.1.0", path = "crates/compiler" }
revive-dt-config = { version = "0.1.0", path = "crates/config" }
revive-dt-core = { version = "0.1.0", path = "crates/core" }
+16
View File
@@ -0,0 +1,16 @@
[package]
name = "revive-dt-common"
description = "A library containing common concepts that other crates in the workspace can rely on"
version.workspace = true
authors.workspace = true
license.workspace = true
edition.workspace = true
repository.workspace = true
rust-version.workspace = true
[dependencies]
anyhow = { workspace = true }
futures = { workspace = true }
tracing = { workspace = true }
once_cell = { workspace = true }
tokio = { workspace = true }
@@ -23,7 +23,7 @@ use tracing::Instrument;
/// executor to drive an async computation:
///
/// ```rust
/// use revive_dt_node_interaction::*;
/// use revive_dt_common::concepts::*;
///
/// fn blocking_function() {
/// let result = BlockingExecutor::execute(async move {
@@ -134,22 +134,17 @@ impl BlockingExecutor {
}
};
match result.map(|result| {
*result
.downcast::<R>()
.expect("Type mismatch in the downcast")
}) {
Ok(result) => Ok(result),
let result = match result {
Ok(result) => result,
Err(error) => {
tracing::error!(
?error,
"Failed to downcast the returned result into the expected type"
);
anyhow::bail!(
"Failed to downcast the returned result into the expected type: {error:?}"
)
tracing::error!(?error, "An error occurred when running the async task");
anyhow::bail!("An error occurred when running the async task: {error:?}")
}
}
};
Ok(*result
.downcast::<R>()
.expect("An error occurred when downcasting into R. This is a bug"))
}
}
/// Represents the state of the async runtime. This runtime is designed to be a singleton runtime
@@ -208,7 +203,9 @@ mod test {
fn panics_in_futures_are_caught() {
// Act
let result = BlockingExecutor::execute(async move {
panic!("This is a panic!");
panic!(
"If this panic causes, well, a panic, then this is an issue. If it's caught then all good!"
);
0xFFu8
});
+3
View File
@@ -0,0 +1,3 @@
mod blocking_executor;
pub use blocking_executor::*;
+3
View File
@@ -0,0 +1,3 @@
mod files_with_extension_iterator;
pub use files_with_extension_iterator::*;
+6
View File
@@ -0,0 +1,6 @@
//! This crate provides common concepts, functionality, types, macros, and more that other crates in
//! the workspace can benefit from.
pub mod concepts;
pub mod iterators;
pub mod macros;
@@ -12,11 +12,9 @@
/// pub struct CaseId(usize);
/// ```
///
/// And would also implement a number of methods on this type making it easier
/// to use.
/// And would also implement a number of methods on this type making it easier to use.
///
/// These wrapper types become very useful as they make the code a lot easier
/// to read.
/// These wrapper types become very useful as they make the code a lot easier to read.
///
/// Take the following as an example:
///
@@ -26,33 +24,31 @@
/// }
/// ```
///
/// In the above code it's hard to understand what the various types refer to or
/// what to expect them to contain.
/// In the above code it's hard to understand what the various types refer to or what to expect them
/// to contain.
///
/// With these wrapper types we're able to create code that's self-documenting
/// in that the types tell us what the code is referring to. The above code is
/// transformed into
/// With these wrapper types we're able to create code that's self-documenting in that the types
/// tell us what the code is referring to. The above code is transformed into
///
/// ```rust,ignore
/// struct State {
/// contracts: HashMap<CaseId, HashMap<ContractName, ContractByteCode>>
/// }
/// ```
///
/// Note that we follow the same syntax for defining wrapper structs but we do not permit the use of
/// generics.
#[macro_export]
macro_rules! define_wrapper_type {
(
$(#[$meta: meta])*
$ident: ident($ty: ty) $(;)?
$vis:vis struct $ident: ident($ty: ty);
) => {
$(#[$meta])*
pub struct $ident($ty);
$vis struct $ident($ty);
impl $ident {
pub fn new(value: $ty) -> Self {
Self(value)
}
pub fn new_from<T: Into<$ty>>(value: T) -> Self {
pub fn new(value: impl Into<$ty>) -> Self {
Self(value.into())
}
@@ -104,3 +100,7 @@ macro_rules! define_wrapper_type {
}
};
}
/// Technically not needed but this allows for the macro to be found in the `macros` module of the
/// crate in addition to being found in the root of the crate.
pub use define_wrapper_type;
+3
View File
@@ -0,0 +1,3 @@
mod define_wrapper_type;
pub use define_wrapper_type::*;
+1
View File
@@ -13,6 +13,7 @@ name = "retester"
path = "src/main.rs"
[dependencies]
revive-dt-common = { workspace = true }
revive-dt-compiler = { workspace = true }
revive-dt-config = { workspace = true }
revive-dt-format = { workspace = true }
+6 -5
View File
@@ -1,6 +1,7 @@
//! The test driver handles the compilation and execution of the test cases.
use std::collections::HashMap;
use std::fmt::Debug;
use std::marker::PhantomData;
use alloy::json_abi::JsonAbi;
@@ -19,6 +20,9 @@ use alloy::{
};
use anyhow::Context;
use indexmap::IndexMap;
use serde_json::Value;
use revive_dt_common::iterators::FilesWithExtensionIterator;
use revive_dt_compiler::{Compiler, SolidityCompiler};
use revive_dt_config::Arguments;
use revive_dt_format::case::CaseIdx;
@@ -29,11 +33,8 @@ use revive_dt_node::Node;
use revive_dt_node_interaction::EthereumNode;
use revive_dt_report::reporter::{CompilationTask, Report, Span};
use revive_solc_json_interface::SolcStandardJsonOutput;
use serde_json::Value;
use std::fmt::Debug;
use crate::Platform;
use crate::common::*;
pub struct State<'a, T: Platform> {
/// The configuration that the framework was started with.
@@ -508,7 +509,7 @@ where
.iter()
.zip(actual_event.topics())
{
let expected = Calldata::Compound(vec![expected.clone()]);
let expected = Calldata::new_compound([expected]);
if !expected.is_equivalent(
&actual.0,
deployed_contracts,
@@ -717,7 +718,7 @@ where
);
let _guard = tracing_span.enter();
let case_idx = CaseIdx::new_from(case_idx);
let case_idx = CaseIdx::new(case_idx);
// For inputs if one of the inputs fail we move on to the next case (we do not move
// on to the next input as it doesn't make sense. It depends on the previous one).
+2 -2
View File
@@ -5,17 +5,17 @@
use revive_dt_compiler::{SolidityCompiler, revive_resolc, solc};
use revive_dt_config::TestingPlatform;
use revive_dt_format::traits::ResolverApi;
use revive_dt_node::{Node, geth, kitchensink::KitchensinkNode};
use revive_dt_node_interaction::EthereumNode;
pub mod common;
pub mod driver;
/// One platform can be tested differentially against another.
///
/// For this we need a blockchain node implementation and a compiler.
pub trait Platform {
type Blockchain: EthereumNode + Node;
type Blockchain: EthereumNode + Node + ResolverApi;
type Compiler: SolidityCompiler;
/// Returns the matching [TestingPlatform] of the [revive_dt_config::Arguments].
+1 -1
View File
@@ -9,7 +9,7 @@ repository.workspace = true
rust-version.workspace = true
[dependencies]
revive-dt-node-interaction = { workspace = true }
revive-dt-common = { workspace = true }
alloy = { workspace = true }
alloy-primitives = { workspace = true }
+3 -2
View File
@@ -1,7 +1,8 @@
use serde::Deserialize;
use revive_dt_common::macros::define_wrapper_type;
use crate::{
define_wrapper_type,
input::{Expected, Input},
mode::Mode,
};
@@ -45,5 +46,5 @@ impl Case {
define_wrapper_type!(
/// A wrapper type for the index of test cases found in metadata file.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
CaseIdx(usize);
pub struct CaseIdx(usize);
);
+545 -288
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -3,6 +3,6 @@
pub mod case;
pub mod corpus;
pub mod input;
pub mod macros;
pub mod metadata;
pub mod mode;
pub mod traits;
+12 -7
View File
@@ -9,9 +9,10 @@ use std::{
use serde::{Deserialize, Serialize};
use revive_dt_common::macros::define_wrapper_type;
use crate::{
case::Case,
define_wrapper_type,
mode::{Mode, SolcMode},
};
@@ -192,10 +193,10 @@ impl Metadata {
metadata.file_path = Some(path.to_path_buf());
metadata.contracts = Some(
[(
ContractInstance::new_from("test"),
ContractInstance::new("test"),
ContractPathAndIdentifier {
contract_source_path: path.to_path_buf(),
contract_ident: ContractIdent::new_from("Test"),
contract_ident: ContractIdent::new("Test"),
},
)]
.into(),
@@ -217,18 +218,22 @@ define_wrapper_type!(
/// Represents a contract instance found a metadata file.
///
/// Typically, this is used as the key to the "contracts" field of metadata files.
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[derive(
Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
)]
#[serde(transparent)]
ContractInstance(String);
pub struct ContractInstance(String);
);
define_wrapper_type!(
/// Represents a contract identifier found a metadata file.
///
/// A contract identifier is the name of the contract in the source code.
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[derive(
Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
)]
#[serde(transparent)]
ContractIdent(String);
pub struct ContractIdent(String);
);
/// Represents an identifier used for contracts.
+30
View File
@@ -0,0 +1,30 @@
use alloy::eips::BlockNumberOrTag;
use alloy::primitives::{Address, BlockHash, BlockNumber, BlockTimestamp, ChainId, U256};
use anyhow::Result;
/// A trait of the interface are required to implement to be used by the resolution logic that this
/// crate implements to go from string calldata and into the bytes calldata.
pub trait ResolverApi {
/// Returns the ID of the chain that the node is on.
fn chain_id(&self) -> Result<ChainId>;
// TODO: This is currently a u128 due to Kitchensink needing more than 64 bits for its gas limit
// when we implement the changes to the gas we need to adjust this to be a u64.
/// Returns the gas limit of the specified block.
fn block_gas_limit(&self, number: BlockNumberOrTag) -> Result<u128>;
/// Returns the coinbase of the specified block.
fn block_coinbase(&self, number: BlockNumberOrTag) -> Result<Address>;
/// Returns the difficulty of the specified block.
fn block_difficulty(&self, number: BlockNumberOrTag) -> Result<U256>;
/// Returns the hash of the specified block.
fn block_hash(&self, number: BlockNumberOrTag) -> Result<BlockHash>;
/// Returns the timestamp of the specified block,
fn block_timestamp(&self, number: BlockNumberOrTag) -> Result<BlockTimestamp>;
/// Returns the number of the last block.
fn last_block_number(&self) -> Result<BlockNumber>;
}
-4
View File
@@ -11,7 +11,3 @@ rust-version.workspace = true
[dependencies]
alloy = { workspace = true }
anyhow = { workspace = true }
futures = { workspace = true }
tracing = { workspace = true }
once_cell = { workspace = true }
tokio = { workspace = true }
-28
View File
@@ -1,14 +1,9 @@
//! This crate implements all node interactions.
use alloy::eips::BlockNumberOrTag;
use alloy::primitives::{Address, BlockHash, BlockNumber, BlockTimestamp, ChainId, U256};
use alloy::rpc::types::trace::geth::{DiffMode, GethDebugTracingOptions, GethTrace};
use alloy::rpc::types::{TransactionReceipt, TransactionRequest};
use anyhow::Result;
mod blocking_executor;
pub use blocking_executor::*;
/// An interface for all interactions with Ethereum compatible nodes.
pub trait EthereumNode {
/// Execute the [TransactionRequest] and return a [TransactionReceipt].
@@ -23,27 +18,4 @@ pub trait EthereumNode {
/// Returns the state diff of the transaction hash in the [TransactionReceipt].
fn state_diff(&self, receipt: &TransactionReceipt) -> Result<DiffMode>;
/// Returns the ID of the chain that the node is on.
fn chain_id(&self) -> Result<ChainId>;
// TODO: This is currently a u128 due to Kitchensink needing more than 64 bits for its gas limit
// when we implement the changes to the gas we need to adjust this to be a u64.
/// Returns the gas limit of the specified block.
fn block_gas_limit(&self, number: BlockNumberOrTag) -> Result<u128>;
/// Returns the coinbase of the specified block.
fn block_coinbase(&self, number: BlockNumberOrTag) -> Result<Address>;
/// Returns the difficulty of the specified block.
fn block_difficulty(&self, number: BlockNumberOrTag) -> Result<U256>;
/// Returns the hash of the specified block.
fn block_hash(&self, number: BlockNumberOrTag) -> Result<BlockHash>;
/// Returns the timestamp of the specified block,
fn block_timestamp(&self, number: BlockNumberOrTag) -> Result<BlockTimestamp>;
/// Returns the number of the last block.
fn last_block_number(&self) -> Result<BlockNumber>;
}
+3 -1
View File
@@ -14,8 +14,10 @@ alloy = { workspace = true }
tracing = { workspace = true }
tokio = { workspace = true }
revive-dt-node-interaction = { workspace = true }
revive-dt-common = { workspace = true }
revive-dt-config = { workspace = true }
revive-dt-format = { workspace = true }
revive-dt-node-interaction = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
+5 -1
View File
@@ -25,8 +25,10 @@ use alloy::{
},
signers::local::PrivateKeySigner,
};
use revive_dt_common::concepts::BlockingExecutor;
use revive_dt_config::Arguments;
use revive_dt_node_interaction::{BlockingExecutor, EthereumNode};
use revive_dt_format::traits::ResolverApi;
use revive_dt_node_interaction::EthereumNode;
use tracing::Level;
use crate::{Node, common::FallbackGasFiller, constants::INITIAL_BALANCE};
@@ -343,7 +345,9 @@ impl EthereumNode for Instance {
_ => anyhow::bail!("expected a diff mode trace"),
}
}
}
impl ResolverApi for Instance {
#[tracing::instrument(skip_all, fields(geth_node_id = self.id))]
fn chain_id(&self) -> anyhow::Result<alloy::primitives::ChainId> {
let provider = self.provider();
+5 -1
View File
@@ -30,14 +30,16 @@ use alloy::{
},
signers::local::PrivateKeySigner,
};
use revive_dt_format::traits::ResolverApi;
use serde::{Deserialize, Serialize};
use serde_json::{Value as JsonValue, json};
use sp_core::crypto::Ss58Codec;
use sp_runtime::AccountId32;
use tracing::Level;
use revive_dt_common::concepts::BlockingExecutor;
use revive_dt_config::Arguments;
use revive_dt_node_interaction::{BlockingExecutor, EthereumNode};
use revive_dt_node_interaction::EthereumNode;
use crate::{Node, common::FallbackGasFiller, constants::INITIAL_BALANCE};
@@ -424,7 +426,9 @@ impl EthereumNode for KitchensinkNode {
_ => anyhow::bail!("expected a diff mode trace"),
}
}
}
impl ResolverApi for KitchensinkNode {
#[tracing::instrument(skip_all, fields(geth_node_id = self.id))]
fn chain_id(&self) -> anyhow::Result<alloy::primitives::ChainId> {
let provider = self.provider();