github.com/ethereum-optimism/optimism@v1.7.2/op-node/rollup/derive/channel_out_test.go (about) 1 package derive 2 3 import ( 4 "bytes" 5 "io" 6 "math/big" 7 "math/rand" 8 "testing" 9 10 "github.com/ethereum/go-ethereum/common" 11 "github.com/ethereum/go-ethereum/core/types" 12 "github.com/ethereum/go-ethereum/rlp" 13 "github.com/stretchr/testify/require" 14 15 "github.com/ethereum-optimism/optimism/op-node/rollup" 16 ) 17 18 var ( 19 rollupCfg rollup.Config 20 ) 21 22 // basic implementation of the Compressor interface that does no compression 23 type nonCompressor struct { 24 bytes.Buffer 25 } 26 27 func (s *nonCompressor) Flush() error { 28 return nil 29 } 30 31 func (s *nonCompressor) Close() error { 32 return nil 33 } 34 35 func (s *nonCompressor) FullErr() error { 36 return nil 37 } 38 39 func TestChannelOutAddBlock(t *testing.T) { 40 cout, err := NewChannelOut(SingularBatchType, &nonCompressor{}, nil) 41 require.NoError(t, err) 42 43 t.Run("returns err if first tx is not an l1info tx", func(t *testing.T) { 44 header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)} 45 block := types.NewBlockWithHeader(header).WithBody( 46 []*types.Transaction{ 47 types.NewTx(&types.DynamicFeeTx{}), 48 }, 49 nil, 50 ) 51 _, err := cout.AddBlock(&rollupCfg, block) 52 require.Error(t, err) 53 require.Equal(t, ErrNotDepositTx, err) 54 }) 55 } 56 57 // TestOutputFrameSmallMaxSize tests that calling [OutputFrame] with a small 58 // max size that is below the fixed frame size overhead of 23, will return 59 // an error. 60 func TestOutputFrameSmallMaxSize(t *testing.T) { 61 cout, err := NewChannelOut(SingularBatchType, &nonCompressor{}, nil) 62 require.NoError(t, err) 63 64 // Call OutputFrame with the range of small max size values that err 65 var w bytes.Buffer 66 for i := 0; i < 23; i++ { 67 fid, err := cout.OutputFrame(&w, uint64(i)) 68 require.ErrorIs(t, err, ErrMaxFrameSizeTooSmall) 69 require.Zero(t, fid) 70 } 71 } 72 73 func TestOutputFrameNoEmptyLastFrame(t *testing.T) { 74 cout, err := NewChannelOut(SingularBatchType, &nonCompressor{}, nil) 75 require.NoError(t, err) 76 77 rng := rand.New(rand.NewSource(0x543331)) 78 chainID := big.NewInt(rng.Int63n(1000)) 79 txCount := 1 80 singularBatch := RandomSingularBatch(rng, txCount, chainID) 81 82 written, err := cout.AddSingularBatch(singularBatch, 0) 83 require.NoError(t, err) 84 85 require.NoError(t, cout.Close()) 86 87 var buf bytes.Buffer 88 // Output a frame which needs exactly `written` bytes. This frame is expected to be the last frame. 89 _, err = cout.OutputFrame(&buf, written+FrameV0OverHeadSize) 90 require.ErrorIs(t, err, io.EOF) 91 92 } 93 94 // TestRLPByteLimit ensures that stream encoder is properly limiting the length. 95 // It will decode the input if `len(input) <= inputLimit`. 96 func TestRLPByteLimit(t *testing.T) { 97 // Should succeed if `len(input) == inputLimit` 98 enc := []byte("\x8bhello world") // RLP encoding of the string "hello world" 99 in := bytes.NewBuffer(enc) 100 var out string 101 stream := rlp.NewStream(in, 12) 102 err := stream.Decode(&out) 103 require.Nil(t, err) 104 require.Equal(t, out, "hello world") 105 106 // Should fail if the `inputLimit = len(input) - 1` 107 enc = []byte("\x8bhello world") // RLP encoding of the string "hello world" 108 in = bytes.NewBuffer(enc) 109 var out2 string 110 stream = rlp.NewStream(in, 11) 111 err = stream.Decode(&out2) 112 require.Equal(t, err, rlp.ErrValueTooLarge) 113 require.Equal(t, out2, "") 114 } 115 116 func TestForceCloseTxData(t *testing.T) { 117 id := [16]byte{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef} 118 tests := []struct { 119 frames []Frame 120 errors bool 121 output string 122 }{ 123 { 124 frames: []Frame{}, 125 errors: true, 126 output: "", 127 }, 128 { 129 frames: []Frame{{FrameNumber: 0, IsLast: false}, {ID: id, FrameNumber: 1, IsLast: true}}, 130 errors: true, 131 output: "", 132 }, 133 { 134 frames: []Frame{{ID: id, FrameNumber: 0, IsLast: false}}, 135 errors: false, 136 output: "00deadbeefdeadbeefdeadbeefdeadbeef00000000000001", 137 }, 138 { 139 frames: []Frame{{ID: id, FrameNumber: 0, IsLast: true}}, 140 errors: false, 141 output: "00", 142 }, 143 { 144 frames: []Frame{{ID: id, FrameNumber: 1, IsLast: false}}, 145 errors: false, 146 output: "00deadbeefdeadbeefdeadbeefdeadbeef00000000000001", 147 }, 148 { 149 frames: []Frame{{ID: id, FrameNumber: 1, IsLast: true}}, 150 errors: false, 151 output: "00deadbeefdeadbeefdeadbeefdeadbeef00000000000000", 152 }, 153 { 154 frames: []Frame{{ID: id, FrameNumber: 2, IsLast: true}}, 155 errors: false, 156 output: "00deadbeefdeadbeefdeadbeefdeadbeef00000000000000deadbeefdeadbeefdeadbeefdeadbeef00010000000000", 157 }, 158 { 159 frames: []Frame{{ID: id, FrameNumber: 1, IsLast: false}, {ID: id, FrameNumber: 3, IsLast: true}}, 160 errors: false, 161 output: "00deadbeefdeadbeefdeadbeefdeadbeef00000000000000deadbeefdeadbeefdeadbeefdeadbeef00020000000000", 162 }, 163 { 164 frames: []Frame{{ID: id, FrameNumber: 1, IsLast: false}, {ID: id, FrameNumber: 3, IsLast: true}, {ID: id, FrameNumber: 5, IsLast: true}}, 165 errors: false, 166 output: "00deadbeefdeadbeefdeadbeefdeadbeef00000000000000deadbeefdeadbeefdeadbeefdeadbeef00020000000000", 167 }, 168 } 169 170 for i, test := range tests { 171 out, err := ForceCloseTxData(test.frames) 172 if test.errors { 173 require.NotNil(t, err, "Should error on tc %v", i) 174 require.Nil(t, out, "Should return no value in tc %v", i) 175 } else { 176 require.NoError(t, err, "Should not error on tc %v", i) 177 require.Equal(t, common.FromHex(test.output), out, "Should match output tc %v", i) 178 } 179 } 180 } 181 182 func TestBlockToBatchValidity(t *testing.T) { 183 block := new(types.Block) 184 _, _, err := BlockToSingularBatch(&rollupCfg, block) 185 require.ErrorContains(t, err, "has no transactions") 186 }