mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2025-01-17 15:28:45 +00:00
1058 lines
34 KiB
Go
1058 lines
34 KiB
Go
package validator
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
types "github.com/prysmaticlabs/eth2-types"
|
|
"github.com/prysmaticlabs/go-bitfield"
|
|
mockChain "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
|
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
|
dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/operations/voluntaryexits"
|
|
mockp2p "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
|
|
p2pMock "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
|
|
mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing"
|
|
v1alpha1validator "github.com/prysmaticlabs/prysm/beacon-chain/rpc/prysm/v1alpha1/validator"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
|
|
mockSync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync/testing"
|
|
v1 "github.com/prysmaticlabs/prysm/proto/eth/v1"
|
|
"github.com/prysmaticlabs/prysm/proto/migration"
|
|
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
|
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
|
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
|
"github.com/prysmaticlabs/prysm/shared/params"
|
|
"github.com/prysmaticlabs/prysm/shared/testutil"
|
|
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
|
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
func TestGetAttesterDuties(t *testing.T) {
|
|
ctx := context.Background()
|
|
genesis := testutil.NewBeaconBlock()
|
|
depChainStart := params.BeaconConfig().MinGenesisActiveValidatorCount
|
|
deposits, _, err := testutil.DeterministicDepositsAndKeys(depChainStart)
|
|
require.NoError(t, err)
|
|
eth1Data, err := testutil.DeterministicEth1Data(len(deposits))
|
|
require.NoError(t, err)
|
|
bs, err := state.GenesisBeaconState(context.Background(), deposits, 0, eth1Data)
|
|
require.NoError(t, err, "Could not set up genesis state")
|
|
// Set state to non-epoch start slot.
|
|
require.NoError(t, bs.SetSlot(5))
|
|
genesisRoot, err := genesis.Block.HashTreeRoot()
|
|
require.NoError(t, err, "Could not get signing root")
|
|
roots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
|
|
roots[0] = genesisRoot[:]
|
|
require.NoError(t, bs.SetBlockRoots(roots))
|
|
|
|
pubKeys := make([][]byte, len(deposits))
|
|
for i := 0; i < len(deposits); i++ {
|
|
pubKeys[i] = deposits[i].Data.PublicKey
|
|
}
|
|
|
|
chainSlot := types.Slot(0)
|
|
chain := &mockChain.ChainService{
|
|
State: bs, Root: genesisRoot[:], Slot: &chainSlot,
|
|
}
|
|
vs := &Server{
|
|
HeadFetcher: chain,
|
|
TimeFetcher: chain,
|
|
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
|
}
|
|
|
|
t.Run("Single validator", func(t *testing.T) {
|
|
req := &v1.AttesterDutiesRequest{
|
|
Epoch: 0,
|
|
Index: []types.ValidatorIndex{0},
|
|
}
|
|
resp, err := vs.GetAttesterDuties(ctx, req)
|
|
require.NoError(t, err)
|
|
assert.DeepEqual(t, genesisRoot[:], resp.DependentRoot)
|
|
require.Equal(t, 1, len(resp.Data))
|
|
duty := resp.Data[0]
|
|
assert.Equal(t, types.CommitteeIndex(2), duty.CommitteeIndex)
|
|
assert.Equal(t, types.Slot(7), duty.Slot)
|
|
assert.Equal(t, types.ValidatorIndex(0), duty.ValidatorIndex)
|
|
assert.DeepEqual(t, pubKeys[0], duty.Pubkey)
|
|
assert.Equal(t, uint64(128), duty.CommitteeLength)
|
|
assert.Equal(t, uint64(4), duty.CommitteesAtSlot)
|
|
assert.Equal(t, types.CommitteeIndex(123), duty.ValidatorCommitteeIndex)
|
|
})
|
|
|
|
t.Run("Multiple validators", func(t *testing.T) {
|
|
req := &v1.AttesterDutiesRequest{
|
|
Epoch: 0,
|
|
Index: []types.ValidatorIndex{0, 1},
|
|
}
|
|
resp, err := vs.GetAttesterDuties(ctx, req)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 2, len(resp.Data))
|
|
})
|
|
|
|
t.Run("Next epoch", func(t *testing.T) {
|
|
req := &v1.AttesterDutiesRequest{
|
|
Epoch: helpers.SlotToEpoch(bs.Slot()) + 1,
|
|
Index: []types.ValidatorIndex{0},
|
|
}
|
|
resp, err := vs.GetAttesterDuties(ctx, req)
|
|
require.NoError(t, err)
|
|
assert.DeepEqual(t, genesisRoot[:], resp.DependentRoot)
|
|
require.Equal(t, 1, len(resp.Data))
|
|
duty := resp.Data[0]
|
|
assert.Equal(t, types.CommitteeIndex(1), duty.CommitteeIndex)
|
|
assert.Equal(t, types.Slot(38), duty.Slot)
|
|
assert.Equal(t, types.ValidatorIndex(0), duty.ValidatorIndex)
|
|
assert.DeepEqual(t, pubKeys[0], duty.Pubkey)
|
|
assert.Equal(t, uint64(128), duty.CommitteeLength)
|
|
assert.Equal(t, uint64(4), duty.CommitteesAtSlot)
|
|
assert.Equal(t, types.CommitteeIndex(27), duty.ValidatorCommitteeIndex)
|
|
})
|
|
|
|
t.Run("Require slot processing", func(t *testing.T) {
|
|
// We create local variables to not interfere with other tests.
|
|
// Slot processing might have unexpected side-effects.
|
|
|
|
bs, err := state.GenesisBeaconState(context.Background(), deposits, 0, eth1Data)
|
|
require.NoError(t, err, "Could not set up genesis state")
|
|
// Set state to non-epoch start slot.
|
|
require.NoError(t, bs.SetSlot(5))
|
|
genesisRoot, err := genesis.Block.HashTreeRoot()
|
|
require.NoError(t, err, "Could not get signing root")
|
|
roots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
|
|
roots[0] = genesisRoot[:]
|
|
require.NoError(t, bs.SetBlockRoots(roots))
|
|
|
|
pubKeys := make([][]byte, len(deposits))
|
|
indices := make([]uint64, len(deposits))
|
|
for i := 0; i < len(deposits); i++ {
|
|
pubKeys[i] = deposits[i].Data.PublicKey
|
|
indices[i] = uint64(i)
|
|
}
|
|
chainSlot := params.BeaconConfig().SlotsPerEpoch.Mul(2)
|
|
chain := &mockChain.ChainService{
|
|
State: bs, Root: genesisRoot[:], Slot: &chainSlot,
|
|
}
|
|
vs := &Server{
|
|
HeadFetcher: chain,
|
|
TimeFetcher: chain,
|
|
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
|
}
|
|
|
|
req := &v1.AttesterDutiesRequest{
|
|
Epoch: 2,
|
|
Index: []types.ValidatorIndex{0},
|
|
}
|
|
resp, err := vs.GetAttesterDuties(ctx, req)
|
|
require.NoError(t, err)
|
|
assert.DeepEqual(t, bs.BlockRoots()[31], resp.DependentRoot)
|
|
require.Equal(t, 1, len(resp.Data))
|
|
duty := resp.Data[0]
|
|
assert.Equal(t, types.CommitteeIndex(1), duty.CommitteeIndex)
|
|
assert.Equal(t, types.Slot(86), duty.Slot)
|
|
assert.Equal(t, types.ValidatorIndex(0), duty.ValidatorIndex)
|
|
assert.DeepEqual(t, pubKeys[0], duty.Pubkey)
|
|
assert.Equal(t, uint64(128), duty.CommitteeLength)
|
|
assert.Equal(t, uint64(4), duty.CommitteesAtSlot)
|
|
assert.Equal(t, types.CommitteeIndex(44), duty.ValidatorCommitteeIndex)
|
|
})
|
|
|
|
t.Run("Epoch out of bound", func(t *testing.T) {
|
|
currentEpoch := helpers.SlotToEpoch(bs.Slot())
|
|
req := &v1.AttesterDutiesRequest{
|
|
Epoch: currentEpoch + 2,
|
|
Index: []types.ValidatorIndex{0},
|
|
}
|
|
_, err := vs.GetAttesterDuties(ctx, req)
|
|
require.NotNil(t, err)
|
|
assert.ErrorContains(t, fmt.Sprintf("Request epoch %d can not be greater than next epoch %d", currentEpoch+2, currentEpoch+1), err)
|
|
})
|
|
|
|
t.Run("Validator index out of bound", func(t *testing.T) {
|
|
req := &v1.AttesterDutiesRequest{
|
|
Epoch: 0,
|
|
Index: []types.ValidatorIndex{types.ValidatorIndex(len(pubKeys))},
|
|
}
|
|
_, err := vs.GetAttesterDuties(ctx, req)
|
|
require.NotNil(t, err)
|
|
assert.ErrorContains(t, "Invalid index", err)
|
|
})
|
|
}
|
|
|
|
func TestGetAttesterDuties_SyncNotReady(t *testing.T) {
|
|
vs := &Server{
|
|
SyncChecker: &mockSync.Sync{IsSyncing: true},
|
|
}
|
|
_, err := vs.GetAttesterDuties(context.Background(), &v1.AttesterDutiesRequest{})
|
|
assert.ErrorContains(t, "Syncing to latest head, not ready to respond", err)
|
|
}
|
|
|
|
func TestGetProposerDuties(t *testing.T) {
|
|
ctx := context.Background()
|
|
genesis := testutil.NewBeaconBlock()
|
|
depChainStart := params.BeaconConfig().MinGenesisActiveValidatorCount
|
|
deposits, _, err := testutil.DeterministicDepositsAndKeys(depChainStart)
|
|
require.NoError(t, err)
|
|
eth1Data, err := testutil.DeterministicEth1Data(len(deposits))
|
|
require.NoError(t, err)
|
|
bs, err := state.GenesisBeaconState(context.Background(), deposits, 0, eth1Data)
|
|
require.NoError(t, err, "Could not set up genesis state")
|
|
// Set state to non-epoch start slot.
|
|
require.NoError(t, bs.SetSlot(5))
|
|
genesisRoot, err := genesis.Block.HashTreeRoot()
|
|
require.NoError(t, err, "Could not get signing root")
|
|
roots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
|
|
roots[0] = genesisRoot[:]
|
|
require.NoError(t, bs.SetBlockRoots(roots))
|
|
|
|
pubKeys := make([][]byte, len(deposits))
|
|
for i := 0; i < len(deposits); i++ {
|
|
pubKeys[i] = deposits[i].Data.PublicKey
|
|
}
|
|
|
|
chainSlot := types.Slot(0)
|
|
chain := &mockChain.ChainService{
|
|
State: bs, Root: genesisRoot[:], Slot: &chainSlot,
|
|
}
|
|
vs := &Server{
|
|
HeadFetcher: chain,
|
|
TimeFetcher: chain,
|
|
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
|
}
|
|
|
|
t.Run("Ok", func(t *testing.T) {
|
|
req := &v1.ProposerDutiesRequest{
|
|
Epoch: 0,
|
|
}
|
|
resp, err := vs.GetProposerDuties(ctx, req)
|
|
require.NoError(t, err)
|
|
assert.DeepEqual(t, genesisRoot[:], resp.DependentRoot)
|
|
assert.Equal(t, 31, len(resp.Data))
|
|
// We expect a proposer duty for slot 11.
|
|
var expectedDuty *v1.ProposerDuty
|
|
for _, duty := range resp.Data {
|
|
if duty.Slot == 11 {
|
|
expectedDuty = duty
|
|
}
|
|
}
|
|
require.NotNil(t, expectedDuty, "Expected duty for slot 11 not found")
|
|
assert.Equal(t, types.ValidatorIndex(12289), expectedDuty.ValidatorIndex)
|
|
assert.DeepEqual(t, pubKeys[12289], expectedDuty.Pubkey)
|
|
})
|
|
|
|
t.Run("Require slot processing", func(t *testing.T) {
|
|
// We create local variables to not interfere with other tests.
|
|
// Slot processing might have unexpected side-effects.
|
|
|
|
bs, err := state.GenesisBeaconState(context.Background(), deposits, 0, eth1Data)
|
|
require.NoError(t, err, "Could not set up genesis state")
|
|
// Set state to non-epoch start slot.
|
|
require.NoError(t, bs.SetSlot(5))
|
|
genesisRoot, err := genesis.Block.HashTreeRoot()
|
|
require.NoError(t, err, "Could not get signing root")
|
|
roots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
|
|
roots[0] = genesisRoot[:]
|
|
require.NoError(t, bs.SetBlockRoots(roots))
|
|
|
|
pubKeys := make([][]byte, len(deposits))
|
|
indices := make([]uint64, len(deposits))
|
|
for i := 0; i < len(deposits); i++ {
|
|
pubKeys[i] = deposits[i].Data.PublicKey
|
|
indices[i] = uint64(i)
|
|
}
|
|
chainSlot := params.BeaconConfig().SlotsPerEpoch.Mul(2)
|
|
chain := &mockChain.ChainService{
|
|
State: bs, Root: genesisRoot[:], Slot: &chainSlot,
|
|
}
|
|
vs := &Server{
|
|
HeadFetcher: chain,
|
|
TimeFetcher: chain,
|
|
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
|
}
|
|
|
|
req := &v1.ProposerDutiesRequest{
|
|
Epoch: 2,
|
|
}
|
|
resp, err := vs.GetProposerDuties(ctx, req)
|
|
require.NoError(t, err)
|
|
assert.DeepEqual(t, bs.BlockRoots()[31], resp.DependentRoot)
|
|
assert.Equal(t, 32, len(resp.Data))
|
|
// We expect a proposer duty for slot 74.
|
|
var expectedDuty *v1.ProposerDuty
|
|
for _, duty := range resp.Data {
|
|
if duty.Slot == 74 {
|
|
expectedDuty = duty
|
|
}
|
|
}
|
|
require.NotNil(t, expectedDuty, "Expected duty for slot 74 not found")
|
|
assert.Equal(t, types.ValidatorIndex(11741), expectedDuty.ValidatorIndex)
|
|
assert.DeepEqual(t, pubKeys[11741], expectedDuty.Pubkey)
|
|
})
|
|
|
|
t.Run("Epoch out of bound", func(t *testing.T) {
|
|
currentEpoch := helpers.SlotToEpoch(bs.Slot())
|
|
req := &v1.ProposerDutiesRequest{
|
|
Epoch: currentEpoch + 1,
|
|
}
|
|
_, err := vs.GetProposerDuties(ctx, req)
|
|
require.NotNil(t, err)
|
|
assert.ErrorContains(t, fmt.Sprintf("Request epoch %d can not be greater than current epoch %d", currentEpoch+1, currentEpoch), err)
|
|
})
|
|
}
|
|
|
|
func TestGetProposerDuties_SyncNotReady(t *testing.T) {
|
|
vs := &Server{
|
|
SyncChecker: &mockSync.Sync{IsSyncing: true},
|
|
}
|
|
_, err := vs.GetProposerDuties(context.Background(), &v1.ProposerDutiesRequest{})
|
|
assert.ErrorContains(t, "Syncing to latest head, not ready to respond", err)
|
|
}
|
|
|
|
func TestProduceBlock(t *testing.T) {
|
|
db := dbutil.SetupDB(t)
|
|
ctx := context.Background()
|
|
|
|
params.SetupTestConfigCleanup(t)
|
|
params.OverrideBeaconConfig(params.MainnetConfig())
|
|
beaconState, privKeys := testutil.DeterministicGenesisState(t, 64)
|
|
|
|
stateRoot, err := beaconState.HashTreeRoot(ctx)
|
|
require.NoError(t, err, "Could not hash genesis state")
|
|
|
|
genesis := b.NewGenesisBlock(stateRoot[:])
|
|
require.NoError(t, db.SaveBlock(ctx, wrapper.WrappedPhase0SignedBeaconBlock(genesis)), "Could not save genesis block")
|
|
|
|
parentRoot, err := genesis.Block.HashTreeRoot()
|
|
require.NoError(t, err, "Could not get signing root")
|
|
require.NoError(t, db.SaveState(ctx, beaconState, parentRoot), "Could not save genesis state")
|
|
require.NoError(t, db.SaveHeadBlockRoot(ctx, parentRoot), "Could not save genesis state")
|
|
|
|
v1Alpha1Server := &v1alpha1validator.Server{
|
|
HeadFetcher: &mockChain.ChainService{State: beaconState, Root: parentRoot[:]},
|
|
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
|
BlockReceiver: &mockChain.ChainService{},
|
|
ChainStartFetcher: &mockPOW.POWChain{},
|
|
Eth1InfoFetcher: &mockPOW.POWChain{},
|
|
Eth1BlockFetcher: &mockPOW.POWChain{},
|
|
MockEth1Votes: true,
|
|
AttPool: attestations.NewPool(),
|
|
SlashingsPool: slashings.NewPool(),
|
|
ExitPool: voluntaryexits.NewPool(),
|
|
StateGen: stategen.New(db),
|
|
}
|
|
|
|
proposerSlashings := make([]*ethpb.ProposerSlashing, params.BeaconConfig().MaxProposerSlashings)
|
|
for i := types.ValidatorIndex(0); uint64(i) < params.BeaconConfig().MaxProposerSlashings; i++ {
|
|
proposerSlashing, err := testutil.GenerateProposerSlashingForValidator(
|
|
beaconState,
|
|
privKeys[i],
|
|
i, /* validator index */
|
|
)
|
|
require.NoError(t, err)
|
|
proposerSlashings[i] = proposerSlashing
|
|
err = v1Alpha1Server.SlashingsPool.InsertProposerSlashing(context.Background(), beaconState, proposerSlashing)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
attSlashings := make([]*ethpb.AttesterSlashing, params.BeaconConfig().MaxAttesterSlashings)
|
|
for i := uint64(0); i < params.BeaconConfig().MaxAttesterSlashings; i++ {
|
|
attesterSlashing, err := testutil.GenerateAttesterSlashingForValidator(
|
|
beaconState,
|
|
privKeys[i+params.BeaconConfig().MaxProposerSlashings],
|
|
types.ValidatorIndex(i+params.BeaconConfig().MaxProposerSlashings), /* validator index */
|
|
)
|
|
require.NoError(t, err)
|
|
attSlashings[i] = attesterSlashing
|
|
err = v1Alpha1Server.SlashingsPool.InsertAttesterSlashing(context.Background(), beaconState, attesterSlashing)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
v1Server := &Server{
|
|
V1Alpha1Server: v1Alpha1Server,
|
|
}
|
|
randaoReveal, err := testutil.RandaoReveal(beaconState, 0, privKeys)
|
|
require.NoError(t, err)
|
|
graffiti := bytesutil.ToBytes32([]byte("eth2"))
|
|
req := &v1.ProduceBlockRequest{
|
|
Slot: 1,
|
|
RandaoReveal: randaoReveal,
|
|
Graffiti: graffiti[:],
|
|
}
|
|
resp, err := v1Server.ProduceBlock(ctx, req)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, req.Slot, resp.Data.Slot, "Expected block to have slot of 1")
|
|
assert.DeepEqual(t, parentRoot[:], resp.Data.ParentRoot, "Expected block to have correct parent root")
|
|
assert.DeepEqual(t, randaoReveal, resp.Data.Body.RandaoReveal, "Expected block to have correct randao reveal")
|
|
assert.DeepEqual(t, req.Graffiti, resp.Data.Body.Graffiti, "Expected block to have correct graffiti")
|
|
assert.Equal(t, params.BeaconConfig().MaxProposerSlashings, uint64(len(resp.Data.Body.ProposerSlashings)))
|
|
expectedPropSlashings := make([]*v1.ProposerSlashing, len(proposerSlashings))
|
|
for i, slash := range proposerSlashings {
|
|
expectedPropSlashings[i] = migration.V1Alpha1ProposerSlashingToV1(slash)
|
|
}
|
|
assert.DeepEqual(t, expectedPropSlashings, resp.Data.Body.ProposerSlashings)
|
|
assert.Equal(t, params.BeaconConfig().MaxAttesterSlashings, uint64(len(resp.Data.Body.AttesterSlashings)))
|
|
expectedAttSlashings := make([]*v1.AttesterSlashing, len(attSlashings))
|
|
for i, slash := range attSlashings {
|
|
expectedAttSlashings[i] = migration.V1Alpha1AttSlashingToV1(slash)
|
|
}
|
|
assert.DeepEqual(t, expectedAttSlashings, resp.Data.Body.AttesterSlashings)
|
|
}
|
|
|
|
func TestProduceAttestationData(t *testing.T) {
|
|
block := testutil.NewBeaconBlock()
|
|
block.Block.Slot = 3*params.BeaconConfig().SlotsPerEpoch + 1
|
|
targetBlock := testutil.NewBeaconBlock()
|
|
targetBlock.Block.Slot = 1 * params.BeaconConfig().SlotsPerEpoch
|
|
justifiedBlock := testutil.NewBeaconBlock()
|
|
justifiedBlock.Block.Slot = 2 * params.BeaconConfig().SlotsPerEpoch
|
|
blockRoot, err := block.Block.HashTreeRoot()
|
|
require.NoError(t, err, "Could not hash beacon block")
|
|
justifiedRoot, err := justifiedBlock.Block.HashTreeRoot()
|
|
require.NoError(t, err, "Could not get signing root for justified block")
|
|
targetRoot, err := targetBlock.Block.HashTreeRoot()
|
|
require.NoError(t, err, "Could not get signing root for target block")
|
|
slot := 3*params.BeaconConfig().SlotsPerEpoch + 1
|
|
beaconState, err := testutil.NewBeaconState()
|
|
require.NoError(t, err)
|
|
require.NoError(t, beaconState.SetSlot(slot))
|
|
err = beaconState.SetCurrentJustifiedCheckpoint(ðpb.Checkpoint{
|
|
Epoch: 2,
|
|
Root: justifiedRoot[:],
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
blockRoots := beaconState.BlockRoots()
|
|
blockRoots[1] = blockRoot[:]
|
|
blockRoots[1*params.BeaconConfig().SlotsPerEpoch] = targetRoot[:]
|
|
blockRoots[2*params.BeaconConfig().SlotsPerEpoch] = justifiedRoot[:]
|
|
require.NoError(t, beaconState.SetBlockRoots(blockRoots))
|
|
chainService := &mockChain.ChainService{
|
|
Genesis: time.Now(),
|
|
}
|
|
offset := int64(slot.Mul(params.BeaconConfig().SecondsPerSlot))
|
|
v1Alpha1Server := &v1alpha1validator.Server{
|
|
P2P: &mockp2p.MockBroadcaster{},
|
|
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
|
AttestationCache: cache.NewAttestationCache(),
|
|
HeadFetcher: &mockChain.ChainService{
|
|
State: beaconState, Root: blockRoot[:],
|
|
},
|
|
FinalizationFetcher: &mockChain.ChainService{
|
|
CurrentJustifiedCheckPoint: beaconState.CurrentJustifiedCheckpoint(),
|
|
},
|
|
TimeFetcher: &mockChain.ChainService{
|
|
Genesis: time.Now().Add(time.Duration(-1*offset) * time.Second),
|
|
},
|
|
StateNotifier: chainService.StateNotifier(),
|
|
}
|
|
v1Server := &Server{
|
|
V1Alpha1Server: v1Alpha1Server,
|
|
}
|
|
|
|
req := &v1.ProduceAttestationDataRequest{
|
|
CommitteeIndex: 0,
|
|
Slot: 3*params.BeaconConfig().SlotsPerEpoch + 1,
|
|
}
|
|
res, err := v1Server.ProduceAttestationData(context.Background(), req)
|
|
require.NoError(t, err, "Could not get attestation info at slot")
|
|
|
|
expectedInfo := &v1.AttestationData{
|
|
Slot: 3*params.BeaconConfig().SlotsPerEpoch + 1,
|
|
BeaconBlockRoot: blockRoot[:],
|
|
Source: &v1.Checkpoint{
|
|
Epoch: 2,
|
|
Root: justifiedRoot[:],
|
|
},
|
|
Target: &v1.Checkpoint{
|
|
Epoch: 3,
|
|
Root: blockRoot[:],
|
|
},
|
|
}
|
|
|
|
if !proto.Equal(res.Data, expectedInfo) {
|
|
t.Errorf("Expected attestation info to match, received %v, wanted %v", res, expectedInfo)
|
|
}
|
|
}
|
|
|
|
func TestGetAggregateAttestation(t *testing.T) {
|
|
ctx := context.Background()
|
|
root1 := bytesutil.PadTo([]byte("root1"), 32)
|
|
sig1 := bytesutil.PadTo([]byte("sig1"), 96)
|
|
attSlot1 := ðpb.Attestation{
|
|
AggregationBits: []byte{0, 1},
|
|
Data: ðpb.AttestationData{
|
|
Slot: 1,
|
|
CommitteeIndex: 1,
|
|
BeaconBlockRoot: root1,
|
|
Source: ðpb.Checkpoint{
|
|
Epoch: 1,
|
|
Root: root1,
|
|
},
|
|
Target: ðpb.Checkpoint{
|
|
Epoch: 1,
|
|
Root: root1,
|
|
},
|
|
},
|
|
Signature: sig1,
|
|
}
|
|
root2_1 := bytesutil.PadTo([]byte("root2_1"), 32)
|
|
sig2_1 := bytesutil.PadTo([]byte("sig2_1"), 96)
|
|
attSlot2_1 := ðpb.Attestation{
|
|
AggregationBits: []byte{0, 1, 1},
|
|
Data: ðpb.AttestationData{
|
|
Slot: 2,
|
|
CommitteeIndex: 2,
|
|
BeaconBlockRoot: root2_1,
|
|
Source: ðpb.Checkpoint{
|
|
Epoch: 1,
|
|
Root: root2_1,
|
|
},
|
|
Target: ðpb.Checkpoint{
|
|
Epoch: 1,
|
|
Root: root2_1,
|
|
},
|
|
},
|
|
Signature: sig2_1,
|
|
}
|
|
root2_2 := bytesutil.PadTo([]byte("root2_2"), 32)
|
|
sig2_2 := bytesutil.PadTo([]byte("sig2_2"), 96)
|
|
attSlot2_2 := ðpb.Attestation{
|
|
AggregationBits: []byte{0, 1, 1, 1},
|
|
Data: ðpb.AttestationData{
|
|
Slot: 2,
|
|
CommitteeIndex: 3,
|
|
BeaconBlockRoot: root2_2,
|
|
Source: ðpb.Checkpoint{
|
|
Epoch: 1,
|
|
Root: root2_2,
|
|
},
|
|
Target: ðpb.Checkpoint{
|
|
Epoch: 1,
|
|
Root: root2_2,
|
|
},
|
|
},
|
|
Signature: sig2_2,
|
|
}
|
|
vs := &Server{
|
|
AttestationsPool: &attestations.PoolMock{AggregatedAtts: []*ethpb.Attestation{attSlot1, attSlot2_1, attSlot2_2}},
|
|
}
|
|
|
|
t.Run("OK", func(t *testing.T) {
|
|
reqRoot, err := attSlot2_2.Data.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
req := &v1.AggregateAttestationRequest{
|
|
AttestationDataRoot: reqRoot[:],
|
|
Slot: 2,
|
|
}
|
|
att, err := vs.GetAggregateAttestation(ctx, req)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, att)
|
|
require.NotNil(t, att.Data)
|
|
assert.DeepEqual(t, bitfield.Bitlist{0, 1, 1, 1}, att.Data.AggregationBits)
|
|
assert.DeepEqual(t, sig2_2, att.Data.Signature)
|
|
assert.Equal(t, types.Slot(2), att.Data.Data.Slot)
|
|
assert.Equal(t, types.CommitteeIndex(3), att.Data.Data.Index)
|
|
assert.DeepEqual(t, root2_2, att.Data.Data.BeaconBlockRoot)
|
|
require.NotNil(t, att.Data.Data.Source)
|
|
assert.Equal(t, types.Epoch(1), att.Data.Data.Source.Epoch)
|
|
assert.DeepEqual(t, root2_2, att.Data.Data.Source.Root)
|
|
require.NotNil(t, att.Data.Data.Target)
|
|
assert.Equal(t, types.Epoch(1), att.Data.Data.Target.Epoch)
|
|
assert.DeepEqual(t, root2_2, att.Data.Data.Target.Root)
|
|
})
|
|
|
|
t.Run("No matching attestation", func(t *testing.T) {
|
|
req := &v1.AggregateAttestationRequest{
|
|
AttestationDataRoot: bytesutil.PadTo([]byte("foo"), 32),
|
|
Slot: 2,
|
|
}
|
|
_, err := vs.GetAggregateAttestation(ctx, req)
|
|
assert.ErrorContains(t, "No matching attestation found", err)
|
|
})
|
|
}
|
|
|
|
func TestGetAggregateAttestation_SameSlotAndRoot_ReturnMostAggregationBits(t *testing.T) {
|
|
ctx := context.Background()
|
|
root := bytesutil.PadTo([]byte("root"), 32)
|
|
sig := bytesutil.PadTo([]byte("sig"), 96)
|
|
att1 := ðpb.Attestation{
|
|
AggregationBits: []byte{0, 1},
|
|
Data: ðpb.AttestationData{
|
|
Slot: 1,
|
|
CommitteeIndex: 1,
|
|
BeaconBlockRoot: root,
|
|
Source: ðpb.Checkpoint{
|
|
Epoch: 1,
|
|
Root: root,
|
|
},
|
|
Target: ðpb.Checkpoint{
|
|
Epoch: 1,
|
|
Root: root,
|
|
},
|
|
},
|
|
Signature: sig,
|
|
}
|
|
att2 := ðpb.Attestation{
|
|
AggregationBits: []byte{0, 1, 1},
|
|
Data: ðpb.AttestationData{
|
|
Slot: 1,
|
|
CommitteeIndex: 1,
|
|
BeaconBlockRoot: root,
|
|
Source: ðpb.Checkpoint{
|
|
Epoch: 1,
|
|
Root: root,
|
|
},
|
|
Target: ðpb.Checkpoint{
|
|
Epoch: 1,
|
|
Root: root,
|
|
},
|
|
},
|
|
Signature: sig,
|
|
}
|
|
vs := &Server{
|
|
AttestationsPool: &attestations.PoolMock{AggregatedAtts: []*ethpb.Attestation{att1, att2}},
|
|
}
|
|
|
|
reqRoot, err := att1.Data.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
req := &v1.AggregateAttestationRequest{
|
|
AttestationDataRoot: reqRoot[:],
|
|
Slot: 1,
|
|
}
|
|
att, err := vs.GetAggregateAttestation(ctx, req)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, att)
|
|
require.NotNil(t, att.Data)
|
|
assert.DeepEqual(t, bitfield.Bitlist{0, 1, 1}, att.Data.AggregationBits)
|
|
}
|
|
|
|
func TestSubmitBeaconCommitteeSubscription(t *testing.T) {
|
|
ctx := context.Background()
|
|
genesis := testutil.NewBeaconBlock()
|
|
depChainStart := params.BeaconConfig().MinGenesisActiveValidatorCount
|
|
deposits, _, err := testutil.DeterministicDepositsAndKeys(depChainStart)
|
|
require.NoError(t, err)
|
|
eth1Data, err := testutil.DeterministicEth1Data(len(deposits))
|
|
require.NoError(t, err)
|
|
bs, err := state.GenesisBeaconState(context.Background(), deposits, 0, eth1Data)
|
|
require.NoError(t, err, "Could not set up genesis state")
|
|
// Set state to non-epoch start slot.
|
|
require.NoError(t, bs.SetSlot(5))
|
|
genesisRoot, err := genesis.Block.HashTreeRoot()
|
|
require.NoError(t, err, "Could not get signing root")
|
|
roots := make([][]byte, params.BeaconConfig().SlotsPerHistoricalRoot)
|
|
roots[0] = genesisRoot[:]
|
|
require.NoError(t, bs.SetBlockRoots(roots))
|
|
|
|
pubKeys := make([][]byte, len(deposits))
|
|
for i := 0; i < len(deposits); i++ {
|
|
pubKeys[i] = deposits[i].Data.PublicKey
|
|
}
|
|
|
|
chainSlot := types.Slot(0)
|
|
chain := &mockChain.ChainService{
|
|
State: bs, Root: genesisRoot[:], Slot: &chainSlot,
|
|
}
|
|
vs := &Server{
|
|
HeadFetcher: chain,
|
|
TimeFetcher: chain,
|
|
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
|
V1Alpha1Server: &v1alpha1validator.Server{},
|
|
}
|
|
|
|
t.Run("Single subscription", func(t *testing.T) {
|
|
cache.SubnetIDs.EmptyAllCaches()
|
|
req := &v1.SubmitBeaconCommitteeSubscriptionsRequest{
|
|
Data: []*v1.BeaconCommitteeSubscribe{
|
|
{
|
|
ValidatorIndex: 1,
|
|
CommitteeIndex: 1,
|
|
Slot: 1,
|
|
IsAggregator: false,
|
|
},
|
|
},
|
|
}
|
|
_, err = vs.SubmitBeaconCommitteeSubscription(ctx, req)
|
|
require.NoError(t, err)
|
|
subnets := cache.SubnetIDs.GetAttesterSubnetIDs(1)
|
|
require.Equal(t, 1, len(subnets))
|
|
assert.Equal(t, uint64(5), subnets[0])
|
|
})
|
|
|
|
t.Run("Multiple subscriptions", func(t *testing.T) {
|
|
cache.SubnetIDs.EmptyAllCaches()
|
|
req := &v1.SubmitBeaconCommitteeSubscriptionsRequest{
|
|
Data: []*v1.BeaconCommitteeSubscribe{
|
|
{
|
|
ValidatorIndex: 1,
|
|
CommitteeIndex: 1,
|
|
Slot: 1,
|
|
IsAggregator: false,
|
|
},
|
|
{
|
|
ValidatorIndex: 1000,
|
|
CommitteeIndex: 16,
|
|
Slot: 1,
|
|
IsAggregator: false,
|
|
},
|
|
},
|
|
}
|
|
_, err = vs.SubmitBeaconCommitteeSubscription(ctx, req)
|
|
require.NoError(t, err)
|
|
subnets := cache.SubnetIDs.GetAttesterSubnetIDs(1)
|
|
require.Equal(t, 2, len(subnets))
|
|
})
|
|
|
|
t.Run("Is aggregator", func(t *testing.T) {
|
|
cache.SubnetIDs.EmptyAllCaches()
|
|
req := &v1.SubmitBeaconCommitteeSubscriptionsRequest{
|
|
Data: []*v1.BeaconCommitteeSubscribe{
|
|
{
|
|
ValidatorIndex: 1,
|
|
CommitteeIndex: 1,
|
|
Slot: 1,
|
|
IsAggregator: true,
|
|
},
|
|
},
|
|
}
|
|
_, err = vs.SubmitBeaconCommitteeSubscription(ctx, req)
|
|
require.NoError(t, err)
|
|
ids := cache.SubnetIDs.GetAggregatorSubnetIDs(types.Slot(1))
|
|
assert.Equal(t, 1, len(ids))
|
|
})
|
|
|
|
t.Run("Validators assigned to subnet", func(t *testing.T) {
|
|
cache.SubnetIDs.EmptyAllCaches()
|
|
req := &v1.SubmitBeaconCommitteeSubscriptionsRequest{
|
|
Data: []*v1.BeaconCommitteeSubscribe{
|
|
{
|
|
ValidatorIndex: 1,
|
|
CommitteeIndex: 1,
|
|
Slot: 1,
|
|
IsAggregator: true,
|
|
},
|
|
{
|
|
ValidatorIndex: 2,
|
|
CommitteeIndex: 1,
|
|
Slot: 1,
|
|
IsAggregator: false,
|
|
},
|
|
},
|
|
}
|
|
_, err = vs.SubmitBeaconCommitteeSubscription(ctx, req)
|
|
require.NoError(t, err)
|
|
ids, ok, _ := cache.SubnetIDs.GetPersistentSubnets(pubKeys[1])
|
|
require.Equal(t, true, ok, "subnet for validator 1 not found")
|
|
assert.Equal(t, 1, len(ids))
|
|
ids, ok, _ = cache.SubnetIDs.GetPersistentSubnets(pubKeys[2])
|
|
require.Equal(t, true, ok, "subnet for validator 2 not found")
|
|
assert.Equal(t, 1, len(ids))
|
|
})
|
|
|
|
t.Run("No subscriptions", func(t *testing.T) {
|
|
req := &v1.SubmitBeaconCommitteeSubscriptionsRequest{
|
|
Data: make([]*v1.BeaconCommitteeSubscribe, 0),
|
|
}
|
|
_, err = vs.SubmitBeaconCommitteeSubscription(ctx, req)
|
|
require.NotNil(t, err)
|
|
assert.ErrorContains(t, "No subscriptions provided", err)
|
|
})
|
|
}
|
|
|
|
func TestSubmitBeaconCommitteeSubscription_SyncNotReady(t *testing.T) {
|
|
vs := &Server{
|
|
SyncChecker: &mockSync.Sync{IsSyncing: true},
|
|
}
|
|
_, err := vs.SubmitBeaconCommitteeSubscription(context.Background(), &v1.SubmitBeaconCommitteeSubscriptionsRequest{})
|
|
assert.ErrorContains(t, "Syncing to latest head, not ready to respond", err)
|
|
}
|
|
|
|
func TestSubmitAggregateAndProofs(t *testing.T) {
|
|
ctx := context.Background()
|
|
params.SetupTestConfigCleanup(t)
|
|
c := params.BeaconNetworkConfig()
|
|
c.MaximumGossipClockDisparity = time.Hour
|
|
params.OverrideBeaconNetworkConfig(c)
|
|
root := bytesutil.PadTo([]byte("root"), 32)
|
|
sig := bytesutil.PadTo([]byte("sig"), 96)
|
|
proof := bytesutil.PadTo([]byte("proof"), 96)
|
|
att := &v1.Attestation{
|
|
AggregationBits: []byte{0, 1},
|
|
Data: &v1.AttestationData{
|
|
Slot: 1,
|
|
Index: 1,
|
|
BeaconBlockRoot: root,
|
|
Source: &v1.Checkpoint{
|
|
Epoch: 1,
|
|
Root: root,
|
|
},
|
|
Target: &v1.Checkpoint{
|
|
Epoch: 1,
|
|
Root: root,
|
|
},
|
|
},
|
|
Signature: sig,
|
|
}
|
|
|
|
t.Run("OK", func(t *testing.T) {
|
|
chainSlot := types.Slot(0)
|
|
chain := &mockChain.ChainService{
|
|
Genesis: time.Now(), Slot: &chainSlot,
|
|
}
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
vs := Server{
|
|
TimeFetcher: chain,
|
|
Broadcaster: broadcaster,
|
|
}
|
|
|
|
req := &v1.SubmitAggregateAndProofsRequest{
|
|
Data: []*v1.SignedAggregateAttestationAndProof{
|
|
{
|
|
Message: &v1.AggregateAttestationAndProof{
|
|
AggregatorIndex: 1,
|
|
Aggregate: att,
|
|
SelectionProof: proof,
|
|
},
|
|
Signature: sig,
|
|
},
|
|
},
|
|
}
|
|
|
|
_, err := vs.SubmitAggregateAndProofs(ctx, req)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, true, broadcaster.BroadcastCalled)
|
|
})
|
|
|
|
t.Run("nil aggregate", func(t *testing.T) {
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
vs := Server{
|
|
Broadcaster: broadcaster,
|
|
}
|
|
|
|
req := &v1.SubmitAggregateAndProofsRequest{
|
|
Data: []*v1.SignedAggregateAttestationAndProof{
|
|
nil,
|
|
},
|
|
}
|
|
_, err := vs.SubmitAggregateAndProofs(ctx, req)
|
|
require.NotNil(t, err)
|
|
assert.ErrorContains(t, "Signed aggregate request can't be nil", err)
|
|
assert.Equal(t, false, broadcaster.BroadcastCalled)
|
|
|
|
req = &v1.SubmitAggregateAndProofsRequest{
|
|
Data: []*v1.SignedAggregateAttestationAndProof{
|
|
{
|
|
Message: nil,
|
|
Signature: sig,
|
|
},
|
|
},
|
|
}
|
|
_, err = vs.SubmitAggregateAndProofs(ctx, req)
|
|
require.NotNil(t, err)
|
|
assert.ErrorContains(t, "Signed aggregate request can't be nil", err)
|
|
assert.Equal(t, false, broadcaster.BroadcastCalled)
|
|
|
|
req = &v1.SubmitAggregateAndProofsRequest{
|
|
Data: []*v1.SignedAggregateAttestationAndProof{
|
|
{
|
|
Message: &v1.AggregateAttestationAndProof{
|
|
AggregatorIndex: 1,
|
|
Aggregate: &v1.Attestation{
|
|
AggregationBits: []byte{0, 1},
|
|
Data: nil,
|
|
Signature: sig,
|
|
},
|
|
SelectionProof: proof,
|
|
},
|
|
Signature: sig,
|
|
},
|
|
},
|
|
}
|
|
_, err = vs.SubmitAggregateAndProofs(ctx, req)
|
|
require.NotNil(t, err)
|
|
assert.ErrorContains(t, "Signed aggregate request can't be nil", err)
|
|
assert.Equal(t, false, broadcaster.BroadcastCalled)
|
|
})
|
|
|
|
t.Run("zero signature", func(t *testing.T) {
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
vs := Server{
|
|
Broadcaster: broadcaster,
|
|
}
|
|
req := &v1.SubmitAggregateAndProofsRequest{
|
|
Data: []*v1.SignedAggregateAttestationAndProof{
|
|
{
|
|
Message: &v1.AggregateAttestationAndProof{
|
|
AggregatorIndex: 1,
|
|
Aggregate: att,
|
|
SelectionProof: proof,
|
|
},
|
|
Signature: make([]byte, 96),
|
|
},
|
|
},
|
|
}
|
|
_, err := vs.SubmitAggregateAndProofs(ctx, req)
|
|
require.NotNil(t, err)
|
|
assert.ErrorContains(t, "Signed signatures can't be zero hashes", err)
|
|
assert.Equal(t, false, broadcaster.BroadcastCalled)
|
|
})
|
|
|
|
t.Run("zero proof", func(t *testing.T) {
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
vs := Server{
|
|
Broadcaster: broadcaster,
|
|
}
|
|
req := &v1.SubmitAggregateAndProofsRequest{
|
|
Data: []*v1.SignedAggregateAttestationAndProof{
|
|
{
|
|
Message: &v1.AggregateAttestationAndProof{
|
|
AggregatorIndex: 1,
|
|
Aggregate: att,
|
|
SelectionProof: make([]byte, 96),
|
|
},
|
|
Signature: sig,
|
|
},
|
|
},
|
|
}
|
|
_, err := vs.SubmitAggregateAndProofs(ctx, req)
|
|
require.NotNil(t, err)
|
|
assert.ErrorContains(t, "Signed signatures can't be zero hashes", err)
|
|
assert.Equal(t, false, broadcaster.BroadcastCalled)
|
|
})
|
|
|
|
t.Run("zero message signature", func(t *testing.T) {
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
vs := Server{
|
|
Broadcaster: broadcaster,
|
|
}
|
|
req := &v1.SubmitAggregateAndProofsRequest{
|
|
Data: []*v1.SignedAggregateAttestationAndProof{
|
|
{
|
|
Message: &v1.AggregateAttestationAndProof{
|
|
AggregatorIndex: 1,
|
|
Aggregate: &v1.Attestation{
|
|
AggregationBits: []byte{0, 1},
|
|
Data: &v1.AttestationData{
|
|
Slot: 1,
|
|
Index: 1,
|
|
BeaconBlockRoot: root,
|
|
Source: &v1.Checkpoint{
|
|
Epoch: 1,
|
|
Root: root,
|
|
},
|
|
Target: &v1.Checkpoint{
|
|
Epoch: 1,
|
|
Root: root,
|
|
},
|
|
},
|
|
Signature: make([]byte, 96),
|
|
},
|
|
SelectionProof: proof,
|
|
},
|
|
Signature: sig,
|
|
},
|
|
},
|
|
}
|
|
_, err := vs.SubmitAggregateAndProofs(ctx, req)
|
|
require.NotNil(t, err)
|
|
assert.ErrorContains(t, "Signed signatures can't be zero hashes", err)
|
|
assert.Equal(t, false, broadcaster.BroadcastCalled)
|
|
})
|
|
|
|
t.Run("wrong signature length", func(t *testing.T) {
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
vs := Server{
|
|
Broadcaster: broadcaster,
|
|
}
|
|
|
|
req := &v1.SubmitAggregateAndProofsRequest{
|
|
Data: []*v1.SignedAggregateAttestationAndProof{
|
|
{
|
|
Message: &v1.AggregateAttestationAndProof{
|
|
AggregatorIndex: 1,
|
|
Aggregate: att,
|
|
SelectionProof: proof,
|
|
},
|
|
Signature: make([]byte, 99),
|
|
},
|
|
},
|
|
}
|
|
_, err := vs.SubmitAggregateAndProofs(ctx, req)
|
|
require.NotNil(t, err)
|
|
assert.ErrorContains(t, "Incorrect signature length. Expected "+strconv.Itoa(96)+" bytes", err)
|
|
assert.Equal(t, false, broadcaster.BroadcastCalled)
|
|
|
|
req = &v1.SubmitAggregateAndProofsRequest{
|
|
Data: []*v1.SignedAggregateAttestationAndProof{
|
|
{
|
|
Message: &v1.AggregateAttestationAndProof{
|
|
AggregatorIndex: 1,
|
|
Aggregate: &v1.Attestation{
|
|
AggregationBits: []byte{0, 1},
|
|
Data: &v1.AttestationData{
|
|
Slot: 1,
|
|
Index: 1,
|
|
BeaconBlockRoot: root,
|
|
Source: &v1.Checkpoint{
|
|
Epoch: 1,
|
|
Root: root,
|
|
},
|
|
Target: &v1.Checkpoint{
|
|
Epoch: 1,
|
|
Root: root,
|
|
},
|
|
},
|
|
Signature: make([]byte, 99),
|
|
},
|
|
SelectionProof: proof,
|
|
},
|
|
Signature: sig,
|
|
},
|
|
},
|
|
}
|
|
_, err = vs.SubmitAggregateAndProofs(ctx, req)
|
|
require.NotNil(t, err)
|
|
assert.ErrorContains(t, "Incorrect signature length. Expected "+strconv.Itoa(96)+" bytes", err)
|
|
assert.Equal(t, false, broadcaster.BroadcastCalled)
|
|
})
|
|
|
|
t.Run("invalid attestation time", func(t *testing.T) {
|
|
chainSlot := types.Slot(0)
|
|
chain := &mockChain.ChainService{
|
|
Genesis: time.Now().Add(time.Hour * 2), Slot: &chainSlot,
|
|
}
|
|
broadcaster := &p2pMock.MockBroadcaster{}
|
|
vs := Server{
|
|
TimeFetcher: chain,
|
|
Broadcaster: broadcaster,
|
|
}
|
|
|
|
req := &v1.SubmitAggregateAndProofsRequest{
|
|
Data: []*v1.SignedAggregateAttestationAndProof{
|
|
{
|
|
Message: &v1.AggregateAttestationAndProof{
|
|
AggregatorIndex: 1,
|
|
Aggregate: att,
|
|
SelectionProof: proof,
|
|
},
|
|
Signature: sig,
|
|
},
|
|
},
|
|
}
|
|
|
|
_, err := vs.SubmitAggregateAndProofs(ctx, req)
|
|
require.NotNil(t, err)
|
|
assert.ErrorContains(t, "Attestation slot is no longer valid from current time", err)
|
|
assert.Equal(t, false, broadcaster.BroadcastCalled)
|
|
})
|
|
}
|