protected override async Task <ISet <TransactionWatch <TContext> > > ExecuteWatchesAsync(
            IEnumerable <TransactionWatch <TContext> > watches,
            Block block,
            int height,
            BlockEventType eventType,
            CancellationToken cancellationToken)
        {
            var confirmationType = GetConfirmationType(eventType);
            var completed        = new HashSet <TransactionWatch <TContext> >();

            foreach (var watch in watches)
            {
                var confirmation = await GetConfirmationAsync(watch, height, CancellationToken.None);

                var success = await this.handler.ConfirmationUpdateAsync(
                    watch,
                    confirmation,
                    confirmationType,
                    CancellationToken.None
                    );

                if (success && !completed.Add(watch))
                {
                    throw new ArgumentException("The collection contains duplicated items.", nameof(watches));
                }
            }

            return(completed);
        }
Esempio n. 2
0
 protected override Task <ISet <Watch <object> > > ExecuteWatchesAsync(
     IEnumerable <Watch <object> > watches,
     Block block,
     int height,
     BlockEventType eventType,
     CancellationToken cancellationToken)
 {
     return(StubbedExecuteWatchesAsync.Object(watches, block, height, eventType, cancellationToken));
 }
Esempio n. 3
0
        public async Task ExecuteAsync(
            Block block,
            int height,
            BlockEventType eventType,
            CancellationToken cancellationToken)
        {
            IEnumerable <TWatch> watches;

            if (block == null)
            {
                throw new ArgumentNullException(nameof(block));
            }

            if (height < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(height), height, "The value is not valid height.");
            }

            // First, inspect block and create new watches.
            if (eventType == BlockEventType.Added)
            {
                watches = await CreateWatchesAsync(block, height, cancellationToken);

                if (watches.Any())
                {
                    await this.handler.AddWatchesAsync(watches, cancellationToken);
                }
            }

            // Load watches that match with the block and execute it.
            watches = await GetWatchesAsync(block, height, cancellationToken);

            if (!watches.Any())
            {
                return;
            }

            var completed = await ExecuteWatchesAsync(watches, block, height, eventType, cancellationToken);

            // First, remove completed watches.
            if (completed.Any())
            {
                await this.handler.RemoveCompletedWatchesAsync(completed, CancellationToken.None);
            }

            // Then remove watches that belong to the block is being removing.
            if (eventType == BlockEventType.Removing)
            {
                await this.handler.RemoveUncompletedWatchesAsync(block.GetHash(), CancellationToken.None);
            }
        }
Esempio n. 4
0
        protected override async Task <ISet <BalanceWatch <TContext, TAmount> > > ExecuteWatchesAsync(
            IEnumerable <BalanceWatch <TContext, TAmount> > watches,
            Block block,
            int height,
            BlockEventType eventType,
            CancellationToken cancellationToken)
        {
            var confirmationType = GetConfirmationType(eventType);
            var completed        = new HashSet <BalanceWatch <TContext, TAmount> >();

            foreach (var group in watches.GroupBy(w => w.Address))
            {
                // Get confirmation number for each change.
                var changes = new Collection <ConfirmedBalanceChange <TContext, TAmount> >();

                foreach (var watch in group) // lgtm[cs/linq/missed-select]
                {
                    var change = new ConfirmedBalanceChange <TContext, TAmount>(
                        watch.Context,
                        watch.BalanceChange,
                        await GetConfirmationAsync(watch, height, CancellationToken.None)
                        );

                    changes.Add(change);
                }

                // Invoke handler.
                var confirm           = new BalanceConfirmation <TContext, TAmount>(group.Key, changes);
                var confirmationCount = changes.Min(c => c.Confirmation);

                var success = await this.handler.ConfirmationUpdateAsync(
                    confirm,
                    confirmationCount,
                    confirmationType,
                    CancellationToken.None
                    );

                if (success)
                {
                    foreach (var watch in group)
                    {
                        if (!completed.Add(watch))
                        {
                            throw new ArgumentException("The collection contains duplicated items.", nameof(watches));
                        }
                    }
                }
            }

            return(completed);
        }
Esempio n. 5
0
        public Task ExecuteAsync_GetWatchesAsyncReturnEmptyList_ShouldDoNothing(BlockEventType eventType)
        {
            return(AsynchronousTesting.WithCancellationTokenAsync(async cancellationToken =>
            {
                // Arrange.
                this.subject.StubbedGetWatchesAsync.Setup(f => f(TestBlock.Regtest0, 0, cancellationToken))
                .Returns(Task.FromResult(Enumerable.Empty <Watch <object> >()));

                // Act.
                await this.subject.ExecuteAsync(TestBlock.Regtest0, 0, eventType, cancellationToken);

                // Assert.
                this.subject.StubbedGetWatchesAsync.Verify(
                    f => f(
                        TestBlock.Regtest0,
                        0,
                        cancellationToken
                        ),
                    Times.Once()
                    );

                this.subject.StubbedExecuteWatchesAsync.Verify(
                    f => f(
                        It.IsAny <IEnumerable <Watch <object> > >(),
                        It.IsAny <Block>(),
                        It.IsAny <int>(),
                        It.IsAny <BlockEventType>(),
                        It.IsAny <CancellationToken>()
                        ),
                    Times.Never()
                    );

                this.handler.Verify(
                    h => h.RemoveCompletedWatchesAsync(
                        It.IsAny <IEnumerable <Watch <object> > >(),
                        It.IsAny <CancellationToken>()
                        ),
                    Times.Never()
                    );

                this.handler.Verify(
                    h => h.RemoveUncompletedWatchesAsync(
                        It.IsAny <uint256>(),
                        It.IsAny <CancellationToken>()
                        ),
                    Times.Never()
                    );
            }));
        }
        protected static ConfirmationType GetConfirmationType(BlockEventType eventType)
        {
            switch (eventType)
            {
            case BlockEventType.Added:
                return(ConfirmationType.Confirmed);

            case BlockEventType.Removing:
                return(ConfirmationType.Unconfirming);

            default:
                throw new ArgumentOutOfRangeException(
                          nameof(eventType),
                          eventType,
                          "The value is not a valid event type."
                          );
            }
        }
Esempio n. 7
0
        public static BlockEvent Parse(Level parent, BlockEventType type, string value)
        {
            var match = Parser.Match(value);

            if (!match.Success)
            {
                throw new FormatException(Localization.UnrecognizedEvent);
            }
            var result = new BlockEvent(parent)
            {
                Type = type, id = new IDReference(match.Groups[1].Value.Trim())
            };

            if (match.Groups[3].Success)
            {
                result.Payload = ushort.Parse(match.Groups[3].Value.Trim(), CultureInfo.InvariantCulture);
            }
            return(result);
        }
        public Task ExecuteAsync_GetCurrentWatchesAsyncReturnNonEmptyList_ShouldInvokeConfirmationUpdateAsync(BlockEventType eventType)
        {
            return(AsynchronousTesting.WithCancellationTokenAsync(async cancellationToken =>
            {
                // Arrange.
                var confirmationType = FakeConfirmationWatcher.GetConfirmationType(eventType);
                var block0 = TestBlock.Regtest0;
                var block1 = TestBlock.Regtest1;
                var ctx1 = new object();
                var ctx2 = new object();
                var ctx3 = new object();
                var watch1 = new BalanceWatch <object, int>(ctx1, block0.GetHash(), block0.Transactions[0].GetHash(), TestAddress.Regtest1, 10);
                var watch2 = new BalanceWatch <object, int>(ctx2, block1.GetHash(), block1.Transactions[0].GetHash(), TestAddress.Regtest1, -10);
                var watch3 = new BalanceWatch <object, int>(ctx3, block1.GetHash(), block1.Transactions[0].GetHash(), TestAddress.Regtest2, 5);
                var watches = new[] { watch1, watch2, watch3 };

                this.handler.Setup(h => h.GetBalanceChangesAsync(It.IsAny <Transaction>(), It.IsAny <CancellationToken>()))
                .ReturnsAsync(new Dictionary <BitcoinAddress, BalanceChange <object, int> >());

                this.handler.Setup(h => h.GetCurrentWatchesAsync(It.IsAny <CancellationToken>()))
                .ReturnsAsync(watches);

                this.handler.Setup(h => h.ConfirmationUpdateAsync(It.Is <BalanceConfirmation <object, int> >(c => c.Address == TestAddress.Regtest1), 1, confirmationType, It.IsAny <CancellationToken>()))
                .ReturnsAsync(true);

                this.blocks.Setup(b => b.GetAsync(block0.GetHash(), It.IsAny <CancellationToken>()))
                .ReturnsAsync((block0, 0));

                this.blocks.Setup(b => b.GetAsync(block1.GetHash(), It.IsAny <CancellationToken>()))
                .ReturnsAsync((block1, 1));

                // Act.
                await this.subject.ExecuteAsync(block1, 1, eventType, cancellationToken);

                // Assert.
                this.handler.Verify(
                    h => h.GetCurrentWatchesAsync(cancellationToken),
                    Times.Once()
                    );

                this.handler.Verify(
                    h => h.ConfirmationUpdateAsync(
                        It.Is <BalanceConfirmation <object, int> >(
                            c => c.Address == TestAddress.Regtest1 &&
                            c.Changes.Count() == 2 &&
                            c.Changes.Count(bc => bc.Amount == 10 && bc.Confirmation == 2 && bc.Context == ctx1) == 1 &&
                            c.Changes.Count(bc => bc.Amount == -10 && bc.Confirmation == 1 && bc.Context == ctx2) == 1
                            ),
                        1,
                        confirmationType,
                        CancellationToken.None
                        ),
                    Times.Once()
                    );

                this.handler.Verify(
                    h => h.ConfirmationUpdateAsync(
                        It.Is <BalanceConfirmation <object, int> >(
                            c => c.Address == TestAddress.Regtest2 &&
                            c.Changes.Count() == 1 &&
                            c.Changes.Count(bc => bc.Amount == 5 && bc.Confirmation == 1 && bc.Context == ctx3) == 1
                            ),
                        1,
                        confirmationType,
                        CancellationToken.None
                        ),
                    Times.Once()
                    );

                this.handler.Verify(
                    h => h.RemoveCompletedWatchesAsync(
                        It.Is <IEnumerable <BalanceWatch <object, int> > >(
                            l => l.Count() == 2 && l.Contains(watch1) && l.Contains(watch2)
                            ),
                        CancellationToken.None
                        ),
                    Times.Once()
                    );
            }));
        }
Esempio n. 9
0
 public BlockEvent(BlockAlias alias, BlockEventType type)
 {
     _value = alias.Value | (((uint)type) << Shift);
 }
Esempio n. 10
0
 protected abstract Task <ISet <TWatch> > ExecuteWatchesAsync(
     IEnumerable <TWatch> watches,
     Block block,
     int height,
     BlockEventType eventType,
     CancellationToken cancellationToken);
        public void GetConfirmationType_WithValidBlockEventType_ShouldSuccess(BlockEventType eventType, ConfirmationType expected)
        {
            var result = FakeConfirmationWatcher.GetConfirmationType(eventType);

            Assert.Equal(expected, result);
        }
 public static new ConfirmationType GetConfirmationType(BlockEventType eventType)
 {
     return(ConfirmationWatcher <object, Watch <object>, object> .GetConfirmationType(eventType));
 }
        public Task ExecuteAsync_ConfirmationUpdateAsyncReturnTrue_ShouldRemoveThatWatch(BlockEventType eventType)
        {
            return(AsynchronousTesting.WithCancellationTokenAsync(async cancellationToken =>
            {
                // Arrange.
                var watch1 = new TransactionWatch <object>(null, TestBlock.Regtest0.GetHash(), uint256.One);
                var watch2 = new TransactionWatch <object>(null, TestBlock.Regtest1.GetHash(), uint256.One);
                var watches = new[] { watch1, watch2 };

                var confirmationType = FakeConfirmationWatcher.GetConfirmationType(eventType);

                this.handler.Setup(h => h.GetCurrentWatchesAsync(It.IsAny <CancellationToken>()))
                .Returns(Task.FromResult <IEnumerable <TransactionWatch <object> > >(watches));

                this.handler.Setup(h => h.ConfirmationUpdateAsync(watch1, 2, confirmationType, It.IsAny <CancellationToken>()))
                .Returns(Task.FromResult(false));
                this.handler.Setup(h => h.ConfirmationUpdateAsync(watch2, 1, confirmationType, It.IsAny <CancellationToken>()))
                .Returns(Task.FromResult(true));

                this.blocks.Setup(b => b.GetAsync(TestBlock.Regtest0.GetHash(), It.IsAny <CancellationToken>()))
                .Returns(Task.FromResult((TestBlock.Regtest0, 0)));
                this.blocks.Setup(b => b.GetAsync(TestBlock.Regtest1.GetHash(), It.IsAny <CancellationToken>()))
                .Returns(Task.FromResult((TestBlock.Regtest1, 1)));

                // Act.
                await this.subject.ExecuteAsync(TestBlock.Regtest1, 1, eventType, cancellationToken);

                // Assert.
                this.handler.Verify(
                    h => h.ConfirmationUpdateAsync(
                        watch1,
                        2,
                        confirmationType,
                        CancellationToken.None
                        ),
                    Times.Once()
                    );

                this.handler.Verify(
                    h => h.ConfirmationUpdateAsync(
                        watch2,
                        1,
                        confirmationType,
                        CancellationToken.None
                        ),
                    Times.Once()
                    );

                this.handler.Verify(
                    h => h.RemoveCompletedWatchesAsync(
                        new[] { watch2 },
                        CancellationToken.None
                        ),
                    Times.Once()
                    );
            }));
        }