diff --git a/pkg/ethereum/execution/embedded_node.go b/pkg/ethereum/execution/embedded_node.go index aba19ca..ff0c3ff 100644 --- a/pkg/ethereum/execution/embedded_node.go +++ b/pkg/ethereum/execution/embedded_node.go @@ -124,6 +124,7 @@ func (n *EmbeddedNode) MarkReady(ctx context.Context) error { for i, cb := range callbacks { n.log.WithField("callback_index", i).Info("Executing OnReady callback") + if err := cb(ctx); err != nil { n.log.WithError(err).Error("Failed to execute OnReady callback") diff --git a/pkg/processor/transaction/structlog/gas_cost.go b/pkg/processor/transaction/structlog/gas_cost.go index cf410bf..2167880 100644 --- a/pkg/processor/transaction/structlog/gas_cost.go +++ b/pkg/processor/transaction/structlog/gas_cost.go @@ -66,6 +66,26 @@ func hasPrecomputedGasUsed(structlogs []execution.StructLog) bool { return structlogs[0].GasUsed > 0 } +// hasPrecomputedCreateAddresses detects whether CREATE/CREATE2 addresses are pre-computed. +// +// In embedded mode, the tracer resolves CREATE addresses inline when the constructor +// returns, populating CallToAddress. In RPC mode, CallToAddress is nil for CREATE +// opcodes and must be computed post-hoc using ComputeCreateAddresses(). +// +// Returns true if any CREATE/CREATE2 opcode has CallToAddress pre-populated. +func hasPrecomputedCreateAddresses(structlogs []execution.StructLog) bool { + for i := range structlogs { + op := structlogs[i].Op + if op == OpcodeCREATE || op == OpcodeCREATE2 { + // If any CREATE has CallToAddress populated, tracer pre-computed. + return structlogs[i].CallToAddress != nil + } + } + + // No CREATE/CREATE2 opcodes found - doesn't matter, return false to use standard path. + return false +} + // ComputeGasUsed calculates the actual gas consumed for each structlog using // the difference between consecutive gas values at the same depth level. // diff --git a/pkg/processor/transaction/structlog/gas_cost_test.go b/pkg/processor/transaction/structlog/gas_cost_test.go index b645bb3..c1f5f75 100644 --- a/pkg/processor/transaction/structlog/gas_cost_test.go +++ b/pkg/processor/transaction/structlog/gas_cost_test.go @@ -32,6 +32,48 @@ func TestHasPrecomputedGasUsed_WithoutGasUsed(t *testing.T) { assert.False(t, hasPrecomputedGasUsed(structlogs)) } +// ============================================================================= +// hasPrecomputedCreateAddresses Tests +// ============================================================================= + +func TestHasPrecomputedCreateAddresses_Empty(t *testing.T) { + assert.False(t, hasPrecomputedCreateAddresses(nil)) + assert.False(t, hasPrecomputedCreateAddresses([]execution.StructLog{})) +} + +func TestHasPrecomputedCreateAddresses_NoCreate(t *testing.T) { + structlogs := []execution.StructLog{ + {Op: "PUSH1"}, + {Op: "CALL"}, + } + assert.False(t, hasPrecomputedCreateAddresses(structlogs)) +} + +func TestHasPrecomputedCreateAddresses_CreateWithAddress(t *testing.T) { + addr := "0x1234567890123456789012345678901234567890" + structlogs := []execution.StructLog{ + {Op: "PUSH1"}, + {Op: "CREATE", CallToAddress: &addr}, + } + assert.True(t, hasPrecomputedCreateAddresses(structlogs)) +} + +func TestHasPrecomputedCreateAddresses_CreateWithoutAddress(t *testing.T) { + structlogs := []execution.StructLog{ + {Op: "PUSH1"}, + {Op: "CREATE", CallToAddress: nil}, + } + assert.False(t, hasPrecomputedCreateAddresses(structlogs)) +} + +func TestHasPrecomputedCreateAddresses_Create2WithAddress(t *testing.T) { + addr := "0x1234567890123456789012345678901234567890" + structlogs := []execution.StructLog{ + {Op: "CREATE2", CallToAddress: &addr}, + } + assert.True(t, hasPrecomputedCreateAddresses(structlogs)) +} + // ============================================================================= // ComputeGasUsed Tests // ============================================================================= diff --git a/pkg/processor/transaction/structlog/transaction_processing.go b/pkg/processor/transaction/structlog/transaction_processing.go index a315872..d133cf0 100644 --- a/pkg/processor/transaction/structlog/transaction_processing.go +++ b/pkg/processor/transaction/structlog/transaction_processing.go @@ -171,8 +171,15 @@ func (p *Processor) ProcessTransaction(ctx context.Context, block execution.Bloc // Initialize call frame tracker callTracker := NewCallTracker() - // Pre-compute CREATE/CREATE2 addresses from trace stack - createAddresses := ComputeCreateAddresses(trace.Structlogs) + // Check if CREATE/CREATE2 addresses are pre-computed by the tracer (embedded mode). + // In embedded mode, skip the multi-pass ComputeCreateAddresses scan. + precomputedCreateAddresses := hasPrecomputedCreateAddresses(trace.Structlogs) + + var createAddresses map[int]*string + if !precomputedCreateAddresses { + // Pre-compute CREATE/CREATE2 addresses from trace stack (RPC mode) + createAddresses = ComputeCreateAddresses(trace.Structlogs) + } // Check if this is a big transaction and register if needed if totalCount >= p.bigTxManager.GetThreshold() { @@ -592,8 +599,14 @@ func (p *Processor) ExtractStructlogs(ctx context.Context, block execution.Block // Initialize call frame tracker callTracker := NewCallTracker() - // Pre-compute CREATE/CREATE2 addresses from trace stack - createAddresses := ComputeCreateAddresses(trace.Structlogs) + // Check if CREATE/CREATE2 addresses are pre-computed by the tracer (embedded mode). + precomputedCreateAddresses := hasPrecomputedCreateAddresses(trace.Structlogs) + + var createAddresses map[int]*string + if !precomputedCreateAddresses { + // Pre-compute CREATE/CREATE2 addresses from trace stack (RPC mode) + createAddresses = ComputeCreateAddresses(trace.Structlogs) + } // Pre-allocate slice for better memory efficiency structlogs = make([]Structlog, 0, len(trace.Structlogs))