예제 #1
0
        private async void Round_StatusChangedAsync(object sender, CcjRoundStatus status)
        {
            var round = sender as CcjRound;

            Money feePerInputs  = null;
            Money feePerOutputs = null;

            // If success save the coinjoin.
            if (status == CcjRoundStatus.Succeded)
            {
                using (await CoinJoinsLock.LockAsync())
                {
                    uint256 coinJoinHash = round.SignedCoinJoin.GetHash();
                    CoinJoins.Add(coinJoinHash);
                    await File.AppendAllLinesAsync(CoinJoinsFilePath, new[] { coinJoinHash.ToString() });

                    // When a round succeeded, adjust the denomination as to users still be able to register with the latest round's active output amount.
                    IEnumerable <(Money value, int count)> outputs = round.SignedCoinJoin.GetIndistinguishableOutputs();
                    var bestOutput = outputs.OrderByDescending(x => x.count).FirstOrDefault();
                    if (bestOutput != default)
                    {
                        Money activeOutputAmount = bestOutput.value;

                        var fees = await CcjRound.CalculateFeesAsync(RpcClient, RoundConfig.ConfirmationTarget.Value);

                        feePerInputs  = fees.feePerInputs;
                        feePerOutputs = fees.feePerOutputs;

                        Money newDenominationToGetInWithactiveOutputs = activeOutputAmount - (feePerInputs + 2 * feePerOutputs);
                        if (newDenominationToGetInWithactiveOutputs < RoundConfig.Denomination)
                        {
                            if (newDenominationToGetInWithactiveOutputs > Money.Coins(0.01m))
                            {
                                RoundConfig.Denomination = newDenominationToGetInWithactiveOutputs;
                                await RoundConfig.ToFileAsync();
                            }
                        }
                    }
                }
            }

            // If aborted in signing phase, then ban Alices those didn't sign.
            if (status == CcjRoundStatus.Aborted && round.Phase == CcjRoundPhase.Signing)
            {
                foreach (Alice alice in round.GetAlicesByNot(AliceState.SignedCoinJoin, syncLock: false))                 // Because the event sometimes is raised from inside the lock.
                {
                    // If its from any coinjoin, then don't ban.
                    IEnumerable <OutPoint> utxosToBan = alice.Inputs.Select(x => x.Outpoint);
                    await UtxoReferee.BanUtxosAsync(1, DateTimeOffset.UtcNow, forceNoted : false, round.RoundId, utxosToBan.ToArray());
                }
            }

            // If finished start a new round.
            if (status == CcjRoundStatus.Aborted || status == CcjRoundStatus.Succeded)
            {
                round.StatusChanged       -= Round_StatusChangedAsync;
                round.CoinJoinBroadcasted -= Round_CoinJoinBroadcasted;
                await MakeSureTwoRunningRoundsAsync(feePerInputs, feePerOutputs);
            }
        }
예제 #2
0
        private async void Round_StatusChangedAsync(object sender, CcjRoundStatus status)
        {
            var round = sender as CcjRound;

            // If success save the coinjoin.
            if (status == CcjRoundStatus.Succeded)
            {
                using (await CoinJoinsLock.LockAsync())
                {
                    uint256 coinJoinHash = round.SignedCoinJoin.GetHash();
                    CoinJoins.Add(coinJoinHash);
                    await File.AppendAllLinesAsync(CoinJoinsFilePath, new[] { coinJoinHash.ToString() });
                }
            }

            // If failed in signing phase, then ban Alices those didn't sign.
            if (status == CcjRoundStatus.Failed && round.Phase == CcjRoundPhase.Signing)
            {
                foreach (Alice alice in round.GetAlicesByNot(AliceState.SignedCoinJoin, syncLock: false))                 // Because the event sometimes is raised from inside the lock.
                {
                    // If its from any coinjoin, then don't ban.
                    IEnumerable <OutPoint> utxosToBan = alice.Inputs.Select(x => x.OutPoint);
                    await UtxoReferee.BanUtxosAsync(1, DateTimeOffset.UtcNow, utxosToBan.ToArray());
                }
            }

            // If finished start a new round.
            if (status == CcjRoundStatus.Failed || status == CcjRoundStatus.Succeded)
            {
                round.StatusChanged -= Round_StatusChangedAsync;
                await MakeSureTwoRunningRoundsAsync();
            }
        }
예제 #3
0
        private async void Round_StatusChangedAsync(object sender, CcjRoundStatus status)
        {
            try
            {
                var round = sender as CcjRound;

                Money feePerInputs  = null;
                Money feePerOutputs = null;

                // If success save the coinjoin.
                if (status == CcjRoundStatus.Succeded)
                {
                    using (await CoinJoinsLock.LockAsync())
                    {
                        uint256 coinJoinHash = round.SignedCoinJoin.GetHash();
                        CoinJoins.Add(coinJoinHash);
                        await File.AppendAllLinesAsync(CoinJoinsFilePath, new[] { coinJoinHash.ToString() });

                        // When a round succeeded, adjust the denomination as to users still be able to register with the latest round's active output amount.
                        IEnumerable <(Money value, int count)> outputs = round.SignedCoinJoin.GetIndistinguishableOutputs(includeSingle: true);
                        var bestOutput = outputs.OrderByDescending(x => x.count).FirstOrDefault();
                        if (bestOutput != default)
                        {
                            Money activeOutputAmount = bestOutput.value;

                            int currentConfirmationTarget = await AdjustConfirmationTargetAsync(lockCoinJoins : false);

                            var fees = await CcjRound.CalculateFeesAsync(RpcClient, currentConfirmationTarget);

                            feePerInputs  = fees.feePerInputs;
                            feePerOutputs = fees.feePerOutputs;

                            Money newDenominationToGetInWithactiveOutputs = activeOutputAmount - (feePerInputs + 2 * feePerOutputs);
                            if (newDenominationToGetInWithactiveOutputs < RoundConfig.Denomination)
                            {
                                if (newDenominationToGetInWithactiveOutputs > Money.Coins(0.01m))
                                {
                                    RoundConfig.Denomination = newDenominationToGetInWithactiveOutputs;
                                    await RoundConfig.ToFileAsync();
                                }
                            }
                        }
                    }
                }

                // If aborted in signing phase, then ban Alices those didn't sign.
                if (status == CcjRoundStatus.Aborted && round.Phase == CcjRoundPhase.Signing)
                {
                    IEnumerable <Alice> alicesDidntSign = round.GetAlicesByNot(AliceState.SignedCoinJoin, syncLock: false);

                    CcjRound nextRound = GetCurrentInputRegisterableRoundOrDefault(syncLock: false);

                    if (nextRound != null)
                    {
                        int nextRoundAlicesCount = nextRound.CountAlices(syncLock: false);
                        var alicesSignedCount    = round.AnonymitySet - alicesDidntSign.Count();

                        // New round's anonset should be the number of alices those signed in this round.
                        // Except if the number of alices in the next round is already larger.
                        var newAnonymitySet = Math.Max(alicesSignedCount, nextRoundAlicesCount);
                        // But it cannot be larger than the current anonset of that round.
                        newAnonymitySet = Math.Min(newAnonymitySet, nextRound.AnonymitySet);

                        // Only change the anonymity set of the next round if new anonset doesnt equal and newanonset larger than 1.
                        if (nextRound.AnonymitySet != newAnonymitySet && newAnonymitySet > 1)
                        {
                            nextRound.UpdateAnonymitySet(newAnonymitySet, syncLock: false);

                            if (nextRoundAlicesCount >= nextRound.AnonymitySet)
                            {
                                // Progress to the next phase, which will be OutputRegistration
                                await nextRound.ExecuteNextPhaseAsync(CcjRoundPhase.ConnectionConfirmation);
                            }
                        }
                    }

                    foreach (Alice alice in alicesDidntSign)                     // Because the event sometimes is raised from inside the lock.
                    {
                        // If its from any coinjoin, then don't ban.
                        IEnumerable <OutPoint> utxosToBan = alice.Inputs.Select(x => x.Outpoint);
                        await UtxoReferee.BanUtxosAsync(1, DateTimeOffset.UtcNow, forceNoted : false, round.RoundId, utxosToBan.ToArray());
                    }
                }

                // If finished start a new round.
                if (status == CcjRoundStatus.Aborted || status == CcjRoundStatus.Succeded)
                {
                    round.StatusChanged       -= Round_StatusChangedAsync;
                    round.CoinJoinBroadcasted -= Round_CoinJoinBroadcasted;
                    await MakeSureTwoRunningRoundsAsync(feePerInputs, feePerOutputs);
                }
            }
            catch (Exception ex)
            {
                Logger.LogWarning <CcjCoordinator>(ex);
            }
        }
예제 #4
0
 private void OnStatusChanged(CcjRoundStatus status) => StatusChanged?.Invoke(this, status);