diff --git a/chain/sync.go b/chain/sync.go index 5a5362ccd..5d3c1d992 100644 --- a/chain/sync.go +++ b/chain/sync.go @@ -727,6 +727,11 @@ func (syncer *Syncer) ValidateBlock(ctx context.Context, b *types.FullBlock, use } // fast checks first + + if h.Height <= baseTs.Height() { + return xerrors.Errorf("block height not greater than parent height: %d != %d", h.Height, baseTs.Height()) + } + nulls := h.Height - (baseTs.Height() + 1) if tgtTs := baseTs.MinTimestamp() + build.BlockDelaySecs*uint64(nulls+1); h.Timestamp != tgtTs { return xerrors.Errorf("block has wrong timestamp: %d != %d", h.Timestamp, tgtTs) diff --git a/chain/sync_test.go b/chain/sync_test.go index 5312dff0b..bda8c60ee 100644 --- a/chain/sync_test.go +++ b/chain/sync_test.go @@ -230,7 +230,7 @@ func (tu *syncTestUtil) pushTsExpectErr(to int, fts *store.FullTipSet, experr bo } } -func (tu *syncTestUtil) mineOnBlock(blk *store.FullTipSet, to int, miners []int, wait, fail bool, msgs [][]*types.SignedMessage, nulls abi.ChainEpoch) *store.FullTipSet { +func (tu *syncTestUtil) mineOnBlock(blk *store.FullTipSet, to int, miners []int, wait, fail bool, msgs [][]*types.SignedMessage, nulls abi.ChainEpoch, push bool) *store.FullTipSet { if miners == nil { for i := range tu.g.Miners { miners = append(miners, i) @@ -247,7 +247,7 @@ func (tu *syncTestUtil) mineOnBlock(blk *store.FullTipSet, to int, miners []int, var nts *store.FullTipSet var err error if msgs != nil { - nts, err = tu.g.NextTipSetFromMinersWithMessagesAndNulls(blk.TipSet(), maddrs, msgs, 0) + nts, err = tu.g.NextTipSetFromMinersWithMessagesAndNulls(blk.TipSet(), maddrs, msgs, nulls) require.NoError(tu.t, err) } else { mt, err := tu.g.NextTipSetFromMiners(blk.TipSet(), maddrs, nulls) @@ -255,17 +255,19 @@ func (tu *syncTestUtil) mineOnBlock(blk *store.FullTipSet, to int, miners []int, nts = mt.TipSet } - if fail { - tu.pushTsExpectErr(to, nts, true) - } else { - tu.pushFtsAndWait(to, nts, wait) + if push { + if fail { + tu.pushTsExpectErr(to, nts, true) + } else { + tu.pushFtsAndWait(to, nts, wait) + } } return nts } func (tu *syncTestUtil) mineNewBlock(src int, miners []int) { - mts := tu.mineOnBlock(tu.g.CurTipset, src, miners, true, false, nil, 0) + mts := tu.mineOnBlock(tu.g.CurTipset, src, miners, true, false, nil, 0, true) tu.g.CurTipset = mts } @@ -510,7 +512,7 @@ func TestSyncBadTimestamp(t *testing.T) { fmt.Println("BASE: ", base.Cids()) tu.printHeads() - a1 := tu.mineOnBlock(base, 0, nil, false, true, nil, 0) + a1 := tu.mineOnBlock(base, 0, nil, false, true, nil, 0, true) tu.g.Timestamper = nil require.NoError(t, tu.g.ResyncBankerNonce(a1.TipSet())) @@ -519,7 +521,7 @@ func TestSyncBadTimestamp(t *testing.T) { fmt.Println("After mine bad block!") tu.printHeads() - a2 := tu.mineOnBlock(base, 0, nil, true, false, nil, 0) + a2 := tu.mineOnBlock(base, 0, nil, true, false, nil, 0, true) tu.waitUntilSync(0, client) @@ -563,7 +565,7 @@ func TestSyncBadWinningPoSt(t *testing.T) { tu.g.SetWinningPoStProver(tu.g.Miners[1], &badWpp{}) // now ensure that new blocks are not accepted - tu.mineOnBlock(base, client, nil, false, true, nil, 0) + tu.mineOnBlock(base, client, nil, false, true, nil, 0, true) } func (tu *syncTestUtil) loadChainToNode(to int) { @@ -613,16 +615,16 @@ func TestSyncFork(t *testing.T) { fmt.Println("Mining base: ", base.TipSet().Cids(), base.TipSet().Height()) // The two nodes fork at this point into 'a' and 'b' - a1 := tu.mineOnBlock(base, p1, []int{0}, true, false, nil, 0) - a := tu.mineOnBlock(a1, p1, []int{0}, true, false, nil, 0) - a = tu.mineOnBlock(a, p1, []int{0}, true, false, nil, 0) + a1 := tu.mineOnBlock(base, p1, []int{0}, true, false, nil, 0, true) + a := tu.mineOnBlock(a1, p1, []int{0}, true, false, nil, 0, true) + a = tu.mineOnBlock(a, p1, []int{0}, true, false, nil, 0, true) require.NoError(t, tu.g.ResyncBankerNonce(a1.TipSet())) // chain B will now be heaviest - b := tu.mineOnBlock(base, p2, []int{1}, true, false, nil, 0) - b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0) - b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0) - b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0) + b := tu.mineOnBlock(base, p2, []int{1}, true, false, nil, 0, true) + b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0, true) + b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0, true) + b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0, true) fmt.Println("A: ", a.Cids(), a.TipSet().Height()) fmt.Println("B: ", b.Cids(), b.TipSet().Height()) @@ -686,13 +688,13 @@ func TestDuplicateNonce(t *testing.T) { msgs[k] = []*types.SignedMessage{makeMsg(tu.g.Miners[k])} } - ts1 := tu.mineOnBlock(base, 0, []int{0, 1}, true, false, msgs, 0) + ts1 := tu.mineOnBlock(base, 0, []int{0, 1}, true, false, msgs, 0, true) tu.waitUntilSyncTarget(0, ts1.TipSet()) // mine another tipset - ts2 := tu.mineOnBlock(ts1, 0, []int{0, 1}, true, false, make([][]*types.SignedMessage, 2), 0) + ts2 := tu.mineOnBlock(ts1, 0, []int{0, 1}, true, false, make([][]*types.SignedMessage, 2), 0, true) tu.waitUntilSyncTarget(0, ts2.TipSet()) var includedMsg cid.Cid @@ -778,7 +780,7 @@ func TestBadNonce(t *testing.T) { msgs := make([][]*types.SignedMessage, 1) msgs[0] = []*types.SignedMessage{makeBadMsg()} - tu.mineOnBlock(base, 0, []int{0}, true, true, msgs, 0) + tu.mineOnBlock(base, 0, []int{0}, true, true, msgs, 0, true) } // This test introduces a block that has 2 messages, with the same sender, and same nonce. @@ -832,7 +834,7 @@ func TestMismatchedNoncesRobustID(t *testing.T) { msgs := make([][]*types.SignedMessage, 1) msgs[0] = []*types.SignedMessage{makeMsg(false), makeMsg(true)} - tu.mineOnBlock(base, 0, []int{0}, true, true, msgs, 0) + tu.mineOnBlock(base, 0, []int{0}, true, true, msgs, 0, true) } // This test introduces a block that has 2 messages, with the same sender, and nonces N and N+1 (so both can be included in a block) @@ -886,7 +888,7 @@ func TestMatchedNoncesRobustID(t *testing.T) { msgs := make([][]*types.SignedMessage, 1) msgs[0] = []*types.SignedMessage{makeMsg(ba.Nonce, false), makeMsg(ba.Nonce+1, true)} - tu.mineOnBlock(base, 0, []int{0}, true, false, msgs, 0) + tu.mineOnBlock(base, 0, []int{0}, true, false, msgs, 0, true) } func BenchmarkSyncBasic(b *testing.B) { @@ -951,19 +953,19 @@ func TestSyncCheckpointHead(t *testing.T) { fmt.Println("Mining base: ", base.TipSet().Cids(), base.TipSet().Height()) // The two nodes fork at this point into 'a' and 'b' - a1 := tu.mineOnBlock(base, p1, []int{0}, true, false, nil, 0) - a := tu.mineOnBlock(a1, p1, []int{0}, true, false, nil, 0) - a = tu.mineOnBlock(a, p1, []int{0}, true, false, nil, 0) + a1 := tu.mineOnBlock(base, p1, []int{0}, true, false, nil, 0, true) + a := tu.mineOnBlock(a1, p1, []int{0}, true, false, nil, 0, true) + a = tu.mineOnBlock(a, p1, []int{0}, true, false, nil, 0, true) tu.waitUntilSyncTarget(p1, a.TipSet()) tu.checkpointTs(p1, a.TipSet().Key()) require.NoError(t, tu.g.ResyncBankerNonce(a1.TipSet())) // chain B will now be heaviest - b := tu.mineOnBlock(base, p2, []int{1}, true, false, nil, 0) - b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0) - b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0) - b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0) + b := tu.mineOnBlock(base, p2, []int{1}, true, false, nil, 0, true) + b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0, true) + b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0, true) + b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0, true) fmt.Println("A: ", a.Cids(), a.TipSet().Height()) fmt.Println("B: ", b.Cids(), b.TipSet().Height()) @@ -998,19 +1000,19 @@ func TestSyncCheckpointEarlierThanHead(t *testing.T) { fmt.Println("Mining base: ", base.TipSet().Cids(), base.TipSet().Height()) // The two nodes fork at this point into 'a' and 'b' - a1 := tu.mineOnBlock(base, p1, []int{0}, true, false, nil, 0) - a := tu.mineOnBlock(a1, p1, []int{0}, true, false, nil, 0) - a = tu.mineOnBlock(a, p1, []int{0}, true, false, nil, 0) + a1 := tu.mineOnBlock(base, p1, []int{0}, true, false, nil, 0, true) + a := tu.mineOnBlock(a1, p1, []int{0}, true, false, nil, 0, true) + a = tu.mineOnBlock(a, p1, []int{0}, true, false, nil, 0, true) tu.waitUntilSyncTarget(p1, a.TipSet()) tu.checkpointTs(p1, a1.TipSet().Key()) require.NoError(t, tu.g.ResyncBankerNonce(a1.TipSet())) // chain B will now be heaviest - b := tu.mineOnBlock(base, p2, []int{1}, true, false, nil, 0) - b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0) - b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0) - b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0) + b := tu.mineOnBlock(base, p2, []int{1}, true, false, nil, 0, true) + b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0, true) + b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0, true) + b = tu.mineOnBlock(b, p2, []int{1}, true, false, nil, 0, true) fmt.Println("A: ", a.Cids(), a.TipSet().Height()) fmt.Println("B: ", b.Cids(), b.TipSet().Height()) @@ -1048,7 +1050,7 @@ func TestDrandNull(t *testing.T) { pers := crypto.DomainSeparationTag_WinningPoStChallengeSeed beforeNull := tu.g.CurTipset - afterNull := tu.mineOnBlock(beforeNull, p0, nil, false, false, nil, 2) + afterNull := tu.mineOnBlock(beforeNull, p0, nil, false, false, nil, 2, true) nullHeight := beforeNull.TipSet().Height() + 1 if afterNull.TipSet().Height() == nullHeight { t.Fatal("didn't inject nulls as expected") @@ -1065,14 +1067,14 @@ func TestDrandNull(t *testing.T) { require.Equal(t, []byte(rand), expectedRand) // zoom zoom to past the v5 upgrade by injecting many many nulls - postUpgrade := tu.mineOnBlock(afterNull, p0, nil, false, false, nil, v5h) + postUpgrade := tu.mineOnBlock(afterNull, p0, nil, false, false, nil, v5h, true) nv, err := tu.nds[p0].StateNetworkVersion(tu.ctx, postUpgrade.TipSet().Key()) require.NoError(t, err) if nv != network.Version13 { t.Fatal("expect to be v13 by now") } - afterNull = tu.mineOnBlock(postUpgrade, p0, nil, false, false, nil, 2) + afterNull = tu.mineOnBlock(postUpgrade, p0, nil, false, false, nil, 2, true) nullHeight = postUpgrade.TipSet().Height() + 1 if afterNull.TipSet().Height() == nullHeight { t.Fatal("didn't inject nulls as expected") @@ -1104,3 +1106,22 @@ func TestDrandNull(t *testing.T) { build.UpgradeHyperdriveHeight = ov5h } + +func TestInvalidHeight(t *testing.T) { + H := 50 + tu := prepSyncTest(t, H) + + client := tu.addClientNode() + + require.NoError(t, tu.mn.LinkAll()) + tu.connect(client, 0) + tu.waitUntilSync(0, client) + + base := tu.g.CurTipset + + for i := 0; i < 5; i++ { + base = tu.mineOnBlock(base, 0, nil, false, false, nil, 0, false) + } + + tu.mineOnBlock(base, 0, nil, false, true, nil, -1, true) +}