Skip to content

Commit fc1e4ce

Browse files
committed
Tests
1 parent 1a9e3e7 commit fc1e4ce

File tree

8 files changed

+149066
-56
lines changed

8 files changed

+149066
-56
lines changed

op-batcher/batcher/channel_builder.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ type ChannelBuilder struct {
7878

7979
// newChannelBuilder creates a new channel builder or returns an error if the
8080
// channel out could not be created.
81+
// it acts as a factory for either a span or singular channel out
8182
func NewChannelBuilder(cfg ChannelConfig, rollupCfg rollup.Config, latestL1OriginBlockNum uint64) (*ChannelBuilder, error) {
8283
c, err := cfg.CompressorConfig.NewCompressor()
8384
if err != nil {
@@ -90,7 +91,7 @@ func NewChannelBuilder(cfg ChannelConfig, rollupCfg rollup.Config, latestL1Origi
9091
return nil, err
9192
}
9293
} else {
93-
co, err = derive.NewChannelOut(c)
94+
co, err = derive.NewSingularChannelOut(c)
9495
if err != nil {
9596
return nil, err
9697
}

op-batcher/batcher/channel_builder_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ func TestChannelBuilderBatchType(t *testing.T) {
297297
{"ChannelBuilder_PendingFrames_TotalFrames", ChannelBuilder_PendingFrames_TotalFrames},
298298
{"ChannelBuilder_InputBytes", ChannelBuilder_InputBytes},
299299
{"ChannelBuilder_OutputBytes", ChannelBuilder_OutputBytes},
300+
{"ChannelBuilder_OutputWrongFramePanic", ChannelBuilder_OutputWrongFramePanic},
300301
}
301302
for _, test := range tests {
302303
test := test
@@ -354,8 +355,9 @@ func TestChannelBuilder_NextFrame(t *testing.T) {
354355
}
355356

356357
// TestChannelBuilder_OutputWrongFramePanic tests that a panic is thrown when a frame is pushed with an invalid frame id
357-
func TestChannelBuilder_OutputWrongFramePanic(t *testing.T) {
358+
func ChannelBuilder_OutputWrongFramePanic(t *testing.T, batchType uint) {
358359
channelConfig := defaultTestChannelConfig()
360+
channelConfig.BatchType = batchType
359361

360362
// Construct a channel builder
361363
cb, err := NewChannelBuilder(channelConfig, defaultTestRollupConfig, latestL1BlockOrigin)
@@ -365,7 +367,9 @@ func TestChannelBuilder_OutputWrongFramePanic(t *testing.T) {
365367
// to construct a single frame
366368
c, err := channelConfig.CompressorConfig.NewCompressor()
367369
require.NoError(t, err)
368-
co, err := derive.NewChannelOut(c)
370+
// co is only created to supply a random ID
371+
co, err := derive.NewSingularChannelOut(c)
372+
369373
require.NoError(t, err)
370374
var buf bytes.Buffer
371375
fn, err := co.OutputFrame(&buf, channelConfig.MaxFrameSize)

op-e2e/actions/l2_batcher.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ func (s *L2Batcher) Buffer(t Testing) error {
199199
t.Fatalf("ForceSubmitSingularBatch and ForceSubmitSpanBatch cannot be set to true at the same time")
200200
} else if s.l2BatcherCfg.ForceSubmitSingularBatch {
201201
// use SingularBatchType
202-
ch, err = derive.NewChannelOut(c)
202+
ch, err = derive.NewSingularChannelOut(c)
203203
} else if s.l2BatcherCfg.ForceSubmitSpanBatch || s.rollupCfg.IsDelta(block.Time()) {
204204
// If both ForceSubmitSingularBatch and ForceSubmitSpanbatch are false, use SpanBatch automatically if Delta HF is activated.
205205
ch, err = derive.NewSpanChannelOut(s.rollupCfg.Genesis.L2Time, s.rollupCfg.L2ChainID, target)

op-e2e/testoutput.txt

Lines changed: 148882 additions & 0 deletions
Large diffs are not rendered by default.

op-node/benchmarks/batchbuilding_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ type compressorAndTarget struct {
5959
func channelOutByType(batchType uint, compKey string) (derive.ChannelOut, error) {
6060
chainID := big.NewInt(333)
6161
if batchType == derive.SingularBatchType {
62-
return derive.NewChannelOut(compressors[compKey].compressor)
62+
return derive.NewSingularChannelOut(compressors[compKey].compressor)
6363
}
6464
if batchType == derive.SpanBatchType {
6565
return derive.NewSpanChannelOut(0, chainID, compressors[compKey].targetOutput)

op-node/rollup/derive/channel_out.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,6 @@ type ChannelOut interface {
6363
OutputFrame(*bytes.Buffer, uint64) (uint16, error)
6464
}
6565

66-
func NewChannelOut(compress Compressor) (ChannelOut, error) {
67-
return NewSingularChannelOut(compress)
68-
}
69-
7066
type SingularChannelOut struct {
7167
id ChannelID
7268
// Frame ID of the next frame to emit. Increment after emitting

op-node/rollup/derive/channel_out_test.go

Lines changed: 162 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package derive
22

33
import (
44
"bytes"
5+
"fmt"
56
"io"
67
"math/big"
78
"math/rand"
@@ -36,63 +37,93 @@ func (s *nonCompressor) FullErr() error {
3637
return nil
3738
}
3839

39-
func (t *nonCompressor) TargetOutputSize() uint64 {
40-
return 0
40+
// channelTypes allows tests to run against different channel types
41+
var channelTypes = []struct {
42+
ChannelOut func(t *testing.T) ChannelOut
43+
Name string
44+
}{
45+
{
46+
Name: "Singular",
47+
ChannelOut: func(t *testing.T) ChannelOut {
48+
cout, err := NewSingularChannelOut(&nonCompressor{})
49+
require.NoError(t, err)
50+
return cout
51+
},
52+
},
53+
{
54+
Name: "Span",
55+
ChannelOut: func(t *testing.T) ChannelOut {
56+
cout, err := NewSpanChannelOut(0, big.NewInt(0), 128_000)
57+
require.NoError(t, err)
58+
return cout
59+
},
60+
},
4161
}
4262

4363
func TestChannelOutAddBlock(t *testing.T) {
44-
cout, err := NewChannelOut(&nonCompressor{})
45-
require.NoError(t, err)
46-
47-
t.Run("returns err if first tx is not an l1info tx", func(t *testing.T) {
48-
header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)}
49-
block := types.NewBlockWithHeader(header).WithBody(
50-
[]*types.Transaction{
51-
types.NewTx(&types.DynamicFeeTx{}),
52-
},
53-
nil,
54-
)
55-
_, err := cout.AddBlock(&rollupCfg, block)
56-
require.Error(t, err)
57-
require.Equal(t, ErrNotDepositTx, err)
58-
})
64+
for _, tcase := range channelTypes {
65+
t.Run(fmt.Sprintf("%s - returns err if first tx is not an l1info tx", tcase.Name), func(t *testing.T) {
66+
cout := tcase.ChannelOut(t)
67+
header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)}
68+
block := types.NewBlockWithHeader(header).WithBody(
69+
[]*types.Transaction{
70+
types.NewTx(&types.DynamicFeeTx{}),
71+
},
72+
nil,
73+
)
74+
_, err := cout.AddBlock(&rollupCfg, block)
75+
require.Error(t, err)
76+
require.Equal(t, ErrNotDepositTx, err)
77+
})
78+
}
5979
}
6080

6181
// TestOutputFrameSmallMaxSize tests that calling [OutputFrame] with a small
6282
// max size that is below the fixed frame size overhead of 23, will return
6383
// an error.
6484
func TestOutputFrameSmallMaxSize(t *testing.T) {
65-
cout, err := NewChannelOut(&nonCompressor{})
66-
require.NoError(t, err)
67-
68-
// Call OutputFrame with the range of small max size values that err
69-
var w bytes.Buffer
70-
for i := 0; i < 23; i++ {
71-
fid, err := cout.OutputFrame(&w, uint64(i))
72-
require.ErrorIs(t, err, ErrMaxFrameSizeTooSmall)
73-
require.Zero(t, fid)
85+
for _, tcase := range channelTypes {
86+
t.Run(fmt.Sprintf("%s - returns ErrMaxFrameSizeTooSmall", tcase.Name), func(t *testing.T) {
87+
cout := tcase.ChannelOut(t)
88+
// Call OutputFrame with the range of small max size values that err
89+
var w bytes.Buffer
90+
for i := 0; i < 23; i++ {
91+
fid, err := cout.OutputFrame(&w, uint64(i))
92+
require.ErrorIs(t, err, ErrMaxFrameSizeTooSmall)
93+
require.Zero(t, fid)
94+
}
95+
})
7496
}
7597
}
7698

7799
func TestOutputFrameNoEmptyLastFrame(t *testing.T) {
78-
cout, err := NewChannelOut(&nonCompressor{})
79-
require.NoError(t, err)
100+
for _, tcase := range channelTypes {
101+
t.Run(fmt.Sprintf("%s - no empty last frame", tcase.Name), func(t *testing.T) {
102+
cout := tcase.ChannelOut(t)
80103

81-
rng := rand.New(rand.NewSource(0x543331))
82-
chainID := big.NewInt(rng.Int63n(1000))
83-
txCount := 1
84-
singularBatch := RandomSingularBatch(rng, txCount, chainID)
104+
rng := rand.New(rand.NewSource(0x543331))
105+
chainID := big.NewInt(0)
106+
txCount := 1
107+
singularBatch := RandomSingularBatch(rng, txCount, chainID)
85108

86-
written, err := cout.AddSingularBatch(singularBatch, 0)
87-
require.NoError(t, err)
109+
written, err := cout.AddSingularBatch(singularBatch, 0)
110+
require.NoError(t, err)
88111

89-
require.NoError(t, cout.Close())
90-
91-
var buf bytes.Buffer
92-
// Output a frame which needs exactly `written` bytes. This frame is expected to be the last frame.
93-
_, err = cout.OutputFrame(&buf, written+FrameV0OverHeadSize)
94-
require.ErrorIs(t, err, io.EOF)
112+
require.NoError(t, cout.Close())
113+
// span batches return the length of the RLP structure, not the compressed length
114+
// so we need to collect the compressed length from the span batch
115+
// no production code relies on the written value from either type of batch
116+
// (span batches don't always compress their data, and can't always determine the compressed length for this reason)
117+
if span, ok := cout.(*SpanChannelOut); ok {
118+
written = uint64(span.compressed.Len())
119+
}
95120

121+
var buf bytes.Buffer
122+
// Output a frame which needs exactly `written` bytes. This frame is expected to be the last frame.
123+
_, err = cout.OutputFrame(&buf, written+FrameV0OverHeadSize)
124+
require.ErrorIs(t, err, io.EOF)
125+
})
126+
}
96127
}
97128

98129
// TestRLPByteLimit ensures that stream encoder is properly limiting the length.
@@ -188,3 +219,94 @@ func TestBlockToBatchValidity(t *testing.T) {
188219
_, _, err := BlockToSingularBatch(&rollupCfg, block)
189220
require.ErrorContains(t, err, "has no transactions")
190221
}
222+
223+
// TestSpanChannelOutCompressionOnlyOneBatch tests that the SpanChannelOut compression works as expected when there is only one batch
224+
// and it is larger than the target size. The single batch should be compressed, and the channel should now be full
225+
func TestSpanChannelOutCompressionOnlyOneBatch(t *testing.T) {
226+
// target is larger than one batch, but smaller than two batches
227+
target := uint64(300)
228+
rng := rand.New(rand.NewSource(0x543331))
229+
chainID := big.NewInt(rng.Int63n(1000))
230+
txCount := 1
231+
cout, err := NewSpanChannelOut(0, chainID, target)
232+
require.NoError(t, err)
233+
234+
compressedLens := []int{}
235+
236+
// adding the first batch should not cause an error
237+
singularBatch := RandomSingularBatch(rng, txCount, chainID)
238+
_, err = cout.AddSingularBatch(singularBatch, 0)
239+
compressedLens = append(compressedLens, cout.compressed.Len())
240+
require.NoError(t, err)
241+
242+
// confirm compression was not skipped
243+
require.Greater(t, compressedLens[0], 0)
244+
// confirm the channel is full
245+
require.ErrorIs(t, cout.FullErr(), ErrCompressorFull)
246+
}
247+
248+
// TestSpanChannelOutCompressionUndo tests that the SpanChannelOut compression works as expected to not accept a second batch
249+
// if that batch would cause the channel to exceed the target size. The first batch should be compressed only
250+
// the channel should be compressed, full, and should not have the second batch
251+
func TestSpanChannelOutCompressionUndo(t *testing.T) {
252+
// target is larger than one batch, but smaller than two batches
253+
target := uint64(750)
254+
rng := rand.New(rand.NewSource(0x543331))
255+
chainID := big.NewInt(rng.Int63n(1000))
256+
txCount := 1
257+
cout, err := NewSpanChannelOut(0, chainID, target)
258+
require.NoError(t, err)
259+
260+
compressedLens := []int{}
261+
rlpLens := []int{}
262+
263+
// adding the first batch should not cause an error
264+
singularBatch := RandomSingularBatch(rng, txCount, chainID)
265+
_, err = cout.AddSingularBatch(singularBatch, 0)
266+
compressedLens = append(compressedLens, cout.compressed.Len())
267+
rlpLens = append(rlpLens, cout.activeRLP().Len())
268+
require.NoError(t, err)
269+
270+
// adding the second batch should cause an error
271+
singularBatch = RandomSingularBatch(rng, txCount, chainID)
272+
_, err = cout.AddSingularBatch(singularBatch, 0)
273+
compressedLens = append(compressedLens, cout.compressed.Len())
274+
rlpLens = append(rlpLens, cout.activeRLP().Len())
275+
require.ErrorIs(t, err, ErrCompressorFull)
276+
277+
// confirm that the first compression was skipped
278+
require.Equal(t, 0, compressedLens[0])
279+
// confirm that the second compression was not skipped
280+
require.Greater(t, compressedLens[1], 0)
281+
// confirm that the second rlp is tht same size as the first (because the second batch was not added)
282+
require.Equal(t, rlpLens[0], rlpLens[1])
283+
}
284+
285+
// TestSpanChannelOutClose tests that the SpanChannelOut compression works as expected when the channel is closed.
286+
// it should compress the batch even if it is smaller than the target size because the channel is closing
287+
func TestSpanChannelOutClose(t *testing.T) {
288+
// target is larger than one batch
289+
target := uint64(600)
290+
rng := rand.New(rand.NewSource(0x543331))
291+
chainID := big.NewInt(rng.Int63n(1000))
292+
txCount := 1
293+
cout, err := NewSpanChannelOut(0, chainID, target)
294+
require.NoError(t, err)
295+
296+
singularBatch := RandomSingularBatch(rng, txCount, chainID)
297+
_, err = cout.AddSingularBatch(singularBatch, 0)
298+
require.NoError(t, err)
299+
300+
rlpLen := cout.activeRLP().Len()
301+
302+
// confirm no compression has happened yet
303+
require.Equal(t, 0, cout.compressed.Len())
304+
require.Less(t, uint64(rlpLen), target)
305+
306+
// close the channel
307+
require.NoError(t, cout.Close())
308+
309+
// confirm that the only batch was compressed, and that the RLP did not change
310+
require.Greater(t, cout.compressed.Len(), 0)
311+
require.Equal(t, rlpLen, cout.activeRLP().Len())
312+
}

op-node/rollup/derive/span_channel_out.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@ func (co *SpanChannelOut) AddBlock(rollupCfg *rollup.Config, block *types.Block)
115115
// and the compression happens on the previous RLP buffer instead
116116
// if the input is too small to need compression, data is accumulated but not compressed
117117
func (co *SpanChannelOut) AddSingularBatch(batch *SingularBatch, seqNum uint64) (uint64, error) {
118-
// sentinel error for closed channel
119-
if co.closed {
118+
// sentinel error for closed or full channel
119+
if co.closed || co.FullErr() != nil {
120120
return 0, ErrChannelOutAlreadyClosed
121121
}
122122

@@ -204,9 +204,10 @@ func (co *SpanChannelOut) ReadyBytes() int {
204204
return 0
205205
}
206206

207-
// Flush implements the Channel Out interface by passing the flush call to the compressor
207+
// Flush implements the Channel Out
208+
// Span Channel Out manages the flushing of the compressor internally, so this is a no-op
208209
func (co *SpanChannelOut) Flush() error {
209-
return co.compressor.Flush()
210+
return nil
210211
}
211212

212213
// checkFull sets the full error if the compressed data is over the target size.
@@ -230,10 +231,14 @@ func (co *SpanChannelOut) Close() error {
230231
return ErrChannelOutAlreadyClosed
231232
}
232233
co.closed = true
233-
if err := co.Flush(); err != nil {
234-
return err
234+
// if the channel was already full,
235+
// the compressor is already flushed and closed
236+
if co.FullErr() != nil {
237+
return nil
235238
}
236-
return co.compressor.Close()
239+
// if this channel is not full, we need to compress the last batch
240+
// this also flushes/closes the compressor
241+
return co.compress()
237242
}
238243

239244
// OutputFrame writes a frame to w with a given max size and returns the frame

0 commit comments

Comments
 (0)