diff --git a/pkg/processor/transaction/structlog/columns.go b/pkg/processor/transaction/structlog/columns.go index 07ab948..bd91f7c 100644 --- a/pkg/processor/transaction/structlog/columns.go +++ b/pkg/processor/transaction/structlog/columns.go @@ -29,7 +29,6 @@ type Columns struct { TransactionFailed proto.ColBool TransactionReturnValue *proto.ColNullable[string] Index proto.ColUInt32 - ProgramCounter proto.ColUInt32 Operation proto.ColStr Gas proto.ColUInt64 GasCost proto.ColUInt64 @@ -67,7 +66,6 @@ func (c *Columns) Append( txFailed bool, txReturnValue *string, index uint32, - pc uint32, op string, gas uint64, gasCost uint64, @@ -90,7 +88,6 @@ func (c *Columns) Append( c.TransactionFailed.Append(txFailed) c.TransactionReturnValue.Append(nullableStr(txReturnValue)) c.Index.Append(index) - c.ProgramCounter.Append(pc) c.Operation.Append(op) c.Gas.Append(gas) c.GasCost.Append(gasCost) @@ -116,7 +113,6 @@ func (c *Columns) Reset() { c.TransactionFailed.Reset() c.TransactionReturnValue.Reset() c.Index.Reset() - c.ProgramCounter.Reset() c.Operation.Reset() c.Gas.Reset() c.GasCost.Reset() @@ -143,7 +139,6 @@ func (c *Columns) Input() proto.Input { {Name: "transaction_failed", Data: &c.TransactionFailed}, {Name: "transaction_return_value", Data: c.TransactionReturnValue}, {Name: "index", Data: &c.Index}, - {Name: "program_counter", Data: &c.ProgramCounter}, {Name: "operation", Data: &c.Operation}, {Name: "gas", Data: &c.Gas}, {Name: "gas_cost", Data: &c.GasCost}, 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/processor.go b/pkg/processor/transaction/structlog/processor.go index bc82ff2..0301168 100644 --- a/pkg/processor/transaction/structlog/processor.go +++ b/pkg/processor/transaction/structlog/processor.go @@ -197,7 +197,6 @@ func (p *Processor) insertStructlogs(ctx context.Context, structlogs []Structlog sl.TransactionFailed, sl.TransactionReturnValue, sl.Index, - sl.ProgramCounter, sl.Operation, sl.Gas, sl.GasCost, diff --git a/pkg/processor/transaction/structlog/processor_test.go b/pkg/processor/transaction/structlog/processor_test.go index a676b5f..deb5332 100644 --- a/pkg/processor/transaction/structlog/processor_test.go +++ b/pkg/processor/transaction/structlog/processor_test.go @@ -150,7 +150,6 @@ func TestStructlogCountReturn(t *testing.T) { mockTrace.Failed, // txFailed mockTrace.ReturnValue, // txReturnValue uint32(i), // index - structLog.PC, // pc structLog.Op, // op structLog.Gas, // gas structLog.GasCost, // gasCost @@ -235,7 +234,6 @@ func TestMemoryManagement(t *testing.T) { false, // txFailed nil, // txReturnValue uint32(i), // index - uint32(i*2), // pc "SSTORE", // op uint64(21000-i), // gas uint64(5000), // gasCost @@ -347,7 +345,7 @@ func TestChunkProcessing(t *testing.T) { for i := 0; i < tt.inputSize; i++ { cols.Append( now, uint64(i), "0xtest", uint32(0), uint64(21000), false, nil, - uint32(i), uint32(i), "PUSH1", uint64(20000), uint64(3), uint64(3), uint64(3), uint64(1), + uint32(i), "PUSH1", uint64(20000), uint64(3), uint64(3), uint64(3), uint64(1), nil, nil, nil, nil, uint32(0), []uint32{}, "test", ) } @@ -397,7 +395,7 @@ func TestColumnsAppendAndReset(t *testing.T) { cols.Append( now, uint64(100), "0xabc", uint32(0), uint64(21000), false, &str, - uint32(0), uint32(100), "PUSH1", uint64(20000), uint64(3), uint64(3), uint64(3), uint64(1), + uint32(0), "PUSH1", uint64(20000), uint64(3), uint64(3), uint64(3), uint64(1), nil, &num, nil, nil, uint32(0), []uint32{}, "mainnet", ) @@ -407,7 +405,7 @@ func TestColumnsAppendAndReset(t *testing.T) { for i := 0; i < 99; i++ { cols.Append( now, uint64(100), "0xabc", uint32(0), uint64(21000), false, nil, - uint32(i+1), uint32(100), "PUSH1", uint64(20000), uint64(3), uint64(3), uint64(3), uint64(1), + uint32(i+1), "PUSH1", uint64(20000), uint64(3), uint64(3), uint64(3), uint64(1), nil, nil, nil, nil, uint32(0), []uint32{}, "mainnet", ) } @@ -423,10 +421,10 @@ func TestColumnsInput(t *testing.T) { cols := transaction_structlog.NewColumns() input := cols.Input() - // Verify all 22 columns are present - assert.Len(t, input, 22) + // Verify all 21 columns are present + assert.Len(t, input, 21) assert.Equal(t, "updated_date_time", input[0].Name) - assert.Equal(t, "meta_network_name", input[21].Name) + assert.Equal(t, "meta_network_name", input[20].Name) } // Tests from tasks_test.go diff --git a/pkg/processor/transaction/structlog/transaction_processing.go b/pkg/processor/transaction/structlog/transaction_processing.go index 5c25e97..68c212d 100644 --- a/pkg/processor/transaction/structlog/transaction_processing.go +++ b/pkg/processor/transaction/structlog/transaction_processing.go @@ -28,7 +28,6 @@ type Structlog struct { TransactionFailed bool `json:"transaction_failed"` TransactionReturnValue *string `json:"transaction_return_value"` Index uint32 `json:"index"` - ProgramCounter uint32 `json:"program_counter"` Operation string `json:"operation"` // Gas is the remaining gas before this opcode executes. @@ -183,8 +182,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) + } chunkSize := p.config.ChunkSize if chunkSize == 0 { @@ -235,7 +241,6 @@ func (p *Processor) ProcessTransaction(ctx context.Context, block execution.Bloc trace.Failed, trace.ReturnValue, uint32(i), //nolint:gosec // index is bounded by structlogs length - sl.PC, sl.Op, sl.Gas, sl.GasCost, @@ -277,7 +282,6 @@ func (p *Processor) ProcessTransaction(ctx context.Context, block execution.Bloc trace.Failed, trace.ReturnValue, uint32(i), //nolint:gosec // Same index as parent CALL - uint32(0), // No PC for EOA "", // Empty = synthetic EOA frame uint64(0), // Gas uint64(0), // GasCost @@ -542,8 +546,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)) @@ -571,7 +581,6 @@ func (p *Processor) ExtractStructlogs(ctx context.Context, block execution.Block TransactionFailed: trace.Failed, TransactionReturnValue: trace.ReturnValue, Index: uint32(i), //nolint:gosec // index is bounded by structlogs length - ProgramCounter: structLog.PC, Operation: structLog.Op, Gas: structLog.Gas, GasCost: structLog.GasCost, @@ -621,7 +630,6 @@ func (p *Processor) ExtractStructlogs(ctx context.Context, block execution.Block TransactionFailed: trace.Failed, TransactionReturnValue: trace.ReturnValue, Index: uint32(i), //nolint:gosec // Same index as parent CALL - ProgramCounter: 0, // No PC for EOA Operation: "", // Empty = synthetic EOA frame Gas: 0, GasCost: 0,