public void When_head_block_is_followed_by_a_block_bodies_gap_it_should_delete_all_levels_after_the_gap_start()
        {
            MemDb     blocksDb     = new MemDb();
            MemDb     blockInfosDb = new MemDb();
            MemDb     headersDb    = new MemDb();
            BlockTree tree         = new BlockTree(blocksDb, headersDb, blockInfosDb, new ChainLevelInfoRepository(blockInfosDb), MainnetSpecProvider.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     block4       = Build.A.Block.WithNumber(4).WithDifficulty(5).WithParent(block3).TestObject;
            Block     block5       = Build.A.Block.WithNumber(5).WithDifficulty(6).WithParent(block4).TestObject;

            tree.SuggestBlock(block0);
            tree.SuggestBlock(block1);
            tree.SuggestBlock(block2);
            tree.SuggestHeader(block3.Header);
            tree.SuggestHeader(block4.Header);
            tree.SuggestBlock(block5);

            tree.UpdateMainChain(block2);

            StartupBlockTreeFixer fixer = new StartupBlockTreeFixer(new SyncConfig(), tree, LimboNoErrorLogger.Instance);

            tree.Accept(fixer, CancellationToken.None);

            Assert.Null(blockInfosDb.Get(3), "level 3");
            Assert.Null(blockInfosDb.Get(4), "level 4");
            Assert.Null(blockInfosDb.Get(5), "level 5");

            Assert.AreEqual(2L, tree.BestKnownNumber, "best known");
            Assert.AreEqual(block2.Header, tree.Head?.Header, "head");
            Assert.AreEqual(block2.Hash, tree.BestSuggestedHeader.Hash, "suggested");
        }
示例#2
0
        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");
        }
示例#3
0
        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_read_dependent_items_from_state_db_while_waiting_for_dependencies()
        {
            StateDb codeDb  = new StateDb();
            StateDb stateDB = new StateDb();
            MemDb   tempDb  = new MemDb();

            SyncConfig syncConfig = new SyncConfig();

            syncConfig.FastSync = true;

            IBlockTree    blockTree = Substitute.For <IBlockTree>();
            ISyncPeerPool pool      = Substitute.For <ISyncPeerPool>();

            SyncProgressResolver syncProgressResolver = new SyncProgressResolver(blockTree, NullReceiptStorage.Instance, stateDB, new MemDb(), syncConfig, LimboLogs.Instance);
            ISyncModeSelector    syncModeSelector     = new MultiSyncModeSelector(syncProgressResolver, pool, syncConfig, LimboLogs.Instance);
            StateSyncFeed        stateSyncFeed        = new StateSyncFeed(codeDb, stateDB, tempDb, syncModeSelector, blockTree, LimboLogs.Instance);

            // so we want to setup a trie in a structure of -> branch into two leaves
            // so we can respond with the branch node and with leaves missing
            // and we can prove that we can read the branch from the temp DB while it is still missing from the State DB

            AccountDecoder accountDecoder = new AccountDecoder();

            TrieNode leaf   = TrieNodeFactory.CreateLeaf(new HexPrefix(true, new byte[] { 1, 2, 3 }), accountDecoder.Encode(Account.TotallyEmpty).Bytes);
            TrieNode branch = TrieNodeFactory.CreateBranch();

            branch.SetChild(1, leaf);
            branch.ResolveKey(true);

            // PatriciaTree tree = new PatriciaTree();
            // tree = new PatriciaTree();
            // tree.Set(branch.Keccak.Bytes, branch.Value);

            stateSyncFeed.ResetStateRoot(0, branch.Keccak);

            var request = await stateSyncFeed.PrepareRequest();

            BuildRequestAndHandleResponse(branch, request, stateSyncFeed);

            byte[] value = tempDb.Get(branch.Keccak);
            value.Should().BeEquivalentTo(branch.FullRlp);

            byte[] valueFromState = stateDB.Get(branch.Keccak);
            valueFromState.Should().BeNull();

            request = await stateSyncFeed.PrepareRequest();

            BuildRequestAndHandleResponse(leaf, request, stateSyncFeed);

            value = tempDb.Get(branch.Keccak);
            value.Should().BeNull();

            valueFromState = stateDB.Get(branch.Keccak);
            valueFromState.Should().BeEquivalentTo(branch.FullRlp);
        }
示例#5
0
        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");
        }
示例#6
0
        public void Can_create_without_arguments()
        {
            MemDb memDb = new MemDb();

            memDb.Set(TestItem.KeccakA, new byte[] { 1, 2, 3 });
            memDb.Get(TestItem.KeccakA);
        }
示例#7
0
 public void Can_create_with_name()
 {
     MemDb memDb = new MemDb("desc");
     memDb.Set(TestItem.KeccakA, new byte[] {1, 2, 3});
     memDb.Get(TestItem.KeccakA);
     memDb.Name.Should().Be("desc");
 }
示例#8
0
 public void Can_create_with_delays()
 {
     MemDb memDb = new MemDb(10, 10);
     memDb.Set(TestItem.KeccakA, new byte[] {1, 2, 3});
     memDb.Get(TestItem.KeccakA);
     var some = memDb[new[] {TestItem.KeccakA.Bytes}];
 }
示例#9
0
 public void Simple_set_get_is_fine()
 {
     IDb memDb = new MemDb();
     byte[] bytes = new byte[] {1, 2, 3};
     memDb.Set(TestItem.KeccakA, bytes);
     byte[] retrievedBytes = memDb.Get(TestItem.KeccakA);
     retrievedBytes.Should().BeEquivalentTo(bytes);
 }
示例#10
0
        public async Task When_cleaning_descendants_of_invalid_does_not_touch_other_branches()
        {
            MemDb     blocksDb     = new MemDb();
            MemDb     blockInfosDb = new MemDb();
            MemDb     headersDb    = new MemDb();
            BlockTree tree         = new BlockTree(blocksDb, headersDb, blockInfosDb, MainNetSpecProvider.Instance, NullTxPool.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;

            tree.SuggestBlock(block0);
            tree.SuggestBlock(block1);
            tree.SuggestBlock(block2);
            tree.SuggestBlock(block3);

            tree.SuggestBlock(block1B);
            tree.SuggestBlock(block2B);
            tree.SuggestBlock(block3B);

            blockInfosDb.Set(BlockTree.DeletePointerAddressInDb, block1.Hash.Bytes);

            CancellationTokenSource tokenSource = new CancellationTokenSource();

#pragma warning disable 4014
            Task.Delay(_dbLoadTimeout).ContinueWith(t => tokenSource.Cancel());
#pragma warning restore 4014
            await tree.LoadBlocksFromDb(tokenSource.Token);

            Assert.AreEqual(3L, tree.BestKnownNumber, "best known");
            Assert.AreEqual(null, tree.Head, "head");
            Assert.AreEqual(block3B.Hash, tree.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.NotNull(blockInfosDb.Get(1), "level 1");
            Assert.NotNull(blockInfosDb.Get(2), "level 2");
            Assert.NotNull(blockInfosDb.Get(3), "level 3");
        }
示例#11
0
 public void Can_use_batches_without_issues()
 {
     MemDb memDb = new MemDb();
     memDb.StartBatch();
     memDb.Set(TestItem.KeccakA, _sampleValue);
     memDb.CommitBatch();
     var retrieved = memDb.Get(TestItem.KeccakA);
     retrieved.Should().BeEquivalentTo(_sampleValue);
 }
        public void Deletes_everything_after_the_missing_level()
        {
            MemDb     blocksDb     = new MemDb();
            MemDb     blockInfosDb = new MemDb();
            MemDb     headersDb    = new MemDb();
            BlockTree tree         = new BlockTree(blocksDb, headersDb, blockInfosDb, new ChainLevelInfoRepository(blockInfosDb), MainnetSpecProvider.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     block4       = Build.A.Block.WithNumber(4).WithDifficulty(5).WithParent(block3).TestObject;
            Block     block5       = Build.A.Block.WithNumber(5).WithDifficulty(6).WithParent(block4).TestObject;

            tree.SuggestBlock(block0);
            tree.SuggestBlock(block1);
            tree.SuggestBlock(block2);
            tree.SuggestBlock(block3);
            tree.SuggestBlock(block4);
            tree.SuggestHeader(block5.Header);

            tree.UpdateMainChain(block0);
            tree.UpdateMainChain(block1);
            tree.UpdateMainChain(block2);

            blockInfosDb.Delete(3);

            tree = new BlockTree(blocksDb, headersDb, blockInfosDb, new ChainLevelInfoRepository(blockInfosDb), MainnetSpecProvider.Instance, NullBloomStorage.Instance, LimboLogs.Instance);

            StartupBlockTreeFixer fixer = new StartupBlockTreeFixer(new SyncConfig(), tree, LimboNoErrorLogger.Instance);

            tree.Accept(fixer, CancellationToken.None);

            Assert.Null(blockInfosDb.Get(3), "level 3");
            Assert.Null(blockInfosDb.Get(4), "level 4");
            Assert.Null(blockInfosDb.Get(5), "level 5");

            tree.Head.Header.Should().BeEquivalentTo(block2.Header, options => options.Excluding(t => t.MaybeParent));
            tree.BestSuggestedHeader.Should().BeEquivalentTo(block2.Header, options => options.Excluding(t => t.MaybeParent));
            tree.BestSuggestedBody.Should().BeEquivalentTo(block2.Body);
            tree.BestKnownNumber.Should().Be(2);
        }
示例#13
0
        public async Task Cleans_invalid_blocks_before_starting_DB_load()
        {
            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);

            blockInfosDb.Set(BlockTree.DeletePointerAddressInDb, block1.Hash.Bytes);

            CancellationTokenSource tokenSource = new CancellationTokenSource();

#pragma warning disable 4014
            Task.Delay(_dbLoadTimeout).ContinueWith(t => tokenSource.Cancel());
#pragma warning restore 4014
            await tree.LoadBlocksFromDb(tokenSource.Token);

            Assert.AreEqual(UInt256.Zero, tree.BestKnownNumber, "best known");
            Assert.AreEqual(null, tree.Head, "head");
            Assert.AreEqual(UInt256.Zero, tree.BestSuggested.Number, "suggested");

            Assert.IsNull(blocksDb.Get(block2.Hash), "block 1");
            Assert.IsNull(blocksDb.Get(block2.Hash), "block 2");
            Assert.IsNull(blocksDb.Get(block3.Hash), "block 3");

            Assert.IsNull(blockInfosDb.Get(2), "level 1");
            Assert.IsNull(blockInfosDb.Get(2), "level 2");
            Assert.IsNull(blockInfosDb.Get(3), "level 3");
        }
示例#14
0
        public void Sets_head_block_info_in_db_on_new_head_block()
        {
            MemDb blocksDb     = new MemDb();
            MemDb blockInfosDb = new MemDb();

            BlockTree blockTree = new BlockTree(blocksDb, blockInfosDb, new MemDb(), OlympicSpecProvider.Instance, NullLogManager.Instance);
            Block     block0    = Build.A.Block.WithNumber(0).WithDifficulty(1).TestObject;
            Block     block1    = Build.A.Block.WithNumber(1).WithDifficulty(2).WithParent(block0).TestObject;

            AddToMain(blockTree, block0);
            AddToMain(blockTree, block1);

            BlockHeader storedInDb = Rlp.Decode <BlockHeader>(new Rlp(blocksDb.Get(Keccak.Zero)));

            Assert.AreEqual(block1.Hash, storedInDb.Hash);
        }
示例#15
0
        public void Sets_head_block_hash_in_db_on_new_head_block()
        {
            MemDb blocksDb     = new MemDb();
            MemDb blockInfosDb = new MemDb();

            BlockTree blockTree = new BlockTree(blocksDb, blockInfosDb, OlympicSpecProvider.Instance, Substitute.For <ITransactionPool>(), LimboLogs.Instance);
            Block     block0    = Build.A.Block.WithNumber(0).WithDifficulty(1).TestObject;
            Block     block1    = Build.A.Block.WithNumber(1).WithDifficulty(2).WithParent(block0).TestObject;

            AddToMain(blockTree, block0);
            AddToMain(blockTree, block1);

            var dec = new Keccak(blockInfosDb.Get(Keccak.Zero));

            Assert.AreEqual(block1.Hash, dec);
        }
示例#16
0
        public void When_moving_to_main_one_of_the_two_blocks_at_given_level_the_was_processed_check_is_executed_on_the_correct_block_index_regression()
        {
            MemDb blocksDb     = new MemDb();
            MemDb blockInfosDb = new MemDb();

            BlockTree blockTree = new BlockTree(blocksDb, blockInfosDb, OlympicSpecProvider.Instance, Substitute.For <ITransactionPool>(), 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(1).WithDifficulty(3).WithParent(block0).TestObject;

            AddToMain(blockTree, block0);

            blockTree.SuggestBlock(block2);
            blockTree.SuggestBlock(block1);
            blockTree.UpdateMainChain(block1);

            Keccak storedInDb = new Keccak(blockInfosDb.Get(Keccak.Zero));

            Assert.AreEqual(block1.Hash, storedInDb);
        }
示例#17
0
        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");
        }