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 ð.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 }