1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
package derive
import (
"context"
"io"
"math/rand"
"testing"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-node/testutils"
"github.com/ethereum-optimism/optimism/op-service/eth"
)
type fakeDataIter struct {
idx int
data []eth.Data
errs []error
}
func (cs *fakeDataIter) Next(ctx context.Context) (eth.Data, error) {
i := cs.idx
cs.idx += 1
return cs.data[i], cs.errs[i]
}
type MockDataSource struct {
mock.Mock
}
func (m *MockDataSource) OpenData(ctx context.Context, id eth.BlockID, batcherAddr common.Address) DataIter {
out := m.Mock.MethodCalled("OpenData", id, batcherAddr)
return out[0].(DataIter)
}
func (m *MockDataSource) ExpectOpenData(id eth.BlockID, iter DataIter, batcherAddr common.Address) {
m.Mock.On("OpenData", id, batcherAddr).Return(iter)
}
var _ DataAvailabilitySource = (*MockDataSource)(nil)
type MockL1Traversal struct {
mock.Mock
}
func (m *MockL1Traversal) ExpectSystemConfig(sysCfg eth.SystemConfig) {
m.Mock.On("SystemConfig").Return(sysCfg)
}
func (m *MockL1Traversal) SystemConfig() eth.SystemConfig {
out := m.Mock.MethodCalled("SystemConfig")
return out[0].(eth.SystemConfig)
}
func (m *MockL1Traversal) Origin() eth.L1BlockRef {
out := m.Mock.MethodCalled("Origin")
return out[0].(eth.L1BlockRef)
}
func (m *MockL1Traversal) ExpectOrigin(block eth.L1BlockRef) {
m.Mock.On("Origin").Return(block)
}
func (m *MockL1Traversal) NextL1Block(_ context.Context) (eth.L1BlockRef, error) {
out := m.Mock.MethodCalled("NextL1Block")
return out[0].(eth.L1BlockRef), *out[1].(*error)
}
func (m *MockL1Traversal) ExpectNextL1Block(block eth.L1BlockRef, err error) {
m.Mock.On("NextL1Block").Return(block, &err)
}
var _ NextBlockProvider = (*MockL1Traversal)(nil)
// TestL1RetrievalReset tests the reset. The reset just opens up a new
// data for the specified block.
func TestL1RetrievalReset(t *testing.T) {
rng := rand.New(rand.NewSource(1234))
dataSrc := &MockDataSource{}
a := testutils.RandomBlockRef(rng)
l1Cfg := eth.SystemConfig{
BatcherAddr: common.Address{42},
}
dataSrc.ExpectOpenData(a.ID(), &fakeDataIter{}, l1Cfg.BatcherAddr)
defer dataSrc.AssertExpectations(t)
l1r := NewL1Retrieval(testlog.Logger(t, log.LvlError), dataSrc, nil)
// We assert that it opens up the correct data on a reset
_ = l1r.Reset(context.Background(), a, l1Cfg)
}
// TestL1RetrievalNextData tests that the `NextData` function properly
// handles different error cases and returns the expected data
// if there is no error.
func TestL1RetrievalNextData(t *testing.T) {
rng := rand.New(rand.NewSource(1234))
a := testutils.RandomBlockRef(rng)
tests := []struct {
name string
prevBlock eth.L1BlockRef
sysCfg eth.SystemConfig
prevErr error // error returned by prev.NextL1Block
openErr error // error returned by NextData if prev.NextL1Block fails
datas []eth.Data
datasErrs []error
expectedErrs []error
}{
{
name: "simple retrieval",
prevBlock: a,
sysCfg: eth.SystemConfig{BatcherAddr: common.Address{0x55}},
prevErr: nil,
openErr: nil,
datas: []eth.Data{testutils.RandomData(rng, 10), testutils.RandomData(rng, 10), testutils.RandomData(rng, 10), nil},
datasErrs: []error{nil, nil, nil, io.EOF},
expectedErrs: []error{nil, nil, nil, io.EOF},
},
{
name: "out of data",
prevErr: io.EOF,
openErr: io.EOF,
},
{
name: "fail to open data",
prevBlock: a,
sysCfg: eth.SystemConfig{BatcherAddr: common.Address{0x55}},
prevErr: nil,
openErr: nil,
datas: []eth.Data{nil},
datasErrs: []error{NewCriticalError(ethereum.NotFound)},
expectedErrs: []error{ErrCritical},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
l1t := &MockL1Traversal{}
l1t.ExpectNextL1Block(test.prevBlock, test.prevErr)
dataSrc := &MockDataSource{}
dataSrc.ExpectOpenData(test.prevBlock.ID(), &fakeDataIter{data: test.datas, errs: test.datasErrs}, test.sysCfg.BatcherAddr)
ret := NewL1Retrieval(testlog.Logger(t, log.LvlCrit), dataSrc, l1t)
// If prevErr != nil we forced an error while getting data from the previous stage
if test.openErr != nil {
data, err := ret.NextData(context.Background())
require.Nil(t, data)
require.ErrorIs(t, err, test.openErr)
}
// Go through the fake data an assert that data is passed through and the correct
// errors are returned.
for i := range test.expectedErrs {
l1t.ExpectSystemConfig(test.sysCfg)
data, err := ret.NextData(context.Background())
require.Equal(t, test.datas[i], hexutil.Bytes(data))
require.ErrorIs(t, err, test.expectedErrs[i])
}
l1t.AssertExpectations(t)
})
}
}