github.com/ethereum/go-ethereum@v1.16.1/beacon/engine/types.go (about) 1 // Copyright 2022 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package engine 18 19 import ( 20 "fmt" 21 "math/big" 22 "slices" 23 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/common/hexutil" 26 "github.com/ethereum/go-ethereum/core/types" 27 "github.com/ethereum/go-ethereum/params" 28 "github.com/ethereum/go-ethereum/trie" 29 ) 30 31 // PayloadVersion denotes the version of PayloadAttributes used to request the 32 // building of the payload to commence. 33 type PayloadVersion byte 34 35 var ( 36 PayloadV1 PayloadVersion = 0x1 37 PayloadV2 PayloadVersion = 0x2 38 PayloadV3 PayloadVersion = 0x3 39 ) 40 41 //go:generate go run github.com/fjl/gencodec -type PayloadAttributes -field-override payloadAttributesMarshaling -out gen_blockparams.go 42 43 // PayloadAttributes describes the environment context in which a block should 44 // be built. 45 type PayloadAttributes struct { 46 Timestamp uint64 `json:"timestamp" gencodec:"required"` 47 Random common.Hash `json:"prevRandao" gencodec:"required"` 48 SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"` 49 Withdrawals []*types.Withdrawal `json:"withdrawals"` 50 BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"` 51 } 52 53 // JSON type overrides for PayloadAttributes. 54 type payloadAttributesMarshaling struct { 55 Timestamp hexutil.Uint64 56 } 57 58 //go:generate go run github.com/fjl/gencodec -type ExecutableData -field-override executableDataMarshaling -out gen_ed.go 59 60 // ExecutableData is the data necessary to execute an EL payload. 61 type ExecutableData struct { 62 ParentHash common.Hash `json:"parentHash" gencodec:"required"` 63 FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"` 64 StateRoot common.Hash `json:"stateRoot" gencodec:"required"` 65 ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"` 66 LogsBloom []byte `json:"logsBloom" gencodec:"required"` 67 Random common.Hash `json:"prevRandao" gencodec:"required"` 68 Number uint64 `json:"blockNumber" gencodec:"required"` 69 GasLimit uint64 `json:"gasLimit" gencodec:"required"` 70 GasUsed uint64 `json:"gasUsed" gencodec:"required"` 71 Timestamp uint64 `json:"timestamp" gencodec:"required"` 72 ExtraData []byte `json:"extraData" gencodec:"required"` 73 BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"` 74 BlockHash common.Hash `json:"blockHash" gencodec:"required"` 75 Transactions [][]byte `json:"transactions" gencodec:"required"` 76 Withdrawals []*types.Withdrawal `json:"withdrawals"` 77 BlobGasUsed *uint64 `json:"blobGasUsed"` 78 ExcessBlobGas *uint64 `json:"excessBlobGas"` 79 ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"` 80 } 81 82 // JSON type overrides for executableData. 83 type executableDataMarshaling struct { 84 Number hexutil.Uint64 85 GasLimit hexutil.Uint64 86 GasUsed hexutil.Uint64 87 Timestamp hexutil.Uint64 88 BaseFeePerGas *hexutil.Big 89 ExtraData hexutil.Bytes 90 LogsBloom hexutil.Bytes 91 Transactions []hexutil.Bytes 92 BlobGasUsed *hexutil.Uint64 93 ExcessBlobGas *hexutil.Uint64 94 } 95 96 // StatelessPayloadStatusV1 is the result of a stateless payload execution. 97 type StatelessPayloadStatusV1 struct { 98 Status string `json:"status"` 99 StateRoot common.Hash `json:"stateRoot"` 100 ReceiptsRoot common.Hash `json:"receiptsRoot"` 101 ValidationError *string `json:"validationError"` 102 } 103 104 //go:generate go run github.com/fjl/gencodec -type ExecutionPayloadEnvelope -field-override executionPayloadEnvelopeMarshaling -out gen_epe.go 105 106 type ExecutionPayloadEnvelope struct { 107 ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"` 108 BlockValue *big.Int `json:"blockValue" gencodec:"required"` 109 BlobsBundle *BlobsBundleV1 `json:"blobsBundle"` 110 Requests [][]byte `json:"executionRequests"` 111 Override bool `json:"shouldOverrideBuilder"` 112 Witness *hexutil.Bytes `json:"witness,omitempty"` 113 } 114 115 type BlobsBundleV1 struct { 116 Commitments []hexutil.Bytes `json:"commitments"` 117 Proofs []hexutil.Bytes `json:"proofs"` 118 Blobs []hexutil.Bytes `json:"blobs"` 119 } 120 121 type BlobAndProofV1 struct { 122 Blob hexutil.Bytes `json:"blob"` 123 Proof hexutil.Bytes `json:"proof"` 124 } 125 126 type BlobAndProofV2 struct { 127 Blob hexutil.Bytes `json:"blob"` 128 CellProofs []hexutil.Bytes `json:"proofs"` 129 } 130 131 // JSON type overrides for ExecutionPayloadEnvelope. 132 type executionPayloadEnvelopeMarshaling struct { 133 BlockValue *hexutil.Big 134 Requests []hexutil.Bytes 135 } 136 137 type PayloadStatusV1 struct { 138 Status string `json:"status"` 139 Witness *hexutil.Bytes `json:"witness,omitempty"` 140 LatestValidHash *common.Hash `json:"latestValidHash"` 141 ValidationError *string `json:"validationError"` 142 } 143 144 type TransitionConfigurationV1 struct { 145 TerminalTotalDifficulty *hexutil.Big `json:"terminalTotalDifficulty"` 146 TerminalBlockHash common.Hash `json:"terminalBlockHash"` 147 TerminalBlockNumber hexutil.Uint64 `json:"terminalBlockNumber"` 148 } 149 150 // PayloadID is an identifier of the payload build process 151 type PayloadID [8]byte 152 153 // Version returns the payload version associated with the identifier. 154 func (b PayloadID) Version() PayloadVersion { 155 return PayloadVersion(b[0]) 156 } 157 158 // Is returns whether the identifier matches any of provided payload versions. 159 func (b PayloadID) Is(versions ...PayloadVersion) bool { 160 return slices.Contains(versions, b.Version()) 161 } 162 163 func (b PayloadID) String() string { 164 return hexutil.Encode(b[:]) 165 } 166 167 func (b PayloadID) MarshalText() ([]byte, error) { 168 return hexutil.Bytes(b[:]).MarshalText() 169 } 170 171 func (b *PayloadID) UnmarshalText(input []byte) error { 172 err := hexutil.UnmarshalFixedText("PayloadID", input, b[:]) 173 if err != nil { 174 return fmt.Errorf("invalid payload id %q: %w", input, err) 175 } 176 return nil 177 } 178 179 type ForkChoiceResponse struct { 180 PayloadStatus PayloadStatusV1 `json:"payloadStatus"` 181 PayloadID *PayloadID `json:"payloadId"` 182 } 183 184 type ForkchoiceStateV1 struct { 185 HeadBlockHash common.Hash `json:"headBlockHash"` 186 SafeBlockHash common.Hash `json:"safeBlockHash"` 187 FinalizedBlockHash common.Hash `json:"finalizedBlockHash"` 188 } 189 190 func encodeTransactions(txs []*types.Transaction) [][]byte { 191 var enc = make([][]byte, len(txs)) 192 for i, tx := range txs { 193 enc[i], _ = tx.MarshalBinary() 194 } 195 return enc 196 } 197 198 func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { 199 var txs = make([]*types.Transaction, len(enc)) 200 for i, encTx := range enc { 201 var tx types.Transaction 202 if err := tx.UnmarshalBinary(encTx); err != nil { 203 return nil, fmt.Errorf("invalid transaction %d: %v", i, err) 204 } 205 txs[i] = &tx 206 } 207 return txs, nil 208 } 209 210 // ExecutableDataToBlock constructs a block from executable data. 211 // It verifies that the following fields: 212 // 213 // len(extraData) <= 32 214 // uncleHash = emptyUncleHash 215 // difficulty = 0 216 // if versionedHashes != nil, versionedHashes match to blob transactions 217 // 218 // and that the blockhash of the constructed block matches the parameters. Nil 219 // Withdrawals value will propagate through the returned block. Empty 220 // Withdrawals value must be passed via non-nil, length 0 value in data. 221 func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte) (*types.Block, error) { 222 block, err := ExecutableDataToBlockNoHash(data, versionedHashes, beaconRoot, requests) 223 if err != nil { 224 return nil, err 225 } 226 if block.Hash() != data.BlockHash { 227 return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", data.BlockHash, block.Hash()) 228 } 229 return block, nil 230 } 231 232 // ExecutableDataToBlockNoHash is analogous to ExecutableDataToBlock, but is used 233 // for stateless execution, so it skips checking if the executable data hashes to 234 // the requested hash (stateless has to *compute* the root hash, it's not given). 235 func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte) (*types.Block, error) { 236 txs, err := decodeTransactions(data.Transactions) 237 if err != nil { 238 return nil, err 239 } 240 if len(data.ExtraData) > int(params.MaximumExtraDataSize) { 241 return nil, fmt.Errorf("invalid extradata length: %v", len(data.ExtraData)) 242 } 243 if len(data.LogsBloom) != 256 { 244 return nil, fmt.Errorf("invalid logsBloom length: %v", len(data.LogsBloom)) 245 } 246 // Check that baseFeePerGas is not negative or too big 247 if data.BaseFeePerGas != nil && (data.BaseFeePerGas.Sign() == -1 || data.BaseFeePerGas.BitLen() > 256) { 248 return nil, fmt.Errorf("invalid baseFeePerGas: %v", data.BaseFeePerGas) 249 } 250 var blobHashes = make([]common.Hash, 0, len(txs)) 251 for _, tx := range txs { 252 blobHashes = append(blobHashes, tx.BlobHashes()...) 253 } 254 if len(blobHashes) != len(versionedHashes) { 255 return nil, fmt.Errorf("invalid number of versionedHashes: %v blobHashes: %v", versionedHashes, blobHashes) 256 } 257 for i := 0; i < len(blobHashes); i++ { 258 if blobHashes[i] != versionedHashes[i] { 259 return nil, fmt.Errorf("invalid versionedHash at %v: %v blobHashes: %v", i, versionedHashes, blobHashes) 260 } 261 } 262 // Only set withdrawalsRoot if it is non-nil. This allows CLs to use 263 // ExecutableData before withdrawals are enabled by marshaling 264 // Withdrawals as the json null value. 265 var withdrawalsRoot *common.Hash 266 if data.Withdrawals != nil { 267 h := types.DeriveSha(types.Withdrawals(data.Withdrawals), trie.NewStackTrie(nil)) 268 withdrawalsRoot = &h 269 } 270 271 var requestsHash *common.Hash 272 if requests != nil { 273 h := types.CalcRequestsHash(requests) 274 requestsHash = &h 275 } 276 277 header := &types.Header{ 278 ParentHash: data.ParentHash, 279 UncleHash: types.EmptyUncleHash, 280 Coinbase: data.FeeRecipient, 281 Root: data.StateRoot, 282 TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)), 283 ReceiptHash: data.ReceiptsRoot, 284 Bloom: types.BytesToBloom(data.LogsBloom), 285 Difficulty: common.Big0, 286 Number: new(big.Int).SetUint64(data.Number), 287 GasLimit: data.GasLimit, 288 GasUsed: data.GasUsed, 289 Time: data.Timestamp, 290 BaseFee: data.BaseFeePerGas, 291 Extra: data.ExtraData, 292 MixDigest: data.Random, 293 WithdrawalsHash: withdrawalsRoot, 294 ExcessBlobGas: data.ExcessBlobGas, 295 BlobGasUsed: data.BlobGasUsed, 296 ParentBeaconRoot: beaconRoot, 297 RequestsHash: requestsHash, 298 } 299 return types.NewBlockWithHeader(header). 300 WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals}). 301 WithWitness(data.ExecutionWitness), 302 nil 303 } 304 305 // BlockToExecutableData constructs the ExecutableData structure by filling the 306 // fields from the given block. It assumes the given block is post-merge block. 307 func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.BlobTxSidecar, requests [][]byte) *ExecutionPayloadEnvelope { 308 data := &ExecutableData{ 309 BlockHash: block.Hash(), 310 ParentHash: block.ParentHash(), 311 FeeRecipient: block.Coinbase(), 312 StateRoot: block.Root(), 313 Number: block.NumberU64(), 314 GasLimit: block.GasLimit(), 315 GasUsed: block.GasUsed(), 316 BaseFeePerGas: block.BaseFee(), 317 Timestamp: block.Time(), 318 ReceiptsRoot: block.ReceiptHash(), 319 LogsBloom: block.Bloom().Bytes(), 320 Transactions: encodeTransactions(block.Transactions()), 321 Random: block.MixDigest(), 322 ExtraData: block.Extra(), 323 Withdrawals: block.Withdrawals(), 324 BlobGasUsed: block.BlobGasUsed(), 325 ExcessBlobGas: block.ExcessBlobGas(), 326 ExecutionWitness: block.ExecutionWitness(), 327 } 328 329 // Add blobs. 330 bundle := BlobsBundleV1{ 331 Commitments: make([]hexutil.Bytes, 0), 332 Blobs: make([]hexutil.Bytes, 0), 333 Proofs: make([]hexutil.Bytes, 0), 334 } 335 for _, sidecar := range sidecars { 336 for j := range sidecar.Blobs { 337 bundle.Blobs = append(bundle.Blobs, hexutil.Bytes(sidecar.Blobs[j][:])) 338 bundle.Commitments = append(bundle.Commitments, hexutil.Bytes(sidecar.Commitments[j][:])) 339 } 340 for _, proof := range sidecar.Proofs { 341 bundle.Proofs = append(bundle.Proofs, hexutil.Bytes(proof[:])) 342 } 343 } 344 345 return &ExecutionPayloadEnvelope{ 346 ExecutionPayload: data, 347 BlockValue: fees, 348 BlobsBundle: &bundle, 349 Requests: requests, 350 Override: false, 351 } 352 } 353 354 // ExecutionPayloadBody is used in the response to GetPayloadBodiesByHash and GetPayloadBodiesByRange 355 type ExecutionPayloadBody struct { 356 TransactionData []hexutil.Bytes `json:"transactions"` 357 Withdrawals []*types.Withdrawal `json:"withdrawals"` 358 } 359 360 // Client identifiers to support ClientVersionV1. 361 const ( 362 ClientCode = "GE" 363 ClientName = "go-ethereum" 364 ) 365 366 // ClientVersionV1 contains information which identifies a client implementation. 367 type ClientVersionV1 struct { 368 Code string `json:"code"` 369 Name string `json:"name"` 370 Version string `json:"version"` 371 Commit string `json:"commit"` 372 } 373 374 func (v *ClientVersionV1) String() string { 375 return fmt.Sprintf("%s-%s-%s-%s", v.Code, v.Name, v.Version, v.Commit) 376 }