add more test cases covering chainstore equivocation

This commit is contained in:
Aayush 2023-08-09 20:50:13 -04:00
parent 2cbdedc209
commit 69f3580ca2

View File

@ -257,6 +257,11 @@ func TestEquivocations(t *testing.T) {
last = ts.TipSet.TipSet() last = ts.TipSet.TipSet()
} }
mTs, err := cg.NextTipSetFromMiners(last, []address.Address{last.Blocks()[0].Miner}, 0)
require.NoError(t, err)
require.Equal(t, 1, len(mTs.TipSet.TipSet().Cids()))
last = mTs.TipSet.TipSet()
require.NotEmpty(t, last.Blocks()) require.NotEmpty(t, last.Blocks())
blk1 := *last.Blocks()[0] blk1 := *last.Blocks()[0]
@ -291,7 +296,9 @@ func TestEquivocations(t *testing.T) {
blk3 := blk1 blk3 := blk1
blk3.Miner, err = address.NewIDAddress(blk1Miner + 100) blk3.Miner, err = address.NewIDAddress(blk1Miner + 100)
require.NoError(t, err) require.NoError(t, err)
blk3.Parents = append(blk3.Parents, blk1.Cid()) blk1Parent, err := cg.ChainStore().GetBlock(ctx, blk3.Parents[0])
require.NoError(t, err)
blk3.Parents = blk1Parent.Parents
addBlockToTracker(t, cg.ChainStore(), &blk3) addBlockToTracker(t, cg.ChainStore(), &blk3)
bestHead, bestHeadWeight, err = cg.ChainStore().FormHeaviestTipSetForHeight(ctx, last.Height()) bestHead, bestHeadWeight, err = cg.ChainStore().FormHeaviestTipSetForHeight(ctx, last.Height())
@ -329,16 +336,68 @@ func TestEquivocations(t *testing.T) {
// NOW, after all that, notify the chainstore to refresh its head // NOW, after all that, notify the chainstore to refresh its head
require.NoError(t, cg.ChainStore().RefreshHeaviestTipSet(ctx, blk1.Height+1)) require.NoError(t, cg.ChainStore().RefreshHeaviestTipSet(ctx, blk1.Height+1))
previousHead := last originalHead := *last
newHead := cg.ChainStore().GetHeaviestTipSet() newHead := cg.ChainStore().GetHeaviestTipSet()
// the newHead should be at the same height as the previousHead // the newHead should be at the same height as the originalHead
require.Equal(t, previousHead.Height(), newHead.Height()) require.Equal(t, originalHead.Height(), newHead.Height())
// the newHead should NOT be the same as the previousHead // the newHead should NOT be the same as the originalHead
require.NotEqual(t, previousHead.Key(), newHead.Key()) require.NotEqual(t, originalHead.Key(), newHead.Key())
// specifically, it should not contain any blocks by blk1Miner // specifically, it should not contain any blocks by blk1Miner
for _, b := range newHead.Blocks() { for _, b := range newHead.Blocks() {
require.NotEqual(t, blk1.Miner, b.Miner) require.NotEqual(t, blk1.Miner, b.Miner)
} }
// now have blk2's Miner equivocate too! this causes us to switch to a tipset with a different parent!
blk5 := blk2
blk5.Timestamp = blk5.Timestamp + 1
addBlockToTracker(t, cg.ChainStore(), &blk5)
// notify the chainstore to refresh its head
require.NoError(t, cg.ChainStore().RefreshHeaviestTipSet(ctx, blk1.Height+1))
newHead = cg.ChainStore().GetHeaviestTipSet()
// the newHead should still be at the same height as the originalHead
require.Equal(t, originalHead.Height(), newHead.Height())
// BUT it should no longer have the same parents -- only blk3's miner is good, and they mined on a different tipset
require.Equal(t, 1, len(newHead.Blocks()))
require.Equal(t, blk3.Cid(), newHead.Cids()[0])
require.NotEqual(t, originalHead.Parents(), newHead.Parents())
// now have blk3's Miner equivocate too! this causes us to switch to a previous epoch entirely :(
blk6 := blk3
blk6.Timestamp = blk6.Timestamp + 1
addBlockToTracker(t, cg.ChainStore(), &blk6)
// trying to form a tipset at our previous height leads to emptiness
tryTs, tryTsWeight, err := cg.ChainStore().FormHeaviestTipSetForHeight(ctx, blk1.Height)
require.NoError(t, err)
require.Nil(t, tryTs)
require.True(t, tryTsWeight.IsZero())
// notify the chainstore to refresh its head
require.NoError(t, cg.ChainStore().RefreshHeaviestTipSet(ctx, blk1.Height+1))
newHead = cg.ChainStore().GetHeaviestTipSet()
// the newHead should now be one epoch behind originalHead
require.Greater(t, originalHead.Height(), newHead.Height())
// next, we create a new tipset with only one block after many null rounds
headAfterNulls, err := cg.NextTipSetFromMiners(newHead, []address.Address{newHead.Blocks()[0].Miner}, 15)
require.NoError(t, err)
require.Equal(t, 1, len(headAfterNulls.TipSet.Blocks))
// now, we disqualify the block in this tipset because of equivocation
blkAfterNulls := headAfterNulls.TipSet.TipSet().Blocks()[0]
equivocatedBlkAfterNulls := *blkAfterNulls
equivocatedBlkAfterNulls.Timestamp = blkAfterNulls.Timestamp + 1
addBlockToTracker(t, cg.ChainStore(), &equivocatedBlkAfterNulls)
// try to form a tipset at this height -- it should be empty
tryTs2, tryTsWeight2, err := cg.ChainStore().FormHeaviestTipSetForHeight(ctx, blkAfterNulls.Height)
require.NoError(t, err)
require.Nil(t, tryTs2)
require.True(t, tryTsWeight2.IsZero())
// now we "notify" at this height -- it should fail, because we cannot refresh our head due to equivocation and nulls
require.ErrorContains(t, cg.ChainStore().RefreshHeaviestTipSet(ctx, blkAfterNulls.Height), "failed to refresh to a new valid tipset")
} }
func addBlockToTracker(t *testing.T, cs *store.ChainStore, blk *types.BlockHeader) { func addBlockToTracker(t *testing.T, cs *store.ChainStore, blk *types.BlockHeader) {