github.com/calmw/ethereum@v0.1.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 23 "github.com/calmw/ethereum/common" 24 "github.com/calmw/ethereum/common/hexutil" 25 "github.com/calmw/ethereum/core/types" 26 "github.com/calmw/ethereum/trie" 27 ) 28 29 //go:generate go run github.com/fjl/gencodec -type PayloadAttributes -field-override payloadAttributesMarshaling -out gen_blockparams.go 30 31 // PayloadAttributes describes the environment context in which a block should 32 // be built. 33 type PayloadAttributes struct { 34 Timestamp uint64 `json:"timestamp" gencodec:"required"` 35 Random common.Hash `json:"prevRandao" gencodec:"required"` 36 SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"` 37 Withdrawals []*types.Withdrawal `json:"withdrawals"` 38 } 39 40 // JSON type overrides for PayloadAttributes. 41 type payloadAttributesMarshaling struct { 42 Timestamp hexutil.Uint64 43 } 44 45 //go:generate go run github.com/fjl/gencodec -type ExecutableData -field-override executableDataMarshaling -out gen_ed.go 46 47 // ExecutableData is the data necessary to execute an EL payload. 48 type ExecutableData struct { 49 ParentHash common.Hash `json:"parentHash" gencodec:"required"` 50 FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"` 51 StateRoot common.Hash `json:"stateRoot" gencodec:"required"` 52 ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"` 53 LogsBloom []byte `json:"logsBloom" gencodec:"required"` 54 Random common.Hash `json:"prevRandao" gencodec:"required"` 55 Number uint64 `json:"blockNumber" gencodec:"required"` 56 GasLimit uint64 `json:"gasLimit" gencodec:"required"` 57 GasUsed uint64 `json:"gasUsed" gencodec:"required"` 58 Timestamp uint64 `json:"timestamp" gencodec:"required"` 59 ExtraData []byte `json:"extraData" gencodec:"required"` 60 BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"` 61 BlockHash common.Hash `json:"blockHash" gencodec:"required"` 62 Transactions [][]byte `json:"transactions" gencodec:"required"` 63 Withdrawals []*types.Withdrawal `json:"withdrawals"` 64 } 65 66 // JSON type overrides for executableData. 67 type executableDataMarshaling struct { 68 Number hexutil.Uint64 69 GasLimit hexutil.Uint64 70 GasUsed hexutil.Uint64 71 Timestamp hexutil.Uint64 72 BaseFeePerGas *hexutil.Big 73 ExtraData hexutil.Bytes 74 LogsBloom hexutil.Bytes 75 Transactions []hexutil.Bytes 76 } 77 78 //go:generate go run github.com/fjl/gencodec -type ExecutionPayloadEnvelope -field-override executionPayloadEnvelopeMarshaling -out gen_epe.go 79 80 type ExecutionPayloadEnvelope struct { 81 ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"` 82 BlockValue *big.Int `json:"blockValue" gencodec:"required"` 83 } 84 85 // JSON type overrides for ExecutionPayloadEnvelope. 86 type executionPayloadEnvelopeMarshaling struct { 87 BlockValue *hexutil.Big 88 } 89 90 type PayloadStatusV1 struct { 91 Status string `json:"status"` 92 LatestValidHash *common.Hash `json:"latestValidHash"` 93 ValidationError *string `json:"validationError"` 94 } 95 96 type TransitionConfigurationV1 struct { 97 TerminalTotalDifficulty *hexutil.Big `json:"terminalTotalDifficulty"` 98 TerminalBlockHash common.Hash `json:"terminalBlockHash"` 99 TerminalBlockNumber hexutil.Uint64 `json:"terminalBlockNumber"` 100 } 101 102 // PayloadID is an identifier of the payload build process 103 type PayloadID [8]byte 104 105 func (b PayloadID) String() string { 106 return hexutil.Encode(b[:]) 107 } 108 109 func (b PayloadID) MarshalText() ([]byte, error) { 110 return hexutil.Bytes(b[:]).MarshalText() 111 } 112 113 func (b *PayloadID) UnmarshalText(input []byte) error { 114 err := hexutil.UnmarshalFixedText("PayloadID", input, b[:]) 115 if err != nil { 116 return fmt.Errorf("invalid payload id %q: %w", input, err) 117 } 118 return nil 119 } 120 121 type ForkChoiceResponse struct { 122 PayloadStatus PayloadStatusV1 `json:"payloadStatus"` 123 PayloadID *PayloadID `json:"payloadId"` 124 } 125 126 type ForkchoiceStateV1 struct { 127 HeadBlockHash common.Hash `json:"headBlockHash"` 128 SafeBlockHash common.Hash `json:"safeBlockHash"` 129 FinalizedBlockHash common.Hash `json:"finalizedBlockHash"` 130 } 131 132 func encodeTransactions(txs []*types.Transaction) [][]byte { 133 var enc = make([][]byte, len(txs)) 134 for i, tx := range txs { 135 enc[i], _ = tx.MarshalBinary() 136 } 137 return enc 138 } 139 140 func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { 141 var txs = make([]*types.Transaction, len(enc)) 142 for i, encTx := range enc { 143 var tx types.Transaction 144 if err := tx.UnmarshalBinary(encTx); err != nil { 145 return nil, fmt.Errorf("invalid transaction %d: %v", i, err) 146 } 147 txs[i] = &tx 148 } 149 return txs, nil 150 } 151 152 // ExecutableDataToBlock constructs a block from executable data. 153 // It verifies that the following fields: 154 // 155 // len(extraData) <= 32 156 // uncleHash = emptyUncleHash 157 // difficulty = 0 158 // 159 // and that the blockhash of the constructed block matches the parameters. Nil 160 // Withdrawals value will propagate through the returned block. Empty 161 // Withdrawals value must be passed via non-nil, length 0 value in params. 162 func ExecutableDataToBlock(params ExecutableData) (*types.Block, error) { 163 txs, err := decodeTransactions(params.Transactions) 164 if err != nil { 165 return nil, err 166 } 167 if len(params.ExtraData) > 32 { 168 return nil, fmt.Errorf("invalid extradata length: %v", len(params.ExtraData)) 169 } 170 if len(params.LogsBloom) != 256 { 171 return nil, fmt.Errorf("invalid logsBloom length: %v", len(params.LogsBloom)) 172 } 173 // Check that baseFeePerGas is not negative or too big 174 if params.BaseFeePerGas != nil && (params.BaseFeePerGas.Sign() == -1 || params.BaseFeePerGas.BitLen() > 256) { 175 return nil, fmt.Errorf("invalid baseFeePerGas: %v", params.BaseFeePerGas) 176 } 177 // Only set withdrawalsRoot if it is non-nil. This allows CLs to use 178 // ExecutableData before withdrawals are enabled by marshaling 179 // Withdrawals as the json null value. 180 var withdrawalsRoot *common.Hash 181 if params.Withdrawals != nil { 182 h := types.DeriveSha(types.Withdrawals(params.Withdrawals), trie.NewStackTrie(nil)) 183 withdrawalsRoot = &h 184 } 185 header := &types.Header{ 186 ParentHash: params.ParentHash, 187 UncleHash: types.EmptyUncleHash, 188 Coinbase: params.FeeRecipient, 189 Root: params.StateRoot, 190 TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)), 191 ReceiptHash: params.ReceiptsRoot, 192 Bloom: types.BytesToBloom(params.LogsBloom), 193 Difficulty: common.Big0, 194 Number: new(big.Int).SetUint64(params.Number), 195 GasLimit: params.GasLimit, 196 GasUsed: params.GasUsed, 197 Time: params.Timestamp, 198 BaseFee: params.BaseFeePerGas, 199 Extra: params.ExtraData, 200 MixDigest: params.Random, 201 WithdrawalsHash: withdrawalsRoot, 202 } 203 block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(params.Withdrawals) 204 if block.Hash() != params.BlockHash { 205 return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", params.BlockHash, block.Hash()) 206 } 207 return block, nil 208 } 209 210 // BlockToExecutableData constructs the ExecutableData structure by filling the 211 // fields from the given block. It assumes the given block is post-merge block. 212 func BlockToExecutableData(block *types.Block, fees *big.Int) *ExecutionPayloadEnvelope { 213 data := &ExecutableData{ 214 BlockHash: block.Hash(), 215 ParentHash: block.ParentHash(), 216 FeeRecipient: block.Coinbase(), 217 StateRoot: block.Root(), 218 Number: block.NumberU64(), 219 GasLimit: block.GasLimit(), 220 GasUsed: block.GasUsed(), 221 BaseFeePerGas: block.BaseFee(), 222 Timestamp: block.Time(), 223 ReceiptsRoot: block.ReceiptHash(), 224 LogsBloom: block.Bloom().Bytes(), 225 Transactions: encodeTransactions(block.Transactions()), 226 Random: block.MixDigest(), 227 ExtraData: block.Extra(), 228 Withdrawals: block.Withdrawals(), 229 } 230 return &ExecutionPayloadEnvelope{ExecutionPayload: data, BlockValue: fees} 231 } 232 233 // ExecutionPayloadBodyV1 is used in the response to GetPayloadBodiesByHashV1 and GetPayloadBodiesByRangeV1 234 type ExecutionPayloadBodyV1 struct { 235 TransactionData []hexutil.Bytes `json:"transactions"` 236 Withdrawals []*types.Withdrawal `json:"withdrawals"` 237 }