attributes_queue.go 3.52 KB
Newer Older
1 2 3 4
package derive

import (
	"context"
5
	"fmt"
6 7 8
	"io"
	"time"

9 10
	"github.com/ethereum/go-ethereum/log"

11
	"github.com/ethereum-optimism/optimism/op-node/rollup"
12
	"github.com/ethereum-optimism/optimism/op-service/eth"
13 14
)

15 16 17 18 19 20 21 22
// The attributes queue sits in between the batch queue and the engine queue
// It transforms batches into payload attributes. The outputted payload
// attributes cannot be buffered because each batch->attributes transformation
// pulls in data about the current L2 safe head.
//
// It also buffers batches that have been output because multiple batches can
// be created at once.
//
23
// This stage can be reset by clearing its batch buffer.
24 25
// This stage does not need to retain any references to L1 blocks.

26 27 28 29
type AttributesBuilder interface {
	PreparePayloadAttributes(ctx context.Context, l2Parent eth.L2BlockRef, epoch eth.BlockID) (attrs *eth.PayloadAttributes, err error)
}

30
type AttributesQueue struct {
31 32 33 34 35 36
	log          log.Logger
	config       *rollup.Config
	builder      AttributesBuilder
	prev         *BatchQueue
	batch        *SingularBatch
	isLastInSpan bool
37 38
}

39
func NewAttributesQueue(log log.Logger, cfg *rollup.Config, builder AttributesBuilder, prev *BatchQueue) *AttributesQueue {
40
	return &AttributesQueue{
41 42 43 44
		log:     log,
		config:  cfg,
		builder: builder,
		prev:    prev,
45 46 47
	}
}

48 49
func (aq *AttributesQueue) Origin() eth.L1BlockRef {
	return aq.prev.Origin()
50 51
}

Tei Im's avatar
Tei Im committed
52
func (aq *AttributesQueue) NextAttributes(ctx context.Context, parent eth.L2BlockRef) (*AttributesWithParent, error) {
53 54
	// Get a batch if we need it
	if aq.batch == nil {
Tei Im's avatar
Tei Im committed
55
		batch, isLastInSpan, err := aq.prev.NextBatch(ctx, parent)
56 57 58 59
		if err != nil {
			return nil, err
		}
		aq.batch = batch
60
		aq.isLastInSpan = isLastInSpan
61
	}
62

63
	// Actually generate the next attributes
Tei Im's avatar
Tei Im committed
64
	if attrs, err := aq.createNextAttributes(ctx, aq.batch, parent); err != nil {
65 66 67
		return nil, err
	} else {
		// Clear out the local state once we will succeed
Tei Im's avatar
Tei Im committed
68
		attr := AttributesWithParent{attrs, parent, aq.isLastInSpan}
69
		aq.batch = nil
70 71
		aq.isLastInSpan = false
		return &attr, nil
72 73
	}

74 75 76 77
}

// createNextAttributes transforms a batch into a payload attributes. This sets `NoTxPool` and appends the batched transactions
// to the attributes transaction list
78
func (aq *AttributesQueue) createNextAttributes(ctx context.Context, batch *SingularBatch, l2SafeHead eth.L2BlockRef) (*eth.PayloadAttributes, error) {
79
	// sanity check parent hash
80 81
	if batch.ParentHash != l2SafeHead.Hash {
		return nil, NewResetError(fmt.Errorf("valid batch has bad parent hash %s, expected %s", batch.ParentHash, l2SafeHead.Hash))
82
	}
83 84 85 86
	// sanity check timestamp
	if expected := l2SafeHead.Time + aq.config.BlockTime; expected != batch.Timestamp {
		return nil, NewResetError(fmt.Errorf("valid batch has bad timestamp %d, expected %d", batch.Timestamp, expected))
	}
87 88
	fetchCtx, cancel := context.WithTimeout(ctx, 20*time.Second)
	defer cancel()
89
	attrs, err := aq.builder.PreparePayloadAttributes(fetchCtx, l2SafeHead, batch.Epoch())
90
	if err != nil {
91
		return nil, err
92 93
	}

94 95 96 97 98 99
	// we are verifying, not sequencing, we've got all transactions and do not pull from the tx-pool
	// (that would make the block derivation non-deterministic)
	attrs.NoTxPool = true
	attrs.Transactions = append(attrs.Transactions, batch.Transactions...)

	aq.log.Info("generated attributes in payload queue", "txs", len(attrs.Transactions), "timestamp", batch.Timestamp)
100

101
	return attrs, nil
102 103
}

104
func (aq *AttributesQueue) Reset(ctx context.Context, _ eth.L1BlockRef, _ eth.SystemConfig) error {
105
	aq.batch = nil
106
	aq.isLastInSpan = false // overwritten later, but set for consistency
107 108
	return io.EOF
}