Skip to content

Commit ce1b486

Browse files
committed
Add support for wildcards in exceptions
1 parent 799f07f commit ce1b486

File tree

2 files changed

+70
-33
lines changed

2 files changed

+70
-33
lines changed

crates/core/src/driver/mod.rs

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use std::marker::PhantomData;
55

66
use alloy::json_abi::JsonAbi;
77
use alloy::network::{Ethereum, TransactionBuilder};
8-
use alloy::primitives::Bytes;
98
use alloy::rpc::types::TransactionReceipt;
109
use alloy::rpc::types::trace::geth::{
1110
DefaultFrame, GethDebugTracingOptions, GethDefaultTracingOptions, GethTrace, PreStateConfig,
@@ -437,6 +436,9 @@ where
437436
// Additionally, what happens if the compiler filter doesn't match? Do we consider that the
438437
// transaction should succeed? Do we just ignore the expectation?
439438

439+
let deployed_contracts = self.deployed_contracts.entry(case_idx).or_default();
440+
let chain_state_provider = node;
441+
440442
// Handling the receipt state assertion.
441443
let expected = !expectation.exception;
442444
let actual = execution_receipt.status();
@@ -454,13 +456,16 @@ where
454456

455457
// Handling the calldata assertion
456458
if let Some(ref expected_calldata) = expectation.return_data {
457-
let expected = expected_calldata
458-
.calldata(self.deployed_contracts.entry(case_idx).or_default(), node)
459-
.map(Bytes::from)?;
460-
let actual = tracing_result.return_value.clone();
461-
if !expected.starts_with(&actual) {
462-
tracing::error!(?execution_receipt, %expected, %actual, "Calldata assertion failed");
463-
anyhow::bail!("Calldata assertion failed - Expected {expected} but got {actual}",);
459+
let expected = expected_calldata;
460+
let actual = &tracing_result.return_value;
461+
if !expected.is_equivalent(actual, deployed_contracts, chain_state_provider)? {
462+
tracing::error!(
463+
?execution_receipt,
464+
?expected,
465+
%actual,
466+
"Calldata assertion failed"
467+
);
468+
anyhow::bail!("Calldata assertion failed - Expected {expected:?} but got {actual}",);
464469
}
465470
}
466471

@@ -503,27 +508,34 @@ where
503508
}
504509

505510
// Handling the topics assertion.
506-
let expected = expected_event.topics.as_slice();
507-
let actual = actual_event.topics();
508-
if actual != expected {
509-
tracing::error!(
510-
?execution_receipt,
511-
?expected,
512-
?actual,
513-
"Event topics assertion failed",
514-
);
515-
anyhow::bail!(
516-
"Event topics assertion failed - Expected {expected:?} but got {actual:?}",
517-
);
511+
for (expected, actual) in expected_event
512+
.topics
513+
.as_slice()
514+
.iter()
515+
.zip(actual_event.topics())
516+
{
517+
let expected = Calldata::Compound(vec![expected.clone()]);
518+
if !expected.is_equivalent(
519+
&actual.0,
520+
deployed_contracts,
521+
chain_state_provider,
522+
)? {
523+
tracing::error!(
524+
?execution_receipt,
525+
?expected,
526+
?actual,
527+
"Event topics assertion failed",
528+
);
529+
anyhow::bail!(
530+
"Event topics assertion failed - Expected {expected:?} but got {actual:?}",
531+
);
532+
}
518533
}
519534

520535
// Handling the values assertion.
521-
let expected = &expected_event
522-
.values
523-
.calldata(self.deployed_contracts.entry(case_idx).or_default(), node)
524-
.map(Bytes::from)?;
536+
let expected = &expected_event.values;
525537
let actual = &actual_event.data().data;
526-
if !expected.starts_with(actual) {
538+
if !expected.is_equivalent(&actual.0, deployed_contracts, chain_state_provider)? {
527539
tracing::error!(
528540
?execution_receipt,
529541
?expected,

crates/format/src/input.rs

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use alloy::{
77
primitives::{Address, Bytes, U256},
88
rpc::types::TransactionRequest,
99
};
10-
use alloy_primitives::B256;
1110
use semver::VersionReq;
1211
use serde::Deserialize;
1312

@@ -50,14 +49,14 @@ pub struct ExpectedOutput {
5049
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)]
5150
pub struct Event {
5251
pub address: Option<Address>,
53-
pub topics: Vec<B256>,
52+
pub topics: Vec<String>,
5453
pub values: Calldata,
5554
}
5655

5756
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
5857
#[serde(untagged)]
5958
pub enum Calldata {
60-
Single(String),
59+
Single(Bytes),
6160
Compound(Vec<String>),
6261
}
6362

@@ -159,14 +158,40 @@ impl Calldata {
159158

160159
pub fn size_requirement(&self) -> usize {
161160
match self {
162-
Calldata::Single(single) => single
163-
.len()
164-
.checked_sub(2)
165-
.and_then(|value| value.checked_div(2))
166-
.unwrap_or_default(),
161+
Calldata::Single(single) => single.len(),
167162
Calldata::Compound(items) => items.len() * 32,
168163
}
169164
}
165+
166+
/// Checks if this [`Calldata`] is equivalent to the passed calldata bytes.
167+
pub fn is_equivalent(
168+
&self,
169+
other: &[u8],
170+
deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>,
171+
chain_state_provider: &impl EthereumNode,
172+
) -> anyhow::Result<bool> {
173+
match self {
174+
Calldata::Single(calldata) => Ok(calldata == other),
175+
Calldata::Compound(items) => {
176+
// Chunking the "other" calldata into 32 byte chunks since each
177+
// one of the items in the compound calldata represents 32 bytes
178+
for (this, other) in items.iter().zip(other.chunks(32)) {
179+
// The matterlabs format supports wildcards and therefore we
180+
// also need to support them.
181+
if this == "*" {
182+
continue;
183+
}
184+
185+
let this = resolve_argument(this, deployed_contracts, chain_state_provider)?;
186+
let other = U256::from_be_slice(other);
187+
if this != other {
188+
return Ok(false);
189+
}
190+
}
191+
Ok(true)
192+
}
193+
}
194+
}
170195
}
171196

172197
impl Input {

0 commit comments

Comments
 (0)