mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-25 04:47:18 +00:00
b586d3784b
* Move, refactor, fix * @rkapka PR feedback: revert irrelevant changes Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
925 lines
36 KiB
Go
925 lines
36 KiB
Go
package sync
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/golang/snappy"
|
|
"github.com/libp2p/go-libp2p-core/peer"
|
|
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
|
pubsub_pb "github.com/libp2p/go-libp2p-pubsub/pb"
|
|
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/core"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/altair"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/transition"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/db"
|
|
testingDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/p2p/encoder"
|
|
mockp2p "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
|
|
p2ptypes "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
|
|
mockSync "github.com/prysmaticlabs/prysm/beacon-chain/sync/initial-sync/testing"
|
|
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
|
"github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1/wrapper"
|
|
"github.com/prysmaticlabs/prysm/shared/bls"
|
|
"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"
|
|
)
|
|
|
|
func TestService_ValidateSyncContributionAndProof(t *testing.T) {
|
|
db := testingDB.SetupDB(t)
|
|
headRoot, keys := fillUpBlocksAndState(context.Background(), t, db)
|
|
defaultTopic := p2p.SyncContributionAndProofSubnetTopicFormat
|
|
defaultTopic = fmt.Sprintf(defaultTopic, []byte{0xAB, 0x00, 0xCC, 0x9E})
|
|
defaultTopic = defaultTopic + "/" + encoder.ProtocolSuffixSSZSnappy
|
|
chainService := &mockChain.ChainService{
|
|
Genesis: time.Now(),
|
|
ValidatorsRoot: [32]byte{'A'},
|
|
}
|
|
emptySig := [96]byte{}
|
|
type args struct {
|
|
ctx context.Context
|
|
pid peer.ID
|
|
msg *ethpb.SignedContributionAndProof
|
|
topic string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
svc *Service
|
|
setupSvc func(s *Service, msg *ethpb.SignedContributionAndProof) *Service
|
|
args args
|
|
want pubsub.ValidationResult
|
|
}{
|
|
{
|
|
name: "Is syncing",
|
|
svc: NewService(context.Background(), &Config{
|
|
P2P: mockp2p.NewTestP2P(t),
|
|
InitialSync: &mockSync.Sync{IsSyncing: true},
|
|
Chain: chainService,
|
|
StateNotifier: chainService.StateNotifier(),
|
|
OperationNotifier: chainService.OperationNotifier(),
|
|
}),
|
|
setupSvc: func(s *Service, msg *ethpb.SignedContributionAndProof) *Service {
|
|
s.cfg.StateGen = stategen.New(db)
|
|
msg.Message.Contribution.BlockRoot = headRoot[:]
|
|
s.cfg.DB = db
|
|
s.initCaches()
|
|
return s
|
|
},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
pid: "random",
|
|
topic: "junk",
|
|
msg: ðpb.SignedContributionAndProof{
|
|
Message: ðpb.ContributionAndProof{
|
|
AggregatorIndex: 1,
|
|
Contribution: ðpb.SyncCommitteeContribution{
|
|
Slot: 1,
|
|
SubcommitteeIndex: 1,
|
|
BlockRoot: params.BeaconConfig().ZeroHash[:],
|
|
AggregationBits: bitfield.NewBitvector128(),
|
|
Signature: emptySig[:],
|
|
},
|
|
SelectionProof: emptySig[:],
|
|
},
|
|
Signature: emptySig[:],
|
|
}},
|
|
want: pubsub.ValidationIgnore,
|
|
},
|
|
{
|
|
name: "Bad Topic",
|
|
svc: NewService(context.Background(), &Config{
|
|
P2P: mockp2p.NewTestP2P(t),
|
|
InitialSync: &mockSync.Sync{IsSyncing: false},
|
|
Chain: chainService,
|
|
StateNotifier: chainService.StateNotifier(),
|
|
OperationNotifier: chainService.OperationNotifier(),
|
|
}),
|
|
setupSvc: func(s *Service, msg *ethpb.SignedContributionAndProof) *Service {
|
|
s.cfg.StateGen = stategen.New(db)
|
|
msg.Message.Contribution.BlockRoot = headRoot[:]
|
|
s.cfg.DB = db
|
|
s.initCaches()
|
|
return s
|
|
},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
pid: "random",
|
|
topic: "junk",
|
|
msg: ðpb.SignedContributionAndProof{
|
|
Message: ðpb.ContributionAndProof{
|
|
AggregatorIndex: 1,
|
|
Contribution: ðpb.SyncCommitteeContribution{
|
|
Slot: 1,
|
|
SubcommitteeIndex: 1,
|
|
BlockRoot: params.BeaconConfig().ZeroHash[:],
|
|
AggregationBits: bitfield.NewBitvector128(),
|
|
Signature: emptySig[:],
|
|
},
|
|
SelectionProof: emptySig[:],
|
|
},
|
|
Signature: emptySig[:],
|
|
}},
|
|
want: pubsub.ValidationReject,
|
|
},
|
|
{
|
|
name: "Future Slot Message",
|
|
svc: NewService(context.Background(), &Config{
|
|
P2P: mockp2p.NewTestP2P(t),
|
|
InitialSync: &mockSync.Sync{IsSyncing: false},
|
|
Chain: chainService,
|
|
StateNotifier: chainService.StateNotifier(),
|
|
OperationNotifier: chainService.OperationNotifier(),
|
|
}),
|
|
setupSvc: func(s *Service, msg *ethpb.SignedContributionAndProof) *Service {
|
|
s.cfg.StateGen = stategen.New(db)
|
|
s.cfg.DB = db
|
|
s.initCaches()
|
|
return s
|
|
},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
pid: "random",
|
|
topic: defaultTopic,
|
|
msg: ðpb.SignedContributionAndProof{
|
|
Message: ðpb.ContributionAndProof{
|
|
AggregatorIndex: 1,
|
|
Contribution: ðpb.SyncCommitteeContribution{
|
|
Slot: 30,
|
|
SubcommitteeIndex: 1,
|
|
BlockRoot: params.BeaconConfig().ZeroHash[:],
|
|
AggregationBits: bitfield.NewBitvector128(),
|
|
Signature: emptySig[:],
|
|
},
|
|
SelectionProof: emptySig[:],
|
|
},
|
|
Signature: emptySig[:],
|
|
}},
|
|
want: pubsub.ValidationIgnore,
|
|
},
|
|
{
|
|
name: "Already Seen Message",
|
|
svc: NewService(context.Background(), &Config{
|
|
P2P: mockp2p.NewTestP2P(t),
|
|
InitialSync: &mockSync.Sync{IsSyncing: false},
|
|
Chain: chainService,
|
|
StateNotifier: chainService.StateNotifier(),
|
|
OperationNotifier: chainService.OperationNotifier(),
|
|
}),
|
|
setupSvc: func(s *Service, msg *ethpb.SignedContributionAndProof) *Service {
|
|
s.cfg.StateGen = stategen.New(db)
|
|
s.cfg.DB = db
|
|
s.initCaches()
|
|
s.cfg.Chain = &mockChain.ChainService{
|
|
ValidatorsRoot: [32]byte{'A'},
|
|
Genesis: time.Now().Add(-time.Second * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Duration(msg.Message.Contribution.Slot)),
|
|
}
|
|
msg.Message.Contribution.BlockRoot = headRoot[:]
|
|
msg.Message.Contribution.AggregationBits.SetBitAt(1, true)
|
|
|
|
s.setSyncContributionIndexSlotSeen(1, 1, 1)
|
|
return s
|
|
},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
pid: "random",
|
|
topic: defaultTopic,
|
|
msg: ðpb.SignedContributionAndProof{
|
|
Message: ðpb.ContributionAndProof{
|
|
AggregatorIndex: 1,
|
|
Contribution: ðpb.SyncCommitteeContribution{
|
|
Slot: 1,
|
|
SubcommitteeIndex: 1,
|
|
BlockRoot: params.BeaconConfig().ZeroHash[:],
|
|
AggregationBits: bitfield.NewBitvector128(),
|
|
Signature: emptySig[:],
|
|
},
|
|
SelectionProof: emptySig[:],
|
|
},
|
|
Signature: emptySig[:],
|
|
}},
|
|
want: pubsub.ValidationIgnore,
|
|
},
|
|
{
|
|
name: "Invalid Subcommittee Index",
|
|
svc: NewService(context.Background(), &Config{
|
|
P2P: mockp2p.NewTestP2P(t),
|
|
InitialSync: &mockSync.Sync{IsSyncing: false},
|
|
Chain: chainService,
|
|
StateNotifier: chainService.StateNotifier(),
|
|
OperationNotifier: chainService.OperationNotifier(),
|
|
}),
|
|
setupSvc: func(s *Service, msg *ethpb.SignedContributionAndProof) *Service {
|
|
s.cfg.StateGen = stategen.New(db)
|
|
s.cfg.DB = db
|
|
s.initCaches()
|
|
s.cfg.Chain = &mockChain.ChainService{
|
|
ValidatorsRoot: [32]byte{'A'},
|
|
Genesis: time.Now().Add(-time.Second * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Duration(msg.Message.Contribution.Slot)),
|
|
}
|
|
msg.Message.Contribution.BlockRoot = headRoot[:]
|
|
msg.Message.Contribution.AggregationBits.SetBitAt(1, true)
|
|
msg.Message.Contribution.SubcommitteeIndex = 20
|
|
|
|
return s
|
|
},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
pid: "random",
|
|
topic: defaultTopic,
|
|
msg: ðpb.SignedContributionAndProof{
|
|
Message: ðpb.ContributionAndProof{
|
|
AggregatorIndex: 1,
|
|
Contribution: ðpb.SyncCommitteeContribution{
|
|
Slot: 1,
|
|
SubcommitteeIndex: 1,
|
|
BlockRoot: params.BeaconConfig().ZeroHash[:],
|
|
AggregationBits: bitfield.NewBitvector128(),
|
|
Signature: emptySig[:],
|
|
},
|
|
SelectionProof: emptySig[:],
|
|
},
|
|
Signature: emptySig[:],
|
|
}},
|
|
want: pubsub.ValidationReject,
|
|
},
|
|
{
|
|
name: "Invalid Selection Proof",
|
|
svc: NewService(context.Background(), &Config{
|
|
P2P: mockp2p.NewTestP2P(t),
|
|
InitialSync: &mockSync.Sync{IsSyncing: false},
|
|
Chain: chainService,
|
|
StateNotifier: chainService.StateNotifier(),
|
|
OperationNotifier: chainService.OperationNotifier(),
|
|
}),
|
|
setupSvc: func(s *Service, msg *ethpb.SignedContributionAndProof) *Service {
|
|
s.cfg.StateGen = stategen.New(db)
|
|
s.cfg.DB = db
|
|
s.initCaches()
|
|
s.cfg.Chain = &mockChain.ChainService{
|
|
ValidatorsRoot: [32]byte{'A'},
|
|
Genesis: time.Now().Add(-time.Second * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Duration(msg.Message.Contribution.Slot)),
|
|
}
|
|
msg.Message.Contribution.BlockRoot = headRoot[:]
|
|
incorrectProof := [96]byte{0xBB}
|
|
msg.Message.SelectionProof = incorrectProof[:]
|
|
msg.Message.Contribution.AggregationBits.SetBitAt(1, true)
|
|
|
|
return s
|
|
},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
pid: "random",
|
|
topic: defaultTopic,
|
|
msg: ðpb.SignedContributionAndProof{
|
|
Message: ðpb.ContributionAndProof{
|
|
AggregatorIndex: 1,
|
|
Contribution: ðpb.SyncCommitteeContribution{
|
|
Slot: 1,
|
|
SubcommitteeIndex: 1,
|
|
BlockRoot: params.BeaconConfig().ZeroHash[:],
|
|
AggregationBits: bitfield.NewBitvector128(),
|
|
Signature: emptySig[:],
|
|
},
|
|
SelectionProof: emptySig[:],
|
|
},
|
|
Signature: emptySig[:],
|
|
}},
|
|
want: pubsub.ValidationReject,
|
|
},
|
|
{
|
|
name: "Invalid Aggregator",
|
|
svc: NewService(context.Background(), &Config{
|
|
P2P: mockp2p.NewTestP2P(t),
|
|
InitialSync: &mockSync.Sync{IsSyncing: false},
|
|
Chain: chainService,
|
|
StateNotifier: chainService.StateNotifier(),
|
|
OperationNotifier: chainService.OperationNotifier(),
|
|
}),
|
|
setupSvc: func(s *Service, msg *ethpb.SignedContributionAndProof) *Service {
|
|
s.cfg.StateGen = stategen.New(db)
|
|
s.cfg.DB = db
|
|
s.initCaches()
|
|
s.cfg.Chain = &mockChain.ChainService{
|
|
ValidatorsRoot: [32]byte{'A'},
|
|
Genesis: time.Now().Add(-time.Second * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Duration(msg.Message.Contribution.Slot)),
|
|
}
|
|
msg.Message.Contribution.BlockRoot = headRoot[:]
|
|
hState, err := db.State(context.Background(), headRoot)
|
|
assert.NoError(t, err)
|
|
sc, err := hState.CurrentSyncCommittee()
|
|
assert.NoError(t, err)
|
|
for i := uint64(0); i < params.BeaconConfig().SyncCommitteeSubnetCount; i++ {
|
|
coms, err := altair.SyncSubCommitteePubkeys(sc, types.CommitteeIndex(i))
|
|
assert.NoError(t, err)
|
|
for _, p := range coms {
|
|
idx, ok := hState.ValidatorIndexByPubkey(bytesutil.ToBytes48(p))
|
|
assert.Equal(t, true, ok)
|
|
rt, err := syncSelectionProofSigningRoot(hState, core.PrevSlot(hState.Slot()), types.CommitteeIndex(i))
|
|
assert.NoError(t, err)
|
|
sig := keys[idx].Sign(rt[:])
|
|
isAggregator, err := altair.IsSyncCommitteeAggregator(sig.Marshal())
|
|
require.NoError(t, err)
|
|
if !isAggregator {
|
|
msg.Message.AggregatorIndex = idx
|
|
break
|
|
}
|
|
}
|
|
}
|
|
msg.Message.Contribution.AggregationBits.SetBitAt(1, true)
|
|
return s
|
|
},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
pid: "random",
|
|
topic: defaultTopic,
|
|
msg: ðpb.SignedContributionAndProof{
|
|
Message: ðpb.ContributionAndProof{
|
|
AggregatorIndex: 1,
|
|
Contribution: ðpb.SyncCommitteeContribution{
|
|
Slot: 1,
|
|
SubcommitteeIndex: 1,
|
|
BlockRoot: params.BeaconConfig().ZeroHash[:],
|
|
AggregationBits: bitfield.NewBitvector128(),
|
|
Signature: emptySig[:],
|
|
},
|
|
SelectionProof: emptySig[:],
|
|
},
|
|
Signature: emptySig[:],
|
|
}},
|
|
want: pubsub.ValidationReject,
|
|
},
|
|
{
|
|
name: "Failed Selection Proof Verification ",
|
|
svc: NewService(context.Background(), &Config{
|
|
P2P: mockp2p.NewTestP2P(t),
|
|
InitialSync: &mockSync.Sync{IsSyncing: false},
|
|
Chain: chainService,
|
|
StateNotifier: chainService.StateNotifier(),
|
|
OperationNotifier: chainService.OperationNotifier(),
|
|
}),
|
|
setupSvc: func(s *Service, msg *ethpb.SignedContributionAndProof) *Service {
|
|
s.cfg.StateGen = stategen.New(db)
|
|
s.cfg.DB = db
|
|
msg.Message.Contribution.BlockRoot = headRoot[:]
|
|
hState, err := db.State(context.Background(), headRoot)
|
|
assert.NoError(t, err)
|
|
sc, err := hState.CurrentSyncCommittee()
|
|
assert.NoError(t, err)
|
|
for i := uint64(0); i < params.BeaconConfig().SyncCommitteeSubnetCount; i++ {
|
|
coms, err := altair.SyncSubCommitteePubkeys(sc, types.CommitteeIndex(i))
|
|
assert.NoError(t, err)
|
|
for _, p := range coms {
|
|
idx, ok := hState.ValidatorIndexByPubkey(bytesutil.ToBytes48(p))
|
|
assert.Equal(t, true, ok)
|
|
rt, err := syncSelectionProofSigningRoot(hState, core.PrevSlot(hState.Slot()), types.CommitteeIndex(i))
|
|
assert.NoError(t, err)
|
|
sig := keys[idx].Sign(rt[:])
|
|
isAggregator, err := altair.IsSyncCommitteeAggregator(sig.Marshal())
|
|
require.NoError(t, err)
|
|
if !isAggregator {
|
|
msg.Message.AggregatorIndex = idx
|
|
break
|
|
}
|
|
}
|
|
}
|
|
subCommitteeSize := params.BeaconConfig().SyncCommitteeSize / params.BeaconConfig().SyncCommitteeSubnetCount
|
|
s.cfg.Chain = &mockChain.ChainService{
|
|
ValidatorsRoot: [32]byte{'A'},
|
|
Genesis: time.Now().Add(-time.Second * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Duration(msg.Message.Contribution.Slot)),
|
|
CurrentSyncCommitteeIndices: []types.CommitteeIndex{types.CommitteeIndex(msg.Message.Contribution.SubcommitteeIndex * subCommitteeSize)},
|
|
}
|
|
msg.Message.Contribution.AggregationBits.SetBitAt(1, true)
|
|
|
|
s.initCaches()
|
|
return s
|
|
},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
pid: "random",
|
|
topic: defaultTopic,
|
|
msg: ðpb.SignedContributionAndProof{
|
|
Message: ðpb.ContributionAndProof{
|
|
AggregatorIndex: 1,
|
|
Contribution: ðpb.SyncCommitteeContribution{
|
|
Slot: 1,
|
|
SubcommitteeIndex: 1,
|
|
BlockRoot: params.BeaconConfig().ZeroHash[:],
|
|
AggregationBits: bitfield.NewBitvector128(),
|
|
Signature: emptySig[:],
|
|
},
|
|
SelectionProof: emptySig[:],
|
|
},
|
|
Signature: emptySig[:],
|
|
}},
|
|
want: pubsub.ValidationReject,
|
|
},
|
|
{
|
|
name: "Invalid Proof Signature",
|
|
svc: NewService(context.Background(), &Config{
|
|
P2P: mockp2p.NewTestP2P(t),
|
|
InitialSync: &mockSync.Sync{IsSyncing: false},
|
|
Chain: chainService,
|
|
StateNotifier: chainService.StateNotifier(),
|
|
OperationNotifier: chainService.OperationNotifier(),
|
|
}),
|
|
setupSvc: func(s *Service, msg *ethpb.SignedContributionAndProof) *Service {
|
|
s.cfg.StateGen = stategen.New(db)
|
|
s.cfg.DB = db
|
|
s.cfg.Chain = chainService
|
|
msg.Message.Contribution.BlockRoot = headRoot[:]
|
|
hState, err := db.State(context.Background(), headRoot)
|
|
assert.NoError(t, err)
|
|
sc, err := hState.CurrentSyncCommittee()
|
|
assert.NoError(t, err)
|
|
var pubkey []byte
|
|
for i := uint64(0); i < params.BeaconConfig().SyncCommitteeSubnetCount; i++ {
|
|
coms, err := altair.SyncSubCommitteePubkeys(sc, types.CommitteeIndex(i))
|
|
assert.NoError(t, err)
|
|
for _, p := range coms {
|
|
idx, ok := hState.ValidatorIndexByPubkey(bytesutil.ToBytes48(p))
|
|
assert.Equal(t, true, ok)
|
|
rt, err := syncSelectionProofSigningRoot(hState, core.PrevSlot(hState.Slot()), types.CommitteeIndex(i))
|
|
assert.NoError(t, err)
|
|
sig := keys[idx].Sign(rt[:])
|
|
isAggregator, err := altair.IsSyncCommitteeAggregator(sig.Marshal())
|
|
require.NoError(t, err)
|
|
if isAggregator {
|
|
infiniteSig := [96]byte{0xC0}
|
|
pubkey = keys[idx].PublicKey().Marshal()
|
|
msg.Message.AggregatorIndex = idx
|
|
msg.Message.SelectionProof = sig.Marshal()
|
|
msg.Message.Contribution.Slot = core.PrevSlot(hState.Slot())
|
|
msg.Message.Contribution.SubcommitteeIndex = i
|
|
msg.Message.Contribution.Signature = infiniteSig[:]
|
|
msg.Message.Contribution.BlockRoot = headRoot[:]
|
|
msg.Message.Contribution.AggregationBits = bitfield.NewBitvector128()
|
|
msg.Message.Contribution.AggregationBits.SetBitAt(1, true)
|
|
msg.Signature = infiniteSig[:]
|
|
break
|
|
}
|
|
}
|
|
}
|
|
d, err := helpers.Domain(hState.Fork(), core.SlotToEpoch(core.PrevSlot(hState.Slot())), params.BeaconConfig().DomainSyncCommitteeSelectionProof, hState.GenesisValidatorRoot())
|
|
require.NoError(t, err)
|
|
subCommitteeSize := params.BeaconConfig().SyncCommitteeSize / params.BeaconConfig().SyncCommitteeSubnetCount
|
|
s.cfg.Chain = &mockChain.ChainService{
|
|
ValidatorsRoot: [32]byte{'A'},
|
|
Genesis: time.Now().Add(-time.Second * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Duration(msg.Message.Contribution.Slot)),
|
|
CurrentSyncCommitteeIndices: []types.CommitteeIndex{types.CommitteeIndex(msg.Message.Contribution.SubcommitteeIndex * subCommitteeSize)},
|
|
PublicKey: bytesutil.ToBytes48(pubkey),
|
|
SyncSelectionProofDomain: d,
|
|
}
|
|
|
|
s.initCaches()
|
|
return s
|
|
},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
pid: "random",
|
|
topic: defaultTopic,
|
|
msg: ðpb.SignedContributionAndProof{
|
|
Message: ðpb.ContributionAndProof{
|
|
AggregatorIndex: 1,
|
|
Contribution: ðpb.SyncCommitteeContribution{
|
|
Slot: 1,
|
|
SubcommitteeIndex: 1,
|
|
BlockRoot: params.BeaconConfig().ZeroHash[:],
|
|
AggregationBits: bitfield.NewBitvector128(),
|
|
Signature: emptySig[:],
|
|
},
|
|
SelectionProof: emptySig[:],
|
|
},
|
|
Signature: emptySig[:],
|
|
}},
|
|
want: pubsub.ValidationReject,
|
|
},
|
|
{
|
|
name: "Invalid Sync Aggregate",
|
|
svc: NewService(context.Background(), &Config{
|
|
P2P: mockp2p.NewTestP2P(t),
|
|
InitialSync: &mockSync.Sync{IsSyncing: false},
|
|
Chain: chainService,
|
|
StateNotifier: chainService.StateNotifier(),
|
|
OperationNotifier: chainService.OperationNotifier(),
|
|
}),
|
|
setupSvc: func(s *Service, msg *ethpb.SignedContributionAndProof) *Service {
|
|
s.cfg.StateGen = stategen.New(db)
|
|
s.cfg.DB = db
|
|
msg.Message.Contribution.BlockRoot = headRoot[:]
|
|
hState, err := db.State(context.Background(), headRoot)
|
|
assert.NoError(t, err)
|
|
sc, err := hState.CurrentSyncCommittee()
|
|
assert.NoError(t, err)
|
|
for i := uint64(0); i < params.BeaconConfig().SyncCommitteeSubnetCount; i++ {
|
|
coms, err := altair.SyncSubCommitteePubkeys(sc, types.CommitteeIndex(i))
|
|
assert.NoError(t, err)
|
|
for _, p := range coms {
|
|
idx, ok := hState.ValidatorIndexByPubkey(bytesutil.ToBytes48(p))
|
|
assert.Equal(t, true, ok)
|
|
rt, err := syncSelectionProofSigningRoot(hState, core.PrevSlot(hState.Slot()), types.CommitteeIndex(i))
|
|
assert.NoError(t, err)
|
|
sig := keys[idx].Sign(rt[:])
|
|
isAggregator, err := altair.IsSyncCommitteeAggregator(sig.Marshal())
|
|
require.NoError(t, err)
|
|
if isAggregator {
|
|
infiniteSig := [96]byte{0xC0}
|
|
junkRoot := [32]byte{'A'}
|
|
badSig := keys[idx].Sign(junkRoot[:])
|
|
msg.Message.AggregatorIndex = idx
|
|
msg.Message.SelectionProof = sig.Marshal()
|
|
msg.Message.Contribution.Slot = core.PrevSlot(hState.Slot())
|
|
msg.Message.Contribution.SubcommitteeIndex = i
|
|
msg.Message.Contribution.Signature = badSig.Marshal()
|
|
msg.Message.Contribution.BlockRoot = headRoot[:]
|
|
msg.Message.Contribution.AggregationBits = bitfield.NewBitvector128()
|
|
msg.Message.Contribution.AggregationBits.SetBitAt(1, true)
|
|
msg.Signature = infiniteSig[:]
|
|
|
|
d, err := helpers.Domain(hState.Fork(), core.SlotToEpoch(core.PrevSlot(hState.Slot())), params.BeaconConfig().DomainContributionAndProof, hState.GenesisValidatorRoot())
|
|
assert.NoError(t, err)
|
|
sigRoot, err := helpers.ComputeSigningRoot(msg.Message, d)
|
|
assert.NoError(t, err)
|
|
contrSig := keys[idx].Sign(sigRoot[:])
|
|
|
|
msg.Signature = contrSig.Marshal()
|
|
break
|
|
}
|
|
}
|
|
}
|
|
s.cfg.Chain = &mockChain.ChainService{
|
|
ValidatorsRoot: [32]byte{'A'},
|
|
Genesis: time.Now().Add(-time.Second * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Duration(msg.Message.Contribution.Slot)),
|
|
CurrentSyncCommitteeIndices: []types.CommitteeIndex{1},
|
|
}
|
|
|
|
s.initCaches()
|
|
return s
|
|
},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
pid: "random",
|
|
topic: defaultTopic,
|
|
msg: ðpb.SignedContributionAndProof{
|
|
Message: ðpb.ContributionAndProof{
|
|
AggregatorIndex: 1,
|
|
Contribution: ðpb.SyncCommitteeContribution{
|
|
Slot: 1,
|
|
SubcommitteeIndex: 1,
|
|
BlockRoot: params.BeaconConfig().ZeroHash[:],
|
|
AggregationBits: bitfield.NewBitvector128(),
|
|
Signature: emptySig[:],
|
|
},
|
|
SelectionProof: emptySig[:],
|
|
},
|
|
Signature: emptySig[:],
|
|
}},
|
|
want: pubsub.ValidationReject,
|
|
},
|
|
{
|
|
name: "Invalid Signed Sync Contribution And Proof - Zero Bits Set",
|
|
svc: NewService(context.Background(), &Config{
|
|
P2P: mockp2p.NewTestP2P(t),
|
|
InitialSync: &mockSync.Sync{IsSyncing: false},
|
|
Chain: chainService,
|
|
StateNotifier: chainService.StateNotifier(),
|
|
OperationNotifier: chainService.OperationNotifier(),
|
|
}),
|
|
setupSvc: func(s *Service, msg *ethpb.SignedContributionAndProof) *Service {
|
|
s.cfg.StateGen = stategen.New(db)
|
|
msg.Message.Contribution.BlockRoot = headRoot[:]
|
|
s.cfg.DB = db
|
|
hState, err := db.State(context.Background(), headRoot)
|
|
assert.NoError(t, err)
|
|
sc, err := hState.CurrentSyncCommittee()
|
|
assert.NoError(t, err)
|
|
cd, err := helpers.Domain(hState.Fork(), core.SlotToEpoch(core.PrevSlot(hState.Slot())), params.BeaconConfig().DomainContributionAndProof, hState.GenesisValidatorRoot())
|
|
assert.NoError(t, err)
|
|
for i := uint64(0); i < params.BeaconConfig().SyncCommitteeSubnetCount; i++ {
|
|
coms, err := altair.SyncSubCommitteePubkeys(sc, types.CommitteeIndex(i))
|
|
assert.NoError(t, err)
|
|
for _, p := range coms {
|
|
idx, ok := hState.ValidatorIndexByPubkey(bytesutil.ToBytes48(p))
|
|
assert.Equal(t, true, ok)
|
|
rt, err := syncSelectionProofSigningRoot(hState, core.PrevSlot(hState.Slot()), types.CommitteeIndex(i))
|
|
assert.NoError(t, err)
|
|
sig := keys[idx].Sign(rt[:])
|
|
isAggregator, err := altair.IsSyncCommitteeAggregator(sig.Marshal())
|
|
require.NoError(t, err)
|
|
if isAggregator {
|
|
infiniteSig := [96]byte{0xC0}
|
|
msg.Message.AggregatorIndex = idx
|
|
msg.Message.SelectionProof = sig.Marshal()
|
|
msg.Message.Contribution.Slot = core.PrevSlot(hState.Slot())
|
|
msg.Message.Contribution.SubcommitteeIndex = i
|
|
msg.Message.Contribution.Signature = infiniteSig[:]
|
|
msg.Message.Contribution.BlockRoot = headRoot[:]
|
|
msg.Message.Contribution.AggregationBits = bitfield.NewBitvector128()
|
|
sigRoot, err := helpers.ComputeSigningRoot(msg.Message, cd)
|
|
assert.NoError(t, err)
|
|
contrSig := keys[idx].Sign(sigRoot[:])
|
|
|
|
msg.Signature = contrSig.Marshal()
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
d, err := helpers.Domain(hState.Fork(), core.SlotToEpoch(core.PrevSlot(hState.Slot())), params.BeaconConfig().DomainSyncCommitteeSelectionProof, hState.GenesisValidatorRoot())
|
|
require.NoError(t, err)
|
|
subCommitteeSize := params.BeaconConfig().SyncCommitteeSize / params.BeaconConfig().SyncCommitteeSubnetCount
|
|
s.cfg.Chain = &mockChain.ChainService{
|
|
ValidatorsRoot: [32]byte{'A'},
|
|
Genesis: time.Now().Add(-time.Second * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Duration(msg.Message.Contribution.Slot)),
|
|
CurrentSyncCommitteeIndices: []types.CommitteeIndex{types.CommitteeIndex(msg.Message.Contribution.SubcommitteeIndex * subCommitteeSize)},
|
|
PublicKey: bytesutil.ToBytes48(keys[msg.Message.AggregatorIndex].PublicKey().Marshal()),
|
|
SyncSelectionProofDomain: d,
|
|
SyncContributionProofDomain: cd,
|
|
SyncCommitteeDomain: make([]byte, 32),
|
|
}
|
|
s.initCaches()
|
|
return s
|
|
},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
pid: "random",
|
|
topic: defaultTopic,
|
|
msg: ðpb.SignedContributionAndProof{
|
|
Message: ðpb.ContributionAndProof{
|
|
AggregatorIndex: 1,
|
|
Contribution: ðpb.SyncCommitteeContribution{
|
|
Slot: 1,
|
|
SubcommitteeIndex: 1,
|
|
BlockRoot: params.BeaconConfig().ZeroHash[:],
|
|
AggregationBits: bitfield.NewBitvector128(),
|
|
Signature: emptySig[:],
|
|
},
|
|
SelectionProof: emptySig[:],
|
|
},
|
|
Signature: emptySig[:],
|
|
}},
|
|
want: pubsub.ValidationReject,
|
|
},
|
|
{
|
|
name: "Valid Signed Sync Contribution And Proof - Single Bit Set",
|
|
svc: NewService(context.Background(), &Config{
|
|
P2P: mockp2p.NewTestP2P(t),
|
|
InitialSync: &mockSync.Sync{IsSyncing: false},
|
|
Chain: chainService,
|
|
StateNotifier: chainService.StateNotifier(),
|
|
OperationNotifier: chainService.OperationNotifier(),
|
|
}),
|
|
setupSvc: func(s *Service, msg *ethpb.SignedContributionAndProof) *Service {
|
|
s.cfg.StateGen = stategen.New(db)
|
|
msg.Message.Contribution.BlockRoot = headRoot[:]
|
|
s.cfg.DB = db
|
|
hState, err := db.State(context.Background(), headRoot)
|
|
assert.NoError(t, err)
|
|
sc, err := hState.CurrentSyncCommittee()
|
|
assert.NoError(t, err)
|
|
cd, err := helpers.Domain(hState.Fork(), core.SlotToEpoch(core.PrevSlot(hState.Slot())), params.BeaconConfig().DomainContributionAndProof, hState.GenesisValidatorRoot())
|
|
assert.NoError(t, err)
|
|
d, err := helpers.Domain(hState.Fork(), core.SlotToEpoch(hState.Slot()), params.BeaconConfig().DomainSyncCommittee, hState.GenesisValidatorRoot())
|
|
assert.NoError(t, err)
|
|
var pubkeys [][]byte
|
|
for i := uint64(0); i < params.BeaconConfig().SyncCommitteeSubnetCount; i++ {
|
|
coms, err := altair.SyncSubCommitteePubkeys(sc, types.CommitteeIndex(i))
|
|
pubkeys = coms
|
|
assert.NoError(t, err)
|
|
for _, p := range coms {
|
|
idx, ok := hState.ValidatorIndexByPubkey(bytesutil.ToBytes48(p))
|
|
assert.Equal(t, true, ok)
|
|
rt, err := syncSelectionProofSigningRoot(hState, core.PrevSlot(hState.Slot()), types.CommitteeIndex(i))
|
|
assert.NoError(t, err)
|
|
sig := keys[idx].Sign(rt[:])
|
|
isAggregator, err := altair.IsSyncCommitteeAggregator(sig.Marshal())
|
|
require.NoError(t, err)
|
|
if isAggregator {
|
|
msg.Message.AggregatorIndex = idx
|
|
msg.Message.SelectionProof = sig.Marshal()
|
|
msg.Message.Contribution.Slot = core.PrevSlot(hState.Slot())
|
|
msg.Message.Contribution.SubcommitteeIndex = i
|
|
msg.Message.Contribution.BlockRoot = headRoot[:]
|
|
msg.Message.Contribution.AggregationBits = bitfield.NewBitvector128()
|
|
// Only Sign for 1 validator.
|
|
rawBytes := p2ptypes.SSZBytes(headRoot[:])
|
|
sigRoot, err := helpers.ComputeSigningRoot(&rawBytes, d)
|
|
assert.NoError(t, err)
|
|
valIdx, ok := hState.ValidatorIndexByPubkey(bytesutil.ToBytes48(coms[0]))
|
|
assert.Equal(t, true, ok)
|
|
sig = keys[valIdx].Sign(sigRoot[:])
|
|
msg.Message.Contribution.AggregationBits.SetBitAt(uint64(0), true)
|
|
msg.Message.Contribution.Signature = sig.Marshal()
|
|
|
|
sigRoot, err = helpers.ComputeSigningRoot(msg.Message, cd)
|
|
assert.NoError(t, err)
|
|
contrSig := keys[idx].Sign(sigRoot[:])
|
|
msg.Signature = contrSig.Marshal()
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
pd, err := helpers.Domain(hState.Fork(), core.SlotToEpoch(core.PrevSlot(hState.Slot())), params.BeaconConfig().DomainSyncCommitteeSelectionProof, hState.GenesisValidatorRoot())
|
|
require.NoError(t, err)
|
|
subCommitteeSize := params.BeaconConfig().SyncCommitteeSize / params.BeaconConfig().SyncCommitteeSubnetCount
|
|
s.cfg.Chain = &mockChain.ChainService{
|
|
ValidatorsRoot: [32]byte{'A'},
|
|
Genesis: time.Now().Add(-time.Second * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Duration(msg.Message.Contribution.Slot)),
|
|
CurrentSyncCommitteeIndices: []types.CommitteeIndex{types.CommitteeIndex(msg.Message.Contribution.SubcommitteeIndex * subCommitteeSize)},
|
|
PublicKey: bytesutil.ToBytes48(keys[msg.Message.AggregatorIndex].PublicKey().Marshal()),
|
|
SyncSelectionProofDomain: pd,
|
|
SyncContributionProofDomain: cd,
|
|
SyncCommitteeDomain: d,
|
|
SyncCommitteePubkeys: pubkeys,
|
|
}
|
|
s.initCaches()
|
|
return s
|
|
},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
pid: "random",
|
|
topic: defaultTopic,
|
|
msg: ðpb.SignedContributionAndProof{
|
|
Message: ðpb.ContributionAndProof{
|
|
AggregatorIndex: 1,
|
|
Contribution: ðpb.SyncCommitteeContribution{
|
|
Slot: 1,
|
|
SubcommitteeIndex: 1,
|
|
BlockRoot: params.BeaconConfig().ZeroHash[:],
|
|
AggregationBits: bitfield.NewBitvector128(),
|
|
Signature: emptySig[:],
|
|
},
|
|
SelectionProof: emptySig[:],
|
|
},
|
|
Signature: emptySig[:],
|
|
}},
|
|
want: pubsub.ValidationAccept,
|
|
},
|
|
{
|
|
name: "Valid Signed Sync Contribution And Proof with Multiple Signatures",
|
|
svc: NewService(context.Background(), &Config{
|
|
P2P: mockp2p.NewTestP2P(t),
|
|
InitialSync: &mockSync.Sync{IsSyncing: false},
|
|
Chain: chainService,
|
|
StateNotifier: chainService.StateNotifier(),
|
|
OperationNotifier: chainService.OperationNotifier(),
|
|
}),
|
|
setupSvc: func(s *Service, msg *ethpb.SignedContributionAndProof) *Service {
|
|
s.cfg.StateGen = stategen.New(db)
|
|
msg.Message.Contribution.BlockRoot = headRoot[:]
|
|
s.cfg.DB = db
|
|
hState, err := db.State(context.Background(), headRoot)
|
|
assert.NoError(t, err)
|
|
sc, err := hState.CurrentSyncCommittee()
|
|
assert.NoError(t, err)
|
|
cd, err := helpers.Domain(hState.Fork(), core.SlotToEpoch(core.PrevSlot(hState.Slot())), params.BeaconConfig().DomainContributionAndProof, hState.GenesisValidatorRoot())
|
|
assert.NoError(t, err)
|
|
d, err := helpers.Domain(hState.Fork(), core.SlotToEpoch(hState.Slot()), params.BeaconConfig().DomainSyncCommittee, hState.GenesisValidatorRoot())
|
|
assert.NoError(t, err)
|
|
var pubkeys [][]byte
|
|
for i := uint64(0); i < params.BeaconConfig().SyncCommitteeSubnetCount; i++ {
|
|
coms, err := altair.SyncSubCommitteePubkeys(sc, types.CommitteeIndex(i))
|
|
pubkeys = coms
|
|
assert.NoError(t, err)
|
|
for _, p := range coms {
|
|
idx, ok := hState.ValidatorIndexByPubkey(bytesutil.ToBytes48(p))
|
|
assert.Equal(t, true, ok)
|
|
rt, err := syncSelectionProofSigningRoot(hState, core.PrevSlot(hState.Slot()), types.CommitteeIndex(i))
|
|
assert.NoError(t, err)
|
|
sig := keys[idx].Sign(rt[:])
|
|
isAggregator, err := altair.IsSyncCommitteeAggregator(sig.Marshal())
|
|
require.NoError(t, err)
|
|
if isAggregator {
|
|
msg.Message.AggregatorIndex = idx
|
|
msg.Message.SelectionProof = sig.Marshal()
|
|
msg.Message.Contribution.Slot = core.PrevSlot(hState.Slot())
|
|
msg.Message.Contribution.SubcommitteeIndex = i
|
|
msg.Message.Contribution.BlockRoot = headRoot[:]
|
|
msg.Message.Contribution.AggregationBits = bitfield.NewBitvector128()
|
|
rawBytes := p2ptypes.SSZBytes(headRoot[:])
|
|
sigRoot, err := helpers.ComputeSigningRoot(&rawBytes, d)
|
|
assert.NoError(t, err)
|
|
sigs := []bls.Signature{}
|
|
for i, p2 := range coms {
|
|
idx, ok := hState.ValidatorIndexByPubkey(bytesutil.ToBytes48(p2))
|
|
assert.Equal(t, true, ok)
|
|
sig := keys[idx].Sign(sigRoot[:])
|
|
sigs = append(sigs, sig)
|
|
msg.Message.Contribution.AggregationBits.SetBitAt(uint64(i), true)
|
|
}
|
|
msg.Message.Contribution.Signature = bls.AggregateSignatures(sigs).Marshal()
|
|
sigRoot, err = helpers.ComputeSigningRoot(msg.Message, cd)
|
|
assert.NoError(t, err)
|
|
contrSig := keys[idx].Sign(sigRoot[:])
|
|
msg.Signature = contrSig.Marshal()
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
pd, err := helpers.Domain(hState.Fork(), core.SlotToEpoch(core.PrevSlot(hState.Slot())), params.BeaconConfig().DomainSyncCommitteeSelectionProof, hState.GenesisValidatorRoot())
|
|
require.NoError(t, err)
|
|
subCommitteeSize := params.BeaconConfig().SyncCommitteeSize / params.BeaconConfig().SyncCommitteeSubnetCount
|
|
s.cfg.Chain = &mockChain.ChainService{
|
|
ValidatorsRoot: [32]byte{'A'},
|
|
Genesis: time.Now().Add(-time.Second * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Duration(msg.Message.Contribution.Slot)),
|
|
CurrentSyncCommitteeIndices: []types.CommitteeIndex{types.CommitteeIndex(msg.Message.Contribution.SubcommitteeIndex * subCommitteeSize)},
|
|
PublicKey: bytesutil.ToBytes48(keys[msg.Message.AggregatorIndex].PublicKey().Marshal()),
|
|
SyncSelectionProofDomain: pd,
|
|
SyncContributionProofDomain: cd,
|
|
SyncCommitteeDomain: d,
|
|
SyncCommitteePubkeys: pubkeys,
|
|
}
|
|
|
|
s.initCaches()
|
|
return s
|
|
},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
pid: "random",
|
|
topic: defaultTopic,
|
|
msg: ðpb.SignedContributionAndProof{
|
|
Message: ðpb.ContributionAndProof{
|
|
AggregatorIndex: 1,
|
|
Contribution: ðpb.SyncCommitteeContribution{
|
|
Slot: 1,
|
|
SubcommitteeIndex: 1,
|
|
BlockRoot: params.BeaconConfig().ZeroHash[:],
|
|
AggregationBits: bitfield.NewBitvector128(),
|
|
Signature: emptySig[:],
|
|
},
|
|
SelectionProof: emptySig[:],
|
|
},
|
|
Signature: emptySig[:],
|
|
}},
|
|
want: pubsub.ValidationAccept,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tt.svc = tt.setupSvc(tt.svc, tt.args.msg)
|
|
marshalledObj, err := tt.args.msg.MarshalSSZ()
|
|
assert.NoError(t, err)
|
|
marshalledObj = snappy.Encode(nil, marshalledObj)
|
|
msg := &pubsub.Message{
|
|
Message: &pubsub_pb.Message{
|
|
Data: marshalledObj,
|
|
Topic: &tt.args.topic,
|
|
},
|
|
ReceivedFrom: "",
|
|
ValidatorData: nil,
|
|
}
|
|
if got := tt.svc.validateSyncContributionAndProof(tt.args.ctx, tt.args.pid, msg); got != tt.want {
|
|
t.Errorf("validateSyncContributionAndProof() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func fillUpBlocksAndState(ctx context.Context, t *testing.T, beaconDB db.Database) ([32]byte, []bls.SecretKey) {
|
|
gs, keys := testutil.DeterministicGenesisStateAltair(t, 64)
|
|
sCom, err := altair.NextSyncCommittee(ctx, gs)
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, gs.SetCurrentSyncCommittee(sCom))
|
|
assert.NoError(t, beaconDB.SaveGenesisData(context.Background(), gs))
|
|
|
|
testState := gs.Copy()
|
|
hRoot := [32]byte{}
|
|
for i := types.Slot(1); i <= params.BeaconConfig().SlotsPerEpoch; i++ {
|
|
blk, err := testutil.GenerateFullBlockAltair(testState, keys, testutil.DefaultBlockGenConfig(), i)
|
|
require.NoError(t, err)
|
|
r, err := blk.Block.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
wsb, err := wrapper.WrappedAltairSignedBeaconBlock(blk)
|
|
require.NoError(t, err)
|
|
_, testState, err = transition.ExecuteStateTransitionNoVerifyAnySig(ctx, testState, wsb)
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, beaconDB.SaveBlock(ctx, wsb))
|
|
assert.NoError(t, beaconDB.SaveStateSummary(ctx, ðpb.StateSummary{Slot: i, Root: r[:]}))
|
|
assert.NoError(t, beaconDB.SaveState(ctx, testState, r))
|
|
require.NoError(t, beaconDB.SaveHeadBlockRoot(ctx, r))
|
|
hRoot = r
|
|
}
|
|
return hRoot, keys
|
|
}
|
|
|
|
func syncSelectionProofSigningRoot(st state.BeaconState, slot types.Slot, comIdx types.CommitteeIndex) ([32]byte, error) {
|
|
dom, err := helpers.Domain(st.Fork(), core.SlotToEpoch(slot), params.BeaconConfig().DomainSyncCommitteeSelectionProof, st.GenesisValidatorRoot())
|
|
if err != nil {
|
|
return [32]byte{}, err
|
|
}
|
|
selectionData := ðpb.SyncAggregatorSelectionData{Slot: slot, SubcommitteeIndex: uint64(comIdx)}
|
|
return helpers.ComputeSigningRoot(selectionData, dom)
|
|
}
|