From 5c90fa88cb73e33eaa2be1d5fcd6b0be44431166 Mon Sep 17 00:00:00 2001 From: kimmy lin <30611210+countvonzero@users.noreply.github.com> Date: Thu, 7 Sep 2023 18:56:15 -0700 Subject: [PATCH] add tests --- activation/handler_test.go | 23 +++ hare3/hare_test.go | 55 +++++++ malfeasance/handler_test.go | 303 ++++++++++++++++++------------------ mesh/mesh_test.go | 36 +++++ 4 files changed, 262 insertions(+), 155 deletions(-) diff --git a/activation/handler_test.go b/activation/handler_test.go index c3bb63cb6d..09bd07e2b2 100644 --- a/activation/handler_test.go +++ b/activation/handler_test.go @@ -728,6 +728,29 @@ func TestHandler_ProcessAtx(t *testing.T) { require.Equal(t, atx2.PublishEpoch.FirstLayer(), got.MalfeasanceProof.Layer) } +func TestHandler_ProcessAtx_OwnMalfeasance(t *testing.T) { + goldenATXID := types.ATXID{2, 3, 4} + atxHdlr := newTestHandler(t, goldenATXID) + + sig, err := signing.NewEdSigner() + require.NoError(t, err) + types.SetMinerNodeID(sig.NodeID()) + + atx1 := newActivationTx(t, sig, 0, types.EmptyATXID, types.EmptyATXID, nil, types.LayerID(layersPerEpoch).GetEpoch(), 0, 100, types.Address{1, 2, 3}, 100, &types.NIPost{}) + atxHdlr.mbeacon.EXPECT().OnAtx(gomock.Any()) + atxHdlr.mtortoise.EXPECT().OnAtx(gomock.Any()) + require.NoError(t, atxHdlr.ProcessAtx(context.Background(), atx1)) + + // another atx for the same epoch is considered malicious + atx2 := newActivationTx(t, sig, 1, atx1.ID(), atx1.ID(), nil, types.LayerID(layersPerEpoch+1).GetEpoch(), 0, 100, types.Address{2, 3, 4}, 100, &types.NIPost{}) + atxHdlr.mbeacon.EXPECT().OnAtx(gomock.Any()) + atxHdlr.mtortoise.EXPECT().OnAtx(gomock.Any()) + require.NoError(t, atxHdlr.ProcessAtx(context.Background(), atx2)) + got, err := identities.IsMalicious(atxHdlr.cdb, sig.NodeID()) + require.NoError(t, err) + require.False(t, got) +} + func TestHandler_ProcessAtxStoresNewVRFNonce(t *testing.T) { // Arrange goldenATXID := types.ATXID{2, 3, 4} diff --git a/hare3/hare_test.go b/hare3/hare_test.go index f36051ec61..f8d2ac9224 100644 --- a/hare3/hare_test.go +++ b/hare3/hare_test.go @@ -699,6 +699,61 @@ func TestHandler(t *testing.T) { }) } +func TestHandlerOwnEquivocation(t *testing.T) { + t.Parallel() + tst := &tester{ + TB: t, + rng: rand.New(rand.NewSource(1001)), + start: time.Now(), + cfg: DefaultConfig(), + layerDuration: 5 * time.Minute, + beacon: types.Beacon{1, 1, 1, 1}, + genesis: types.GetEffectiveGenesis(), + } + cluster := newLockstepCluster(tst) + cluster.addActive(1) + n := cluster.nodes[0] + require.NoError(t, beacons.Add(n.db, tst.genesis.GetEpoch()+1, tst.beacon)) + require.NoError(t, atxs.Add(n.db, n.atx)) + n.oracle.UpdateActiveSet(tst.genesis.GetEpoch()+1, []types.ATXID{n.atx.ID()}) + n.mpublisher.EXPECT().Publish(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + layer := tst.genesis + 1 + n.nclock.StartLayer(layer) + n.clock.Set(tst.start. + Add(tst.layerDuration * time.Duration(layer)). + Add(tst.cfg.PreroundDelay)) + elig := n.tracer.waitEligibility() + + msg1 := &Message{} + msg1.Layer = layer + msg1.Value.Proposals = []types.ProposalID{{1}} + msg1.Eligibility = *elig + msg1.Sender = n.signer.NodeID() + msg1.Signature = n.signer.Sign(signing.HARE, msg1.ToMetadata().ToBytes()) + + msg2 := &Message{} + msg2.Layer = layer + msg2.Value.Proposals = []types.ProposalID{{2}} + msg2.Eligibility = *elig + msg2.Sender = n.signer.NodeID() + msg2.Signature = n.signer.Sign(signing.HARE, msg2.ToMetadata().ToBytes()) + + types.SetMinerNodeID(n.hare.signer.NodeID()) + + require.NoError(t, n.hare.Handler(context.Background(), "", codec.MustEncode(msg1))) + require.NoError(t, n.hare.Handler(context.Background(), "", codec.MustEncode(msg2))) + + malicious, err := n.db.IsMalicious(n.signer.NodeID()) + require.NoError(t, err) + require.False(t, malicious) + + require.ErrorContains(t, + n.hare.Handler(context.Background(), "", codec.MustEncode(msg2)), + "dropped by graded", + ) + types.SetMinerNodeID(types.EmptyNodeID) +} + func gatx(id types.ATXID, epoch types.EpochID, smesher types.NodeID, base, height uint64) types.VerifiedActivationTx { atx := &types.ActivationTx{} atx.NumUnits = 10 diff --git a/malfeasance/handler_test.go b/malfeasance/handler_test.go index 5c86a04fd6..a6eb8102f8 100644 --- a/malfeasance/handler_test.go +++ b/malfeasance/handler_test.go @@ -7,6 +7,7 @@ import ( "time" "unsafe" + "github.com/spacemeshos/go-scale" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" @@ -836,176 +837,168 @@ func TestHandler_CrossDomain(t *testing.T) { require.False(t, malicious) } -func TestHandler_HandleSyncedMalfeasanceProof_multipleATXs(t *testing.T) { - db := sql.InMemory() - lg := logtest.New(t) - ctrl := gomock.NewController(t) - trt := malfeasance.NewMocktortoise(ctrl) - mcp := malfeasance.NewMockconsensusProtocol(ctrl) - sigVerifier, err := signing.NewEdVerifier() - require.NoError(t, err) - - h := malfeasance.NewHandler(datastore.NewCachedDB(db, lg), lg, "self", types.EmptyNodeID, mcp, sigVerifier, trt) - sig, err := signing.NewEdSigner() - require.NoError(t, err) - createIdentity(t, db, sig) - - malicious, err := identities.IsMalicious(db, sig.NodeID()) - require.NoError(t, err) - require.False(t, malicious) - +func TestHandler_HandleSyncedMalfeasanceProof(t *testing.T) { lid := types.LayerID(11) - ap := types.AtxProof{ - Messages: [2]types.AtxProofMsg{ - { - InnerMsg: types.ATXMetadata{ - PublishEpoch: types.EpochID(3), - MsgHash: types.RandomHash(), - }, - }, - { - InnerMsg: types.ATXMetadata{ - PublishEpoch: types.EpochID(3), - MsgHash: types.RandomHash(), + tcs := []struct { + desc string + malType uint8 + proofData any + ownMalicious bool + expected bool + }{ + { + desc: "multiple atxs", + malType: types.MultipleATXs, + proofData: &types.AtxProof{ + Messages: [2]types.AtxProofMsg{ + { + InnerMsg: types.ATXMetadata{ + PublishEpoch: types.EpochID(3), + MsgHash: types.RandomHash(), + }, + }, + { + InnerMsg: types.ATXMetadata{ + PublishEpoch: types.EpochID(3), + MsgHash: types.RandomHash(), + }, + }, }, }, + expected: true, }, - } - - ap.Messages[0].Signature = sig.Sign(signing.ATX, ap.Messages[0].SignedBytes()) - ap.Messages[0].SmesherID = sig.NodeID() - ap.Messages[1].Signature = sig.Sign(signing.ATX, ap.Messages[1].SignedBytes()) - ap.Messages[1].SmesherID = sig.NodeID() - proof := types.MalfeasanceProof{ - Layer: lid, - Proof: types.Proof{ - Type: types.MultipleATXs, - Data: &ap, - }, - } - data, err := codec.Encode(&proof) - require.NoError(t, err) - trt.EXPECT().OnMalfeasance(sig.NodeID()) - require.NoError(t, h.HandleSyncedMalfeasanceProof(context.Background(), types.Hash32(sig.NodeID()), "peer", data)) - - malicious, err = identities.IsMalicious(db, sig.NodeID()) - require.NoError(t, err) - require.True(t, malicious) -} - -func TestHandler_HandleSyncedMalfeasanceProof_multipleBallots(t *testing.T) { - db := sql.InMemory() - lg := logtest.New(t) - ctrl := gomock.NewController(t) - trt := malfeasance.NewMocktortoise(ctrl) - mcp := malfeasance.NewMockconsensusProtocol(ctrl) - sigVerifier, err := signing.NewEdVerifier() - require.NoError(t, err) - - h := malfeasance.NewHandler(datastore.NewCachedDB(db, lg), lg, "self", types.EmptyNodeID, mcp, sigVerifier, trt) - sig, err := signing.NewEdSigner() - require.NoError(t, err) - createIdentity(t, db, sig) - - malicious, err := identities.IsMalicious(db, sig.NodeID()) - require.NoError(t, err) - require.False(t, malicious) - - lid := types.LayerID(11) - bp := types.BallotProof{ - Messages: [2]types.BallotProofMsg{ - { - InnerMsg: types.BallotMetadata{ - Layer: lid, - MsgHash: types.RandomHash(), - }, - }, - { - InnerMsg: types.BallotMetadata{ - Layer: lid, - MsgHash: types.RandomHash(), + { + desc: "multiple ballots", + malType: types.MultipleBallots, + proofData: &types.BallotProof{ + Messages: [2]types.BallotProofMsg{ + { + InnerMsg: types.BallotMetadata{ + Layer: lid, + MsgHash: types.RandomHash(), + }, + }, + { + InnerMsg: types.BallotMetadata{ + Layer: lid, + MsgHash: types.RandomHash(), + }, + }, }, }, + expected: true, }, - } - bp.Messages[0].Signature = sig.Sign(signing.BALLOT, bp.Messages[0].SignedBytes()) - bp.Messages[0].SmesherID = sig.NodeID() - bp.Messages[1].Signature = sig.Sign(signing.BALLOT, bp.Messages[1].SignedBytes()) - bp.Messages[1].SmesherID = sig.NodeID() - proof := types.MalfeasanceProof{ - Layer: lid, - Proof: types.Proof{ - Type: types.MultipleBallots, - Data: &bp, - }, - } - data, err := codec.Encode(&proof) - require.NoError(t, err) - trt.EXPECT().OnMalfeasance(sig.NodeID()) - require.NoError(t, h.HandleSyncedMalfeasanceProof(context.Background(), types.Hash32(sig.NodeID()), "peer", data)) - - malicious, err = identities.IsMalicious(db, sig.NodeID()) - require.NoError(t, err) - require.True(t, malicious) -} - -func TestHandler_HandleSyncedMalfeasanceProof_hareEquivocation(t *testing.T) { - db := sql.InMemory() - lg := logtest.New(t) - ctrl := gomock.NewController(t) - trt := malfeasance.NewMocktortoise(ctrl) - mcp := malfeasance.NewMockconsensusProtocol(ctrl) - sigVerifier, err := signing.NewEdVerifier() - require.NoError(t, err) - - h := malfeasance.NewHandler(datastore.NewCachedDB(db, lg), lg, "self", types.EmptyNodeID, mcp, sigVerifier, trt) - sig, err := signing.NewEdSigner() - require.NoError(t, err) - createIdentity(t, db, sig) - - malicious, err := identities.IsMalicious(db, sig.NodeID()) - require.NoError(t, err) - require.False(t, malicious) - - lid := types.LayerID(11) - hp := types.HareProof{ - Messages: [2]types.HareProofMsg{ - { - InnerMsg: types.HareMetadata{ - Layer: lid, - Round: 3, - MsgHash: types.RandomHash(), + { + desc: "hare equivocation", + malType: types.HareEquivocation, + proofData: &types.HareProof{ + Messages: [2]types.HareProofMsg{ + { + InnerMsg: types.HareMetadata{ + Layer: lid, + Round: 3, + MsgHash: types.RandomHash(), + }, + }, + { + InnerMsg: types.HareMetadata{ + Layer: lid, + Round: 3, + MsgHash: types.RandomHash(), + }, + }, }, }, - { - InnerMsg: types.HareMetadata{ - Layer: lid, - Round: 3, - MsgHash: types.RandomHash(), + expected: true, + }, + { + desc: "own malicious", + malType: types.MultipleATXs, + proofData: &types.AtxProof{ + Messages: [2]types.AtxProofMsg{ + { + InnerMsg: types.ATXMetadata{ + PublishEpoch: types.EpochID(3), + MsgHash: types.RandomHash(), + }, + }, + { + InnerMsg: types.ATXMetadata{ + PublishEpoch: types.EpochID(3), + MsgHash: types.RandomHash(), + }, + }, }, }, + ownMalicious: true, + expected: false, }, } - hp.Messages[0].Signature = sig.Sign(signing.HARE, hp.Messages[0].SignedBytes()) - hp.Messages[0].SmesherID = sig.NodeID() - hp.Messages[1].Signature = sig.Sign(signing.HARE, hp.Messages[1].SignedBytes()) - hp.Messages[1].SmesherID = sig.NodeID() + for _, tc := range tcs { + tc := tc + t.Run(tc.desc, func(t *testing.T) { + db := sql.InMemory() + lg := logtest.New(t) + ctrl := gomock.NewController(t) + trt := malfeasance.NewMocktortoise(ctrl) + mcp := malfeasance.NewMockconsensusProtocol(ctrl) + sigVerifier, err := signing.NewEdVerifier() + require.NoError(t, err) + + h := malfeasance.NewHandler(datastore.NewCachedDB(db, lg), lg, "self", types.EmptyNodeID, mcp, sigVerifier, trt) + sig, err := signing.NewEdSigner() + require.NoError(t, err) + createIdentity(t, db, sig) + if tc.ownMalicious { + types.SetMinerNodeID(sig.NodeID()) + } else { + trt.EXPECT().OnMalfeasance(sig.NodeID()) + } + + malicious, err := identities.IsMalicious(db, sig.NodeID()) + require.NoError(t, err) + require.False(t, malicious) + + lid := types.LayerID(11) + var pdata scale.Type + switch tc.malType { + case types.MultipleATXs: + ap := tc.proofData.(*types.AtxProof) + for i := range ap.Messages { + ap.Messages[i].Signature = sig.Sign(signing.ATX, ap.Messages[i].SignedBytes()) + ap.Messages[i].SmesherID = sig.NodeID() + } + pdata = ap + case types.MultipleBallots: + bp := tc.proofData.(*types.BallotProof) + for i := range bp.Messages { + bp.Messages[i].Signature = sig.Sign(signing.BALLOT, bp.Messages[i].SignedBytes()) + bp.Messages[i].SmesherID = sig.NodeID() + } + pdata = bp + case types.HareEquivocation: + hp := tc.proofData.(*types.HareProof) + for i := range hp.Messages { + hp.Messages[i].Signature = sig.Sign(signing.HARE, hp.Messages[i].SignedBytes()) + hp.Messages[i].SmesherID = sig.NodeID() + } + pdata = hp + } + data, err := codec.Encode(&types.MalfeasanceProof{ + Layer: lid, + Proof: types.Proof{ + Type: tc.malType, + Data: pdata, + }, + }) + require.NoError(t, err) + require.NoError(t, h.HandleSyncedMalfeasanceProof(context.Background(), types.Hash32(sig.NodeID()), "peer", data)) - proof := types.MalfeasanceProof{ - Layer: lid, - Proof: types.Proof{ - Type: types.HareEquivocation, - Data: &hp, - }, + malicious, err = identities.IsMalicious(db, sig.NodeID()) + require.NoError(t, err) + require.Equal(t, tc.expected, malicious) + }) } - data, err := codec.Encode(&proof) - require.NoError(t, err) - trt.EXPECT().OnMalfeasance(sig.NodeID()) - require.NoError(t, h.HandleSyncedMalfeasanceProof(context.Background(), types.Hash32(sig.NodeID()), "peer", data)) - - malicious, err = identities.IsMalicious(db, sig.NodeID()) - require.NoError(t, err) - require.True(t, malicious) } func TestHandler_HandleSyncedMalfeasanceProof_wrongHash(t *testing.T) { diff --git a/mesh/mesh_test.go b/mesh/mesh_test.go index 8426f97906..7c7206269a 100644 --- a/mesh/mesh_test.go +++ b/mesh/mesh_test.go @@ -366,6 +366,42 @@ func TestMesh_MaliciousBallots(t *testing.T) { require.EqualValues(t, expected, saved) } +func TestMesh_OwnMaliciousBallots(t *testing.T) { + tm := createTestMesh(t) + + lid := types.LayerID(1) + sig, err := signing.NewEdSigner() + require.NoError(t, err) + createIdentity(t, tm.cdb, sig) + types.SetMinerNodeID(sig.NodeID()) + + var blts []*types.Ballot + for i := 0; i < 2; i++ { + b := &types.Ballot{ + InnerBallot: types.InnerBallot{ + Layer: lid, + OpinionHash: types.RandomHash(), + }, + SmesherID: sig.NodeID(), + } + b.Signature = sig.Sign(signing.BALLOT, b.SignedBytes()) + require.NoError(t, b.Initialize()) + blts = append(blts, b) + } + malProof, err := tm.AddBallot(context.Background(), blts[0]) + require.NoError(t, err) + require.Nil(t, malProof) + require.False(t, blts[0].IsMalicious()) + // second one will NOT create a MalfeasanceProof + malProof, err = tm.AddBallot(context.Background(), blts[1]) + require.NoError(t, err) + require.Nil(t, malProof) + require.False(t, blts[1].IsMalicious()) + got, err := identities.IsMalicious(tm.cdb, sig.NodeID()) + require.NoError(t, err) + require.False(t, got) +} + func TestProcessLayer(t *testing.T) { t.Parallel() type call struct {