batch.go 3.78 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 46 47 48 49 50 51 52 53 54
	AsSingularBatch() (*SingularBatch, bool)
	AsSpanBatch() (*SpanBatch, bool)
}

type batchWithMetadata struct {
	Batch
	comprAlgo CompressionAlgo
}

func (b batchWithMetadata) LogContext(l log.Logger) log.Logger {
	lgr := b.Batch.LogContext(l)
	if b.comprAlgo == "" {
		return lgr
	}
	return lgr.With("compression_algo", b.comprAlgo)
55 56
}

57 58 59 60
// 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.
61
type BatchData struct {
62 63
	inner     InnerBatchData
	ComprAlgo CompressionAlgo
64 65 66 67 68 69 70 71
}

// 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
72 73
}

74 75 76 77 78 79 80 81 82 83 84
// 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())
}

85 86 87 88
func (bd *BatchData) GetBatchType() uint8 {
	return uint8(bd.inner.GetBatchType())
}

89 90 91 92 93 94 95
// 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
}

96
// encodeTyped encodes batch type and payload for each batch type.
97
func (b *BatchData) encodeTyped(buf *bytes.Buffer) error {
98 99
	if err := buf.WriteByte(b.GetBatchType()); err != nil {
		return err
100
	}
101
	return b.inner.encode(buf)
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
}

// 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)
}

124
// decodeTyped decodes a typed batchData
125 126
func (b *BatchData) decodeTyped(data []byte) error {
	if len(data) == 0 {
127
		return errors.New("batch too short")
128
	}
129
	var inner InnerBatchData
130
	switch data[0] {
131
	case SingularBatchType:
132
		inner = new(SingularBatch)
133
	case SpanBatchType:
134
		inner = new(RawSpanBatch)
135 136 137
	default:
		return fmt.Errorf("unrecognized batch type: %d", data[0])
	}
138
	if err := inner.decode(bytes.NewReader(data[1:])); err != nil {
139
		return err
140
	}
141 142
	b.inner = inner
	return nil
143 144
}

145 146 147
// NewBatchData creates a new BatchData
func NewBatchData(inner InnerBatchData) *BatchData {
	return &BatchData{inner: inner}
148
}