diff --git a/arbos/programs/api.go b/arbos/programs/api.go index 787f127ea4..65a58a47c2 100644 --- a/arbos/programs/api.go +++ b/arbos/programs/api.go @@ -228,6 +228,9 @@ func newApiClosures( return addr, res, cost, nil } emitLog := func(topics []common.Hash, data []byte) error { + if tracingInfo != nil { + tracingInfo.RecordEmitLog(topics, data) + } if readOnly { return vm.ErrWriteProtection } diff --git a/arbos/util/tracing.go b/arbos/util/tracing.go index 49b82d6d64..f0f101bc20 100644 --- a/arbos/util/tracing.go +++ b/arbos/util/tracing.go @@ -4,6 +4,7 @@ package util import ( + "fmt" "math/big" "github.com/ethereum/go-ethereum/common" @@ -47,6 +48,26 @@ func NewTracingInfo(evm *vm.EVM, from, to common.Address, scenario TracingScenar } } +func (info *TracingInfo) RecordEmitLog(topics []common.Hash, data []byte) { + size := uint64(len(data)) + var args []uint256.Int + args = append(args, *uint256.NewInt(0)) // offset: byte offset in the memory in bytes + args = append(args, *uint256.NewInt(size)) // size: byte size to copy (length of data) + for _, topic := range topics { + args = append(args, HashToUint256(topic)) // topic: 32-byte value. Max topics count is 4 + } + memory := vm.NewMemory() + memory.Resize(size) + memory.Set(0, size, data) + scope := &vm.ScopeContext{ + Memory: memory, + Stack: TracingStackFromArgs(args...), + Contract: info.Contract, + } + logType := fmt.Sprintf("LOG%d", len(topics)) + info.Tracer.CaptureState(0, vm.StringToOp(logType), 0, 0, scope, []byte{}, info.Depth, nil) +} + func (info *TracingInfo) RecordStorageGet(key common.Hash) { tracer := info.Tracer if info.Scenario == TracingDuringEVM { diff --git a/system_tests/program_test.go b/system_tests/program_test.go index 3b4909b639..b05589a1bf 100644 --- a/system_tests/program_test.go +++ b/system_tests/program_test.go @@ -19,6 +19,7 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -739,10 +740,15 @@ func testReturnData(t *testing.T, jit bool) { func TestProgramLogs(t *testing.T) { t.Parallel() - testLogs(t, true) + testLogs(t, true, false) } -func testLogs(t *testing.T, jit bool) { +func TestProgramLogsWithTracing(t *testing.T) { + t.Parallel() + testLogs(t, true, true) +} + +func testLogs(t *testing.T, jit, tracing bool) { builder, auth, cleanup := setupProgramTest(t, jit) ctx := builder.ctx l2info := builder.L2Info @@ -751,6 +757,27 @@ func testLogs(t *testing.T, jit bool) { logAddr := deployWasm(t, ctx, auth, l2client, rustFile("log")) multiAddr := deployWasm(t, ctx, auth, l2client, rustFile("multicall")) + type traceLog struct { + Address common.Address `json:"address"` + Topics []common.Hash `json:"topics"` + Data hexutil.Bytes `json:"data"` + } + traceTx := func(tx *types.Transaction) []traceLog { + type traceLogs struct { + Logs []traceLog `json:"logs"` + } + var trace traceLogs + traceConfig := map[string]interface{}{ + "tracer": "callTracer", + "tracerConfig": map[string]interface{}{ + "withLog": true, + }, + } + rpc := l2client.Client() + err := rpc.CallContext(ctx, &trace, "debug_traceTransaction", tx.Hash(), traceConfig) + Require(t, err) + return trace.Logs + } ensure := func(tx *types.Transaction, err error) *types.Receipt { t.Helper() Require(t, err) @@ -777,6 +804,20 @@ func testLogs(t *testing.T, jit bool) { topics[j] = testhelpers.RandomHash() } data := randBytes(0, 48) + verifyLogTopicsAndData := func(logData []byte, logTopics []common.Hash) { + if !bytes.Equal(logData, data) { + Fatal(t, "data mismatch", logData, data) + } + if len(logTopics) != len(topics) { + Fatal(t, "topics mismatch", len(logTopics), len(topics)) + } + for j := 0; j < i; j++ { + if logTopics[j] != topics[j] { + Fatal(t, "topic mismatch", logTopics, topics) + } + } + } + args := encode(topics, data) tx := l2info.PrepareTxTo("Owner", &logAddr, 1e9, nil, args) receipt := ensure(tx, l2client.SendTransaction(ctx, tx)) @@ -785,16 +826,14 @@ func testLogs(t *testing.T, jit bool) { Fatal(t, "wrong number of logs", len(receipt.Logs)) } log := receipt.Logs[0] - if !bytes.Equal(log.Data, data) { - Fatal(t, "data mismatch", log.Data, data) - } - if len(log.Topics) != len(topics) { - Fatal(t, "topics mismatch", len(log.Topics), len(topics)) - } - for j := 0; j < i; j++ { - if log.Topics[j] != topics[j] { - Fatal(t, "topic mismatch", log.Topics, topics) + verifyLogTopicsAndData(log.Data, log.Topics) + if tracing { + logs := traceTx(tx) + if len(logs) != 1 { + Fatal(t, "wrong number of logs in trace", len(logs)) } + log := logs[0] + verifyLogTopicsAndData(log.Data, log.Topics) } } diff --git a/system_tests/stylus_test.go b/system_tests/stylus_test.go index 21f54a763c..331bca2e9c 100644 --- a/system_tests/stylus_test.go +++ b/system_tests/stylus_test.go @@ -41,7 +41,7 @@ func TestProgramArbitratorReturnData(t *testing.T) { } func TestProgramArbitratorLogs(t *testing.T) { - testLogs(t, false) + testLogs(t, false, false) } func TestProgramArbitratorCreate(t *testing.T) {