/// <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()}'."); } }
/// <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); }
/// <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); }
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); }