Commit babe5e7d authored by protolambda's avatar protolambda Committed by GitHub

Merge pull request #7752 from testinprod-io/tip/span-batch-types-refactor

op-node: Span Batch Type, Encoding, and Decoding Refactoring
parents 8ac11e31 9f9cf618
......@@ -759,7 +759,7 @@ func TestFramePublished(t *testing.T) {
}
func ChannelBuilder_PendingFrames_TotalFrames(t *testing.T, batchType uint) {
const tnf = 8
const tnf = 9
rng := rand.New(rand.NewSource(94572314))
require := require.New(t)
cfg := defaultTestChannelConfig
......@@ -828,7 +828,7 @@ func ChannelBuilder_InputBytes(t *testing.T, batchType uint) {
spanBatchBuilder.AppendSingularBatch(singularBatch, l1Info.SequenceNumber)
rawSpanBatch, err := spanBatchBuilder.GetRawSpanBatch()
require.NoError(err)
batch := derive.NewSpanBatchData(*rawSpanBatch)
batch := derive.NewBatchData(rawSpanBatch)
var buf bytes.Buffer
require.NoError(batch.EncodeRLP(&buf))
l = buf.Len()
......@@ -878,7 +878,7 @@ func ChannelBuilder_OutputBytes(t *testing.T, batchType uint) {
func blockBatchRlpSize(t *testing.T, b *types.Block) int {
t.Helper()
singularBatch, _, err := derive.BlockToSingularBatch(b)
batch := derive.NewSingularBatchData(*singularBatch)
batch := derive.NewBatchData(singularBatch)
require.NoError(t, err)
var buf bytes.Buffer
require.NoError(t, batch.EncodeRLP(&buf), "RLP-encoding batch")
......
......@@ -255,13 +255,13 @@ func blockToBatch(block *types.Block) (*derive.BatchData, error) {
return nil, fmt.Errorf("could not parse the L1 Info deposit: %w", err)
}
return &derive.BatchData{
SingularBatch: derive.SingularBatch{
ParentHash: block.ParentHash(),
EpochNum: rollup.Epoch(l1Info.Number),
EpochHash: l1Info.BlockHash,
Timestamp: block.Time(),
Transactions: opaqueTxs,
},
}, nil
singularBatch := &derive.SingularBatch{
ParentHash: block.ParentHash(),
EpochNum: rollup.Epoch(l1Info.Number),
EpochHash: l1Info.BlockHash,
Timestamp: block.Time(),
Transactions: opaqueTxs,
}
return derive.NewBatchData(singularBatch), nil
}
......@@ -19,12 +19,12 @@ import (
)
type ChannelWithMetadata struct {
ID derive.ChannelID `json:"id"`
IsReady bool `json:"is_ready"`
InvalidFrames bool `json:"invalid_frames"`
InvalidBatches bool `json:"invalid_batches"`
Frames []FrameWithMetadata `json:"frames"`
Batches []derive.SingularBatch `json:"batches"`
ID derive.ChannelID `json:"id"`
IsReady bool `json:"is_ready"`
InvalidFrames bool `json:"invalid_frames"`
InvalidBatches bool `json:"invalid_batches"`
Frames []FrameWithMetadata `json:"frames"`
Batches []derive.BatchData `json:"batches"`
}
type FrameWithMetadata struct {
......@@ -104,7 +104,7 @@ func processFrames(cfg *rollup.Config, id derive.ChannelID, frames []FrameWithMe
}
}
var batches []derive.SingularBatch
var batches []derive.BatchData
invalidBatches := false
if ch.IsReady() {
br, err := derive.BatchReader(ch.Reader())
......@@ -114,11 +114,7 @@ func processFrames(cfg *rollup.Config, id derive.ChannelID, frames []FrameWithMe
fmt.Printf("Error reading batch for channel %v. Err: %v\n", id.String(), err)
invalidBatches = true
} else {
if batch.BatchType != derive.SingularBatchType {
batches = append(batches, batch.SingularBatch)
} else {
fmt.Printf("batch-type %d is not supported", batch.BatchType)
}
batches = append(batches, *batch)
}
}
} else {
......
......@@ -25,9 +25,9 @@ var encodeBufferPool = sync.Pool{
const (
// SingularBatchType is the first version of Batch format, representing a single L2 block.
SingularBatchType = iota
SingularBatchType = 0
// SpanBatchType is the Batch version used after SpanBatch hard fork, representing a span of L2 blocks.
SpanBatchType
SpanBatchType = 1
)
// Batch contains information to build one or multiple L2 blocks.
......@@ -39,12 +39,20 @@ type Batch interface {
LogContext(log.Logger) log.Logger
}
// BatchData is a composition type that contains raw data of each batch version.
// It has encoding & decoding methods to implement typed encoding.
// BatchData is used to represent the typed encoding & decoding.
// and wraps around a single interface InnerBatchData.
// Further fields such as cache can be added in the future, without embedding each type of InnerBatchData.
// Similar design with op-geth's types.Transaction struct.
type BatchData struct {
BatchType int
SingularBatch
RawSpanBatch
inner InnerBatchData
}
// InnerBatchData is the underlying data of a BatchData.
// This is implemented by SingularBatch and RawSpanBatch.
type InnerBatchData interface {
GetBatchType() int
encode(w io.Writer) error
decode(r *bytes.Reader) error
}
// EncodeRLP implements rlp.Encoder
......@@ -58,6 +66,10 @@ func (b *BatchData) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, buf.Bytes())
}
func (bd *BatchData) GetBatchType() uint8 {
return uint8(bd.inner.GetBatchType())
}
// MarshalBinary returns the canonical encoding of the batch.
func (b *BatchData) MarshalBinary() ([]byte, error) {
var buf bytes.Buffer
......@@ -67,16 +79,10 @@ func (b *BatchData) MarshalBinary() ([]byte, error) {
// encodeTyped encodes batch type and payload for each batch type.
func (b *BatchData) encodeTyped(buf *bytes.Buffer) error {
switch b.BatchType {
case SingularBatchType:
buf.WriteByte(SingularBatchType)
return rlp.Encode(buf, &b.SingularBatch)
case SpanBatchType:
buf.WriteByte(SpanBatchType)
return b.RawSpanBatch.encode(buf)
default:
return fmt.Errorf("unrecognized batch type: %d", b.BatchType)
if err := buf.WriteByte(b.GetBatchType()); err != nil {
return err
}
return b.inner.encode(buf)
}
// DecodeRLP implements rlp.Decoder
......@@ -99,35 +105,28 @@ func (b *BatchData) UnmarshalBinary(data []byte) error {
return b.decodeTyped(data)
}
// decodeTyped decodes batch type and payload for each batch type.
// decodeTyped decodes a typed batchData
func (b *BatchData) decodeTyped(data []byte) error {
if len(data) == 0 {
return fmt.Errorf("batch too short")
return errors.New("batch too short")
}
var inner InnerBatchData
switch data[0] {
case SingularBatchType:
b.BatchType = SingularBatchType
return rlp.DecodeBytes(data[1:], &b.SingularBatch)
inner = new(SingularBatch)
case SpanBatchType:
b.BatchType = int(data[0])
return b.RawSpanBatch.decodeBytes(data[1:])
inner = new(RawSpanBatch)
default:
return fmt.Errorf("unrecognized batch type: %d", data[0])
}
}
// NewSingularBatchData creates new BatchData with SingularBatch
func NewSingularBatchData(singularBatch SingularBatch) *BatchData {
return &BatchData{
BatchType: SingularBatchType,
SingularBatch: singularBatch,
if err := inner.decode(bytes.NewReader(data[1:])); err != nil {
return err
}
b.inner = inner
return nil
}
// NewSpanBatchData creates new BatchData with SpanBatch
func NewSpanBatchData(spanBatch RawSpanBatch) *BatchData {
return &BatchData{
BatchType: SpanBatchType,
RawSpanBatch: spanBatch,
}
// NewBatchData creates a new BatchData
func NewBatchData(inner InnerBatchData) *BatchData {
return &BatchData{inner: inner}
}
......@@ -6,15 +6,16 @@ import (
"math/rand"
"testing"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/testutils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/testutils"
)
func RandomRawSpanBatch(rng *rand.Rand, chainId *big.Int) *RawSpanBatch {
......@@ -52,8 +53,8 @@ func RandomRawSpanBatch(rng *rand.Rand, chainId *big.Int) *RawSpanBatch {
spanBatchPrefix: spanBatchPrefix{
relTimestamp: uint64(rng.Uint32()),
l1OriginNum: rng.Uint64(),
parentCheck: testutils.RandomData(rng, 20),
l1OriginCheck: testutils.RandomData(rng, 20),
parentCheck: [20]byte(testutils.RandomData(rng, 20)),
l1OriginCheck: [20]byte(testutils.RandomData(rng, 20)),
},
spanBatchPayload: spanBatchPayload{
blockCount: blockCount,
......@@ -141,40 +142,42 @@ func TestBatchRoundTrip(t *testing.T) {
chainID := new(big.Int).SetUint64(rng.Uint64())
batches := []*BatchData{
{
SingularBatch: SingularBatch{
NewBatchData(
&SingularBatch{
ParentHash: common.Hash{},
EpochNum: 0,
Timestamp: 0,
Transactions: []hexutil.Bytes{},
},
},
{
SingularBatch: SingularBatch{
),
NewBatchData(
&SingularBatch{
ParentHash: common.Hash{31: 0x42},
EpochNum: 1,
Timestamp: 1647026951,
Transactions: []hexutil.Bytes{[]byte{0, 0, 0}, []byte{0x76, 0xfd, 0x7c}},
},
},
NewSingularBatchData(*RandomSingularBatch(rng, 5, chainID)),
NewSingularBatchData(*RandomSingularBatch(rng, 7, chainID)),
NewSpanBatchData(*RandomRawSpanBatch(rng, chainID)),
NewSpanBatchData(*RandomRawSpanBatch(rng, chainID)),
NewSpanBatchData(*RandomRawSpanBatch(rng, chainID)),
),
NewBatchData(RandomSingularBatch(rng, 5, chainID)),
NewBatchData(RandomSingularBatch(rng, 7, chainID)),
NewBatchData(RandomRawSpanBatch(rng, chainID)),
NewBatchData(RandomRawSpanBatch(rng, chainID)),
NewBatchData(RandomRawSpanBatch(rng, chainID)),
}
for i, batch := range batches {
enc, err := batch.MarshalBinary()
assert.NoError(t, err)
require.NoError(t, err)
var dec BatchData
err = dec.UnmarshalBinary(enc)
assert.NoError(t, err)
if dec.BatchType == SpanBatchType {
_, err := dec.RawSpanBatch.derive(blockTime, genesisTimestamp, chainID)
assert.NoError(t, err)
require.NoError(t, err)
if dec.GetBatchType() == SpanBatchType {
rawSpanBatch, ok := dec.inner.(*RawSpanBatch)
require.True(t, ok)
_, err := rawSpanBatch.derive(blockTime, genesisTimestamp, chainID)
require.NoError(t, err)
}
assert.Equal(t, batch, &dec, "Batch not equal test case %v", i)
require.Equal(t, batch, &dec, "Batch not equal test case %v", i)
}
}
......@@ -185,43 +188,45 @@ func TestBatchRoundTripRLP(t *testing.T) {
chainID := new(big.Int).SetUint64(rng.Uint64())
batches := []*BatchData{
{
SingularBatch: SingularBatch{
NewBatchData(
&SingularBatch{
ParentHash: common.Hash{},
EpochNum: 0,
Timestamp: 0,
Transactions: []hexutil.Bytes{},
},
},
{
SingularBatch: SingularBatch{
),
NewBatchData(
&SingularBatch{
ParentHash: common.Hash{31: 0x42},
EpochNum: 1,
Timestamp: 1647026951,
Transactions: []hexutil.Bytes{[]byte{0, 0, 0}, []byte{0x76, 0xfd, 0x7c}},
},
},
NewSingularBatchData(*RandomSingularBatch(rng, 5, chainID)),
NewSingularBatchData(*RandomSingularBatch(rng, 7, chainID)),
NewSpanBatchData(*RandomRawSpanBatch(rng, chainID)),
NewSpanBatchData(*RandomRawSpanBatch(rng, chainID)),
NewSpanBatchData(*RandomRawSpanBatch(rng, chainID)),
),
NewBatchData(RandomSingularBatch(rng, 5, chainID)),
NewBatchData(RandomSingularBatch(rng, 7, chainID)),
NewBatchData(RandomRawSpanBatch(rng, chainID)),
NewBatchData(RandomRawSpanBatch(rng, chainID)),
NewBatchData(RandomRawSpanBatch(rng, chainID)),
}
for i, batch := range batches {
var buf bytes.Buffer
err := batch.EncodeRLP(&buf)
assert.NoError(t, err)
require.NoError(t, err)
result := buf.Bytes()
var dec BatchData
r := bytes.NewReader(result)
s := rlp.NewStream(r, 0)
err = dec.DecodeRLP(s)
assert.NoError(t, err)
if dec.BatchType == SpanBatchType {
_, err := dec.RawSpanBatch.derive(blockTime, genesisTimestamp, chainID)
assert.NoError(t, err)
require.NoError(t, err)
if dec.GetBatchType() == SpanBatchType {
rawSpanBatch, ok := dec.inner.(*RawSpanBatch)
require.True(t, ok)
_, err := rawSpanBatch.derive(blockTime, genesisTimestamp, chainID)
require.NoError(t, err)
}
assert.Equal(t, batch, &dec, "Batch not equal test case %v", i)
require.Equal(t, batch, &dec, "Batch not equal test case %v", i)
}
}
......@@ -17,13 +17,13 @@ func FuzzBatchRoundTrip(f *testing.F) {
typeProvider := fuzz.NewFromGoFuzz(fuzzedData).NilChance(0).MaxDepth(10000).NumElements(0, 0x100).AllowUnexportedFields(true)
fuzzerutils.AddFuzzerFunctions(typeProvider)
var singularBatch SingularBatch
typeProvider.Fuzz(&singularBatch)
// Create our batch data from fuzzed data
var batchData BatchData
typeProvider.Fuzz(&batchData)
// force batchdata to only contain singular batch
batchData.BatchType = SingularBatchType
batchData.RawSpanBatch = RawSpanBatch{}
batchData.inner = &singularBatch
// Encode our batch data
enc, err := batchData.MarshalBinary()
......
......@@ -3,6 +3,7 @@ package derive
import (
"bytes"
"context"
"errors"
"fmt"
"io"
......@@ -89,22 +90,30 @@ func (cr *ChannelInReader) NextBatch(ctx context.Context) (Batch, error) {
cr.NextChannel()
return nil, NotEnoughData
}
switch batchData.BatchType {
switch batchData.GetBatchType() {
case SingularBatchType:
return &batchData.SingularBatch, nil
singularBatch, ok := batchData.inner.(*SingularBatch)
if !ok {
return nil, NewCriticalError(errors.New("failed type assertion to SingularBatch"))
}
return singularBatch, nil
case SpanBatchType:
if origin := cr.Origin(); !cr.cfg.IsSpanBatch(origin.Time) {
return nil, NewTemporaryError(fmt.Errorf("cannot accept span batch in L1 block %s at time %d", origin, origin.Time))
}
rawSpanBatch, ok := batchData.inner.(*RawSpanBatch)
if !ok {
return nil, NewCriticalError(errors.New("failed type assertion to SpanBatch"))
}
// If the batch type is Span batch, derive block inputs from RawSpanBatch.
spanBatch, err := batchData.RawSpanBatch.derive(cr.cfg.BlockTime, cr.cfg.Genesis.L2Time, cr.cfg.L2ChainID)
spanBatch, err := rawSpanBatch.derive(cr.cfg.BlockTime, cr.cfg.Genesis.L2Time, cr.cfg.L2ChainID)
if err != nil {
return nil, err
}
return spanBatch, nil
default:
// error is bubbled up to user, but pipeline can skip the batch and continue after.
return nil, NewTemporaryError(fmt.Errorf("unrecognized batch type: %w", err))
return nil, NewTemporaryError(fmt.Errorf("unrecognized batch type: %d", batchData.GetBatchType()))
}
}
......
......@@ -145,7 +145,7 @@ func (co *SingularChannelOut) AddSingularBatch(batch *SingularBatch, _ uint64) (
// We encode to a temporary buffer to determine the encoded length to
// ensure that the total size of all RLP elements is less than or equal to MAX_RLP_BYTES_PER_CHANNEL
var buf bytes.Buffer
if err := rlp.Encode(&buf, NewSingularBatchData(*batch)); err != nil {
if err := rlp.Encode(&buf, NewBatchData(batch)); err != nil {
return 0, err
}
if co.rlpLength+buf.Len() > MaxRLPBytesPerChannel {
......
package derive
import (
"bytes"
"io"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
)
// Batch format
......@@ -51,3 +55,13 @@ func (b *SingularBatch) LogContext(log log.Logger) log.Logger {
func (b *SingularBatch) Epoch() eth.BlockID {
return eth.BlockID{Hash: b.EpochHash, Number: uint64(b.EpochNum)}
}
// encode writes the byte encoding of SingularBatch to Writer stream
func (b *SingularBatch) encode(w io.Writer) error {
return rlp.Encode(w, b)
}
// decode reads the byte encoding of SingularBatch from Reader stream
func (b *SingularBatch) decode(r *bytes.Reader) error {
return rlp.Decode(r, b)
}
......@@ -5,7 +5,7 @@ import (
"math/rand"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestSingularBatchForBatchInterface(t *testing.T) {
......@@ -15,7 +15,7 @@ func TestSingularBatchForBatchInterface(t *testing.T) {
singularBatch := RandomSingularBatch(rng, txCount, chainID)
assert.Equal(t, SingularBatchType, singularBatch.GetBatchType())
assert.Equal(t, singularBatch.Timestamp, singularBatch.GetTimestamp())
assert.Equal(t, singularBatch.EpochNum, singularBatch.GetEpochNum())
require.Equal(t, SingularBatchType, singularBatch.GetBatchType())
require.Equal(t, singularBatch.Timestamp, singularBatch.GetTimestamp())
require.Equal(t, singularBatch.EpochNum, singularBatch.GetEpochNum())
}
......@@ -32,10 +32,10 @@ var ErrTooBigSpanBatchSize = errors.New("span batch size limit reached")
var ErrEmptySpanBatch = errors.New("span-batch must not be empty")
type spanBatchPrefix struct {
relTimestamp uint64 // Relative timestamp of the first block
l1OriginNum uint64 // L1 origin number
parentCheck []byte // First 20 bytes of the first block's parent hash
l1OriginCheck []byte // First 20 bytes of the last block's L1 origin hash
relTimestamp uint64 // Relative timestamp of the first block
l1OriginNum uint64 // L1 origin number
parentCheck [20]byte // First 20 bytes of the first block's parent hash
l1OriginCheck [20]byte // First 20 bytes of the last block's L1 origin hash
}
type spanBatchPayload struct {
......@@ -51,6 +51,11 @@ type RawSpanBatch struct {
spanBatchPayload
}
// GetBatchType returns its batch type (batch_version)
func (b *RawSpanBatch) GetBatchType() int {
return SpanBatchType
}
// decodeOriginBits parses data into bp.originBits
// originBits is bitlist right-padded to a multiple of 8 bits
func (bp *spanBatchPayload) decodeOriginBits(r *bytes.Reader) error {
......@@ -105,8 +110,7 @@ func (bp *spanBatchPrefix) decodeL1OriginNum(r *bytes.Reader) error {
// decodeParentCheck parses data into bp.parentCheck
func (bp *spanBatchPrefix) decodeParentCheck(r *bytes.Reader) error {
bp.parentCheck = make([]byte, 20)
_, err := io.ReadFull(r, bp.parentCheck)
_, err := io.ReadFull(r, bp.parentCheck[:])
if err != nil {
return fmt.Errorf("failed to read parent check: %w", err)
}
......@@ -115,8 +119,7 @@ func (bp *spanBatchPrefix) decodeParentCheck(r *bytes.Reader) error {
// decodeL1OriginCheck parses data into bp.decodeL1OriginCheck
func (bp *spanBatchPrefix) decodeL1OriginCheck(r *bytes.Reader) error {
bp.l1OriginCheck = make([]byte, 20)
_, err := io.ReadFull(r, bp.l1OriginCheck)
_, err := io.ReadFull(r, bp.l1OriginCheck[:])
if err != nil {
return fmt.Errorf("failed to read l1 origin check: %w", err)
}
......@@ -221,12 +224,6 @@ func (bp *spanBatchPayload) decodePayload(r *bytes.Reader) error {
return nil
}
// decodeBytes parses data into b from data
func (b *RawSpanBatch) decodeBytes(data []byte) error {
r := bytes.NewReader(data)
return b.decode(r)
}
// decode reads the byte encoding of SpanBatch from Reader stream
func (b *RawSpanBatch) decode(r *bytes.Reader) error {
if r.Len() > MaxSpanBatchSize {
......@@ -263,7 +260,7 @@ func (bp *spanBatchPrefix) encodeL1OriginNum(w io.Writer) error {
// encodeParentCheck encodes bp.parentCheck
func (bp *spanBatchPrefix) encodeParentCheck(w io.Writer) error {
if _, err := w.Write(bp.parentCheck); err != nil {
if _, err := w.Write(bp.parentCheck[:]); err != nil {
return fmt.Errorf("cannot write parent check: %w", err)
}
return nil
......@@ -271,7 +268,7 @@ func (bp *spanBatchPrefix) encodeParentCheck(w io.Writer) error {
// encodeL1OriginCheck encodes bp.l1OriginCheck
func (bp *spanBatchPrefix) encodeL1OriginCheck(w io.Writer) error {
if _, err := w.Write(bp.l1OriginCheck); err != nil {
if _, err := w.Write(bp.l1OriginCheck[:]); err != nil {
return fmt.Errorf("cannot write l1 origin check: %w", err)
}
return nil
......@@ -380,17 +377,6 @@ func (b *RawSpanBatch) encode(w io.Writer) error {
return nil
}
// encodeBytes returns the byte encoding of SpanBatch
func (b *RawSpanBatch) encodeBytes() ([]byte, error) {
buf := encodeBufferPool.Get().(*bytes.Buffer)
defer encodeBufferPool.Put(buf)
buf.Reset()
if err := b.encode(buf); err != nil {
return []byte{}, err
}
return buf.Bytes(), nil
}
// derive converts RawSpanBatch into SpanBatch, which has a list of spanBatchElement.
// We need chain config constants to derive values for making payload attributes.
func (b *RawSpanBatch) derive(blockTime, genesisTimestamp uint64, chainID *big.Int) (*SpanBatch, error) {
......@@ -451,8 +437,8 @@ func singularBatchToElement(singularBatch *SingularBatch) *spanBatchElement {
// SpanBatch is an implementation of Batch interface,
// containing the input to build a span of L2 blocks in derived form (spanBatchElement)
type SpanBatch struct {
parentCheck []byte // First 20 bytes of the first block's parent hash
l1OriginCheck []byte // First 20 bytes of the last block's L1 origin hash
parentCheck [20]byte // First 20 bytes of the first block's parent hash
l1OriginCheck [20]byte // First 20 bytes of the last block's L1 origin hash
batches []*spanBatchElement // List of block input in derived form
}
......@@ -473,8 +459,8 @@ func (b *SpanBatch) LogContext(log log.Logger) log.Logger {
}
return log.New(
"batch_timestamp", b.batches[0].Timestamp,
"parent_check", hexutil.Encode(b.parentCheck),
"origin_check", hexutil.Encode(b.l1OriginCheck),
"parent_check", hexutil.Encode(b.parentCheck[:]),
"origin_check", hexutil.Encode(b.l1OriginCheck[:]),
"start_epoch_number", b.GetStartEpochNum(),
"end_epoch_number", b.GetBlockEpochNum(len(b.batches)-1),
"block_count", len(b.batches),
......@@ -488,12 +474,12 @@ func (b *SpanBatch) GetStartEpochNum() rollup.Epoch {
// CheckOriginHash checks if the l1OriginCheck matches the first 20 bytes of given hash, probably L1 block hash from the current canonical L1 chain.
func (b *SpanBatch) CheckOriginHash(hash common.Hash) bool {
return bytes.Equal(b.l1OriginCheck, hash.Bytes()[:20])
return bytes.Equal(b.l1OriginCheck[:], hash.Bytes()[:20])
}
// CheckParentHash checks if the parentCheck matches the first 20 bytes of given hash, probably the current L2 safe head.
func (b *SpanBatch) CheckParentHash(hash common.Hash) bool {
return bytes.Equal(b.parentCheck, hash.Bytes()[:20])
return bytes.Equal(b.parentCheck[:], hash.Bytes()[:20])
}
// GetBlockEpochNum returns the epoch number(L1 origin block number) of the block at the given index in the span.
......@@ -520,10 +506,10 @@ func (b *SpanBatch) GetBlockCount() int {
// updates l1OriginCheck or parentCheck if needed.
func (b *SpanBatch) AppendSingularBatch(singularBatch *SingularBatch) {
if len(b.batches) == 0 {
b.parentCheck = singularBatch.ParentHash.Bytes()[:20]
copy(b.parentCheck[:], singularBatch.ParentHash.Bytes()[:20])
}
b.batches = append(b.batches, singularBatchToElement(singularBatch))
b.l1OriginCheck = singularBatch.EpochHash.Bytes()[:20]
copy(b.l1OriginCheck[:], singularBatch.EpochHash.Bytes()[:20])
}
// ToRawSpanBatch merges SingularBatch List and initialize single RawSpanBatch
......@@ -541,10 +527,8 @@ func (b *SpanBatch) ToRawSpanBatch(originChangedBit uint, genesisTimestamp uint6
span_end := b.batches[len(b.batches)-1]
raw.relTimestamp = span_start.Timestamp - genesisTimestamp
raw.l1OriginNum = uint64(span_end.EpochNum)
raw.parentCheck = make([]byte, 20)
copy(raw.parentCheck, b.parentCheck)
raw.l1OriginCheck = make([]byte, 20)
copy(raw.l1OriginCheck, b.l1OriginCheck)
raw.parentCheck = b.parentCheck
raw.l1OriginCheck = b.l1OriginCheck
// spanBatchPayload
raw.blockCount = uint64(len(b.batches))
raw.originBits = new(big.Int)
......@@ -608,17 +592,16 @@ func (b *SpanBatch) GetSingularBatches(l1Origins []eth.L1BlockRef, l2SafeHead et
// NewSpanBatch converts given singularBatches into spanBatchElements, and creates a new SpanBatch.
func NewSpanBatch(singularBatches []*SingularBatch) *SpanBatch {
spanBatch := &SpanBatch{}
if len(singularBatches) == 0 {
return &SpanBatch{}
}
spanBatch := SpanBatch{
parentCheck: singularBatches[0].ParentHash.Bytes()[:20],
l1OriginCheck: singularBatches[len(singularBatches)-1].EpochHash.Bytes()[:20],
return spanBatch
}
copy(spanBatch.parentCheck[:], singularBatches[0].ParentHash.Bytes()[:20])
copy(spanBatch.l1OriginCheck[:], singularBatches[len(singularBatches)-1].EpochHash.Bytes()[:20])
for _, singularBatch := range singularBatches {
spanBatch.batches = append(spanBatch.batches, singularBatchToElement(singularBatch))
}
return &spanBatch
return spanBatch
}
// SpanBatchBuilder is a utility type to build a SpanBatch by adding a SingularBatch one by one.
......
......@@ -30,11 +30,11 @@ func TestSpanBatchForBatchInterface(t *testing.T) {
spanBatch := NewSpanBatch(singularBatches)
// check interface method implementations except logging
assert.Equal(t, SpanBatchType, spanBatch.GetBatchType())
assert.Equal(t, singularBatches[0].Timestamp, spanBatch.GetTimestamp())
assert.Equal(t, singularBatches[0].EpochNum, spanBatch.GetStartEpochNum())
assert.True(t, spanBatch.CheckOriginHash(singularBatches[blockCount-1].EpochHash))
assert.True(t, spanBatch.CheckParentHash(singularBatches[0].ParentHash))
require.Equal(t, SpanBatchType, spanBatch.GetBatchType())
require.Equal(t, singularBatches[0].Timestamp, spanBatch.GetTimestamp())
require.Equal(t, singularBatches[0].EpochNum, spanBatch.GetStartEpochNum())
require.True(t, spanBatch.CheckOriginHash(singularBatches[blockCount-1].EpochHash))
require.True(t, spanBatch.CheckParentHash(singularBatches[0].ParentHash))
}
func TestEmptySpanBatch(t *testing.T) {
......@@ -47,8 +47,8 @@ func TestEmptySpanBatch(t *testing.T) {
spanBatchPrefix: spanBatchPrefix{
relTimestamp: uint64(rng.Uint32()),
l1OriginNum: rng.Uint64(),
parentCheck: testutils.RandomData(rng, 20),
l1OriginCheck: testutils.RandomData(rng, 20),
parentCheck: *(*[20]byte)(testutils.RandomData(rng, 20)),
l1OriginCheck: *(*[20]byte)(testutils.RandomData(rng, 20)),
},
spanBatchPayload: spanBatchPayload{
blockCount: 0,
......@@ -80,23 +80,23 @@ func TestSpanBatchOriginBits(t *testing.T) {
var buf bytes.Buffer
err := rawSpanBatch.encodeOriginBits(&buf)
assert.NoError(t, err)
require.NoError(t, err)
// originBit field is fixed length: single bit
originBitBufferLen := blockCount / 8
if blockCount%8 != 0 {
originBitBufferLen++
}
assert.Equal(t, buf.Len(), int(originBitBufferLen))
require.Equal(t, buf.Len(), int(originBitBufferLen))
result := buf.Bytes()
var sb RawSpanBatch
sb.blockCount = blockCount
r := bytes.NewReader(result)
err = sb.decodeOriginBits(r)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, rawSpanBatch.originBits, sb.originBits)
require.Equal(t, rawSpanBatch.originBits, sb.originBits)
}
func TestSpanBatchPrefix(t *testing.T) {
......@@ -109,15 +109,15 @@ func TestSpanBatchPrefix(t *testing.T) {
var buf bytes.Buffer
err := rawSpanBatch.encodePrefix(&buf)
assert.NoError(t, err)
require.NoError(t, err)
result := buf.Bytes()
r := bytes.NewReader(result)
var sb RawSpanBatch
err = sb.decodePrefix(r)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, rawSpanBatch, &sb)
require.Equal(t, rawSpanBatch, &sb)
}
func TestSpanBatchRelTimestamp(t *testing.T) {
......@@ -128,15 +128,15 @@ func TestSpanBatchRelTimestamp(t *testing.T) {
var buf bytes.Buffer
err := rawSpanBatch.encodeRelTimestamp(&buf)
assert.NoError(t, err)
require.NoError(t, err)
result := buf.Bytes()
r := bytes.NewReader(result)
var sb RawSpanBatch
err = sb.decodeRelTimestamp(r)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, rawSpanBatch.relTimestamp, sb.relTimestamp)
require.Equal(t, rawSpanBatch.relTimestamp, sb.relTimestamp)
}
func TestSpanBatchL1OriginNum(t *testing.T) {
......@@ -147,15 +147,15 @@ func TestSpanBatchL1OriginNum(t *testing.T) {
var buf bytes.Buffer
err := rawSpanBatch.encodeL1OriginNum(&buf)
assert.NoError(t, err)
require.NoError(t, err)
result := buf.Bytes()
r := bytes.NewReader(result)
var sb RawSpanBatch
err = sb.decodeL1OriginNum(r)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, rawSpanBatch.l1OriginNum, sb.l1OriginNum)
require.Equal(t, rawSpanBatch.l1OriginNum, sb.l1OriginNum)
}
func TestSpanBatchParentCheck(t *testing.T) {
......@@ -166,18 +166,18 @@ func TestSpanBatchParentCheck(t *testing.T) {
var buf bytes.Buffer
err := rawSpanBatch.encodeParentCheck(&buf)
assert.NoError(t, err)
require.NoError(t, err)
// parent check field is fixed length: 20 bytes
assert.Equal(t, buf.Len(), 20)
require.Equal(t, buf.Len(), 20)
result := buf.Bytes()
r := bytes.NewReader(result)
var sb RawSpanBatch
err = sb.decodeParentCheck(r)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, rawSpanBatch.parentCheck, sb.parentCheck)
require.Equal(t, rawSpanBatch.parentCheck, sb.parentCheck)
}
func TestSpanBatchL1OriginCheck(t *testing.T) {
......@@ -188,18 +188,18 @@ func TestSpanBatchL1OriginCheck(t *testing.T) {
var buf bytes.Buffer
err := rawSpanBatch.encodeL1OriginCheck(&buf)
assert.NoError(t, err)
require.NoError(t, err)
// l1 origin check field is fixed length: 20 bytes
assert.Equal(t, buf.Len(), 20)
require.Equal(t, buf.Len(), 20)
result := buf.Bytes()
r := bytes.NewReader(result)
var sb RawSpanBatch
err = sb.decodeL1OriginCheck(r)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, rawSpanBatch.l1OriginCheck, sb.l1OriginCheck)
require.Equal(t, rawSpanBatch.l1OriginCheck, sb.l1OriginCheck)
}
func TestSpanBatchPayload(t *testing.T) {
......@@ -210,18 +210,18 @@ func TestSpanBatchPayload(t *testing.T) {
var buf bytes.Buffer
err := rawSpanBatch.encodePayload(&buf)
assert.NoError(t, err)
require.NoError(t, err)
result := buf.Bytes()
r := bytes.NewReader(result)
var sb RawSpanBatch
err = sb.decodePayload(r)
assert.NoError(t, err)
require.NoError(t, err)
sb.txs.recoverV(chainID)
assert.Equal(t, rawSpanBatch.spanBatchPayload, sb.spanBatchPayload)
require.Equal(t, rawSpanBatch.spanBatchPayload, sb.spanBatchPayload)
}
func TestSpanBatchBlockCount(t *testing.T) {
......@@ -232,16 +232,16 @@ func TestSpanBatchBlockCount(t *testing.T) {
var buf bytes.Buffer
err := rawSpanBatch.encodeBlockCount(&buf)
assert.NoError(t, err)
require.NoError(t, err)
result := buf.Bytes()
r := bytes.NewReader(result)
var sb RawSpanBatch
err = sb.decodeBlockCount(r)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, rawSpanBatch.blockCount, sb.blockCount)
require.Equal(t, rawSpanBatch.blockCount, sb.blockCount)
}
func TestSpanBatchBlockTxCounts(t *testing.T) {
......@@ -252,7 +252,7 @@ func TestSpanBatchBlockTxCounts(t *testing.T) {
var buf bytes.Buffer
err := rawSpanBatch.encodeBlockTxCounts(&buf)
assert.NoError(t, err)
require.NoError(t, err)
result := buf.Bytes()
r := bytes.NewReader(result)
......@@ -260,9 +260,9 @@ func TestSpanBatchBlockTxCounts(t *testing.T) {
sb.blockCount = rawSpanBatch.blockCount
err = sb.decodeBlockTxCounts(r)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, rawSpanBatch.blockTxCounts, sb.blockTxCounts)
require.Equal(t, rawSpanBatch.blockTxCounts, sb.blockTxCounts)
}
func TestSpanBatchTxs(t *testing.T) {
......@@ -273,7 +273,7 @@ func TestSpanBatchTxs(t *testing.T) {
var buf bytes.Buffer
err := rawSpanBatch.encodeTxs(&buf)
assert.NoError(t, err)
require.NoError(t, err)
result := buf.Bytes()
r := bytes.NewReader(result)
......@@ -281,11 +281,11 @@ func TestSpanBatchTxs(t *testing.T) {
sb.blockTxCounts = rawSpanBatch.blockTxCounts
err = sb.decodeTxs(r)
assert.NoError(t, err)
require.NoError(t, err)
sb.txs.recoverV(chainID)
assert.Equal(t, rawSpanBatch.txs, sb.txs)
require.Equal(t, rawSpanBatch.txs, sb.txs)
}
func TestSpanBatchRoundTrip(t *testing.T) {
......@@ -294,16 +294,17 @@ func TestSpanBatchRoundTrip(t *testing.T) {
rawSpanBatch := RandomRawSpanBatch(rng, chainID)
result, err := rawSpanBatch.encodeBytes()
assert.NoError(t, err)
var result bytes.Buffer
err := rawSpanBatch.encode(&result)
require.NoError(t, err)
var sb RawSpanBatch
err = sb.decodeBytes(result)
assert.NoError(t, err)
err = sb.decode(bytes.NewReader(result.Bytes()))
require.NoError(t, err)
sb.txs.recoverV(chainID)
assert.Equal(t, rawSpanBatch, &sb)
require.Equal(t, rawSpanBatch, &sb)
}
func TestSpanBatchDerive(t *testing.T) {
......@@ -321,24 +322,24 @@ func TestSpanBatchDerive(t *testing.T) {
spanBatch := NewSpanBatch(singularBatches)
originChangedBit := uint(originChangedBit)
rawSpanBatch, err := spanBatch.ToRawSpanBatch(originChangedBit, genesisTimeStamp, chainID)
assert.NoError(t, err)
require.NoError(t, err)
spanBatchDerived, err := rawSpanBatch.derive(l2BlockTime, genesisTimeStamp, chainID)
assert.NoError(t, err)
require.NoError(t, err)
blockCount := len(singularBatches)
assert.Equal(t, safeL2Head.Hash.Bytes()[:20], spanBatchDerived.parentCheck)
assert.Equal(t, singularBatches[blockCount-1].Epoch().Hash.Bytes()[:20], spanBatchDerived.l1OriginCheck)
assert.Equal(t, len(singularBatches), int(rawSpanBatch.blockCount))
require.Equal(t, safeL2Head.Hash.Bytes()[:20], spanBatchDerived.parentCheck[:])
require.Equal(t, singularBatches[blockCount-1].Epoch().Hash.Bytes()[:20], spanBatchDerived.l1OriginCheck[:])
require.Equal(t, len(singularBatches), int(rawSpanBatch.blockCount))
for i := 1; i < len(singularBatches); i++ {
assert.Equal(t, spanBatchDerived.batches[i].Timestamp, spanBatchDerived.batches[i-1].Timestamp+l2BlockTime)
require.Equal(t, spanBatchDerived.batches[i].Timestamp, spanBatchDerived.batches[i-1].Timestamp+l2BlockTime)
}
for i := 0; i < len(singularBatches); i++ {
assert.Equal(t, singularBatches[i].EpochNum, spanBatchDerived.batches[i].EpochNum)
assert.Equal(t, singularBatches[i].Timestamp, spanBatchDerived.batches[i].Timestamp)
assert.Equal(t, singularBatches[i].Transactions, spanBatchDerived.batches[i].Transactions)
require.Equal(t, singularBatches[i].EpochNum, spanBatchDerived.batches[i].EpochNum)
require.Equal(t, singularBatches[i].Timestamp, spanBatchDerived.batches[i].Timestamp)
require.Equal(t, singularBatches[i].Transactions, spanBatchDerived.batches[i].Transactions)
}
}
}
......@@ -359,7 +360,7 @@ func TestSpanBatchAppend(t *testing.T) {
// initialize with two singular batches
spanBatch2 := NewSpanBatch(singularBatches[:L])
assert.Equal(t, spanBatch, spanBatch2)
require.Equal(t, spanBatch, spanBatch2)
}
func TestSpanBatchMerge(t *testing.T) {
......@@ -375,32 +376,32 @@ func TestSpanBatchMerge(t *testing.T) {
spanBatch := NewSpanBatch(singularBatches)
originChangedBit := uint(originChangedBit)
rawSpanBatch, err := spanBatch.ToRawSpanBatch(originChangedBit, genesisTimeStamp, chainID)
assert.NoError(t, err)
require.NoError(t, err)
// check span batch prefix
assert.Equal(t, rawSpanBatch.relTimestamp, singularBatches[0].Timestamp-genesisTimeStamp, "invalid relative timestamp")
assert.Equal(t, rollup.Epoch(rawSpanBatch.l1OriginNum), singularBatches[blockCount-1].EpochNum)
assert.Equal(t, rawSpanBatch.parentCheck, singularBatches[0].ParentHash.Bytes()[:20], "invalid parent check")
assert.Equal(t, rawSpanBatch.l1OriginCheck, singularBatches[blockCount-1].EpochHash.Bytes()[:20], "invalid l1 origin check")
require.Equal(t, rawSpanBatch.relTimestamp, singularBatches[0].Timestamp-genesisTimeStamp, "invalid relative timestamp")
require.Equal(t, rollup.Epoch(rawSpanBatch.l1OriginNum), singularBatches[blockCount-1].EpochNum)
require.Equal(t, rawSpanBatch.parentCheck[:], singularBatches[0].ParentHash.Bytes()[:20], "invalid parent check")
require.Equal(t, rawSpanBatch.l1OriginCheck[:], singularBatches[blockCount-1].EpochHash.Bytes()[:20], "invalid l1 origin check")
// check span batch payload
assert.Equal(t, int(rawSpanBatch.blockCount), len(singularBatches))
assert.Equal(t, rawSpanBatch.originBits.Bit(0), originChangedBit)
require.Equal(t, int(rawSpanBatch.blockCount), len(singularBatches))
require.Equal(t, rawSpanBatch.originBits.Bit(0), originChangedBit)
for i := 1; i < blockCount; i++ {
if rawSpanBatch.originBits.Bit(i) == 1 {
assert.Equal(t, singularBatches[i].EpochNum, singularBatches[i-1].EpochNum+1)
require.Equal(t, singularBatches[i].EpochNum, singularBatches[i-1].EpochNum+1)
} else {
assert.Equal(t, singularBatches[i].EpochNum, singularBatches[i-1].EpochNum)
require.Equal(t, singularBatches[i].EpochNum, singularBatches[i-1].EpochNum)
}
}
for i := 0; i < len(singularBatches); i++ {
txCount := len(singularBatches[i].Transactions)
assert.Equal(t, txCount, int(rawSpanBatch.blockTxCounts[i]))
require.Equal(t, txCount, int(rawSpanBatch.blockTxCounts[i]))
}
// check invariants
endEpochNum := rawSpanBatch.l1OriginNum
assert.Equal(t, endEpochNum, uint64(singularBatches[blockCount-1].EpochNum))
require.Equal(t, endEpochNum, uint64(singularBatches[blockCount-1].EpochNum))
// we do not check txs field because it has to be derived to be compared
}
......@@ -420,12 +421,12 @@ func TestSpanBatchToSingularBatch(t *testing.T) {
spanBatch := NewSpanBatch(singularBatches)
originChangedBit := uint(originChangedBit)
rawSpanBatch, err := spanBatch.ToRawSpanBatch(originChangedBit, genesisTimeStamp, chainID)
assert.NoError(t, err)
require.NoError(t, err)
l1Origins := mockL1Origin(rng, rawSpanBatch, singularBatches)
singularBatches2, err := spanBatch.GetSingularBatches(l1Origins, safeL2Head)
assert.NoError(t, err)
require.NoError(t, err)
// GetSingularBatches does not fill in parent hash of singular batches
// empty out parent hash for comparison
......@@ -434,52 +435,54 @@ func TestSpanBatchToSingularBatch(t *testing.T) {
}
// check parent hash is empty
for i := 0; i < len(singularBatches2); i++ {
assert.Equal(t, singularBatches2[i].ParentHash, common.Hash{})
require.Equal(t, singularBatches2[i].ParentHash, common.Hash{})
}
assert.Equal(t, singularBatches, singularBatches2)
require.Equal(t, singularBatches, singularBatches2)
}
}
func TestSpanBatchReadTxData(t *testing.T) {
rng := rand.New(rand.NewSource(0x109550))
chainID := new(big.Int).SetUint64(rng.Uint64())
txCount := 64
signer := types.NewLondonSigner(chainID)
var rawTxs [][]byte
var txs []*types.Transaction
m := make(map[byte]int)
for i := 0; i < txCount; i++ {
tx := testutils.RandomTx(rng, new(big.Int).SetUint64(rng.Uint64()), signer)
m[tx.Type()] += 1
rawTx, err := tx.MarshalBinary()
assert.NoError(t, err)
rawTxs = append(rawTxs, rawTx)
txs = append(txs, tx)
cases := []spanBatchTxTest{
{"legacy tx", 32, testutils.RandomLegacyTx},
{"access list tx", 32, testutils.RandomAccessListTx},
{"dynamic fee tx", 32, testutils.RandomDynamicFeeTx},
}
for i := 0; i < txCount; i++ {
r := bytes.NewReader(rawTxs[i])
_, txType, err := ReadTxData(r)
assert.NoError(t, err)
assert.Equal(t, int(txs[i].Type()), txType)
for i, testCase := range cases {
t.Run(testCase.name, func(t *testing.T) {
rng := rand.New(rand.NewSource(int64(0x109550 + i)))
chainID := new(big.Int).SetUint64(rng.Uint64())
signer := types.NewLondonSigner(chainID)
var rawTxs [][]byte
var txs []*types.Transaction
for txIdx := 0; txIdx < testCase.trials; txIdx++ {
tx := testCase.mkTx(rng, signer)
rawTx, err := tx.MarshalBinary()
require.NoError(t, err)
rawTxs = append(rawTxs, rawTx)
txs = append(txs, tx)
}
for txIdx := 0; txIdx < testCase.trials; txIdx++ {
r := bytes.NewReader(rawTxs[i])
_, txType, err := ReadTxData(r)
require.NoError(t, err)
assert.Equal(t, int(txs[i].Type()), txType)
}
})
}
// make sure every tx type is tested
assert.Positive(t, m[types.LegacyTxType])
assert.Positive(t, m[types.AccessListTxType])
assert.Positive(t, m[types.DynamicFeeTxType])
}
func TestSpanBatchReadTxDataInvalid(t *testing.T) {
dummy, err := rlp.EncodeToBytes("dummy")
assert.NoError(t, err)
require.NoError(t, err)
// test non list rlp decoding
r := bytes.NewReader(dummy)
_, _, err = ReadTxData(r)
assert.ErrorContains(t, err, "tx RLP prefix type must be list")
require.ErrorContains(t, err, "tx RLP prefix type must be list")
}
func TestSpanBatchBuilder(t *testing.T) {
......@@ -500,28 +503,28 @@ func TestSpanBatchBuilder(t *testing.T) {
}
spanBatchBuilder := NewSpanBatchBuilder(genesisTimeStamp, chainID)
assert.Equal(t, 0, spanBatchBuilder.GetBlockCount())
require.Equal(t, 0, spanBatchBuilder.GetBlockCount())
for i := 0; i < len(singularBatches); i++ {
spanBatchBuilder.AppendSingularBatch(singularBatches[i], seqNum)
assert.Equal(t, i+1, spanBatchBuilder.GetBlockCount())
assert.Equal(t, singularBatches[0].ParentHash.Bytes()[:20], spanBatchBuilder.spanBatch.parentCheck)
assert.Equal(t, singularBatches[i].EpochHash.Bytes()[:20], spanBatchBuilder.spanBatch.l1OriginCheck)
require.Equal(t, i+1, spanBatchBuilder.GetBlockCount())
require.Equal(t, singularBatches[0].ParentHash.Bytes()[:20], spanBatchBuilder.spanBatch.parentCheck[:])
require.Equal(t, singularBatches[i].EpochHash.Bytes()[:20], spanBatchBuilder.spanBatch.l1OriginCheck[:])
}
rawSpanBatch, err := spanBatchBuilder.GetRawSpanBatch()
assert.NoError(t, err)
require.NoError(t, err)
// compare with rawSpanBatch not using spanBatchBuilder
spanBatch := NewSpanBatch(singularBatches)
originChangedBit := uint(originChangedBit)
rawSpanBatch2, err := spanBatch.ToRawSpanBatch(originChangedBit, genesisTimeStamp, chainID)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, rawSpanBatch2, rawSpanBatch)
require.Equal(t, rawSpanBatch2, rawSpanBatch)
spanBatchBuilder.Reset()
assert.Equal(t, 0, spanBatchBuilder.GetBlockCount())
require.Equal(t, 0, spanBatchBuilder.GetBlockCount())
}
}
......@@ -533,7 +536,7 @@ func TestSpanBatchMaxTxData(t *testing.T) {
})
txEncoded, err := invalidTx.MarshalBinary()
assert.NoError(t, err)
require.NoError(t, err)
r := bytes.NewReader(txEncoded)
_, _, err = ReadTxData(r)
......
......@@ -4,7 +4,6 @@ import (
"bytes"
"errors"
"fmt"
"io"
"math/big"
"github.com/ethereum/go-ethereum/common"
......@@ -70,21 +69,6 @@ func (tx *spanBatchTx) MarshalBinary() ([]byte, error) {
return buf.Bytes(), err
}
// EncodeRLP implements rlp.Encoder
func (tx *spanBatchTx) EncodeRLP(w io.Writer) error {
if tx.Type() == types.LegacyTxType {
return rlp.Encode(w, tx.inner)
}
// It's an EIP-2718 typed TX envelope.
buf := encodeBufferPool.Get().(*bytes.Buffer)
defer encodeBufferPool.Put(buf)
buf.Reset()
if err := tx.encodeTyped(buf); err != nil {
return err
}
return rlp.Encode(w, buf.Bytes())
}
// setDecoded sets the inner transaction after decoding.
func (tx *spanBatchTx) setDecoded(inner spanBatchTxData, size uint64) {
tx.inner = inner
......@@ -115,36 +99,6 @@ func (tx *spanBatchTx) decodeTyped(b []byte) (spanBatchTxData, error) {
}
}
// DecodeRLP implements rlp.Decoder
func (tx *spanBatchTx) DecodeRLP(s *rlp.Stream) error {
kind, size, err := s.Kind()
switch {
case err != nil:
return err
case kind == rlp.List:
// It's a legacy transaction.
var inner spanBatchLegacyTxData
err = s.Decode(&inner)
if err != nil {
return fmt.Errorf("failed to decode spanBatchLegacyTxData: %w", err)
}
tx.setDecoded(&inner, rlp.ListSize(size))
return nil
default:
// It's an EIP-2718 typed TX envelope.
var b []byte
if b, err = s.Bytes(); err != nil {
return err
}
inner, err := tx.decodeTyped(b)
if err != nil {
return err
}
tx.setDecoded(inner, uint64(len(b)))
return nil
}
}
// UnmarshalBinary decodes the canonical encoding of transactions.
// It supports legacy RLP transactions and EIP2718 typed transactions.
func (tx *spanBatchTx) UnmarshalBinary(b []byte) error {
......
package derive
import (
"bytes"
"math/big"
"math/rand"
"testing"
"github.com/ethereum-optimism/optimism/op-service/testutils"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type spanBatchTxTest struct {
name string
trials int
mkTx func(rng *rand.Rand, signer types.Signer) *types.Transaction
}
func TestSpanBatchTxConvert(t *testing.T) {
rng := rand.New(rand.NewSource(0x1331))
chainID := big.NewInt(rng.Int63n(1000))
signer := types.NewLondonSigner(chainID)
m := make(map[byte]int)
for i := 0; i < 32; i++ {
tx := testutils.RandomTx(rng, new(big.Int).SetUint64(rng.Uint64()), signer)
m[tx.Type()] += 1
v, r, s := tx.RawSignatureValues()
sbtx, err := newSpanBatchTx(*tx)
assert.NoError(t, err)
tx2, err := sbtx.convertToFullTx(tx.Nonce(), tx.Gas(), tx.To(), chainID, v, r, s)
assert.NoError(t, err)
// compare after marshal because we only need inner field of transaction
txEncoded, err := tx.MarshalBinary()
assert.NoError(t, err)
tx2Encoded, err := tx2.MarshalBinary()
assert.NoError(t, err)
assert.Equal(t, txEncoded, tx2Encoded)
cases := []spanBatchTxTest{
{"legacy tx", 32, testutils.RandomLegacyTx},
{"access list tx", 32, testutils.RandomAccessListTx},
{"dynamic fee tx", 32, testutils.RandomDynamicFeeTx},
}
// make sure every tx type is tested
assert.Positive(t, m[types.LegacyTxType])
assert.Positive(t, m[types.AccessListTxType])
assert.Positive(t, m[types.DynamicFeeTxType])
}
func TestSpanBatchTxRoundTrip(t *testing.T) {
rng := rand.New(rand.NewSource(0x1332))
chainID := big.NewInt(rng.Int63n(1000))
signer := types.NewLondonSigner(chainID)
for i, testCase := range cases {
t.Run(testCase.name, func(t *testing.T) {
rng := rand.New(rand.NewSource(int64(0x1331 + i)))
chainID := big.NewInt(rng.Int63n(1000))
signer := types.NewLondonSigner(chainID)
m := make(map[byte]int)
for i := 0; i < 32; i++ {
tx := testutils.RandomTx(rng, new(big.Int).SetUint64(rng.Uint64()), signer)
m[tx.Type()] += 1
sbtx, err := newSpanBatchTx(*tx)
assert.NoError(t, err)
for txIdx := 0; txIdx < testCase.trials; txIdx++ {
tx := testCase.mkTx(rng, signer)
sbtxEncoded, err := sbtx.MarshalBinary()
assert.NoError(t, err)
v, r, s := tx.RawSignatureValues()
sbtx, err := newSpanBatchTx(*tx)
require.NoError(t, err)
var sbtx2 spanBatchTx
err = sbtx2.UnmarshalBinary(sbtxEncoded)
assert.NoError(t, err)
tx2, err := sbtx.convertToFullTx(tx.Nonce(), tx.Gas(), tx.To(), chainID, v, r, s)
require.NoError(t, err)
assert.Equal(t, sbtx, &sbtx2)
// compare after marshal because we only need inner field of transaction
txEncoded, err := tx.MarshalBinary()
require.NoError(t, err)
tx2Encoded, err := tx2.MarshalBinary()
require.NoError(t, err)
assert.Equal(t, txEncoded, tx2Encoded)
}
})
}
// make sure every tx type is tested
assert.Positive(t, m[types.LegacyTxType])
assert.Positive(t, m[types.AccessListTxType])
assert.Positive(t, m[types.DynamicFeeTxType])
}
func TestSpanBatchTxRoundTripRLP(t *testing.T) {
rng := rand.New(rand.NewSource(0x1333))
chainID := big.NewInt(rng.Int63n(1000))
signer := types.NewLondonSigner(chainID)
m := make(map[byte]int)
for i := 0; i < 32; i++ {
tx := testutils.RandomTx(rng, new(big.Int).SetUint64(rng.Uint64()), signer)
m[tx.Type()] += 1
sbtx, err := newSpanBatchTx(*tx)
assert.NoError(t, err)
var buf bytes.Buffer
err = sbtx.EncodeRLP(&buf)
assert.NoError(t, err)
result := buf.Bytes()
var sbtx2 spanBatchTx
r := bytes.NewReader(result)
rlpReader := rlp.NewStream(r, 0)
err = sbtx2.DecodeRLP(rlpReader)
assert.NoError(t, err)
assert.Equal(t, sbtx, &sbtx2)
func TestSpanBatchTxRoundTrip(t *testing.T) {
cases := []spanBatchTxTest{
{"legacy tx", 32, testutils.RandomLegacyTx},
{"access list tx", 32, testutils.RandomAccessListTx},
{"dynamic fee tx", 32, testutils.RandomDynamicFeeTx},
}
for i, testCase := range cases {
t.Run(testCase.name, func(t *testing.T) {
rng := rand.New(rand.NewSource(int64(0x1332 + i)))
chainID := big.NewInt(rng.Int63n(1000))
signer := types.NewLondonSigner(chainID)
for txIdx := 0; txIdx < testCase.trials; txIdx++ {
tx := testCase.mkTx(rng, signer)
sbtx, err := newSpanBatchTx(*tx)
require.NoError(t, err)
sbtxEncoded, err := sbtx.MarshalBinary()
require.NoError(t, err)
var sbtx2 spanBatchTx
err = sbtx2.UnmarshalBinary(sbtxEncoded)
require.NoError(t, err)
assert.Equal(t, sbtx, &sbtx2)
}
})
}
// make sure every tx type is tested
assert.Positive(t, m[types.LegacyTxType])
assert.Positive(t, m[types.AccessListTxType])
assert.Positive(t, m[types.DynamicFeeTxType])
}
type spanBatchDummyTxData struct{}
......@@ -107,44 +91,44 @@ func TestSpanBatchTxInvalidTxType(t *testing.T) {
// span batch never contain deposit tx
depositTx := types.NewTx(&types.DepositTx{})
_, err := newSpanBatchTx(*depositTx)
assert.ErrorContains(t, err, "invalid tx type")
require.ErrorContains(t, err, "invalid tx type")
var sbtx spanBatchTx
sbtx.inner = &spanBatchDummyTxData{}
_, err = sbtx.convertToFullTx(0, 0, nil, nil, nil, nil, nil)
assert.ErrorContains(t, err, "invalid tx type")
require.ErrorContains(t, err, "invalid tx type")
}
func TestSpanBatchTxDecodeInvalid(t *testing.T) {
var sbtx spanBatchTx
_, err := sbtx.decodeTyped([]byte{})
assert.EqualError(t, err, "typed transaction too short")
require.EqualError(t, err, "typed transaction too short")
tx := types.NewTx(&types.LegacyTx{})
txEncoded, err := tx.MarshalBinary()
assert.NoError(t, err)
require.NoError(t, err)
// legacy tx is not typed tx
_, err = sbtx.decodeTyped(txEncoded)
assert.EqualError(t, err, types.ErrTxTypeNotSupported.Error())
require.EqualError(t, err, types.ErrTxTypeNotSupported.Error())
tx2 := types.NewTx(&types.AccessListTx{})
tx2Encoded, err := tx2.MarshalBinary()
assert.NoError(t, err)
require.NoError(t, err)
tx2Encoded[0] = types.DynamicFeeTxType
_, err = sbtx.decodeTyped(tx2Encoded)
assert.ErrorContains(t, err, "failed to decode spanBatchDynamicFeeTxData")
require.ErrorContains(t, err, "failed to decode spanBatchDynamicFeeTxData")
tx3 := types.NewTx(&types.DynamicFeeTx{})
tx3Encoded, err := tx3.MarshalBinary()
assert.NoError(t, err)
require.NoError(t, err)
tx3Encoded[0] = types.AccessListTxType
_, err = sbtx.decodeTyped(tx3Encoded)
assert.ErrorContains(t, err, "failed to decode spanBatchAccessListTxData")
require.ErrorContains(t, err, "failed to decode spanBatchAccessListTxData")
invalidLegacyTxDecoded := []byte{0xFF, 0xFF}
err = sbtx.UnmarshalBinary(invalidLegacyTxDecoded)
assert.ErrorContains(t, err, "failed to decode spanBatchLegacyTxData")
require.ErrorContains(t, err, "failed to decode spanBatchLegacyTxData")
}
......@@ -6,11 +6,12 @@ import (
"math/rand"
"testing"
"github.com/ethereum-optimism/optimism/op-service/testutils"
"github.com/ethereum/go-ethereum/core/types"
"github.com/holiman/uint256"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum-optimism/optimism/op-service/testutils"
)
func TestSpanBatchTxsContractCreationBits(t *testing.T) {
......@@ -27,23 +28,23 @@ func TestSpanBatchTxsContractCreationBits(t *testing.T) {
var buf bytes.Buffer
err := sbt.encodeContractCreationBits(&buf)
assert.NoError(t, err)
require.NoError(t, err)
// contractCreationBit field is fixed length: single bit
contractCreationBitBufferLen := totalBlockTxCount / 8
if totalBlockTxCount%8 != 0 {
contractCreationBitBufferLen++
}
assert.Equal(t, buf.Len(), int(contractCreationBitBufferLen))
require.Equal(t, buf.Len(), int(contractCreationBitBufferLen))
result := buf.Bytes()
sbt.contractCreationBits = nil
r := bytes.NewReader(result)
err = sbt.decodeContractCreationBits(r)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, contractCreationBits, sbt.contractCreationBits)
require.Equal(t, contractCreationBits, sbt.contractCreationBits)
}
func TestSpanBatchTxsContractCreationCount(t *testing.T) {
......@@ -62,16 +63,16 @@ func TestSpanBatchTxsContractCreationCount(t *testing.T) {
var buf bytes.Buffer
err := sbt.encodeContractCreationBits(&buf)
assert.NoError(t, err)
require.NoError(t, err)
result := buf.Bytes()
sbt.contractCreationBits = nil
r := bytes.NewReader(result)
err = sbt.decodeContractCreationBits(r)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, contractCreationCount, sbt.contractCreationCount())
require.Equal(t, contractCreationCount, sbt.contractCreationCount())
}
func TestSpanBatchTxsYParityBits(t *testing.T) {
......@@ -88,23 +89,23 @@ func TestSpanBatchTxsYParityBits(t *testing.T) {
var buf bytes.Buffer
err := sbt.encodeYParityBits(&buf)
assert.NoError(t, err)
require.NoError(t, err)
// yParityBit field is fixed length: single bit
yParityBitBufferLen := totalBlockTxCount / 8
if totalBlockTxCount%8 != 0 {
yParityBitBufferLen++
}
assert.Equal(t, buf.Len(), int(yParityBitBufferLen))
require.Equal(t, buf.Len(), int(yParityBitBufferLen))
result := buf.Bytes()
sbt.yParityBits = nil
r := bytes.NewReader(result)
err = sbt.decodeYParityBits(r)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, yParityBits, sbt.yParityBits)
require.Equal(t, yParityBits, sbt.yParityBits)
}
func TestSpanBatchTxsTxSigs(t *testing.T) {
......@@ -121,22 +122,22 @@ func TestSpanBatchTxsTxSigs(t *testing.T) {
var buf bytes.Buffer
err := sbt.encodeTxSigsRS(&buf)
assert.NoError(t, err)
require.NoError(t, err)
// txSig field is fixed length: 32 byte + 32 byte = 64 byte
assert.Equal(t, buf.Len(), 64*int(totalBlockTxCount))
require.Equal(t, buf.Len(), 64*int(totalBlockTxCount))
result := buf.Bytes()
sbt.txSigs = nil
r := bytes.NewReader(result)
err = sbt.decodeTxSigsRS(r)
assert.NoError(t, err)
require.NoError(t, err)
// v field is not set
for i := 0; i < int(totalBlockTxCount); i++ {
assert.Equal(t, txSigs[i].r, sbt.txSigs[i].r)
assert.Equal(t, txSigs[i].s, sbt.txSigs[i].s)
require.Equal(t, txSigs[i].r, sbt.txSigs[i].r)
require.Equal(t, txSigs[i].s, sbt.txSigs[i].s)
}
}
......@@ -154,16 +155,16 @@ func TestSpanBatchTxsTxNonces(t *testing.T) {
var buf bytes.Buffer
err := sbt.encodeTxNonces(&buf)
assert.NoError(t, err)
require.NoError(t, err)
result := buf.Bytes()
sbt.txNonces = nil
r := bytes.NewReader(result)
err = sbt.decodeTxNonces(r)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, txNonces, sbt.txNonces)
require.Equal(t, txNonces, sbt.txNonces)
}
func TestSpanBatchTxsTxGases(t *testing.T) {
......@@ -180,16 +181,16 @@ func TestSpanBatchTxsTxGases(t *testing.T) {
var buf bytes.Buffer
err := sbt.encodeTxGases(&buf)
assert.NoError(t, err)
require.NoError(t, err)
result := buf.Bytes()
sbt.txGases = nil
r := bytes.NewReader(result)
err = sbt.decodeTxGases(r)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, txGases, sbt.txGases)
require.Equal(t, txGases, sbt.txGases)
}
func TestSpanBatchTxsTxTos(t *testing.T) {
......@@ -209,19 +210,19 @@ func TestSpanBatchTxsTxTos(t *testing.T) {
var buf bytes.Buffer
err := sbt.encodeTxTos(&buf)
assert.NoError(t, err)
require.NoError(t, err)
// to field is fixed length: 20 bytes
assert.Equal(t, buf.Len(), 20*len(txTos))
require.Equal(t, buf.Len(), 20*len(txTos))
result := buf.Bytes()
sbt.txTos = nil
r := bytes.NewReader(result)
err = sbt.decodeTxTos(r)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, txTos, sbt.txTos)
require.Equal(t, txTos, sbt.txTos)
}
func TestSpanBatchTxsTxDatas(t *testing.T) {
......@@ -240,7 +241,7 @@ func TestSpanBatchTxsTxDatas(t *testing.T) {
var buf bytes.Buffer
err := sbt.encodeTxDatas(&buf)
assert.NoError(t, err)
require.NoError(t, err)
result := buf.Bytes()
sbt.txDatas = nil
......@@ -248,10 +249,10 @@ func TestSpanBatchTxsTxDatas(t *testing.T) {
r := bytes.NewReader(result)
err = sbt.decodeTxDatas(r)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, txDatas, sbt.txDatas)
assert.Equal(t, txTypes, sbt.txTypes)
require.Equal(t, txDatas, sbt.txDatas)
require.Equal(t, txTypes, sbt.txTypes)
}
func TestSpanBatchTxsRecoverV(t *testing.T) {
......@@ -291,7 +292,7 @@ func TestSpanBatchTxsRecoverV(t *testing.T) {
recoveredVs = append(recoveredVs, txSig.v)
}
assert.Equal(t, originalVs, recoveredVs, "recovered v mismatch")
require.Equal(t, originalVs, recoveredVs, "recovered v mismatch")
}
func TestSpanBatchTxsRoundTrip(t *testing.T) {
......@@ -305,7 +306,7 @@ func TestSpanBatchTxsRoundTrip(t *testing.T) {
var buf bytes.Buffer
err := sbt.encode(&buf)
assert.NoError(t, err)
require.NoError(t, err)
result := buf.Bytes()
r := bytes.NewReader(result)
......@@ -313,10 +314,10 @@ func TestSpanBatchTxsRoundTrip(t *testing.T) {
var sbt2 spanBatchTxs
sbt2.totalBlockTxCount = totalBlockTxCount
err = sbt2.decode(r)
assert.NoError(t, err)
require.NoError(t, err)
sbt2.recoverV(chainID)
assert.Equal(t, sbt, &sbt2)
require.Equal(t, sbt, &sbt2)
}
}
......@@ -331,16 +332,16 @@ func TestSpanBatchTxsRoundTripFullTxs(t *testing.T) {
for i := 0; i < int(totalblockTxCounts); i++ {
tx := testutils.RandomTx(rng, new(big.Int).SetUint64(rng.Uint64()), signer)
rawTx, err := tx.MarshalBinary()
assert.NoError(t, err)
require.NoError(t, err)
txs = append(txs, rawTx)
}
sbt, err := newSpanBatchTxs(txs, chainID)
assert.NoError(t, err)
require.NoError(t, err)
txs2, err := sbt.fullTxs(chainID)
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, txs, txs2)
require.Equal(t, txs, txs2)
}
}
......@@ -373,17 +374,17 @@ func TestSpanBatchTxsFullTxNotEnoughTxTos(t *testing.T) {
for i := 0; i < int(totalblockTxCounts); i++ {
tx := testutils.RandomTx(rng, new(big.Int).SetUint64(rng.Uint64()), signer)
rawTx, err := tx.MarshalBinary()
assert.NoError(t, err)
require.NoError(t, err)
txs = append(txs, rawTx)
}
sbt, err := newSpanBatchTxs(txs, chainID)
assert.NoError(t, err)
require.NoError(t, err)
// drop single to field
sbt.txTos = sbt.txTos[:len(sbt.txTos)-2]
_, err = sbt.fullTxs(chainID)
assert.EqualError(t, err, "tx to not enough")
require.EqualError(t, err, "tx to not enough")
}
func TestSpanBatchTxsMaxContractCreationBitsLength(t *testing.T) {
......
......@@ -106,12 +106,12 @@ func (co *SpanChannelOut) AddSingularBatch(batch *SingularBatch, seqNum uint64)
return 0, fmt.Errorf("failed to convert SpanBatch into RawSpanBatch: %w", err)
}
// Encode RawSpanBatch into bytes
if err = rlp.Encode(&buf, NewSpanBatchData(*rawSpanBatch)); err != nil {
if err = rlp.Encode(&buf, NewBatchData(rawSpanBatch)); err != nil {
return 0, fmt.Errorf("failed to encode RawSpanBatch into bytes: %w", err)
}
// Ensure that the total size of all RLP elements is less than or equal to MAX_RLP_BYTES_PER_CHANNEL
if buf.Len() > MaxRLPBytesPerChannel {
return 0, fmt.Errorf("could not add %d bytes to channel of %d bytes, max is %d. err: %w",
return 0, fmt.Errorf("could not take %d bytes as replacement of channel of %d bytes, max is %d. err: %w",
buf.Len(), co.rlpLength, MaxRLPBytesPerChannel, ErrTooManyRLPBytes)
}
co.rlpLength = buf.Len()
......
......@@ -141,49 +141,72 @@ func RandomTo(rng *rand.Rand) *common.Address {
}
func RandomTx(rng *rand.Rand, baseFee *big.Int, signer types.Signer) *types.Transaction {
gas := params.TxGas + uint64(rng.Int63n(2_000_000))
key := InsecureRandomKey(rng)
tip := big.NewInt(rng.Int63n(10 * params.GWei))
txTypeList := []int{types.LegacyTxType, types.AccessListTxType, types.DynamicFeeTxType}
txType := txTypeList[rng.Intn(len(txTypeList))]
var txData types.TxData
var tx *types.Transaction
switch txType {
case types.LegacyTxType:
txData = &types.LegacyTx{
Nonce: rng.Uint64(),
GasPrice: new(big.Int).SetUint64(rng.Uint64()),
Gas: gas,
To: RandomTo(rng),
Value: RandomETH(rng, 10),
Data: RandomData(rng, rng.Intn(1000)),
}
tx = RandomLegacyTx(rng, signer)
case types.AccessListTxType:
txData = &types.AccessListTx{
ChainID: signer.ChainID(),
Nonce: rng.Uint64(),
GasPrice: new(big.Int).SetUint64(rng.Uint64()),
Gas: gas,
To: RandomTo(rng),
Value: RandomETH(rng, 10),
Data: RandomData(rng, rng.Intn(1000)),
AccessList: nil,
}
tx = RandomAccessListTx(rng, signer)
case types.DynamicFeeTxType:
txData = &types.DynamicFeeTx{
ChainID: signer.ChainID(),
Nonce: rng.Uint64(),
GasTipCap: tip,
GasFeeCap: new(big.Int).Add(baseFee, tip),
Gas: gas,
To: RandomTo(rng),
Value: RandomETH(rng, 10),
Data: RandomData(rng, rng.Intn(1000)),
AccessList: nil,
}
tx = RandomDynamicFeeTxWithBaseFee(rng, baseFee, signer)
default:
panic("invalid tx type")
}
return tx
}
func RandomLegacyTx(rng *rand.Rand, signer types.Signer) *types.Transaction {
key := InsecureRandomKey(rng)
txData := &types.LegacyTx{
Nonce: rng.Uint64(),
GasPrice: new(big.Int).SetUint64(rng.Uint64()),
Gas: params.TxGas + uint64(rng.Int63n(2_000_000)),
To: RandomTo(rng),
Value: RandomETH(rng, 10),
Data: RandomData(rng, rng.Intn(1000)),
}
tx, err := types.SignNewTx(key, signer, txData)
if err != nil {
panic(err)
}
return tx
}
func RandomAccessListTx(rng *rand.Rand, signer types.Signer) *types.Transaction {
key := InsecureRandomKey(rng)
txData := &types.AccessListTx{
ChainID: signer.ChainID(),
Nonce: rng.Uint64(),
GasPrice: new(big.Int).SetUint64(rng.Uint64()),
Gas: params.TxGas + uint64(rng.Int63n(2_000_000)),
To: RandomTo(rng),
Value: RandomETH(rng, 10),
Data: RandomData(rng, rng.Intn(1000)),
AccessList: nil,
}
tx, err := types.SignNewTx(key, signer, txData)
if err != nil {
panic(err)
}
return tx
}
func RandomDynamicFeeTxWithBaseFee(rng *rand.Rand, baseFee *big.Int, signer types.Signer) *types.Transaction {
key := InsecureRandomKey(rng)
tip := big.NewInt(rng.Int63n(10 * params.GWei))
txData := &types.DynamicFeeTx{
ChainID: signer.ChainID(),
Nonce: rng.Uint64(),
GasTipCap: tip,
GasFeeCap: new(big.Int).Add(baseFee, tip),
Gas: params.TxGas + uint64(rng.Int63n(2_000_000)),
To: RandomTo(rng),
Value: RandomETH(rng, 10),
Data: RandomData(rng, rng.Intn(1000)),
AccessList: nil,
}
tx, err := types.SignNewTx(key, signer, txData)
if err != nil {
panic(err)
......@@ -191,6 +214,11 @@ func RandomTx(rng *rand.Rand, baseFee *big.Int, signer types.Signer) *types.Tran
return tx
}
func RandomDynamicFeeTx(rng *rand.Rand, signer types.Signer) *types.Transaction {
baseFee := new(big.Int).SetUint64(rng.Uint64())
return RandomDynamicFeeTxWithBaseFee(rng, baseFee, signer)
}
func RandomReceipt(rng *rand.Rand, signer types.Signer, tx *types.Transaction, txIndex uint64, cumulativeGasUsed uint64) *types.Receipt {
gasUsed := params.TxGas + uint64(rng.Int63n(int64(tx.Gas()-params.TxGas+1)))
logs := make([]*types.Log, rng.Intn(10))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment