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  }