Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
xenowits committed Nov 3, 2022
1 parent 2260111 commit 5b64b93
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 27 deletions.
14 changes: 9 additions & 5 deletions app/simnet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ func TestSimnetNoNetwork_WithSyncCommitteeMockVCs(t *testing.T) {
args := newSimnetArgs(t)
args.BMockOpts = append(args.BMockOpts, beaconmock.WithNoAttesterDuties())
args.BMockOpts = append(args.BMockOpts, beaconmock.WithNoProposerDuties())
expect := newSimnetExpect(args.N, core.DutySyncMessage)
expect := newSimnetExpect(args.N, core.DutyPrepareSyncContribution, core.DutySyncMessage)
testSimnet(t, args, expect)
}

Expand All @@ -202,11 +202,14 @@ type simnetArgs struct {
func newSimnetArgs(t *testing.T) simnetArgs {
t.Helper()

const n = 3
lock, p2pKeys, secretShares := cluster.NewForT(t, 1, n, n, 99)
const (
n = 3
numDVs = 1
)
lock, p2pKeys, secretShares := cluster.NewForT(t, numDVs, n, n, 99)

var secrets []*bls_sig.SecretKey
for _, share := range secretShares[0] {
for _, share := range secretShares[0] { // Using only index 0 since we only have 1 DV.
secret, err := tblsconv.ShareToSecret(share)
require.NoError(t, err)
secrets = append(secrets, secret)
Expand Down Expand Up @@ -271,7 +274,7 @@ func newSimnetExpect(count int, duties ...core.DutyType) simnetExpect {
}
}

// testSimnet spins of a simnet cluster or N charon nodes connected via in-memory transports.
// testSimnet spins up a simnet cluster of N charon nodes connected via in-memory transports.
// It asserts successful end-2-end attestation broadcast from all nodes for 2 slots.
func testSimnet(t *testing.T, args simnetArgs, expect simnetExpect) {
t.Helper()
Expand Down Expand Up @@ -430,6 +433,7 @@ var (
)

// startTeku starts a teku validator client for the provided node and returns updated args.
// See https://docs.teku.consensys.net/en/latest/Reference/CLI/CLI-Syntax/.
func startTeku(t *testing.T, args simnetArgs, node int, cmd tekuCmd) simnetArgs {
t.Helper()

Expand Down
2 changes: 1 addition & 1 deletion app/vmock.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ func newVMockSigner(conf Config, pubshares []eth2p0.BLSPubKey) (validatormock.Si
return signer, nil
}

// handleVMockDuty calls appropriate validator mock function to attestation and block proposal.
// handleVMockDuty calls appropriate validator mock function for attestations, block proposals and sync committee contributions.
func handleVMockDuty(ctx context.Context, duty core.Duty, eth2Cl eth2wrap.Client,
signer validatormock.SignFunc, pubshares []eth2p0.BLSPubKey, attester *validatormock.SlotAttester,
syncCommMember *validatormock.SyncCommMember,
Expand Down
2 changes: 1 addition & 1 deletion testutil/validatormock/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ func wait(ctx context.Context, chs ...chan struct{}) {
}
}

// activeValidators returns the head active validators for the public keys.
// activeValidators returns the head active validators from the provided public keys.
func activeValidators(ctx context.Context, eth2Cl eth2wrap.Client,
pubkeys []eth2p0.BLSPubKey,
) (map[eth2p0.ValidatorIndex]*eth2v1.Validator, error) {
Expand Down
108 changes: 89 additions & 19 deletions testutil/validatormock/synccomm.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ import (
"github.com/obolnetwork/charon/app/log"
"github.com/obolnetwork/charon/app/z"
"github.com/obolnetwork/charon/eth2util"
"github.com/obolnetwork/charon/eth2util/eth2exp"
"github.com/obolnetwork/charon/eth2util/signing"
)

type (
syncDuties []*eth2v1.SyncCommitteeDuty
syncSelections []any
syncSelections []*eth2exp.SyncCommitteeSelection
subCommittees map[eth2p0.ValidatorIndex][]eth2p0.CommitteeIndex // Sync subcommittees to which validators are assigned.
)

func NewSyncCommMember(eth2Cl eth2wrap.Client, epoch eth2p0.Epoch, signFunc SignFunc, pubkeys []eth2p0.BLSPubKey) *SyncCommMember {
Expand All @@ -42,6 +44,7 @@ func NewSyncCommMember(eth2Cl eth2wrap.Client, epoch eth2p0.Epoch, signFunc Sign
pubkeys: pubkeys,
signFunc: signFunc,
dutiesOK: make(chan struct{}),
subcomms: make(map[eth2p0.ValidatorIndex][]eth2p0.CommitteeIndex),
selections: make(map[eth2p0.Slot]syncSelections),
selectionsOK: make(map[eth2p0.Slot]chan struct{}),
}
Expand All @@ -58,6 +61,7 @@ type SyncCommMember struct {
// Mutable state
mu sync.Mutex
vals validators
subcomms subCommittees
duties syncDuties
dutiesOK chan struct{}
selections map[eth2p0.Slot]syncSelections
Expand All @@ -74,7 +78,7 @@ func (s *SyncCommMember) setSelections(slot eth2p0.Slot, selections syncSelectio

s.selections[slot] = selections

// Mark selections as done
// Mark selections as done.
ch, ok := s.selectionsOK[slot]
if !ok {
ch = make(chan struct{})
Expand All @@ -91,6 +95,8 @@ func (s *SyncCommMember) getSelections(slot eth2p0.Slot) syncSelections {
return s.selections[slot]
}

// getSelectionsOK returns a channel for sync committee selections. When this channel is closed, it means
// that selections are ready for this slot.
func (s *SyncCommMember) getSelectionsOK(slot eth2p0.Slot) chan struct{} {
s.mu.Lock()
defer s.mu.Unlock()
Expand All @@ -104,14 +110,19 @@ func (s *SyncCommMember) getSelectionsOK(slot eth2p0.Slot) chan struct{} {
return ch
}

// PrepareEpoch stores sync committee attDuties and submits sync committee subscriptions at the start of an epoch.
// PrepareEpoch stores sync committee duties and submits sync committee subscriptions at the start of an epoch.
func (s *SyncCommMember) PrepareEpoch(ctx context.Context) error {
var err error
s.vals, err = activeValidators(ctx, s.eth2Cl, s.pubkeys)
if err != nil {
return err
}

s.subcomms, err = prepareSubcommittees(ctx, s.eth2Cl, s.vals)
if err != nil {
return err
}

s.duties, err = prepareSyncCommDuties(ctx, s.eth2Cl, s.vals, s.epoch)
if err != nil {
return err
Expand All @@ -129,31 +140,62 @@ func (s *SyncCommMember) PrepareEpoch(ctx context.Context) error {
// PrepareSlot prepares selection proofs at the start of a slot.
func (s *SyncCommMember) PrepareSlot(ctx context.Context, slot eth2p0.Slot) error {
wait(ctx, s.dutiesOK)
selections, err := prepareSyncContributions(ctx, s.eth2Cl, s.signFunc, s.vals, s.duties, slot)

selections, err := prepareSyncSelections(ctx, s.eth2Cl, s.signFunc, s.vals, s.subcomms, slot)
if err != nil {
return err
}

s.setSelections(slot, selections)

return nil
}

// Message submits Sync committee messages at desired i.e., 1/3rd into the slot.
// Message submits sync committee messages at 1/3rd into the slot.
func (s *SyncCommMember) Message(ctx context.Context, slot eth2p0.Slot) error {
wait(ctx, s.dutiesOK)
return submitSyncMessage(ctx, s.eth2Cl, slot, s.signFunc, s.duties)
}

// Aggregate submits Sync committee messages at desired i.e., 2/3rd into the slot.
// Aggregate submits SignedContributionAndProof at 2/3rd into the slot. It does sync committee aggregations.
// It blocks until sync committee selections are ready for this slot.
func (s *SyncCommMember) Aggregate(ctx context.Context, slot eth2p0.Slot) error {
wait(ctx, s.getSelectionsOK(slot))
wait(ctx, s.dutiesOK, s.getSelectionsOK(slot))
// TODO(xenowits): Add aggregate function.

return nil
}

func prepareSyncContributions(context.Context, eth2wrap.Client, SignFunc,
validators, syncDuties, eth2p0.Slot,
) (syncSelections, error) {
return nil, nil
// prepareSubcommittees returns the assignment of validators to sync subcommittees. It assumes that all validators are included in all sync subnets (subcommittees).
func prepareSubcommittees(ctx context.Context, eth2Cl eth2wrap.Client, vals validators) (subCommittees, error) {
spec, err := eth2Cl.Spec(ctx)
if err != nil {
return nil, err
}

subnetCount := spec["SYNC_COMMITTEE_SUBNET_COUNT"].(uint64)
subcomms := make(subCommittees)
for vIdx := range vals {
for i := uint64(0); i < subnetCount; i++ {
subcomms[vIdx] = append(subcomms[vIdx], eth2p0.CommitteeIndex(i))
}
}

return subcomms, nil
}

// prepareSyncCommDuties returns sync committee duties for the epoch.
func prepareSyncCommDuties(ctx context.Context, eth2Cl eth2wrap.Client, vals validators, epoch eth2p0.Epoch) (syncDuties, error) {
if len(vals) == 0 {
return nil, nil
}

var vIdxs []eth2p0.ValidatorIndex
for idx := range vals {
vIdxs = append(vIdxs, idx)
}

return eth2Cl.SyncCommitteeDuties(ctx, epoch, vIdxs)
}

// subscribeSyncCommSubnets submits sync committee subscriptions at the start of an epoch until next epoch.
Expand Down Expand Up @@ -181,18 +223,46 @@ func subscribeSyncCommSubnets(ctx context.Context, eth2Cl eth2wrap.Client, epoch
return nil
}

// prepareSyncCommDuties returns sync committee duties for the epoch.
func prepareSyncCommDuties(ctx context.Context, eth2Cl eth2wrap.Client, vals validators, epoch eth2p0.Epoch) (syncDuties, error) {
if len(vals) == 0 {
return nil, nil
// prepareSyncSelections returns the sync committee selections for the slot corresponding to the provided validators.
func prepareSyncSelections(ctx context.Context, eth2Cl eth2wrap.Client, signFunc SignFunc, vals validators, subcomms subCommittees, slot eth2p0.Slot) (syncSelections, error) {
epoch, err := eth2util.EpochFromSlot(ctx, eth2Cl, slot)
if err != nil {
return nil, err
}

var vIdxs []eth2p0.ValidatorIndex
for idx := range vals {
vIdxs = append(vIdxs, idx)
var selections []*eth2exp.SyncCommitteeSelection
for vIdx, val := range vals {
for _, subcommIdx := range subcomms[vIdx] {
data := altair.SyncAggregatorSelectionData{
Slot: slot,
SubcommitteeIndex: uint64(subcommIdx),
}

sigRoot, err := data.HashTreeRoot()
if err != nil {
return nil, err
}

sigData, err := signing.GetDataRoot(ctx, eth2Cl, signing.DomainSyncCommitteeSelectionProof, epoch, sigRoot)
if err != nil {
return nil, err
}

sig, err := signFunc(val.Validator.PublicKey, sigData[:])
if err != nil {
return nil, err
}

selections = append(selections, &eth2exp.SyncCommitteeSelection{
ValidatorIndex: vIdx,
Slot: slot,
SubcommitteeIndex: subcommIdx,
SelectionProof: sig,
})
}
}

return eth2Cl.SyncCommitteeDuties(ctx, epoch, vIdxs)
return eth2Cl.AggregateSyncCommitteeSelections(ctx, selections)
}

// submitSyncMessage submits signed sync committee messages for desired slot.
Expand Down
2 changes: 1 addition & 1 deletion testutil/validatormock/validatormock.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ func Register(ctx context.Context, eth2Cl eth2wrap.Client, signFunc SignFunc,
return eth2Cl.SubmitValidatorRegistrations(ctx, []*eth2api.VersionedSignedValidatorRegistration{signedRegistration})
}

// NewSigner returns a singing function supporting the provided private keys.
// NewSigner returns a signing function supporting the provided private keys.
func NewSigner(secrets ...*bls_sig.SecretKey) SignFunc {
return func(pubkey eth2p0.BLSPubKey, msg []byte) (eth2p0.BLSSignature, error) {
secret, err := getSecret(secrets, pubkey)
Expand Down

0 comments on commit 5b64b93

Please sign in to comment.