batch.go 3.42 KB
Newer Older
1 2 3 4 5 6 7 8 9
package derive

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"sync"

10
	"github.com/ethereum/go-ethereum/log"
11 12 13 14 15 16 17 18 19
	"github.com/ethereum/go-ethereum/rlp"
)

// Batch format
// first byte is type followed by bytestring.
//
// An empty input is not a valid batch.
//
// Note: the type system is based on L1 typed transactions.
20
//
21 22
// encodeBufferPool holds temporary encoder buffers for batch encoding
var encodeBufferPool = sync.Pool{
23
	New: func() any { return new(bytes.Buffer) },
24 25 26
}

const (
27
	// SingularBatchType is the first version of Batch format, representing a single L2 block.
28
	SingularBatchType = 0
29
	// SpanBatchType is the Batch version used after Delta hard fork, representing a span of L2 blocks.
30
	SpanBatchType = 1
31 32
)

33 34 35 36 37 38 39
// Batch contains information to build one or multiple L2 blocks.
// Batcher converts L2 blocks into Batch and writes encoded bytes to Channel.
// Derivation pipeline decodes Batch from Channel, and converts to one or multiple payload attributes.
type Batch interface {
	GetBatchType() int
	GetTimestamp() uint64
	LogContext(log.Logger) log.Logger
40 41
}

42 43 44 45
// 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.
46
type BatchData struct {
47 48 49 50 51 52 53 54 55
	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
56 57
}

58 59 60 61 62 63 64 65 66 67 68
// EncodeRLP implements rlp.Encoder
func (b *BatchData) EncodeRLP(w io.Writer) error {
	buf := encodeBufferPool.Get().(*bytes.Buffer)
	defer encodeBufferPool.Put(buf)
	buf.Reset()
	if err := b.encodeTyped(buf); err != nil {
		return err
	}
	return rlp.Encode(w, buf.Bytes())
}

69 70 71 72
func (bd *BatchData) GetBatchType() uint8 {
	return uint8(bd.inner.GetBatchType())
}

73 74 75 76 77 78 79
// MarshalBinary returns the canonical encoding of the batch.
func (b *BatchData) MarshalBinary() ([]byte, error) {
	var buf bytes.Buffer
	err := b.encodeTyped(&buf)
	return buf.Bytes(), err
}

80
// encodeTyped encodes batch type and payload for each batch type.
81
func (b *BatchData) encodeTyped(buf *bytes.Buffer) error {
82 83
	if err := buf.WriteByte(b.GetBatchType()); err != nil {
		return err
84
	}
85
	return b.inner.encode(buf)
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
}

// DecodeRLP implements rlp.Decoder
func (b *BatchData) DecodeRLP(s *rlp.Stream) error {
	if b == nil {
		return errors.New("cannot decode into nil BatchData")
	}
	v, err := s.Bytes()
	if err != nil {
		return err
	}
	return b.decodeTyped(v)
}

// UnmarshalBinary decodes the canonical encoding of batch.
func (b *BatchData) UnmarshalBinary(data []byte) error {
	if b == nil {
		return errors.New("cannot decode into nil BatchData")
	}
	return b.decodeTyped(data)
}

108
// decodeTyped decodes a typed batchData
109 110
func (b *BatchData) decodeTyped(data []byte) error {
	if len(data) == 0 {
111
		return errors.New("batch too short")
112
	}
113
	var inner InnerBatchData
114
	switch data[0] {
115
	case SingularBatchType:
116
		inner = new(SingularBatch)
117
	case SpanBatchType:
118
		inner = new(RawSpanBatch)
119 120 121
	default:
		return fmt.Errorf("unrecognized batch type: %d", data[0])
	}
122
	if err := inner.decode(bytes.NewReader(data[1:])); err != nil {
123
		return err
124
	}
125 126
	b.inner = inner
	return nil
127 128
}

129 130 131
// NewBatchData creates a new BatchData
func NewBatchData(inner InnerBatchData) *BatchData {
	return &BatchData{inner: inner}
132
}