Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - use atx grading for hare #4718

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion hare/eligibility/oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ func (o *Oracle) computeActiveSet(ctx context.Context, targetEpoch types.EpochID
return activeSet, nil
}

activeSet, err := miner.ActiveSetFromBlock(o.cdb, targetEpoch)
activeSet, err := miner.ActiveSetFromEpochFirstBlock(o.cdb, targetEpoch)
if err != nil && !errors.Is(err, sql.ErrNotFound) {
return nil, err
}
Expand Down
10 changes: 2 additions & 8 deletions hare/flows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,10 +258,7 @@ func Test_multipleCPs(t *testing.T) {
pList := make(map[types.LayerID][]*types.Proposal)
for j := types.GetEffectiveGenesis().Add(1); !j.After(finalLyr); j = j.Add(1) {
for i := uint64(0); i < 20; i++ {
p := genLayerProposal(j, []types.TransactionID{})
p.EpochData = &types.EpochData{
Beacon: types.EmptyBeacon,
}
p := randomProposal(j, types.EmptyBeacon)
pList[j] = append(pList[j], p)
}
}
Expand Down Expand Up @@ -378,10 +375,7 @@ func Test_multipleCPsAndIterations(t *testing.T) {
pList := make(map[types.LayerID][]*types.Proposal)
for j := types.GetEffectiveGenesis().Add(1); !j.After(finalLyr); j = j.Add(1) {
for i := uint64(0); i < 20; i++ {
p := genLayerProposal(j, []types.TransactionID{})
p.EpochData = &types.EpochData{
Beacon: types.EmptyBeacon,
}
p := randomProposal(j, types.EmptyBeacon)
pList[j] = append(pList[j], p)
}
}
Expand Down
94 changes: 87 additions & 7 deletions hare/hare.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"github.com/spacemeshos/go-spacemesh/hare/config"
"github.com/spacemeshos/go-spacemesh/log"
"github.com/spacemeshos/go-spacemesh/malfeasance"
"github.com/spacemeshos/go-spacemesh/miner"
"github.com/spacemeshos/go-spacemesh/p2p/pubsub"
"github.com/spacemeshos/go-spacemesh/signing"
"github.com/spacemeshos/go-spacemesh/sql"
Expand Down Expand Up @@ -388,7 +389,7 @@
report: h.outputChan,
wc: h.wcChan,
}
props := goodProposals(ctx, h.Log, h.msh, h.nodeID, lid, beacon)
props := goodProposals(ctx, h.Log, h.msh, h.nodeID, lid, beacon, h.layerClock.LayerToTime(lid.GetEpoch().FirstLayer()), h.config.WakeupDelta)
preNumProposals.Add(float64(len(props)))
set := NewSet(props)
cp := h.factory(ctx, h.config, lid, set, h.rolacle, et, h.sign, h.publisher, comm, clock)
Expand Down Expand Up @@ -441,9 +442,21 @@
}

// goodProposals finds the "good proposals" for the specified layer. a proposal is good if
// it has the same beacon value as the node's beacon value.
// - it has the same beacon value as the node's beacon value.
// - its miner is not malicious
// - its active set contains only grade 1 or grade 2 atxs
// see (https://community.spacemesh.io/t/grading-atxs-for-the-active-set/335#proposal-voting-4)
// any error encountered will be ignored and an empty set is returned.
func goodProposals(ctx context.Context, logger log.Log, msh mesh, nodeID types.NodeID, lid types.LayerID, epochBeacon types.Beacon) []types.ProposalID {
func goodProposals(
fasmat marked this conversation as resolved.
Show resolved Hide resolved
ctx context.Context,
logger log.Log,
msh mesh,
nodeID types.NodeID,
lid types.LayerID,
epochBeacon types.Beacon,
epochStart time.Time,
networkDelay time.Duration,
) []types.ProposalID {
props, err := msh.Proposals(lid)
if err != nil {
if errors.Is(err, sql.ErrNotFound) {
Expand All @@ -456,13 +469,14 @@

var (
beacon types.Beacon
activeSet []types.ATXID
result []types.ProposalID
ownHdr *types.ActivationTxHeader
ownTickHeight = uint64(math.MaxUint64)
)
// a non-smesher will not filter out any proposals, as it doesn't have voting power
// and only observes the consensus process.
ownHdr, err = msh.GetEpochAtx(lid.GetEpoch(), nodeID)
ownHdr, err = msh.GetEpochAtx(lid.GetEpoch()-1, nodeID)
if err != nil && !errors.Is(err, sql.ErrNotFound) {
logger.With().Error("failed to get own atx", log.Context(ctx), lid, log.Err(err))
return []types.ProposalID{}
Expand All @@ -474,6 +488,7 @@
for _, p := range props {
atxs[p.AtxID]++
}
cache := map[types.ATXID]miner.AtxGrade{}
for _, p := range props {
if p.IsMalicious() {
logger.With().Warning("not voting on proposal from malicious identity",
Expand Down Expand Up @@ -508,17 +523,59 @@
}
if p.EpochData != nil {
beacon = p.EpochData.Beacon
activeSet = p.ActiveSet
} else if p.RefBallot == types.EmptyBallotID {
logger.With().Error("proposal missing ref ballot", p.ID())
logger.With().Error("proposal missing ref ballot",
log.Context(ctx),
lid,
p.ID(),
)

Check warning on line 532 in hare/hare.go

View check run for this annotation

Codecov / codecov/patch

hare/hare.go#L528-L532

Added lines #L528 - L532 were not covered by tests
return []types.ProposalID{}
} else if refBallot, err := msh.Ballot(p.RefBallot); err != nil {
logger.With().Error("failed to get ref ballot", p.ID(), p.RefBallot, log.Err(err))
logger.With().Error("failed to get ref ballot",
log.Context(ctx),
lid,
p.ID(),
p.RefBallot,
log.Err(err))

Check warning on line 540 in hare/hare.go

View check run for this annotation

Codecov / codecov/patch

hare/hare.go#L535-L540

Added lines #L535 - L540 were not covered by tests
return []types.ProposalID{}
} else if refBallot.EpochData == nil {
logger.With().Error("ref ballot missing epoch data", log.Context(ctx), lid, refBallot.ID())
logger.With().Error("ref ballot missing epoch data",
log.Context(ctx),
p.ID(),
lid,
refBallot.ID(),
)

Check warning on line 548 in hare/hare.go

View check run for this annotation

Codecov / codecov/patch

hare/hare.go#L543-L548

Added lines #L543 - L548 were not covered by tests
return []types.ProposalID{}
} else {
beacon = refBallot.EpochData.Beacon
activeSet = refBallot.ActiveSet
}

if len(activeSet) == 0 {
logger.With().Error("proposal missing active set",
log.Context(ctx),
p.ID(),
lid,
)
return []types.ProposalID{}
}

Check warning on line 562 in hare/hare.go

View check run for this annotation

Codecov / codecov/patch

hare/hare.go#L556-L562

Added lines #L556 - L562 were not covered by tests
if evil, err := gradeActiveSet(cache, activeSet, msh, epochStart, networkDelay); err != nil {
logger.With().Error("failed to grade active set",
log.Context(ctx),
lid,
p.ID(),
log.Err(err),
)
return []types.ProposalID{}

Check warning on line 570 in hare/hare.go

View check run for this annotation

Codecov / codecov/patch

hare/hare.go#L564-L570

Added lines #L564 - L570 were not covered by tests
} else if evil != types.EmptyATXID {
logger.With().Warning("proposal has grade 0 active set",
log.Context(ctx),
lid,
p.ID(),
log.Stringer("evil atx", evil),
)
continue
}

if beacon == epochBeacon {
Expand All @@ -535,6 +592,29 @@
return result
}

func gradeActiveSet(cache map[types.ATXID]miner.AtxGrade, activeSet []types.ATXID, msh mesh, epochStart time.Time, networkDelay time.Duration) (types.ATXID, error) {
for _, id := range activeSet {
var grade miner.AtxGrade
if g, ok := cache[id]; ok {
grade = g
} else {
hdr, err := msh.GetAtxHeader(id)
if err != nil {
return types.EmptyATXID, fmt.Errorf("get header %v: %s", id, err)
}

Check warning on line 604 in hare/hare.go

View check run for this annotation

Codecov / codecov/patch

hare/hare.go#L603-L604

Added lines #L603 - L604 were not covered by tests
grade, err = miner.GradeAtx(msh, hdr.NodeID, hdr.Received, epochStart, networkDelay)
if err != nil {
return types.EmptyATXID, fmt.Errorf("grade %v: %w", id, err)
}

Check warning on line 608 in hare/hare.go

View check run for this annotation

Codecov / codecov/patch

hare/hare.go#L607-L608

Added lines #L607 - L608 were not covered by tests
cache[id] = grade
}
if grade == miner.Evil {
return id, nil
}
}
return types.EmptyATXID, nil
}

var (
errTooOld = errors.New("layer has already been evacuated from buffer")
errNoResult = errors.New("no result for the requested layer")
Expand Down
5 changes: 3 additions & 2 deletions hare/hare_rounds_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,11 @@ func runNodesFor(t *testing.T, ctx context.Context, nodes, leaders, maxLayers, l
mockMesh := mocks.NewMockmesh(gomock.NewController(t))
if createProposal {
for lid := types.GetEffectiveGenesis().Add(1); !lid.After(types.GetEffectiveGenesis().Add(uint32(maxLayers))); lid = lid.Add(1) {
p := genLayerProposal(lid, []types.TransactionID{})
p := randomProposal(lid, types.RandomBeacon())
mockMesh.EXPECT().Ballot(p.Ballot.ID()).Return(&p.Ballot, nil).AnyTimes()
mockMesh.EXPECT().Proposals(lid).Return([]*types.Proposal{p}, nil).AnyTimes()
mockMesh.EXPECT().GetAtxHeader(p.AtxID).Return(&types.ActivationTxHeader{BaseTickHeight: 11, TickCount: 1}, nil).AnyTimes()
mockMesh.EXPECT().GetAtxHeader(p.AtxID).Return(&types.ActivationTxHeader{BaseTickHeight: 11, TickCount: 1, NodeID: p.SmesherID}, nil).AnyTimes()
mockMesh.EXPECT().GetMalfeasanceProof(p.SmesherID)
}
} else {
mockMesh.EXPECT().Proposals(gomock.Any()).Return([]*types.Proposal{}, nil).AnyTimes()
Expand Down