Skip to content

Commit

Permalink
fix(tracing/js): toHex returns with 0x as prefix (#226)
Browse files Browse the repository at this point in the history
1. go-ethereum/erigon's `toHex` returns with `0x` prefix(ref
https://github.com/ethereum/go-ethereum/blob/fad7e74a1b2995dd79002c184e532eff31382579/eth/tracers/js/goja.go#L467),
we should keep the same;
2. add more unit tests for the builtin functions.

---------

Signed-off-by: jsvisa <delweng@gmail.com>
  • Loading branch information
jsvisa authored Oct 17, 2024
1 parent aa6471e commit fb4ee4e
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 12 deletions.
101 changes: 95 additions & 6 deletions src/tracing/js/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,10 +208,15 @@ pub(crate) fn to_bigint(value: U256, ctx: &mut Context) -> JsResult<JsValue> {
ctx,
)
}
/// Takes three arguments: a JavaScript value that represents the sender's address, a string salt
/// value, and the initcode for the contract. Compute the address of a contract created by the
/// sender with the given salt and code hash, then converts the resulting address back into a byte
/// buffer for output.

/// Compute the address of a contract created using CREATE2.
///
/// Arguments:
/// 1. creator: The address of the contract creator
/// 2. salt: A 32-byte salt value
/// 3. initcode: The contract's initialization code
///
/// Returns: The computed contract address as an ArrayBuffer
pub(crate) fn to_contract2(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult<JsValue> {
// Extract the sender's address, salt and initcode from the arguments
let from = args.get_or_undefined(0).clone();
Expand Down Expand Up @@ -243,7 +248,13 @@ pub(crate) fn to_contract2(_: &JsValue, args: &[JsValue], ctx: &mut Context) ->
address_to_byte_array_value(contract_addr, ctx)
}

/// Converts the sender's address to a byte buffer
/// Compute the address of a contract created by the sender with the given nonce.
///
/// Arguments:
/// 1. from: The address of the contract creator
/// 2. nonce: The creator's transaction count (optional, none is 0)
///
/// Returns: The computed contract address as an ArrayBuffer
pub(crate) fn to_contract(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult<JsValue> {
// Extract the sender's address and nonce from the arguments
let from = args.get_or_undefined(0).clone();
Expand Down Expand Up @@ -280,7 +291,7 @@ pub(crate) fn to_word(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsRes
pub(crate) fn to_hex(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult<JsValue> {
let val = args.get_or_undefined(0).clone();
let buf = from_buf_value(val, ctx)?;
let s = js_string!(hex::encode(buf));
let s = js_string!(hex::encode_prefixed(buf));
Ok(JsValue::from(s))
}

Expand Down Expand Up @@ -342,4 +353,82 @@ mod tests {
big_int.as_callable().unwrap().call(&JsValue::undefined(), &[value], &mut ctx).unwrap();
assert_eq!(result.to_string(&mut ctx).unwrap().to_std_string().unwrap(), "100");
}

fn as_length<T>(array: T) -> usize
where
T: Borrow<JsValue>,
{
let array = array.borrow();
let array = array.as_object().unwrap();
let array = JsUint8Array::from_object(array.clone()).unwrap();
array.length(&mut Context::default()).unwrap()
}

#[test]
fn test_to_hex() {
let mut ctx = Context::default();
let value = JsValue::from(js_string!("0xdeadbeef"));
let result = to_hex(&JsValue::undefined(), &[value], &mut ctx).unwrap();
assert_eq!(result.to_string(&mut ctx).unwrap().to_std_string().unwrap(), "0xdeadbeef");
}

#[test]
fn test_to_address() {
let mut ctx = Context::default();
let value = JsValue::from(js_string!("0xdeadbeef"));
let result = to_address(&JsValue::undefined(), &[value], &mut ctx).unwrap();
assert_eq!(as_length(&result), 20);
assert_eq!(
result.to_string(&mut ctx).unwrap().to_std_string().unwrap(),
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,222,173,190,239"
);
}

#[test]
fn test_to_word() {
let mut ctx = Context::default();
let value = JsValue::from(js_string!("0xdeadbeef"));
let result = to_word(&JsValue::undefined(), &[value], &mut ctx).unwrap();
assert_eq!(as_length(&result), 32);
assert_eq!(
result.to_string(&mut ctx).unwrap().to_std_string().unwrap(),
"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,222,173,190,239"
);
}

#[test]
fn test_to_contract() {
let mut ctx = Context::default();
let from = JsValue::from(js_string!("0xdeadbeef"));
let nonce = JsValue::from(0);
let result = to_contract(&JsValue::undefined(), &[from.clone(), nonce], &mut ctx).unwrap();
assert_eq!(as_length(&result), 20);
let addr = to_hex(&JsValue::undefined(), &[result], &mut ctx).unwrap();
assert_eq!(
addr.to_string(&mut ctx).unwrap().to_std_string().unwrap(),
"0xe8279be14e9fe2ad2d8e52e42ca96fb33a813bbe",
);

// without nonce
let result = to_contract(&JsValue::undefined(), &[from], &mut ctx).unwrap();
let addr = to_hex(&JsValue::undefined(), &[result], &mut ctx).unwrap();
assert_eq!(
addr.to_string(&mut ctx).unwrap().to_std_string().unwrap(),
"0xe8279be14e9fe2ad2d8e52e42ca96fb33a813bbe",
);
}
#[test]
fn test_to_contract2() {
let mut ctx = Context::default();
let from = JsValue::from(js_string!("0xdeadbeef"));
let salt = JsValue::from(js_string!("0xdead4a17"));
let code = JsValue::from(js_string!("0xdeadbeef"));
let result = to_contract2(&JsValue::undefined(), &[from, salt, code], &mut ctx).unwrap();
assert_eq!(as_length(&result), 20);
let addr = to_hex(&JsValue::undefined(), &[result], &mut ctx).unwrap();
assert_eq!(
addr.to_string(&mut ctx).unwrap().to_std_string().unwrap(),
"0x8a0d8a428b30200a296dfbe693310e5d6d2c64c5"
);
}
}
17 changes: 11 additions & 6 deletions tests/it/geth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,23 +73,28 @@ fn test_geth_calltracer_logs() {
.geth_builder()
.geth_call_traces(CallConfig::default().with_log(), res.result.gas_used());

// three subcalls
// top-level call succeeded, no log and three subcalls
assert_eq!(call_frame.calls.len(), 3);

// top-level call emitted one log
assert_eq!(call_frame.logs.len(), 1);
assert!(call_frame.error.is_none());

// first call failed, no logs
// first subcall failed, and no logs
assert!(call_frame.calls[0].logs.is_empty());
assert!(call_frame.calls[0].error.is_some());

// second call failed, with a two nested subcalls that emitted logs, but none should be included
// second subcall failed, with a two nested subcalls that emitted logs, but none should be
// included
assert_eq!(call_frame.calls[1].calls.len(), 1);
assert!(call_frame.calls[1].logs.is_empty());
assert!(call_frame.calls[1].error.is_some());
assert!(call_frame.calls[1].calls[0].logs.is_empty());
assert!(call_frame.calls[1].calls[0].error.is_none());
assert!(call_frame.calls[1].calls[0].calls[0].logs.is_empty());
assert!(call_frame.calls[1].calls[0].calls[0].error.is_none());

// third call succeeded, one log
// third subcall succeeded, one log
assert_eq!(call_frame.calls[2].logs.len(), 1);
assert!(call_frame.calls[2].error.is_none());
}

#[test]
Expand Down

0 comments on commit fb4ee4e

Please sign in to comment.