mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-23 11:57:18 +00:00
a9b003e1fe
* HTTP Beacon API: `/eth/v1/validator/contribution_and_proofs` * add comment to invalid test case * fix validation and test * review --------- Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
211 lines
8.0 KiB
Go
211 lines
8.0 KiB
Go
package builder
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
"github.com/gorilla/mux"
|
|
mock "github.com/prysmaticlabs/prysm/v4/beacon-chain/blockchain/testing"
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/testutil"
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/state"
|
|
"github.com/prysmaticlabs/prysm/v4/config/params"
|
|
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
|
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
|
|
http2 "github.com/prysmaticlabs/prysm/v4/network/http"
|
|
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
|
"github.com/prysmaticlabs/prysm/v4/testing/assert"
|
|
"github.com/prysmaticlabs/prysm/v4/testing/require"
|
|
"github.com/prysmaticlabs/prysm/v4/testing/util"
|
|
"github.com/prysmaticlabs/prysm/v4/time/slots"
|
|
)
|
|
|
|
func TestExpectedWithdrawals_BadRequest(t *testing.T) {
|
|
st, err := util.NewBeaconStateCapella()
|
|
slotsAhead := 5000
|
|
require.NoError(t, err)
|
|
capellaSlot, err := slots.EpochStart(params.BeaconConfig().CapellaForkEpoch)
|
|
require.NoError(t, err)
|
|
currentSlot := capellaSlot + primitives.Slot(slotsAhead)
|
|
require.NoError(t, st.SetSlot(currentSlot))
|
|
mockChainService := &mock.ChainService{Optimistic: true}
|
|
|
|
testCases := []struct {
|
|
name string
|
|
path string
|
|
urlParams map[string]string
|
|
state state.BeaconState
|
|
errorMessage string
|
|
}{
|
|
{
|
|
name: "no state_id url params",
|
|
path: "/eth/v1/builder/states/{state_id}/expected_withdrawals?proposal_slot" +
|
|
strconv.FormatUint(uint64(currentSlot), 10),
|
|
urlParams: map[string]string{},
|
|
state: nil,
|
|
errorMessage: "state_id is required in URL params",
|
|
},
|
|
{
|
|
name: "invalid proposal slot value",
|
|
path: "/eth/v1/builder/states/{state_id}/expected_withdrawals?proposal_slot=aaa",
|
|
urlParams: map[string]string{"state_id": "head"},
|
|
state: st,
|
|
errorMessage: "invalid proposal slot value",
|
|
},
|
|
{
|
|
name: "proposal slot < Capella start slot",
|
|
path: "/eth/v1/builder/states/{state_id}/expected_withdrawals?proposal_slot=" +
|
|
strconv.FormatUint(uint64(capellaSlot)-1, 10),
|
|
urlParams: map[string]string{"state_id": "head"},
|
|
state: st,
|
|
errorMessage: "expected withdrawals are not supported before Capella fork",
|
|
},
|
|
{
|
|
name: "proposal slot == Capella start slot",
|
|
path: "/eth/v1/builder/states/{state_id}/expected_withdrawals?proposal_slot=" +
|
|
strconv.FormatUint(uint64(capellaSlot), 10),
|
|
urlParams: map[string]string{"state_id": "head"},
|
|
state: st,
|
|
errorMessage: "proposal slot must be bigger than state slot",
|
|
},
|
|
{
|
|
name: "Proposal slot >= 128 slots ahead of state slot",
|
|
path: "/eth/v1/builder/states/{state_id}/expected_withdrawals?proposal_slot=" +
|
|
strconv.FormatUint(uint64(currentSlot+128), 10),
|
|
urlParams: map[string]string{"state_id": "head"},
|
|
state: st,
|
|
errorMessage: "proposal slot cannot be >= 128 slots ahead of state slot",
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
s := &Server{
|
|
FinalizationFetcher: mockChainService,
|
|
OptimisticModeFetcher: mockChainService,
|
|
Stater: &testutil.MockStater{BeaconState: testCase.state},
|
|
}
|
|
request := httptest.NewRequest("GET", testCase.path, nil)
|
|
request = mux.SetURLVars(request, testCase.urlParams)
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.ExpectedWithdrawals(writer, request)
|
|
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
|
e := &http2.DefaultErrorJson{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
|
|
assert.Equal(t, http.StatusBadRequest, e.Code)
|
|
assert.StringContains(t, testCase.errorMessage, e.Message)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestExpectedWithdrawals(t *testing.T) {
|
|
st, err := util.NewBeaconStateCapella()
|
|
slotsAhead := 5000
|
|
require.NoError(t, err)
|
|
capellaSlot, err := slots.EpochStart(params.BeaconConfig().CapellaForkEpoch)
|
|
require.NoError(t, err)
|
|
currentSlot := capellaSlot + primitives.Slot(slotsAhead)
|
|
require.NoError(t, st.SetSlot(currentSlot))
|
|
mockChainService := &mock.ChainService{Optimistic: true}
|
|
|
|
t.Run("get correct expected withdrawals", func(t *testing.T) {
|
|
params.SetupTestConfigCleanup(t)
|
|
cfg := params.BeaconConfig().Copy()
|
|
cfg.MaxValidatorsPerWithdrawalsSweep = 16
|
|
params.OverrideBeaconConfig(cfg)
|
|
|
|
// Update state with updated validator fields
|
|
valCount := 17
|
|
validators := make([]*eth.Validator, 0, valCount)
|
|
balances := make([]uint64, 0, valCount)
|
|
for i := 0; i < valCount; i++ {
|
|
blsKey, err := bls.RandKey()
|
|
require.NoError(t, err)
|
|
val := ð.Validator{
|
|
PublicKey: blsKey.PublicKey().Marshal(),
|
|
WithdrawalCredentials: make([]byte, 32),
|
|
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
|
|
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
|
}
|
|
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
|
|
validators = append(validators, val)
|
|
balances = append(balances, params.BeaconConfig().MaxEffectiveBalance)
|
|
}
|
|
|
|
epoch := slots.ToEpoch(st.Slot())
|
|
// Fully withdrawable now with more than 0 balance
|
|
validators[5].WithdrawableEpoch = epoch
|
|
// Fully withdrawable now but 0 balance
|
|
validators[10].WithdrawableEpoch = epoch
|
|
balances[10] = 0
|
|
// Partially withdrawable now but fully withdrawable after 1 epoch
|
|
validators[14].WithdrawableEpoch = epoch + 1
|
|
balances[14] += params.BeaconConfig().MinDepositAmount
|
|
// Partially withdrawable
|
|
validators[15].WithdrawableEpoch = epoch + 2
|
|
balances[15] += params.BeaconConfig().MinDepositAmount
|
|
// Above sweep bound
|
|
validators[16].WithdrawableEpoch = epoch + 1
|
|
balances[16] += params.BeaconConfig().MinDepositAmount
|
|
|
|
require.NoError(t, st.SetValidators(validators))
|
|
require.NoError(t, st.SetBalances(balances))
|
|
inactivityScores := make([]uint64, valCount)
|
|
for i := range inactivityScores {
|
|
inactivityScores[i] = 10
|
|
}
|
|
require.NoError(t, st.SetInactivityScores(inactivityScores))
|
|
|
|
s := &Server{
|
|
FinalizationFetcher: mockChainService,
|
|
OptimisticModeFetcher: mockChainService,
|
|
Stater: &testutil.MockStater{BeaconState: st},
|
|
}
|
|
request := httptest.NewRequest(
|
|
"GET", "/eth/v1/builder/states/{state_id}/expected_withdrawals?proposal_slot="+
|
|
strconv.FormatUint(uint64(currentSlot+params.BeaconConfig().SlotsPerEpoch), 10), nil)
|
|
request = mux.SetURLVars(request, map[string]string{"state_id": "head"})
|
|
writer := httptest.NewRecorder()
|
|
writer.Body = &bytes.Buffer{}
|
|
|
|
s.ExpectedWithdrawals(writer, request)
|
|
assert.Equal(t, http.StatusOK, writer.Code)
|
|
resp := &ExpectedWithdrawalsResponse{}
|
|
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
|
assert.Equal(t, true, resp.ExecutionOptimistic)
|
|
assert.Equal(t, false, resp.Finalized)
|
|
assert.Equal(t, 3, len(resp.Data))
|
|
expectedWithdrawal1 := &ExpectedWithdrawal{
|
|
Index: strconv.FormatUint(0, 10),
|
|
ValidatorIndex: strconv.FormatUint(5, 10),
|
|
Address: hexutil.Encode(validators[5].WithdrawalCredentials[12:]),
|
|
// Decreased due to epoch processing when state advanced forward
|
|
Amount: strconv.FormatUint(31998257885, 10),
|
|
}
|
|
expectedWithdrawal2 := &ExpectedWithdrawal{
|
|
Index: strconv.FormatUint(1, 10),
|
|
ValidatorIndex: strconv.FormatUint(14, 10),
|
|
Address: hexutil.Encode(validators[14].WithdrawalCredentials[12:]),
|
|
// MaxEffectiveBalance + MinDepositAmount + decrease after epoch processing
|
|
Amount: strconv.FormatUint(32998257885, 10),
|
|
}
|
|
expectedWithdrawal3 := &ExpectedWithdrawal{
|
|
Index: strconv.FormatUint(2, 10),
|
|
ValidatorIndex: strconv.FormatUint(15, 10),
|
|
Address: hexutil.Encode(validators[15].WithdrawalCredentials[12:]),
|
|
// MinDepositAmount + decrease after epoch processing
|
|
Amount: strconv.FormatUint(998257885, 10),
|
|
}
|
|
require.DeepEqual(t, expectedWithdrawal1, resp.Data[0])
|
|
require.DeepEqual(t, expectedWithdrawal2, resp.Data[1])
|
|
require.DeepEqual(t, expectedWithdrawal3, resp.Data[2])
|
|
})
|
|
}
|