diff --git a/beacon/engine/gen_epe.go b/beacon/engine/gen_epe.go index 8f51533f29..deada06166 100644 --- a/beacon/engine/gen_epe.go +++ b/beacon/engine/gen_epe.go @@ -26,7 +26,12 @@ func (e ExecutionPayloadEnvelope) MarshalJSON() ([]byte, error) { enc.ExecutionPayload = e.ExecutionPayload enc.BlockValue = (*hexutil.Big)(e.BlockValue) enc.BlobsBundle = e.BlobsBundle - enc.Requests = e.Requests + if e.Requests != nil { + enc.Requests = make([]hexutil.Bytes, len(e.Requests)) + for k, v := range e.Requests { + enc.Requests[k] = v + } + } enc.Override = e.Override enc.Witness = e.Witness return json.Marshal(&enc) @@ -58,7 +63,10 @@ func (e *ExecutionPayloadEnvelope) UnmarshalJSON(input []byte) error { e.BlobsBundle = dec.BlobsBundle } if dec.Requests != nil { - e.Requests = dec.Requests + e.Requests = make([][]byte, len(dec.Requests)) + for k, v := range dec.Requests { + e.Requests[k] = v + } } if dec.Override != nil { e.Override = *dec.Override diff --git a/beacon/engine/types.go b/beacon/engine/types.go index 8e3e26aa8c..34365ecfa8 100644 --- a/beacon/engine/types.go +++ b/beacon/engine/types.go @@ -107,7 +107,7 @@ type ExecutionPayloadEnvelope struct { ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"` BlockValue *big.Int `json:"blockValue" gencodec:"required"` BlobsBundle *BlobsBundleV1 `json:"blobsBundle"` - Requests []hexutil.Bytes `json:"executionRequests"` + Requests [][]byte `json:"executionRequests"` Override bool `json:"shouldOverrideBuilder"` Witness *hexutil.Bytes `json:"witness,omitempty"` } @@ -121,6 +121,7 @@ type BlobsBundleV1 struct { // JSON type overrides for ExecutionPayloadEnvelope. type executionPayloadEnvelopeMarshaling struct { BlockValue *hexutil.Big + Requests []hexutil.Bytes } type PayloadStatusV1 struct { @@ -207,7 +208,7 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { // and that the blockhash of the constructed block matches the parameters. Nil // Withdrawals value will propagate through the returned block. Empty // Withdrawals value must be passed via non-nil, length 0 value in data. -func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests []hexutil.Bytes) (*types.Block, error) { +func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte) (*types.Block, error) { block, err := ExecutableDataToBlockNoHash(data, versionedHashes, beaconRoot, requests) if err != nil { return nil, err @@ -221,7 +222,7 @@ func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, b // ExecutableDataToBlockNoHash is analogous to ExecutableDataToBlock, but is used // for stateless execution, so it skips checking if the executable data hashes to // the requested hash (stateless has to *compute* the root hash, it's not given). -func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests []hexutil.Bytes) (*types.Block, error) { +func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte) (*types.Block, error) { txs, err := decodeTransactions(data.Transactions) if err != nil { return nil, err @@ -338,9 +339,9 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types. } // Remove type byte in requests. - var plainRequests []hexutil.Bytes + var plainRequests [][]byte if requests != nil { - plainRequests = make([]hexutil.Bytes, len(requests)) + plainRequests = make([][]byte, len(requests)) for i, reqdata := range requests { plainRequests[i] = reqdata[1:] } diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go index 2c0b65b821..85a24ff47d 100644 --- a/eth/catalyst/api.go +++ b/eth/catalyst/api.go @@ -23,6 +23,7 @@ import ( "strconv" "sync" "time" + "unsafe" "github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/common" @@ -593,8 +594,7 @@ func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHas } // NewPayloadV4 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. -// NewPayloadV4 creates an Eth1 block, inserts it in the chain, and returns the status of the chain. -func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests []hexutil.Bytes) (engine.PayloadStatusV1, error) { +func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes) (engine.PayloadStatusV1, error) { if params.Withdrawals == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) } @@ -611,13 +611,14 @@ func (api *ConsensusAPI) NewPayloadV4(params engine.ExecutableData, versionedHas if beaconRoot == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun")) } - if requests == nil { + if executionRequests == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequests post-prague")) } if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadV4 must only be called for prague payloads")) } + requests := convertRequests(executionRequests) return api.newPayload(params, versionedHashes, beaconRoot, requests, false) } @@ -682,7 +683,7 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV3(params engine.ExecutableData, v // NewPayloadWithWitnessV4 is analogous to NewPayloadV4, only it also generates // and returns a stateless witness after running the payload. -func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests []hexutil.Bytes) (engine.PayloadStatusV1, error) { +func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes) (engine.PayloadStatusV1, error) { if params.Withdrawals == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) } @@ -699,13 +700,14 @@ func (api *ConsensusAPI) NewPayloadWithWitnessV4(params engine.ExecutableData, v if beaconRoot == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun")) } - if requests == nil { + if executionRequests == nil { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequests post-prague")) } if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague { return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadWithWitnessV4 must only be called for prague payloads")) } + requests := convertRequests(executionRequests) return api.newPayload(params, versionedHashes, beaconRoot, requests, true) } @@ -770,7 +772,7 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV3(params engine.ExecutableData, // ExecuteStatelessPayloadV4 is analogous to NewPayloadV4, only it operates in // a stateless mode on top of a provided witness instead of the local database. -func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests []hexutil.Bytes, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { +func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, executionRequests []hexutil.Bytes, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { if params.Withdrawals == nil { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai")) } @@ -787,17 +789,18 @@ func (api *ConsensusAPI) ExecuteStatelessPayloadV4(params engine.ExecutableData, if beaconRoot == nil { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil beaconRoot post-cancun")) } - if requests == nil { + if executionRequests == nil { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil executionRequests post-prague")) } if api.eth.BlockChain().Config().LatestFork(params.Timestamp) != forks.Prague { return engine.StatelessPayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("executeStatelessPayloadV4 must only be called for prague payloads")) } + requests := convertRequests(executionRequests) return api.executeStatelessPayload(params, versionedHashes, beaconRoot, requests, opaqueWitness) } -func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests []hexutil.Bytes, witness bool) (engine.PayloadStatusV1, error) { +func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte, witness bool) (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 @@ -815,7 +818,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe defer api.newPayloadLock.Unlock() log.Trace("Engine API request received", "method", "NewPayload", "number", params.Number, "hash", params.BlockHash) - block, err := engine.ExecutableDataToBlock(params, versionedHashes, beaconRoot, convertRequests(requests)) + block, err := engine.ExecutableDataToBlock(params, versionedHashes, beaconRoot, requests) if err != nil { bgu := "nil" if params.BlobGasUsed != nil { @@ -927,9 +930,9 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe return engine.PayloadStatusV1{Status: engine.VALID, Witness: ow, LatestValidHash: &hash}, nil } -func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests []hexutil.Bytes, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { +func (api *ConsensusAPI) executeStatelessPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte, opaqueWitness hexutil.Bytes) (engine.StatelessPayloadStatusV1, error) { log.Trace("Engine API request received", "method", "ExecuteStatelessPayload", "number", params.Number, "hash", params.BlockHash) - block, err := engine.ExecutableDataToBlockNoHash(params, versionedHashes, beaconRoot, convertRequests(requests)) + block, err := engine.ExecutableDataToBlockNoHash(params, versionedHashes, beaconRoot, requests) if err != nil { bgu := "nil" if params.BlobGasUsed != nil { @@ -1254,12 +1257,10 @@ func getBody(block *types.Block) *engine.ExecutionPayloadBody { return &result } -func convertRequests(hex []hexutil.Bytes) (requests [][]byte) { - if hex != nil { - requests = make([][]byte, len(hex)) - for i, r := range hex { - hex[i] = r - } +// convertRequests converts a hex requests slice to plain [][]byte. +func convertRequests(hex []hexutil.Bytes) [][]byte { + if hex == nil { + return nil } - return requests + return unsafe.Slice((*[]byte)(unsafe.SliceData(hex)), len(hex)) } diff --git a/eth/catalyst/simulated_beacon.go b/eth/catalyst/simulated_beacon.go index 9f14f3f949..db46afc30d 100644 --- a/eth/catalyst/simulated_beacon.go +++ b/eth/catalyst/simulated_beacon.go @@ -220,7 +220,8 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal, timestamp u } } // Mark the payload as canon - if _, err = c.engineAPI.NewPayloadV4(*payload, blobHashes, &common.Hash{}, envelope.Requests); err != nil { + _, err = c.engineAPI.newPayload(*payload, blobHashes, &common.Hash{}, envelope.Requests, false) + if err != nil { return err } c.setCurrentState(payload.BlockHash, finalizedHash)