Beispiel #1
0
        public BlockPolicySource(ILogger logger, LogEventLevel logEventLevel = LogEventLevel.Verbose)
        {
            LoggedActionRenderer =
                new LoggedActionRenderer <NCAction>(ActionRenderer, logger, logEventLevel);

            LoggedBlockRenderer =
                new LoggedRenderer <NCAction>(BlockRenderer, logger, logEventLevel);
        }
        public void RenderBlock(bool exception)
        {
            bool     called   = false;
            LogEvent firstLog = null;

            IRenderer <DumbAction> renderer = new AnonymousRenderer <DumbAction>
            {
                BlockRenderer = (oldTip, newTip) =>
                {
                    LogEvent[] logs = LogEvents.ToArray();
                    Assert.Single(logs);
                    firstLog = logs[0];
                    Assert.Equal(_genesis, oldTip);
                    Assert.Equal(_blockA, newTip);
                    called = true;
                    if (exception)
                    {
                        throw new ThrowException.SomeException(string.Empty);
                    }
                },
            };

            renderer = new LoggedRenderer <DumbAction>(renderer, _logger);

            Assert.False(called);
            Assert.Empty(LogEvents);

            renderer.RenderReorg(_blockA, _blockB, _genesis);
            Assert.False(called);
            Assert.Equal(2, LogEvents.Count());
            ResetContext();

            if (exception)
            {
                Assert.Throws <ThrowException.SomeException>(
                    () => renderer.RenderBlock(_genesis, _blockA)
                    );
            }
            else
            {
                renderer.RenderBlock(_genesis, _blockA);
            }

            Assert.True(called);
            LogEvent[] logEvents = LogEvents.ToArray();
            Assert.Equal(2, logEvents.Length);
            Assert.Equal(firstLog, logEvents[0]);
            Assert.Equal(LogEventLevel.Debug, firstLog.Level);
            Assert.Equal(
                "Invoking {MethodName}() for #{NewIndex} {NewHash} (was #{OldIndex} {OldHash})...",
                firstLog.MessageTemplate.Text
                );
            Assert.Equal("\"RenderBlock\"", firstLog.Properties["MethodName"].ToString());
            Assert.Equal(
                _blockA.Index.ToString(CultureInfo.InvariantCulture),
                firstLog.Properties["NewIndex"].ToString()
                );
            Assert.Equal($"\"{_blockA.Hash}\"", firstLog.Properties["NewHash"].ToString());
            Assert.Equal(
                _genesis.Index.ToString(CultureInfo.InvariantCulture),
                firstLog.Properties["OldIndex"].ToString()
                );
            Assert.Equal($"\"{_genesis.Hash}\"", firstLog.Properties["OldHash"].ToString());
            Assert.Equal(
                $"\"{typeof(AnonymousRenderer<DumbAction>).FullName}\"",
                firstLog.Properties[Constants.SourceContextPropertyName].ToString()
                );
            Assert.Null(firstLog.Exception);

            LogEvent secondLog = logEvents[1];

            Assert.Equal(
                exception ? LogEventLevel.Error : LogEventLevel.Debug,
                secondLog.Level
                );
            Assert.Equal(
                exception
                    ? "An exception was thrown during {MethodName}() for #{NewIndex} {NewHash} " +
                "(was #{OldIndex} {OldHash}): {Exception}"
                    : "Invoked {MethodName}() for #{NewIndex} {NewHash} " +
                "(was #{OldIndex} {OldHash}).",
                secondLog.MessageTemplate.Text
                );
            Assert.Equal(firstLog.Properties["MethodName"], secondLog.Properties["MethodName"]);
            Assert.Equal(firstLog.Properties["NewIndex"], secondLog.Properties["NewIndex"]);
            Assert.Equal(firstLog.Properties["NewHash"], secondLog.Properties["NewHash"]);
            Assert.Equal(firstLog.Properties["OldIndex"], secondLog.Properties["OldIndex"]);
            Assert.Equal(firstLog.Properties["OldHash"], secondLog.Properties["OldHash"]);
            Assert.Equal(
                firstLog.Properties[Constants.SourceContextPropertyName],
                secondLog.Properties[Constants.SourceContextPropertyName]
                );
            if (exception)
            {
                Assert.StartsWith(
                    $"\"{typeof(ThrowException.SomeException).FullName}",
                    secondLog.Properties["Exception"].ToString()
                    );
                Assert.IsType <ThrowException.SomeException>(secondLog.Exception);
            }
            else
            {
                Assert.False(secondLog.Properties.ContainsKey("Exception"));
                Assert.Null(secondLog.Exception);
            }
        }
        public void RenderReorg(bool end, bool exception)
        {
            bool     called   = false;
            LogEvent firstLog = null;

            void Callback(DumbBlock old, DumbBlock @new, DumbBlock bp)
            {
                LogEvent[] logs = LogEvents.ToArray();
                Assert.Single(logs);
                firstLog = logs[0];
                Assert.Equal(_blockA, old);
                Assert.Equal(_blockB, @new);
                Assert.Equal(_genesis, bp);
                called = true;
                if (exception)
                {
                    throw new ThrowException.SomeException(string.Empty);
                }
            }

            IRenderer <DumbAction> renderer = new AnonymousRenderer <DumbAction>
            {
                ReorgRenderer    = end ? (Action <DumbBlock, DumbBlock, DumbBlock>)null : Callback,
                ReorgEndRenderer = end ? Callback : (Action <DumbBlock, DumbBlock, DumbBlock>)null,
            };

            renderer = new LoggedRenderer <DumbAction>(renderer, _logger, LogEventLevel.Verbose);
            var invoke = end
                ? (Action <DumbBlock, DumbBlock, DumbBlock>)renderer.RenderReorgEnd
                : renderer.RenderReorg;
            var invokeOpposite = end
                ? (Action <DumbBlock, DumbBlock, DumbBlock>)renderer.RenderReorg
                : renderer.RenderReorgEnd;
            var methodName = end ? "RenderReorgEnd" : "RenderReorg";

            Assert.False(called);
            Assert.Empty(LogEvents);

            renderer.RenderBlock(_genesis, _blockA);
            Assert.False(called);
            Assert.Equal(2, LogEvents.Count());
            ResetContext();

            invokeOpposite(_blockA, _blockB, _genesis);
            Assert.False(called);
            Assert.Equal(2, LogEvents.Count());
            ResetContext();

            if (exception)
            {
                Assert.Throws <ThrowException.SomeException>(
                    () => invoke(_blockA, _blockB, _genesis)
                    );
            }
            else
            {
                invoke(_blockA, _blockB, _genesis);
            }

            Assert.True(called);
            LogEvent[] logEvents = LogEvents.ToArray();
            Assert.Equal(2, logEvents.Length);
            Assert.Equal(firstLog, logEvents[0]);
            Assert.Equal(LogEventLevel.Verbose, firstLog.Level);
            Assert.Equal(
                "Invoking {MethodName}() for #{NewIndex} {NewHash} (was #{OldIndex} {OldHash} " +
                "through #{BranchpointIndex} {BranchpointHash})...",
                firstLog.MessageTemplate.Text
                );
            Assert.Equal($"\"{methodName}\"", firstLog.Properties["MethodName"].ToString());
            Assert.Equal(
                _blockB.Index.ToString(CultureInfo.InvariantCulture),
                firstLog.Properties["NewIndex"].ToString()
                );
            Assert.Equal($"\"{_blockB.Hash}\"", firstLog.Properties["NewHash"].ToString());
            Assert.Equal(
                _blockA.Index.ToString(CultureInfo.InvariantCulture),
                firstLog.Properties["OldIndex"].ToString()
                );
            Assert.Equal($"\"{_blockA.Hash}\"", firstLog.Properties["OldHash"].ToString());
            Assert.Equal(
                _genesis.Index.ToString(CultureInfo.InvariantCulture),
                firstLog.Properties["BranchpointIndex"].ToString()
                );
            Assert.Equal($"\"{_genesis.Hash}\"", firstLog.Properties["BranchpointHash"].ToString());
            Assert.Equal(
                $"\"{typeof(AnonymousRenderer<DumbAction>).FullName}\"",
                firstLog.Properties[Constants.SourceContextPropertyName].ToString()
                );
            Assert.Null(firstLog.Exception);

            LogEvent secondLog = logEvents[1];

            Assert.Equal(
                exception ? LogEventLevel.Error : LogEventLevel.Verbose,
                secondLog.Level
                );
            Assert.Equal(
                exception
                    ? "An exception was thrown during {MethodName}() for #{NewIndex} {NewHash} " +
                "(was #{OldIndex} {OldHash} through #{BranchpointIndex} " +
                "{BranchpointHash}): {Exception}"
                    : "Invoked {MethodName}() for #{NewIndex} {NewHash} (was #{OldIndex} " +
                "{OldHash} through #{BranchpointIndex} {BranchpointHash}).",
                secondLog.MessageTemplate.Text
                );
            Assert.Equal(firstLog.Properties["MethodName"], secondLog.Properties["MethodName"]);
            Assert.Equal(firstLog.Properties["NewIndex"], secondLog.Properties["NewIndex"]);
            Assert.Equal(firstLog.Properties["NewHash"], secondLog.Properties["NewHash"]);
            Assert.Equal(firstLog.Properties["OldIndex"], secondLog.Properties["OldIndex"]);
            Assert.Equal(firstLog.Properties["OldHash"], secondLog.Properties["OldHash"]);
            Assert.Equal(
                firstLog.Properties["BranchpointIndex"],
                secondLog.Properties["BranchpointIndex"]
                );
            Assert.Equal(
                firstLog.Properties["BranchpointHash"],
                secondLog.Properties["BranchpointHash"]
                );
            Assert.Equal(
                firstLog.Properties[Constants.SourceContextPropertyName],
                secondLog.Properties[Constants.SourceContextPropertyName]
                );
            if (exception)
            {
                Assert.StartsWith(
                    $"\"{typeof(ThrowException.SomeException).FullName}",
                    secondLog.Properties["Exception"].ToString()
                    );
                Assert.IsType <ThrowException.SomeException>(secondLog.Exception);
            }
            else
            {
                Assert.False(secondLog.Properties.ContainsKey("Exception"));
                Assert.Null(secondLog.Exception);
            }
        }
        public virtual void BlocksBeingAppendedInParallel()
        {
            var blockLogs = new List <(Block <DumbAction> OldTip, Block <DumbAction> NewTip)>();
            var reorgLogs = new List <(
                                          Block <DumbAction> OldTip,
                                          Block <DumbAction> NewTip,
                                          Block <DumbAction> Branchpoint
                                          )>();
            var innerRenderer = new AnonymousRenderer <DumbAction>
            {
                BlockRenderer = (oldTip, newTip) => blockLogs.Add((oldTip, newTip)),
                ReorgRenderer = (oldTip, newTip, bp) => reorgLogs.Add((oldTip, newTip, bp)),
            };
            var delayedRenderer = new DelayedRenderer <DumbAction>(
                innerRenderer,
                _canonicalChainComparer,
                _store,
                confirmations: 3
                );
            var renderer = new LoggedRenderer <DumbAction>(
                delayedRenderer,
                _logger,
                LogEventLevel.Verbose
                );

            Assert.Null(delayedRenderer.Tip);
            Assert.Empty(blockLogs);
            Assert.Empty(reorgLogs);

            // Some explanation on the fixture: there are two chains that shares a certain block
            // as a branchpoint: _chainA and _chainB.  The topmost mutual block, i.e., branchpoint,
            // between _chainA and _chainB is _chainA[4] (== _chainB[4]).
            // In this test, we tries to simulate blocks from two parallel chains being "appended"
            // (discovered) to a peer.  The order of discovery can be drawn as below (the prime
            // [apostrophe] after the block index number like #N' shows it's from _chainB; the bare
            // block index without that like #N means it's from _chainA):
            //
            //          #4 (1st)
            //            |
            //          /   \
            //    #5 (2nd)  #5' (3rd)
            //        |       |
            //    #6 (3rd)  #6' (4th)
            //        |       |
            //    #7 (5th)  #7' (6th)
            //        |       |
            //    #8 (7th)  #8' (8th)
            //                |
            //              #9' (9th)

            // #4  -> 1 confirm
            // #5  -> no confirms
            // #5' -> no confirms
            renderer.RenderBlock(_chainA[4], _chainA[5]);
            var expectedBlockLogs = new List <(Block <DumbAction> OldTip, Block <DumbAction> NewTip)>
            {
                (_chainA[1], _chainA[2]),
            };

            Assert.Equal(_chainA[2], delayedRenderer.Tip);
            Assert.Empty(reorgLogs);
            Assert.Equal(expectedBlockLogs, blockLogs);

            // #4  -> 1 confirms
            // #5  -> no confirms
            // #5' -> no confirms
            renderer.RenderReorg(_chainA[5], _chainB[5], _branchpoint);
            renderer.RenderBlock(_chainA[5], _chainB[5]);
            renderer.RenderReorgEnd(_chainA[5], _chainB[5], _branchpoint);
            Assert.Equal(_chainA[2], delayedRenderer.Tip);
            Assert.Empty(reorgLogs);
            Assert.Equal(expectedBlockLogs, blockLogs);

            // #4  -> 2 confirms; tip changed -> #3
            // #5  -> 1 confirm;  #6 -> no confirm
            // #5' -> no confirms
            renderer.RenderReorg(_chainB[5], _chainA[6], _branchpoint);
            renderer.RenderBlock(_chainB[5], _chainA[6]);
            renderer.RenderReorgEnd(_chainB[5], _chainA[6], _branchpoint);
            expectedBlockLogs.Add((_chainA[2], _chainA[3]));
            Assert.Equal(_chainA[3], delayedRenderer.Tip);
            Assert.Empty(reorgLogs);
            Assert.Equal(expectedBlockLogs, blockLogs);

            // #4  -> 2 confirms
            // #5  -> 1 confirm; #6  -> no confirm
            // #5' -> 1 confirm; #6' -> no confirm
            renderer.RenderReorg(_chainA[6], _chainB[6], _branchpoint);
            renderer.RenderBlock(_chainA[6], _chainB[6]);
            renderer.RenderReorgEnd(_chainA[6], _chainB[6], _branchpoint);
            Assert.Equal(_chainA[3], delayedRenderer.Tip);
            Assert.Empty(reorgLogs);
            Assert.Equal(expectedBlockLogs, blockLogs);

            // #4  -> 3 confirms; tip changed -> #4
            // #5  -> 2 confirms; #6  -> 1 confirm; #7 -> no confirm
            // #5' -> 1 confirm;  #6' -> no confirm
            renderer.RenderReorg(_chainB[6], _chainA[7], _branchpoint);
            renderer.RenderBlock(_chainB[6], _chainA[7]);
            renderer.RenderReorgEnd(_chainB[6], _chainA[7], _branchpoint);
            expectedBlockLogs.Add((_chainA[3], _chainA[4]));
            Assert.Equal(_chainA[4], delayedRenderer.Tip);
            Assert.Empty(reorgLogs);
            Assert.Equal(expectedBlockLogs, blockLogs);

            // #4  -> gone but still is tip
            // #5  -> 2 confirms; #6  -> 1 confirm; #7  -> no confirm
            // #5' -> 2 confirms; #6' -> 1 confirm; #7' -> no confirm
            renderer.RenderReorg(_chainA[7], _chainB[7], _branchpoint);
            renderer.RenderBlock(_chainA[7], _chainB[7]);
            renderer.RenderReorgEnd(_chainA[7], _chainB[7], _branchpoint);
            Assert.Equal(_chainA[4], delayedRenderer.Tip);
            Assert.Empty(reorgLogs);
            Assert.Equal(expectedBlockLogs, blockLogs);

            // #4  -> gone; tip changed -> #5; render(#4, #5)
            // #5  -> 3 confirms; #6  -> 2 confirms; #7  -> 1 confirm; #8 -> no confirm
            // #5' -> 2 confirms; #6' -> 1 confirm;  #7' -> no confirm
            renderer.RenderReorg(_chainB[7], _chainA[8], _branchpoint);
            renderer.RenderBlock(_chainB[7], _chainA[8]);
            renderer.RenderReorgEnd(_chainB[7], _chainA[8], _branchpoint);
            expectedBlockLogs.Add((_chainA[4], _chainA[5]));
            Assert.Equal(_chainA[5], delayedRenderer.Tip);
            Assert.Empty(reorgLogs);
            Assert.Equal(expectedBlockLogs, blockLogs);

            // tip changed -> #5'; render(#5, #5'); reorg(#5, #5', #4)
            // #5  -> 3 confirms; #6  -> 2 confirms; #7  -> 1 confirm; #8  -> no confirm
            // #5' -> 3 confirms; #6' -> 2 confirms; #7' -> 1 confirm; #8' -> no confirm
            renderer.RenderReorg(_chainA[8], _chainB[8], _branchpoint);
            renderer.RenderBlock(_chainA[8], _chainB[8]);
            renderer.RenderReorgEnd(_chainA[8], _chainB[8], _branchpoint);
            expectedBlockLogs.Add((_chainA[5], _chainB[5]));
            Assert.Equal(_chainB[5], delayedRenderer.Tip);
            Assert.Equal(new[] { (_chainA[5], _chainB[5], _branchpoint) }, reorgLogs);