Skip to content
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

refactor: Port ReturnData and StackTraceEntryType #589

Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/edr_napi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition = "2021"
crate-type = ["cdylib"]

[dependencies]
alloy-sol-types = { version = "0.5.1", default-features = false, features = ["std"] }
ansi_term = { version = "0.12.1", default-features = false }
crossbeam-channel = { version = "0.5.6", default-features = false }
itertools = { version = "0.12.0", default-features = false }
Expand Down
164 changes: 164 additions & 0 deletions crates/edr_napi/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,160 @@ export function isPush(opcode: Opcode): boolean
export function isJump(opcode: Opcode): boolean
export function isCall(opcode: Opcode): boolean
export function isCreate(opcode: Opcode): boolean
export const enum StackTraceEntryType {
CALLSTACK_ENTRY = 0,
UNRECOGNIZED_CREATE_CALLSTACK_ENTRY = 1,
UNRECOGNIZED_CONTRACT_CALLSTACK_ENTRY = 2,
PRECOMPILE_ERROR = 3,
REVERT_ERROR = 4,
PANIC_ERROR = 5,
CUSTOM_ERROR = 6,
FUNCTION_NOT_PAYABLE_ERROR = 7,
INVALID_PARAMS_ERROR = 8,
FALLBACK_NOT_PAYABLE_ERROR = 9,
FALLBACK_NOT_PAYABLE_AND_NO_RECEIVE_ERROR = 10,
UNRECOGNIZED_FUNCTION_WITHOUT_FALLBACK_ERROR = 11,
MISSING_FALLBACK_OR_RECEIVE_ERROR = 12,
RETURNDATA_SIZE_ERROR = 13,
NONCONTRACT_ACCOUNT_CALLED_ERROR = 14,
CALL_FAILED_ERROR = 15,
DIRECT_LIBRARY_CALL_ERROR = 16,
UNRECOGNIZED_CREATE_ERROR = 17,
UNRECOGNIZED_CONTRACT_ERROR = 18,
OTHER_EXECUTION_ERROR = 19,
UNMAPPED_SOLC_0_6_3_REVERT_ERROR = 20,
CONTRACT_TOO_LARGE_ERROR = 21,
INTERNAL_FUNCTION_CALLSTACK_ENTRY = 22,
CONTRACT_CALL_RUN_OUT_OF_GAS_ERROR = 23
}
export function stackTraceEntryTypeToString(val: StackTraceEntryType): string
export const FALLBACK_FUNCTION_NAME: string
export const RECEIVE_FUNCTION_NAME: string
export const CONSTRUCTOR_FUNCTION_NAME: string
export const UNRECOGNIZED_FUNCTION_NAME: string
export const UNKNOWN_FUNCTION_NAME: string
export const PRECOMPILE_FUNCTION_NAME: string
export const UNRECOGNIZED_CONTRACT_NAME: string
export interface SourceReference {
sourceName: string
sourceContent: string
contract?: string
function?: string
line: number
range: Array<number>
}
export interface CallstackEntryStackTraceEntry {
type: StackTraceEntryType.CALLSTACK_ENTRY
sourceReference: SourceReference
functionType: ContractFunctionType
}
export interface UnrecognizedCreateCallstackEntryStackTraceEntry {
type: StackTraceEntryType.UNRECOGNIZED_CREATE_CALLSTACK_ENTRY
sourceReference?: undefined
}
export interface UnrecognizedContractCallstackEntryStackTraceEntry {
type: StackTraceEntryType.UNRECOGNIZED_CONTRACT_CALLSTACK_ENTRY
address: Uint8Array
sourceReference?: undefined
}
export interface PrecompileErrorStackTraceEntry {
type: StackTraceEntryType.PRECOMPILE_ERROR
precompile: number
sourceReference?: undefined
}
export interface RevertErrorStackTraceEntry {
type: StackTraceEntryType.REVERT_ERROR
message: ReturnData
sourceReference: SourceReference
isInvalidOpcodeError: boolean
}
export interface PanicErrorStackTraceEntry {
type: StackTraceEntryType.PANIC_ERROR
errorCode: bigint
sourceReference?: SourceReference
}
export interface CustomErrorStackTraceEntry {
type: StackTraceEntryType.CUSTOM_ERROR
message: string
sourceReference: SourceReference
}
export interface UnmappedSolc063RevertErrorStackTraceEntry {
type: StackTraceEntryType.UNMAPPED_SOLC_0_6_3_REVERT_ERROR
sourceReference?: SourceReference
}
export interface FunctionNotPayableErrorStackTraceEntry {
type: StackTraceEntryType.FUNCTION_NOT_PAYABLE_ERROR
value: bigint
sourceReference: SourceReference
}
export interface InvalidParamsErrorStackTraceEntry {
type: StackTraceEntryType.INVALID_PARAMS_ERROR
sourceReference: SourceReference
}
export interface FallbackNotPayableErrorStackTraceEntry {
type: StackTraceEntryType.FALLBACK_NOT_PAYABLE_ERROR
value: bigint
sourceReference: SourceReference
}
export interface FallbackNotPayableAndNoReceiveErrorStackTraceEntry {
type: StackTraceEntryType.FALLBACK_NOT_PAYABLE_AND_NO_RECEIVE_ERROR
value: bigint
sourceReference: SourceReference
}
export interface UnrecognizedFunctionWithoutFallbackErrorStackTraceEntry {
type: StackTraceEntryType.UNRECOGNIZED_FUNCTION_WITHOUT_FALLBACK_ERROR
sourceReference: SourceReference
}
export interface MissingFallbackOrReceiveErrorStackTraceEntry {
type: StackTraceEntryType.MISSING_FALLBACK_OR_RECEIVE_ERROR
sourceReference: SourceReference
}
export interface ReturndataSizeErrorStackTraceEntry {
type: StackTraceEntryType.RETURNDATA_SIZE_ERROR
sourceReference: SourceReference
}
export interface NonContractAccountCalledErrorStackTraceEntry {
type: StackTraceEntryType.NONCONTRACT_ACCOUNT_CALLED_ERROR
sourceReference: SourceReference
}
export interface CallFailedErrorStackTraceEntry {
type: StackTraceEntryType.CALL_FAILED_ERROR
sourceReference: SourceReference
}
export interface DirectLibraryCallErrorStackTraceEntry {
type: StackTraceEntryType.DIRECT_LIBRARY_CALL_ERROR
sourceReference: SourceReference
}
export interface UnrecognizedCreateErrorStackTraceEntry {
type: StackTraceEntryType.UNRECOGNIZED_CREATE_ERROR
message: ReturnData
sourceReference?: undefined
isInvalidOpcodeError: boolean
}
export interface UnrecognizedContractErrorStackTraceEntry {
type: StackTraceEntryType.UNRECOGNIZED_CONTRACT_ERROR
address: Uint8Array
message: ReturnData
sourceReference?: undefined
isInvalidOpcodeError: boolean
}
export interface OtherExecutionErrorStackTraceEntry {
type: StackTraceEntryType.OTHER_EXECUTION_ERROR
sourceReference?: SourceReference
}
export interface ContractTooLargeErrorStackTraceEntry {
type: StackTraceEntryType.CONTRACT_TOO_LARGE_ERROR
sourceReference?: SourceReference
}
export interface InternalFunctionCallStackEntry {
type: StackTraceEntryType.INTERNAL_FUNCTION_CALLSTACK_ENTRY
pc: number
sourceReference: SourceReference
}
export interface ContractCallRunOutOfGasError {
type: StackTraceEntryType.CONTRACT_CALL_RUN_OUT_OF_GAS_ERROR
sourceReference?: SourceReference
}
export interface ContractAndFunctionName {
contractName: string
functionName: string | undefined
Expand Down Expand Up @@ -852,6 +1006,16 @@ export class Exit {
isError(): boolean
getReason(): string
}
export class ReturnData {
readonly value: Uint8Array
constructor(value: Uint8Array)
isEmpty(): boolean
matchesSelector(selector: Uint8Array): boolean
isErrorReturnData(): boolean
isPanicReturnData(): boolean
decodeError(): string
decodePanic(): bigint
}
export class VmTraceDecoder {
constructor(contractsIdentifier: ContractsIdentifier)
addBytecode(bytecode: Bytecode): void
Expand Down
12 changes: 11 additions & 1 deletion crates/edr_napi/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}

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, VmTraceDecoder, initializeVmTraceDecoder, VmTracer, RawTrace } = nativeBinding
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

module.exports.SpecId = SpecId
module.exports.EdrContext = EdrContext
Expand Down Expand Up @@ -342,6 +342,16 @@ module.exports.isPush = isPush
module.exports.isJump = isJump
module.exports.isCall = isCall
module.exports.isCreate = isCreate
module.exports.ReturnData = ReturnData
module.exports.StackTraceEntryType = StackTraceEntryType
module.exports.stackTraceEntryTypeToString = stackTraceEntryTypeToString
module.exports.FALLBACK_FUNCTION_NAME = FALLBACK_FUNCTION_NAME
module.exports.RECEIVE_FUNCTION_NAME = RECEIVE_FUNCTION_NAME
module.exports.CONSTRUCTOR_FUNCTION_NAME = CONSTRUCTOR_FUNCTION_NAME
module.exports.UNRECOGNIZED_FUNCTION_NAME = UNRECOGNIZED_FUNCTION_NAME
module.exports.UNKNOWN_FUNCTION_NAME = UNKNOWN_FUNCTION_NAME
module.exports.PRECOMPILE_FUNCTION_NAME = PRECOMPILE_FUNCTION_NAME
module.exports.UNRECOGNIZED_CONTRACT_NAME = UNRECOGNIZED_CONTRACT_NAME
module.exports.VmTraceDecoder = VmTraceDecoder
module.exports.initializeVmTraceDecoder = initializeVmTraceDecoder
module.exports.VmTracer = VmTracer
Expand Down
2 changes: 2 additions & 0 deletions crates/edr_napi/src/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ mod contracts_identifier;
mod exit;
mod message_trace;
mod opcodes;
mod return_data;
mod solidity_stack_trace;
mod vm_trace_decoder;
mod vm_tracer;

Expand Down
84 changes: 84 additions & 0 deletions crates/edr_napi/src/trace/return_data.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//! Rewrite of `hardhat-network/provider/return-data.ts` from Hardhat.

use alloy_sol_types::SolError;
use napi::bindgen_prelude::{BigInt, Uint8Array};
use napi_derive::napi;

// Built-in error types
// See <https://docs.soliditylang.org/en/v0.8.26/control-structures.html#error-handling-assert-require-revert-and-exceptions>
alloy_sol_types::sol! {
error Error(string);
error Panic(uint256);
}

#[napi]
pub struct ReturnData {
#[napi(readonly)]
pub value: Uint8Array,
selector: Option<[u8; 4]>,
}

#[napi]
impl ReturnData {
#[napi(constructor)]
pub fn new(value: Uint8Array) -> Self {
let selector = if value.len() >= 4 {
Some(value[0..4].try_into().unwrap())
} else {
None
};

Self { value, selector }
}

#[napi]
pub fn is_empty(&self) -> bool {
self.value.is_empty()
}

#[napi]
pub fn matches_selector(&self, selector: Uint8Array) -> bool {
self.selector.map_or(false, |value| value == selector[..])
}

#[napi]
pub fn is_error_return_data(&self) -> bool {
self.selector == Some(Error::SELECTOR)
}

#[napi]
pub fn is_panic_return_data(&self) -> bool {
self.selector == Some(Panic::SELECTOR)
}

#[napi]
pub fn decode_error(&self) -> napi::Result<String> {
if self.is_empty() {
return Ok(String::new());
}

let result = Error::abi_decode(&self.value[..], false).map_err(|_err| {
napi::Error::new(
napi::Status::InvalidArg,
"Expected return data to be a Error(string) and contain a valid string",
)
})?;

Ok(result._0)
}

#[napi]
pub fn decode_panic(&self) -> napi::Result<BigInt> {
let result = Panic::abi_decode(&self.value[..], false).map_err(|_err| {
napi::Error::new(
napi::Status::InvalidArg,
"Expected return data to be a Error(string) and contain a valid string",
)
})?;

Ok(BigInt {
sign_bit: false,
words: result._0.as_limbs().to_vec(),
})
}
}
Loading
Loading