forked from cerc-io/plugeth
beacon/engine, eth/catalyst: EIP-4844 updates for the engine API (#27736)
This is a spin-out from the EIP-4844 devnet branch, containing just the Engine API modifications and nothing else. The newPayloadV3 endpoint won't really work in this version, but we need the data structures for testing so I'd like to get this in early. Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de>
This commit is contained in:
co-authored by
Marius van der Wijden
parent
d4d88f9bce
commit
e86ad52640
+39
-24
@@ -81,8 +81,10 @@ var caps = []string{
|
||||
"engine_exchangeTransitionConfigurationV1",
|
||||
"engine_getPayloadV1",
|
||||
"engine_getPayloadV2",
|
||||
"engine_getPayloadV3",
|
||||
"engine_newPayloadV1",
|
||||
"engine_newPayloadV2",
|
||||
"engine_newPayloadV3",
|
||||
"engine_getPayloadBodiesByHashV1",
|
||||
"engine_getPayloadBodiesByRangeV1",
|
||||
}
|
||||
@@ -405,23 +407,13 @@ func (api *ConsensusAPI) GetPayloadV2(payloadID engine.PayloadID) (*engine.Execu
|
||||
return api.getPayload(payloadID)
|
||||
}
|
||||
|
||||
func (api *ConsensusAPI) getPayload(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) {
|
||||
log.Trace("Engine API request received", "method", "GetPayload", "id", payloadID)
|
||||
data := api.localBlocks.get(payloadID, false)
|
||||
if data == nil {
|
||||
return nil, engine.UnknownPayload
|
||||
}
|
||||
return data, nil
|
||||
// GetPayloadV3 returns a cached payload by id.
|
||||
func (api *ConsensusAPI) GetPayloadV3(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) {
|
||||
return api.getPayload(payloadID)
|
||||
}
|
||||
|
||||
// getFullPayload returns a cached payload by it. The difference is that this
|
||||
// function always expects a non-empty payload, but can also return empty one
|
||||
// if no transaction is executable.
|
||||
//
|
||||
// Note, this function is not a part of standard engine API, meant to be used
|
||||
// by consensus client mock in dev mode.
|
||||
func (api *ConsensusAPI) getFullPayload(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) {
|
||||
log.Trace("Engine API request received", "method", "GetFullPayload", "id", payloadID)
|
||||
func (api *ConsensusAPI) getPayload(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) {
|
||||
log.Trace("Engine API request received", "method", "GetPayload", "id", payloadID)
|
||||
data := api.localBlocks.get(payloadID, true)
|
||||
if data == nil {
|
||||
return nil, engine.UnknownPayload
|
||||
@@ -434,7 +426,7 @@ func (api *ConsensusAPI) NewPayloadV1(params engine.ExecutableData) (engine.Payl
|
||||
if params.Withdrawals != nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("withdrawals not supported in V1"))
|
||||
}
|
||||
return api.newPayload(params)
|
||||
return api.newPayload(params, nil)
|
||||
}
|
||||
|
||||
// NewPayloadV2 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
||||
@@ -446,10 +438,29 @@ func (api *ConsensusAPI) NewPayloadV2(params engine.ExecutableData) (engine.Payl
|
||||
} else if params.Withdrawals != nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil withdrawals pre-shanghai"))
|
||||
}
|
||||
return api.newPayload(params)
|
||||
if api.eth.BlockChain().Config().IsCancun(new(big.Int).SetUint64(params.Number), params.Timestamp) {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("newPayloadV2 called post-cancun"))
|
||||
}
|
||||
return api.newPayload(params, nil)
|
||||
}
|
||||
|
||||
func (api *ConsensusAPI) newPayload(params engine.ExecutableData) (engine.PayloadStatusV1, error) {
|
||||
// NewPayloadV3 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
||||
func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHashes *[]common.Hash) (engine.PayloadStatusV1, error) {
|
||||
if !api.eth.BlockChain().Config().IsCancun(new(big.Int).SetUint64(params.Number), params.Timestamp) {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("newPayloadV3 called pre-cancun"))
|
||||
}
|
||||
|
||||
if params.ExcessDataGas == nil {
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(fmt.Errorf("nil excessDataGas post-cancun"))
|
||||
}
|
||||
var hashes []common.Hash
|
||||
if versionedHashes != nil {
|
||||
hashes = *versionedHashes
|
||||
}
|
||||
return api.newPayload(params, hashes)
|
||||
}
|
||||
|
||||
func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashes []common.Hash) (engine.PayloadStatusV1, error) {
|
||||
// The locking here is, strictly, not required. Without these locks, this can happen:
|
||||
//
|
||||
// 1. NewPayload( execdata-N ) is invoked from the CL. It goes all the way down to
|
||||
@@ -467,9 +478,9 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData) (engine.Payloa
|
||||
defer api.newPayloadLock.Unlock()
|
||||
|
||||
log.Trace("Engine API request received", "method", "NewPayload", "number", params.Number, "hash", params.BlockHash)
|
||||
block, err := engine.ExecutableDataToBlock(params)
|
||||
block, err := engine.ExecutableDataToBlock(params, versionedHashes)
|
||||
if err != nil {
|
||||
log.Debug("Invalid NewPayload params", "params", params, "error", err)
|
||||
log.Warn("Invalid NewPayload params", "params", params, "error", err)
|
||||
return engine.PayloadStatusV1{Status: engine.INVALID}, nil
|
||||
}
|
||||
// Stash away the last update to warn the user if the beacon client goes offline
|
||||
@@ -730,8 +741,8 @@ func (api *ConsensusAPI) ExchangeCapabilities([]string) []string {
|
||||
return caps
|
||||
}
|
||||
|
||||
// GetPayloadBodiesByHashV1 implements engine_getPayloadBodiesByHashV1 which
|
||||
// allows for retrieval of a list of block bodies by the engine api.
|
||||
// GetPayloadBodiesByHashV1 implements engine_getPayloadBodiesByHashV1 which allows for retrieval of a list
|
||||
// of block bodies by the engine api.
|
||||
func (api *ConsensusAPI) GetPayloadBodiesByHashV1(hashes []common.Hash) []*engine.ExecutionPayloadBodyV1 {
|
||||
var bodies = make([]*engine.ExecutionPayloadBodyV1, len(hashes))
|
||||
for i, hash := range hashes {
|
||||
@@ -741,8 +752,8 @@ func (api *ConsensusAPI) GetPayloadBodiesByHashV1(hashes []common.Hash) []*engin
|
||||
return bodies
|
||||
}
|
||||
|
||||
// GetPayloadBodiesByRangeV1 implements engine_getPayloadBodiesByRangeV1 which
|
||||
// allows for retrieval of a range of block bodies by the engine api.
|
||||
// GetPayloadBodiesByRangeV1 implements engine_getPayloadBodiesByRangeV1 which allows for retrieval of a range
|
||||
// of block bodies by the engine api.
|
||||
func (api *ConsensusAPI) GetPayloadBodiesByRangeV1(start, count hexutil.Uint64) ([]*engine.ExecutionPayloadBodyV1, error) {
|
||||
if start == 0 || count == 0 {
|
||||
return nil, engine.InvalidParams.With(fmt.Errorf("invalid start or count, start: %v count: %v", start, count))
|
||||
@@ -768,19 +779,23 @@ func getBody(block *types.Block) *engine.ExecutionPayloadBodyV1 {
|
||||
if block == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
body = block.Body()
|
||||
txs = make([]hexutil.Bytes, len(body.Transactions))
|
||||
withdrawals = body.Withdrawals
|
||||
)
|
||||
|
||||
for j, tx := range body.Transactions {
|
||||
data, _ := tx.MarshalBinary()
|
||||
txs[j] = hexutil.Bytes(data)
|
||||
}
|
||||
|
||||
// Post-shanghai withdrawals MUST be set to empty slice instead of nil
|
||||
if withdrawals == nil && block.Header().WithdrawalsHash != nil {
|
||||
withdrawals = make([]*types.Withdrawal, 0)
|
||||
}
|
||||
|
||||
return &engine.ExecutionPayloadBodyV1{
|
||||
TransactionData: txs,
|
||||
Withdrawals: withdrawals,
|
||||
|
||||
@@ -38,6 +38,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/txpool"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
@@ -322,7 +323,7 @@ func TestEth2NewBlock(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create the executable data %v", err)
|
||||
}
|
||||
block, err := engine.ExecutableDataToBlock(*execData)
|
||||
block, err := engine.ExecutableDataToBlock(*execData, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to convert executable data to block %v", err)
|
||||
}
|
||||
@@ -364,7 +365,7 @@ func TestEth2NewBlock(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create the executable data %v", err)
|
||||
}
|
||||
block, err := engine.ExecutableDataToBlock(*execData)
|
||||
block, err := engine.ExecutableDataToBlock(*execData, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to convert executable data to block %v", err)
|
||||
}
|
||||
@@ -996,7 +997,7 @@ func TestSimultaneousNewBlock(t *testing.T) {
|
||||
t.Fatal(testErr)
|
||||
}
|
||||
}
|
||||
block, err := engine.ExecutableDataToBlock(*execData)
|
||||
block, err := engine.ExecutableDataToBlock(*execData, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to convert executable data to block %v", err)
|
||||
}
|
||||
@@ -1519,3 +1520,38 @@ func equalBody(a *types.Body, b *engine.ExecutionPayloadBodyV1) bool {
|
||||
}
|
||||
return reflect.DeepEqual(a.Withdrawals, b.Withdrawals)
|
||||
}
|
||||
|
||||
func TestBlockToPayloadWithBlobs(t *testing.T) {
|
||||
header := types.Header{}
|
||||
var txs []*types.Transaction
|
||||
|
||||
inner := types.BlobTx{
|
||||
BlobHashes: make([]common.Hash, 1),
|
||||
}
|
||||
|
||||
txs = append(txs, types.NewTx(&inner))
|
||||
|
||||
blobs := make([]kzg4844.Blob, 1)
|
||||
commitments := make([]kzg4844.Commitment, 1)
|
||||
proofs := make([]kzg4844.Proof, 1)
|
||||
|
||||
block := types.NewBlock(&header, txs, nil, nil, trie.NewStackTrie(nil))
|
||||
envelope := engine.BlockToExecutableData(block, nil, blobs, commitments, proofs)
|
||||
var want int
|
||||
for _, tx := range txs {
|
||||
want += len(tx.BlobHashes())
|
||||
}
|
||||
if got := len(envelope.BlobsBundle.Commitments); got != want {
|
||||
t.Fatalf("invalid number of commitments: got %v, want %v", got, want)
|
||||
}
|
||||
if got := len(envelope.BlobsBundle.Proofs); got != want {
|
||||
t.Fatalf("invalid number of proofs: got %v, want %v", got, want)
|
||||
}
|
||||
if got := len(envelope.BlobsBundle.Blobs); got != want {
|
||||
t.Fatalf("invalid number of blobs: got %v, want %v", got, want)
|
||||
}
|
||||
_, err := engine.ExecutableDataToBlock(*envelope.ExecutionPayload, make([]common.Hash, 1))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal) error {
|
||||
return fmt.Errorf("error calling forkchoice update: %v", err)
|
||||
}
|
||||
|
||||
envelope, err := c.engineAPI.getFullPayload(*fcResponse.PayloadID)
|
||||
envelope, err := c.engineAPI.getPayload(*fcResponse.PayloadID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error retrieving payload: %v", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user