From fe23c05da69b73357ef5c983fb413b9304bcf3a5 Mon Sep 17 00:00:00 2001 From: Dmitry Shulyak Date: Wed, 27 Sep 2023 23:33:39 +0000 Subject: [PATCH] generator doesn't need to recompute eligibility count and loop over atxs (#5105) closes: https://github.com/spacemeshos/go-spacemesh/issues/5077 --- blocks/utils.go | 28 ++++--- proposals/util.go | 2 - proposals/util/util.go | 57 -------------- proposals/util_test.go | 172 ----------------------------------------- 4 files changed, 19 insertions(+), 240 deletions(-) delete mode 100644 proposals/util_test.go diff --git a/blocks/utils.go b/blocks/utils.go index 177c989154..78caad3fbe 100644 --- a/blocks/utils.go +++ b/blocks/utils.go @@ -17,7 +17,7 @@ import ( "github.com/spacemeshos/go-spacemesh/datastore" "github.com/spacemeshos/go-spacemesh/events" "github.com/spacemeshos/go-spacemesh/log" - "github.com/spacemeshos/go-spacemesh/proposals" + "github.com/spacemeshos/go-spacemesh/sql/ballots" "github.com/spacemeshos/go-spacemesh/sql/layers" "github.com/spacemeshos/go-spacemesh/sql/transactions" "github.com/spacemeshos/go-spacemesh/txs" @@ -225,16 +225,26 @@ func rewardInfoAndHeight(logger log.Log, cdb *datastore.CachedDB, cfg Config, pr if atx.BaseTickHeight > max { max = atx.BaseTickHeight } - ballot := &p.Ballot - weightPer, err := proposals.ComputeWeightPerEligibility(cdb, ballot) - if err != nil { - logger.With().Error("failed to calculate weight per eligibility", p.ID(), log.Err(err)) - return 0, nil, err + var count uint32 + if p.Ballot.EpochData != nil { + count = p.Ballot.EpochData.EligibilityCount + } else { + ref, err := ballots.Get(cdb, p.RefBallot) + if err != nil { + return 0, nil, fmt.Errorf("get ballot %s: %w", p.RefBallot.String(), err) + } + if ref.EpochData == nil { + return 0, nil, fmt.Errorf("corrupted data: ref ballot %s with empty epoch data", p.RefBallot.String()) + } + count = ref.EpochData.EligibilityCount } - logger.With().Debug("weight per eligibility", p.ID(), log.Stringer("weight_per", weightPer)) - actual := weightPer.Mul(weightPer, new(big.Rat).SetUint64(uint64(len(ballot.EligibilityProofs)))) if _, ok := weights[atx.ID]; !ok { - weights[atx.ID] = actual + weight := new(big.Rat).SetFrac( + new(big.Int).SetUint64(atx.GetWeight()), + new(big.Int).SetUint64(uint64(count)), + ) + weight.Mul(weight, new(big.Rat).SetUint64(uint64(len(p.Ballot.EligibilityProofs)))) + weights[atx.ID] = weight atxids = append(atxids, atx.ID) } else { logger.With().Error("multiple proposals with the same ATX", atx.ID, p.ID()) diff --git a/proposals/util.go b/proposals/util.go index ca5e70c431..0396d4af49 100644 --- a/proposals/util.go +++ b/proposals/util.go @@ -11,8 +11,6 @@ import ( var ( CalcEligibleLayer = util.CalcEligibleLayer GetNumEligibleSlots = util.GetNumEligibleSlots - // ComputeWeightPerEligibility computes the ballot weight per eligibility w.r.t the active set recorded in its reference ballot. - ComputeWeightPerEligibility = util.ComputeWeightPerEligibility ) //go:generate scalegen -types VrfMessage diff --git a/proposals/util/util.go b/proposals/util/util.go index 55d5405f69..77aa7c6f7a 100644 --- a/proposals/util/util.go +++ b/proposals/util/util.go @@ -3,13 +3,8 @@ package util import ( "encoding/binary" "errors" - "fmt" - "math/big" "github.com/spacemeshos/go-spacemesh/common/types" - "github.com/spacemeshos/go-spacemesh/datastore" - "github.com/spacemeshos/go-spacemesh/sql/activesets" - "github.com/spacemeshos/go-spacemesh/sql/ballots" ) var ( @@ -41,55 +36,3 @@ func GetNumEligibleSlots(weight, minWeight, totalWeight uint64, committeeSize, l } return uint32(numEligible), nil } - -// ComputeWeightPerEligibility computes the ballot weight per eligibility w.r.t the active set recorded in its reference ballot. -func ComputeWeightPerEligibility( - cdb *datastore.CachedDB, - ballot *types.Ballot, -) (*big.Rat, error) { - var ( - refBallot = ballot - hdr *types.ActivationTxHeader - err error - atxWeight uint64 - ) - if ballot.EpochData == nil { - if ballot.RefBallot == types.EmptyBallotID { - return nil, fmt.Errorf("%w: empty ref ballot but no epoch data %s", ErrBadBallotData, ballot.ID()) - } - refBallot, err = ballots.Get(cdb, ballot.RefBallot) - if err != nil { - return nil, fmt.Errorf("%w: missing ref ballot %s (for %s)", err, ballot.RefBallot, ballot.ID()) - } - } - if refBallot.EpochData == nil { - return nil, fmt.Errorf("epoch data is nil on ballot %d/%s", refBallot.Layer, refBallot.ID()) - } - if refBallot.EpochData.EligibilityCount == 0 { - return nil, fmt.Errorf("eligibility count is 0 on ballot %d/%s", refBallot.Layer, refBallot.ID()) - } - actives, err := activesets.Get(cdb, refBallot.EpochData.ActiveSetHash) - if err != nil { - return nil, fmt.Errorf("get active set %s (%s)", refBallot.EpochData.ActiveSetHash.ShortString(), refBallot.ID()) - } - if len(actives.Set) == 0 { - return nil, fmt.Errorf("empty active set %s (%s)", refBallot.EpochData.ActiveSetHash.ShortString(), refBallot.ID()) - } - for _, atxID := range actives.Set { - hdr, err = cdb.GetAtxHeader(atxID) - if err != nil { - return nil, fmt.Errorf("%w: missing atx %s in active set of %s (for %s)", err, atxID, refBallot.ID(), ballot.ID()) - } - if atxID == ballot.AtxID { - atxWeight = hdr.GetWeight() - break - } - } - if atxWeight == 0 { - return nil, fmt.Errorf("atx id %v is not found in the active set of the reference ballot %v with atxid %v", ballot.AtxID, refBallot.ID(), refBallot.AtxID) - } - return new(big.Rat).SetFrac( - new(big.Int).SetUint64(atxWeight), - new(big.Int).SetUint64(uint64(refBallot.EpochData.EligibilityCount)), - ), nil -} diff --git a/proposals/util_test.go b/proposals/util_test.go deleted file mode 100644 index 196dc28620..0000000000 --- a/proposals/util_test.go +++ /dev/null @@ -1,172 +0,0 @@ -package proposals - -import ( - "math/big" - "math/rand" - "sort" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/spacemeshos/go-spacemesh/common/types" - "github.com/spacemeshos/go-spacemesh/datastore" - "github.com/spacemeshos/go-spacemesh/log/logtest" - putil "github.com/spacemeshos/go-spacemesh/proposals/util" - "github.com/spacemeshos/go-spacemesh/signing" - "github.com/spacemeshos/go-spacemesh/sql" - "github.com/spacemeshos/go-spacemesh/sql/activesets" - "github.com/spacemeshos/go-spacemesh/sql/atxs" - "github.com/spacemeshos/go-spacemesh/sql/ballots" -) - -const ( - defaultATXUnit = uint32(5) - testedATXUnit = uint32(2) - // eligibleSlots is calculated based on layerAvgSize, layersPerEpoch, epoch ATX weight and smesher's own weight. - eligibleSlots = uint32(3) - epoch = types.EpochID(3) -) - -func genActiveSet() types.ATXIDList { - return types.ATXIDList{types.RandomATXID(), types.RandomATXID(), types.RandomATXID(), types.RandomATXID()} -} - -func createBallots(tb testing.TB, signer *signing.EdSigner, activeSet types.ATXIDList, beacon types.Beacon) []*types.Ballot { - totalWeight := uint64(len(activeSet)-1)*uint64(defaultATXUnit) + uint64(testedATXUnit) - slots, err := GetNumEligibleSlots(uint64(testedATXUnit), 0, totalWeight, layerAvgSize, layersPerEpoch) - require.NoError(tb, err) - require.Equal(tb, eligibleSlots, slots) - eligibilityProofs := map[types.LayerID][]types.VotingEligibility{} - order := make([]types.LayerID, 0, eligibleSlots) - - nonce := types.VRFPostIndex(1) - vrfSigner, err := signer.VRFSigner() - require.NoError(tb, err) - - for counter := uint32(0); counter < eligibleSlots; counter++ { - message, err := SerializeVRFMessage(beacon, epoch, nonce, counter) - require.NoError(tb, err) - vrfSig := vrfSigner.Sign(message) - eligibleLayer := CalcEligibleLayer(epoch, layersPerEpoch, vrfSig) - if _, exist := eligibilityProofs[eligibleLayer]; !exist { - order = append(order, eligibleLayer) - } - eligibilityProofs[eligibleLayer] = append(eligibilityProofs[eligibleLayer], types.VotingEligibility{ - J: counter, - Sig: vrfSig, - }) - } - sort.Slice(order, func(i, j int) bool { return order[i].Before(order[j]) }) - blts := make([]*types.Ballot, 0, eligibleSlots) - for _, lyr := range order { - proofs := eligibilityProofs[lyr] - isRef := len(blts) == 0 - b := types.RandomBallot() - b.AtxID = activeSet[0] - b.Layer = lyr - if isRef { - b.RefBallot = types.EmptyBallotID - b.EpochData = &types.EpochData{ - ActiveSetHash: activeSet.Hash(), - Beacon: beacon, - EligibilityCount: eligibleSlots, - } - } else { - b.RefBallot = blts[0].ID() - } - b.EligibilityProofs = proofs - b.Signature = signer.Sign(signing.BALLOT, b.SignedBytes()) - b.SmesherID = signer.NodeID() - require.NoError(tb, b.Initialize()) - blts = append(blts, b) - } - return blts -} - -func TestComputeWeightPerEligibility(t *testing.T) { - signer, err := signing.NewEdSigner( - signing.WithKeyFromRand(rand.New(rand.NewSource(1001))), - ) - require.NoError(t, err) - cdb := datastore.NewCachedDB(sql.InMemory(), logtest.New(t)) - beacon := types.Beacon{1, 1, 1} - actives := genActiveSet() - blts := createBallots(t, signer, actives, beacon) - rb := blts[0] - activesets.Add(cdb, rb.EpochData.ActiveSetHash, &types.EpochActiveSet{Set: actives}) - require.NoError(t, ballots.Add(cdb, rb)) - for _, id := range actives { - atx := &types.ActivationTx{InnerActivationTx: types.InnerActivationTx{ - NIPostChallenge: types.NIPostChallenge{ - PublishEpoch: epoch - 1, - }, - NumUnits: defaultATXUnit, - }} - atx.SetID(id) - if id == rb.AtxID { - atx.NumUnits = testedATXUnit - } - atx.SetEffectiveNumUnits(atx.NumUnits) - atx.SetReceived(time.Now()) - vAtx, err := atx.Verify(0, 1) - require.NoError(t, err) - require.NoError(t, atxs.Add(cdb, vAtx)) - } - expectedWeight := big.NewRat(int64(testedATXUnit), int64(eligibleSlots)) - for _, b := range blts { - got, err := ComputeWeightPerEligibility(cdb, b) - require.NoError(t, err) - require.NotNil(t, got) - require.Equal(t, 0, got.Cmp(expectedWeight)) - } -} - -func TestComputeWeightPerEligibility_EmptyRefBallotID(t *testing.T) { - signer, err := signing.NewEdSigner( - signing.WithKeyFromRand(rand.New(rand.NewSource(1001))), - ) - require.NoError(t, err) - beacon := types.Beacon{1, 1, 1} - blts := createBallots(t, signer, genActiveSet(), beacon) - require.GreaterOrEqual(t, 2, len(blts)) - b := blts[1] - b.RefBallot = types.EmptyBallotID - cdb := datastore.NewCachedDB(sql.InMemory(), logtest.New(t)) - got, err := ComputeWeightPerEligibility(cdb, b) - require.ErrorIs(t, err, putil.ErrBadBallotData) - require.Nil(t, got) -} - -func TestComputeWeightPerEligibility_FailToGetRefBallot(t *testing.T) { - signer, err := signing.NewEdSigner( - signing.WithKeyFromRand(rand.New(rand.NewSource(1001))), - ) - require.NoError(t, err) - beacon := types.Beacon{1, 1, 1} - blts := createBallots(t, signer, genActiveSet(), beacon) - require.GreaterOrEqual(t, 2, len(blts)) - cdb := datastore.NewCachedDB(sql.InMemory(), logtest.New(t)) - got, err := ComputeWeightPerEligibility(cdb, blts[1]) - require.ErrorIs(t, err, sql.ErrNotFound) - require.True(t, strings.Contains(err.Error(), "missing ref ballot")) - require.Nil(t, got) -} - -func TestComputeWeightPerEligibility_FailATX(t *testing.T) { - signer, err := signing.NewEdSigner( - signing.WithKeyFromRand(rand.New(rand.NewSource(1001))), - ) - require.NoError(t, err) - beacon := types.Beacon{1, 1, 1} - cdb := datastore.NewCachedDB(sql.InMemory(), logtest.New(t)) - actives := genActiveSet() - blts := createBallots(t, signer, actives, beacon) - rb := blts[0] - activesets.Add(cdb, rb.EpochData.ActiveSetHash, &types.EpochActiveSet{Set: actives}) - got, err := ComputeWeightPerEligibility(cdb, rb) - require.ErrorIs(t, err, sql.ErrNotFound) - require.True(t, strings.Contains(err.Error(), "missing atx")) - require.Nil(t, got) -}