mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
eth/catalyst: add timestamp checks to fcu and new payload and improve param checks
This commit is contained in:
parent
f55a10b64d
commit
c2e350bfda
2 changed files with 74 additions and 48 deletions
|
|
@ -184,47 +184,37 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update engine.ForkchoiceStateV1, pa
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForkchoiceUpdatedV2 is equivalent to V1 with the addition of withdrawals in the payload attributes.
|
// ForkchoiceUpdatedV2 is equivalent to V1 with the addition of withdrawals in the payload attributes.
|
||||||
func (api *ConsensusAPI) ForkchoiceUpdatedV2(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
func (api *ConsensusAPI) ForkchoiceUpdatedV2(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||||
if payloadAttributes != nil {
|
if params != nil {
|
||||||
if err := api.verifyPayloadAttributes(payloadAttributes); err != nil {
|
if params.Withdrawals == nil {
|
||||||
return engine.STATUS_INVALID, engine.InvalidParams.With(err)
|
return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("missing withdrawals"))
|
||||||
|
}
|
||||||
|
if params.BeaconRoot != nil {
|
||||||
|
return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("missing beacon root"))
|
||||||
|
}
|
||||||
|
c := api.eth.BlockChain().Config()
|
||||||
|
if !active(c.IsShanghai, c.IsCancun, c.LondonBlock, params.Timestamp) {
|
||||||
|
return engine.STATUS_INVALID, engine.UnsupportedFork.With(errors.New("forkchoiceUpdatedV2 must only be called for shanghai payloads"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return api.forkchoiceUpdated(update, payloadAttributes)
|
return api.forkchoiceUpdated(update, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForkchoiceUpdatedV3 is equivalent to V2 with the addition of parent beacon block root in the payload attributes.
|
// ForkchoiceUpdatedV3 is equivalent to V2 with the addition of parent beacon block root in the payload attributes.
|
||||||
func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
func (api *ConsensusAPI) ForkchoiceUpdatedV3(update engine.ForkchoiceStateV1, params *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||||
if payloadAttributes != nil {
|
if params != nil {
|
||||||
if err := api.verifyPayloadAttributes(payloadAttributes); err != nil {
|
if params.Withdrawals == nil {
|
||||||
return engine.STATUS_INVALID, engine.InvalidParams.With(err)
|
return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("missing withdrawals"))
|
||||||
}
|
}
|
||||||
|
if params.BeaconRoot == nil {
|
||||||
|
return engine.STATUS_INVALID, engine.InvalidParams.With(errors.New("missing beacon root"))
|
||||||
}
|
}
|
||||||
return api.forkchoiceUpdated(update, payloadAttributes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *ConsensusAPI) verifyPayloadAttributes(attr *engine.PayloadAttributes) error {
|
|
||||||
c := api.eth.BlockChain().Config()
|
c := api.eth.BlockChain().Config()
|
||||||
|
if !active(c.IsCancun, c.IsPrague, c.LondonBlock, params.Timestamp) {
|
||||||
// Verify withdrawals attribute for Shanghai.
|
return engine.STATUS_INVALID, engine.UnsupportedFork.With(errors.New("forkchoiceUpdatedV3 must only be called for shanghai payloads"))
|
||||||
if err := checkAttribute(c.IsShanghai, attr.Withdrawals != nil, c.LondonBlock, attr.Timestamp); err != nil {
|
|
||||||
return fmt.Errorf("invalid withdrawals: %w", err)
|
|
||||||
}
|
}
|
||||||
// Verify beacon root attribute for Cancun.
|
|
||||||
if err := checkAttribute(c.IsCancun, attr.BeaconRoot != nil, c.LondonBlock, attr.Timestamp); err != nil {
|
|
||||||
return fmt.Errorf("invalid parent beacon block root: %w", err)
|
|
||||||
}
|
}
|
||||||
return nil
|
return api.forkchoiceUpdated(update, params)
|
||||||
}
|
|
||||||
|
|
||||||
func checkAttribute(active func(*big.Int, uint64) bool, exists bool, block *big.Int, time uint64) error {
|
|
||||||
if active(block, time) && !exists {
|
|
||||||
return errors.New("fork active, missing expected attribute")
|
|
||||||
}
|
|
||||||
if !active(block, time) && exists {
|
|
||||||
return errors.New("fork inactive, unexpected attribute set")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payloadAttributes *engine.PayloadAttributes) (engine.ForkChoiceResponse, error) {
|
||||||
|
|
@ -457,27 +447,36 @@ func (api *ConsensusAPI) NewPayloadV1(params engine.ExecutableData) (engine.Payl
|
||||||
|
|
||||||
// NewPayloadV2 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
// NewPayloadV2 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
||||||
func (api *ConsensusAPI) NewPayloadV2(params engine.ExecutableData) (engine.PayloadStatusV1, error) {
|
func (api *ConsensusAPI) NewPayloadV2(params engine.ExecutableData) (engine.PayloadStatusV1, error) {
|
||||||
if api.eth.BlockChain().Config().IsShanghai(new(big.Int).SetUint64(params.Number), params.Timestamp) {
|
|
||||||
if params.Withdrawals == nil {
|
if params.Withdrawals == nil {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
||||||
}
|
}
|
||||||
} else if params.Withdrawals != nil {
|
if params.ExcessBlobGas != nil {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil withdrawals pre-shanghai"))
|
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("non-nil excessBlobGas pre-cancun"))
|
||||||
}
|
}
|
||||||
if api.eth.BlockChain().Config().IsCancun(new(big.Int).SetUint64(params.Number), params.Timestamp) {
|
if params.BlobGasUsed != nil {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("newPayloadV2 called post-cancun"))
|
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil params.BlobGasUsed pre-cancun"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c := api.eth.BlockChain().Config()
|
||||||
|
if !active(c.IsShanghai, c.IsCancun, c.LondonBlock, params.Timestamp) {
|
||||||
|
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadV2 must only be called for shanghai payloads"))
|
||||||
|
}
|
||||||
|
|
||||||
return api.newPayload(params, nil, nil)
|
return api.newPayload(params, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPayloadV3 creates an Eth1 block, inserts it in the chain, and returns the status of the chain.
|
// 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, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) {
|
func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) {
|
||||||
|
if params.Withdrawals == nil {
|
||||||
|
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil withdrawals post-shanghai"))
|
||||||
|
}
|
||||||
if params.ExcessBlobGas == nil {
|
if params.ExcessBlobGas == nil {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil excessBlobGas post-cancun"))
|
||||||
}
|
}
|
||||||
if params.BlobGasUsed == nil {
|
if params.BlobGasUsed == nil {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil params.BlobGasUsed post-cancun"))
|
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil params.BlobGasUsed post-cancun"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if versionedHashes == nil {
|
if versionedHashes == nil {
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
|
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil versionedHashes post-cancun"))
|
||||||
}
|
}
|
||||||
|
|
@ -485,13 +484,25 @@ func (api *ConsensusAPI) NewPayloadV3(params engine.ExecutableData, versionedHas
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil parentBeaconBlockRoot post-cancun"))
|
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.InvalidParams.With(errors.New("nil parentBeaconBlockRoot post-cancun"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !api.eth.BlockChain().Config().IsCancun(new(big.Int).SetUint64(params.Number), params.Timestamp) {
|
c := api.eth.BlockChain().Config()
|
||||||
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadV3 called pre-cancun"))
|
if !active(c.IsCancun, c.IsPrague, c.LondonBlock, params.Timestamp) {
|
||||||
|
return engine.PayloadStatusV1{Status: engine.INVALID}, engine.UnsupportedFork.With(errors.New("newPayloadV3 must only be called for cancun payloads"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return api.newPayload(params, versionedHashes, beaconRoot)
|
return api.newPayload(params, versionedHashes, beaconRoot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// active returns true only if current returns true and next returns false. It returns false otherwise.
|
||||||
|
func active(current, next func(*big.Int, uint64) bool, london *big.Int, time uint64) bool {
|
||||||
|
if !current(london, time) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if next(london, time) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) {
|
func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (engine.PayloadStatusV1, error) {
|
||||||
// The locking here is, strictly, not required. Without these locks, this can happen:
|
// The locking here is, strictly, not required. Without these locks, this can happen:
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -1237,7 +1237,15 @@ func TestNilWithdrawals(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
_, err := api.ForkchoiceUpdatedV2(fcState, &test.blockParams)
|
var (
|
||||||
|
err error
|
||||||
|
shanghai = genesis.Config.IsShanghai(genesis.Config.LondonBlock, test.blockParams.Timestamp)
|
||||||
|
)
|
||||||
|
if !shanghai {
|
||||||
|
_, err = api.ForkchoiceUpdatedV1(fcState, &test.blockParams)
|
||||||
|
} else {
|
||||||
|
_, err = api.ForkchoiceUpdatedV2(fcState, &test.blockParams)
|
||||||
|
}
|
||||||
if test.wantErr {
|
if test.wantErr {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("wanted error on fcuv2 with invalid withdrawals")
|
t.Fatal("wanted error on fcuv2 with invalid withdrawals")
|
||||||
|
|
@ -1254,14 +1262,21 @@ func TestNilWithdrawals(t *testing.T) {
|
||||||
Timestamp: test.blockParams.Timestamp,
|
Timestamp: test.blockParams.Timestamp,
|
||||||
FeeRecipient: test.blockParams.SuggestedFeeRecipient,
|
FeeRecipient: test.blockParams.SuggestedFeeRecipient,
|
||||||
Random: test.blockParams.Random,
|
Random: test.blockParams.Random,
|
||||||
BeaconRoot: test.blockParams.BeaconRoot,
|
|
||||||
}).Id()
|
}).Id()
|
||||||
execData, err := api.GetPayloadV2(payloadID)
|
execData, err := api.GetPayloadV2(payloadID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error getting payload, err=%v", err)
|
t.Fatalf("error getting payload, err=%v", err)
|
||||||
}
|
}
|
||||||
if status, err := api.NewPayloadV2(*execData.ExecutionPayload); err != nil {
|
var (
|
||||||
t.Fatalf("error validating payload: %v", err)
|
status engine.PayloadStatusV1
|
||||||
|
)
|
||||||
|
if !shanghai {
|
||||||
|
status, err = api.NewPayloadV1(*execData.ExecutionPayload)
|
||||||
|
} else {
|
||||||
|
status, err = api.NewPayloadV2(*execData.ExecutionPayload)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error validating payload: %v", err.(*engine.EngineAPIError).ErrorData())
|
||||||
} else if status.Status != engine.VALID {
|
} else if status.Status != engine.VALID {
|
||||||
t.Fatalf("invalid payload")
|
t.Fatalf("invalid payload")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue