log_processor_test.go 6.22 KB
Newer Older
1 2 3 4
package source

import (
	"context"
5
	"fmt"
6 7 8
	"testing"

	"github.com/ethereum-optimism/optimism/op-service/eth"
9 10
	"github.com/ethereum-optimism/optimism/op-service/predeploys"
	backendTypes "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/types"
11
	supTypes "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
12
	"github.com/ethereum/go-ethereum/common"
13
	ethTypes "github.com/ethereum/go-ethereum/core/types"
14 15 16
	"github.com/stretchr/testify/require"
)

17 18
var logProcessorChainID = supTypes.ChainIDFromUInt64(4)

19 20 21 22 23
func TestLogProcessor(t *testing.T) {
	ctx := context.Background()
	block1 := eth.L1BlockRef{Number: 100, Hash: common.Hash{0x11}, Time: 1111}
	t.Run("NoOutputWhenLogsAreEmpty", func(t *testing.T) {
		store := &stubLogStorage{}
24
		processor := newLogProcessor(logProcessorChainID, store)
25

26
		err := processor.ProcessLogs(ctx, block1, ethTypes.Receipts{})
27 28 29 30 31
		require.NoError(t, err)
		require.Empty(t, store.logs)
	})

	t.Run("OutputLogs", func(t *testing.T) {
32
		rcpts := ethTypes.Receipts{
33
			{
34
				Logs: []*ethTypes.Log{
35 36 37 38 39 40 41 42 43 44 45 46 47
					{
						Address: common.Address{0x11},
						Topics:  []common.Hash{{0xaa}},
						Data:    []byte{0xbb},
					},
					{
						Address: common.Address{0x22},
						Topics:  []common.Hash{{0xcc}},
						Data:    []byte{0xdd},
					},
				},
			},
			{
48
				Logs: []*ethTypes.Log{
49 50 51 52 53 54 55 56 57
					{
						Address: common.Address{0x33},
						Topics:  []common.Hash{{0xee}},
						Data:    []byte{0xff},
					},
				},
			},
		}
		store := &stubLogStorage{}
58
		processor := newLogProcessor(logProcessorChainID, store)
59 60 61 62 63 64 65 66

		err := processor.ProcessLogs(ctx, block1, rcpts)
		require.NoError(t, err)
		expected := []storedLog{
			{
				block:     block1.ID(),
				timestamp: block1.Time,
				logIdx:    0,
67
				logHash:   logToLogHash(rcpts[0].Logs[0]),
68 69 70 71 72 73
				execMsg:   nil,
			},
			{
				block:     block1.ID(),
				timestamp: block1.Time,
				logIdx:    0,
74
				logHash:   logToLogHash(rcpts[0].Logs[1]),
75 76 77 78 79 80
				execMsg:   nil,
			},
			{
				block:     block1.ID(),
				timestamp: block1.Time,
				logIdx:    0,
81
				logHash:   logToLogHash(rcpts[1].Logs[0]),
82 83 84 85 86
				execMsg:   nil,
			},
		}
		require.Equal(t, expected, store.logs)
	})
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126

	t.Run("IncludeExecutingMessage", func(t *testing.T) {
		rcpts := ethTypes.Receipts{
			{
				Logs: []*ethTypes.Log{
					{
						Address: predeploys.CrossL2InboxAddr,
						Topics:  []common.Hash{},
						Data:    []byte{0xff},
					},
				},
			},
		}
		execMsg := backendTypes.ExecutingMessage{
			Chain:     4,
			BlockNum:  6,
			LogIdx:    8,
			Timestamp: 10,
			Hash:      backendTypes.TruncatedHash{0xaa},
		}
		store := &stubLogStorage{}
		processor := newLogProcessor(supTypes.ChainID{4}, store)
		processor.eventDecoder = EventDecoderFn(func(l *ethTypes.Log) (backendTypes.ExecutingMessage, error) {
			require.Equal(t, rcpts[0].Logs[0], l)
			return execMsg, nil
		})

		err := processor.ProcessLogs(ctx, block1, rcpts)
		require.NoError(t, err)
		expected := []storedLog{
			{
				block:     block1.ID(),
				timestamp: block1.Time,
				logIdx:    0,
				logHash:   logToLogHash(rcpts[0].Logs[0]),
				execMsg:   &execMsg,
			},
		}
		require.Equal(t, expected, store.logs)
	})
127 128 129
}

func TestToLogHash(t *testing.T) {
130 131
	mkLog := func() *ethTypes.Log {
		return &ethTypes.Log{
132 133 134 135 136 137 138 139 140 141 142 143 144 145
			Address: common.Address{0xaa, 0xbb},
			Topics: []common.Hash{
				{0xcc},
				{0xdd},
			},
			Data:        []byte{0xee, 0xff, 0x00},
			BlockNumber: 12345,
			TxHash:      common.Hash{0x11, 0x22, 0x33},
			TxIndex:     4,
			BlockHash:   common.Hash{0x44, 0x55},
			Index:       8,
			Removed:     false,
		}
	}
146 147 148 149 150 151 152 153
	relevantMods := []func(l *ethTypes.Log){
		func(l *ethTypes.Log) { l.Address = common.Address{0xab, 0xcd} },
		func(l *ethTypes.Log) { l.Topics = append(l.Topics, common.Hash{0x12, 0x34}) },
		func(l *ethTypes.Log) { l.Topics = l.Topics[:len(l.Topics)-1] },
		func(l *ethTypes.Log) { l.Topics[0] = common.Hash{0x12, 0x34} },
		func(l *ethTypes.Log) { l.Data = append(l.Data, 0x56) },
		func(l *ethTypes.Log) { l.Data = l.Data[:len(l.Data)-1] },
		func(l *ethTypes.Log) { l.Data[0] = 0x45 },
154
	}
155 156 157 158 159 160 161
	irrelevantMods := []func(l *ethTypes.Log){
		func(l *ethTypes.Log) { l.BlockNumber = 987 },
		func(l *ethTypes.Log) { l.TxHash = common.Hash{0xab, 0xcd} },
		func(l *ethTypes.Log) { l.TxIndex = 99 },
		func(l *ethTypes.Log) { l.BlockHash = common.Hash{0xab, 0xcd} },
		func(l *ethTypes.Log) { l.Index = 98 },
		func(l *ethTypes.Log) { l.Removed = true },
162
	}
163
	refHash := logToLogHash(mkLog())
164
	// The log hash is stored in the database so test that it matches the actual value.
165 166
	// If this changes, compatibility with existing databases may be affected
	expectedRefHash := backendTypes.TruncateHash(common.HexToHash("0x4e1dc08fddeb273275f787762cdfe945cf47bb4e80a1fabbc7a825801e81b73f"))
167 168 169 170 171 172
	require.Equal(t, expectedRefHash, refHash, "reference hash changed, check that database compatibility is not broken")

	// Check that the hash is changed when any data it should include changes
	for i, mod := range relevantMods {
		l := mkLog()
		mod(l)
173
		hash := logToLogHash(l)
174 175 176 177 178 179
		require.NotEqualf(t, refHash, hash, "expected relevant modification %v to affect the hash but it did not", i)
	}
	// Check that the hash is not changed when any data it should not include changes
	for i, mod := range irrelevantMods {
		l := mkLog()
		mod(l)
180
		hash := logToLogHash(l)
181 182 183 184 185 186 187 188
		require.Equal(t, refHash, hash, "expected irrelevant modification %v to not affect the hash but it did", i)
	}
}

type stubLogStorage struct {
	logs []storedLog
}

189
func (s *stubLogStorage) AddLog(chainID supTypes.ChainID, logHash backendTypes.TruncatedHash, block eth.BlockID, timestamp uint64, logIdx uint32, execMsg *backendTypes.ExecutingMessage) error {
190 191 192
	if logProcessorChainID != chainID {
		return fmt.Errorf("chain id mismatch, expected %v but got %v", logProcessorChainID, chainID)
	}
193 194 195 196 197 198 199 200 201 202 203 204 205 206
	s.logs = append(s.logs, storedLog{
		block:     block,
		timestamp: timestamp,
		logIdx:    logIdx,
		logHash:   logHash,
		execMsg:   execMsg,
	})
	return nil
}

type storedLog struct {
	block     eth.BlockID
	timestamp uint64
	logIdx    uint32
207 208 209 210 211 212 213 214
	logHash   backendTypes.TruncatedHash
	execMsg   *backendTypes.ExecutingMessage
}

type EventDecoderFn func(*ethTypes.Log) (backendTypes.ExecutingMessage, error)

func (f EventDecoderFn) DecodeExecutingMessageLog(log *ethTypes.Log) (backendTypes.ExecutingMessage, error) {
	return f(log)
215
}