Documentation
¶
Index ¶
- Constants
- func SanitizeGasCost(log *StructLog)
- func SanitizeStructLogs(logs []StructLog)
- type Address
- type Block
- type Config
- type DataSource
- type EmbeddedNode
- func (n *EmbeddedNode) BlockByNumber(ctx context.Context, number *big.Int) (Block, error)
- func (n *EmbeddedNode) BlockNumber(ctx context.Context) (*uint64, error)
- func (n *EmbeddedNode) BlockReceipts(ctx context.Context, number *big.Int) ([]Receipt, error)
- func (n *EmbeddedNode) BlocksByNumbers(ctx context.Context, numbers []*big.Int) ([]Block, error)
- func (n *EmbeddedNode) ChainID() int64
- func (n *EmbeddedNode) ClientType() string
- func (n *EmbeddedNode) DebugTraceTransaction(ctx context.Context, hash string, blockNumber *big.Int, opts TraceOptions) (*TraceTransaction, error)
- func (n *EmbeddedNode) IsReady() bool
- func (n *EmbeddedNode) IsSynced() bool
- func (n *EmbeddedNode) MarkReady(ctx context.Context) error
- func (n *EmbeddedNode) Name() string
- func (n *EmbeddedNode) OnReady(_ context.Context, callback func(ctx context.Context) error)
- func (n *EmbeddedNode) Start(_ context.Context) error
- func (n *EmbeddedNode) Stop(_ context.Context) error
- func (n *EmbeddedNode) TransactionReceipt(ctx context.Context, hash string) (Receipt, error)
- type ErigonResult
- type ErigonStructLog
- type Hash
- type Node
- type Receipt
- type StructLog
- type TraceOptions
- type TraceTransaction
- type Transaction
Constants ¶
const ( LegacyTxType = 0 AccessListTxType = 1 DynamicFeeTxType = 2 BlobTxType = 3 )
Transaction type constants matching go-ethereum values.
Variables ¶
This section is empty.
Functions ¶
func SanitizeGasCost ¶ added in v0.1.5
func SanitizeGasCost(log *StructLog)
SanitizeGasCost detects and corrects corrupted gasCost values from Erigon's debug_traceTransaction RPC.
Bug: Erigon has an unsigned integer underflow bug in gas.go:callGas() where `availableGas - base` underflows when availableGas < base, producing huge corrupted values (e.g., 18158513697557845033).
Detection: gasCost can never legitimately exceed the available gas at that opcode. If gasCost > Gas, the value is corrupted.
Correction: Set gasCost = Gas (all available gas consumed), matching Reth's behavior for failed CALL opcodes.
func SanitizeStructLogs ¶ added in v0.1.5
func SanitizeStructLogs(logs []StructLog)
SanitizeStructLogs applies gas cost sanitization to all structlogs. This corrects corrupted values from Erigon's unsigned integer underflow bug.
Types ¶
type Address ¶ added in v0.1.5
type Address [20]byte
Address represents a 20-byte Ethereum address.
type Block ¶ added in v0.1.5
type Block interface {
// Number returns the block number.
Number() *big.Int
// Hash returns the block hash.
Hash() Hash
// ParentHash returns the parent block hash.
ParentHash() Hash
// BaseFee returns the base fee per gas (EIP-1559), or nil for pre-London blocks.
BaseFee() *big.Int
// Transactions returns all transactions in the block.
Transactions() []Transaction
}
Block interface defines methods for accessing block data. Implementations are provided by data sources (RPC, embedded clients).
type Config ¶
type DataSource ¶ added in v0.1.5
type DataSource interface {
// BlockNumber returns the current block number.
BlockNumber(ctx context.Context) (*uint64, error)
// BlockByNumber returns the block at the given number.
BlockByNumber(ctx context.Context, number *big.Int) (Block, error)
// BlocksByNumbers returns blocks at the given numbers.
// Returns blocks up to the first not-found (contiguous only).
BlocksByNumbers(ctx context.Context, numbers []*big.Int) ([]Block, error)
// BlockReceipts returns all receipts for the block at the given number.
BlockReceipts(ctx context.Context, number *big.Int) ([]Receipt, error)
// TransactionReceipt returns the receipt for the transaction with the given hash.
TransactionReceipt(ctx context.Context, hash string) (Receipt, error)
// DebugTraceTransaction returns the execution trace for the transaction.
DebugTraceTransaction(ctx context.Context, hash string, blockNumber *big.Int, opts TraceOptions) (*TraceTransaction, error)
// ChainID returns the chain ID.
ChainID() int64
// ClientType returns the client type/version string.
ClientType() string
// IsSynced returns true if the data source is fully synced.
IsSynced() bool
}
DataSource is the interface host applications implement to provide execution data directly without JSON-RPC. This enables embedding execution-processor as a library within an execution client.
All methods must be safe for concurrent calls from multiple goroutines. Context cancellation should be respected for all I/O operations.
The interface uses abstract types (Block, Transaction, Receipt) instead of go-ethereum types to avoid CGO dependencies. Host applications should implement these interfaces with their own types.
Example implementation:
type MyDataSource struct {
client *MyExecutionClient
}
func (ds *MyDataSource) BlockNumber(ctx context.Context) (*uint64, error) {
num := ds.client.CurrentBlock()
return &num, nil
}
type EmbeddedNode ¶ added in v0.1.5
type EmbeddedNode struct {
// contains filtered or unexported fields
}
EmbeddedNode implements Node by delegating to a DataSource. This allows host applications to provide execution data directly without going through JSON-RPC, eliminating serialization overhead.
Lifecycle:
- Create with NewEmbeddedNode(log, name, dataSource)
- Register OnReady callbacks (optional)
- Pool calls Start() (no-op for embedded)
- Host calls MarkReady() when DataSource is ready to serve data
- Callbacks execute in registration order, node becomes healthy in pool
- Pool calls Stop() on shutdown (no-op for embedded)
Thread-safety: All methods are safe for concurrent use.
func NewEmbeddedNode ¶ added in v0.1.5
func NewEmbeddedNode(log logrus.FieldLogger, name string, source DataSource) *EmbeddedNode
NewEmbeddedNode creates a new EmbeddedNode with the given DataSource.
Parameters:
- log: Logger for node operations
- name: Human-readable name for this node (used in logs and metrics)
- source: DataSource implementation providing execution data
The returned node is not yet ready. Call MarkReady() when the DataSource is ready to serve data.
func (*EmbeddedNode) BlockByNumber ¶ added in v0.1.5
BlockByNumber delegates to the DataSource.
func (*EmbeddedNode) BlockNumber ¶ added in v0.1.5
func (n *EmbeddedNode) BlockNumber(ctx context.Context) (*uint64, error)
BlockNumber delegates to the DataSource.
func (*EmbeddedNode) BlockReceipts ¶ added in v0.1.5
BlockReceipts delegates to the DataSource.
func (*EmbeddedNode) BlocksByNumbers ¶ added in v0.1.5
BlocksByNumbers delegates to the DataSource.
func (*EmbeddedNode) ChainID ¶ added in v0.1.5
func (n *EmbeddedNode) ChainID() int64
ChainID delegates to the DataSource.
func (*EmbeddedNode) ClientType ¶ added in v0.1.5
func (n *EmbeddedNode) ClientType() string
ClientType delegates to the DataSource.
func (*EmbeddedNode) DebugTraceTransaction ¶ added in v0.1.5
func (n *EmbeddedNode) DebugTraceTransaction( ctx context.Context, hash string, blockNumber *big.Int, opts TraceOptions, ) (*TraceTransaction, error)
DebugTraceTransaction delegates to the DataSource.
OPTIMIZATION: In embedded mode, the tracer extracts CallToAddress directly for CALL-family opcodes instead of capturing the full stack. We explicitly set DisableStack: true to signal this intent, even though the tracer ignores this setting (it always uses the optimized path).
func (*EmbeddedNode) IsReady ¶ added in v0.1.5
func (n *EmbeddedNode) IsReady() bool
IsReady returns true if the node has been marked as ready.
func (*EmbeddedNode) IsSynced ¶ added in v0.1.5
func (n *EmbeddedNode) IsSynced() bool
IsSynced delegates to the DataSource.
func (*EmbeddedNode) MarkReady ¶ added in v0.1.5
func (n *EmbeddedNode) MarkReady(ctx context.Context) error
MarkReady is called by the host application when the DataSource is ready. This triggers all registered OnReady callbacks.
func (*EmbeddedNode) Name ¶ added in v0.1.5
func (n *EmbeddedNode) Name() string
Name returns the configured name for this node.
func (*EmbeddedNode) OnReady ¶ added in v0.1.5
OnReady registers a callback to be called when the node becomes ready.
func (*EmbeddedNode) Start ¶ added in v0.1.5
func (n *EmbeddedNode) Start(_ context.Context) error
Start is a no-op for EmbeddedNode. The host controls readiness via MarkReady().
func (*EmbeddedNode) Stop ¶ added in v0.1.5
func (n *EmbeddedNode) Stop(_ context.Context) error
Stop is a no-op for EmbeddedNode. The host manages the DataSource lifecycle.
func (*EmbeddedNode) TransactionReceipt ¶ added in v0.1.5
TransactionReceipt delegates to the DataSource.
type ErigonResult ¶
type ErigonResult struct {
Gas uint64 `json:"gas"`
Failed bool `json:"failed"`
ReturnValue *string `json:"returnValue"`
// empty array on transfer
StructLogs []ErigonStructLog `json:"structLogs"`
}
type ErigonStructLog ¶
type ErigonStructLog struct {
PC uint32 `json:"pc"`
Op string `json:"op"`
Gas uint64 `json:"gas"`
GasCost uint64 `json:"gasCost"`
Depth uint64 `json:"depth"`
ReturnData []byte `json:"returnData"`
Refund *uint64 `json:"refund,omitempty"`
Error *string `json:"error,omitempty"`
Stack *[]string `json:"stack,omitempty"`
}
type Hash ¶ added in v0.1.5
type Hash [32]byte
Hash represents a 32-byte hash.
type Node ¶
type Node interface {
// Start initializes the node and begins any background operations.
// For RPCNode, this establishes the RPC connection and starts health monitoring.
// For EmbeddedNode, this is a no-op as the host controls the DataSource lifecycle.
Start(ctx context.Context) error
// Stop gracefully shuts down the node and releases resources.
// Should be called when the node is no longer needed.
Stop(ctx context.Context) error
// OnReady registers a callback to be invoked when the node becomes ready.
// For RPCNode, callbacks execute when the RPC connection is healthy.
// For EmbeddedNode, callbacks execute when MarkReady is called by the host.
// Multiple callbacks can be registered and will execute in registration order.
OnReady(ctx context.Context, callback func(ctx context.Context) error)
// BlockNumber returns the current block number from the execution client.
BlockNumber(ctx context.Context) (*uint64, error)
// BlockByNumber returns the block at the given number.
BlockByNumber(ctx context.Context, number *big.Int) (Block, error)
// BlocksByNumbers returns blocks at the given numbers using batch RPC.
// Returns blocks up to the first not-found (contiguous only).
// If a block is not found, the returned slice contains all blocks before that point.
BlocksByNumbers(ctx context.Context, numbers []*big.Int) ([]Block, error)
// BlockReceipts returns all receipts for the block at the given number.
BlockReceipts(ctx context.Context, number *big.Int) ([]Receipt, error)
// TransactionReceipt returns the receipt for the transaction with the given hash.
TransactionReceipt(ctx context.Context, hash string) (Receipt, error)
// DebugTraceTransaction returns the execution trace for the transaction.
DebugTraceTransaction(ctx context.Context, hash string, blockNumber *big.Int, opts TraceOptions) (*TraceTransaction, error)
// ChainID returns the chain ID reported by the execution client.
ChainID() int64
// ClientType returns the client type/version string (e.g., "geth/1.10.0").
ClientType() string
// IsSynced returns true if the execution client is fully synced.
IsSynced() bool
// Name returns the configured name for this node.
Name() string
}
Node defines the interface for execution data providers.
Implementations include:
- geth.RPCNode: connects to execution clients via JSON-RPC over HTTP
- EmbeddedNode: receives data directly from host application via DataSource
All methods must be safe for concurrent use by multiple goroutines.
Lifecycle:
- Create node with appropriate constructor (geth.NewRPCNode or NewEmbeddedNode)
- Register OnReady callbacks before calling Start
- Call Start to begin initialization
- Node signals readiness by executing OnReady callbacks
- Call Stop for graceful shutdown
type Receipt ¶ added in v0.1.5
type Receipt interface {
// Status returns the transaction status (1=success, 0=failure).
Status() uint64
// TxHash returns the transaction hash.
TxHash() Hash
// GasUsed returns the post-refund gas used by the transaction (what the user pays).
//
// EIP-7778 context: This remains post-refund. The EIP-7778 split between receipt gas
// and block gas only affects ExecutionResult at the EVM layer; the Receipt's GasUsed
// field and its derivation from CumulativeGasUsed are unchanged.
GasUsed() uint64
}
Receipt interface defines methods for accessing transaction receipt data.
type StructLog ¶
type StructLog struct {
// PC is the program counter. Kept for RPC backward compatibility but not
// populated in embedded mode (always 0).
PC uint32 `json:"pc"`
// Op is the opcode name (e.g., "PUSH1", "CALL", "SSTORE").
Op string `json:"op"`
// Gas is the remaining gas before this opcode executes.
Gas uint64 `json:"gas"`
// GasCost is the static gas cost of the opcode (may differ from actual GasUsed).
GasCost uint64 `json:"gasCost"`
// GasUsed is the actual gas consumed by this opcode.
// In embedded mode: pre-computed by tracer using gas difference to next opcode.
// In RPC mode: computed post-hoc by ComputeGasUsed(), this field will be 0.
GasUsed uint64 `json:"gasUsed,omitempty"`
// Depth is the call stack depth (1 = top-level, increases with CALL/CREATE).
Depth uint64 `json:"depth"`
// ReturnData contains the return data from the last CALL/STATICCALL/etc.
ReturnData *string `json:"returnData"`
// Refund is the gas refund counter value.
Refund *uint64 `json:"refund,omitempty"`
// Error contains any error message if the opcode failed.
Error *string `json:"error,omitempty"`
// Stack contains the EVM stack state (RPC mode only).
// In embedded mode this is nil - use CallToAddress instead.
Stack *[]string `json:"stack,omitempty"`
// CallToAddress is the target address for CALL/STATICCALL/DELEGATECALL/CALLCODE.
// In embedded mode: pre-extracted by tracer from stack[len-2].
// In RPC mode: nil, extracted post-hoc from Stack by extractCallAddress().
CallToAddress *string `json:"callToAddress,omitempty"`
// MemorySize is the EVM memory size in bytes at the time this opcode executes.
// Used to compute memory expansion gas between consecutive opcodes.
// In embedded mode: captured by tracer from scope.MemoryData().
// In RPC mode: 0 (not available).
MemorySize uint32 `json:"memSize,omitempty"`
// CallTransfersValue indicates whether a CALL/CALLCODE transfers non-zero ETH value.
// True only for CALL/CALLCODE with value > 0 on the stack.
// Used to normalize CALL gas for cold access detection.
CallTransfersValue bool `json:"callTransfersValue,omitempty"`
// ExtCodeCopySize is the size parameter for EXTCODECOPY opcodes.
// Used to compute the copy cost component for cold access detection.
ExtCodeCopySize uint32 `json:"extCodeCopySize,omitempty"`
}
StructLog represents a single EVM opcode execution trace entry.
This struct supports two operation modes:
- RPC mode: Stack is populated for CALL opcodes, CallToAddress/GasUsed computed post-hoc
- Embedded mode: CallToAddress/GasUsed pre-computed by tracer, Stack remains nil
The embedded mode optimizations eliminate ~99% of stack-related allocations and remove the post-processing GasUsed computation pass.
type TraceOptions ¶ added in v0.0.11
type TraceOptions struct {
DisableStorage bool
DisableStack bool
DisableMemory bool
EnableReturnData bool
}
TraceOptions configures debug_traceTransaction parameters.
func DefaultTraceOptions ¶ added in v0.0.11
func DefaultTraceOptions() TraceOptions
DefaultTraceOptions returns standard options.
func StackTraceOptions ¶ added in v0.0.11
func StackTraceOptions() TraceOptions
StackTraceOptions returns options with stack enabled.
type TraceTransaction ¶
type TraceTransaction struct {
// Gas is the post-refund gas used by this transaction (what the user pays).
// Set by the data source from the execution result or receipt's GasUsed.
//
// EIP-7778 context: After EIP-7778, Ethereum splits gas accounting into:
// - ReceiptGasUsed (post-refund): what the user pays, stored in receipts
// - BlockGasUsed (pre-refund): used for block gas limit accounting
// This field carries the receipt (post-refund) value. The computeIntrinsicGas()
// formula in structlog_agg depends on this being post-refund.
Gas uint64 `json:"gas"`
Failed bool `json:"failed"`
ReturnValue *string `json:"returnValue"`
Structlogs []StructLog
}
TraceTransaction holds the result of a debug_traceTransaction call.
type Transaction ¶ added in v0.1.5
type Transaction interface {
// Hash returns the transaction hash.
Hash() Hash
// Type returns the transaction type (0=legacy, 1=access list, 2=dynamic fee, 3=blob).
Type() uint8
// To returns the recipient address, or nil for contract creation.
To() *Address
// From returns the sender address.
// This is computed by the data source using types.Sender() or equivalent.
From() Address
// Nonce returns the sender account nonce.
Nonce() uint64
// Gas returns the gas limit.
Gas() uint64
// GasPrice returns the gas price (for legacy transactions).
GasPrice() *big.Int
// GasTipCap returns the max priority fee per gas (EIP-1559).
GasTipCap() *big.Int
// GasFeeCap returns the max fee per gas (EIP-1559).
GasFeeCap() *big.Int
// Value returns the value transferred in wei.
Value() *big.Int
// Data returns the input data (calldata).
Data() []byte
// Size returns the encoded transaction size in bytes.
Size() uint64
// ChainId returns the chain ID, or nil for legacy transactions.
ChainId() *big.Int
// BlobGas returns the blob gas used (for blob transactions).
BlobGas() uint64
// BlobGasFeeCap returns the max blob fee per gas (for blob transactions).
BlobGasFeeCap() *big.Int
// BlobHashes returns the versioned hashes (for blob transactions).
BlobHashes() []Hash
}
Transaction interface defines methods for accessing transaction data. The From() method returns the sender address, computed by the data source using its own crypto implementation (avoiding go-ethereum crypto imports).