Пример #1
0
        /// <summary>
        /// Synchronizes the items that exist in the destination only.
        /// </summary>
        /// <param name="batchKeys">The keys of the items.</param>
        /// <param name="cancellationToken">A cancellation token that can be used to cancel the work.</param>
        /// <returns></returns>
        private async Task SyncItemsInDestinationOnlyBatchAsync(List <TKey> batchKeys, CancellationToken cancellationToken)
        {
            if (!batchKeys.Any())
            {
                return;
            }

            switch (Configurations.SyncMode.ItemsInDestinationOnly)
            {
            case SyncItemOperation.None:      // do nothing
                break;

            case SyncItemOperation.Add:
                var comparisonResult = new ComparisonResult <TItem>();
                comparisonResult.ItemsInDestinationOnly.AddRange(await DestinationProvider.GetAsync(batchKeys, cancellationToken).ConfigureAwait(false));
                await SyncAsync(comparisonResult, cancellationToken).ConfigureAwait(false);

                break;

            case SyncItemOperation.Delete:
                BeforeDeletingItemsFromDestinationAction?.Invoke(batchKeys);
                await DestinationProvider.DeleteAsync(batchKeys, cancellationToken).ConfigureAwait(false);

                break;

            default:
                throw new NotSupportedException($"Not supported destination {nameof(SyncItemOperation)} '{Configurations.SyncMode.ItemsInDestinationOnly.ToString()}'.");
            }
        }
Пример #2
0
        /// <summary>
        /// Delete the items from the destination.
        /// </summary>
        /// <param name="items">The items to be deleted.</param>
        /// <param name="cancellationToken">A cancellation token that can be used to cancel the work.</param>
        /// <returns></returns>
        protected override async Task DeleteFromDestinationAsync(List <TItem> items, CancellationToken cancellationToken)
        {
            if (items == null || !items.Any())
            {
                return;
            }

            await DestinationProvider.DeleteAsync(items.Select(x => KeySelector(x)).ToList(), cancellationToken).ConfigureAwait(false);
        }
Пример #3
0
        /// <summary>
        /// Synchronizes the items that exist in the source and destination.
        /// </summary>
        /// <param name="batchKeys">The keys of the items.</param>
        /// <param name="cancellationToken">A cancellation token that can be used to cancel the work.</param>
        /// <returns></returns>
        private async Task SyncMatchesBatchAsync(List <TKey> batchKeys, CancellationToken cancellationToken)
        {
            var srcTask = SourceProvider.GetAsync(batchKeys, cancellationToken);
            var dstTask = DestinationProvider.GetAsync(batchKeys, cancellationToken);

            await Task.WhenAll(srcTask, dstTask);

            // Compare
            var comparisonResult = await ComparerAgent <TKey, TItem> .Create()
                                   .Configure((c) =>
            {
                c.AllowDuplicateItems = RuleAllowanceType.None;
                c.AllowDuplicateKeys  = RuleAllowanceType.None;
                c.AllowNullableItems  = RuleAllowanceType.None;
            })
                                   .SetKeySelector(KeySelector)
                                   .SetCompareItemFunc(CompareItemFunc)
                                   .SetSourceProvider(srcTask.Result)
                                   .SetDestinationProvider(dstTask.Result)
                                   .CompareAsync(cancellationToken).ConfigureAwait(false);

            // Sync
            await SyncAsync(comparisonResult, cancellationToken).ConfigureAwait(false);
        }
Пример #4
0
    private async Task <IEnumerable <TxOut> > ProceedWithOutputRegistrationPhaseAsync(uint256 roundId, ImmutableArray <AliceClient> registeredAliceClients, CancellationToken cancellationToken)
    {
        // Waiting for OutputRegistration phase, all the Alices confirmed their connections, so the list of the inputs will be complete.
        var roundState = await RoundStatusUpdater.CreateRoundAwaiter(roundId, Phase.OutputRegistration, cancellationToken).ConfigureAwait(false);

        var roundParameters = roundState.CoinjoinState.Parameters;
        var remainingTime   = roundParameters.OutputRegistrationTimeout - RoundStatusUpdater.Period;
        var now             = DateTimeOffset.UtcNow;
        var outputRegistrationPhaseEndTime = now + remainingTime;

        // Splitting the remaining time.
        // Both operations are done under output registration phase, so we have to do the random timing taking that into account.
        var outputRegistrationEndTime = now + (remainingTime * 0.8);                     // 80% of the time.
        var readyToSignEndTime        = outputRegistrationEndTime + remainingTime * 0.2; // 20% of the time.

        CoinJoinClientProgress.SafeInvoke(this, new EnteringOutputRegistrationPhase(roundState, outputRegistrationPhaseEndTime));

        using CancellationTokenSource phaseTimeoutCts = new(remainingTime + ExtraPhaseTimeoutMargin);
        using CancellationTokenSource linkedCts       = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, phaseTimeoutCts.Token);

        var registeredCoins = registeredAliceClients.Select(x => x.SmartCoin.Coin);
        var inputEffectiveValuesAndSizes = registeredAliceClients.Select(x => (x.EffectiveValue, x.SmartCoin.ScriptPubKey.EstimateInputVsize()));
        var availableVsize = registeredAliceClients.SelectMany(x => x.IssuedVsizeCredentials).Sum(x => x.Value);

        // Calculate outputs values
        var constructionState = roundState.Assert <ConstructionState>();

        AmountDecomposer amountDecomposer = new(roundParameters.MiningFeeRate, roundParameters.AllowedOutputAmounts, Constants.P2wpkhOutputVirtualSize, Constants.P2wpkhInputVirtualSize, (int)availableVsize);
        var theirCoins = constructionState.Inputs.Where(x => !registeredCoins.Any(y => x.Outpoint == y.Outpoint));
        var registeredCoinEffectiveValues = registeredAliceClients.Select(x => x.EffectiveValue);
        var theirCoinEffectiveValues      = theirCoins.Select(x => x.EffectiveValue(roundParameters.MiningFeeRate, roundParameters.CoordinationFeeRate));
        var outputValues = amountDecomposer.Decompose(registeredCoinEffectiveValues, theirCoinEffectiveValues);

        // Get as many destinations as outputs we need.
        var destinations = DestinationProvider.GetNextDestinations(outputValues.Count()).ToArray();
        var outputTxOuts = outputValues.Zip(destinations, (amount, destination) => new TxOut(amount, destination.ScriptPubKey));

        DependencyGraph dependencyGraph        = DependencyGraph.ResolveCredentialDependencies(inputEffectiveValuesAndSizes, outputTxOuts, roundParameters.MiningFeeRate, roundParameters.CoordinationFeeRate, roundParameters.MaxVsizeAllocationPerAlice);
        DependencyGraphTaskScheduler scheduler = new(dependencyGraph);

        // Re-issuances.
        var bobClient = CreateBobClient(roundState);

        roundState.LogInfo("Starting reissuances.");
        var combinedToken = linkedCts.Token;
        await scheduler.StartReissuancesAsync(registeredAliceClients, bobClient, combinedToken).ConfigureAwait(false);

        // Output registration.
        roundState.LogDebug($"Output registration started - it will end in: {outputRegistrationEndTime - DateTimeOffset.UtcNow:hh\\:mm\\:ss}.");

        var outputRegistrationScheduledDates = GetScheduledDates(outputTxOuts.Count(), outputRegistrationEndTime, MaximumRequestDelay);
        await scheduler.StartOutputRegistrationsAsync(outputTxOuts, bobClient, KeyChain, outputRegistrationScheduledDates, combinedToken).ConfigureAwait(false);

        roundState.LogDebug($"Outputs({outputTxOuts.Count()}) were registered.");

        // ReadyToSign.
        roundState.LogDebug($"ReadyToSign phase started - it will end in: {readyToSignEndTime - DateTimeOffset.UtcNow:hh\\:mm\\:ss}.");
        await ReadyToSignAsync(registeredAliceClients, readyToSignEndTime, combinedToken).ConfigureAwait(false);

        roundState.LogDebug($"Alices({registeredAliceClients.Length}) are ready to sign.");
        return(outputTxOuts);
    }