github.com/ethereum-optimism/optimism@v1.7.2/op-node/rollup/derive/attributes.go (about)

     1  package derive
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/ethereum/go-ethereum/common"
     8  	"github.com/ethereum/go-ethereum/common/hexutil"
     9  	"github.com/ethereum/go-ethereum/core/types"
    10  
    11  	"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
    12  	"github.com/ethereum-optimism/optimism/op-node/rollup"
    13  	"github.com/ethereum-optimism/optimism/op-service/eth"
    14  )
    15  
    16  // L1ReceiptsFetcher fetches L1 header info and receipts for the payload attributes derivation (the info tx and deposits)
    17  type L1ReceiptsFetcher interface {
    18  	InfoByHash(ctx context.Context, hash common.Hash) (eth.BlockInfo, error)
    19  	FetchReceipts(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Receipts, error)
    20  }
    21  
    22  type SystemConfigL2Fetcher interface {
    23  	SystemConfigByL2Hash(ctx context.Context, hash common.Hash) (eth.SystemConfig, error)
    24  }
    25  
    26  // FetchingAttributesBuilder fetches inputs for the building of L2 payload attributes on the fly.
    27  type FetchingAttributesBuilder struct {
    28  	rollupCfg *rollup.Config
    29  	l1        L1ReceiptsFetcher
    30  	l2        SystemConfigL2Fetcher
    31  }
    32  
    33  func NewFetchingAttributesBuilder(rollupCfg *rollup.Config, l1 L1ReceiptsFetcher, l2 SystemConfigL2Fetcher) *FetchingAttributesBuilder {
    34  	return &FetchingAttributesBuilder{
    35  		rollupCfg: rollupCfg,
    36  		l1:        l1,
    37  		l2:        l2,
    38  	}
    39  }
    40  
    41  // PreparePayloadAttributes prepares a PayloadAttributes template that is ready to build a L2 block with deposits only, on top of the given l2Parent, with the given epoch as L1 origin.
    42  // The template defaults to NoTxPool=true, and no sequencer transactions: the caller has to modify the template to add transactions,
    43  // by setting NoTxPool=false as sequencer, or by appending batch transactions as verifier.
    44  // The severity of the error is returned; a crit=false error means there was a temporary issue, like a failed RPC or time-out.
    45  // A crit=true error means the input arguments are inconsistent or invalid.
    46  func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Context, l2Parent eth.L2BlockRef, epoch eth.BlockID) (attrs *eth.PayloadAttributes, err error) {
    47  	var l1Info eth.BlockInfo
    48  	var depositTxs []hexutil.Bytes
    49  	var seqNumber uint64
    50  
    51  	sysConfig, err := ba.l2.SystemConfigByL2Hash(ctx, l2Parent.Hash)
    52  	if err != nil {
    53  		return nil, NewTemporaryError(fmt.Errorf("failed to retrieve L2 parent block: %w", err))
    54  	}
    55  
    56  	// If the L1 origin changed in this block, then we are in the first block of the epoch. In this
    57  	// case we need to fetch all transaction receipts from the L1 origin block so we can scan for
    58  	// user deposits.
    59  	if l2Parent.L1Origin.Number != epoch.Number {
    60  		info, receipts, err := ba.l1.FetchReceipts(ctx, epoch.Hash)
    61  		if err != nil {
    62  			return nil, NewTemporaryError(fmt.Errorf("failed to fetch L1 block info and receipts: %w", err))
    63  		}
    64  		if l2Parent.L1Origin.Hash != info.ParentHash() {
    65  			return nil, NewResetError(
    66  				fmt.Errorf("cannot create new block with L1 origin %s (parent %s) on top of L1 origin %s",
    67  					epoch, info.ParentHash(), l2Parent.L1Origin))
    68  		}
    69  
    70  		deposits, err := DeriveDeposits(receipts, ba.rollupCfg.DepositContractAddress)
    71  		if err != nil {
    72  			// deposits may never be ignored. Failing to process them is a critical error.
    73  			return nil, NewCriticalError(fmt.Errorf("failed to derive some deposits: %w", err))
    74  		}
    75  		// apply sysCfg changes
    76  		if err := UpdateSystemConfigWithL1Receipts(&sysConfig, receipts, ba.rollupCfg, info.Time()); err != nil {
    77  			return nil, NewCriticalError(fmt.Errorf("failed to apply derived L1 sysCfg updates: %w", err))
    78  		}
    79  
    80  		l1Info = info
    81  		depositTxs = deposits
    82  		seqNumber = 0
    83  	} else {
    84  		if l2Parent.L1Origin.Hash != epoch.Hash {
    85  			return nil, NewResetError(fmt.Errorf("cannot create new block with L1 origin %s in conflict with L1 origin %s", epoch, l2Parent.L1Origin))
    86  		}
    87  		info, err := ba.l1.InfoByHash(ctx, epoch.Hash)
    88  		if err != nil {
    89  			return nil, NewTemporaryError(fmt.Errorf("failed to fetch L1 block info: %w", err))
    90  		}
    91  		l1Info = info
    92  		depositTxs = nil
    93  		seqNumber = l2Parent.SequenceNumber + 1
    94  	}
    95  
    96  	// Sanity check the L1 origin was correctly selected to maintain the time invariant between L1 and L2
    97  	nextL2Time := l2Parent.Time + ba.rollupCfg.BlockTime
    98  	if nextL2Time < l1Info.Time() {
    99  		return nil, NewResetError(fmt.Errorf("cannot build L2 block on top %s for time %d before L1 origin %s at time %d",
   100  			l2Parent, nextL2Time, eth.ToBlockID(l1Info), l1Info.Time()))
   101  	}
   102  
   103  	var upgradeTxs []hexutil.Bytes
   104  	if ba.rollupCfg.IsEcotoneActivationBlock(nextL2Time) {
   105  		upgradeTxs, err = EcotoneNetworkUpgradeTransactions()
   106  		if err != nil {
   107  			return nil, NewCriticalError(fmt.Errorf("failed to build ecotone network upgrade txs: %w", err))
   108  		}
   109  	}
   110  
   111  	l1InfoTx, err := L1InfoDepositBytes(ba.rollupCfg, sysConfig, seqNumber, l1Info, nextL2Time)
   112  	if err != nil {
   113  		return nil, NewCriticalError(fmt.Errorf("failed to create l1InfoTx: %w", err))
   114  	}
   115  
   116  	txs := make([]hexutil.Bytes, 0, 1+len(depositTxs)+len(upgradeTxs))
   117  	txs = append(txs, l1InfoTx)
   118  	txs = append(txs, depositTxs...)
   119  	txs = append(txs, upgradeTxs...)
   120  
   121  	var withdrawals *types.Withdrawals
   122  	if ba.rollupCfg.IsCanyon(nextL2Time) {
   123  		withdrawals = &types.Withdrawals{}
   124  	}
   125  
   126  	var parentBeaconRoot *common.Hash
   127  	if ba.rollupCfg.IsEcotone(nextL2Time) {
   128  		parentBeaconRoot = l1Info.ParentBeaconRoot()
   129  		if parentBeaconRoot == nil { // default to zero hash if there is no beacon-block-root available
   130  			parentBeaconRoot = new(common.Hash)
   131  		}
   132  	}
   133  
   134  	return &eth.PayloadAttributes{
   135  		Timestamp:             hexutil.Uint64(nextL2Time),
   136  		PrevRandao:            eth.Bytes32(l1Info.MixDigest()),
   137  		SuggestedFeeRecipient: predeploys.SequencerFeeVaultAddr,
   138  		Transactions:          txs,
   139  		NoTxPool:              true,
   140  		GasLimit:              (*eth.Uint64Quantity)(&sysConfig.GasLimit),
   141  		Withdrawals:           withdrawals,
   142  		ParentBeaconBlockRoot: parentBeaconRoot,
   143  	}, nil
   144  }