github.com/ethereum/go-ethereum@v1.14.3/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/trie" 28 ) 29 30 // PayloadVersion denotes the version of PayloadAttributes used to request the 31 // building of the payload to commence. 32 type PayloadVersion byte 33 34 var ( 35 PayloadV1 PayloadVersion = 0x1 36 PayloadV2 PayloadVersion = 0x2 37 PayloadV3 PayloadVersion = 0x3 38 ) 39 40 //go:generate go run github.com/fjl/gencodec -type PayloadAttributes -field-override payloadAttributesMarshaling -out gen_blockparams.go 41 42 // PayloadAttributes describes the environment context in which a block should 43 // be built. 44 type PayloadAttributes struct { 45 Timestamp uint64 `json:"timestamp" gencodec:"required"` 46 Random common.Hash `json:"prevRandao" gencodec:"required"` 47 SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"` 48 Withdrawals []*types.Withdrawal `json:"withdrawals"` 49 BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"` 50 } 51 52 // JSON type overrides for PayloadAttributes. 53 type payloadAttributesMarshaling struct { 54 Timestamp hexutil.Uint64 55 } 56 57 //go:generate go run github.com/fjl/gencodec -type ExecutableData -field-override executableDataMarshaling -out gen_ed.go 58 59 // ExecutableData is the data necessary to execute an EL payload. 60 type ExecutableData struct { 61 ParentHash common.Hash `json:"parentHash" gencodec:"required"` 62 FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"` 63 StateRoot common.Hash `json:"stateRoot" gencodec:"required"` 64 ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"` 65 LogsBloom []byte `json:"logsBloom" gencodec:"required"` 66 Random common.Hash `json:"prevRandao" gencodec:"required"` 67 Number uint64 `json:"blockNumber" gencodec:"required"` 68 GasLimit uint64 `json:"gasLimit" gencodec:"required"` 69 GasUsed uint64 `json:"gasUsed" gencodec:"required"` 70 Timestamp uint64 `json:"timestamp" gencodec:"required"` 71 ExtraData []byte `json:"extraData" gencodec:"required"` 72 BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"` 73 BlockHash common.Hash `json:"blockHash" gencodec:"required"` 74 Transactions [][]byte `json:"transactions" gencodec:"required"` 75 Withdrawals []*types.Withdrawal `json:"withdrawals"` 76 BlobGasUsed *uint64 `json:"blobGasUsed"` 77 ExcessBlobGas *uint64 `json:"excessBlobGas"` 78 } 79 80 // JSON type overrides for executableData. 81 type executableDataMarshaling struct { 82 Number hexutil.Uint64 83 GasLimit hexutil.Uint64 84 GasUsed hexutil.Uint64 85 Timestamp hexutil.Uint64 86 BaseFeePerGas *hexutil.Big 87 ExtraData hexutil.Bytes 88 LogsBloom hexutil.Bytes 89 Transactions []hexutil.Bytes 90 BlobGasUsed *hexutil.Uint64 91 ExcessBlobGas *hexutil.Uint64 92 } 93 94 //go:generate go run github.com/fjl/gencodec -type ExecutionPayloadEnvelope -field-override executionPayloadEnvelopeMarshaling -out gen_epe.go 95 96 type ExecutionPayloadEnvelope struct { 97 ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"` 98 BlockValue *big.Int `json:"blockValue" gencodec:"required"` 99 BlobsBundle *BlobsBundleV1 `json:"blobsBundle"` 100 Override bool `json:"shouldOverrideBuilder"` 101 } 102 103 type BlobsBundleV1 struct { 104 Commitments []hexutil.Bytes `json:"commitments"` 105 Proofs []hexutil.Bytes `json:"proofs"` 106 Blobs []hexutil.Bytes `json:"blobs"` 107 } 108 109 // JSON type overrides for ExecutionPayloadEnvelope. 110 type executionPayloadEnvelopeMarshaling struct { 111 BlockValue *hexutil.Big 112 } 113 114 type PayloadStatusV1 struct { 115 Status string `json:"status"` 116 LatestValidHash *common.Hash `json:"latestValidHash"` 117 ValidationError *string `json:"validationError"` 118 } 119 120 type TransitionConfigurationV1 struct { 121 TerminalTotalDifficulty *hexutil.Big `json:"terminalTotalDifficulty"` 122 TerminalBlockHash common.Hash `json:"terminalBlockHash"` 123 TerminalBlockNumber hexutil.Uint64 `json:"terminalBlockNumber"` 124 } 125 126 // PayloadID is an identifier of the payload build process 127 type PayloadID [8]byte 128 129 // Version returns the payload version associated with the identifier. 130 func (b PayloadID) Version() PayloadVersion { 131 return PayloadVersion(b[0]) 132 } 133 134 // Is returns whether the identifier matches any of provided payload versions. 135 func (b PayloadID) Is(versions ...PayloadVersion) bool { 136 return slices.Contains(versions, b.Version()) 137 } 138 139 func (b PayloadID) String() string { 140 return hexutil.Encode(b[:]) 141 } 142 143 func (b PayloadID) MarshalText() ([]byte, error) { 144 return hexutil.Bytes(b[:]).MarshalText() 145 } 146 147 func (b *PayloadID) UnmarshalText(input []byte) error { 148 err := hexutil.UnmarshalFixedText("PayloadID", input, b[:]) 149 if err != nil { 150 return fmt.Errorf("invalid payload id %q: %w", input, err) 151 } 152 return nil 153 } 154 155 type ForkChoiceResponse struct { 156 PayloadStatus PayloadStatusV1 `json:"payloadStatus"` 157 PayloadID *PayloadID `json:"payloadId"` 158 } 159 160 type ForkchoiceStateV1 struct { 161 HeadBlockHash common.Hash `json:"headBlockHash"` 162 SafeBlockHash common.Hash `json:"safeBlockHash"` 163 FinalizedBlockHash common.Hash `json:"finalizedBlockHash"` 164 } 165 166 func encodeTransactions(txs []*types.Transaction) [][]byte { 167 var enc = make([][]byte, len(txs)) 168 for i, tx := range txs { 169 enc[i], _ = tx.MarshalBinary() 170 } 171 return enc 172 } 173 174 func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { 175 var txs = make([]*types.Transaction, len(enc)) 176 for i, encTx := range enc { 177 var tx types.Transaction 178 if err := tx.UnmarshalBinary(encTx); err != nil { 179 return nil, fmt.Errorf("invalid transaction %d: %v", i, err) 180 } 181 txs[i] = &tx 182 } 183 return txs, nil 184 } 185 186 // ExecutableDataToBlock constructs a block from executable data. 187 // It verifies that the following fields: 188 // 189 // len(extraData) <= 32 190 // uncleHash = emptyUncleHash 191 // difficulty = 0 192 // if versionedHashes != nil, versionedHashes match to blob transactions 193 // 194 // and that the blockhash of the constructed block matches the parameters. Nil 195 // Withdrawals value will propagate through the returned block. Empty 196 // Withdrawals value must be passed via non-nil, length 0 value in params. 197 func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (*types.Block, error) { 198 txs, err := decodeTransactions(params.Transactions) 199 if err != nil { 200 return nil, err 201 } 202 if len(params.ExtraData) > 32 { 203 return nil, fmt.Errorf("invalid extradata length: %v", len(params.ExtraData)) 204 } 205 if len(params.LogsBloom) != 256 { 206 return nil, fmt.Errorf("invalid logsBloom length: %v", len(params.LogsBloom)) 207 } 208 // Check that baseFeePerGas is not negative or too big 209 if params.BaseFeePerGas != nil && (params.BaseFeePerGas.Sign() == -1 || params.BaseFeePerGas.BitLen() > 256) { 210 return nil, fmt.Errorf("invalid baseFeePerGas: %v", params.BaseFeePerGas) 211 } 212 var blobHashes []common.Hash 213 for _, tx := range txs { 214 blobHashes = append(blobHashes, tx.BlobHashes()...) 215 } 216 if len(blobHashes) != len(versionedHashes) { 217 return nil, fmt.Errorf("invalid number of versionedHashes: %v blobHashes: %v", versionedHashes, blobHashes) 218 } 219 for i := 0; i < len(blobHashes); i++ { 220 if blobHashes[i] != versionedHashes[i] { 221 return nil, fmt.Errorf("invalid versionedHash at %v: %v blobHashes: %v", i, versionedHashes, blobHashes) 222 } 223 } 224 // Only set withdrawalsRoot if it is non-nil. This allows CLs to use 225 // ExecutableData before withdrawals are enabled by marshaling 226 // Withdrawals as the json null value. 227 var withdrawalsRoot *common.Hash 228 if params.Withdrawals != nil { 229 h := types.DeriveSha(types.Withdrawals(params.Withdrawals), trie.NewStackTrie(nil)) 230 withdrawalsRoot = &h 231 } 232 header := &types.Header{ 233 ParentHash: params.ParentHash, 234 UncleHash: types.EmptyUncleHash, 235 Coinbase: params.FeeRecipient, 236 Root: params.StateRoot, 237 TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)), 238 ReceiptHash: params.ReceiptsRoot, 239 Bloom: types.BytesToBloom(params.LogsBloom), 240 Difficulty: common.Big0, 241 Number: new(big.Int).SetUint64(params.Number), 242 GasLimit: params.GasLimit, 243 GasUsed: params.GasUsed, 244 Time: params.Timestamp, 245 BaseFee: params.BaseFeePerGas, 246 Extra: params.ExtraData, 247 MixDigest: params.Random, 248 WithdrawalsHash: withdrawalsRoot, 249 ExcessBlobGas: params.ExcessBlobGas, 250 BlobGasUsed: params.BlobGasUsed, 251 ParentBeaconRoot: beaconRoot, 252 } 253 block := types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: params.Withdrawals}) 254 if block.Hash() != params.BlockHash { 255 return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", params.BlockHash, block.Hash()) 256 } 257 return block, nil 258 } 259 260 // BlockToExecutableData constructs the ExecutableData structure by filling the 261 // fields from the given block. It assumes the given block is post-merge block. 262 func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.BlobTxSidecar) *ExecutionPayloadEnvelope { 263 data := &ExecutableData{ 264 BlockHash: block.Hash(), 265 ParentHash: block.ParentHash(), 266 FeeRecipient: block.Coinbase(), 267 StateRoot: block.Root(), 268 Number: block.NumberU64(), 269 GasLimit: block.GasLimit(), 270 GasUsed: block.GasUsed(), 271 BaseFeePerGas: block.BaseFee(), 272 Timestamp: block.Time(), 273 ReceiptsRoot: block.ReceiptHash(), 274 LogsBloom: block.Bloom().Bytes(), 275 Transactions: encodeTransactions(block.Transactions()), 276 Random: block.MixDigest(), 277 ExtraData: block.Extra(), 278 Withdrawals: block.Withdrawals(), 279 BlobGasUsed: block.BlobGasUsed(), 280 ExcessBlobGas: block.ExcessBlobGas(), 281 } 282 bundle := BlobsBundleV1{ 283 Commitments: make([]hexutil.Bytes, 0), 284 Blobs: make([]hexutil.Bytes, 0), 285 Proofs: make([]hexutil.Bytes, 0), 286 } 287 for _, sidecar := range sidecars { 288 for j := range sidecar.Blobs { 289 bundle.Blobs = append(bundle.Blobs, hexutil.Bytes(sidecar.Blobs[j][:])) 290 bundle.Commitments = append(bundle.Commitments, hexutil.Bytes(sidecar.Commitments[j][:])) 291 bundle.Proofs = append(bundle.Proofs, hexutil.Bytes(sidecar.Proofs[j][:])) 292 } 293 } 294 return &ExecutionPayloadEnvelope{ExecutionPayload: data, BlockValue: fees, BlobsBundle: &bundle, Override: false} 295 } 296 297 // ExecutionPayloadBodyV1 is used in the response to GetPayloadBodiesByHashV1 and GetPayloadBodiesByRangeV1 298 type ExecutionPayloadBodyV1 struct { 299 TransactionData []hexutil.Bytes `json:"transactions"` 300 Withdrawals []*types.Withdrawal `json:"withdrawals"` 301 } 302 303 // Client identifiers to support ClientVersionV1. 304 const ( 305 ClientCode = "GE" 306 ClientName = "go-ethereum" 307 ) 308 309 // ClientVersionV1 contains information which identifies a client implementation. 310 type ClientVersionV1 struct { 311 Code string `json:"code"` 312 Name string `json:"name"` 313 Version string `json:"version"` 314 Commit string `json:"commit"` 315 } 316 317 func (v *ClientVersionV1) String() string { 318 return fmt.Sprintf("%s-%s-%s-%s", v.Code, v.Name, v.Version, v.Commit) 319 }