public void GetHistoricalTree_return_expected_results([ValueSource(nameof(HistoricalTreeTestCases))] GetHistoricalLeavesTest test) { var address = TestItem.AddressA; var logFinder = Substitute.For <ILogFinder>(); var mainDb = new MemDb(); var metadataDataDb = new MemDb(); var baselineTreeHelper = new BaselineTreeHelper(logFinder, mainDb, metadataDataDb, LimboNoErrorLogger.Instance); var baselineTree = new ShaBaselineTree(mainDb, metadataDataDb, address.Bytes, BaselineModule.TruncationLength, LimboNoErrorLogger.Instance); for (int i = 0; i < test.Blocks.Length; i++) { var block = test.Blocks[i]; for (int j = 0; j < block.Leaves.Length; j++) { baselineTree.Insert(block.Leaves[j]); } baselineTree.MemorizeCurrentCount(TestItem.Keccaks[block.BlockNumber], block.BlockNumber, (uint)block.Leaves.Length); } var historicalTree = baselineTreeHelper.CreateHistoricalTree(address, 1); Assert.AreNotEqual(historicalTree.Count, baselineTree.Count); Assert.AreNotEqual(historicalTree.Root, baselineTree.Root); }
// TODO: fuzzer with concurrent inserts public void Baseline_tree_fuzzer( int leavesPerBlock, int blocksCount, int emptyBlocksRatio, bool recalculateOnInsert, bool withReorgs, int?randomSeed) { MemDb mainDb = new MemDb(); MemDb metadataDb = new MemDb(); Address address = Address.Zero; BaselineTreeHelper helper = new BaselineTreeHelper( Substitute.For <ILogFinder>(), mainDb, metadataDb, LimboNoErrorLogger.Instance); BaselineTree baselineTree = new ShaBaselineTree( mainDb, metadataDb, address.Bytes, 0, LimboNoErrorLogger.Instance); randomSeed ??= _random.Next(); Console.WriteLine($"random seed was {randomSeed} - hardcode it to recreate the failign test"); // Random random = new Random(1524199427); <- example Random random = new Random(randomSeed.Value); int currentBlockNumber = 0; uint totalCountCheck = 0; Stack <long> lastBlockWithLeavesCheck = new Stack <long>(); Dictionary <long, uint> historicalCountChecks = new Dictionary <long, uint>(); historicalCountChecks[0] = 0; for (int i = 0; i < blocksCount; i++) { if (i == 18) { } currentBlockNumber++; uint numberOfLeaves = (uint)random.Next(leavesPerBlock) + 1; // not zero bool hasLeaves = random.Next(100) < emptyBlocksRatio; if (hasLeaves) { totalCountCheck += numberOfLeaves; TestContext.WriteLine($"Adding {numberOfLeaves} at block {currentBlockNumber}"); for (int j = 0; j < numberOfLeaves; j++) { byte[] leafBytes = new byte[32]; random.NextBytes(leafBytes); baselineTree.Insert(new Keccak(leafBytes), recalculateOnInsert); } lastBlockWithLeavesCheck.TryPeek(out long previous); TestContext.WriteLine($"Previous is {previous}"); baselineTree.LastBlockWithLeaves.Should().Be(previous); baselineTree.MemorizeCurrentCount(TestItem.Keccaks[currentBlockNumber], currentBlockNumber, baselineTree.Count); lastBlockWithLeavesCheck.Push(currentBlockNumber); baselineTree.Count.Should().Be(totalCountCheck); baselineTree.LastBlockWithLeaves.Should().Be(lastBlockWithLeavesCheck.Peek()); } else { TestContext.WriteLine($"Block {currentBlockNumber} has no leaves"); } historicalCountChecks[currentBlockNumber] = totalCountCheck; WriteHistory(historicalCountChecks, baselineTree); for (int j = 1; j <= currentBlockNumber; j++) { TestContext.WriteLine($"Creating historical at {j}"); var historicalTrie = helper.CreateHistoricalTree(address, j); TestContext.WriteLine($"Checking if trie count ({historicalTrie.Count}) is {historicalCountChecks[j]} as expected"); historicalTrie.Count.Should().Be(historicalCountChecks[j], $"Block is {currentBlockNumber}, checking count at block {j}."); } if (withReorgs) { bool shouldReorg = random.Next(100) < 50; if (shouldReorg && currentBlockNumber >= 1) { int reorgDepth = random.Next(currentBlockNumber) + 1; TestContext.WriteLine($"Reorganizing {reorgDepth} from {currentBlockNumber}"); uint expectedDeleteCount = historicalCountChecks[currentBlockNumber] - historicalCountChecks[currentBlockNumber - reorgDepth]; baselineTree.GoBackTo(currentBlockNumber - reorgDepth).Should().Be(expectedDeleteCount); for (int j = 0; j < reorgDepth; j++) { historicalCountChecks.Remove(currentBlockNumber - j); } currentBlockNumber -= reorgDepth; totalCountCheck = historicalCountChecks[currentBlockNumber]; baselineTree.MemorizeCurrentCount(TestItem.Keccaks[currentBlockNumber], currentBlockNumber, totalCountCheck); TestContext.WriteLine($"Total count after reorg is {totalCountCheck} at block {currentBlockNumber}"); while (lastBlockWithLeavesCheck.Any() && lastBlockWithLeavesCheck.Peek() > currentBlockNumber) { lastBlockWithLeavesCheck.Pop(); } lastBlockWithLeavesCheck.TryPeek(out long last); if (last != currentBlockNumber) { TestContext.WriteLine($"Pushing {currentBlockNumber} on test stack after reorg."); // after reorg we always push a memorized count lastBlockWithLeavesCheck.Push(currentBlockNumber); } } WriteHistory(historicalCountChecks, baselineTree); } } }