github.com/ethereum-optimism/optimism@v1.7.2/op-node/rollup/derive/l1_block_info.go (about) 1 package derive 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "math/big" 9 10 "github.com/ethereum/go-ethereum/common" 11 "github.com/ethereum/go-ethereum/core/types" 12 "github.com/ethereum/go-ethereum/crypto" 13 14 "github.com/ethereum-optimism/optimism/op-bindings/predeploys" 15 "github.com/ethereum-optimism/optimism/op-node/rollup" 16 "github.com/ethereum-optimism/optimism/op-service/eth" 17 "github.com/ethereum-optimism/optimism/op-service/solabi" 18 ) 19 20 const ( 21 L1InfoFuncBedrockSignature = "setL1BlockValues(uint64,uint64,uint256,bytes32,uint64,bytes32,uint256,uint256)" 22 L1InfoFuncEcotoneSignature = "setL1BlockValuesEcotone()" 23 L1InfoArguments = 8 24 L1InfoBedrockLen = 4 + 32*L1InfoArguments 25 L1InfoEcotoneLen = 4 + 32*5 // after Ecotone upgrade, args are packed into 5 32-byte slots 26 ) 27 28 var ( 29 L1InfoFuncBedrockBytes4 = crypto.Keccak256([]byte(L1InfoFuncBedrockSignature))[:4] 30 L1InfoFuncEcotoneBytes4 = crypto.Keccak256([]byte(L1InfoFuncEcotoneSignature))[:4] 31 L1InfoDepositerAddress = common.HexToAddress("0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001") 32 L1BlockAddress = predeploys.L1BlockAddr 33 ) 34 35 const ( 36 RegolithSystemTxGas = 1_000_000 37 ) 38 39 // L1BlockInfo presents the information stored in a L1Block.setL1BlockValues call 40 type L1BlockInfo struct { 41 Number uint64 42 Time uint64 43 BaseFee *big.Int 44 BlockHash common.Hash 45 // Not strictly a piece of L1 information. Represents the number of L2 blocks since the start of the epoch, 46 // i.e. when the actual L1 info was first introduced. 47 SequenceNumber uint64 48 // BatcherHash version 0 is just the address with 0 padding to the left. 49 BatcherAddr common.Address 50 51 L1FeeOverhead eth.Bytes32 // ignored after Ecotone upgrade 52 L1FeeScalar eth.Bytes32 // ignored after Ecotone upgrade 53 54 BlobBaseFee *big.Int // added by Ecotone upgrade 55 BaseFeeScalar uint32 // added by Ecotone upgrade 56 BlobBaseFeeScalar uint32 // added by Ecotone upgrade 57 } 58 59 // Bedrock Binary Format 60 // +---------+--------------------------+ 61 // | Bytes | Field | 62 // +---------+--------------------------+ 63 // | 4 | Function signature | 64 // | 32 | Number | 65 // | 32 | Time | 66 // | 32 | BaseFee | 67 // | 32 | BlockHash | 68 // | 32 | SequenceNumber | 69 // | 32 | BatcherHash | 70 // | 32 | L1FeeOverhead | 71 // | 32 | L1FeeScalar | 72 // +---------+--------------------------+ 73 74 func (info *L1BlockInfo) marshalBinaryBedrock() ([]byte, error) { 75 w := bytes.NewBuffer(make([]byte, 0, L1InfoBedrockLen)) 76 if err := solabi.WriteSignature(w, L1InfoFuncBedrockBytes4); err != nil { 77 return nil, err 78 } 79 if err := solabi.WriteUint64(w, info.Number); err != nil { 80 return nil, err 81 } 82 if err := solabi.WriteUint64(w, info.Time); err != nil { 83 return nil, err 84 } 85 if err := solabi.WriteUint256(w, info.BaseFee); err != nil { 86 return nil, err 87 } 88 if err := solabi.WriteHash(w, info.BlockHash); err != nil { 89 return nil, err 90 } 91 if err := solabi.WriteUint64(w, info.SequenceNumber); err != nil { 92 return nil, err 93 } 94 if err := solabi.WriteAddress(w, info.BatcherAddr); err != nil { 95 return nil, err 96 } 97 if err := solabi.WriteEthBytes32(w, info.L1FeeOverhead); err != nil { 98 return nil, err 99 } 100 if err := solabi.WriteEthBytes32(w, info.L1FeeScalar); err != nil { 101 return nil, err 102 } 103 return w.Bytes(), nil 104 } 105 106 func (info *L1BlockInfo) unmarshalBinaryBedrock(data []byte) error { 107 if len(data) != L1InfoBedrockLen { 108 return fmt.Errorf("data is unexpected length: %d", len(data)) 109 } 110 reader := bytes.NewReader(data) 111 112 var err error 113 if _, err := solabi.ReadAndValidateSignature(reader, L1InfoFuncBedrockBytes4); err != nil { 114 return err 115 } 116 if info.Number, err = solabi.ReadUint64(reader); err != nil { 117 return err 118 } 119 if info.Time, err = solabi.ReadUint64(reader); err != nil { 120 return err 121 } 122 if info.BaseFee, err = solabi.ReadUint256(reader); err != nil { 123 return err 124 } 125 if info.BlockHash, err = solabi.ReadHash(reader); err != nil { 126 return err 127 } 128 if info.SequenceNumber, err = solabi.ReadUint64(reader); err != nil { 129 return err 130 } 131 if info.BatcherAddr, err = solabi.ReadAddress(reader); err != nil { 132 return err 133 } 134 if info.L1FeeOverhead, err = solabi.ReadEthBytes32(reader); err != nil { 135 return err 136 } 137 if info.L1FeeScalar, err = solabi.ReadEthBytes32(reader); err != nil { 138 return err 139 } 140 if !solabi.EmptyReader(reader) { 141 return errors.New("too many bytes") 142 } 143 return nil 144 } 145 146 // Ecotone Binary Format 147 // +---------+--------------------------+ 148 // | Bytes | Field | 149 // +---------+--------------------------+ 150 // | 4 | Function signature | 151 // | 4 | BaseFeeScalar | 152 // | 4 | BlobBaseFeeScalar | 153 // | 8 | SequenceNumber | 154 // | 8 | Timestamp | 155 // | 8 | L1BlockNumber | 156 // | 32 | BaseFee | 157 // | 32 | BlobBaseFee | 158 // | 32 | BlockHash | 159 // | 32 | BatcherHash | 160 // +---------+--------------------------+ 161 162 func (info *L1BlockInfo) marshalBinaryEcotone() ([]byte, error) { 163 w := bytes.NewBuffer(make([]byte, 0, L1InfoEcotoneLen)) 164 if err := solabi.WriteSignature(w, L1InfoFuncEcotoneBytes4); err != nil { 165 return nil, err 166 } 167 if err := binary.Write(w, binary.BigEndian, info.BaseFeeScalar); err != nil { 168 return nil, err 169 } 170 if err := binary.Write(w, binary.BigEndian, info.BlobBaseFeeScalar); err != nil { 171 return nil, err 172 } 173 if err := binary.Write(w, binary.BigEndian, info.SequenceNumber); err != nil { 174 return nil, err 175 } 176 if err := binary.Write(w, binary.BigEndian, info.Time); err != nil { 177 return nil, err 178 } 179 if err := binary.Write(w, binary.BigEndian, info.Number); err != nil { 180 return nil, err 181 } 182 if err := solabi.WriteUint256(w, info.BaseFee); err != nil { 183 return nil, err 184 } 185 blobBasefee := info.BlobBaseFee 186 if blobBasefee == nil { 187 blobBasefee = big.NewInt(1) // set to 1, to match the min blob basefee as defined in EIP-4844 188 } 189 if err := solabi.WriteUint256(w, blobBasefee); err != nil { 190 return nil, err 191 } 192 if err := solabi.WriteHash(w, info.BlockHash); err != nil { 193 return nil, err 194 } 195 // ABI encoding will perform the left-padding with zeroes to 32 bytes, matching the "batcherHash" SystemConfig format and version 0 byte. 196 if err := solabi.WriteAddress(w, info.BatcherAddr); err != nil { 197 return nil, err 198 } 199 return w.Bytes(), nil 200 } 201 202 func (info *L1BlockInfo) unmarshalBinaryEcotone(data []byte) error { 203 if len(data) != L1InfoEcotoneLen { 204 return fmt.Errorf("data is unexpected length: %d", len(data)) 205 } 206 r := bytes.NewReader(data) 207 208 var err error 209 if _, err := solabi.ReadAndValidateSignature(r, L1InfoFuncEcotoneBytes4); err != nil { 210 return err 211 } 212 if err := binary.Read(r, binary.BigEndian, &info.BaseFeeScalar); err != nil { 213 return fmt.Errorf("invalid ecotone l1 block info format") 214 } 215 if err := binary.Read(r, binary.BigEndian, &info.BlobBaseFeeScalar); err != nil { 216 return fmt.Errorf("invalid ecotone l1 block info format") 217 } 218 if err := binary.Read(r, binary.BigEndian, &info.SequenceNumber); err != nil { 219 return fmt.Errorf("invalid ecotone l1 block info format") 220 } 221 if err := binary.Read(r, binary.BigEndian, &info.Time); err != nil { 222 return fmt.Errorf("invalid ecotone l1 block info format") 223 } 224 if err := binary.Read(r, binary.BigEndian, &info.Number); err != nil { 225 return fmt.Errorf("invalid ecotone l1 block info format") 226 } 227 if info.BaseFee, err = solabi.ReadUint256(r); err != nil { 228 return err 229 } 230 if info.BlobBaseFee, err = solabi.ReadUint256(r); err != nil { 231 return err 232 } 233 if info.BlockHash, err = solabi.ReadHash(r); err != nil { 234 return err 235 } 236 // The "batcherHash" will be correctly parsed as address, since the version 0 and left-padding matches the ABI encoding format. 237 if info.BatcherAddr, err = solabi.ReadAddress(r); err != nil { 238 return err 239 } 240 if !solabi.EmptyReader(r) { 241 return errors.New("too many bytes") 242 } 243 return nil 244 } 245 246 // isEcotoneButNotFirstBlock returns whether the specified block is subject to the Ecotone upgrade, 247 // but is not the actiation block itself. 248 func isEcotoneButNotFirstBlock(rollupCfg *rollup.Config, l2BlockTime uint64) bool { 249 return rollupCfg.IsEcotone(l2BlockTime) && !rollupCfg.IsEcotoneActivationBlock(l2BlockTime) 250 } 251 252 // L1BlockInfoFromBytes is the inverse of L1InfoDeposit, to see where the L2 chain is derived from 253 func L1BlockInfoFromBytes(rollupCfg *rollup.Config, l2BlockTime uint64, data []byte) (*L1BlockInfo, error) { 254 var info L1BlockInfo 255 if isEcotoneButNotFirstBlock(rollupCfg, l2BlockTime) { 256 return &info, info.unmarshalBinaryEcotone(data) 257 } 258 return &info, info.unmarshalBinaryBedrock(data) 259 } 260 261 // L1InfoDeposit creates a L1 Info deposit transaction based on the L1 block, 262 // and the L2 block-height difference with the start of the epoch. 263 func L1InfoDeposit(rollupCfg *rollup.Config, sysCfg eth.SystemConfig, seqNumber uint64, block eth.BlockInfo, l2BlockTime uint64) (*types.DepositTx, error) { 264 l1BlockInfo := L1BlockInfo{ 265 Number: block.NumberU64(), 266 Time: block.Time(), 267 BaseFee: block.BaseFee(), 268 BlockHash: block.Hash(), 269 SequenceNumber: seqNumber, 270 BatcherAddr: sysCfg.BatcherAddr, 271 } 272 var data []byte 273 if isEcotoneButNotFirstBlock(rollupCfg, l2BlockTime) { 274 l1BlockInfo.BlobBaseFee = block.BlobBaseFee() 275 if l1BlockInfo.BlobBaseFee == nil { 276 // The L2 spec states to use the MIN_BLOB_GASPRICE from EIP-4844 if not yet active on L1. 277 l1BlockInfo.BlobBaseFee = big.NewInt(1) 278 } 279 blobBaseFeeScalar, baseFeeScalar, err := sysCfg.EcotoneScalars() 280 if err != nil { 281 return nil, err 282 } 283 l1BlockInfo.BlobBaseFeeScalar = blobBaseFeeScalar 284 l1BlockInfo.BaseFeeScalar = baseFeeScalar 285 out, err := l1BlockInfo.marshalBinaryEcotone() 286 if err != nil { 287 return nil, fmt.Errorf("failed to marshal Ecotone l1 block info: %w", err) 288 } 289 data = out 290 } else { 291 l1BlockInfo.L1FeeOverhead = sysCfg.Overhead 292 l1BlockInfo.L1FeeScalar = sysCfg.Scalar 293 out, err := l1BlockInfo.marshalBinaryBedrock() 294 if err != nil { 295 return nil, fmt.Errorf("failed to marshal Bedrock l1 block info: %w", err) 296 } 297 data = out 298 } 299 300 source := L1InfoDepositSource{ 301 L1BlockHash: block.Hash(), 302 SeqNumber: seqNumber, 303 } 304 // Set a very large gas limit with `IsSystemTransaction` to ensure 305 // that the L1 Attributes Transaction does not run out of gas. 306 out := &types.DepositTx{ 307 SourceHash: source.SourceHash(), 308 From: L1InfoDepositerAddress, 309 To: &L1BlockAddress, 310 Mint: nil, 311 Value: big.NewInt(0), 312 Gas: 150_000_000, 313 IsSystemTransaction: true, 314 Data: data, 315 } 316 // With the regolith fork we disable the IsSystemTx functionality, and allocate real gas 317 if rollupCfg.IsRegolith(l2BlockTime) { 318 out.IsSystemTransaction = false 319 out.Gas = RegolithSystemTxGas 320 } 321 return out, nil 322 } 323 324 // L1InfoDepositBytes returns a serialized L1-info attributes transaction. 325 func L1InfoDepositBytes(rollupCfg *rollup.Config, sysCfg eth.SystemConfig, seqNumber uint64, l1Info eth.BlockInfo, l2BlockTime uint64) ([]byte, error) { 326 dep, err := L1InfoDeposit(rollupCfg, sysCfg, seqNumber, l1Info, l2BlockTime) 327 if err != nil { 328 return nil, fmt.Errorf("failed to create L1 info tx: %w", err) 329 } 330 l1Tx := types.NewTx(dep) 331 opaqueL1Tx, err := l1Tx.MarshalBinary() 332 if err != nil { 333 return nil, fmt.Errorf("failed to encode L1 info tx: %w", err) 334 } 335 return opaqueL1Tx, nil 336 }