Exemplo n.º 1
0
 public void ClearRegistration()
 {
     CoinsRegistered.Clear();
     ChangeOutputAddress = null;
     ActiveOutputAddress = null;
     UnblindedSignature  = null;
     RoundHash           = null;
     AliceClient?.Dispose();
     AliceClient  = null;
     Signed       = false;
     PostedOutput = false;
 }
Exemplo n.º 2
0
        private async Task TryRegisterCoinsAsync(CcjClientRound inputRegistrableRound)
        {
            try
            {
                // Select the most suitable coins to regiter.
                List <TxoRef> registrableCoins = State.GetRegistrableCoins(
                    inputRegistrableRound.State.MaximumInputCountPerPeer,
                    inputRegistrableRound.State.Denomination,
                    inputRegistrableRound.State.FeePerInputs,
                    inputRegistrableRound.State.FeePerOutputs).ToList();

                // If there are no suitable coins to register return.
                if (!registrableCoins.Any())
                {
                    return;
                }

                (HdPubKey change, IEnumerable <HdPubKey> actives)outputAddresses = GetOutputsToRegister(inputRegistrableRound.State.Denomination, inputRegistrableRound.State.SchnorrPubKeys.Count(), registrableCoins);

                SchnorrPubKey[]  schnorrPubKeys = inputRegistrableRound.State.SchnorrPubKeys.ToArray();
                List <Requester> requesters     = new List <Requester>();
                var blindedOutputScriptHashes   = new List <uint256>();

                var registeredAddresses = new List <BitcoinAddress>();
                for (int i = 0; i < schnorrPubKeys.Length; i++)
                {
                    if (outputAddresses.actives.Count() <= i)
                    {
                        break;
                    }

                    BitcoinAddress address = outputAddresses.actives.Select(x => x.GetP2wpkhAddress(Network)).ElementAt(i);

                    SchnorrPubKey schnorrPubKey           = schnorrPubKeys[i];
                    var           outputScriptHash        = new uint256(Hashes.SHA256(address.ScriptPubKey.ToBytes()));
                    var           requester               = new Requester();
                    uint256       blindedOutputScriptHash = requester.BlindMessage(outputScriptHash, schnorrPubKey);
                    requesters.Add(requester);
                    blindedOutputScriptHashes.Add(blindedOutputScriptHash);
                    registeredAddresses.Add(address);
                }

                byte[]  blindedOutputScriptHashesByte = ByteHelpers.Combine(blindedOutputScriptHashes.Select(x => x.ToBytes()));
                uint256 blindedOutputScriptsHash      = new uint256(Hashes.SHA256(blindedOutputScriptHashesByte));

                var inputProofs = new List <InputProofModel>();
                foreach (TxoRef coinReference in registrableCoins)
                {
                    SmartCoin coin = State.GetSingleOrDefaultFromWaitingList(coinReference);
                    if (coin is null)
                    {
                        throw new NotSupportedException("This is impossible.");
                    }

                    coin.Secret = coin.Secret ?? KeyManager.GetSecrets(SaltSoup(), coin.ScriptPubKey).Single();
                    var inputProof = new InputProofModel {
                        Input = coin.GetTxoRef(),
                        Proof = coin.Secret.PrivateKey.SignCompact(blindedOutputScriptsHash)
                    };
                    inputProofs.Add(inputProof);
                }

                AliceClient aliceClient = null;
                try
                {
                    aliceClient = await AliceClient.CreateNewAsync(inputRegistrableRound.RoundId, registeredAddresses, schnorrPubKeys, requesters, Network, outputAddresses.change.GetP2wpkhAddress(Network), blindedOutputScriptHashes, inputProofs, CcjHostUriAction, TorSocks5EndPoint);
                }
                catch (HttpRequestException ex) when(ex.Message.Contains("Input is banned", StringComparison.InvariantCultureIgnoreCase))
                {
                    string[] parts             = ex.Message.Split(new[] { "Input is banned from participation for ", " minutes: " }, StringSplitOptions.RemoveEmptyEntries);
                    string   minutesString     = parts[1];
                    int      minuteInt         = int.Parse(minutesString);
                    string   bannedInputString = parts[2].TrimEnd('.');

                    string[]  bannedInputStringParts = bannedInputString.Split(':', StringSplitOptions.RemoveEmptyEntries);
                    TxoRef    coinReference          = new TxoRef(new uint256(bannedInputStringParts[1]), uint.Parse(bannedInputStringParts[0]));
                    SmartCoin coin = State.GetSingleOrDefaultFromWaitingList(coinReference);

                    if (coin is null)
                    {
                        throw new NotSupportedException("This is impossible.");
                    }

                    coin.BannedUntilUtc = DateTimeOffset.UtcNow + TimeSpan.FromMinutes(minuteInt);

                    Logger.LogWarning <CcjClient>(ex.Message.Split('\n')[1]);

                    await DequeueCoinsFromMixNoLockAsync(coinReference, "Failed to register the coin with the coordinator.");

                    aliceClient?.Dispose();
                    return;
                }
                catch (HttpRequestException ex) when(ex.Message.Contains("Provided input is not unspent", StringComparison.InvariantCultureIgnoreCase))
                {
                    string[] parts            = ex.Message.Split(new[] { "Provided input is not unspent: " }, StringSplitOptions.RemoveEmptyEntries);
                    string   spentInputString = parts[1].TrimEnd('.');

                    string[]  bannedInputStringParts = spentInputString.Split(':', StringSplitOptions.RemoveEmptyEntries);
                    TxoRef    coinReference          = new TxoRef(new uint256(bannedInputStringParts[1]), uint.Parse(bannedInputStringParts[0]));
                    SmartCoin coin = State.GetSingleOrDefaultFromWaitingList(coinReference);

                    if (coin is null)
                    {
                        throw new NotSupportedException("This is impossible.");
                    }

                    coin.SpentAccordingToBackend = true;

                    Logger.LogWarning <CcjClient>(ex.Message.Split('\n')[1]);

                    await DequeueCoinsFromMixNoLockAsync(coinReference, "Failed to register the coin with the coordinator. The coin is already spent.");

                    aliceClient?.Dispose();
                    return;
                }
                catch (HttpRequestException ex) when(ex.Message.Contains("No such running round in InputRegistration.", StringComparison.InvariantCultureIgnoreCase))
                {
                    Logger.LogInfo <CcjClient>("Client tried to register a round that is not in InputRegistration anymore. Trying again later.");
                    aliceClient?.Dispose();
                    return;
                }
                catch (HttpRequestException ex) when(ex.Message.Contains("too-long-mempool-chain", StringComparison.InvariantCultureIgnoreCase))
                {
                    Logger.LogInfo <CcjClient>("Coordinator failed because too much unconfirmed parent transactions. Trying again later.");
                    aliceClient?.Dispose();
                    return;
                }

                var coinsRegistered = new List <SmartCoin>();
                foreach (TxoRef coinReference in registrableCoins)
                {
                    var coin = State.GetSingleOrDefaultFromWaitingList(coinReference);
                    if (coin is null)
                    {
                        throw new NotSupportedException("This is impossible.");
                    }

                    coinsRegistered.Add(coin);
                    State.RemoveCoinFromWaitingList(coin);
                }

                var registration = new ClientRoundRegistration(aliceClient, coinsRegistered, outputAddresses.change.GetP2wpkhAddress(Network));

                CcjClientRound roundRegistered = State.GetSingleOrDefaultRound(aliceClient.RoundId);
                if (roundRegistered is null)
                {
                    // If our SatoshiClient does not yet know about the round, because of delay, then delay the round registration.
                    DelayedRoundRegistration?.Dispose();
                    DelayedRoundRegistration = registration;
                }

                roundRegistered.Registration = registration;
            }
            catch (Exception ex)
            {
                Logger.LogError <CcjClient>(ex);
            }
        }