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
package derive
import (
"context"
"errors"
"io"
"math/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-node/testutils"
)
// TestL1TraversalNext tests that the `Next` function only returns
// a block reference once and then properly returns io.EOF afterwards
func TestL1TraversalNext(t *testing.T) {
rng := rand.New(rand.NewSource(1234))
a := testutils.RandomBlockRef(rng)
// Load up the initial state with a reset
l1Cfg := eth.SystemConfig{
BatcherAddr: testutils.RandomAddress(rng),
Overhead: [32]byte{42},
Scalar: [32]byte{69},
}
sysCfgAddr := testutils.RandomAddress(rng)
cfg := &rollup.Config{
Genesis: rollup.Genesis{SystemConfig: l1Cfg},
L1SystemConfigAddress: sysCfgAddr,
}
tr := NewL1Traversal(testlog.Logger(t, log.LvlError), cfg, nil)
_ = tr.Reset(context.Background(), a, l1Cfg)
// First call should always succeed
ref, err := tr.NextL1Block(context.Background())
require.Nil(t, err)
require.Equal(t, a, ref)
// Subsequent calls should return io.EOF
ref, err = tr.NextL1Block(context.Background())
require.Equal(t, eth.L1BlockRef{}, ref)
require.Equal(t, io.EOF, err)
ref, err = tr.NextL1Block(context.Background())
require.Equal(t, eth.L1BlockRef{}, ref)
require.Equal(t, io.EOF, err)
}
// TestL1TraversalAdvance tests that the `Advance` function properly
// handles different error cases and returns the expected block ref
// if there is no error.
func TestL1TraversalAdvance(t *testing.T) {
rng := rand.New(rand.NewSource(1234))
a := testutils.RandomBlockRef(rng)
b := testutils.NextRandomRef(rng, a)
// x is at the same height as b but does not extend `a`
x := testutils.RandomBlockRef(rng)
x.Number = b.Number
sysCfgAddr := testutils.RandomAddress(rng)
tests := []struct {
name string
startBlock eth.L1BlockRef
nextBlock eth.L1BlockRef
initialL1Cfg eth.SystemConfig
l1Receipts []*types.Receipt
fetcherErr error
expectedErr error
}{
{
name: "simple extension",
startBlock: a,
nextBlock: b,
initialL1Cfg: eth.SystemConfig{
BatcherAddr: common.Address{11},
Overhead: [32]byte{22},
Scalar: [32]byte{33},
},
l1Receipts: []*types.Receipt{},
fetcherErr: nil,
expectedErr: nil,
},
{
name: "reorg",
startBlock: a,
nextBlock: x,
fetcherErr: nil,
expectedErr: ErrReset,
},
{
name: "not found",
startBlock: a,
nextBlock: eth.L1BlockRef{},
fetcherErr: ethereum.NotFound,
expectedErr: io.EOF,
},
{
name: "temporary error",
startBlock: a,
nextBlock: eth.L1BlockRef{},
fetcherErr: errors.New("interrupted connection"),
expectedErr: ErrTemporary,
},
// TODO: add tests that cover the receipts to config data updates
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
src := &testutils.MockL1Source{}
src.ExpectL1BlockRefByNumber(test.startBlock.Number+1, test.nextBlock, test.fetcherErr)
info := &testutils.MockBlockInfo{
InfoHash: test.nextBlock.Hash,
InfoParentHash: test.nextBlock.ParentHash,
InfoNum: test.nextBlock.Number,
InfoTime: test.nextBlock.Time,
// TODO: don't need full L1 info in receipts fetching API maybe?
}
if test.l1Receipts != nil {
src.ExpectFetchReceipts(test.nextBlock.Hash, info, test.l1Receipts, nil)
}
cfg := &rollup.Config{
Genesis: rollup.Genesis{SystemConfig: test.initialL1Cfg},
L1SystemConfigAddress: sysCfgAddr,
}
tr := NewL1Traversal(testlog.Logger(t, log.LvlError), cfg, src)
// Load up the initial state with a reset
_ = tr.Reset(context.Background(), test.startBlock, test.initialL1Cfg)
// Advance it + assert output
err := tr.AdvanceL1Block(context.Background())
require.ErrorIs(t, err, test.expectedErr)
if test.expectedErr == nil {
ref, err := tr.NextL1Block(context.Background())
require.Nil(t, err)
require.Equal(t, test.nextBlock, ref)
}
src.AssertExpectations(t)
})
}
}