Skip to content

Commit 6f2ecd1

Browse files
authored
fix(js-tracer): correctly handle isPrecompiled and slice builtins (#363)
Closes #350 Closes #349 - Fixes `isPrecompiled` built-in to work correctly. Before we were incorrectly checking `precompiles_registered` flag which caused the fn to never be injected. Added test for it as well - Adds `slice` built-in and a test for it
1 parent 2a4ae16 commit 6f2ecd1

File tree

3 files changed

+75
-3
lines changed

3 files changed

+75
-3
lines changed

src/tracing/js/bindings.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -803,11 +803,15 @@ impl EvmDbRef {
803803

804804
fn read_code(&self, address: JsValue, ctx: &mut Context) -> JsResult<JsUint8Array> {
805805
let acc = self.read_basic(address, ctx)?;
806-
let code_hash = acc.map(|acc| acc.code_hash).unwrap_or(KECCAK_EMPTY);
806+
let code_hash = acc.as_ref().map(|acc| acc.code_hash).unwrap_or(KECCAK_EMPTY);
807807
if code_hash == KECCAK_EMPTY {
808808
return JsUint8Array::from_iter(core::iter::empty(), ctx);
809809
}
810810

811+
if let Some(bytecode) = acc.as_ref().and_then(|acc| acc.code.as_ref()) {
812+
return to_uint8_array(bytecode.original_bytes().to_vec(), ctx);
813+
}
814+
811815
let Some(Ok(bytecode)) = self.inner.db.0.with_inner(|db| db.code_by_hash_ref(code_hash))
812816
else {
813817
return Err(JsError::from_native(

src/tracing/js/builtins.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ pub(crate) fn register_builtins(ctx: &mut Context) -> JsResult<()> {
8787
3,
8888
NativeFunction::from_fn_ptr(to_contract2),
8989
)?;
90+
ctx.register_global_callable(js_string!("slice"), 3, NativeFunction::from_fn_ptr(slice))?;
9091

9192
Ok(())
9293
}
@@ -307,6 +308,26 @@ fn hex_decode_js_string(js_string: &JsString) -> JsResult<Vec<u8>> {
307308
}
308309
}
309310

311+
/// Returns a slice of the given value.
312+
pub(crate) fn slice(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult<JsValue> {
313+
let val = args.get_or_undefined(0).clone();
314+
315+
let buf = bytes_from_value(val, ctx)?;
316+
let start = args.get_or_undefined(1).to_number(ctx)? as usize;
317+
let end = args.get_or_undefined(2).to_number(ctx)? as usize;
318+
319+
if start > end || end > buf.len() {
320+
Err(JsError::from_native(JsNativeError::error().with_message(format!(
321+
"Tracer accessed out of bound memory: available {}, start {}, end {}",
322+
buf.len(),
323+
start,
324+
end
325+
))))
326+
} else {
327+
to_uint8_array_value(buf[start..end].iter().copied(), ctx)
328+
}
329+
}
330+
310331
/// A container for all precompile addresses used for the `isPrecompiled` global callable.
311332
#[derive(Clone, Debug)]
312333
pub(crate) struct PrecompileList(pub(crate) HashSet<Address>);

src/tracing/js/mod.rs

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ impl JsInspector {
410410

411411
/// Registers the precompiles in the JS context
412412
fn register_precompiles<CTX: ContextTr<Journal: JournalExt>>(&mut self, context: &mut CTX) {
413-
if !self.precompiles_registered {
413+
if self.precompiles_registered {
414414
return;
415415
}
416416
let precompiles = PrecompileList(context.journal().precompile_addresses().clone());
@@ -692,7 +692,7 @@ fn js_error_to_revert(err: JsError) -> InterpreterResult {
692692
mod tests {
693693
use super::*;
694694

695-
use alloy_primitives::{hex, Address};
695+
use alloy_primitives::{bytes, hex, Address};
696696
use revm::{
697697
context::TxEnv,
698698
database::CacheDB,
@@ -975,4 +975,51 @@ mod tests {
975975
vec![0, 3, 3]
976976
);
977977
}
978+
979+
#[test]
980+
fn test_slice_builtin() {
981+
let code = r#"{
982+
res: [],
983+
step: function(log) {
984+
// Test slicing a hex string
985+
var hex = '0xdeadbeefcafe';
986+
this.res.push(toHex(slice(hex, 0, 2)));
987+
this.res.push(toHex(slice(hex, 2, 4)));
988+
this.res.push(toHex(slice(hex, 4, 6)));
989+
990+
// Test slicing an array
991+
var arr = [0x01, 0x02, 0x03, 0x04, 0x05];
992+
this.res.push(toHex(slice(arr, 0, 3)));
993+
this.res.push(toHex(slice(arr, 1, 4)));
994+
995+
// Test slicing a Uint8Array
996+
var uint8 = new Uint8Array([0xff, 0xee, 0xdd, 0xcc, 0xbb]);
997+
this.res.push(toHex(slice(uint8, 0, 2)));
998+
this.res.push(toHex(slice(uint8, 2, 5)));
999+
},
1000+
fault: function() {},
1001+
result: function() { return this.res }
1002+
}"#;
1003+
let res = run_trace(code, Some(bytes!("0x00")), true);
1004+
assert_eq!(
1005+
res,
1006+
json!(["0xdead", "0xbeef", "0xcafe", "0x010203", "0x020304", "0xffee", "0xddccbb"])
1007+
);
1008+
}
1009+
1010+
#[test]
1011+
fn test_is_precompiled_builtin() {
1012+
let code = r#"{
1013+
res: [],
1014+
step: function(log) {
1015+
this.res.push(isPrecompiled("0x01"));
1016+
this.res.push(isPrecompiled("0x0000000000000000000000000000000000000002"));
1017+
this.res.push(isPrecompiled("0x0000000000000000000000000000000000000000"));
1018+
},
1019+
fault: function() {},
1020+
result: function() { return this.res }
1021+
}"#;
1022+
let res = run_trace(code, Some(bytes!("0x00")), true);
1023+
assert_eq!(res, json!([true, true, false]));
1024+
}
9781025
}

0 commit comments

Comments
 (0)