Skip to content

Commit

Permalink
add kinds of tests
Browse files Browse the repository at this point in the history
  • Loading branch information
xenowits committed Oct 26, 2022
1 parent 7338411 commit b66a9ec
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 130 deletions.
4 changes: 2 additions & 2 deletions core/fetcher/fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ func (f *Fetcher) fetchContributionData(ctx context.Context, slot int64, defSet
if err != nil {
return core.UnsignedDataSet{}, err
} else if !ok {
log.Debug(ctx, "Validator not selected for sync committee contribution duty", z.Any("pubkey", pubkey))
log.Debug(ctx, "Sync committee member not selected for contribution aggregation duty", z.Any("pubkey", pubkey))
continue
}

Expand All @@ -347,7 +347,7 @@ func (f *Fetcher) fetchContributionData(ctx context.Context, slot int64, defSet
} else if contribution == nil {
// Some beacon nodes return nil if the beacon block root is not found for the subcommittee, return retryable error.
// This could happen if the beacon node didn't subscribe to the correct subnet.
return core.UnsignedDataSet{}, errors.New("sync committee contribution not found", z.U64("subcommIdx", subcommIdx), z.Hex("root", blockRoot[:]))
return core.UnsignedDataSet{}, errors.New("sync committee contribution not found by root (retryable)", z.U64("subcommidx", subcommIdx), z.Hex("root", blockRoot[:]))
}
log.Info(ctx, "Resolved sync committee contribution duty", z.Any("pubkey", pubkey))

Expand Down
228 changes: 151 additions & 77 deletions core/fetcher/fetcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package fetcher_test

import (
"context"
"encoding/hex"
"fmt"
"testing"

Expand All @@ -31,6 +32,7 @@ import (
"github.com/obolnetwork/charon/app/errors"
"github.com/obolnetwork/charon/core"
"github.com/obolnetwork/charon/core/fetcher"
"github.com/obolnetwork/charon/eth2util/eth2exp"
"github.com/obolnetwork/charon/testutil"
"github.com/obolnetwork/charon/testutil/beaconmock"
)
Expand Down Expand Up @@ -341,16 +343,16 @@ func TestFetchBuilderProposer(t *testing.T) {
require.NoError(t, err)
}

//nolint:gocognit
func TestFetchSyncContribution(t *testing.T) {
ctx := context.Background()

const (
slot = 1
vIdxA = 2
vIdxB = 3
subCommIdxA = 4
subCommIdxB = 5
syncCommSize = 16
slot = 1
vIdxA = 2
vIdxB = 3
subCommIdxA = 4
subCommIdxB = 5
)

var (
Expand All @@ -370,11 +372,24 @@ func TestFetchSyncContribution(t *testing.T) {
pubkeysByIdx[vIdxB]: core.NewSyncCommitteeDefinition(testutil.RandomSyncCommitteeDuty(t)),
}

// Selection proofs corresponding to aggregators. Refer https://github.com/prysmaticlabs/prysm/blob/39a7988e9edbed5b517229b4d66c2a8aab7c7b4d/beacon-chain/sync/validate_sync_contribution_proof_test.go#L460.
sigA := "a9dbd88a49a7269e91b8ef1296f1e07f87fed919d51a446b67122bfdfd61d23f3f929fc1cd5209bd6862fd60f739b27213fb0a8d339f7f081fc84281f554b190bb49cc97a6b3364e622af9e7ca96a97fe2b766f9e746dead0b33b58473d91562"
sigB := "99e60f20dde4d4872b048d703f1943071c20213d504012e7e520c229da87661803b9f139b9a0c5be31de3cef6821c080125aed38ebaf51ba9a2e9d21d7fbf2903577983109d097a8599610a92c0305408d97c1fd4b0b2d1743fb4eedf5443f99"

// Construct sync committee selections.
selectionA := testutil.RandomSyncCommitteeSelection()
selectionA.SubcommitteeIndex = subCommIdxA
selectionB := testutil.RandomSyncCommitteeSelection()
selectionB.SubcommitteeIndex = subCommIdxB
selectionA := &eth2exp.SyncCommitteeSelection{
ValidatorIndex: vIdxA,
Slot: slot,
SubcommitteeIndex: subCommIdxA,
SelectionProof: blsSigFromHex(t, sigA),
}

selectionB := &eth2exp.SyncCommitteeSelection{
ValidatorIndex: vIdxB,
Slot: slot,
SubcommitteeIndex: subCommIdxB,
SelectionProof: blsSigFromHex(t, sigB),
}
commSelectionsByPubkey := map[core.PubKey]core.SignedData{
pubkeysByIdx[vIdxA]: core.NewSyncCommitteeSelection(selectionA),
pubkeysByIdx[vIdxB]: core.NewSyncCommitteeSelection(selectionB),
Expand All @@ -390,89 +405,135 @@ func TestFetchSyncContribution(t *testing.T) {
pubkeysByIdx[vIdxB]: core.NewSignedSyncMessage(msgB),
}

// Construct beaconmock.
bmock, err := beaconmock.New(
beaconmock.WithSyncCommitteeSize(syncCommSize),
beaconmock.WithSyncCommitteeSubnetCount(syncCommSize),
beaconmock.WithTargetAggregatorsPerSyncSubcommittee(syncCommSize),
)
require.NoError(t, err)

bmock.SyncCommitteeContributionFunc = func(ctx context.Context, resSlot eth2p0.Slot, subcommitteeIndex uint64, beaconBlockRoot eth2p0.Root) (*altair.SyncCommitteeContribution, error) {
require.Equal(t, eth2p0.Slot(slot), resSlot)
t.Run("contribution aggregator", func(t *testing.T) {
// Construct beaconmock.
bmock, err := beaconmock.New()
require.NoError(t, err)

var (
signedMsg core.SignedSyncMessage
signedSelection core.SyncCommitteeSelection
)
for _, msg := range syncMsgsByPubkey {
m, ok := msg.(core.SignedSyncMessage)
require.True(t, ok)
if m.BeaconBlockRoot == beaconBlockRoot {
signedMsg = m
bmock.SyncCommitteeContributionFunc = func(ctx context.Context, resSlot eth2p0.Slot, subcommitteeIndex uint64, beaconBlockRoot eth2p0.Root) (*altair.SyncCommitteeContribution, error) {
require.Equal(t, eth2p0.Slot(slot), resSlot)

var (
signedMsg core.SignedSyncMessage
signedSelection core.SyncCommitteeSelection
)
for _, msg := range syncMsgsByPubkey {
m, ok := msg.(core.SignedSyncMessage)
require.True(t, ok)
if m.BeaconBlockRoot == beaconBlockRoot {
signedMsg = m
}
}
require.NotNil(t, signedMsg)

for _, selection := range commSelectionsByPubkey {
s, ok := selection.(core.SyncCommitteeSelection)
require.True(t, ok)
if s.SubcommitteeIndex == eth2p0.CommitteeIndex(subcommitteeIndex) {
signedSelection = s
}
}
require.NotNil(t, signedSelection)

return &altair.SyncCommitteeContribution{
Slot: slot,
BeaconBlockRoot: beaconBlockRoot,
SubcommitteeIndex: subcommitteeIndex,
AggregationBits: bitfield.Bitvector128(testutil.RandomBitList()),
Signature: testutil.RandomEth2Signature(),
}, nil
}
require.NotNil(t, signedMsg)

for _, selection := range commSelectionsByPubkey {
s, ok := selection.(core.SyncCommitteeSelection)
require.True(t, ok)
if s.SubcommitteeIndex == eth2p0.CommitteeIndex(subcommitteeIndex) {
signedSelection = s
// Construct fetcher component.
fetch, err := fetcher.New(bmock, "")
require.NoError(t, err)

fetch.RegisterAggSigDB(func(ctx context.Context, duty core.Duty, key core.PubKey) (core.SignedData, error) {
if duty.Type == core.DutyPrepareSyncContribution {
require.Equal(t, core.NewPrepareSyncContributionDuty(slot), duty)
return commSelectionsByPubkey[key], nil
}
}
require.NotNil(t, signedSelection)

return &altair.SyncCommitteeContribution{
Slot: slot,
BeaconBlockRoot: beaconBlockRoot,
SubcommitteeIndex: subcommitteeIndex,
AggregationBits: bitfield.Bitvector128(testutil.RandomBitList()),
Signature: testutil.RandomEth2Signature(),
}, nil
}

// Construct fetcher component.
fetch, err := fetcher.New(bmock, "")
require.NoError(t, err)
if duty.Type == core.DutySyncMessage {
require.Equal(t, core.NewSyncMessageDuty(slot), duty)
return syncMsgsByPubkey[key], nil
}

fetch.RegisterAggSigDB(func(ctx context.Context, duty core.Duty, key core.PubKey) (core.SignedData, error) {
if duty.Type == core.DutyPrepareSyncContribution {
require.Equal(t, core.NewPrepareSyncContributionDuty(slot), duty)
return commSelectionsByPubkey[key], nil
}
return nil, errors.New("unsupported duty")
})

fetch.Subscribe(func(ctx context.Context, resDuty core.Duty, resDataSet core.UnsignedDataSet) error {
require.Equal(t, duty, resDuty)
require.Len(t, resDataSet, 2)

for pubkey, data := range resDataSet {
contribution, ok := data.(core.SyncContribution)
require.True(t, ok)
require.Equal(t, eth2p0.Slot(slot), contribution.Slot)

selection, ok := commSelectionsByPubkey[pubkey].(core.SyncCommitteeSelection)
require.True(t, ok)

for vIdx, pk := range pubkeysByIdx {
if pubkey == pk {
require.Equal(t, selection.ValidatorIndex, vIdx)
}
}
require.Equal(t, eth2p0.Slot(slot), selection.Slot)
require.Equal(t, eth2p0.CommitteeIndex(contribution.SubcommitteeIndex), selection.SubcommitteeIndex)

if selection.ValidatorIndex == eth2p0.ValidatorIndex(vIdxA) {
require.Equal(t, eth2p0.CommitteeIndex(subCommIdxA), selection.SubcommitteeIndex)
} else if selection.ValidatorIndex == eth2p0.ValidatorIndex(vIdxB) {
require.Equal(t, eth2p0.CommitteeIndex(subCommIdxB), selection.SubcommitteeIndex)
}

message, ok := syncMsgsByPubkey[pubkey].(core.SignedSyncMessage)
require.True(t, ok)
require.Equal(t, message.BeaconBlockRoot, contribution.BeaconBlockRoot)
}

if duty.Type == core.DutySyncMessage {
require.Equal(t, core.NewSyncMessageDuty(slot), duty)
return syncMsgsByPubkey[key], nil
}
return nil
})

return nil, errors.New("unsupported duty")
err = fetch.Fetch(ctx, duty, defSet)
require.NoError(t, err)
})

fetch.Subscribe(func(ctx context.Context, resDuty core.Duty, resDataSet core.UnsignedDataSet) error {
require.Equal(t, duty, resDuty)
require.Len(t, resDataSet, 2)
t.Run("not contribution aggregator", func(t *testing.T) {
// Construct beaconmock.
bmock, err := beaconmock.New()
require.NoError(t, err)

for pubkey, data := range resDataSet {
contribution, ok := data.(core.SyncContribution)
require.True(t, ok)
require.Equal(t, eth2p0.Slot(slot), contribution.Slot)
// Construct fetcher component.
fetch, err := fetcher.New(bmock, "")
require.NoError(t, err)

selection, ok := commSelectionsByPubkey[pubkey].(core.SyncCommitteeSelection)
require.True(t, ok)
require.Equal(t, selection.SubcommitteeIndex, eth2p0.CommitteeIndex(contribution.SubcommitteeIndex))
fetch.RegisterAggSigDB(func(ctx context.Context, duty core.Duty, key core.PubKey) (core.SignedData, error) {
if duty.Type == core.DutyPrepareSyncContribution {
require.Equal(t, core.NewPrepareSyncContributionDuty(slot), duty)

message, ok := syncMsgsByPubkey[pubkey].(core.SignedSyncMessage)
require.True(t, ok)
require.Equal(t, message.BeaconBlockRoot, contribution.BeaconBlockRoot)
}
// The following signature corresponds to a non-aggregator. Refer https://github.com/prysmaticlabs/prysm/blob/39a7988e9edbed5b517229b4d66c2a8aab7c7b4d/beacon-chain/sync/validate_sync_contribution_proof_test.go#L336
sigHex := "b9251a82040d4620b8c5665f328ee6c2eaa02d31d71d153f4abba31a7922a981e541e85283f0ced387d26e86aef9386d18c6982b9b5f8759882fe7f25a328180d86e146994ef19d28bc1432baf29751dec12b5f3d65dbbe224d72cf900c6831a"
sig, err := hex.DecodeString(sigHex)
require.NoError(t, err)

return nil
})
var resp eth2p0.BLSSignature
copy(resp[:], sig)

err = fetch.Fetch(ctx, duty, defSet)
require.NoError(t, err)
selection := &eth2exp.SyncCommitteeSelection{
SelectionProof: resp,
}

return core.NewSyncCommitteeSelection(selection), nil
}

return nil, errors.New("unsupported duty")
})

err = fetch.Fetch(ctx, duty, defSet)
require.NoError(t, err)
})
}

func assertRandao(t *testing.T, randao eth2p0.BLSSignature, block core.VersionedBeaconBlock) {
Expand Down Expand Up @@ -500,3 +561,16 @@ func assertRandaoBlindedBlock(t *testing.T, randao eth2p0.BLSSignature, block co
require.Fail(t, "invalid block")
}
}

// blsSigFromHex returns the BLS signature from the input hex signature.
func blsSigFromHex(t *testing.T, sig string) eth2p0.BLSSignature {
t.Helper()

s, err := hex.DecodeString(sig)
require.NoError(t, err)

var resp eth2p0.BLSSignature
copy(resp[:], s)

return resp
}
7 changes: 6 additions & 1 deletion core/signeddata.go
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,12 @@ type SyncCommitteeSelection struct {
}

func (s SyncCommitteeSelection) MessageRoot() ([32]byte, error) {
return eth2util.SlotHashRoot(s.Slot)
data := altair.SyncAggregatorSelectionData{
Slot: s.Slot,
SubcommitteeIndex: uint64(s.SubcommitteeIndex),
}

return data.HashTreeRoot()
}

func (s SyncCommitteeSelection) Signature() Signature {
Expand Down
8 changes: 4 additions & 4 deletions eth2util/eth2exp/attagg_old.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func IsAttAggregator(ctx context.Context, specProvider eth2client.SpecProvider,
modulo = 1
}

return hash(slotSig, modulo)
return hashModulo(slotSig, modulo)
}

// IsSyncCommAggregator returns true if the validator is the aggregator for the provided sync subcommittee.
Expand Down Expand Up @@ -75,11 +75,11 @@ func IsSyncCommAggregator(ctx context.Context, specProvider eth2client.SpecProvi
modulo = 1
}

return hash(sig, modulo)
return hashModulo(sig, modulo)
}

// hash returns true if the first 8 bytes of the sha256 hash of the input signature is divisible by the provided modulo.
func hash(sig eth2p0.BLSSignature, modulo uint64) (bool, error) {
// hashModulo returns true if the first 8 bytes of the sha256 hashModulo of the input signature is divisible by the provided modulo.
func hashModulo(sig eth2p0.BLSSignature, modulo uint64) (bool, error) {
h := sha256.New()
_, err := h.Write(sig[:])
if err != nil {
Expand Down
Loading

0 comments on commit b66a9ec

Please sign in to comment.