public void When_deleting_invalid_block_deletes_its_descendants() { MemDb blocksDb = new MemDb(); MemDb blockInfosDb = new MemDb(); BlockTree tree = new BlockTree(blocksDb, blockInfosDb, MainNetSpecProvider.Instance, NullTransactionPool.Instance, LimboLogs.Instance); Block block0 = Build.A.Block.WithNumber(0).WithDifficulty(1).TestObject; Block block1 = Build.A.Block.WithNumber(1).WithDifficulty(2).WithParent(block0).TestObject; Block block2 = Build.A.Block.WithNumber(2).WithDifficulty(3).WithParent(block1).TestObject; Block block3 = Build.A.Block.WithNumber(3).WithDifficulty(4).WithParent(block2).TestObject; tree.SuggestBlock(block0); tree.SuggestBlock(block1); tree.SuggestBlock(block2); tree.SuggestBlock(block3); tree.UpdateMainChain(block0); tree.UpdateMainChain(block1); tree.DeleteInvalidBlock(block2); Assert.AreEqual(UInt256.One, tree.BestKnownNumber, "best known"); Assert.AreEqual(UInt256.One, tree.Head.Number, "head"); Assert.AreEqual(UInt256.One, tree.BestSuggested.Number, "suggested"); Assert.NotNull(blocksDb.Get(block1.Hash), "block 1"); Assert.IsNull(blocksDb.Get(block2.Hash), "block 2"); Assert.IsNull(blocksDb.Get(block3.Hash), "block 3"); Assert.NotNull(blockInfosDb.Get(1), "level 1"); Assert.IsNull(blockInfosDb.Get(2), "level 2"); Assert.IsNull(blockInfosDb.Get(3), "level 3"); }
public async Task Can_load_from_DB_when_there_is_only_an_invalid_chain_in_DB() { MemDb blocksDb = new MemDb(); MemDb blockInfosDb = new MemDb(); MemDb headersDb = new MemDb(); BlockTree tree1 = new BlockTree(blocksDb, headersDb, blockInfosDb, new ChainLevelInfoRepository(blockInfosDb), MainnetSpecProvider.Instance, NullTxPool.Instance, NullBloomStorage.Instance, LimboLogs.Instance); Block block0 = Build.A.Block.WithNumber(0).WithDifficulty(1).TestObject; Block block1 = Build.A.Block.WithNumber(1).WithDifficulty(2).WithParent(block0).TestObject; Block block2 = Build.A.Block.WithNumber(2).WithDifficulty(3).WithParent(block1).TestObject; Block block3 = Build.A.Block.WithNumber(3).WithDifficulty(4).WithParent(block2).TestObject; tree1.SuggestBlock(block0); tree1.SuggestBlock(block1); tree1.SuggestBlock(block2); tree1.SuggestBlock(block3); tree1.UpdateMainChain(block0); BlockTree tree2 = new BlockTree(blocksDb, headersDb, blockInfosDb, new ChainLevelInfoRepository(blockInfosDb), MainnetSpecProvider.Instance, NullTxPool.Instance, NullBloomStorage.Instance, LimboLogs.Instance); CancellationTokenSource tokenSource = new CancellationTokenSource(); #pragma warning disable 4014 Task.Delay(_dbLoadTimeout).ContinueWith(t => tokenSource.Cancel()); #pragma warning restore 4014 tree2.NewBestSuggestedBlock += (sender, args) => { if (args.Block.Hash == block1.Hash) { tree2.DeleteInvalidBlock(args.Block); } else { tree2.UpdateMainChain(args.Block); } }; DbBlocksLoader loader = new DbBlocksLoader(tree2, LimboNoErrorLogger.Instance, null, 1); await tree2.Accept(loader, tokenSource.Token); /* note the block tree historically loads one less block than it could */ Assert.AreEqual(0L, tree2.BestKnownNumber, "best known"); Assert.AreEqual(block0.Hash, tree2.Head.Hash, "head"); Assert.AreEqual(block0.Hash, tree2.BestSuggestedHeader.Hash, "suggested"); Assert.IsNull(blocksDb.Get(block1.Hash), "block 1"); Assert.IsNull(blocksDb.Get(block2.Hash), "block 2"); Assert.IsNull(blocksDb.Get(block3.Hash), "block 3"); Assert.IsNull(blockInfosDb.Get(1), "level 1"); Assert.IsNull(blockInfosDb.Get(2), "level 2"); Assert.IsNull(blockInfosDb.Get(3), "level 3"); }
public async Task Can_load_from_DB_when_there_is_only_an_invalid_chain_in_DB() { MemDb blocksDb = new MemDb(); MemDb blockInfosDb = new MemDb(); BlockTree tree1 = new BlockTree(blocksDb, blockInfosDb, MainNetSpecProvider.Instance, NullTransactionPool.Instance, LimboLogs.Instance); Block block0 = Build.A.Block.WithNumber(0).WithDifficulty(1).TestObject; Block block1 = Build.A.Block.WithNumber(1).WithDifficulty(2).WithParent(block0).TestObject; Block block2 = Build.A.Block.WithNumber(2).WithDifficulty(3).WithParent(block1).TestObject; Block block3 = Build.A.Block.WithNumber(3).WithDifficulty(4).WithParent(block2).TestObject; tree1.SuggestBlock(block0); tree1.SuggestBlock(block1); tree1.SuggestBlock(block2); tree1.SuggestBlock(block3); tree1.UpdateMainChain(block0); BlockTree tree2 = new BlockTree(blocksDb, blockInfosDb, MainNetSpecProvider.Instance, NullTransactionPool.Instance, LimboLogs.Instance); CancellationTokenSource tokenSource = new CancellationTokenSource(); #pragma warning disable 4014 Task.Delay(_dbLoadTimeout).ContinueWith(t => tokenSource.Cancel()); #pragma warning restore 4014 tree2.NewBestSuggestedBlock += (sender, args) => { if (args.Block.Hash == block1.Hash) { tree2.DeleteInvalidBlock(args.Block); } else { tree2.UpdateMainChain(args.Block); } }; await tree2.LoadBlocksFromDb(tokenSource.Token, startBlockNumber : null, batchSize : 1); /* note the block tree historically loads one less block than it could */ Assert.AreEqual((UInt256)0, tree2.BestKnownNumber, "best known"); Assert.AreEqual(block0.Hash, tree2.Head.Hash, "head"); Assert.AreEqual(block0.Hash, tree2.BestSuggested.Hash, "suggested"); Assert.IsNull(blocksDb.Get(block1.Hash), "block 1"); Assert.IsNull(blocksDb.Get(block2.Hash), "block 2"); Assert.IsNull(blocksDb.Get(block3.Hash), "block 3"); Assert.IsNull(blockInfosDb.Get(1), "level 1"); Assert.IsNull(blockInfosDb.Get(2), "level 2"); Assert.IsNull(blockInfosDb.Get(3), "level 3"); }
public void After_removing_invalid_block_will_not_accept_it_again() { MemDb blocksDb = new MemDb(); MemDb blockInfosDb = new MemDb(); BlockTree tree = new BlockTree(blocksDb, blockInfosDb, MainNetSpecProvider.Instance, NullTransactionPool.Instance, LimboLogs.Instance); Block block0 = Build.A.Block.WithNumber(0).WithDifficulty(1).TestObject; Block block1 = Build.A.Block.WithNumber(1).WithDifficulty(2).WithParent(block0).TestObject; Block block2 = Build.A.Block.WithNumber(2).WithDifficulty(3).WithParent(block1).TestObject; Block block3 = Build.A.Block.WithNumber(3).WithDifficulty(4).WithParent(block2).TestObject; tree.SuggestBlock(block0); tree.SuggestBlock(block1); tree.SuggestBlock(block2); tree.SuggestBlock(block3); tree.DeleteInvalidBlock(block1); AddBlockResult result = tree.SuggestBlock(block1); Assert.AreEqual(AddBlockResult.InvalidBlock, result); }
public void When_deleting_invalid_block_sets_head_bestKnown_and_suggested_right() { BlockTree tree = BuildBlockTree(); Block block0 = Build.A.Block.WithNumber(0).WithDifficulty(1).TestObject; Block block1 = Build.A.Block.WithNumber(1).WithDifficulty(2).WithParent(block0).TestObject; Block block2 = Build.A.Block.WithNumber(2).WithDifficulty(3).WithParent(block1).TestObject; Block block3 = Build.A.Block.WithNumber(3).WithDifficulty(4).WithParent(block2).TestObject; tree.SuggestBlock(block0); tree.SuggestBlock(block1); tree.SuggestBlock(block2); tree.SuggestBlock(block3); tree.UpdateMainChain(block0); tree.UpdateMainChain(block1); tree.DeleteInvalidBlock(block2); Assert.AreEqual(block1.Number, tree.BestKnownNumber); Assert.AreEqual(block1.Header, tree.Head); Assert.AreEqual(block1.Header, tree.BestSuggested); }
public async Task Can_load_from_DB_when_there_is_an_invalid_block_in_DB_and_a_valid_branch() { MemDb blocksDb = new MemDb(); MemDb blockInfosDb = new MemDb(); MemDb headersDb = new MemDb(); BlockTree tree1 = new BlockTree(blocksDb, headersDb, blockInfosDb, new ChainLevelInfoRepository(blockInfosDb), MainnetSpecProvider.Instance, NullTxPool.Instance, NullBloomStorage.Instance, LimboLogs.Instance); Block block0 = Build.A.Block.WithNumber(0).WithDifficulty(1).TestObject; Block block1 = Build.A.Block.WithNumber(1).WithDifficulty(2).WithParent(block0).TestObject; Block block2 = Build.A.Block.WithNumber(2).WithDifficulty(3).WithParent(block1).TestObject; Block block3 = Build.A.Block.WithNumber(3).WithDifficulty(4).WithParent(block2).TestObject; Block block1B = Build.A.Block.WithNumber(1).WithDifficulty(1).WithParent(block0).TestObject; Block block2B = Build.A.Block.WithNumber(2).WithDifficulty(1).WithParent(block1B).TestObject; Block block3B = Build.A.Block.WithNumber(3).WithDifficulty(1).WithParent(block2B).TestObject; tree1.SuggestBlock(block0); tree1.SuggestBlock(block1); // invalid block tree1.SuggestBlock(block2); // invalid branch tree1.SuggestBlock(block3); // invalid branch tree1.SuggestBlock(block1B); tree1.SuggestBlock(block2B); tree1.SuggestBlock(block3B); // expected to be head tree1.UpdateMainChain(block0); BlockTree tree2 = new BlockTree(blocksDb, headersDb, blockInfosDb, new ChainLevelInfoRepository(blockInfosDb), MainnetSpecProvider.Instance, NullTxPool.Instance, NullBloomStorage.Instance, LimboLogs.Instance); CancellationTokenSource tokenSource = new CancellationTokenSource(); #pragma warning disable 4014 Task.Delay(_dbLoadTimeout).ContinueWith(t => tokenSource.Cancel()); #pragma warning restore 4014 tree2.NewBestSuggestedBlock += (sender, args) => { if (args.Block.Hash == block1.Hash) { tree2.DeleteInvalidBlock(args.Block); } else { tree2.UpdateMainChain(args.Block); } }; DbBlocksLoader loader = new DbBlocksLoader(tree2, LimboNoErrorLogger.Instance, null, 1); await tree2.Accept(loader, tokenSource.Token); Assert.AreEqual(3L, tree2.BestKnownNumber, "best known"); tree2.Head.Header.Should().BeEquivalentTo(block3B.Header, options => { return(options.Excluding(t => t.MaybeParent)); }); tree2.BestSuggestedHeader.Should().BeEquivalentTo(block3B.Header, options => { return(options.Excluding(t => t.MaybeParent)); }); Assert.IsNull(blocksDb.Get(block1.Hash), "block 1"); Assert.IsNull(blocksDb.Get(block2.Hash), "block 2"); Assert.IsNull(blocksDb.Get(block3.Hash), "block 3"); Assert.NotNull(blockInfosDb.Get(1), "level 1"); Assert.NotNull(blockInfosDb.Get(2), "level 2"); Assert.NotNull(blockInfosDb.Get(3), "level 3"); }