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"); }
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_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); }
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 Can_create_without_arguments() { MemDb memDb = new MemDb(); memDb.Set(TestItem.KeccakA, new byte[] { 1, 2, 3 }); memDb.Get(TestItem.KeccakA); }
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"); }
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}]; }
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); }
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"); }
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); }
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"); }
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); }
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); }
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); }
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"); }