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);
        }
Exemplo n.º 2
0
        // 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);
                }
            }
        }