Skip to content

Commit

Permalink
chore(tracing/js): add more unit tests (#231)
Browse files Browse the repository at this point in the history
  • Loading branch information
jsvisa and mattsse authored Oct 17, 2024
1 parent 1e06993 commit 873b15a
Showing 1 changed file with 242 additions and 0 deletions.
242 changes: 242 additions & 0 deletions src/tracing/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,17 @@ fn js_error_to_revert(err: JsError) -> InterpreterResult {
mod tests {
use super::*;

use alloy_primitives::{hex, Address};
use revm::{
db::{CacheDB, EmptyDB},
inspector_handle_register,
primitives::{
AccountInfo, BlockEnv, Bytecode, CfgEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg,
HandlerCfg, SpecId, TransactTo, TxEnv,
},
};
use serde_json::json;

#[test]
fn test_loop_iteration_limit() {
let mut context = Context::default();
Expand All @@ -664,4 +675,235 @@ mod tests {
let result = JsInspector::new(code.to_string(), config);
assert!(matches!(result, Err(JsInspectorError::FaultFunctionMissing)));
}

// Helper function to run a trace and return the result
fn run_trace(code: &str, contract: Option<Bytes>, success: bool) -> serde_json::Value {
let addr = Address::repeat_byte(0x01);
let mut db = CacheDB::new(EmptyDB::default());

// Insert the caller
db.insert_account_info(
Address::ZERO,
AccountInfo { balance: U256::from(1e18), ..Default::default() },
);
// Insert the contract
db.insert_account_info(
addr,
AccountInfo {
code: Some(Bytecode::LegacyRaw(
/* PUSH1 1, PUSH1 1, STOP */
contract.unwrap_or_else(|| hex!("6001600100").into()),
)),
..Default::default()
},
);

let cfg = CfgEnvWithHandlerCfg::new(CfgEnv::default(), HandlerCfg::new(SpecId::CANCUN));
let env = EnvWithHandlerCfg::new_with_cfg_env(
cfg.clone(),
BlockEnv::default(),
TxEnv {
gas_price: U256::from(1024),
gas_limit: 1_000_000,
transact_to: TransactTo::Call(addr),
..Default::default()
},
);

let mut insp = JsInspector::new(code.to_string(), serde_json::Value::Null).unwrap();

let res = revm::Evm::builder()
.with_db(db.clone())
.with_external_context(&mut insp)
.with_env_with_handler_cfg(env.clone())
.append_handler_register(inspector_handle_register)
.build()
.transact()
.unwrap();

assert_eq!(res.result.is_success(), success);
insp.json_result(res, &env, &db).unwrap()
}

#[test]
fn test_general_counting() {
let code = r#"{
count: 0,
step: function() { this.count += 1; },
fault: function() {},
result: function() { return this.count; }
}"#;
let res = run_trace(code, None, true);
assert_eq!(res.as_u64().unwrap(), 3);
}

#[test]
fn test_memory_access() {
let code = r#"{
depths: [],
step: function(log) { this.depths.push(log.memory.slice(-1,-2)); },
fault: function() {},
result: function() { return this.depths; }
}"#;
let res = run_trace(code, None, false);
assert_eq!(res.as_array().unwrap().len(), 0);
}

#[test]
fn test_stack_peek() {
let code = r#"{
depths: [],
step: function(log) { this.depths.push(log.stack.peek(-1)); },
fault: function() {},
result: function() { return this.depths; }
}"#;
let res = run_trace(code, None, false);
assert_eq!(res.as_array().unwrap().len(), 0);
}

#[test]
fn test_memory_get_uint() {
let code = r#"{
depths: [],
step: function(log, db) { this.depths.push(log.memory.getUint(-64)); },
fault: function() {},
result: function() { return this.depths; }
}"#;
let res = run_trace(code, None, false);
assert_eq!(res.as_array().unwrap().len(), 0);
}

#[test]
fn test_stack_depth() {
let code = r#"{
depths: [],
step: function(log) { this.depths.push(log.stack.length()); },
fault: function() {},
result: function() { return this.depths; }
}"#;
let res = run_trace(code, None, true);
assert_eq!(res, json!([0, 1, 2]));
}

#[test]
fn test_memory_length() {
let code = r#"{
lengths: [],
step: function(log) { this.lengths.push(log.memory.length()); },
fault: function() {},
result: function() { return this.lengths; }
}"#;
let res = run_trace(code, None, true);
assert_eq!(res, json!([0, 0, 0]));
}

#[test]
fn test_opcode_to_string() {
let code = r#"{
opcodes: [],
step: function(log) { this.opcodes.push(log.op.toString()); },
fault: function() {},
result: function() { return this.opcodes; }
}"#;
let res = run_trace(code, None, true);
assert_eq!(res, json!(["PUSH1", "PUSH1", "STOP"]));
}

#[test]
fn test_gas_used() {
let code = r#"{
depths: [],
step: function() {},
fault: function() {},
result: function(ctx) { return ctx.gasPrice+'.'+ctx.gasUsed; }
}"#;
let res = run_trace(code, None, true);
assert_eq!(res.as_str().unwrap(), "1024.21006");
}

#[test]
fn test_to_word() {
let code = r#"{
res: null,
step: function(log) {},
fault: function() {},
result: function() { return toWord('0xffaa') }
}"#;
let res = run_trace(code, None, true);
assert_eq!(
res,
json!({
"0": 0, "1": 0, "2": 0, "3": 0, "4": 0, "5": 0, "6": 0, "7": 0, "8": 0,
"9": 0, "10": 0, "11": 0, "12": 0, "13": 0, "14": 0, "15": 0, "16": 0,
"17": 0, "18": 0, "19": 0, "20": 0, "21": 0, "22": 0, "23": 0, "24": 0,
"25": 0, "26": 0, "27": 0, "28": 0, "29": 0, "30": 255, "31": 170,
})
);
}

#[test]
fn test_to_address() {
let code = r#"{
res: null,
step: function(log) { var address = log.contract.getAddress(); this.res = toAddress(address); },
fault: function() {},
result: function() { return toHex(this.res) }
}"#;
let res = run_trace(code, None, true);
assert_eq!(res.as_str().unwrap(), "0x0101010101010101010101010101010101010101");
}

#[test]
fn test_to_address_string() {
let code = r#"{
res: null,
step: function(log) { var address = '0x0000000000000000000000000000000000000000'; this.res = toAddress(address); },
fault: function() {},
result: function() { return this.res }
}"#;
let res = run_trace(code, None, true);
assert_eq!(res.as_object().unwrap().values().map(|v| v.as_u64().unwrap()).sum::<u64>(), 0);
}

#[test]
fn test_memory_slice() {
let code = r#"{
res: [],
step: function(log) {
var op = log.op.toString();
if (op === 'MSTORE8' || op === 'STOP') {
this.res.push(log.memory.slice(0, 2))
}
},
fault: function() {},
result: function() { return this.res }
}"#;
let contract = hex!("60ff60005300"); // PUSH1, 0xff, PUSH1, 0x00, MSTORE8, STOP
let res = run_trace(code, Some(contract.into()), false);
assert_eq!(res, json!([]));
}

#[test]
fn test_memory_limit() {
let code = r#"{
res: [],
step: function(log) { if (log.op.toString() === 'STOP') { this.res.push(log.memory.slice(5, 1025 * 1024)) } },
fault: function() {},
result: function() { return this.res }
}"#;
let res = run_trace(code, None, false);
assert_eq!(res, json!([]));
}

#[test]
fn test_coinbase() {
let code = r#"{
lengths: [],
step: function(log) { },
fault: function() {},
result: function(ctx) { var coinbase = ctx.coinbase; return toAddress(coinbase); }
}"#;
let res = run_trace(code, None, true);
assert_eq!(res.as_object().unwrap().values().map(|v| v.as_u64().unwrap()).sum::<u64>(), 0);
}
}

0 comments on commit 873b15a

Please sign in to comment.