@@ -1,9 +1,16 @@
Loading
1 1
using System;
2 2
using System.Collections.Generic;
3 +
using System.Collections.Immutable;
4 +
using System.Threading.Tasks;
3 5
using Libplanet.Action;
6 +
using Libplanet.Blockchain;
7 +
using Libplanet.Blockchain.Policies;
4 8
using Libplanet.Blockchain.Renderers;
5 9
using Libplanet.Blocks;
10 +
using Libplanet.Crypto;
11 +
using Libplanet.Tests.Common;
6 12
using Libplanet.Tests.Common.Action;
13 +
using Libplanet.Tests.Store;
7 14
using Serilog;
8 15
using Serilog.Events;
9 16
using Xunit;
@@ -418,6 +425,217 @@
Loading
418 425
            );
419 426
        }
420 427
428 +
        [Fact]
429 +
        public async Task DelayedRendererInReorg()
430 +
        {
431 +
            var policy = new BlockPolicy<DumbAction>(new MinerReward(1));
432 +
            var fx = new DefaultStoreFixture();
433 +
            var blockLogs = new List<(Block<DumbAction> OldTip, Block<DumbAction> NewTip)>();
434 +
            var reorgLogs = new List<(
435 +
                Block<DumbAction> OldTip,
436 +
                Block<DumbAction> NewTip,
437 +
                Block<DumbAction> Branchpoint
438 +
            )>();
439 +
            var renderLogs = new List<(bool Unrender, ActionEvaluation Evaluation)>();
440 +
            var innerRenderer = new AnonymousActionRenderer<DumbAction>
441 +
            {
442 +
                BlockRenderer = (oldTip, newTip) => blockLogs.Add((oldTip, newTip)),
443 +
                ReorgRenderer = (oldTip, newTip, bp) => reorgLogs.Add((oldTip, newTip, bp)),
444 +
                ActionRenderer = (action, context, nextStates) =>
445 +
                    renderLogs.Add((false, new ActionEvaluation(action, context, nextStates))),
446 +
                ActionErrorRenderer = (act, ctx, e) =>
447 +
                    renderLogs.Add((false, new ActionEvaluation(act, ctx, ctx.PreviousStates, e))),
448 +
                ActionUnrenderer = (action, context, nextStates) =>
449 +
                    renderLogs.Add((true, new ActionEvaluation(action, context, nextStates))),
450 +
                ActionErrorUnrenderer = (act, ctx, e) =>
451 +
                    renderLogs.Add((true, new ActionEvaluation(act, ctx, ctx.PreviousStates, e))),
452 +
            };
453 +
            var delayedRenderer = new DelayedActionRenderer<DumbAction>(
454 +
                innerRenderer, fx.Store, 2);
455 +
            var renderer = new LoggedActionRenderer<DumbAction>(
456 +
                delayedRenderer,
457 +
                Log.Logger,
458 +
                LogEventLevel.Verbose
459 +
            );
460 +
            var delayedValidatingActionRenderer = new DelayedActionRenderer<DumbAction>(
461 +
                new ValidatingActionRenderer<DumbAction>(), fx.Store, 2);
462 +
463 +
            var chain = new BlockChain<DumbAction>(
464 +
                policy,
465 +
                fx.Store,
466 +
                fx.StateStore,
467 +
                fx.GenesisBlock,
468 +
                new IActionRenderer<DumbAction>[] { renderer, delayedValidatingActionRenderer }
469 +
            );
470 +
471 +
            Assert.Null(delayedRenderer.Tip);
472 +
            Assert.Empty(blockLogs);
473 +
            Assert.Empty(reorgLogs);
474 +
            Assert.Empty(renderLogs);
475 +
476 +
            var key = new PrivateKey();
477 +
478 +
            var tx1 = chain.MakeTransaction(key, new[] { new DumbAction(fx.Address2, "#1") });
479 +
            await chain.MineBlock(fx.Address1);
480 +
481 +
            Assert.Null(delayedRenderer.Tip);
482 +
            Assert.Empty(reorgLogs);
483 +
            Assert.Empty(blockLogs);
484 +
            Assert.Empty(renderLogs);
485 +
486 +
            var tx2 = chain.MakeTransaction(key, new[] { new DumbAction(fx.Address2, "#2") });
487 +
            await chain.MineBlock(fx.Address1);
488 +
489 +
            Assert.Equal(chain[0], delayedRenderer.Tip);
490 +
            Assert.Empty(reorgLogs);
491 +
            Assert.Empty(blockLogs);
492 +
            Assert.Empty(renderLogs);
493 +
494 +
            var forked = chain.Fork(chain[0].Hash);
495 +
            fx.Store.StageTransactionIds(new[] { tx1.Id }.ToImmutableHashSet());
496 +
            var block = await forked.MineBlock(fx.Address1, append: false);
497 +
            forked.Append(
498 +
                    block,
499 +
                    DateTimeOffset.UtcNow,
500 +
                    evaluateActions: true,
501 +
                    renderBlocks: false,
502 +
                    renderActions: false
503 +
                );
504 +
            fx.Store.StageTransactionIds(new[] { tx2.Id }.ToImmutableHashSet());
505 +
            block = await forked.MineBlock(fx.Address1, append: false);
506 +
            forked.Append(
507 +
                    block,
508 +
                    DateTimeOffset.UtcNow,
509 +
                    evaluateActions: true,
510 +
                    renderBlocks: false,
511 +
                    renderActions: false
512 +
                );
513 +
            forked.MakeTransaction(key, new[] { new DumbAction(fx.Address2, "#3") });
514 +
            block = await forked.MineBlock(fx.Address1, append: false);
515 +
            forked.Append(
516 +
                    block,
517 +
                    DateTimeOffset.UtcNow,
518 +
                    evaluateActions: true,
519 +
                    renderBlocks: false,
520 +
                    renderActions: false
521 +
                );
522 +
            forked.MakeTransaction(key, new[] { new DumbAction(fx.Address2, "#4") });
523 +
            block = await forked.MineBlock(fx.Address1, append: false);
524 +
            forked.Append(
525 +
                    block,
526 +
                    DateTimeOffset.UtcNow,
527 +
                    evaluateActions: true,
528 +
                    renderBlocks: false,
529 +
                    renderActions: false
530 +
                );
531 +
532 +
            chain.Swap(forked, true);
533 +
534 +
            Assert.Equal(chain[2], delayedRenderer.Tip);
535 +
            Assert.Empty(reorgLogs);
536 +
            Assert.Equal(new[] { (chain[0], chain[1]), (chain[1], chain[2]) }, blockLogs);
537 +
            Assert.Equal(4, renderLogs.Count);
538 +
        }
539 +
540 +
        [Fact]
541 +
        public async Task DelayedRendererAfterReorg()
542 +
        {
543 +
            var policy = new BlockPolicy<DumbAction>(new MinerReward(1));
544 +
            var fx = new DefaultStoreFixture();
545 +
            var blockLogs = new List<(Block<DumbAction> OldTip, Block<DumbAction> NewTip)>();
546 +
            var reorgLogs = new List<(
547 +
                Block<DumbAction> OldTip,
548 +
                Block<DumbAction> NewTip,
549 +
                Block<DumbAction> Branchpoint
550 +
            )>();
551 +
            var renderLogs = new List<(bool Unrender, ActionEvaluation Evaluation)>();
552 +
            var innerRenderer = new AnonymousActionRenderer<DumbAction>
553 +
            {
554 +
                BlockRenderer = (oldTip, newTip) => blockLogs.Add((oldTip, newTip)),
555 +
                ReorgRenderer = (oldTip, newTip, bp) => reorgLogs.Add((oldTip, newTip, bp)),
556 +
                ActionRenderer = (action, context, nextStates) =>
557 +
                    renderLogs.Add((false, new ActionEvaluation(action, context, nextStates))),
558 +
                ActionErrorRenderer = (act, ctx, e) =>
559 +
                    renderLogs.Add((false, new ActionEvaluation(act, ctx, ctx.PreviousStates, e))),
560 +
                ActionUnrenderer = (action, context, nextStates) =>
561 +
                    renderLogs.Add((true, new ActionEvaluation(action, context, nextStates))),
562 +
                ActionErrorUnrenderer = (act, ctx, e) =>
563 +
                    renderLogs.Add((true, new ActionEvaluation(act, ctx, ctx.PreviousStates, e))),
564 +
            };
565 +
            var delayedRenderer = new DelayedActionRenderer<DumbAction>(
566 +
                innerRenderer, fx.Store, 2);
567 +
            var renderer = new LoggedActionRenderer<DumbAction>(
568 +
                delayedRenderer,
569 +
                Log.Logger,
570 +
                LogEventLevel.Verbose
571 +
            );
572 +
            var delayedValidatingActionRenderer = new DelayedActionRenderer<DumbAction>(
573 +
                new ValidatingActionRenderer<DumbAction>(), fx.Store, 2);
574 +
575 +
            var chain = new BlockChain<DumbAction>(
576 +
                policy,
577 +
                fx.Store,
578 +
                fx.StateStore,
579 +
                fx.GenesisBlock,
580 +
                new IActionRenderer<DumbAction>[] { renderer, delayedValidatingActionRenderer }
581 +
            );
582 +
583 +
            Assert.Null(delayedRenderer.Tip);
584 +
            Assert.Empty(blockLogs);
585 +
            Assert.Empty(reorgLogs);
586 +
            Assert.Empty(renderLogs);
587 +
588 +
            var key = new PrivateKey();
589 +
590 +
            var tx1 = chain.MakeTransaction(key, new[] { new DumbAction(fx.Address2, "#1") });
591 +
            await chain.MineBlock(fx.Address1);
592 +
593 +
            Assert.Null(delayedRenderer.Tip);
594 +
            Assert.Empty(reorgLogs);
595 +
            Assert.Empty(blockLogs);
596 +
            Assert.Empty(renderLogs);
597 +
598 +
            var tx2 = chain.MakeTransaction(key, new[] { new DumbAction(fx.Address2, "#2") });
599 +
            await chain.MineBlock(fx.Address1);
600 +
601 +
            Assert.Equal(chain[0], delayedRenderer.Tip);
602 +
            Assert.Empty(reorgLogs);
603 +
            Assert.Empty(blockLogs);
604 +
            Assert.Empty(renderLogs);
605 +
606 +
            var forked = chain.Fork(chain[1].Hash);
607 +
            fx.Store.StageTransactionIds(new[] { tx2.Id }.ToImmutableHashSet());
608 +
            var block = await forked.MineBlock(fx.Address1, append: false);
609 +
            forked.Append(
610 +
                    block,
611 +
                    DateTimeOffset.UtcNow,
612 +
                    evaluateActions: true,
613 +
                    renderBlocks: false,
614 +
                    renderActions: false
615 +
                );
616 +
            block = await forked.MineBlock(fx.Address1, append: false);
617 +
            forked.Append(
618 +
                    block,
619 +
                    DateTimeOffset.UtcNow,
620 +
                    evaluateActions: true,
621 +
                    renderBlocks: false,
622 +
                    renderActions: false
623 +
                );
624 +
625 +
            chain.Swap(forked, true);
626 +
627 +
            Assert.Equal(chain[1], delayedRenderer.Tip);
628 +
            Assert.Empty(reorgLogs);
629 +
            Assert.Equal(new[] { (chain[0], chain[1]) }, blockLogs);
630 +
            Assert.Equal(2, renderLogs.Count);
631 +
632 +
            await chain.MineBlock(fx.Address1);
633 +
            Assert.Equal(chain[2], delayedRenderer.Tip);
634 +
            Assert.Empty(reorgLogs);
635 +
            Assert.Equal(new[] { (chain[0], chain[1]), (chain[1], chain[2]) }, blockLogs);
636 +
            Assert.Equal(4, renderLogs.Count);
637 +
        }
638 +
421 639
        private IActionContext FakeContext(long blockIndex = 1) =>
422 640
            new ActionContext(default, default, blockIndex, _emptyStates, 0);
423 641
    }
Files Coverage
Libplanet 86.53%
Libplanet.Analyzers.Tests 78.48%
Libplanet.RocksDBStore 95.47%
Libplanet.RocksDBStore.Tests 77.46%
Libplanet.Stun.Tests/Stun 99.56%
Libplanet.Stun/Stun 77.88%
Libplanet.Tests 89.66%
Libplanet.Analyzers/ActionAnalyzer.cs 97.78%
Project Totals (342 files) 88.41%
1
coverage:
2
  range: 70..95
3
  status:
4
    project:
5
      default:
6
        target: auto
7
        threshold: "1"
8
        base: auto
9

10
comment:
11
  require_changes: true
12
  layout: "diff, files"
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading