core/vm: implement eip-7843

This commit is contained in:
MariusVanDerWijden 2026-01-12 22:18:22 +01:00 committed by Jared Wasinger
parent f7ce5467e3
commit 3b0d27b1e7
4 changed files with 62 additions and 8 deletions

View file

@ -51,6 +51,12 @@ var (
// ExecutionPayloadV3 has the syntax of ExecutionPayloadV2 and appends the new // ExecutionPayloadV3 has the syntax of ExecutionPayloadV2 and appends the new
// fields: blobGasUsed and excessBlobGas. // fields: blobGasUsed and excessBlobGas.
PayloadV3 PayloadVersion = 0x3 PayloadV3 PayloadVersion = 0x3
// PayloadV4 is the identifier of ExecutionPayloadV3 introduced in amsterdam fork.
//
// https://github.com/ethereum/execution-apis/blob/main/src/engine/amsterdam.md#executionpayloadv4
// ExecutionPayloadV3 has the syntax of ExecutionPayloadV3 and appends the new
// field slotNumber.
PayloadV4 PayloadVersion = 0x4 PayloadV4 PayloadVersion = 0x4
) )
@ -64,11 +70,13 @@ type PayloadAttributes struct {
SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"` SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
Withdrawals []*types.Withdrawal `json:"withdrawals"` Withdrawals []*types.Withdrawal `json:"withdrawals"`
BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"` BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"`
SlotNumber *uint64 `json:"slotNumber"`
} }
// JSON type overrides for PayloadAttributes. // JSON type overrides for PayloadAttributes.
type payloadAttributesMarshaling struct { type payloadAttributesMarshaling struct {
Timestamp hexutil.Uint64 Timestamp hexutil.Uint64
SlotNumber *hexutil.Uint64
} }
//go:generate go run github.com/fjl/gencodec -type ExecutableData -field-override executableDataMarshaling -out gen_ed.go //go:generate go run github.com/fjl/gencodec -type ExecutableData -field-override executableDataMarshaling -out gen_ed.go

View file

@ -213,6 +213,28 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, pa
return api.forkchoiceUpdated(update, params, engine.PayloadV3, false) return api.forkchoiceUpdated(update, params, engine.PayloadV3, false)
} }
// ForkchoiceUpdatedV4 is equivalent to V3 with the addition of slot number
// in the payload attributes. It supports only PayloadAttributesV4.
func (api *ConsensusAPI) ForkchoiceUpdatedV4(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
if params != nil {
switch {
case params.Withdrawals == nil:
return engine.STATUS_INVALID, attributesErr("missing withdrawals")
case params.BeaconRoot == nil:
return engine.STATUS_INVALID, attributesErr("missing beacon root")
case params.SlotNumber == nil:
return engine.STATUS_INVALID, attributesErr("missing slot number")
case !api.checkFork(params.Timestamp, forks.Amsterdam):
return engine.STATUS_INVALID, unsupportedForkErr("fcuV4 must only be called for amsterdam payloads")
}
}
// TODO(matt): the spec requires that fcu is applied when called on a valid
// hash, even if params are wrong. To do this we need to split up
// forkchoiceUpdate into a function that only updates the head and then a
// function that kicks off block construction.
return api.forkchoiceUpdated(update, params, engine.PayloadV4, false)
}
func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes, payloadVersion engine.PayloadVersion, payloadWitness bool) (engine.ForkChoiceResponse, error) { func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes, payloadVersion engine.PayloadVersion, payloadWitness bool) (engine.ForkChoiceResponse, error) {
api.forkchoiceLock.Lock() api.forkchoiceLock.Lock()
defer api.forkchoiceLock.Unlock() defer api.forkchoiceLock.Unlock()
@ -346,6 +368,7 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
Random: payloadAttributes.Random, Random: payloadAttributes.Random,
Withdrawals: payloadAttributes.Withdrawals, Withdrawals: payloadAttributes.Withdrawals,
BeaconRoot: payloadAttributes.BeaconRoot, BeaconRoot: payloadAttributes.BeaconRoot,
SlotNum: payloadAttributes.SlotNumber,
Version: payloadVersion, Version: payloadVersion,
} }
id := args.Id() id := args.Id()
@ -459,15 +482,16 @@ func (api *ConsensusAPI) GetPayloadV5(payloadID engine.PayloadID) (*engine.Execu
}) })
} }
// GetPayloadV6 returns a cached payload by id. // GetPayloadV6 returns a cached payload by id. This endpoint should only
// be used after the Amsterdam fork.
func (api *ConsensusAPI) GetPayloadV6(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) { func (api *ConsensusAPI) GetPayloadV6(payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) {
if !payloadID.Is(engine.PayloadV4) { return api.getPayload(
return nil, engine.UnsupportedFork payloadID,
}
return api.getPayload(payloadID,
false, false,
[]engine.PayloadVersion{engine.PayloadV4}, []engine.PayloadVersion{engine.PayloadV4},
nil) []forks.Fork{
forks.Amsterdam,
})
} }
// getPayload will retrieve the specified payload and verify it conforms to the // getPayload will retrieve the specified payload and verify it conforms to the
@ -728,9 +752,12 @@ func (api *ConsensusAPI) NewPayloadV5(params engine.ExecutableData, versionedHas
return invalidStatus, paramsErr("nil beaconRoot post-cancun") return invalidStatus, paramsErr("nil beaconRoot post-cancun")
case executionRequests == nil: case executionRequests == nil:
return invalidStatus, paramsErr("nil executionRequests post-prague") return invalidStatus, paramsErr("nil executionRequests post-prague")
case !api.checkFork(params.Timestamp, forks.Prague, forks.Osaka, forks.Amsterdam):
case params.BlockAccessList == nil: case params.BlockAccessList == nil:
return invalidStatus, paramsErr("nil block access list post-amsterdam") return invalidStatus, paramsErr("nil block access list post-amsterdam")
case !api.checkFork(params.Timestamp, forks.Prague, forks.Osaka, forks.Amsterdam): case params.SlotNumber == nil:
return invalidStatus, paramsErr("nil slotnumber post-amsterdam")
case !api.checkFork(params.Timestamp, forks.Amsterdam):
return invalidStatus, unsupportedForkErr("newPayloadV5 must only be called for amsterdam payloads") return invalidStatus, unsupportedForkErr("newPayloadV5 must only be called for amsterdam payloads")
} }
requests := convertRequests(executionRequests) requests := convertRequests(executionRequests)
@ -768,6 +795,10 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
if params.ExcessBlobGas != nil { if params.ExcessBlobGas != nil {
ebg = strconv.Itoa(int(*params.ExcessBlobGas)) ebg = strconv.Itoa(int(*params.ExcessBlobGas))
} }
slotnum := "nil"
if params.SlotNumber != nil {
ebg = strconv.Itoa(int(*params.SlotNumber))
}
log.Warn("Invalid NewPayload params", log.Warn("Invalid NewPayload params",
"params.Number", params.Number, "params.Number", params.Number,
"params.ParentHash", params.ParentHash, "params.ParentHash", params.ParentHash,
@ -783,6 +814,7 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
"params.BaseFeePerGas", params.BaseFeePerGas, "params.BaseFeePerGas", params.BaseFeePerGas,
"params.BlobGasUsed", bgu, "params.BlobGasUsed", bgu,
"params.ExcessBlobGas", ebg, "params.ExcessBlobGas", ebg,
"params.SlotNumber", slotnum,
"len(params.Transactions)", len(params.Transactions), "len(params.Transactions)", len(params.Transactions),
"len(params.Withdrawals)", len(params.Withdrawals), "len(params.Withdrawals)", len(params.Withdrawals),
"beaconRoot", beaconRoot, "beaconRoot", beaconRoot,

View file

@ -43,6 +43,7 @@ type BuildPayloadArgs struct {
Random common.Hash // The provided randomness value Random common.Hash // The provided randomness value
Withdrawals types.Withdrawals // The provided withdrawals Withdrawals types.Withdrawals // The provided withdrawals
BeaconRoot *common.Hash // The provided beaconRoot (Cancun) BeaconRoot *common.Hash // The provided beaconRoot (Cancun)
SlotNum *uint64 // The provided slotNumber
Version engine.PayloadVersion // Versioning byte for payload id calculation. Version engine.PayloadVersion // Versioning byte for payload id calculation.
} }
@ -57,6 +58,9 @@ func (args *BuildPayloadArgs) Id() engine.PayloadID {
if args.BeaconRoot != nil { if args.BeaconRoot != nil {
hasher.Write(args.BeaconRoot[:]) hasher.Write(args.BeaconRoot[:])
} }
if args.SlotNum != nil {
binary.Write(hasher, binary.BigEndian, args.SlotNum)
}
var out engine.PayloadID var out engine.PayloadID
copy(out[:], hasher.Sum(nil)[:8]) copy(out[:], hasher.Sum(nil)[:8])
out[0] = byte(args.Version) out[0] = byte(args.Version)
@ -218,6 +222,7 @@ func (miner *Miner) buildPayload(args *BuildPayloadArgs, witness bool) (*Payload
random: args.Random, random: args.Random,
withdrawals: args.Withdrawals, withdrawals: args.Withdrawals,
beaconRoot: args.BeaconRoot, beaconRoot: args.BeaconRoot,
slotNum: args.SlotNum,
noTxs: true, noTxs: true,
} }
empty := miner.generateWork(emptyParams, witness) empty := miner.generateWork(emptyParams, witness)
@ -248,6 +253,7 @@ func (miner *Miner) buildPayload(args *BuildPayloadArgs, witness bool) (*Payload
random: args.Random, random: args.Random,
withdrawals: args.Withdrawals, withdrawals: args.Withdrawals,
beaconRoot: args.BeaconRoot, beaconRoot: args.BeaconRoot,
slotNum: args.SlotNum,
noTxs: false, noTxs: false,
} }

View file

@ -113,6 +113,7 @@ type generateParams struct {
random common.Hash // The randomness generated by beacon chain, empty before the merge random common.Hash // The randomness generated by beacon chain, empty before the merge
withdrawals types.Withdrawals // List of withdrawals to include in block (shanghai field) withdrawals types.Withdrawals // List of withdrawals to include in block (shanghai field)
beaconRoot *common.Hash // The beacon root (cancun field). beaconRoot *common.Hash // The beacon root (cancun field).
slotNum *uint64 // The slot number (amsterdam field).
noTxs bool // Flag whether an empty block without any transaction is expected noTxs bool // Flag whether an empty block without any transaction is expected
} }
@ -271,6 +272,13 @@ func (miner *Miner) prepareWork(genParams *generateParams, witness bool) (*envir
header.ExcessBlobGas = &excessBlobGas header.ExcessBlobGas = &excessBlobGas
header.ParentBeaconRoot = genParams.beaconRoot header.ParentBeaconRoot = genParams.beaconRoot
} }
// Apply EIP-7843.
if miner.chainConfig.IsAmsterdam(header.Number, header.Time) {
if genParams.slotNum == nil {
return nil, errors.New("no slot number set post-amsterdam")
}
header.SlotNumber = genParams.slotNum
}
// Could potentially happen if starting to mine in an odd state. // Could potentially happen if starting to mine in an odd state.
// Note genParams.coinbase can be different with header.Coinbase // Note genParams.coinbase can be different with header.Coinbase
// since clique algorithm can modify the coinbase field in header. // since clique algorithm can modify the coinbase field in header.