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  }