예제 #1
0
        async Task OnTimeoutAsync(BitcoinAddress address, CancellationToken cancellationToken)
        {
            await this.semaphore.WaitAsync();

            try
            {
                if (!this.watchings.Remove(address, out var watching))
                {
                    // Already completed.
                    return;
                }

                var rule = watching.Rule;

                await this.rules.SetTimedOutAsync(rule.Id, CancellationToken.None);

                await this.addressPool.ReleaseAddressAsync(rule.AddressReservation.Id, CancellationToken.None);

                // Mark all watches that was created by this rule as timed out and calculate total received amount.
                var watches = await this.watches.TransitionToTimedOutAsync(rule, CancellationToken.None);

                var confirmed = watches.Where(p => p.Value > 0).ToDictionary(p => p.Key, p => p.Value);
                var amount    = SumChanges(confirmed, rule.TargetConfirmation);

                // Invoke callback.
                var result = new CallbackData()
                {
                    Received = amount,
                };

                await InvokeCallbackAsync(rule.Callback, rule.TimeoutStatus, result, CancellationToken.None);
            }
            catch (Exception ex) // lgtm[cs/catch-of-all-exceptions]
            {
                this.logger.LogError(ex, "Failed to trigger timeout for address {Address}", address);
            }
            finally
            {
                this.semaphore.Release();
            }
        }
예제 #2
0
        async Task <bool> IConfirmationWatcherHandler <Rule, Watch, Confirmation> .ConfirmationUpdateAsync(
            Confirmation confirm,
            int count,
            ConfirmationType type,
            CancellationToken cancellationToken)
        {
            // All watches MUST come from the same rule; otherwise that mean there is a bug somewhere.
            var      rule    = confirm.Watches.Select(w => w.Key.Context).Distinct().Single();
            var      address = rule.AddressReservation.Address.Address;
            Watching watching;

            await this.semaphore.WaitAsync(cancellationToken);

            try
            {
                if (!this.watchings.ContainsKey(address))
                {
                    // Already timed out.
                    return(false);
                }

                // Update confirmation count for all watches.
                switch (type)
                {
                case ConfirmationType.Confirmed:
                    await this.watches.SetConfirmationCountAsync(confirm.Watches, cancellationToken);

                    break;

                case ConfirmationType.Unconfirming:
                    await this.watches.SetConfirmationCountAsync(
                        confirm.Watches.ToDictionary(
                            p => p.Key,
                            p =>
                    {
                        var confirmation = p.Value - 1;

                        if (confirmation < 0)
                        {
                            throw new ArgumentException(
                                "Some watches contains an invalid confirmation count.",
                                nameof(confirm));
                        }

                        return(confirmation);
                    }),
                        cancellationToken);

                    return(false);

                default:
                    throw new ArgumentOutOfRangeException(nameof(type), type, "The value is unsupported.");
                }

                // Check if completed.
                var amount = SumChanges(confirm.Watches, rule.TargetConfirmation);

                if (amount.Confirmed == null || amount.Confirmed < rule.TargetAmount)
                {
                    return(false);
                }

                // Trigger completion.
                this.watchings.Remove(address, out watching);

                await this.rules.SetSucceededAsync(rule.Id, CancellationToken.None);

                await this.addressPool.ReleaseAddressAsync(rule.AddressReservation.Id, CancellationToken.None);

                // Invoke callback.
                var result = new CallbackData()
                {
                    Received = amount,
                };

                await InvokeCallbackAsync(rule.Callback, CallbackResult.StatusSuccess, result, CancellationToken.None);
            }
            finally
            {
                this.semaphore.Release();
            }

            // Stop timer. We need to do this outside semaphore otherwise it will cause dead lock.
            await watching.Timer.StopAsync(CancellationToken.None);

            return(true);
        }