Compare commits

..

9 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
Omar 6f4aa731ab Handle exceptions (#54)
* Add support for wrapper types

* Move `FilesWithExtensionIterator` to `core::common`

* Remove unneeded use of two `HashMap`s

* Make metadata structs more typed

* Impl new_from for wrapper types

* Implement the new input handling logic

* Fix edge-case in input handling

* Ignore macro doc comment tests

* Correct comment

* Fix edge-case in deployment order

* Handle calldata better

* Allow for the use of function signatures

* Add support for exceptions

* Cached nonce allocator

* Fix tests

* Add support for address replacement

* Cleanup implementation

* Cleanup mutability

* Wire up address replacement with rest of code

* Implement caller replacement

* Switch to callframe trace for exceptions

* Add a way to skip tests if they don't match the target

* Handle values from the metadata files

* Remove address replacement

* Correct the arguments

* Remove empty impl

* Remove address replacement

* Correct the arguments

* Remove empty impl

* Fix size_requirement underflow

* Add support for wildcards in exceptions

* Fix calldata construction of single calldata

* Better handling for length in equivalency checks

* Make initial balance a constant

* Fix size_requirement underflow

* Add support for wildcards in exceptions

* Fix calldata construction of single calldata

* Better handling for length in equivalency checks

* Fix tests
2025-07-24 03:45:53 +00:00
8 changed files with 93 additions and 43 deletions
+1 -1
View File
@@ -76,7 +76,7 @@ pub struct Arguments {
/// This argument controls which private keys the nodes should have access to and be added to
/// its wallet signers. With a value of N, private keys (0, N] will be added to the signer set
/// of the node.
#[arg(short, long = "private-keys-count", default_value_t = 30)]
#[arg(long = "private-keys-count", default_value_t = 30)]
pub private_keys_to_add: usize,
/// The differential testing leader node implementation.
+29 -23
View File
@@ -5,7 +5,6 @@ use std::marker::PhantomData;
use alloy::json_abi::JsonAbi;
use alloy::network::{Ethereum, TransactionBuilder};
use alloy::primitives::Bytes;
use alloy::rpc::types::TransactionReceipt;
use alloy::rpc::types::trace::geth::{
CallFrame, GethDebugBuiltInTracerType, GethDebugTracerType, GethDebugTracingOptions, GethTrace,
@@ -442,9 +441,8 @@ where
// Additionally, what happens if the compiler filter doesn't match? Do we consider that the
// transaction should succeed? Do we just ignore the expectation?
let error_span =
tracing::error_span!("Exception failed", ?tracing_result, ?execution_receipt,);
let _guard = error_span.enter();
let deployed_contracts = self.deployed_contracts.entry(case_idx).or_default();
let chain_state_provider = node;
// Handling the receipt state assertion.
let expected = !expectation.exception;
@@ -458,17 +456,16 @@ where
// Handling the calldata assertion
if let Some(ref expected_calldata) = expectation.return_data {
let expected = expected_calldata
.calldata(self.deployed_contracts.entry(case_idx).or_default(), node)
.map(Bytes::from)?;
let actual = tracing_result.output.clone().unwrap_or_default();
if !expected.starts_with(&actual) {
let expected = expected_calldata;
let actual = &tracing_result.output.as_ref().unwrap_or_default();
if !expected.is_equivalent(actual, deployed_contracts, chain_state_provider)? {
tracing::error!(
%expected,
?execution_receipt,
?expected,
%actual,
"Calldata assertion failed"
);
anyhow::bail!("Calldata assertion failed - Expected {expected} but got {actual}",);
anyhow::bail!("Calldata assertion failed - Expected {expected:?} but got {actual}",);
}
}
@@ -505,17 +502,24 @@ where
}
// Handling the topics assertion.
for (expected_topic, actual_topic) in expected_event
for (expected, actual) in expected_event
.topics
.as_slice()
.iter()
.zip(actual_event.topics())
{
let expected = Calldata::Compound(vec![expected_topic.clone()])
.calldata(self.deployed_contracts.entry(case_idx).or_default(), node)?;
let actual = actual_topic.to_vec();
if actual != expected {
tracing::error!(?expected, ?actual, "Event topics assertion failed",);
let expected = Calldata::Compound(vec![expected.clone()]);
if !expected.is_equivalent(
&actual.0,
deployed_contracts,
chain_state_provider,
)? {
tracing::error!(
?execution_receipt,
?expected,
?actual,
"Event topics assertion failed",
);
anyhow::bail!(
"Event topics assertion failed - Expected {expected:?} but got {actual:?}",
);
@@ -523,13 +527,15 @@ where
}
// Handling the values assertion.
let expected = &expected_event
.values
.calldata(self.deployed_contracts.entry(case_idx).or_default(), node)
.map(Bytes::from)?;
let expected = &expected_event.values;
let actual = &actual_event.data().data;
if !expected.starts_with(actual) {
tracing::error!(?expected, ?actual, "Event value assertion failed",);
if !expected.is_equivalent(&actual.0, deployed_contracts, chain_state_provider)? {
tracing::error!(
?execution_receipt,
?expected,
?actual,
"Event value assertion failed",
);
anyhow::bail!(
"Event value assertion failed - Expected {expected:?} but got {actual:?}",
);
+42 -4
View File
@@ -57,7 +57,7 @@ pub struct Event {
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
#[serde(untagged)]
pub enum Calldata {
Single(String),
Single(Bytes),
Compound(Vec<String>),
}
@@ -168,8 +168,8 @@ impl Calldata {
chain_state_provider: &impl EthereumNode,
) -> anyhow::Result<()> {
match self {
Calldata::Single(string) => {
alloy::hex::decode_to_slice(string, buffer)?;
Calldata::Single(bytes) => {
buffer.extend_from_slice(bytes);
}
Calldata::Compound(items) => {
for (arg_idx, arg) in items.iter().enumerate() {
@@ -190,10 +190,48 @@ impl Calldata {
pub fn size_requirement(&self) -> usize {
match self {
Calldata::Single(single) => (single.len() - 2) / 2,
Calldata::Single(single) => single.len(),
Calldata::Compound(items) => items.len() * 32,
}
}
/// Checks if this [`Calldata`] is equivalent to the passed calldata bytes.
pub fn is_equivalent(
&self,
other: &[u8],
deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>,
chain_state_provider: &impl EthereumNode,
) -> anyhow::Result<bool> {
match self {
Calldata::Single(calldata) => Ok(calldata == other),
Calldata::Compound(items) => {
// Chunking the "other" calldata into 32 byte chunks since each
// one of the items in the compound calldata represents 32 bytes
for (this, other) in items.iter().zip(other.chunks(32)) {
// The matterlabs format supports wildcards and therefore we
// also need to support them.
if this == "*" {
continue;
}
let other = if other.len() < 32 {
let mut vec = other.to_vec();
vec.resize(32, 0);
std::borrow::Cow::Owned(vec)
} else {
std::borrow::Cow::Borrowed(other)
};
let this = resolve_argument(this, deployed_contracts, chain_state_provider)?;
let other = U256::from_be_slice(&other);
if this != other {
return Ok(false);
}
}
Ok(true)
}
}
}
}
impl Input {
+5
View File
@@ -0,0 +1,5 @@
/// This constant defines how much Wei accounts are pre-seeded with in genesis.
///
/// Note: After changing this number, check that the tests for kitchensink work as we encountered
/// some issues with different values of the initial balance on Kitchensink.
pub const INITIAL_BALANCE: u128 = 10u128.pow(37);
+7 -5
View File
@@ -29,7 +29,7 @@ use revive_dt_config::Arguments;
use revive_dt_node_interaction::{BlockingExecutor, EthereumNode};
use tracing::Level;
use crate::{Node, common::FallbackGasFiller};
use crate::{Node, common::FallbackGasFiller, constants::INITIAL_BALANCE};
static NODE_COUNT: AtomicU32 = AtomicU32::new(0);
@@ -84,10 +84,12 @@ impl Instance {
for signer_address in
<EthereumWallet as NetworkWallet<Ethereum>>::signer_addresses(&self.wallet)
{
genesis.alloc.entry(signer_address).or_insert(
GenesisAccount::default()
.with_balance(10000000000000000000000u128.try_into().unwrap()),
);
// Note, the use of the entry API here means that we only modify the entries for any
// account that is not in the `alloc` field of the genesis state.
genesis
.alloc
.entry(signer_address)
.or_insert(GenesisAccount::default().with_balance(U256::from(INITIAL_BALANCE)));
}
let genesis_path = self.base_directory.join(Self::GENESIS_JSON_FILE);
serde_json::to_writer(File::create(&genesis_path)?, &genesis)?;
+7 -5
View File
@@ -39,7 +39,7 @@ use tracing::Level;
use revive_dt_config::Arguments;
use revive_dt_node_interaction::{BlockingExecutor, EthereumNode};
use crate::{Node, common::FallbackGasFiller};
use crate::{Node, common::FallbackGasFiller, constants::INITIAL_BALANCE};
static NODE_COUNT: AtomicU32 = AtomicU32::new(0);
@@ -131,10 +131,12 @@ impl KitchensinkNode {
for signer_address in
<EthereumWallet as NetworkWallet<Ethereum>>::signer_addresses(&self.wallet)
{
genesis.alloc.entry(signer_address).or_insert(
GenesisAccount::default()
.with_balance(10000000000000000000000u128.try_into().unwrap()),
);
// Note, the use of the entry API here means that we only modify the entries for any
// account that is not in the `alloc` field of the genesis state.
genesis
.alloc
.entry(signer_address)
.or_insert(GenesisAccount::default().with_balance(U256::from(INITIAL_BALANCE)));
}
self.extract_balance_from_genesis_file(&genesis)?
};
+1
View File
@@ -4,6 +4,7 @@ use revive_dt_config::Arguments;
use revive_dt_node_interaction::EthereumNode;
pub mod common;
pub mod constants;
pub mod geth;
pub mod kitchensink;
pub mod pool;
+1 -5
View File
@@ -33,9 +33,5 @@
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x00",
"alloc": {
"90F8bf6A479f320ead074411a4B0e7944Ea8c9C1": {
"balance": "10000000000000000000000"
}
}
"alloc": {}
}