Skip to content

Commit 54e7234

Browse files
authored
refactor: Port ErrorInferrer and SolidityTracer from Hardhat (#593)
* Start porting SolidityTracer * Start porting ErrorInferrer * WIP: Keep Reference in MessageTraces * Port 99% of the ErrorInferrer Except `ErrorInferrer.inferAfterTracing`. I discovered that the original code creates a shallow copy of the stack trace during select heuristics and returns the modified one or the *original* one if the heuristic does not hit. One could keep track of possible series of changes that would have to be subsequently applied if the heuristic hits and returns a modified the stack trace but instead, I want the code to do exactly what the original code did. Rather than wrap every entry in a shareable `Rc` (they are not modified), I decided to make them clonable in the next PR, which means we need to not keep the `ClassInstance` around to derive the Clone trait. * Port mapped-inlined-internal-functions-heuristics.ts * refactor: Prune StackTraceEntry.message to make it clonable See previous commit "Port 99% of the ErrorInferrer". * refactor: Drop unused inner funcs in the ErrorInferrer * feat: Finish porting `ErrorInferrer` * feat: Finish porting `SolidityTracer` * refactor: Remove now unused utils IntoEither/IntoOption * patch(hardhat): Port ErrorInferrer and SolidityTracer * fixup! refactor: Prune StackTraceEntry.message to make it clonable * fixup: Reformat * Document the unsafety surrounding napi-rs's References * refactor: Remove some now unused functions * Address review feedback * fix a missing period * fix alphabetical sorting
1 parent e9f7484 commit 54e7234

21 files changed

+7435
-849
lines changed

Cargo.lock

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/edr_napi/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ edition = "2021"
77
crate-type = ["cdylib"]
88

99
[dependencies]
10+
alloy-dyn-abi = { version = "0.7.6", default-features = false }
11+
alloy-json-abi = { version = "0.7.4", default-features = false }
1012
alloy-sol-types = { version = "0.5.1", default-features = false, features = ["std"] }
1113
ansi_term = { version = "0.12.1", default-features = false }
1214
crossbeam-channel = { version = "0.5.6", default-features = false }
@@ -32,6 +34,7 @@ tracing-subscriber = { version = "0.3.18", default-features = false, features =
3234
parking_lot = { version = "0.12.1", default-features = false }
3335
lazy_static = { version = "1.4.0", features = [] }
3436
rand = { version = "0.8.4", optional = true }
37+
semver = "1.0.22"
3538
serde = { version = "1.0.189", features = ["derive"] }
3639
strum = { version = "0.26.0", features = ["derive"] }
3740
mimalloc = { version = "0.1.39", default-features = false, features = ["local_dynamic_tls"] }

crates/edr_napi/index.d.ts

+17-8
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,11 @@ export interface SourceMap {
387387
location: SourceMapLocation
388388
jumpType: JumpType
389389
}
390+
export interface SubmessageData {
391+
messageTrace: PrecompileMessageTrace | CallMessageTrace | CreateMessageTrace
392+
stacktrace: SolidityStackTrace
393+
stepIndex: number
394+
}
390395
/** Represents the exit code of the EVM. */
391396
export const enum ExitCode {
392397
/** Execution was successful. */
@@ -770,7 +775,7 @@ export interface PrecompileErrorStackTraceEntry {
770775
}
771776
export interface RevertErrorStackTraceEntry {
772777
type: StackTraceEntryType.REVERT_ERROR
773-
message: ReturnData
778+
returnData: Uint8Array
774779
sourceReference: SourceReference
775780
isInvalidOpcodeError: boolean
776781
}
@@ -784,10 +789,6 @@ export interface CustomErrorStackTraceEntry {
784789
message: string
785790
sourceReference: SourceReference
786791
}
787-
export interface UnmappedSolc063RevertErrorStackTraceEntry {
788-
type: StackTraceEntryType.UNMAPPED_SOLC_0_6_3_REVERT_ERROR
789-
sourceReference?: SourceReference
790-
}
791792
export interface FunctionNotPayableErrorStackTraceEntry {
792793
type: StackTraceEntryType.FUNCTION_NOT_PAYABLE_ERROR
793794
value: bigint
@@ -833,21 +834,25 @@ export interface DirectLibraryCallErrorStackTraceEntry {
833834
}
834835
export interface UnrecognizedCreateErrorStackTraceEntry {
835836
type: StackTraceEntryType.UNRECOGNIZED_CREATE_ERROR
836-
message: ReturnData
837+
returnData: Uint8Array
837838
sourceReference?: undefined
838839
isInvalidOpcodeError: boolean
839840
}
840841
export interface UnrecognizedContractErrorStackTraceEntry {
841842
type: StackTraceEntryType.UNRECOGNIZED_CONTRACT_ERROR
842843
address: Uint8Array
843-
message: ReturnData
844+
returnData: Uint8Array
844845
sourceReference?: undefined
845846
isInvalidOpcodeError: boolean
846847
}
847848
export interface OtherExecutionErrorStackTraceEntry {
848849
type: StackTraceEntryType.OTHER_EXECUTION_ERROR
849850
sourceReference?: SourceReference
850851
}
852+
export interface UnmappedSolc063RevertErrorStackTraceEntry {
853+
type: StackTraceEntryType.UNMAPPED_SOLC_0_6_3_REVERT_ERROR
854+
sourceReference?: SourceReference
855+
}
851856
export interface ContractTooLargeErrorStackTraceEntry {
852857
type: StackTraceEntryType.CONTRACT_TOO_LARGE_ERROR
853858
sourceReference?: SourceReference
@@ -1010,12 +1015,16 @@ export class ReturnData {
10101015
readonly value: Uint8Array
10111016
constructor(value: Uint8Array)
10121017
isEmpty(): boolean
1013-
matchesSelector(selector: Uint8Array): boolean
10141018
isErrorReturnData(): boolean
10151019
isPanicReturnData(): boolean
10161020
decodeError(): string
10171021
decodePanic(): bigint
10181022
}
1023+
export class SolidityTracer {
1024+
1025+
constructor()
1026+
getStackTrace(trace: PrecompileMessageTrace | CallMessageTrace | CreateMessageTrace): SolidityStackTrace
1027+
}
10191028
export class VmTraceDecoder {
10201029
constructor(contractsIdentifier: ContractsIdentifier)
10211030
addBytecode(bytecode: Bytecode): void

crates/edr_napi/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ if (!nativeBinding) {
310310
throw new Error(`Failed to load native binding`)
311311
}
312312

313-
const { SpecId, EdrContext, MineOrdering, Provider, Response, SuccessReason, ExceptionalHalt, createModelsAndDecodeBytecodes, linkHexStringBytecode, SourceFile, SourceLocation, ContractFunctionType, ContractFunctionVisibility, ContractFunction, CustomError, Instruction, JumpType, jumpTypeToString, Bytecode, ContractType, Contract, ContractsIdentifier, Exit, ExitCode, Opcode, opcodeToString, isPush, isJump, isCall, isCreate, ReturnData, StackTraceEntryType, stackTraceEntryTypeToString, FALLBACK_FUNCTION_NAME, RECEIVE_FUNCTION_NAME, CONSTRUCTOR_FUNCTION_NAME, UNRECOGNIZED_FUNCTION_NAME, UNKNOWN_FUNCTION_NAME, PRECOMPILE_FUNCTION_NAME, UNRECOGNIZED_CONTRACT_NAME, VmTraceDecoder, initializeVmTraceDecoder, VmTracer, RawTrace } = nativeBinding
313+
const { SpecId, EdrContext, MineOrdering, Provider, Response, SuccessReason, ExceptionalHalt, createModelsAndDecodeBytecodes, linkHexStringBytecode, SourceFile, SourceLocation, ContractFunctionType, ContractFunctionVisibility, ContractFunction, CustomError, Instruction, JumpType, jumpTypeToString, Bytecode, ContractType, Contract, ContractsIdentifier, Exit, ExitCode, Opcode, opcodeToString, isPush, isJump, isCall, isCreate, ReturnData, StackTraceEntryType, stackTraceEntryTypeToString, FALLBACK_FUNCTION_NAME, RECEIVE_FUNCTION_NAME, CONSTRUCTOR_FUNCTION_NAME, UNRECOGNIZED_FUNCTION_NAME, UNKNOWN_FUNCTION_NAME, PRECOMPILE_FUNCTION_NAME, UNRECOGNIZED_CONTRACT_NAME, SolidityTracer, VmTraceDecoder, initializeVmTraceDecoder, VmTracer, RawTrace } = nativeBinding
314314

315315
module.exports.SpecId = SpecId
316316
module.exports.EdrContext = EdrContext
@@ -352,6 +352,7 @@ module.exports.UNRECOGNIZED_FUNCTION_NAME = UNRECOGNIZED_FUNCTION_NAME
352352
module.exports.UNKNOWN_FUNCTION_NAME = UNKNOWN_FUNCTION_NAME
353353
module.exports.PRECOMPILE_FUNCTION_NAME = PRECOMPILE_FUNCTION_NAME
354354
module.exports.UNRECOGNIZED_CONTRACT_NAME = UNRECOGNIZED_CONTRACT_NAME
355+
module.exports.SolidityTracer = SolidityTracer
355356
module.exports.VmTraceDecoder = VmTraceDecoder
356357
module.exports.initializeVmTraceDecoder = initializeVmTraceDecoder
357358
module.exports.VmTracer = VmTracer

crates/edr_napi/src/trace.rs

+3
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@ mod model;
2222
mod source_map;
2323

2424
mod contracts_identifier;
25+
mod error_inferrer;
2526
mod exit;
27+
mod mapped_inlined_internal_functions_heuristics;
2628
mod message_trace;
2729
mod opcodes;
2830
mod return_data;
2931
mod solidity_stack_trace;
32+
mod solidity_tracer;
3033
mod vm_trace_decoder;
3134
mod vm_tracer;
3235

crates/edr_napi/src/trace/compiler.rs

+6-17
Original file line numberDiff line numberDiff line change
@@ -326,14 +326,8 @@ fn process_function_definition_ast_node(
326326

327327
let param_types = matching_function_abi
328328
.as_ref()
329-
.and_then(|matching_function_abi| {
330-
matching_function_abi.inputs.as_ref().map(|inputs| {
331-
inputs
332-
.iter()
333-
.map(|input| input["type"].as_str().unwrap())
334-
.collect::<Vec<_>>()
335-
})
336-
});
329+
.and_then(|abi| abi.inputs.as_ref())
330+
.cloned();
337331

338332
let contract_func = ContractFunction {
339333
name: node["name"].as_str().unwrap().to_string(),
@@ -343,7 +337,7 @@ fn process_function_definition_ast_node(
343337
visibility: Some(visibility),
344338
is_payable: Some(node["stateMutability"].as_str().unwrap() == "payable"),
345339
selector: selector.map(Uint8Array::from),
346-
param_types: Some(param_types.into_iter().map(Into::into).collect()),
340+
param_types,
347341
}
348342
.into_instance(env)?;
349343
let contract_func = Rc::new(ClassInstanceRef::from_obj(contract_func, env)?);
@@ -414,13 +408,8 @@ fn process_variable_declaration_ast_node(
414408

415409
let param_types = getter_abi
416410
.as_ref()
417-
.and_then(|getter_abi| getter_abi.inputs.as_ref())
418-
.map(|inputs| {
419-
inputs
420-
.iter()
421-
.map(|input| input["type"].as_str().unwrap())
422-
.collect::<Vec<_>>()
423-
});
411+
.and_then(|abi| abi.inputs.as_ref())
412+
.cloned();
424413

425414
let contract_func = ContractFunction {
426415
name: node["name"].as_str().unwrap().to_string(),
@@ -432,7 +421,7 @@ fn process_variable_declaration_ast_node(
432421
selector: Some(Uint8Array::from(
433422
get_public_variable_selector_from_declaration_ast_node(node)?,
434423
)),
435-
param_types: Some(param_types.into_iter().map(Into::into).collect()),
424+
param_types,
436425
}
437426
.into_instance(env)?;
438427
let contract_func = Rc::new(ClassInstanceRef::from_obj(contract_func, env)?);

0 commit comments

Comments
 (0)