Skip to content

Commit

Permalink
miner: do not omit own atx in active set (#4952)
Browse files Browse the repository at this point in the history
## Motivation
syncing from devnet-405, malicious identities filter out own atx and cause other nodes to fail processing the ballot

```
2023-09-02T14:58:23.344-0700	WARN	5fbb1.sync	failed fetching new ballots	{"node_id": "5fbb17185b5850709b3c545e3be4fbea25a30d3362a0627d974495e96d8b640b", "module": "sync", "sessionId": "6123c073-483d-41ad-b514-77e47167f48f", "layer_id": 11549, "ballot_ids": ["6a70a5a8d0", "f3205c8f79", "1efadf7bbe", "dc0881473b", "57577ea37f", "1f3110b841", "c9f0910457", "f2e8dac11e", "c97b5ff911", "f824639d3b"], "errmsg": "hint: ballotDB, hash: 0x1f3110b8411255368ffc35e90c2f17ad3a898a13000000000000000000000000, err: fetch ballots: hint: ballotDB, hash: 0x926af68ad85b7db9d5a0fbd57ddefdbb32838149000000000000000000000000, err: ballot not eligible: atx 086658fac6 from ballot 926af68ad8 (refballot 926af68ad8) is not included into the active set\nhint: ballotDB, hash: 0xc9f0910457dfab80334c80e77cf6673fe408adcb000000000000000000000000, err: fetch ballots: hint: ballotDB, hash: 0x37df2b14296708054511e2627f1094f5b8528eb3000000000000000000000000, err: fetch ballots: hint: ballotDB, hash: 0x5b91d74480e89f741045aa9c4f95501b02fc3d53000000000000000000000000, err: ballot not eligible: atx e9aff052ae from ballot 5b91d74480 (refballot 5b91d74480) is not included into the active set", "name": "sync"}
```
  • Loading branch information
countvonzero committed Sep 4, 2023
1 parent 61e3c63 commit f08f25a
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 52 deletions.
2 changes: 1 addition & 1 deletion miner/oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ func (o *Oracle) activeSet(targetEpoch types.EpochID) (uint64, uint64, types.ATX
if err != nil {
return err
}
if grade != Good {
if grade != Good && header.NodeID != o.cfg.nodeID {
o.log.With().Info("atx omitted from active set",
header.ID,
log.Int("grade", int(grade)),
Expand Down
120 changes: 70 additions & 50 deletions miner/oracle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,57 +300,77 @@ func TestOracle_MinimalActiveSetWeight(t *testing.T) {
}

func TestOracle_ATXGrade(t *testing.T) {
avgLayerSize := uint32(50)
layersPerEpoch := uint32(10)
o := createTestOracle(t, avgLayerSize, layersPerEpoch, 0)
lid := types.LayerID(layersPerEpoch * 3)
epochStart := time.Now()
o.mClock.EXPECT().LayerToTime(lid).Return(epochStart)

goodTime := epochStart.Add(-4*networkDelay - time.Nanosecond)
okTime := epochStart.Add(-3*networkDelay - time.Nanosecond)
evilTime := epochStart.Add(-3 * networkDelay)
publishLayer := (lid.GetEpoch() - 1).FirstLayer()
ownAtx := genMinerATX(t, o.cdb, types.RandomATXID(), publishLayer, o.edSigner, goodTime)
expected := []types.ATXID{ownAtx.ID()}
for i := 1; i < activeSetSize; i++ {
sig, err := signing.NewEdSigner()
require.NoError(t, err)
atx := genMinerATX(t, o.cdb, types.RandomATXID(), publishLayer, sig, goodTime)
expected = append(expected, atx.ID())
}
// add some atx that have good timing, with malicious proof arriving before epoch start
for i := 0; i < activeSetSize; i++ {
sig, err := signing.NewEdSigner()
require.NoError(t, err)
atx := genMinerATX(t, o.cdb, types.RandomATXID(), publishLayer, sig, goodTime)
genMinerMalfeasance(t, o.cdb, sig.NodeID(), epochStart)
expected = append(expected, atx.ID())
}
// add some atx that have good timing, with malfeasance proof arriving after epoch start
for i := 0; i < activeSetSize; i++ {
sig, err := signing.NewEdSigner()
require.NoError(t, err)
genMinerATX(t, o.cdb, types.RandomATXID(), publishLayer, sig, goodTime)
genMinerMalfeasance(t, o.cdb, sig.NodeID(), epochStart.Add(-1*time.Nanosecond))
}
// add some atx that are acceptable
for i := 0; i < activeSetSize; i++ {
sig, err := signing.NewEdSigner()
require.NoError(t, err)
genMinerATX(t, o.cdb, types.RandomATXID(), publishLayer, sig, okTime)
}
// add some atx that are evil
for i := 0; i < activeSetSize; i++ {
sig, err := signing.NewEdSigner()
require.NoError(t, err)
genMinerATX(t, o.cdb, types.RandomATXID(), publishLayer, sig, evilTime)
for _, tc := range []struct {
desc string
ownMalicious bool
}{
{
desc: "own atx is good",
},
{
desc: "own atx is malicious",
ownMalicious: true,
},
} {
tc := tc
t.Run(tc.desc, func(t *testing.T) {
avgLayerSize := uint32(50)
layersPerEpoch := uint32(10)
o := createTestOracle(t, avgLayerSize, layersPerEpoch, 0)
lid := types.LayerID(layersPerEpoch * 3)
epochStart := time.Now()
o.mClock.EXPECT().LayerToTime(lid).Return(epochStart)

goodTime := epochStart.Add(-4*networkDelay - time.Nanosecond)
okTime := epochStart.Add(-3*networkDelay - time.Nanosecond)
evilTime := epochStart.Add(-3 * networkDelay)
publishLayer := (lid.GetEpoch() - 1).FirstLayer()
received := goodTime
if tc.ownMalicious {
received = evilTime
}
ownAtx := genMinerATX(t, o.cdb, types.RandomATXID(), publishLayer, o.edSigner, received)
expected := []types.ATXID{ownAtx.ID()}
for i := 1; i < activeSetSize; i++ {
sig, err := signing.NewEdSigner()
require.NoError(t, err)
atx := genMinerATX(t, o.cdb, types.RandomATXID(), publishLayer, sig, goodTime)
expected = append(expected, atx.ID())
}
// add some atx that have good timing, with malicious proof arriving before epoch start
for i := 0; i < activeSetSize; i++ {
sig, err := signing.NewEdSigner()
require.NoError(t, err)
atx := genMinerATX(t, o.cdb, types.RandomATXID(), publishLayer, sig, goodTime)
genMinerMalfeasance(t, o.cdb, sig.NodeID(), epochStart)
expected = append(expected, atx.ID())
}
// add some atx that have good timing, with malfeasance proof arriving after epoch start
for i := 0; i < activeSetSize; i++ {
sig, err := signing.NewEdSigner()
require.NoError(t, err)
genMinerATX(t, o.cdb, types.RandomATXID(), publishLayer, sig, goodTime)
genMinerMalfeasance(t, o.cdb, sig.NodeID(), epochStart.Add(-1*time.Nanosecond))
}
// add some atx that are acceptable
for i := 0; i < activeSetSize; i++ {
sig, err := signing.NewEdSigner()
require.NoError(t, err)
genMinerATX(t, o.cdb, types.RandomATXID(), publishLayer, sig, okTime)
}
// add some atx that are evil
for i := 0; i < activeSetSize; i++ {
sig, err := signing.NewEdSigner()
require.NoError(t, err)
genMinerATX(t, o.cdb, types.RandomATXID(), publishLayer, sig, evilTime)
}
ee, err := o.ProposalEligibility(lid, types.RandomBeacon(), types.VRFPostIndex(1))
require.NoError(t, err)
require.Equal(t, ownAtx.ID(), ee.Atx)
require.ElementsMatch(t, expected, ee.ActiveSet)
require.NotEmpty(t, ee.Proofs)
})
}
ee, err := o.ProposalEligibility(lid, types.RandomBeacon(), types.VRFPostIndex(1))
require.NoError(t, err)
require.Equal(t, ownAtx.ID(), ee.Atx)
require.ElementsMatch(t, expected, ee.ActiveSet)
require.NotEmpty(t, ee.Proofs)
}

func createBallots(tb testing.TB, cdb *datastore.CachedDB, lid types.LayerID, numBallots int, common []types.ATXID) []*types.Ballot {
Expand Down
6 changes: 5 additions & 1 deletion proposals/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,11 @@ func (h *Handler) checkBallotSyntacticValidity(ctx context.Context, logger log.L
t4 := time.Now()
if eligible, err := h.validator.CheckEligibility(ctx, b); err != nil || !eligible {
notEligible.Inc()
return nil, errNotEligible
var reason string
if err != nil {
reason = err.Error()
}
return nil, fmt.Errorf("%w: %v", errNotEligible, reason)
}
ballotDuration.WithLabelValues(eligible).Observe(float64(time.Since(t4)))

Expand Down

0 comments on commit f08f25a

Please sign in to comment.