Compare commits

..

8 Commits

Author SHA1 Message Date
Omar Abdulla 2a9d497827 Update the initial balance 2025-07-24 11:40:33 +03:00
Omar Abdulla 4763d763f6 Revert "Add extra comments"
This reverts commit bd4de2c83d.
2025-07-24 10:38:23 +03:00
Omar Abdulla bd4de2c83d Add extra comments 2025-07-24 09:48:18 +03:00
Omar Abdulla 3c70e0ac76 Revert "Bump commit hash of polkadot SDK"
This reverts commit 8aaa69780e.
2025-07-24 09:39:08 +03:00
Omar Abdulla 995ef80940 Revert "Change how the cache key is computed"
This reverts commit 75afdd9cfd.
2025-07-24 09:39:00 +03:00
Omar Abdulla 75afdd9cfd Change how the cache key is computed 2025-07-24 08:31:50 +03:00
Omar Abdulla 8aaa69780e Bump commit hash of polkadot SDK 2025-07-24 08:19:05 +03:00
Omar Abdulla d7795fff9d Pre-seed accounts with more ETH.
This commit fixes and solves some issues around how much ETH we seed an
account with in genesis. Currently, any account that the node has keys
to sign for will be seeded with u128::MAX WEI in genesis. This also
includes the default signer account.
2025-07-24 06:58:46 +03:00
24 changed files with 421 additions and 723 deletions
Generated
+5 -15
View File
@@ -3948,17 +3948,6 @@ 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"
@@ -3993,7 +3982,6 @@ dependencies = [
"clap",
"indexmap 2.10.0",
"rayon",
"revive-dt-common",
"revive-dt-compiler",
"revive-dt-config",
"revive-dt-format",
@@ -4015,7 +4003,7 @@ dependencies = [
"alloy-primitives",
"alloy-sol-types",
"anyhow",
"revive-dt-common",
"revive-dt-node-interaction",
"semver 1.0.26",
"serde",
"serde_json",
@@ -4028,9 +4016,7 @@ version = "0.1.0"
dependencies = [
"alloy",
"anyhow",
"revive-dt-common",
"revive-dt-config",
"revive-dt-format",
"revive-dt-node-interaction",
"serde",
"serde_json",
@@ -4047,6 +4033,10 @@ version = "0.1.0"
dependencies = [
"alloy",
"anyhow",
"futures",
"once_cell",
"tokio",
"tracing",
]
[[package]]
-1
View File
@@ -11,7 +11,6 @@ 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
@@ -1,16 +0,0 @@
[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 }
-3
View File
@@ -1,3 +0,0 @@
mod blocking_executor;
pub use blocking_executor::*;
-3
View File
@@ -1,3 +0,0 @@
mod files_with_extension_iterator;
pub use files_with_extension_iterator::*;
-6
View File
@@ -1,6 +0,0 @@
//! 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;
-3
View File
@@ -1,3 +0,0 @@
mod define_wrapper_type;
pub use define_wrapper_type::*;
-1
View File
@@ -13,7 +13,6 @@ 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 }
+5 -6
View File
@@ -1,7 +1,6 @@
//! 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;
@@ -20,9 +19,6 @@ 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;
@@ -33,8 +29,11 @@ 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.
@@ -509,7 +508,7 @@ where
.iter()
.zip(actual_event.topics())
{
let expected = Calldata::new_compound([expected]);
let expected = Calldata::Compound(vec![expected.clone()]);
if !expected.is_equivalent(
&actual.0,
deployed_contracts,
@@ -718,7 +717,7 @@ where
);
let _guard = tracing_span.enter();
let case_idx = CaseIdx::new(case_idx);
let case_idx = CaseIdx::new_from(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 + ResolverApi;
type Blockchain: EthereumNode + Node;
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-common = { workspace = true }
revive-dt-node-interaction = { workspace = true }
alloy = { workspace = true }
alloy-primitives = { workspace = true }
+2 -3
View File
@@ -1,8 +1,7 @@
use serde::Deserialize;
use revive_dt_common::macros::define_wrapper_type;
use crate::{
define_wrapper_type,
input::{Expected, Input},
mode::Mode,
};
@@ -46,5 +45,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)]
pub struct CaseIdx(usize);
CaseIdx(usize);
);
+288 -545
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,9 +12,11 @@
/// 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:
///
@@ -24,31 +26,33 @@
/// }
/// ```
///
/// 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])*
$vis:vis struct $ident: ident($ty: ty);
$ident: ident($ty: ty) $(;)?
) => {
$(#[$meta])*
$vis struct $ident($ty);
pub struct $ident($ty);
impl $ident {
pub fn new(value: impl Into<$ty>) -> Self {
pub fn new(value: $ty) -> Self {
Self(value)
}
pub fn new_from<T: Into<$ty>>(value: T) -> Self {
Self(value.into())
}
@@ -100,7 +104,3 @@ 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;
+7 -12
View File
@@ -9,10 +9,9 @@ use std::{
use serde::{Deserialize, Serialize};
use revive_dt_common::macros::define_wrapper_type;
use crate::{
case::Case,
define_wrapper_type,
mode::{Mode, SolcMode},
};
@@ -193,10 +192,10 @@ impl Metadata {
metadata.file_path = Some(path.to_path_buf());
metadata.contracts = Some(
[(
ContractInstance::new("test"),
ContractInstance::new_from("test"),
ContractPathAndIdentifier {
contract_source_path: path.to_path_buf(),
contract_ident: ContractIdent::new("Test"),
contract_ident: ContractIdent::new_from("Test"),
},
)]
.into(),
@@ -218,22 +217,18 @@ 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)]
pub struct ContractInstance(String);
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)]
pub struct ContractIdent(String);
ContractIdent(String);
);
/// Represents an identifier used for contracts.
-30
View File
@@ -1,30 +0,0 @@
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,3 +11,7 @@ rust-version.workspace = true
[dependencies]
alloy = { workspace = true }
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_common::concepts::*;
/// use revive_dt_node_interaction::*;
///
/// fn blocking_function() {
/// let result = BlockingExecutor::execute(async move {
@@ -134,17 +134,22 @@ impl BlockingExecutor {
}
};
let result = match result {
Ok(result) => result,
match result.map(|result| {
*result
.downcast::<R>()
.expect("Type mismatch in the downcast")
}) {
Ok(result) => Ok(result),
Err(error) => {
tracing::error!(?error, "An error occurred when running the async task");
anyhow::bail!("An error occurred when running the async task: {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:?}"
)
}
};
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
@@ -203,9 +208,7 @@ mod test {
fn panics_in_futures_are_caught() {
// Act
let result = BlockingExecutor::execute(async move {
panic!(
"If this panic causes, well, a panic, then this is an issue. If it's caught then all good!"
);
panic!("This is a panic!");
0xFFu8
});
+28
View File
@@ -1,9 +1,14 @@
//! 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].
@@ -18,4 +23,27 @@ 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>;
}
+1 -3
View File
@@ -14,10 +14,8 @@ alloy = { workspace = true }
tracing = { workspace = true }
tokio = { workspace = true }
revive-dt-common = { workspace = true }
revive-dt-config = { workspace = true }
revive-dt-format = { workspace = true }
revive-dt-node-interaction = { workspace = true }
revive-dt-config = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
+44 -38
View File
@@ -25,10 +25,8 @@ use alloy::{
},
signers::local::PrivateKeySigner,
};
use revive_dt_common::concepts::BlockingExecutor;
use revive_dt_config::Arguments;
use revive_dt_format::traits::ResolverApi;
use revive_dt_node_interaction::EthereumNode;
use revive_dt_node_interaction::{BlockingExecutor, EthereumNode};
use tracing::Level;
use crate::{Node, common::FallbackGasFiller, constants::INITIAL_BALANCE};
@@ -76,8 +74,6 @@ impl Instance {
const GETH_STDOUT_LOG_FILE_NAME: &str = "node_stdout.log";
const GETH_STDERR_LOG_FILE_NAME: &str = "node_stderr.log";
const TRANSACTION_INDEXING_ERROR: &str = "transaction indexing is in progress";
/// Create the node directory and call `geth init` to configure the genesis.
#[tracing::instrument(skip_all, fields(geth_node_id = self.id))]
fn init(&mut self, genesis: String) -> anyhow::Result<&mut Self> {
@@ -271,45 +267,57 @@ impl EthereumNode for Instance {
// it eventually works, but we only do that if the error we get back is the "transaction
// indexing is in progress" error or if the receipt is None.
//
// Getting the transaction indexed and taking a receipt can take a long time especially
// when a lot of transactions are being submitted to the node. Thus, while initially we
// only allowed for 60 seconds of waiting with a 1 second delay in polling, we need to
// allow for a larger wait time. Therefore, in here we allow for 5 minutes of waiting
// with exponential backoff each time we attempt to get the receipt and find that it's
// not available.
// At the moment we do not allow for the 60 seconds to be modified and we take it as
// being an implementation detail that's invisible to anything outside of this module.
//
// We allow a total of 60 retries for getting the receipt with one second between each
// retry and the next which means that we allow for a total of 60 seconds of waiting
// before we consider that we're unable to get the transaction receipt.
let mut retries = 0;
let mut total_wait_duration = Duration::from_secs(0);
let max_allowed_wait_duration = Duration::from_secs(5 * 60);
loop {
if total_wait_duration >= max_allowed_wait_duration {
tracing::error!(
?total_wait_duration,
?max_allowed_wait_duration,
retry_count = retries,
"Failed to get receipt after polling for it"
);
anyhow::bail!(
"Polled for receipt for {total_wait_duration:?} but failed to get it"
);
}
match provider.get_transaction_receipt(*transaction_hash).await {
Ok(Some(receipt)) => break Ok(receipt),
Ok(None) => {}
Ok(Some(receipt)) => {
tracing::info!("Obtained the transaction receipt");
break Ok(receipt);
}
Ok(None) => {
if retries == 60 {
tracing::error!(
"Polled for transaction receipt for 60 seconds but failed to get it"
);
break Err(anyhow::anyhow!("Failed to get the transaction receipt"));
} else {
tracing::trace!(
retries,
"Sleeping for 1 second and trying to get the receipt again"
);
retries += 1;
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
continue;
}
}
Err(error) => {
let error_string = error.to_string();
if !error_string.contains(Self::TRANSACTION_INDEXING_ERROR) {
if error_string.contains("transaction indexing is in progress") {
if retries == 60 {
tracing::error!(
"Polled for transaction receipt for 60 seconds but failed to get it"
);
break Err(error.into());
} else {
tracing::trace!(
retries,
"Sleeping for 1 second and trying to get the receipt again"
);
retries += 1;
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
continue;
}
} else {
break Err(error.into());
}
}
};
let next_wait_duration = Duration::from_secs(2u64.pow(retries))
.min(max_allowed_wait_duration - total_wait_duration);
total_wait_duration += next_wait_duration;
retries += 1;
tokio::time::sleep(next_wait_duration).await;
}
}
})?
}
@@ -345,9 +353,7 @@ 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();
+1 -5
View File
@@ -30,16 +30,14 @@ 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::EthereumNode;
use revive_dt_node_interaction::{BlockingExecutor, EthereumNode};
use crate::{Node, common::FallbackGasFiller, constants::INITIAL_BALANCE};
@@ -426,9 +424,7 @@ 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();