示例#1
0
    private void LogCoinJoinSummary(ImmutableArray <AliceClient> registeredAliceClients, IEnumerable <TxOut> myOutputs, Transaction unsignedCoinJoinTransaction, RoundState roundState)
    {
        RoundParameters roundParameters = roundState.CoinjoinState.Parameters;
        FeeRate         feeRate         = roundParameters.MiningFeeRate;

        var totalEffectiveInputAmount  = Money.Satoshis(registeredAliceClients.Sum(a => a.EffectiveValue));
        var totalEffectiveOutputAmount = Money.Satoshis(myOutputs.Sum(a => a.Value - feeRate.GetFee(a.ScriptPubKey.EstimateOutputVsize())));
        var effectiveDifference        = totalEffectiveInputAmount - totalEffectiveOutputAmount;

        var totalInputAmount  = Money.Satoshis(registeredAliceClients.Sum(a => a.SmartCoin.Amount));
        var totalOutputAmount = Money.Satoshis(myOutputs.Sum(a => a.Value));
        var totalDifference   = Money.Satoshis(totalInputAmount - totalOutputAmount);

        var inputNetworkFee      = Money.Satoshis(registeredAliceClients.Sum(alice => feeRate.GetFee(alice.SmartCoin.Coin.ScriptPubKey.EstimateInputVsize())));
        var outputNetworkFee     = Money.Satoshis(myOutputs.Sum(output => feeRate.GetFee(output.ScriptPubKey.EstimateOutputVsize())));
        var totalNetworkFee      = inputNetworkFee + outputNetworkFee;
        var totalCoordinationFee = Money.Satoshis(registeredAliceClients.Where(a => a.IsPayingZeroCoordinationFee).Sum(a => roundParameters.CoordinationFeeRate.GetFee(a.SmartCoin.Amount)));

        string[] summary = new string[]
        {
            "",
            $"\tInput total: {totalInputAmount.ToString(true, false)} Eff: {totalEffectiveInputAmount.ToString(true, false)} NetwFee: {inputNetworkFee.ToString(true, false)} CoordFee: {totalCoordinationFee.ToString(true)}",
            $"\tOutpu total: {totalOutputAmount.ToString(true, false)} Eff: {totalEffectiveOutputAmount.ToString(true, false)} NetwFee: {outputNetworkFee.ToString(true, false)}",
            $"\tTotal diff : {totalDifference.ToString(true, false)}",
            $"\tEffec diff : {effectiveDifference.ToString(true, false)}",
            $"\tTotal fee  : {totalNetworkFee.ToString(true, false)}"
        };

        roundState.LogDebug(string.Join(Environment.NewLine, summary));
    }
示例#2
0
    public async Task <CoinJoinResult> StartRoundAsync(IEnumerable <SmartCoin> smartCoins, RoundState roundState, CancellationToken cancellationToken)
    {
        var roundId = roundState.Id;

        ImmutableArray <(AliceClient AliceClient, PersonCircuit PersonCircuit)> registeredAliceClientAndCircuits = ImmutableArray <(AliceClient, PersonCircuit)> .Empty;

        // Because of the nature of the protocol, the input registration and the connection confirmation phases are done subsequently thus they have a merged timeout.
        var timeUntilOutputReg = (roundState.InputRegistrationEnd - DateTimeOffset.UtcNow) + roundState.CoinjoinState.Parameters.ConnectionConfirmationTimeout;

        try
        {
            try
            {
                using CancellationTokenSource timeUntilOutputRegCts = new(timeUntilOutputReg + ExtraPhaseTimeoutMargin);
                using CancellationTokenSource linkedCts             = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeUntilOutputRegCts.Token);

                registeredAliceClientAndCircuits = await ProceedWithInputRegAndConfirmAsync(smartCoins, roundState, linkedCts.Token).ConfigureAwait(false);
            }
            catch (UnexpectedRoundPhaseException ex)
            {
                roundState = ex.RoundState;
                var message = ex.RoundState.EndRoundState switch
                {
                    EndRoundState.AbortedNotEnoughAlices => $"Not enough participants in the round to continue. Waiting for a new round.",
                    _ => $"Registration phase ended by the coordinator: '{ex.Message}' code: '{ex.RoundState.EndRoundState}'."
                };

                roundState.LogInfo(message);
                return(new CoinJoinResult(false));
            }

            if (!registeredAliceClientAndCircuits.Any())
            {
                roundState.LogInfo("There are no available Alices to participate with.");
                return(new CoinJoinResult(false));
            }

            roundState.LogDebug($"Successfully registered {registeredAliceClientAndCircuits.Length} inputs.");

            var registeredAliceClients = registeredAliceClientAndCircuits.Select(x => x.AliceClient).ToImmutableArray();

            var outputTxOuts = await ProceedWithOutputRegistrationPhaseAsync(roundId, registeredAliceClients, cancellationToken).ConfigureAwait(false);

            var(unsignedCoinJoin, aliceClientsThatSigned) = await ProceedWithSigningStateAsync(roundId, registeredAliceClients, outputTxOuts, cancellationToken).ConfigureAwait(false);

            roundState = await RoundStatusUpdater.CreateRoundAwaiter(s => s.Id == roundId && s.Phase == Phase.Ended, cancellationToken).ConfigureAwait(false);

            var msg = roundState.EndRoundState switch
            {
                EndRoundState.TransactionBroadcasted => $"Broadcasted. Coinjoin TxId: ({unsignedCoinJoin.GetHash()})",
                EndRoundState.TransactionBroadcastFailed => $"Failed to broadcast. Coinjoin TxId: ({unsignedCoinJoin.GetHash()})",
                EndRoundState.AbortedWithError => "Round abnormally finished.",
                EndRoundState.AbortedNotEnoughAlices => "Aborted. Not enough participants.",
                EndRoundState.AbortedNotEnoughAlicesSigned => "Aborted. Not enough participants signed the coinjoin transaction.",
                EndRoundState.NotAllAlicesSign => "Aborted. Some Alices didn't sign. Go to blame round.",
                EndRoundState.None => "Unknown.",
                _ => throw new ArgumentOutOfRangeException()
            };
            roundState.LogInfo(msg);

            LogCoinJoinSummary(registeredAliceClients, outputTxOuts, unsignedCoinJoin, roundState);

            return(new CoinJoinResult(
                       GoForBlameRound: roundState.EndRoundState == EndRoundState.NotAllAlicesSign,
                       SuccessfulBroadcast: roundState.EndRoundState == EndRoundState.TransactionBroadcasted,
                       RegisteredCoins: aliceClientsThatSigned.Select(a => a.SmartCoin).ToImmutableList(),
                       RegisteredOutputs: outputTxOuts.Select(o => o.ScriptPubKey).ToImmutableList()));
        }
        finally
        {
            foreach (var coins in smartCoins)
            {
                coins.CoinJoinInProgress = false;
            }

            foreach (var aliceClientAndCircuit in registeredAliceClientAndCircuits)
            {
                aliceClientAndCircuit.AliceClient.Finish();
                aliceClientAndCircuit.PersonCircuit.Dispose();
            }
            CoinJoinClientProgress.SafeInvoke(this, new LeavingCriticalPhase());
            CoinJoinClientProgress.SafeInvoke(this, new RoundEnded(roundState));
        }
    }