-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Run solidity tracing logic in EDR (#5769)
* refactor: Remove the experimental MessageTrace hook This removes the `MessageTrace` from the (experimental) public API and only hides it behind our internal API, where it is used for stack trace logic. We want to move this type to Rust to decrease the overhead of the value passing across the FFI and it would allow us to remove/not export this type entirely from Rust when porting the stack trace logic. * Add await in provider creation * refactor: Use the new EDR internals for the stack trace decoding * refactor: Merge ContractsIdentifier into VmTraceDecoder * refactor: Adapt to the now removed Bytecode * fix: Use nested scopes in a stack trace entry message switch * Bump EDR version * Create happy-kiwis-draw.md --------- Co-authored-by: Igor Matuszewski <[email protected]>
- Loading branch information
Showing
23 changed files
with
220 additions
and
5,311 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"hardhat": patch | ||
--- | ||
|
||
Adapted Hardhat to trace Solidity logic on EDR. This resulted in a 10% performance improvement for most workloads. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
78 changes: 5 additions & 73 deletions
78
packages/hardhat-core/src/internal/hardhat-network/provider/return-data.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,75 +1,7 @@ | ||
import { bytesToBigInt } from "@nomicfoundation/ethereumjs-util"; | ||
import { assertHardhatInvariant } from "../../core/errors"; | ||
import { requireNapiRsModule } from "../../../common/napi-rs"; | ||
|
||
const { rawDecode } = require("ethereumjs-abi"); | ||
const { ReturnData } = requireNapiRsModule( | ||
"@nomicfoundation/edr" | ||
) as typeof import("@nomicfoundation/edr"); | ||
|
||
// selector of Error(string) | ||
const ERROR_SELECTOR = "08c379a0"; | ||
// selector of Panic(uint256) | ||
const PANIC_SELECTOR = "4e487b71"; | ||
|
||
/** | ||
* Represents the returnData of a transaction, whose contents are unknown. | ||
*/ | ||
export class ReturnData { | ||
private _selector: string | undefined; | ||
|
||
constructor(public value: Uint8Array) { | ||
if (value.length >= 4) { | ||
this._selector = Buffer.from(value.slice(0, 4)).toString("hex"); | ||
} | ||
} | ||
|
||
public isEmpty(): boolean { | ||
return this.value.length === 0; | ||
} | ||
|
||
public matchesSelector(selector: Uint8Array): boolean { | ||
if (this._selector === undefined) { | ||
return false; | ||
} | ||
|
||
return this._selector === Buffer.from(selector).toString("hex"); | ||
} | ||
|
||
public isErrorReturnData(): boolean { | ||
return this._selector === ERROR_SELECTOR; | ||
} | ||
|
||
public isPanicReturnData(): boolean { | ||
return this._selector === PANIC_SELECTOR; | ||
} | ||
|
||
public decodeError(): string { | ||
if (this.isEmpty()) { | ||
return ""; | ||
} | ||
|
||
assertHardhatInvariant( | ||
this._selector === ERROR_SELECTOR, | ||
"Expected return data to be a Error(string)" | ||
); | ||
|
||
const [decodedError] = rawDecode(["string"], this.value.slice(4)) as [ | ||
string | ||
]; | ||
|
||
return decodedError; | ||
} | ||
|
||
public decodePanic(): bigint { | ||
assertHardhatInvariant( | ||
this._selector === PANIC_SELECTOR, | ||
"Expected return data to be a Panic(uint256)" | ||
); | ||
|
||
// we are assuming that panic codes are smaller than Number.MAX_SAFE_INTEGER | ||
const errorCode = bytesToBigInt(this.value.slice(4)); | ||
|
||
return errorCode; | ||
} | ||
|
||
public getSelector(): string | undefined { | ||
return this._selector; | ||
} | ||
} | ||
export { ReturnData }; |
96 changes: 4 additions & 92 deletions
96
packages/hardhat-core/src/internal/hardhat-network/provider/vm/exit.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,95 +1,7 @@ | ||
import type { ExceptionalHalt, SuccessReason } from "@nomicfoundation/edr"; | ||
|
||
import { requireNapiRsModule } from "../../../../common/napi-rs"; | ||
|
||
export enum ExitCode { | ||
SUCCESS, | ||
REVERT, | ||
OUT_OF_GAS, | ||
INTERNAL_ERROR, | ||
INVALID_OPCODE, | ||
STACK_UNDERFLOW, | ||
CODESIZE_EXCEEDS_MAXIMUM, | ||
CREATE_COLLISION, | ||
STATIC_STATE_CHANGE, | ||
} | ||
|
||
export class Exit { | ||
public static fromEdrSuccessReason(reason: SuccessReason): Exit { | ||
const { SuccessReason } = requireNapiRsModule( | ||
"@nomicfoundation/edr" | ||
) as typeof import("@nomicfoundation/edr"); | ||
|
||
switch (reason) { | ||
case SuccessReason.Stop: | ||
case SuccessReason.Return: | ||
case SuccessReason.SelfDestruct: | ||
case SuccessReason.EofReturnContract: | ||
return new Exit(ExitCode.SUCCESS); | ||
} | ||
|
||
const _exhaustiveCheck: never = reason; | ||
} | ||
|
||
public static fromEdrExceptionalHalt(halt: ExceptionalHalt): Exit { | ||
const { ExceptionalHalt } = requireNapiRsModule( | ||
"@nomicfoundation/edr" | ||
) as typeof import("@nomicfoundation/edr"); | ||
|
||
switch (halt) { | ||
case ExceptionalHalt.OutOfGas: | ||
return new Exit(ExitCode.OUT_OF_GAS); | ||
|
||
case ExceptionalHalt.OpcodeNotFound: | ||
case ExceptionalHalt.InvalidFEOpcode: | ||
// Returned when an opcode is not implemented for the hardfork | ||
case ExceptionalHalt.NotActivated: | ||
return new Exit(ExitCode.INVALID_OPCODE); | ||
|
||
case ExceptionalHalt.StackUnderflow: | ||
return new Exit(ExitCode.STACK_UNDERFLOW); | ||
|
||
case ExceptionalHalt.CreateCollision: | ||
return new Exit(ExitCode.CREATE_COLLISION); | ||
|
||
case ExceptionalHalt.CreateContractSizeLimit: | ||
return new Exit(ExitCode.CODESIZE_EXCEEDS_MAXIMUM); | ||
|
||
default: { | ||
// eslint-disable-next-line @nomicfoundation/hardhat-internal-rules/only-hardhat-error | ||
throw new Error(`Unmatched EDR exceptional halt: ${halt}`); | ||
} | ||
} | ||
} | ||
|
||
constructor(public kind: ExitCode) {} | ||
|
||
public isError(): boolean { | ||
return this.kind !== ExitCode.SUCCESS; | ||
} | ||
|
||
public getReason(): string { | ||
switch (this.kind) { | ||
case ExitCode.SUCCESS: | ||
return "Success"; | ||
case ExitCode.REVERT: | ||
return "Reverted"; | ||
case ExitCode.OUT_OF_GAS: | ||
return "Out of gas"; | ||
case ExitCode.INTERNAL_ERROR: | ||
return "Internal error"; | ||
case ExitCode.INVALID_OPCODE: | ||
return "Invalid opcode"; | ||
case ExitCode.STACK_UNDERFLOW: | ||
return "Stack underflow"; | ||
case ExitCode.CODESIZE_EXCEEDS_MAXIMUM: | ||
return "Codesize exceeds maximum"; | ||
case ExitCode.CREATE_COLLISION: | ||
return "Create collision"; | ||
case ExitCode.STATIC_STATE_CHANGE: | ||
return "Static state change"; | ||
} | ||
const { ExitCode } = requireNapiRsModule( | ||
"@nomicfoundation/edr" | ||
) as typeof import("@nomicfoundation/edr"); | ||
|
||
const _exhaustiveCheck: never = this.kind; | ||
} | ||
} | ||
export { ExitCode }; |
Oops, something went wrong.