eth/catalyst: error on nil withdrawals post-shanghai (#26549)

This adds explicit checks for the presence of withdrawals in the engine API.

Co-authored-by: Felix Lange <fjl@twurst.com>
This commit is contained in:
Marius van der Wijden 2023-01-27 11:42:14 +01:00 committed by GitHub
parent 55f41d198c
commit 245cff0a1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 124 additions and 0 deletions

@ -164,6 +164,18 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, pa
// ForkchoiceUpdatedV2 is equivalent to V1 with the addition of withdrawals in the payload attributes.
func (api *ConsensusAPI) ForkchoiceUpdatedV2(update beacon.ForkchoiceStateV1, payloadAttributes *beacon.PayloadAttributes) (beacon.ForkChoiceResponse, error) {
if !api.eth.BlockChain().Config().IsShanghai(payloadAttributes.Timestamp) {
// Reject payload attributes with withdrawals before shanghai
if payloadAttributes != nil && payloadAttributes.Withdrawals != nil {
return beacon.STATUS_INVALID, beacon.InvalidPayloadAttributes.With(errors.New("withdrawals before shanghai"))
}
} else {
// Reject payload attributes with nil withdrawals after shanghai
if payloadAttributes != nil && payloadAttributes.Withdrawals == nil {
return beacon.STATUS_INVALID, beacon.InvalidPayloadAttributes.With(errors.New("missing withdrawals list"))
}
}
return api.forkchoiceUpdated(update, payloadAttributes)
}

@ -1113,3 +1113,115 @@ func TestWithdrawals(t *testing.T) {
}
}
}
func TestNilWithdrawals(t *testing.T) {
genesis, blocks := generateMergeChain(10, true)
// Set shanghai time to last block + 4 seconds (first post-merge block)
time := blocks[len(blocks)-1].Time() + 4
genesis.Config.ShanghaiTime = &time
n, ethservice := startEthService(t, genesis, blocks)
ethservice.Merger().ReachTTD()
defer n.Close()
api := NewConsensusAPI(ethservice)
parent := ethservice.BlockChain().CurrentHeader()
aa := common.Address{0xaa}
type test struct {
blockParams beacon.PayloadAttributes
wantErr bool
}
tests := []test{
// Before Shanghai
{
blockParams: beacon.PayloadAttributes{
Timestamp: parent.Time + 2,
Withdrawals: nil,
},
wantErr: false,
},
{
blockParams: beacon.PayloadAttributes{
Timestamp: parent.Time + 2,
Withdrawals: make([]*types.Withdrawal, 0),
},
wantErr: true,
},
{
blockParams: beacon.PayloadAttributes{
Timestamp: parent.Time + 2,
Withdrawals: []*types.Withdrawal{
{
Index: 0,
Address: aa,
Amount: 32,
},
},
},
wantErr: true,
},
// After Shanghai
{
blockParams: beacon.PayloadAttributes{
Timestamp: parent.Time + 5,
Withdrawals: nil,
},
wantErr: true,
},
{
blockParams: beacon.PayloadAttributes{
Timestamp: parent.Time + 5,
Withdrawals: make([]*types.Withdrawal, 0),
},
wantErr: false,
},
{
blockParams: beacon.PayloadAttributes{
Timestamp: parent.Time + 5,
Withdrawals: []*types.Withdrawal{
{
Index: 0,
Address: aa,
Amount: 32,
},
},
},
wantErr: false,
},
}
fcState := beacon.ForkchoiceStateV1{
HeadBlockHash: parent.Hash(),
}
for _, test := range tests {
_, err := api.ForkchoiceUpdatedV2(fcState, &test.blockParams)
if test.wantErr {
if err == nil {
t.Fatal("wanted error on fcuv2 with invalid withdrawals")
}
continue
}
if err != nil {
t.Fatalf("error preparing payload, err=%v", err)
}
// 11: verify locally build block.
payloadID := (&miner.BuildPayloadArgs{
Parent: fcState.HeadBlockHash,
Timestamp: test.blockParams.Timestamp,
FeeRecipient: test.blockParams.SuggestedFeeRecipient,
Random: test.blockParams.Random,
}).Id()
execData, err := api.GetPayloadV2(payloadID)
if err != nil {
t.Fatalf("error getting payload, err=%v", err)
}
if status, err := api.NewPayloadV2(*execData.ExecutionPayload); err != nil {
t.Fatalf("error validating payload: %v", err)
} else if status.Status != beacon.VALID {
t.Fatalf("invalid payload")
}
}
}