-
Notifications
You must be signed in to change notification settings - Fork 28
Normalise rpc debug trace format #839
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
d3206e9
8882efc
745b094
17948b3
aa51ae6
d7a49de
75638d1
f346eb0
567fef3
746f8bf
3af4ced
6725885
eafe808
1c0f167
68d1998
d4ffe6f
090729a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@nomicfoundation/edr": minor | ||
--- | ||
|
||
Normalise JSON-RPC format for rpcDebugTrace on the EDR side. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
use core::fmt::Debug; | ||
use std::collections::HashMap; | ||
|
||
use edr_eth::{BlockSpec, B256}; | ||
use edr_evm::{state::StateOverrides, trace::Trace, DebugTraceResult, DebugTraceResultWithTraces}; | ||
|
@@ -19,7 +20,7 @@ pub fn handle_debug_trace_transaction<LoggerErrorT: Debug, TimerT: Clone + TimeS | |
data: &mut ProviderData<LoggerErrorT, TimerT>, | ||
transaction_hash: B256, | ||
config: Option<DebugTraceConfig>, | ||
) -> Result<(DebugTraceResult, Vec<Trace>), ProviderError<LoggerErrorT>> { | ||
) -> Result<(RpcDebugTraceResult, Vec<Trace>), ProviderError<LoggerErrorT>> { | ||
let DebugTraceResultWithTraces { result, traces } = data | ||
.debug_trace_transaction( | ||
&transaction_hash, | ||
|
@@ -32,6 +33,8 @@ pub fn handle_debug_trace_transaction<LoggerErrorT: Debug, TimerT: Clone + TimeS | |
_ => error, | ||
})?; | ||
|
||
let result = normalise_rpc_debug_trace(result); | ||
|
||
Ok((result, traces)) | ||
} | ||
|
||
|
@@ -40,7 +43,7 @@ pub fn handle_debug_trace_call<LoggerErrorT: Debug, TimerT: Clone + TimeSinceEpo | |
call_request: CallRequest, | ||
block_spec: Option<BlockSpec>, | ||
config: Option<DebugTraceConfig>, | ||
) -> Result<(DebugTraceResult, Vec<Trace>), ProviderError<LoggerErrorT>> { | ||
) -> Result<(RpcDebugTraceResult, Vec<Trace>), ProviderError<LoggerErrorT>> { | ||
let block_spec = resolve_block_spec_for_call_request(block_spec); | ||
validate_call_request(data.spec_id(), &call_request, &block_spec)?; | ||
|
||
|
@@ -53,6 +56,8 @@ pub fn handle_debug_trace_call<LoggerErrorT: Debug, TimerT: Clone + TimeSinceEpo | |
config.map(Into::into).unwrap_or_default(), | ||
)?; | ||
|
||
let result = normalise_rpc_debug_trace(result); | ||
|
||
Ok((result, traces)) | ||
} | ||
|
||
|
@@ -120,3 +125,108 @@ impl From<DebugTraceConfig> for edr_evm::DebugTraceConfig { | |
} | ||
} | ||
} | ||
|
||
/// This is the JSON-RPC Debug trace format | ||
#[derive(Debug, Clone, serde::Serialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct RpcDebugTraceResult { | ||
pub failed: bool, | ||
pub gas: u64, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did the old format require a Do you have a reference to support this choice? If we should support a hex representation of a quantity, you should use This applies to all There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes so I used There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Noted the point on |
||
// Adding pass and gass used since Hardhat tests still | ||
// depend on them | ||
pub pass: bool, | ||
pub gas_used: u64, | ||
pub return_value: String, | ||
pub struct_logs: Vec<RpcDebugTraceLogItem>, | ||
} | ||
|
||
#[derive(Debug, Clone, serde::Serialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct RpcDebugTraceLogItem { | ||
/// Program Counter | ||
pub pc: u64, | ||
/// Name of the operation | ||
pub op: String, | ||
/// Name of the operation (Needed for Hardhat tests) | ||
pub op_name: String, | ||
/// Gas left before executing this operation as hex number. | ||
pub gas: u64, | ||
/// Gas cost of this operation as hex number. | ||
pub gas_cost: u64, | ||
/// Array of all values (hex numbers) on the stack | ||
#[serde(skip_serializing_if = "Option::is_none")] | ||
pub stack: Option<Vec<String>>, | ||
/// Depth of the call stack | ||
pub depth: u64, | ||
/// Size of memory array | ||
pub mem_size: u64, | ||
/// Description of an error as a hex string. | ||
#[serde(skip_serializing_if = "Option::is_none")] | ||
pub error: Option<String>, | ||
/// Array of all allocated values as hex strings. | ||
#[serde(skip_serializing_if = "Option::is_none")] | ||
pub memory: Option<Vec<String>>, | ||
/// Map of all stored values with keys and values encoded as hex strings. | ||
#[serde(skip_serializing_if = "Option::is_none")] | ||
pub storage: Option<HashMap<String, String>>, | ||
} | ||
|
||
// Rust port of https://github.com/NomicFoundation/hardhat/blob/024d72b09c6edefb00c012e9514a0948c255d0ab/v-next/hardhat/src/internal/builtin-plugins/network-manager/edr/utils/convert-to-edr.ts#L176 | ||
/// This normalization is done because this is the format Hardhat expects | ||
fn normalise_rpc_debug_trace(trace: DebugTraceResult) -> RpcDebugTraceResult { | ||
let mut struct_logs = Vec::new(); | ||
|
||
for log in trace.logs { | ||
let rpc_log = RpcDebugTraceLogItem { | ||
pc: log.pc, | ||
op: log.op_name.clone(), | ||
op_name: log.op_name, | ||
gas: u64::from_str_radix(log.gas.trim_start_matches("0x"), 16).unwrap_or(0), | ||
gas_cost: u64::from_str_radix(log.gas_cost.trim_start_matches("0x"), 16).unwrap_or(0), | ||
stack: log.stack.map(|values| { | ||
values | ||
.into_iter() | ||
// Removing this trim temporarily as the Hardhat test assumes 0x is there | ||
// .map(|value| value.trim_start_matches("0x").to_string()) | ||
.collect() | ||
}), | ||
depth: log.depth, | ||
mem_size: log.mem_size, | ||
error: log.error, | ||
memory: log.memory, | ||
storage: log.storage.map(|storage| { | ||
storage | ||
.into_iter() | ||
// Removing this trim temporarily as the Hardhat test assumes 0x is there | ||
// .map(|(key, value)| { | ||
// let stripped_key = key.strip_prefix("0x").unwrap_or(&key).to_string(); | ||
// let stripped_value = | ||
// value.strip_prefix("0x").unwrap_or(&value).to_string(); | ||
// (stripped_key, stripped_value) | ||
// }) | ||
.collect() | ||
}), | ||
}; | ||
|
||
struct_logs.push(rpc_log); | ||
} | ||
|
||
// REVM trace adds initial STOP that Hardhat doesn't expect | ||
if !struct_logs.is_empty() && struct_logs[0].op == "STOP" { | ||
struct_logs.remove(0); | ||
} | ||
|
||
let return_value = trace | ||
.output | ||
.map(|b| b.to_string().trim_start_matches("0x").to_string()) | ||
.unwrap_or_default(); | ||
|
||
RpcDebugTraceResult { | ||
failed: !trace.pass, | ||
gas: trace.gas_used, | ||
pass: trace.pass, | ||
gas_used: trace.gas_used, | ||
return_value, | ||
struct_logs, | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are these two disabled?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disabled these two because at this moment, the normalisation is still being done in Hardhat so it is still correct to have
0x
at the start of the strings.Here is the reference in Hardhat: https://github.com/NomicFoundation/hardhat/blob/024d72b09c6edefb00c012e9514a0948c255d0ab/v-next/hardhat/src/internal/builtin-plugins/network-manager/edr/utils/convert-to-edr.ts#L197-L203
The Hardhat tests also operate on this assumption so I believe these two asserts should be disabled until the normalisation is completely removed from Hardhat.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a process for incorporating changes to Hardhat into your PR. It's explained here. That's what would be required in order to test your changes in Hardhat and make sure everything is working as expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah perfect. Thank you