Skip to content

Commit 36acad9

Browse files
committed
Added unit tests for ABI parameter extraction logic
1 parent 9d20848 commit 36acad9

File tree

2 files changed

+236
-10
lines changed

2 files changed

+236
-10
lines changed

crates/core/src/driver/mod.rs

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ where
325325
}
326326
}
327327
} else {
328-
log::debug!("No metadata found for contract {}", contract_name);
328+
anyhow::bail!("No metadata found for contract {}", contract_name);
329329
}
330330
}
331331
}
@@ -400,14 +400,41 @@ where
400400
for case in &self.metadata.cases {
401401
for input in &case.inputs {
402402
log::debug!("Starting deploying contract {}", &input.instance);
403-
leader_state.deploy_contracts(input, self.leader_node)?;
404-
follower_state.deploy_contracts(input, self.follower_node)?;
403+
if let Err(err) = leader_state.deploy_contracts(input, self.leader_node) {
404+
log::error!("Leader deployment failed for {}: {err}", input.instance);
405+
continue;
406+
} else {
407+
log::debug!("Leader deployment succeeded for {}", &input.instance);
408+
}
409+
410+
if let Err(err) = follower_state.deploy_contracts(input, self.follower_node) {
411+
log::error!("Follower deployment failed for {}: {err}", input.instance);
412+
continue;
413+
} else {
414+
log::debug!("Follower deployment succeeded for {}", &input.instance);
415+
}
405416

406417
log::debug!("Starting executing contract {}", &input.instance);
407-
let (leader_receipt, _, leader_diff) =
408-
leader_state.execute_input(input, self.leader_node)?;
409-
let (follower_receipt, _, follower_diff) =
410-
follower_state.execute_input(input, self.follower_node)?;
418+
419+
let (leader_receipt, _, leader_diff) = match leader_state
420+
.execute_input(input, self.leader_node)
421+
{
422+
Ok(result) => result,
423+
Err(err) => {
424+
log::error!("Leader execution failed for {}: {err}", input.instance);
425+
continue;
426+
}
427+
};
428+
429+
let (follower_receipt, _, follower_diff) = match follower_state
430+
.execute_input(input, self.follower_node)
431+
{
432+
Ok(result) => result,
433+
Err(err) => {
434+
log::error!("Follower execution failed for {}: {err}", input.instance);
435+
continue;
436+
}
437+
};
411438

412439
if leader_diff == follower_diff {
413440
log::debug!("State diffs match between leader and follower.");

crates/format/src/input.rs

Lines changed: 202 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,6 @@ impl Input {
121121
return Ok(Bytes::default()); // fallback or deployer — no input
122122
};
123123

124-
// ABI
125124
let abi = deployed_abis
126125
.get(&self.instance)
127126
.ok_or_else(|| anyhow::anyhow!("ABI for instance '{}' not found", &self.instance))?;
@@ -142,7 +141,6 @@ impl Input {
142141

143142
log::trace!("Functions found for instance: {}", &self.instance);
144143

145-
// Parse calldata
146144
let calldata_args = match &self.calldata {
147145
Some(Calldata::Compound(args)) => args,
148146
_ => anyhow::bail!("Expected compound calldata for function call"),
@@ -156,7 +154,10 @@ impl Input {
156154
);
157155
}
158156

159-
log::trace!("Starting encoding ABI's paramters for instance: {}", &self.instance);
157+
log::trace!(
158+
"Starting encoding ABI's parameters for instance: {}",
159+
&self.instance
160+
);
160161

161162
let mut encoded = selector.to_vec();
162163

@@ -252,3 +253,201 @@ fn default_instance() -> String {
252253
fn default_caller() -> Address {
253254
"90F8bf6A479f320ead074411a4B0e7944Ea8c9C1".parse().unwrap()
254255
}
256+
257+
#[cfg(test)]
258+
mod tests {
259+
260+
use super::*;
261+
use alloy::json_abi::JsonAbi;
262+
use alloy_primitives::{address, keccak256};
263+
use std::collections::HashMap;
264+
265+
#[test]
266+
fn test_encoded_input_uint256() {
267+
let raw_metadata = r#"
268+
[
269+
{
270+
"inputs": [{"name": "value", "type": "uint256"}],
271+
"name": "store",
272+
"outputs": [],
273+
"stateMutability": "nonpayable",
274+
"type": "function"
275+
}
276+
]
277+
"#;
278+
279+
let parsed_abi: JsonAbi = serde_json::from_str(raw_metadata).unwrap();
280+
let selector = keccak256("store(uint256)".as_bytes())[0..4]
281+
.try_into()
282+
.unwrap();
283+
284+
let input = Input {
285+
instance: "Contract".to_string(),
286+
method: Method::Function(selector),
287+
calldata: Some(Calldata::Compound(vec![CalldataArg::Literal(
288+
"42".to_string(),
289+
)])),
290+
..Default::default()
291+
};
292+
293+
let mut deployed_abis = HashMap::new();
294+
deployed_abis.insert("Contract".to_string(), parsed_abi);
295+
let deployed_contracts = HashMap::new();
296+
297+
let encoded = input
298+
.encoded_input(&deployed_abis, &deployed_contracts)
299+
.unwrap();
300+
assert!(encoded.0.starts_with(&selector));
301+
302+
type T = (u64,);
303+
let decoded: T = T::abi_decode(&encoded.0[4..]).unwrap();
304+
assert_eq!(decoded.0, 42);
305+
}
306+
307+
#[test]
308+
fn test_encoded_input_bool() {
309+
let raw_abi = r#"[
310+
{
311+
"inputs": [{"name": "flag", "type": "bool"}],
312+
"name": "toggle",
313+
"outputs": [],
314+
"stateMutability": "nonpayable",
315+
"type": "function"
316+
}
317+
]"#;
318+
319+
let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap();
320+
let selector = keccak256("toggle(bool)".as_bytes())[0..4]
321+
.try_into()
322+
.unwrap();
323+
324+
let input = Input {
325+
instance: "Contract".to_string(),
326+
method: Method::Function(selector),
327+
calldata: Some(Calldata::Compound(vec![CalldataArg::Literal(
328+
"true".to_string(),
329+
)])),
330+
..Default::default()
331+
};
332+
333+
let mut abis = HashMap::new();
334+
abis.insert("Contract".to_string(), parsed_abi);
335+
let contracts = HashMap::new();
336+
337+
let encoded = input.encoded_input(&abis, &contracts).unwrap();
338+
assert!(encoded.0.starts_with(&selector));
339+
340+
type T = (bool,);
341+
let decoded: T = T::abi_decode(&encoded.0[4..]).unwrap();
342+
assert_eq!(decoded.0, true);
343+
}
344+
345+
#[test]
346+
fn test_encoded_input_string() {
347+
let raw_abi = r#"[
348+
{
349+
"inputs": [{"name": "msg", "type": "string"}],
350+
"name": "echo",
351+
"outputs": [],
352+
"stateMutability": "nonpayable",
353+
"type": "function"
354+
}
355+
]"#;
356+
357+
let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap();
358+
let selector = keccak256("echo(string)".as_bytes())[0..4]
359+
.try_into()
360+
.unwrap();
361+
362+
let input = Input {
363+
instance: "Contract".to_string(),
364+
method: Method::Function(selector),
365+
calldata: Some(Calldata::Compound(vec![CalldataArg::Literal(
366+
"hello".to_string(),
367+
)])),
368+
..Default::default()
369+
};
370+
371+
let mut abis = HashMap::new();
372+
abis.insert("Contract".to_string(), parsed_abi);
373+
let contracts = HashMap::new();
374+
375+
let encoded = input.encoded_input(&abis, &contracts).unwrap();
376+
assert!(encoded.0.starts_with(&selector));
377+
}
378+
379+
#[test]
380+
fn test_encoded_input_uint256_array() {
381+
let raw_abi = r#"[
382+
{
383+
"inputs": [{"name": "arr", "type": "uint256[]"}],
384+
"name": "sum",
385+
"outputs": [],
386+
"stateMutability": "nonpayable",
387+
"type": "function"
388+
}
389+
]"#;
390+
391+
let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap();
392+
let selector = keccak256("sum(uint256[])".as_bytes())[0..4]
393+
.try_into()
394+
.unwrap();
395+
396+
let input = Input {
397+
instance: "Contract".to_string(),
398+
method: Method::Function(selector),
399+
calldata: Some(Calldata::Compound(vec![CalldataArg::Literal(
400+
"[1,2,3]".to_string(),
401+
)])),
402+
..Default::default()
403+
};
404+
405+
let mut abis = HashMap::new();
406+
abis.insert("Contract".to_string(), parsed_abi);
407+
let contracts = HashMap::new();
408+
409+
let encoded = input.encoded_input(&abis, &contracts).unwrap();
410+
assert!(encoded.0.starts_with(&selector));
411+
}
412+
413+
#[test]
414+
fn test_encoded_input_address() {
415+
let raw_abi = r#"[
416+
{
417+
"inputs": [{"name": "recipient", "type": "address"}],
418+
"name": "send",
419+
"outputs": [],
420+
"stateMutability": "nonpayable",
421+
"type": "function"
422+
}
423+
]"#;
424+
425+
let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap();
426+
let selector = keccak256("send(address)".as_bytes())[0..4]
427+
.try_into()
428+
.unwrap();
429+
430+
let input = Input {
431+
instance: "Contract".to_string(),
432+
method: Method::Function(selector),
433+
calldata: Some(Calldata::Compound(vec![CalldataArg::Literal(
434+
"0x1000000000000000000000000000000000000001".to_string(),
435+
)])),
436+
..Default::default()
437+
};
438+
439+
let mut abis = HashMap::new();
440+
abis.insert("Contract".to_string(), parsed_abi);
441+
let contracts = HashMap::new();
442+
443+
let encoded = input.encoded_input(&abis, &contracts).unwrap();
444+
assert!(encoded.0.starts_with(&selector));
445+
446+
type T = (alloy_primitives::Address,);
447+
let decoded: T = T::abi_decode(&encoded.0[4..]).unwrap();
448+
assert_eq!(
449+
decoded.0,
450+
address!("0x1000000000000000000000000000000000000001")
451+
);
452+
}
453+
}

0 commit comments

Comments
 (0)