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
package driver
import (
"context"
"errors"
"fmt"
"io"
"testing"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)
func TestDerivationComplete(t *testing.T) {
driver := createDriver(t, fmt.Errorf("derivation complete: %w", io.EOF))
err := driver.Step(context.Background())
require.ErrorIs(t, err, io.EOF)
}
func TestTemporaryError(t *testing.T) {
driver := createDriver(t, fmt.Errorf("whoopsie: %w", derive.ErrTemporary))
err := driver.Step(context.Background())
require.NoError(t, err, "should allow derivation to continue after temporary error")
}
func TestNotEnoughDataError(t *testing.T) {
driver := createDriver(t, fmt.Errorf("idk: %w", derive.NotEnoughData))
err := driver.Step(context.Background())
require.NoError(t, err)
}
func TestGenericError(t *testing.T) {
expected := errors.New("boom")
driver := createDriver(t, expected)
err := driver.Step(context.Background())
require.ErrorIs(t, err, expected)
}
func TestTargetBlock(t *testing.T) {
t.Run("Reached", func(t *testing.T) {
driver := createDriverWithNextBlock(t, derive.NotEnoughData, 1000)
driver.targetBlockNum = 1000
err := driver.Step(context.Background())
require.ErrorIs(t, err, io.EOF)
})
t.Run("Exceeded", func(t *testing.T) {
driver := createDriverWithNextBlock(t, derive.NotEnoughData, 1000)
driver.targetBlockNum = 500
err := driver.Step(context.Background())
require.ErrorIs(t, err, io.EOF)
})
t.Run("NotYetReached", func(t *testing.T) {
driver := createDriverWithNextBlock(t, derive.NotEnoughData, 1000)
driver.targetBlockNum = 1001
err := driver.Step(context.Background())
// No error to indicate derivation should continue
require.NoError(t, err)
})
}
func TestNoError(t *testing.T) {
driver := createDriver(t, nil)
err := driver.Step(context.Background())
require.NoError(t, err)
}
func TestValidateClaim(t *testing.T) {
t.Run("Valid", func(t *testing.T) {
driver := createDriver(t, io.EOF)
expected := eth.Bytes32{0x11}
driver.l2OutputRoot = func(_ uint64) (eth.Bytes32, error) {
return expected, nil
}
err := driver.ValidateClaim(uint64(0), expected)
require.NoError(t, err)
})
t.Run("Valid-PriorToSafeHead", func(t *testing.T) {
driver := createDriverWithNextBlock(t, io.EOF, 10)
expected := eth.Bytes32{0x11}
requestedOutputRoot := uint64(0)
driver.l2OutputRoot = func(blockNum uint64) (eth.Bytes32, error) {
requestedOutputRoot = blockNum
return expected, nil
}
err := driver.ValidateClaim(uint64(20), expected)
require.NoError(t, err)
require.Equal(t, uint64(10), requestedOutputRoot)
})
t.Run("Invalid", func(t *testing.T) {
driver := createDriver(t, io.EOF)
driver.l2OutputRoot = func(_ uint64) (eth.Bytes32, error) {
return eth.Bytes32{0x22}, nil
}
err := driver.ValidateClaim(uint64(0), eth.Bytes32{0x11})
require.ErrorIs(t, err, ErrClaimNotValid)
})
t.Run("Invalid-PriorToSafeHead", func(t *testing.T) {
driver := createDriverWithNextBlock(t, io.EOF, 10)
expected := eth.Bytes32{0x11}
requestedOutputRoot := uint64(0)
driver.l2OutputRoot = func(blockNum uint64) (eth.Bytes32, error) {
requestedOutputRoot = blockNum
return expected, nil
}
err := driver.ValidateClaim(uint64(20), eth.Bytes32{0x55})
require.ErrorIs(t, err, ErrClaimNotValid)
require.Equal(t, uint64(10), requestedOutputRoot)
})
t.Run("Error", func(t *testing.T) {
driver := createDriver(t, io.EOF)
expectedErr := errors.New("boom")
driver.l2OutputRoot = func(_ uint64) (eth.Bytes32, error) {
return eth.Bytes32{}, expectedErr
}
err := driver.ValidateClaim(uint64(0), eth.Bytes32{0x11})
require.ErrorIs(t, err, expectedErr)
})
}
func createDriver(t *testing.T, derivationResult error) *Driver {
return createDriverWithNextBlock(t, derivationResult, 0)
}
func createDriverWithNextBlock(t *testing.T, derivationResult error, nextBlockNum uint64) *Driver {
derivation := &stubDerivation{nextErr: derivationResult, nextBlockNum: nextBlockNum}
return &Driver{
logger: testlog.Logger(t, log.LevelDebug),
pipeline: derivation,
engine: derivation,
targetBlockNum: 1_000_000,
}
}
type stubDerivation struct {
nextErr error
nextBlockNum uint64
}
func (s stubDerivation) Step(ctx context.Context) error {
return s.nextErr
}
func (s stubDerivation) SafeL2Head() eth.L2BlockRef {
return eth.L2BlockRef{
Number: s.nextBlockNum,
}
}