@nomicfoundation/edr 0.6.4 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +24 -91
- package/index.js +4 -9
- package/package.json +8 -8
- package/src/context.rs +17 -9
- package/src/logger.rs +34 -67
- package/src/provider.rs +68 -15
- package/src/trace/debug.rs +2 -262
- package/src/trace/exit.rs +7 -4
- package/src/trace/model.rs +5 -5
- package/src/trace/solidity_stack_trace.rs +220 -76
- package/src/trace.rs +11 -11
- package/src/trace/compiler.rs +0 -27
- package/src/trace/error_inferrer.rs +0 -2255
- package/src/trace/mapped_inlined_internal_functions_heuristics.rs +0 -180
- package/src/trace/message_trace.rs +0 -179
- package/src/trace/solidity_tracer.rs +0 -315
- package/src/trace/vm_trace_decoder.rs +0 -234
- package/src/trace/vm_tracer.rs +0 -71
package/index.d.ts
CHANGED
|
@@ -149,8 +149,6 @@ export interface LoggerConfig {
|
|
|
149
149
|
/** Whether to enable the logger. */
|
|
150
150
|
enable: boolean
|
|
151
151
|
decodeConsoleLogInputsCallback: (inputs: Buffer[]) => string[]
|
|
152
|
-
/** Used to resolve the contract and function name when logging. */
|
|
153
|
-
getContractAndFunctionNameCallback: (code: Buffer, calldata?: Buffer) => ContractAndFunctionName
|
|
154
152
|
printLineCallback: (message: string, replace: boolean) => void
|
|
155
153
|
}
|
|
156
154
|
/** Configuration for a chain */
|
|
@@ -346,18 +344,7 @@ export interface SubscriptionEvent {
|
|
|
346
344
|
filterId: bigint
|
|
347
345
|
result: any
|
|
348
346
|
}
|
|
349
|
-
export declare function createModelsAndDecodeBytecodes(solcVersion: string, compilerInput: any, compilerOutput: any): Array<BytecodeWrapper>
|
|
350
347
|
export declare function linkHexStringBytecode(code: string, address: string, position: number): string
|
|
351
|
-
export const enum ContractFunctionType {
|
|
352
|
-
CONSTRUCTOR = 0,
|
|
353
|
-
FUNCTION = 1,
|
|
354
|
-
FALLBACK = 2,
|
|
355
|
-
RECEIVE = 3,
|
|
356
|
-
GETTER = 4,
|
|
357
|
-
MODIFIER = 5,
|
|
358
|
-
FREE_FUNCTION = 6
|
|
359
|
-
}
|
|
360
|
-
export declare function printMessageTrace(trace: PrecompileMessageTrace | CallMessageTrace | CreateMessageTrace, depth?: number | undefined | null): void
|
|
361
348
|
export declare function printStackTrace(trace: SolidityStackTrace): void
|
|
362
349
|
/** Represents the exit code of the EVM. */
|
|
363
350
|
export const enum ExitCode {
|
|
@@ -376,53 +363,18 @@ export const enum ExitCode {
|
|
|
376
363
|
/** Create init code size exceeds limit (runtime). */
|
|
377
364
|
CODESIZE_EXCEEDS_MAXIMUM = 6,
|
|
378
365
|
/** Create collision. */
|
|
379
|
-
CREATE_COLLISION = 7
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
pc: number
|
|
366
|
+
CREATE_COLLISION = 7,
|
|
367
|
+
/** Unknown halt reason. */
|
|
368
|
+
UNKNOWN_HALT_REASON = 8
|
|
383
369
|
}
|
|
384
|
-
export
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
}
|
|
393
|
-
export interface CreateMessageTrace {
|
|
394
|
-
value: bigint
|
|
395
|
-
returnData: Uint8Array
|
|
396
|
-
exit: Exit
|
|
397
|
-
gasUsed: bigint
|
|
398
|
-
depth: number
|
|
399
|
-
code: Uint8Array
|
|
400
|
-
steps: Array<EvmStep | PrecompileMessageTrace | CallMessageTrace | CreateMessageTrace>
|
|
401
|
-
/**
|
|
402
|
-
* Reference to the resolved `Bytecode` EDR data.
|
|
403
|
-
* Only used on the JS side by the `VmTraceDecoder` class.
|
|
404
|
-
*/
|
|
405
|
-
bytecode?: BytecodeWrapper
|
|
406
|
-
numberOfSubtraces: number
|
|
407
|
-
deployedContract?: Uint8Array | undefined
|
|
408
|
-
}
|
|
409
|
-
export interface CallMessageTrace {
|
|
410
|
-
value: bigint
|
|
411
|
-
returnData: Uint8Array
|
|
412
|
-
exit: Exit
|
|
413
|
-
gasUsed: bigint
|
|
414
|
-
depth: number
|
|
415
|
-
code: Uint8Array
|
|
416
|
-
steps: Array<EvmStep | PrecompileMessageTrace | CallMessageTrace | CreateMessageTrace>
|
|
417
|
-
/**
|
|
418
|
-
* Reference to the resolved `Bytecode` EDR data.
|
|
419
|
-
* Only used on the JS side by the `VmTraceDecoder` class.
|
|
420
|
-
*/
|
|
421
|
-
bytecode?: BytecodeWrapper
|
|
422
|
-
numberOfSubtraces: number
|
|
423
|
-
calldata: Uint8Array
|
|
424
|
-
address: Uint8Array
|
|
425
|
-
codeAddress: Uint8Array
|
|
370
|
+
export const enum ContractFunctionType {
|
|
371
|
+
CONSTRUCTOR = 0,
|
|
372
|
+
FUNCTION = 1,
|
|
373
|
+
FALLBACK = 2,
|
|
374
|
+
RECEIVE = 3,
|
|
375
|
+
GETTER = 4,
|
|
376
|
+
MODIFIER = 5,
|
|
377
|
+
FREE_FUNCTION = 6
|
|
426
378
|
}
|
|
427
379
|
export const enum StackTraceEntryType {
|
|
428
380
|
CALLSTACK_ENTRY = 0,
|
|
@@ -578,11 +530,6 @@ export interface ContractCallRunOutOfGasError {
|
|
|
578
530
|
type: StackTraceEntryType.CONTRACT_CALL_RUN_OUT_OF_GAS_ERROR
|
|
579
531
|
sourceReference?: SourceReference
|
|
580
532
|
}
|
|
581
|
-
export interface ContractAndFunctionName {
|
|
582
|
-
contractName: string
|
|
583
|
-
functionName: string | undefined
|
|
584
|
-
}
|
|
585
|
-
export declare function initializeVmTraceDecoder(vmTraceDecoder: VmTraceDecoder, tracingConfig: any): void
|
|
586
533
|
export interface TracingMessage {
|
|
587
534
|
/** Sender address */
|
|
588
535
|
readonly caller: Buffer
|
|
@@ -626,6 +573,11 @@ export interface TracingMessageResult {
|
|
|
626
573
|
/** Execution result */
|
|
627
574
|
readonly executionResult: ExecutionResult
|
|
628
575
|
}
|
|
576
|
+
/**
|
|
577
|
+
* Returns the latest version of solc that EDR officially
|
|
578
|
+
* supports and is tested against.
|
|
579
|
+
*/
|
|
580
|
+
export declare function getLatestSupportedSolcVersion(): string
|
|
629
581
|
export interface Withdrawal {
|
|
630
582
|
/** The index of withdrawal */
|
|
631
583
|
index: bigint
|
|
@@ -643,7 +595,7 @@ export declare class EdrContext {
|
|
|
643
595
|
/** A JSON-RPC provider for Ethereum. */
|
|
644
596
|
export declare class Provider {
|
|
645
597
|
/**Constructs a new provider with the provided configuration. */
|
|
646
|
-
static withConfig(context: EdrContext, config: ProviderConfig, loggerConfig: LoggerConfig, subscriberCallback: (event: SubscriptionEvent) => void): Promise<Provider>
|
|
598
|
+
static withConfig(context: EdrContext, config: ProviderConfig, loggerConfig: LoggerConfig, tracingConfig: any, subscriberCallback: (event: SubscriptionEvent) => void): Promise<Provider>
|
|
647
599
|
/**Handles a JSON-RPC request and returns a JSON-RPC response. */
|
|
648
600
|
handleRequest(jsonRequest: string): Promise<Response>
|
|
649
601
|
setCallOverrideCallback(callOverrideCallback: (contract_address: Buffer, data: Buffer) => Promise<CallOverrideResult | undefined>): void
|
|
@@ -658,19 +610,20 @@ export declare class Provider {
|
|
|
658
610
|
export declare class Response {
|
|
659
611
|
/** Returns the response data as a JSON string or a JSON object. */
|
|
660
612
|
get data(): string | any
|
|
661
|
-
get solidityTrace(): RawTrace | null
|
|
662
613
|
get traces(): Array<RawTrace>
|
|
614
|
+
/**Compute the error stack trace. Return the stack trace if it can be decoded, otherwise returns none. Throws if there was an error computing the stack trace. */
|
|
615
|
+
stackTrace(): SolidityStackTrace | null
|
|
663
616
|
}
|
|
664
|
-
/**
|
|
665
|
-
* Opaque handle to the `Bytecode` struct.
|
|
666
|
-
* Only used on the JS side by the `VmTraceDecoder` class.
|
|
667
|
-
*/
|
|
668
|
-
export declare class BytecodeWrapper { }
|
|
669
617
|
export declare class Exit {
|
|
670
618
|
get kind(): ExitCode
|
|
671
619
|
isError(): boolean
|
|
672
620
|
getReason(): string
|
|
673
621
|
}
|
|
622
|
+
/**
|
|
623
|
+
* Opaque handle to the `Bytecode` struct.
|
|
624
|
+
* Only used on the JS side by the `VmTraceDecoder` class.
|
|
625
|
+
*/
|
|
626
|
+
export declare class BytecodeWrapper { }
|
|
674
627
|
export declare class ReturnData {
|
|
675
628
|
readonly value: Uint8Array
|
|
676
629
|
constructor(value: Uint8Array)
|
|
@@ -680,26 +633,6 @@ export declare class ReturnData {
|
|
|
680
633
|
decodeError(): string
|
|
681
634
|
decodePanic(): bigint
|
|
682
635
|
}
|
|
683
|
-
export declare class SolidityTracer {
|
|
684
|
-
|
|
685
|
-
constructor()
|
|
686
|
-
getStackTrace(trace: PrecompileMessageTrace | CallMessageTrace | CreateMessageTrace): SolidityStackTrace
|
|
687
|
-
}
|
|
688
|
-
export declare class VmTraceDecoder {
|
|
689
|
-
constructor()
|
|
690
|
-
addBytecode(bytecode: BytecodeWrapper): void
|
|
691
|
-
tryToDecodeMessageTrace(messageTrace: PrecompileMessageTrace | CallMessageTrace | CreateMessageTrace): PrecompileMessageTrace | CallMessageTrace | CreateMessageTrace
|
|
692
|
-
getContractAndFunctionNamesForCall(code: Uint8Array, calldata: Uint8Array | undefined): ContractAndFunctionName
|
|
693
|
-
}
|
|
694
|
-
export type VMTracer = VmTracer
|
|
695
|
-
/** N-API bindings for the Rust port of `VMTracer` from Hardhat. */
|
|
696
|
-
export declare class VmTracer {
|
|
697
|
-
constructor()
|
|
698
|
-
/** Observes a trace, collecting information about the execution of the EVM. */
|
|
699
|
-
observe(trace: RawTrace): void
|
|
700
|
-
getLastTopLevelMessageTrace(): PrecompileMessageTrace | CallMessageTrace | CreateMessageTrace | undefined
|
|
701
|
-
getLastError(): Error | undefined
|
|
702
|
-
}
|
|
703
636
|
export declare class RawTrace {
|
|
704
637
|
trace(): Array<TracingMessage | TracingStep | TracingMessageResult>
|
|
705
638
|
}
|
package/index.js
CHANGED
|
@@ -310,7 +310,7 @@ if (!nativeBinding) {
|
|
|
310
310
|
throw new Error(`Failed to load native binding`)
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
-
const { SpecId, EdrContext, MineOrdering, Provider, Response, SuccessReason, ExceptionalHalt,
|
|
313
|
+
const { SpecId, EdrContext, MineOrdering, Provider, Response, SuccessReason, ExceptionalHalt, linkHexStringBytecode, printStackTrace, Exit, ExitCode, BytecodeWrapper, ContractFunctionType, ReturnData, StackTraceEntryType, stackTraceEntryTypeToString, FALLBACK_FUNCTION_NAME, RECEIVE_FUNCTION_NAME, CONSTRUCTOR_FUNCTION_NAME, UNRECOGNIZED_FUNCTION_NAME, UNKNOWN_FUNCTION_NAME, PRECOMPILE_FUNCTION_NAME, UNRECOGNIZED_CONTRACT_NAME, RawTrace, getLatestSupportedSolcVersion } = nativeBinding
|
|
314
314
|
|
|
315
315
|
module.exports.SpecId = SpecId
|
|
316
316
|
module.exports.EdrContext = EdrContext
|
|
@@ -319,14 +319,12 @@ module.exports.Provider = Provider
|
|
|
319
319
|
module.exports.Response = Response
|
|
320
320
|
module.exports.SuccessReason = SuccessReason
|
|
321
321
|
module.exports.ExceptionalHalt = ExceptionalHalt
|
|
322
|
-
module.exports.createModelsAndDecodeBytecodes = createModelsAndDecodeBytecodes
|
|
323
322
|
module.exports.linkHexStringBytecode = linkHexStringBytecode
|
|
324
|
-
module.exports.BytecodeWrapper = BytecodeWrapper
|
|
325
|
-
module.exports.ContractFunctionType = ContractFunctionType
|
|
326
|
-
module.exports.printMessageTrace = printMessageTrace
|
|
327
323
|
module.exports.printStackTrace = printStackTrace
|
|
328
324
|
module.exports.Exit = Exit
|
|
329
325
|
module.exports.ExitCode = ExitCode
|
|
326
|
+
module.exports.BytecodeWrapper = BytecodeWrapper
|
|
327
|
+
module.exports.ContractFunctionType = ContractFunctionType
|
|
330
328
|
module.exports.ReturnData = ReturnData
|
|
331
329
|
module.exports.StackTraceEntryType = StackTraceEntryType
|
|
332
330
|
module.exports.stackTraceEntryTypeToString = stackTraceEntryTypeToString
|
|
@@ -337,8 +335,5 @@ module.exports.UNRECOGNIZED_FUNCTION_NAME = UNRECOGNIZED_FUNCTION_NAME
|
|
|
337
335
|
module.exports.UNKNOWN_FUNCTION_NAME = UNKNOWN_FUNCTION_NAME
|
|
338
336
|
module.exports.PRECOMPILE_FUNCTION_NAME = PRECOMPILE_FUNCTION_NAME
|
|
339
337
|
module.exports.UNRECOGNIZED_CONTRACT_NAME = UNRECOGNIZED_CONTRACT_NAME
|
|
340
|
-
module.exports.SolidityTracer = SolidityTracer
|
|
341
|
-
module.exports.VmTraceDecoder = VmTraceDecoder
|
|
342
|
-
module.exports.initializeVmTraceDecoder = initializeVmTraceDecoder
|
|
343
|
-
module.exports.VmTracer = VmTracer
|
|
344
338
|
module.exports.RawTrace = RawTrace
|
|
339
|
+
module.exports.getLatestSupportedSolcVersion = getLatestSupportedSolcVersion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nomicfoundation/edr",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"devDependencies": {
|
|
5
5
|
"@napi-rs/cli": "^2.18.4",
|
|
6
6
|
"@types/chai": "^4.2.0",
|
|
@@ -52,13 +52,13 @@
|
|
|
52
52
|
"repository": "NomicFoundation/edr.git",
|
|
53
53
|
"types": "index.d.ts",
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@nomicfoundation/edr-darwin-arm64": "0.
|
|
56
|
-
"@nomicfoundation/edr-darwin-x64": "0.
|
|
57
|
-
"@nomicfoundation/edr-linux-arm64-gnu": "0.
|
|
58
|
-
"@nomicfoundation/edr-linux-arm64-musl": "0.
|
|
59
|
-
"@nomicfoundation/edr-linux-x64-gnu": "0.
|
|
60
|
-
"@nomicfoundation/edr-linux-x64-musl": "0.
|
|
61
|
-
"@nomicfoundation/edr-win32-x64-msvc": "0.
|
|
55
|
+
"@nomicfoundation/edr-darwin-arm64": "0.7.0",
|
|
56
|
+
"@nomicfoundation/edr-darwin-x64": "0.7.0",
|
|
57
|
+
"@nomicfoundation/edr-linux-arm64-gnu": "0.7.0",
|
|
58
|
+
"@nomicfoundation/edr-linux-arm64-musl": "0.7.0",
|
|
59
|
+
"@nomicfoundation/edr-linux-x64-gnu": "0.7.0",
|
|
60
|
+
"@nomicfoundation/edr-linux-x64-musl": "0.7.0",
|
|
61
|
+
"@nomicfoundation/edr-win32-x64-msvc": "0.7.0"
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
64
|
"artifacts": "napi artifacts",
|
package/src/context.rs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
use std::{
|
|
1
|
+
use std::{ops::Deref, sync::Arc};
|
|
2
2
|
|
|
3
|
+
#[cfg(feature = "tracing")]
|
|
3
4
|
use napi::Status;
|
|
4
5
|
use napi_derive::napi;
|
|
5
6
|
use tracing_subscriber::{prelude::*, EnvFilter, Registry};
|
|
@@ -23,8 +24,7 @@ impl EdrContext {
|
|
|
23
24
|
#[doc = "Creates a new [`EdrContext`] instance. Should only be called once!"]
|
|
24
25
|
#[napi(constructor)]
|
|
25
26
|
pub fn new() -> napi::Result<Self> {
|
|
26
|
-
let context =
|
|
27
|
-
Context::new().map_err(|e| napi::Error::new(Status::GenericFailure, e.to_string()))?;
|
|
27
|
+
let context = Context::new()?;
|
|
28
28
|
|
|
29
29
|
Ok(Self {
|
|
30
30
|
inner: Arc::new(context),
|
|
@@ -34,14 +34,13 @@ impl EdrContext {
|
|
|
34
34
|
|
|
35
35
|
#[derive(Debug)]
|
|
36
36
|
pub struct Context {
|
|
37
|
-
_subscriber_guard: tracing::subscriber::DefaultGuard,
|
|
38
37
|
#[cfg(feature = "tracing")]
|
|
39
38
|
_tracing_write_guard: tracing_flame::FlushGuard<std::io::BufWriter<std::fs::File>>,
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
impl Context {
|
|
43
42
|
/// Creates a new [`Context`] instance. Should only be called once!
|
|
44
|
-
pub fn new() ->
|
|
43
|
+
pub fn new() -> napi::Result<Self> {
|
|
45
44
|
let fmt_layer = tracing_subscriber::fmt::layer()
|
|
46
45
|
.with_file(true)
|
|
47
46
|
.with_line_number(true)
|
|
@@ -54,8 +53,13 @@ impl Context {
|
|
|
54
53
|
|
|
55
54
|
#[cfg(feature = "tracing")]
|
|
56
55
|
let (flame_layer, guard) = {
|
|
57
|
-
let (flame_layer, guard) =
|
|
58
|
-
|
|
56
|
+
let (flame_layer, guard) = tracing_flame::FlameLayer::with_file("tracing.folded")
|
|
57
|
+
.map_err(|err| {
|
|
58
|
+
napi::Error::new(
|
|
59
|
+
Status::GenericFailure,
|
|
60
|
+
format!("Failed to create tracing.folded file with error: {err:?}"),
|
|
61
|
+
)
|
|
62
|
+
})?;
|
|
59
63
|
|
|
60
64
|
let flame_layer = flame_layer.with_empty_samples(false);
|
|
61
65
|
(flame_layer, guard)
|
|
@@ -64,10 +68,14 @@ impl Context {
|
|
|
64
68
|
#[cfg(feature = "tracing")]
|
|
65
69
|
let subscriber = subscriber.with(flame_layer);
|
|
66
70
|
|
|
67
|
-
let
|
|
71
|
+
if let Err(error) = tracing::subscriber::set_global_default(subscriber) {
|
|
72
|
+
println!(
|
|
73
|
+
"Failed to set global tracing subscriber with error: {error}\n\
|
|
74
|
+
Please only initialize EdrContext once per process to avoid this error."
|
|
75
|
+
);
|
|
76
|
+
}
|
|
68
77
|
|
|
69
78
|
Ok(Self {
|
|
70
|
-
_subscriber_guard: subscriber_guard,
|
|
71
79
|
#[cfg(feature = "tracing")]
|
|
72
80
|
_tracing_write_guard: guard,
|
|
73
81
|
})
|
package/src/logger.rs
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
use std::{
|
|
1
|
+
use std::{
|
|
2
|
+
fmt::Display,
|
|
3
|
+
sync::{mpsc::channel, Arc},
|
|
4
|
+
};
|
|
2
5
|
|
|
3
6
|
use ansi_term::{Color, Style};
|
|
4
7
|
use edr_eth::{
|
|
@@ -14,6 +17,7 @@ use edr_evm::{
|
|
|
14
17
|
ExecutionResult, SyncBlock,
|
|
15
18
|
};
|
|
16
19
|
use edr_provider::{ProviderError, TransactionFailure};
|
|
20
|
+
use edr_solidity::contract_decoder::ContractDecoder;
|
|
17
21
|
use itertools::izip;
|
|
18
22
|
use napi::{
|
|
19
23
|
threadsafe_function::{
|
|
@@ -41,21 +45,12 @@ impl TryCast<(String, Option<String>)> for ContractAndFunctionName {
|
|
|
41
45
|
}
|
|
42
46
|
}
|
|
43
47
|
|
|
44
|
-
struct ContractAndFunctionNameCall {
|
|
45
|
-
code: Bytes,
|
|
46
|
-
/// Only present for calls.
|
|
47
|
-
calldata: Option<Bytes>,
|
|
48
|
-
}
|
|
49
|
-
|
|
50
48
|
#[napi(object)]
|
|
51
49
|
pub struct LoggerConfig {
|
|
52
50
|
/// Whether to enable the logger.
|
|
53
51
|
pub enable: bool,
|
|
54
52
|
#[napi(ts_type = "(inputs: Buffer[]) => string[]")]
|
|
55
53
|
pub decode_console_log_inputs_callback: JsFunction,
|
|
56
|
-
#[napi(ts_type = "(code: Buffer, calldata?: Buffer) => ContractAndFunctionName")]
|
|
57
|
-
/// Used to resolve the contract and function name when logging.
|
|
58
|
-
pub get_contract_and_function_name_callback: JsFunction,
|
|
59
54
|
#[napi(ts_type = "(message: string, replace: boolean) => void")]
|
|
60
55
|
pub print_line_callback: JsFunction,
|
|
61
56
|
}
|
|
@@ -118,9 +113,13 @@ pub struct Logger {
|
|
|
118
113
|
}
|
|
119
114
|
|
|
120
115
|
impl Logger {
|
|
121
|
-
pub fn new(
|
|
116
|
+
pub fn new(
|
|
117
|
+
env: &Env,
|
|
118
|
+
config: LoggerConfig,
|
|
119
|
+
contract_decoder: Arc<ContractDecoder>,
|
|
120
|
+
) -> napi::Result<Self> {
|
|
122
121
|
Ok(Self {
|
|
123
|
-
collector: LogCollector::new(env, config)?,
|
|
122
|
+
collector: LogCollector::new(env, config, contract_decoder)?,
|
|
124
123
|
})
|
|
125
124
|
}
|
|
126
125
|
}
|
|
@@ -235,6 +234,15 @@ impl edr_provider::Logger for Logger {
|
|
|
235
234
|
|
|
236
235
|
Ok(())
|
|
237
236
|
}
|
|
237
|
+
|
|
238
|
+
fn print_contract_decoding_error(&mut self, error: &str) -> Result<(), Self::LoggerError> {
|
|
239
|
+
self.collector.log(
|
|
240
|
+
"Contract decoder failed to be updated. Please report this to help us improve Hardhat.",
|
|
241
|
+
);
|
|
242
|
+
self.collector.print_empty_line()?;
|
|
243
|
+
self.collector.log(error);
|
|
244
|
+
Ok(())
|
|
245
|
+
}
|
|
238
246
|
}
|
|
239
247
|
|
|
240
248
|
#[derive(Clone)]
|
|
@@ -245,9 +253,8 @@ pub struct CollapsedMethod {
|
|
|
245
253
|
|
|
246
254
|
#[derive(Clone)]
|
|
247
255
|
struct LogCollector {
|
|
256
|
+
contract_decoder: Arc<ContractDecoder>,
|
|
248
257
|
decode_console_log_inputs_fn: ThreadsafeFunction<Vec<Bytes>, ErrorStrategy::Fatal>,
|
|
249
|
-
get_contract_and_function_name_fn:
|
|
250
|
-
ThreadsafeFunction<ContractAndFunctionNameCall, ErrorStrategy::Fatal>,
|
|
251
258
|
indentation: usize,
|
|
252
259
|
is_enabled: bool,
|
|
253
260
|
logs: Vec<LogLine>,
|
|
@@ -257,7 +264,11 @@ struct LogCollector {
|
|
|
257
264
|
}
|
|
258
265
|
|
|
259
266
|
impl LogCollector {
|
|
260
|
-
pub fn new(
|
|
267
|
+
pub fn new(
|
|
268
|
+
env: &Env,
|
|
269
|
+
config: LoggerConfig,
|
|
270
|
+
contract_decoder: Arc<ContractDecoder>,
|
|
271
|
+
) -> napi::Result<Self> {
|
|
261
272
|
let mut decode_console_log_inputs_fn = config
|
|
262
273
|
.decode_console_log_inputs_callback
|
|
263
274
|
.create_threadsafe_function(0, |ctx: ThreadSafeCallContext<Vec<Bytes>>| {
|
|
@@ -281,34 +292,6 @@ impl LogCollector {
|
|
|
281
292
|
// exiting.
|
|
282
293
|
decode_console_log_inputs_fn.unref(env)?;
|
|
283
294
|
|
|
284
|
-
let mut get_contract_and_function_name_fn = config
|
|
285
|
-
.get_contract_and_function_name_callback
|
|
286
|
-
.create_threadsafe_function(
|
|
287
|
-
0,
|
|
288
|
-
|ctx: ThreadSafeCallContext<ContractAndFunctionNameCall>| {
|
|
289
|
-
// Buffer
|
|
290
|
-
let code = ctx
|
|
291
|
-
.env
|
|
292
|
-
.create_buffer_with_data(ctx.value.code.to_vec())?
|
|
293
|
-
.into_unknown();
|
|
294
|
-
|
|
295
|
-
// Option<Buffer>
|
|
296
|
-
let calldata = if let Some(calldata) = ctx.value.calldata {
|
|
297
|
-
ctx.env
|
|
298
|
-
.create_buffer_with_data(calldata.to_vec())?
|
|
299
|
-
.into_unknown()
|
|
300
|
-
} else {
|
|
301
|
-
ctx.env.get_undefined()?.into_unknown()
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
Ok(vec![code, calldata])
|
|
305
|
-
},
|
|
306
|
-
)?;
|
|
307
|
-
|
|
308
|
-
// Maintain a weak reference to the function to avoid the event loop from
|
|
309
|
-
// exiting.
|
|
310
|
-
get_contract_and_function_name_fn.unref(env)?;
|
|
311
|
-
|
|
312
295
|
let mut print_line_fn = config.print_line_callback.create_threadsafe_function(
|
|
313
296
|
0,
|
|
314
297
|
|ctx: ThreadSafeCallContext<(String, bool)>| {
|
|
@@ -327,8 +310,8 @@ impl LogCollector {
|
|
|
327
310
|
print_line_fn.unref(env)?;
|
|
328
311
|
|
|
329
312
|
Ok(Self {
|
|
313
|
+
contract_decoder,
|
|
330
314
|
decode_console_log_inputs_fn,
|
|
331
|
-
get_contract_and_function_name_fn,
|
|
332
315
|
indentation: 0,
|
|
333
316
|
is_enabled: config.enable,
|
|
334
317
|
logs: Vec::new(),
|
|
@@ -560,29 +543,13 @@ impl LogCollector {
|
|
|
560
543
|
code: Bytes,
|
|
561
544
|
calldata: Option<Bytes>,
|
|
562
545
|
) -> (String, Option<String>) {
|
|
563
|
-
let
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
.
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
move |result: ContractAndFunctionName| {
|
|
571
|
-
let contract_and_function_name = result.try_cast();
|
|
572
|
-
sender.send(contract_and_function_name).map_err(|_error| {
|
|
573
|
-
napi::Error::new(
|
|
574
|
-
Status::GenericFailure,
|
|
575
|
-
"Failed to send result from get_contract_and_function_name",
|
|
576
|
-
)
|
|
577
|
-
})
|
|
578
|
-
},
|
|
579
|
-
);
|
|
580
|
-
assert_eq!(status, Status::Ok);
|
|
581
|
-
|
|
582
|
-
receiver
|
|
583
|
-
.recv()
|
|
584
|
-
.unwrap()
|
|
585
|
-
.expect("Failed call to get_contract_and_function_name")
|
|
546
|
+
let edr_solidity::contract_decoder::ContractAndFunctionName {
|
|
547
|
+
contract_name,
|
|
548
|
+
function_name,
|
|
549
|
+
} = self
|
|
550
|
+
.contract_decoder
|
|
551
|
+
.get_contract_and_function_names_for_call(&code, calldata.as_ref());
|
|
552
|
+
(contract_name, function_name)
|
|
586
553
|
}
|
|
587
554
|
|
|
588
555
|
fn format(&self, message: impl ToString) -> String {
|
package/src/provider.rs
CHANGED
|
@@ -4,6 +4,7 @@ use std::sync::Arc;
|
|
|
4
4
|
|
|
5
5
|
use edr_provider::{time::CurrentTime, InvalidRequestReason};
|
|
6
6
|
use edr_rpc_eth::jsonrpc;
|
|
7
|
+
use edr_solidity::contract_decoder::ContractDecoder;
|
|
7
8
|
use napi::{tokio::runtime, Either, Env, JsFunction, JsObject, Status};
|
|
8
9
|
use napi_derive::napi;
|
|
9
10
|
|
|
@@ -13,7 +14,7 @@ use crate::{
|
|
|
13
14
|
context::EdrContext,
|
|
14
15
|
logger::{Logger, LoggerConfig, LoggerError},
|
|
15
16
|
subscribe::SubscriberCallback,
|
|
16
|
-
trace::RawTrace,
|
|
17
|
+
trace::{solidity_stack_trace::SolidityStackTrace, RawTrace},
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
/// A JSON-RPC provider for Ethereum.
|
|
@@ -21,6 +22,7 @@ use crate::{
|
|
|
21
22
|
pub struct Provider {
|
|
22
23
|
provider: Arc<edr_provider::Provider<LoggerError>>,
|
|
23
24
|
runtime: runtime::Handle,
|
|
25
|
+
contract_decoder: Arc<ContractDecoder>,
|
|
24
26
|
#[cfg(feature = "scenarios")]
|
|
25
27
|
scenario_file: Option<napi::tokio::sync::Mutex<napi::tokio::fs::File>>,
|
|
26
28
|
}
|
|
@@ -35,12 +37,25 @@ impl Provider {
|
|
|
35
37
|
_context: &EdrContext,
|
|
36
38
|
config: ProviderConfig,
|
|
37
39
|
logger_config: LoggerConfig,
|
|
40
|
+
tracing_config: serde_json::Value,
|
|
38
41
|
#[napi(ts_arg_type = "(event: SubscriptionEvent) => void")] subscriber_callback: JsFunction,
|
|
39
42
|
) -> napi::Result<JsObject> {
|
|
40
|
-
let config = edr_provider::ProviderConfig::try_from(config)?;
|
|
41
43
|
let runtime = runtime::Handle::current();
|
|
42
44
|
|
|
43
|
-
let
|
|
45
|
+
let config = edr_provider::ProviderConfig::try_from(config)?;
|
|
46
|
+
|
|
47
|
+
// TODO https://github.com/NomicFoundation/edr/issues/760
|
|
48
|
+
let build_info_config: edr_solidity::contract_decoder::BuildInfoConfig =
|
|
49
|
+
serde_json::from_value(tracing_config)?;
|
|
50
|
+
let contract_decoder = ContractDecoder::new(&build_info_config)
|
|
51
|
+
.map_err(|error| napi::Error::from_reason(error.to_string()))?;
|
|
52
|
+
let contract_decoder = Arc::new(contract_decoder);
|
|
53
|
+
|
|
54
|
+
let logger = Box::new(Logger::new(
|
|
55
|
+
&env,
|
|
56
|
+
logger_config,
|
|
57
|
+
Arc::clone(&contract_decoder),
|
|
58
|
+
)?);
|
|
44
59
|
let subscriber_callback = SubscriberCallback::new(&env, subscriber_callback)?;
|
|
45
60
|
let subscriber_callback = Box::new(move |event| subscriber_callback.call(event));
|
|
46
61
|
|
|
@@ -58,6 +73,7 @@ impl Provider {
|
|
|
58
73
|
logger,
|
|
59
74
|
subscriber_callback,
|
|
60
75
|
config,
|
|
76
|
+
Arc::clone(&contract_decoder),
|
|
61
77
|
CurrentTime,
|
|
62
78
|
)
|
|
63
79
|
.map_or_else(
|
|
@@ -66,6 +82,7 @@ impl Provider {
|
|
|
66
82
|
Ok(Provider {
|
|
67
83
|
provider: Arc::new(provider),
|
|
68
84
|
runtime,
|
|
85
|
+
contract_decoder,
|
|
69
86
|
#[cfg(feature = "scenarios")]
|
|
70
87
|
scenario_file,
|
|
71
88
|
})
|
|
@@ -184,10 +201,16 @@ impl Provider {
|
|
|
184
201
|
}
|
|
185
202
|
})
|
|
186
203
|
.map_err(|error| napi::Error::new(Status::GenericFailure, error.to_string()))
|
|
187
|
-
.map(|data|
|
|
188
|
-
solidity_trace
|
|
189
|
-
|
|
190
|
-
|
|
204
|
+
.map(|data| {
|
|
205
|
+
let solidity_trace = solidity_trace.map(|trace| SolidityTraceData {
|
|
206
|
+
trace,
|
|
207
|
+
contract_decoder: Arc::clone(&self.contract_decoder),
|
|
208
|
+
});
|
|
209
|
+
Response {
|
|
210
|
+
solidity_trace,
|
|
211
|
+
data,
|
|
212
|
+
traces: traces.into_iter().map(Arc::new).collect(),
|
|
213
|
+
}
|
|
191
214
|
})
|
|
192
215
|
}
|
|
193
216
|
|
|
@@ -222,6 +245,12 @@ impl Provider {
|
|
|
222
245
|
}
|
|
223
246
|
}
|
|
224
247
|
|
|
248
|
+
#[derive(Debug)]
|
|
249
|
+
struct SolidityTraceData {
|
|
250
|
+
trace: Arc<edr_evm::trace::Trace>,
|
|
251
|
+
contract_decoder: Arc<ContractDecoder>,
|
|
252
|
+
}
|
|
253
|
+
|
|
225
254
|
#[napi]
|
|
226
255
|
pub struct Response {
|
|
227
256
|
// N-API is known to be slow when marshalling `serde_json::Value`s, so we try to return a
|
|
@@ -230,7 +259,7 @@ pub struct Response {
|
|
|
230
259
|
data: Either<String, serde_json::Value>,
|
|
231
260
|
/// When a transaction fails to execute, the provider returns a trace of the
|
|
232
261
|
/// transaction.
|
|
233
|
-
solidity_trace: Option<
|
|
262
|
+
solidity_trace: Option<SolidityTraceData>,
|
|
234
263
|
/// This may contain zero or more traces, depending on the (batch) request
|
|
235
264
|
traces: Vec<Arc<edr_evm::trace::Trace>>,
|
|
236
265
|
}
|
|
@@ -243,13 +272,6 @@ impl Response {
|
|
|
243
272
|
self.data.clone()
|
|
244
273
|
}
|
|
245
274
|
|
|
246
|
-
#[napi(getter)]
|
|
247
|
-
pub fn solidity_trace(&self) -> Option<RawTrace> {
|
|
248
|
-
self.solidity_trace
|
|
249
|
-
.as_ref()
|
|
250
|
-
.map(|trace| RawTrace::new(trace.clone()))
|
|
251
|
-
}
|
|
252
|
-
|
|
253
275
|
#[napi(getter)]
|
|
254
276
|
pub fn traces(&self) -> Vec<RawTrace> {
|
|
255
277
|
self.traces
|
|
@@ -257,4 +279,35 @@ impl Response {
|
|
|
257
279
|
.map(|trace| RawTrace::new(trace.clone()))
|
|
258
280
|
.collect()
|
|
259
281
|
}
|
|
282
|
+
|
|
283
|
+
// Rust port of https://github.com/NomicFoundation/hardhat/blob/c20bf195a6efdc2d74e778b7a4a7799aac224841/packages/hardhat-core/src/internal/hardhat-network/provider/provider.ts#L590
|
|
284
|
+
#[doc = "Compute the error stack trace. Return the stack trace if it can be decoded, otherwise returns none. Throws if there was an error computing the stack trace."]
|
|
285
|
+
#[napi]
|
|
286
|
+
pub fn stack_trace(&self) -> napi::Result<Option<SolidityStackTrace>> {
|
|
287
|
+
let Some(SolidityTraceData {
|
|
288
|
+
trace,
|
|
289
|
+
contract_decoder,
|
|
290
|
+
}) = &self.solidity_trace
|
|
291
|
+
else {
|
|
292
|
+
return Ok(None);
|
|
293
|
+
};
|
|
294
|
+
let nested_trace = edr_solidity::nested_tracer::convert_trace_messages_to_nested_trace(
|
|
295
|
+
trace.as_ref().clone(),
|
|
296
|
+
)
|
|
297
|
+
.map_err(|err| napi::Error::from_reason(err.to_string()))?;
|
|
298
|
+
|
|
299
|
+
if let Some(vm_trace) = nested_trace {
|
|
300
|
+
let decoded_trace = contract_decoder.try_to_decode_message_trace(vm_trace);
|
|
301
|
+
let stack_trace = edr_solidity::solidity_tracer::get_stack_trace(decoded_trace)
|
|
302
|
+
.map_err(|err| napi::Error::from_reason(err.to_string()))?;
|
|
303
|
+
let stack_trace = stack_trace
|
|
304
|
+
.into_iter()
|
|
305
|
+
.map(super::cast::TryCast::try_cast)
|
|
306
|
+
.collect::<Result<Vec<_>, _>>()?;
|
|
307
|
+
|
|
308
|
+
Ok(Some(stack_trace))
|
|
309
|
+
} else {
|
|
310
|
+
Ok(None)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
260
313
|
}
|