Exemplo n.º 1
0
            public Input(OutPoint previousOutPoint, byte[] signatureScript, uint sequence)
            {
                if (signatureScript == null)
                    throw new ArgumentNullException(nameof(signatureScript));

                PreviousOutpoint = previousOutPoint;
                SignatureScript = signatureScript;
                Sequence = sequence;
            }
Exemplo n.º 2
0
		public void CanGetUTXOsMempool()
		{
			var client = CreateRestClient();
			var txId = uint256.Parse("3a3422dfd155f1d2ffc3e46cf978a9c5698c17c187f04cfa1b93358699c4ed3f");
			var outPoint = new OutPoint(txId, 0);
			var utxos = client.GetUnspectOutputsAsync(new []{ outPoint }, true).Result;
			Assert.Equal(1, utxos.Outputs.Length);
			Assert.Equal(1, (int)utxos.Outputs[0].Version);
			Assert.Equal(Money.Parse("0.1"), (int)utxos.Outputs[0].Output.Value);
		}
Exemplo n.º 3
0
		public void CanGetUTXOs()
		{
			var client = CreateRestClient();
			var txId = uint256.Parse("3a3422dfd155f1d2ffc3e46cf978a9c5698c17c187f04cfa1b93358699c4ed3f");
			var outPoint = new OutPoint(txId, 0);
			var utxos = client.GetUnspectOutputsAsync(new[] { outPoint }, false).Result;
			Assert.Equal(true, utxos.Bitmap[0]);
			Assert.Equal(false, utxos.Bitmap[1]);
			Assert.Equal(0, utxos.Outputs.Length);
		}
Exemplo n.º 4
0
		public UnspentCoin(JObject unspent)
		{
			OutPoint = new OutPoint(uint256.Parse((string)unspent["txid"]), (uint)unspent["vout"]);
			Address = Network.CreateFromBase58Data<BitcoinAddress>((string)unspent["address"]);
			Account = (string)unspent["account"];
			ScriptPubKey = new Script(Encoders.Hex.DecodeData((string)unspent["scriptPubKey"]));
			var amount = (decimal)unspent["amount"];
			Amount = new Money((long)(amount * Money.COIN));
			Confirmations = (uint)unspent["confirmations"];
		}
Exemplo n.º 5
0
		public void CanGetUTXOsMempool()
		{
			var client = CreateRestClient();
			var txId = uint256.Parse("bd3052b71c4fdd8f49c54880db4a59aadcfff3c11e8f23e9ed7f7ab4a9bb5700");
			var outPoint = new OutPoint(txId, 0);
			var utxos = client.GetUnspentOutputsAsync(new []{ outPoint }, true).Result;
			Assert.Equal(1, utxos.Outputs.Length);
			Assert.Equal(1, (int)utxos.Outputs[0].Version);
			Assert.Equal(Money.Parse("0.01"), (int)utxos.Outputs[0].Output.Value);
		}
Exemplo n.º 6
0
            public Input(OutPoint previousOutPoint, uint sequence, Amount inputAmount,
                uint blockHeight, uint blockIndex, byte[] signatureScript)
            {
                if (signatureScript == null)
                    throw new ArgumentNullException(nameof(signatureScript));

                PreviousOutpoint = previousOutPoint;
                Sequence = sequence;

                SignatureScript = signatureScript;
                InputAmount = inputAmount;
                BlockHeight = blockHeight;
                BlockIndex = blockIndex;
            }
Exemplo n.º 7
0
    public void OnConnectUp(GameObject pObject)
    {
        setPoint(pObject);
        if (choosedInPoint && choosedOutPoint)
        {
            choosedOutPoint.connect(choosedInPoint);
            choosedInPoint.showLine();
        }
        choosedInPoint = null;
        choosedOutPoint = null;
        choosedPointTransform = null;

        connectLine.visible = false;
    }
Exemplo n.º 8
0
    public bool setPoint(GameObject pObject)
    {
        if (!pObject)
            return false;

        if (pObject.GetComponent<InPoint>())
            choosedInPoint = pObject.GetComponent<InPoint>();
        else if (pObject.GetComponent<OutPoint>())
            choosedOutPoint = pObject.GetComponent<OutPoint>();
        else
            return false;

        choosedPointTransform = pObject.transform;
        connectLine.visible = true;
        refreshLineShow();
        return true;
    }
Exemplo n.º 9
0
        public async Task TestRewindAsync()
        {
            HashHeightPair tip = this.cachedCoinView.GetTipHash();

            Assert.Equal(this.chainIndexer.Genesis.HashBlock, tip.Hash);

            int currentHeight = 0;

            // Create a lot of new coins.
            List <UnspentOutput> outputsList = this.CreateOutputsList(currentHeight + 1, 100);

            this.SaveChanges(outputsList, currentHeight + 1);
            currentHeight++;

            this.cachedCoinView.Flush(true);

            HashHeightPair tipAfterOriginalCoinsCreation = this.cachedCoinView.GetTipHash();

            // Collection that will be used as a coinview that we will update in parallel. Needed to verify that actual coinview is ok.
            List <OutPoint> outPoints = this.ConvertToListOfOutputPoints(outputsList);

            // Copy of current state to later rewind and verify against it.
            List <OutPoint> copyOfOriginalOutPoints = new List <OutPoint>(outPoints);

            List <OutPoint> copyAfterHalfOfAdditions = new List <OutPoint>();
            HashHeightPair  coinviewTipAfterHalf     = null;

            int addChangesTimes = 500;

            // Spend some coins in the next N saves.
            for (int i = 0; i < addChangesTimes; ++i)
            {
                OutPoint        txId     = outPoints[this.random.Next(0, outPoints.Count)];
                List <OutPoint> txPoints = outPoints.Where(x => x.Hash == txId.Hash).ToList();
                this.Shuffle(txPoints);
                List <OutPoint> txPointsToSpend = txPoints.Take(txPoints.Count / 2).ToList();

                // First spend in cached coinview
                FetchCoinsResponse response = this.cachedCoinView.FetchCoins(txPoints.ToArray());
                Assert.Equal(txPoints.Count, response.UnspentOutputs.Count);
                var toSpend = new List <UnspentOutput>();
                foreach (OutPoint outPointToSpend in txPointsToSpend)
                {
                    response.UnspentOutputs[outPointToSpend].Spend();
                    toSpend.Add(response.UnspentOutputs[outPointToSpend]);
                }

                // Spend from outPoints.
                outPoints.RemoveAll(x => txPointsToSpend.Contains(x));

                // Save coinview
                this.SaveChanges(toSpend, currentHeight + 1);

                currentHeight++;

                if (i == addChangesTimes / 2)
                {
                    copyAfterHalfOfAdditions = new List <OutPoint>(outPoints);
                    coinviewTipAfterHalf     = this.cachedCoinView.GetTipHash();
                }
            }

            await this.ValidateCoinviewIntegrityAsync(outPoints);

            for (int i = 0; i < addChangesTimes; i++)
            {
                this.cachedCoinView.Rewind();

                HashHeightPair currentTip = this.cachedCoinView.GetTipHash();

                if (currentTip == coinviewTipAfterHalf)
                {
                    await this.ValidateCoinviewIntegrityAsync(copyAfterHalfOfAdditions);
                }
            }

            Assert.Equal(tipAfterOriginalCoinsCreation, this.cachedCoinView.GetTipHash());

            await this.ValidateCoinviewIntegrityAsync(copyOfOriginalOutPoints);
        }
Exemplo n.º 10
0
        public async Task <IActionResult> PostInputsAsync([FromBody, Required] InputsRequest request)
        {
            // Validate request.
            if (request.RoundId < 0 || !ModelState.IsValid)
            {
                return(BadRequest("Invalid request."));
            }

            if (request.Inputs.Count() > 7)
            {
                return(BadRequest("Maximum 7 inputs can be registered."));
            }

            using (await InputsLock.LockAsync())
            {
                CcjRound round = Coordinator.TryGetRound(request.RoundId);

                if (round is null || round.Phase != CcjRoundPhase.InputRegistration)
                {
                    return(NotFound("No such running round in InputRegistration. Try another round."));
                }

                // Do more checks.
                try
                {
                    uint256[] blindedOutputs        = request.BlindedOutputScripts.ToArray();
                    int       blindedOutputCount    = blindedOutputs.Length;
                    int       maxBlindedOutputCount = round.MixingLevels.Count();
                    if (blindedOutputCount > maxBlindedOutputCount)
                    {
                        return(BadRequest($"Too many blinded output was provided: {blindedOutputCount}, maximum: {maxBlindedOutputCount}."));
                    }

                    if (blindedOutputs.Distinct().Count() < blindedOutputs.Length)
                    {
                        return(BadRequest("Duplicate blinded output found."));
                    }

                    if (round.ContainsAnyBlindedOutputScript(blindedOutputs))
                    {
                        return(BadRequest("Blinded output has already been registered."));
                    }

                    if (request.ChangeOutputAddress.Network != Network)
                    {
                        // RegTest and TestNet address formats are sometimes the same.
                        if (Network == Network.Main)
                        {
                            return(BadRequest($"Invalid ChangeOutputAddress Network."));
                        }
                    }

                    var uniqueInputs = new HashSet <TxoRef>();
                    foreach (InputProofModel inputProof in request.Inputs)
                    {
                        if (uniqueInputs.Contains(inputProof.Input))
                        {
                            return(BadRequest("Cannot register an input twice."));
                        }
                        uniqueInputs.Add(inputProof.Input);
                    }

                    var alicesToRemove    = new HashSet <Guid>();
                    var getTxOutResponses = new List <(InputProofModel inputModel, Task <GetTxOutResponse> getTxOutTask)>();

                    var batch = RpcClient.PrepareBatch();

                    foreach (InputProofModel inputProof in request.Inputs)
                    {
                        if (round.ContainsInput(inputProof.Input.ToOutPoint(), out List <Alice> tr))
                        {
                            alicesToRemove.UnionWith(tr.Select(x => x.UniqueId));                             // Input is already registered by this alice, remove it later if all the checks are completed fine.
                        }
                        if (Coordinator.AnyRunningRoundContainsInput(inputProof.Input.ToOutPoint(), out List <Alice> tnr))
                        {
                            if (tr.Union(tnr).Count() > tr.Count)
                            {
                                return(BadRequest("Input is already registered in another round."));
                            }
                        }

                        OutPoint outpoint   = inputProof.Input.ToOutPoint();
                        var      bannedElem = await Coordinator.UtxoReferee.TryGetBannedAsync(outpoint, notedToo : false);

                        if (bannedElem != null)
                        {
                            return(BadRequest($"Input is banned from participation for {(int)bannedElem.BannedRemaining.TotalMinutes} minutes: {inputProof.Input.Index}:{inputProof.Input.TransactionId}."));
                        }

                        var txOutResponseTask = batch.GetTxOutAsync(inputProof.Input.TransactionId, (int)inputProof.Input.Index, includeMempool: true);
                        getTxOutResponses.Add((inputProof, txOutResponseTask));
                    }

                    // Perform all RPC request at once
                    var waiting = Task.WhenAll(getTxOutResponses.Select(x => x.getTxOutTask));
                    await batch.SendBatchAsync();

                    await waiting;

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

                    var inputs = new HashSet <Coin>();

                    foreach (var responses in getTxOutResponses)
                    {
                        var(inputProof, getTxOutResponseTask) = responses;
                        var getTxOutResponse = await getTxOutResponseTask;

                        // Check if inputs are unspent.
                        if (getTxOutResponse is null)
                        {
                            return(BadRequest($"Provided input is not unspent: {inputProof.Input.Index}:{inputProof.Input.TransactionId}."));
                        }

                        // Check if unconfirmed.
                        if (getTxOutResponse.Confirmations <= 0)
                        {
                            // If it spends a CJ then it may be acceptable to register.
                            if (!await Coordinator.ContainsCoinJoinAsync(inputProof.Input.TransactionId))
                            {
                                return(BadRequest("Provided input is neither confirmed, nor is from an unconfirmed coinjoin."));
                            }

                            // Check if mempool would accept a fake transaction created with the registered inputs.
                            // This will catch ascendant/descendant count and size limits for example.
                            var result = await RpcClient.TestMempoolAcceptAsync(new[] { new Coin(inputProof.Input.ToOutPoint(), getTxOutResponse.TxOut) });

                            if (!result.accept)
                            {
                                return(BadRequest($"Provided input is from an unconfirmed coinjoin, but a limit is reached: {result.rejectReason}"));
                            }
                        }

                        // Check if immature.
                        if (getTxOutResponse.Confirmations <= 100)
                        {
                            if (getTxOutResponse.IsCoinBase)
                            {
                                return(BadRequest("Provided input is immature."));
                            }
                        }

                        // Check if inputs are native segwit.
                        if (getTxOutResponse.ScriptPubKeyType != "witness_v0_keyhash")
                        {
                            return(BadRequest("Provided input must be witness_v0_keyhash."));
                        }

                        TxOut txOut = getTxOutResponse.TxOut;

                        var address = (BitcoinWitPubKeyAddress)txOut.ScriptPubKey.GetDestinationAddress(Network);
                        // Check if proofs are valid.
                        if (!address.VerifyMessage(blindedOutputScriptsHash, inputProof.Proof))
                        {
                            return(BadRequest("Provided proof is invalid."));
                        }

                        inputs.Add(new Coin(inputProof.Input.ToOutPoint(), txOut));
                    }

                    var acceptedBlindedOutputScripts = new List <uint256>();

                    // Calculate expected networkfee to pay after base denomination.
                    int   inputCount = inputs.Count;
                    Money networkFeeToPayAfterBaseDenomination = (inputCount * round.FeePerInputs) + (2 * round.FeePerOutputs);

                    // Check if inputs have enough coins.
                    Money inputSum     = inputs.Sum(x => x.Amount);
                    Money changeAmount = (inputSum - (round.MixingLevels.GetBaseDenomination() + networkFeeToPayAfterBaseDenomination));
                    if (changeAmount < Money.Zero)
                    {
                        return(BadRequest($"Not enough inputs are provided. Fee to pay: {networkFeeToPayAfterBaseDenomination.ToString(false, true)} BTC. Round denomination: {round.MixingLevels.GetBaseDenomination().ToString(false, true)} BTC. Only provided: {inputSum.ToString(false, true)} BTC."));
                    }
                    acceptedBlindedOutputScripts.Add(blindedOutputs.First());

                    Money networkFeeToPay = networkFeeToPayAfterBaseDenomination;
                    // Make sure we sign the proper number of additional blinded outputs.
                    var moneySoFar = Money.Zero;
                    for (int i = 1; i < blindedOutputCount; i++)
                    {
                        if (!round.MixingLevels.TryGetDenomination(i, out Money denomination))
                        {
                            break;
                        }

                        Money coordinatorFee = denomination.Percentage(round.CoordinatorFeePercent * round.AnonymitySet);                         // It should be the number of bobs, but we must make sure they'd have money to pay all.
                        changeAmount    -= (denomination + round.FeePerOutputs + coordinatorFee);
                        networkFeeToPay += round.FeePerOutputs;

                        if (changeAmount < Money.Zero)
                        {
                            break;
                        }

                        acceptedBlindedOutputScripts.Add(blindedOutputs[i]);
                    }

                    // Make sure Alice checks work.
                    var alice = new Alice(inputs, networkFeeToPayAfterBaseDenomination, request.ChangeOutputAddress, acceptedBlindedOutputScripts);

                    foreach (Guid aliceToRemove in alicesToRemove)
                    {
                        round.RemoveAlicesBy(aliceToRemove);
                    }
                    round.AddAlice(alice);

                    // All checks are good. Sign.
                    var blindSignatures = new List <uint256>();
                    for (int i = 0; i < acceptedBlindedOutputScripts.Count; i++)
                    {
                        var     blindedOutput  = acceptedBlindedOutputScripts[i];
                        var     signer         = round.MixingLevels.GetLevel(i).Signer;
                        uint256 blindSignature = signer.Sign(blindedOutput);
                        blindSignatures.Add(blindSignature);
                    }
                    alice.BlindedOutputSignatures = blindSignatures.ToArray();

                    // Check if phase changed since.
                    if (round.Status != CcjRoundStatus.Running || round.Phase != CcjRoundPhase.InputRegistration)
                    {
                        return(StatusCode(StatusCodes.Status503ServiceUnavailable, "The state of the round changed while handling the request. Try again."));
                    }

                    // Progress round if needed.
                    if (round.CountAlices() >= round.AnonymitySet)
                    {
                        await round.RemoveAlicesIfAnInputRefusedByMempoolAsync();

                        if (round.CountAlices() >= round.AnonymitySet)
                        {
                            await round.ExecuteNextPhaseAsync(CcjRoundPhase.ConnectionConfirmation);
                        }
                    }

                    var resp = new InputsResponse {
                        UniqueId = alice.UniqueId,
                        RoundId  = round.RoundId
                    };
                    return(Ok(resp));
                }
                catch (Exception ex)
                {
                    Logger.LogDebug <ChaumianCoinJoinController>(ex);
                    return(BadRequest(ex.Message));
                }
            }
        }
Exemplo n.º 11
0
        /// <summary>
        /// Sign a transaction
        /// </summary>
        /// <param name="request">The transaction to be signed</param>
        /// <returns>The signed transaction</returns>
        public async Task <SignRawTransactionResponse> SignRawTransactionWithKeyAsync(SignRawTransactionWithKeyRequest request)
        {
            Dictionary <string, object> values = new Dictionary <string, object>();

            values.Add("hexstring", request.Transaction.ToHex());
            JArray keys = new JArray();

            foreach (var k in request.PrivateKeys ?? new Key[0])
            {
                keys.Add(k.GetBitcoinSecret(Network).ToString());
            }
            values.Add("privkeys", keys);

            if (request.PreviousTransactions != null)
            {
                JArray prevs = new JArray();
                foreach (var prev in request.PreviousTransactions)
                {
                    JObject prevObj = new JObject();
                    prevObj.Add(new JProperty("txid", prev.OutPoint.Hash.ToString()));
                    prevObj.Add(new JProperty("vout", prev.OutPoint.N));
                    prevObj.Add(new JProperty("scriptPubKey", prev.ScriptPubKey.ToHex()));
                    if (prev.RedeemScript != null)
                    {
                        prevObj.Add(new JProperty("redeemScript", prev.RedeemScript.ToHex()));
                    }
                    prevObj.Add(new JProperty("amount", prev.Amount.ToDecimal(MoneyUnit.BTC).ToString()));
                    prevs.Add(prevObj);
                }
                values.Add("prevtxs", prevs);

                if (request.SigHash.HasValue)
                {
                    values.Add("sighashtype", SigHashToString(request.SigHash.Value));
                }
            }

            var result = await SendCommandWithNamedArgsAsync("signrawtransactionwithkey", values).ConfigureAwait(false);

            var response = new SignRawTransactionResponse();

            response.SignedTransaction = ParseTxHex(result.Result["hex"].Value <string>());
            response.Complete          = result.Result["complete"].Value <bool>();
            var errors    = result.Result["errors"] as JArray;
            var errorList = new List <SignRawTransactionResponse.ScriptError>();

            if (errors != null)
            {
                foreach (var error in errors)
                {
                    var scriptError = new SignRawTransactionResponse.ScriptError();
                    scriptError.OutPoint  = OutPoint.Parse($"{error["txid"].Value<string>()}-{(int)error["vout"].Value<long>()}");
                    scriptError.ScriptSig = Script.FromBytesUnsafe(Encoders.Hex.DecodeData(error["scriptSig"].Value <string>()));
                    scriptError.Sequence  = new Sequence((uint)error["sequence"].Value <long>());
                    scriptError.Error     = error["error"].Value <string>();
                    errorList.Add(scriptError);
                }
            }
            response.Errors = errorList.ToArray();
            return(response);
        }
Exemplo n.º 12
0
        public async Task NotingTestsAsync()
        {
            (_, IRPCClient rpc, Network network, Coordinator coordinator, _, _, _) = await Common.InitializeTestEnvironmentAsync(RegTestFixture, 1);

            Money   denomination                  = Money.Coins(1m);
            decimal coordinatorFeePercent         = 0.1m;
            int     anonymitySet                  = 2;
            int     connectionConfirmationTimeout = 1;
            bool    doesNoteBeforeBan             = true;
            CoordinatorRoundConfig roundConfig    = RegTestFixture.CreateRoundConfig(denomination, 140, 0.7, coordinatorFeePercent, anonymitySet, 240, connectionConfirmationTimeout, 1, 1, 1, 24, doesNoteBeforeBan, 11);

            coordinator.RoundConfig.UpdateOrDefault(roundConfig, toFile: true);
            coordinator.AbortAllRoundsInInputRegistration("");

            Uri baseUri = new(RegTestFixture.BackendEndPoint);

            List <(BitcoinAddress changeOutputAddress, BlindedOutputWithNonceIndex blindedData, InputProofModel[] inputsProofs)> registerRequests = new();
            AliceClient4?aliceClientBackup = null;

            Assert.True(coordinator.TryGetCurrentInputRegisterableRound(out CoordinatorRound? round));
            for (int i = 0; i < roundConfig.AnonymitySet; i++)
            {
                var activeOutputAddress = new Key().GetAddress(ScriptPubKeyType.Segwit, network);
                var changeOutputAddress = new Key().GetAddress(ScriptPubKeyType.Segwit, network);
                Key inputKey            = new();
                var inputAddress        = inputKey.PubKey.GetAddress(ScriptPubKeyType.Segwit, network);

                Requester requester = new();
                var       nonce     = round !.NonceProvider.GetNextNonce();

                BlindedOutputWithNonceIndex blinded = new(nonce.N, requester.BlindScript(round.MixingLevels.GetBaseLevel().SignerKey.PubKey, nonce.R, activeOutputAddress.ScriptPubKey));
                uint256 blindedOutputScriptsHash    = new(Hashes.SHA256(blinded.BlindedOutput.ToBytes()));

                uint256 txHash = await rpc.SendToAddressAsync(inputAddress, Money.Coins(2));

                await rpc.GenerateAsync(1);

                Transaction transaction = await rpc.GetRawTransactionAsync(txHash);

                Coin     coin  = transaction.Outputs.GetCoins(inputAddress.ScriptPubKey).Single();
                OutPoint input = coin.Outpoint;

                InputProofModel   inputProof   = new() { Input = input, Proof = inputKey.SignCompact(blindedOutputScriptsHash) };
                InputProofModel[] inputsProofs = new InputProofModel[] { inputProof };
                registerRequests.Add((changeOutputAddress, blinded, inputsProofs));
                aliceClientBackup = await AliceClientBase.CreateNewAsync(round.RoundId, new[] { activeOutputAddress }, new[] { round.MixingLevels.GetBaseLevel().SignerKey.PubKey }, new[] { requester }, network, changeOutputAddress, new[] { blinded }, inputsProofs, BackendHttpClient);
            }

            await WaitForTimeoutAsync();

            int bannedCount = coordinator.UtxoReferee.CountBanned(false);

            Assert.Equal(0, bannedCount);
            int notedCount = coordinator.UtxoReferee.CountBanned(true);

            Assert.Equal(anonymitySet, notedCount);
            Assert.True(coordinator.TryGetCurrentInputRegisterableRound(out round));

            foreach (var registerRequest in registerRequests)
            {
                await AliceClientBase.CreateNewAsync(round !.RoundId, aliceClientBackup.RegisteredAddresses, round.MixingLevels.GetAllLevels().Select(x => x.SignerKey.PubKey), aliceClientBackup.Requesters, network, registerRequest.changeOutputAddress, new[] { registerRequest.blindedData }, registerRequest.inputsProofs, BackendHttpClient);
            }

            await WaitForTimeoutAsync();

            bannedCount = coordinator.UtxoReferee.CountBanned(false);
            Assert.Equal(anonymitySet, bannedCount);
            notedCount = coordinator.UtxoReferee.CountBanned(true);
            Assert.Equal(anonymitySet, notedCount);
        }
    }
Exemplo n.º 13
0
 public SmartCoin GetByOutPoint(OutPoint outpoint) => Coins.FirstOrDefault(x => x.GetOutPoint() == outpoint);
Exemplo n.º 14
0
        public static Transaction Deserialize(byte[] rawTransaction)
        {
            if (rawTransaction == null)
                throw new ArgumentNullException(nameof(rawTransaction));

            int version;
            Input[] inputs;
            Output[] outputs;
            uint lockTime;
            uint expiry;

            var cursor = new ByteCursor(rawTransaction);

            try
            {
                version = cursor.ReadInt32();
                if (version != SupportedVersion)
                {
                    var reason = $"Unsupported transaction version `{version}`";
                    throw new EncodingException(typeof(Transaction), cursor, reason);
                }

                // Prefix deserialization
                var prefixInputCount = cursor.ReadCompact();
                if (prefixInputCount > TransactionRules.MaxInputs)
                {
                    var reason = $"Input count {prefixInputCount} exceeds maximum value {TransactionRules.MaxInputs}";
                    throw new EncodingException(typeof(Transaction), cursor, reason);
                }
                inputs = new Input[prefixInputCount];
                for (int i = 0; i < (int)prefixInputCount; i++)
                {
                    var previousHash = new Blake256Hash(cursor.ReadBytes(Blake256Hash.Length));
                    var previousIndex = cursor.ReadUInt32();
                    var previousTree = cursor.ReadByte();
                    var previousOutPoint = new OutPoint(previousHash, previousIndex, previousTree);
                    var sequence = cursor.ReadUInt32();
                    inputs[i] = Input.CreateFromPrefix(previousOutPoint, sequence);
                }
                var outputCount = cursor.ReadCompact();
                if (outputCount > TransactionRules.MaxOutputs)
                {
                    var reason = $"Output count {outputCount} exceeds maximum value {TransactionRules.MaxOutputs}";
                    throw new EncodingException(typeof(Transaction), cursor, reason);
                }
                outputs = new Output[outputCount];
                for (int i = 0; i < (int)outputCount; i++)
                {
                    var amount = (Amount)cursor.ReadInt64();
                    var outputVersion = cursor.ReadUInt16();
                    var pkScript = cursor.ReadVarBytes(TransactionRules.MaxPayload);
                    outputs[i] = new Output(amount, outputVersion, pkScript);
                }
                lockTime = cursor.ReadUInt32();
                expiry = cursor.ReadUInt32();

                // Witness deserialization
                var witnessInputCount = cursor.ReadCompact();
                if (witnessInputCount != prefixInputCount)
                {
                    var reason = $"Input counts in prefix and witness do not match ({prefixInputCount} != {witnessInputCount})";
                    throw new EncodingException(typeof(Transaction), cursor, reason);
                }
                for (int i = 0; i < (int)witnessInputCount; i++)
                {
                    var inputAmount = (Amount)cursor.ReadInt64();
                    var blockHeight = cursor.ReadUInt32();
                    var blockIndex = cursor.ReadUInt32();
                    var signatureScript = cursor.ReadVarBytes(TransactionRules.MaxPayload);
                    inputs[i] = Input.AddWitness(inputs[i], inputAmount, blockHeight, blockIndex, signatureScript);
                }
            }
            catch (Exception ex) when (!(ex is EncodingException))
            {
                throw new EncodingException(typeof(Transaction), cursor, ex);
            }

            return new Transaction(version, inputs, outputs, lockTime, expiry);
        }
Exemplo n.º 15
0
        public void Synchronize()
        {
            Task.Run(async() =>
            {
                try
                {
                    if (Interlocked.Read(ref _runner) >= 2)
                    {
                        return;
                    }

                    Interlocked.Increment(ref _runner);
                    while (Interlocked.Read(ref _runner) != 1)
                    {
                        await Task.Delay(100);
                    }

                    if (Interlocked.Read(ref _running) >= 2)
                    {
                        return;
                    }

                    try
                    {
                        Interlocked.Exchange(ref _running, 1);

                        var isImmature    = false;                      // The last 100 blocks are reorgable. (Assume it is mature at first.)
                        SyncInfo syncInfo = null;
                        while (IsRunning)
                        {
                            try
                            {
                                // If we didn't yet initialized syncInfo, do so.
                                if (syncInfo is null)
                                {
                                    syncInfo = await GetSyncInfoAsync();
                                }

                                Height heightToRequest = StartingHeight;
                                uint256 currentHash    = null;
                                using (await IndexLock.LockAsync())
                                {
                                    if (Index.Count != 0)
                                    {
                                        var lastIndex   = Index.Last();
                                        heightToRequest = lastIndex.BlockHeight + 1;
                                        currentHash     = lastIndex.BlockHash;
                                    }
                                }

                                // If not synchronized or already 5 min passed since last update, get the latest blockchain info.
                                if (!syncInfo.IsCoreSynchornized || (syncInfo.BlockchainInfoUpdated - DateTimeOffset.UtcNow) > TimeSpan.FromMinutes(5))
                                {
                                    syncInfo = await GetSyncInfoAsync();
                                }

                                if (syncInfo.BlockCount - heightToRequest <= 100)
                                {
                                    // Both Wasabi and our Core node is in sync. Start doing stuff through P2P from now on.
                                    if (syncInfo.IsCoreSynchornized && syncInfo.BlockCount == heightToRequest - 1)
                                    {
                                        syncInfo = await GetSyncInfoAsync();
                                        // Double it to make sure not to accidentally miss any notification.
                                        if (syncInfo.IsCoreSynchornized && syncInfo.BlockCount == heightToRequest - 1)
                                        {
                                            // Mark the process notstarted, so it can be started again and finally block can mark it is stopped.
                                            Interlocked.Exchange(ref _running, 0);
                                            return;
                                        }
                                    }

                                    // Mark the synchronizing process is working with immature blocks from now on.
                                    isImmature = true;
                                }

                                Block block = await RpcClient.GetBlockAsync(heightToRequest);

                                // Reorg check, except if we're requesting the starting height, because then the "currentHash" wouldn't exist.

                                if (heightToRequest != StartingHeight && currentHash != block.Header.HashPrevBlock)
                                {
                                    // Reorg can happen only when immature. (If it'd not be immature, that'd be a huge issue.)
                                    if (isImmature)
                                    {
                                        await ReorgOneAsync();
                                    }
                                    else
                                    {
                                        Logger.LogCritical <IndexBuilderService>("This is something serious! Over 100 block reorg is noticed! We cannot handle that!");
                                    }

                                    // Skip the current block.
                                    continue;
                                }

                                if (isImmature)
                                {
                                    PrepareBech32UtxoSetHistory();
                                }

                                var scripts = new HashSet <Script>();

                                foreach (var tx in block.Transactions)
                                {
                                    // If stop was requested return.
                                    // Because this tx iteration can take even minutes
                                    // It doesn't need to be accessed with a thread safe fasion with Interlocked through IsRunning, this may have some performance benefit
                                    if (_running != 1)
                                    {
                                        return;
                                    }

                                    for (int i = 0; i < tx.Outputs.Count; i++)
                                    {
                                        var output = tx.Outputs[i];
                                        if (!output.ScriptPubKey.IsPayToScriptHash && output.ScriptPubKey.IsWitness)
                                        {
                                            var outpoint = new OutPoint(tx.GetHash(), i);
                                            Bech32UtxoSet.Add(outpoint, output.ScriptPubKey);
                                            if (isImmature)
                                            {
                                                Bech32UtxoSetHistory.Last().StoreAction(ActionHistoryHelper.Operation.Add, outpoint, output.ScriptPubKey);
                                            }
                                            scripts.Add(output.ScriptPubKey);
                                        }
                                    }

                                    foreach (var input in tx.Inputs)
                                    {
                                        OutPoint prevOut = input.PrevOut;
                                        if (Bech32UtxoSet.TryGetValue(prevOut, out Script foundScript))
                                        {
                                            Bech32UtxoSet.Remove(prevOut);
                                            if (isImmature)
                                            {
                                                Bech32UtxoSetHistory.Last().StoreAction(ActionHistoryHelper.Operation.Remove, prevOut, foundScript);
                                            }
                                            scripts.Add(foundScript);
                                        }
                                    }
                                }

                                GolombRiceFilter filter = null;
                                if (scripts.Count != 0)
                                {
                                    filter = new GolombRiceFilterBuilder()
                                             .SetKey(block.GetHash())
                                             .SetP(20)
                                             .SetM(1 << 20)
                                             .AddEntries(scripts.Select(x => x.ToCompressedBytes()))
                                             .Build();
                                }

                                var filterModel = new FilterModel
                                {
                                    BlockHash   = block.GetHash(),
                                    BlockHeight = heightToRequest,
                                    Filter      = filter
                                };

                                await File.AppendAllLinesAsync(IndexFilePath, new[] { filterModel.ToHeightlessLine() });
                                using (await IndexLock.LockAsync())
                                {
                                    Index.Add(filterModel);
                                }
                                if (File.Exists(Bech32UtxoSetFilePath))
                                {
                                    File.Delete(Bech32UtxoSetFilePath);
                                }
                                await File.WriteAllLinesAsync(Bech32UtxoSetFilePath, Bech32UtxoSet
                                                              .Select(entry => entry.Key.Hash + ":" + entry.Key.N + ":" + ByteHelpers.ToHex(entry.Value.ToCompressedBytes())));

                                // If not close to the tip, just log debug.
                                // Use height.Value instead of simply height, because it cannot be negative height.
                                if (syncInfo.BlockCount - heightToRequest.Value <= 3 || heightToRequest % 100 == 0)
                                {
                                    Logger.LogInfo <IndexBuilderService>($"Created filter for block: {heightToRequest}.");
                                }
                                else
                                {
                                    Logger.LogDebug <IndexBuilderService>($"Created filter for block: {heightToRequest}.");
                                }
                            }
                            catch (Exception ex)
                            {
                                Logger.LogDebug <IndexBuilderService>(ex);
                            }
                        }
                    }
                    finally
                    {
                        Interlocked.CompareExchange(ref _running, 3, 2);                         // If IsStopping, make it stopped.
                        Interlocked.Decrement(ref _runner);
                    }
                }
                catch (Exception ex)
                {
                    Logger.LogError <IndexBuilderService>($"Synchronization attempt failed to start: {ex}");
                }
            });
        }
Exemplo n.º 16
0
 public ScriptCoin(OutPoint fromOutpoint, TxOut fromTxOut, Script redeem)
     : base(fromOutpoint, fromTxOut)
 {
     Redeem = redeem;
     AssertCoherent();
 }
Exemplo n.º 17
0
 public IssuanceCoin(OutPoint outpoint, TxOut txout)
 {
     Bearer = new Coin(outpoint, txout);
 }
Exemplo n.º 18
0
 public Coin(uint256 fromTxHash, uint fromOutputIndex, Money amount, Script scriptPubKey)
 {
     Outpoint = new OutPoint(fromTxHash, fromOutputIndex);
     TxOut    = new TxOut(amount, scriptPubKey);
 }
Exemplo n.º 19
0
 public Coin(IndexedTxOut txOut)
 {
     Outpoint = new OutPoint(txOut.Transaction.GetHash(), txOut.N);
     TxOut    = txOut.TxOut;
 }
Exemplo n.º 20
0
 public Coin(OutPoint fromOutpoint, TxOut fromTxOut)
 {
     Outpoint = fromOutpoint;
     TxOut    = fromTxOut;
 }
Exemplo n.º 21
0
        public async Task <BroadcastResponse> Send(string to, Money amount, Money minerfee, string message = "")
        {
            QBitNinjaClient client = new QBitNinjaClient(Network.TestNet);
            var             xxx    = client.GetBalance(privateKey.ScriptPubKey, true).Result;


            OutPoint outPointToSpend = null;
            var      trans           = xxx.Operations.First().ReceivedCoins;

            foreach (var coin in trans)
            {
                if (coin.TxOut.ScriptPubKey == privateKey.ScriptPubKey)
                {
                    outPointToSpend = coin.Outpoint;
                }
            }
            Money txInAmount = null;

            if (trans.Count == 1)
            {
                txInAmount = (Money)trans.First().Amount;
            }
            else
            {
                txInAmount = (Money)trans[(int)outPointToSpend.N].Amount;
            }
            var changeAmount           = txInAmount - amount - minerfee;
            var hallOfTheMakersAddress = BitcoinAddress.Create(to, network);

            TxOut hallOfTheMakersTxOut = new TxOut()
            {
                Value        = amount,
                ScriptPubKey = hallOfTheMakersAddress.ScriptPubKey
            };

            TxOut changeTxOut = new TxOut()
            {
                Value        = changeAmount,
                ScriptPubKey = privateKey.ScriptPubKey
            };
            var transaction = new Transaction();

            transaction.Inputs.Add(new TxIn()
            {
                PrevOut = outPointToSpend
            });
            transaction.Outputs.Add(hallOfTheMakersTxOut);
            transaction.Outputs.Add(changeTxOut);

            var bytes = Encoding.UTF8.GetBytes(message);

            transaction.Outputs.Add(new TxOut()
            {
                Value        = Money.Zero,
                ScriptPubKey = TxNullDataTemplate.Instance.GenerateScriptPubKey(bytes)
            });
            transaction.Inputs[0].ScriptSig = secret.ScriptPubKey;
            transaction.Sign(privateKey, trans.ToArray());


            BroadcastResponse broadcastResponse = await client.Broadcast(transaction);

            return(broadcastResponse);
        }
 public IEnumerable<WalletRule> GetMatchedRules(OutPoint outPoint)
 {
     if(outPoint.Hash == TransactionId)
         return GetMatchedRules((int)outPoint.N, MatchLocation.Output);
     else
     {
         var index = SpentOutpoints.IndexOf(outPoint);
         if(index == -1)
             return new WalletRule[0];
         return GetMatchedRules((int)SpentIndices[index], MatchLocation.Input);
     }
 }
Exemplo n.º 23
0
			public static string GetId(OutPoint outPoint)
			{
				return "o-" + Encoders.Hex.EncodeData(outPoint.ToBytes());
			}
Exemplo n.º 24
0
 public ActionItem(Operation action, OutPoint outPoint, Script script)
 {
     Action   = action;
     OutPoint = outPoint;
     Script   = script;
 }
Exemplo n.º 25
0
 public UnspentOutput AccessCoins(OutPoint outpoint)
 {
     return(this.unspents.TryGet(outpoint));
 }
Exemplo n.º 26
0
 public void StoreAction(Operation action, OutPoint outpoint, Script script)
 {
     StoreAction(new ActionItem(action, outpoint, script));
 }
Exemplo n.º 27
0
 public void SaveOffer(Script scriptPubKey, OutPoint outpoint)
 {
     _Repo.UpdateOrInsert("1", scriptPubKey.Hash.ToString(), outpoint, (o, n) => n);
 }
Exemplo n.º 28
0
        public async Task CanUsePayjoin()
        {
            using (var tester = ServerTester.Create())
            {
                await tester.StartAsync();

                ////var payJoinStateProvider = tester.PayTester.GetService<PayJoinStateProvider>();
                var btcPayNetwork = tester.NetworkProvider.GetNetwork <BTCPayNetwork>("BTC");
                var btcPayWallet  = tester.PayTester.GetService <BTCPayWalletProvider>().GetWallet(btcPayNetwork);
                var cashCow       = tester.ExplorerNode;
                cashCow.Generate(2); // get some money in case

                var senderUser = tester.NewAccount();
                senderUser.GrantAccess(true);
                senderUser.RegisterDerivationScheme("BTC", ScriptPubKeyType.Segwit, true);

                var invoice = senderUser.BitPay.CreateInvoice(
                    new Invoice()
                {
                    Price = 100, Currency = "USD", FullNotifications = true
                });
                //payjoin is not enabled by default.
                Assert.DoesNotContain($"{PayjoinClient.BIP21EndpointKey}", invoice.CryptoInfo.First().PaymentUrls.BIP21);
                cashCow.SendToAddress(BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network),
                                      Money.Coins(0.06m));

                var receiverUser = tester.NewAccount();
                receiverUser.GrantAccess(true);
                receiverUser.RegisterDerivationScheme("BTC", ScriptPubKeyType.Segwit, true);

                await receiverUser.EnablePayJoin();

                // payjoin is enabled, with a segwit wallet, and the keys are available in nbxplorer
                invoice = receiverUser.BitPay.CreateInvoice(
                    new Invoice()
                {
                    Price = 0.02m, Currency = "BTC", FullNotifications = true
                });
                cashCow.SendToAddress(BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network),
                                      Money.Coins(0.06m));
                var receiverWalletId = new WalletId(receiverUser.StoreId, "BTC");

                //give the cow some cash
                await cashCow.GenerateAsync(1);

                //let's get some more utxos first
                await receiverUser.ReceiveUTXO(Money.Coins(0.011m), btcPayNetwork);

                await receiverUser.ReceiveUTXO(Money.Coins(0.012m), btcPayNetwork);

                await receiverUser.ReceiveUTXO(Money.Coins(0.013m), btcPayNetwork);

                await receiverUser.ReceiveUTXO(Money.Coins(0.014m), btcPayNetwork);

                await senderUser.ReceiveUTXO(Money.Coins(0.021m), btcPayNetwork);

                await senderUser.ReceiveUTXO(Money.Coins(0.022m), btcPayNetwork);

                await senderUser.ReceiveUTXO(Money.Coins(0.023m), btcPayNetwork);

                await senderUser.ReceiveUTXO(Money.Coins(0.024m), btcPayNetwork);

                await senderUser.ReceiveUTXO(Money.Coins(0.025m), btcPayNetwork);

                await senderUser.ReceiveUTXO(Money.Coins(0.026m), btcPayNetwork);

                var senderChange = await senderUser.GetNewAddress(btcPayNetwork);

                //Let's start the harassment
                invoice = receiverUser.BitPay.CreateInvoice(
                    new Invoice()
                {
                    Price = 0.02m, Currency = "BTC", FullNotifications = true
                });

                var parsedBip21 = new BitcoinUrlBuilder(invoice.CryptoInfo.First().PaymentUrls.BIP21,
                                                        tester.ExplorerClient.Network.NBitcoinNetwork);

                var invoice2 = receiverUser.BitPay.CreateInvoice(
                    new Invoice()
                {
                    Price = 0.02m, Currency = "BTC", FullNotifications = true
                });
                var secondInvoiceParsedBip21 = new BitcoinUrlBuilder(invoice2.CryptoInfo.First().PaymentUrls.BIP21,
                                                                     tester.ExplorerClient.Network.NBitcoinNetwork);

                var senderStore = await tester.PayTester.StoreRepository.FindStore(senderUser.StoreId);

                var paymentMethodId          = new PaymentMethodId("BTC", PaymentTypes.BTCLike);
                var derivationSchemeSettings = senderStore.GetSupportedPaymentMethods(tester.NetworkProvider)
                                               .OfType <DerivationSchemeSettings>().SingleOrDefault(settings =>
                                                                                                    settings.PaymentId == paymentMethodId);

                ReceivedCoin[] senderCoins = null;
                await TestUtils.EventuallyAsync(async() =>
                {
                    senderCoins = await btcPayWallet.GetUnspentCoins(senderUser.DerivationScheme);
                    Assert.Contains(senderCoins, coin => coin.Value.GetValue(btcPayNetwork) == 0.026m);
                });

                var coin  = senderCoins.Single(coin => coin.Value.GetValue(btcPayNetwork) == 0.021m);
                var coin2 = senderCoins.Single(coin => coin.Value.GetValue(btcPayNetwork) == 0.022m);
                var coin3 = senderCoins.Single(coin => coin.Value.GetValue(btcPayNetwork) == 0.023m);
                var coin4 = senderCoins.Single(coin => coin.Value.GetValue(btcPayNetwork) == 0.024m);
                var coin5 = senderCoins.Single(coin => coin.Value.GetValue(btcPayNetwork) == 0.025m);
                var coin6 = senderCoins.Single(coin => coin.Value.GetValue(btcPayNetwork) == 0.026m);

                var signingKeySettings = derivationSchemeSettings.GetSigningAccountKeySettings();
                signingKeySettings.RootFingerprint =
                    senderUser.GenerateWalletResponseV.MasterHDKey.GetPublicKey().GetHDFingerPrint();

                var extKey =
                    senderUser.GenerateWalletResponseV.MasterHDKey.Derive(signingKeySettings.GetRootedKeyPath()
                                                                          .KeyPath);


                var n             = tester.ExplorerClient.Network.NBitcoinNetwork;
                var Invoice1Coin1 = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder()
                                    .SetChange(senderChange)
                                    .Send(parsedBip21.Address, parsedBip21.Amount)
                                    .AddCoins(coin.Coin)
                                    .AddKeys(extKey.Derive(coin.KeyPath))
                                    .SendEstimatedFees(new FeeRate(100m))
                                    .BuildTransaction(true);

                var Invoice1Coin2 = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder()
                                    .SetChange(senderChange)
                                    .Send(parsedBip21.Address, parsedBip21.Amount)
                                    .AddCoins(coin2.Coin)
                                    .AddKeys(extKey.Derive(coin2.KeyPath))
                                    .SendEstimatedFees(new FeeRate(100m))
                                    .BuildTransaction(true);

                var Invoice2Coin1 = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder()
                                    .SetChange(senderChange)
                                    .Send(secondInvoiceParsedBip21.Address, secondInvoiceParsedBip21.Amount)
                                    .AddCoins(coin.Coin)
                                    .AddKeys(extKey.Derive(coin.KeyPath))
                                    .SendEstimatedFees(new FeeRate(100m))
                                    .BuildTransaction(true);

                var Invoice2Coin2 = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder()
                                    .SetChange(senderChange)
                                    .Send(secondInvoiceParsedBip21.Address, secondInvoiceParsedBip21.Amount)
                                    .AddCoins(coin2.Coin)
                                    .AddKeys(extKey.Derive(coin2.KeyPath))
                                    .SendEstimatedFees(new FeeRate(100m))
                                    .BuildTransaction(true);

                //Attempt 1: Send a signed tx to invoice 1 that does not pay the invoice at all
                //Result: reject
                // Assert.False((await tester.PayTester.HttpClient.PostAsync(endpoint,
                //     new StringContent(Invoice2Coin1.ToHex(), Encoding.UTF8, "text/plain"))).IsSuccessStatusCode);

                //Attempt 2: Create two transactions using different inputs and send them to the same invoice.
                //Result: Second Tx should be rejected.
                var Invoice1Coin1ResponseTx = await senderUser.SubmitPayjoin(invoice, Invoice1Coin1, btcPayNetwork);

                await senderUser.SubmitPayjoin(invoice, Invoice1Coin1, btcPayNetwork, "already-paid");

                var contributedInputsInvoice1Coin1ResponseTx =
                    Invoice1Coin1ResponseTx.Inputs.Where(txin => coin.OutPoint != txin.PrevOut);
                Assert.Single(contributedInputsInvoice1Coin1ResponseTx);

                //Attempt 3: Send the same inputs from invoice 1 to invoice 2 while invoice 1 tx has not been broadcasted
                //Result: Reject Tx1 but accept tx 2 as its inputs were never accepted by invoice 1
                await senderUser.SubmitPayjoin(invoice2, Invoice2Coin1, btcPayNetwork, "inputs-already-used");

                var Invoice2Coin2ResponseTx = await senderUser.SubmitPayjoin(invoice2, Invoice2Coin2, btcPayNetwork);

                var contributedInputsInvoice2Coin2ResponseTx =
                    Invoice2Coin2ResponseTx.Inputs.Where(txin => coin2.OutPoint != txin.PrevOut);
                Assert.Single(contributedInputsInvoice2Coin2ResponseTx);

                //Attempt 4: Make tx that pays invoice 3 and 4 and submit to both
                //Result: reject on 4: the protocol should not worry about this complexity

                var invoice3 = receiverUser.BitPay.CreateInvoice(
                    new Invoice()
                {
                    Price = 0.01m, Currency = "BTC", FullNotifications = true
                });
                var invoice3ParsedBip21 = new BitcoinUrlBuilder(invoice3.CryptoInfo.First().PaymentUrls.BIP21,
                                                                tester.ExplorerClient.Network.NBitcoinNetwork);


                var invoice4 = receiverUser.BitPay.CreateInvoice(
                    new Invoice()
                {
                    Price = 0.01m, Currency = "BTC", FullNotifications = true
                });
                var invoice4ParsedBip21 = new BitcoinUrlBuilder(invoice4.CryptoInfo.First().PaymentUrls.BIP21,
                                                                tester.ExplorerClient.Network.NBitcoinNetwork);


                var Invoice3AndInvoice4Coin3 = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder()
                                               .SetChange(senderChange)
                                               .Send(invoice3ParsedBip21.Address, invoice3ParsedBip21.Amount)
                                               .Send(invoice4ParsedBip21.Address, invoice4ParsedBip21.Amount)
                                               .AddCoins(coin3.Coin)
                                               .AddKeys(extKey.Derive(coin3.KeyPath))
                                               .SendEstimatedFees(new FeeRate(100m))
                                               .BuildTransaction(true);

                await senderUser.SubmitPayjoin(invoice3, Invoice3AndInvoice4Coin3, btcPayNetwork);

                await senderUser.SubmitPayjoin(invoice4, Invoice3AndInvoice4Coin3, btcPayNetwork, "already-paid");

                //Attempt 5: Make tx that pays invoice 5 with 2 outputs
                //Result: proposed tx consolidates the outputs

                var invoice5 = receiverUser.BitPay.CreateInvoice(
                    new Invoice()
                {
                    Price = 0.01m, Currency = "BTC", FullNotifications = true
                });
                var invoice5ParsedBip21 = new BitcoinUrlBuilder(invoice5.CryptoInfo.First().PaymentUrls.BIP21,
                                                                tester.ExplorerClient.Network.NBitcoinNetwork);

                var Invoice5Coin4TxBuilder = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder()
                                             .SetChange(senderChange)
                                             .Send(invoice5ParsedBip21.Address, invoice5ParsedBip21.Amount / 2)
                                             .Send(invoice5ParsedBip21.Address, invoice5ParsedBip21.Amount / 2)
                                             .AddCoins(coin4.Coin)
                                             .AddKeys(extKey.Derive(coin4.KeyPath))
                                             .SendEstimatedFees(new FeeRate(100m));

                var Invoice5Coin4           = Invoice5Coin4TxBuilder.BuildTransaction(true);
                var Invoice5Coin4ResponseTx = await senderUser.SubmitPayjoin(invoice5, Invoice5Coin4, btcPayNetwork);

                Assert.Single(Invoice5Coin4ResponseTx.Outputs.To(invoice5ParsedBip21.Address));

                //Attempt 10: send tx with rbf, broadcast payjoin tx, bump the rbf payjoin , attempt to submit tx again
                //Result: same tx gets sent back

                //give the receiver some more utxos
                Assert.NotNull(await tester.ExplorerNode.SendToAddressAsync(
                                   (await btcPayWallet.ReserveAddressAsync(receiverUser.DerivationScheme)).Address,
                                   new Money(0.1m, MoneyUnit.BTC)));

                var invoice6 = receiverUser.BitPay.CreateInvoice(
                    new Invoice()
                {
                    Price = 0.01m, Currency = "BTC", FullNotifications = true
                });
                var invoice6ParsedBip21 = new BitcoinUrlBuilder(invoice6.CryptoInfo.First().PaymentUrls.BIP21,
                                                                tester.ExplorerClient.Network.NBitcoinNetwork);

                var invoice6Coin5TxBuilder = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder()
                                             .SetChange(senderChange)
                                             .Send(invoice6ParsedBip21.Address, invoice6ParsedBip21.Amount)
                                             .AddCoins(coin5.Coin)
                                             .AddKeys(extKey.Derive(coin5.KeyPath))
                                             .SendEstimatedFees(new FeeRate(100m))
                                             .SetLockTime(0);

                var invoice6Coin5 = invoice6Coin5TxBuilder
                                    .BuildTransaction(true);

                var Invoice6Coin5Response1Tx = await senderUser.SubmitPayjoin(invoice6, invoice6Coin5, btcPayNetwork);

                var Invoice6Coin5Response1TxSigned = invoice6Coin5TxBuilder.SignTransaction(Invoice6Coin5Response1Tx);
                //broadcast the first payjoin
                await tester.ExplorerClient.BroadcastAsync(Invoice6Coin5Response1TxSigned);

                // invoice6Coin5TxBuilder = invoice6Coin5TxBuilder.SendEstimatedFees(new FeeRate(100m));
                // var invoice6Coin5Bumpedfee = invoice6Coin5TxBuilder
                //     .BuildTransaction(true);
                //
                // var Invoice6Coin5Response3 = await tester.PayTester.HttpClient.PostAsync(invoice6Endpoint,
                //     new StringContent(invoice6Coin5Bumpedfee.ToHex(), Encoding.UTF8, "text/plain"));
                // Assert.True(Invoice6Coin5Response3.IsSuccessStatusCode);
                // var Invoice6Coin5Response3Tx =
                //     Transaction.Parse(await Invoice6Coin5Response3.Content.ReadAsStringAsync(), n);
                // Assert.True(invoice6Coin5Bumpedfee.Inputs.All(txin =>
                //     Invoice6Coin5Response3Tx.Inputs.Any(txin2 => txin2.PrevOut == txin.PrevOut)));

                //Attempt 11:
                //send tx with rbt, broadcast payjoin,
                //create tx spending the original tx inputs with rbf to self,
                //Result: the exposed utxos are priorized in the next p2ep

                //give the receiver some more utxos
                Assert.NotNull(await tester.ExplorerNode.SendToAddressAsync(
                                   (await btcPayWallet.ReserveAddressAsync(receiverUser.DerivationScheme)).Address,
                                   new Money(0.1m, MoneyUnit.BTC)));

                var invoice7 = receiverUser.BitPay.CreateInvoice(
                    new Invoice()
                {
                    Price = 0.01m, Currency = "BTC", FullNotifications = true
                });
                var invoice7ParsedBip21 = new BitcoinUrlBuilder(invoice7.CryptoInfo.First().PaymentUrls.BIP21,
                                                                tester.ExplorerClient.Network.NBitcoinNetwork);

                var invoice7Coin6TxBuilder = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder()
                                             .SetChange(senderChange)
                                             .Send(invoice7ParsedBip21.Address, invoice7ParsedBip21.Amount)
                                             .AddCoins(coin6.Coin)
                                             .AddKeys(extKey.Derive(coin6.KeyPath))
                                             .SendEstimatedFees(new FeeRate(100m))
                                             .SetLockTime(0);

                var invoice7Coin6Tx = invoice7Coin6TxBuilder
                                      .BuildTransaction(true);

                var invoice7Coin6Response1Tx = await senderUser.SubmitPayjoin(invoice7, invoice7Coin6Tx, btcPayNetwork);

                var Invoice7Coin6Response1TxSigned = invoice7Coin6TxBuilder.SignTransaction(invoice7Coin6Response1Tx);
                var contributedInputsInvoice7Coin6Response1TxSigned =
                    Invoice7Coin6Response1TxSigned.Inputs.Single(txin => coin6.OutPoint != txin.PrevOut);


                ////var receiverWalletPayJoinState = payJoinStateProvider.Get(receiverWalletId);
                ////Assert.Contains(receiverWalletPayJoinState.GetRecords(), item => item.InvoiceId == invoice7.Id);
                //broadcast the payjoin
                var res = (await tester.ExplorerClient.BroadcastAsync(Invoice7Coin6Response1TxSigned));
                Assert.True(res.Success);

                // Paid with coinjoin
                await TestUtils.EventuallyAsync(async() =>
                {
                    var invoiceEntity = await tester.PayTester.GetService <InvoiceRepository>().GetInvoice(invoice7.Id);
                    Assert.Equal(InvoiceStatus.Paid, invoiceEntity.Status);
                    Assert.Contains(invoiceEntity.GetPayments(), p => p.Accounted &&
                                    ((BitcoinLikePaymentData)p.GetCryptoPaymentData()).PayjoinInformation is null);
                });

                ////Assert.Contains(receiverWalletPayJoinState.GetRecords(), item => item.InvoiceId == invoice7.Id && item.TxSeen);

                var invoice7Coin6Tx2 = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder()
                                       .SetChange(senderChange)
                                       .AddCoins(coin6.Coin)
                                       .SendAll(senderChange)
                                       .SubtractFees()
                                       .AddKeys(extKey.Derive(coin6.KeyPath))
                                       .SendEstimatedFees(new FeeRate(200m))
                                       .SetLockTime(0)
                                       .BuildTransaction(true);

                //broadcast the "rbf cancel" tx
                res = (await tester.ExplorerClient.BroadcastAsync(invoice7Coin6Tx2));
                Assert.True(res.Success);

                // Make a block, this should put back the invoice to new
                var blockhash = tester.ExplorerNode.Generate(1)[0];
                Assert.NotNull(await tester.ExplorerNode.GetRawTransactionAsync(invoice7Coin6Tx2.GetHash(), blockhash));
                Assert.Null(await tester.ExplorerNode.GetRawTransactionAsync(Invoice7Coin6Response1TxSigned.GetHash(), blockhash, false));
                // Now we should return to New
                OutPoint ourOutpoint = null;
                await TestUtils.EventuallyAsync(async() =>
                {
                    var invoiceEntity = await tester.PayTester.GetService <InvoiceRepository>().GetInvoice(invoice7.Id);
                    Assert.Equal(InvoiceStatus.New, invoiceEntity.Status);
                    Assert.True(invoiceEntity.GetPayments().All(p => !p.Accounted));
                    ourOutpoint = invoiceEntity.GetAllBitcoinPaymentData().First().PayjoinInformation.ContributedOutPoints[0];
                });

                var payjoinRepository = tester.PayTester.GetService <PayJoinRepository>();
                // The outpoint should now be available for next pj selection
                Assert.False(await payjoinRepository.TryUnlock(ourOutpoint));
            }
        }
Exemplo n.º 29
0
        public async Task BanningTestsAsync()
        {
            (_, IRPCClient rpc, Network network, Coordinator coordinator, _, _, _) = await Common.InitializeTestEnvironmentAsync(RegTestFixture, 1);

            Money   denomination                  = Money.Coins(0.1m);
            decimal coordinatorFeePercent         = 0.1m;
            int     anonymitySet                  = 3;
            int     connectionConfirmationTimeout = 120;
            var     roundConfig = RegTestFixture.CreateRoundConfig(denomination, 140, 0.7, coordinatorFeePercent, anonymitySet, 240, connectionConfirmationTimeout, 1, 1, 1, 24, true, 11);

            coordinator.RoundConfig.UpdateOrDefault(roundConfig, toFile: true);
            coordinator.AbortAllRoundsInInputRegistration("");

            await rpc.GenerateAsync(3);             // So to make sure we have enough money.

            Uri baseUri        = new(RegTestFixture.BackendEndPoint);
            var fundingTxCount = 0;
            List <(Requester requester, BlindedOutputWithNonceIndex blinded, BitcoinAddress activeOutputAddress, BitcoinAddress changeOutputAddress, IEnumerable <InputProofModel> inputProofModels, List <(Key key, BitcoinAddress address, uint256 txHash, Transaction tx, OutPoint input)> userInputData)> inputRegistrationUsers = new();
            CoordinatorRound?round = null;

            for (int i = 0; i < roundConfig.AnonymitySet; i++)
            {
                List <(Key key, BitcoinAddress inputAddress, uint256 txHash, Transaction tx, OutPoint input)> userInputData = new();
                var activeOutputAddress = new Key().GetAddress(ScriptPubKeyType.Segwit, network);
                var changeOutputAddress = new Key().GetAddress(ScriptPubKeyType.Segwit, network);
                Assert.True(coordinator.TryGetCurrentInputRegisterableRound(out round));
                Requester requester = new();
                var       nonce     = round !.NonceProvider.GetNextNonce();

                BlindedOutputWithNonceIndex blinded = new(nonce.N, requester.BlindScript(round.MixingLevels.GetBaseLevel().SignerKey.PubKey, nonce.R, activeOutputAddress.ScriptPubKey));
                uint256 blindedOutputScriptsHash    = new(Hashes.SHA256(blinded.BlindedOutput.ToBytes()));

                List <InputProofModel> inputProofModels = new();
                int numberOfInputs    = CryptoHelpers.RandomInt(1, 6);
                var receiveSatoshiSum = 0;
                for (int j = 0; j < numberOfInputs; j++)
                {
                    Key key            = new();
                    var receiveSatoshi = CryptoHelpers.RandomInt(1000, 100000000);
                    receiveSatoshiSum += receiveSatoshi;
                    if (j == numberOfInputs - 1)
                    {
                        receiveSatoshi = 100000000;
                    }
                    var     inputAddress = key.PubKey.GetAddress(ScriptPubKeyType.Segwit, network);
                    uint256 txHash       = await rpc.SendToAddressAsync(inputAddress, Money.Satoshis(receiveSatoshi));

                    fundingTxCount++;
                    Assert.NotNull(txHash);
                    Transaction transaction = await rpc.GetRawTransactionAsync(txHash);

                    var coin = transaction.Outputs.GetCoins(inputAddress.ScriptPubKey).Single();

                    OutPoint input      = coin.Outpoint;
                    var      inputProof = new InputProofModel {
                        Input = input, Proof = key.SignCompact(blindedOutputScriptsHash)
                    };
                    inputProofModels.Add(inputProof);

                    GetTxOutResponse?getTxOutResponse = await rpc.GetTxOutAsync(input.Hash, (int)input.N, includeMempool : true);

                    // Check if inputs are unspent.
                    Assert.NotNull(getTxOutResponse);

                    userInputData.Add((key, inputAddress, txHash, transaction, input));
                }

                inputRegistrationUsers.Add((requester, blinded, activeOutputAddress, changeOutputAddress, inputProofModels, userInputData));
            }

            var mempool = await rpc.GetRawMempoolAsync();

            Assert.Equal(inputRegistrationUsers.SelectMany(x => x.userInputData).Count(), mempool.Length);

            while ((await rpc.GetRawMempoolAsync()).Length != 0)
            {
                await rpc.GenerateAsync(1);
            }

            List <Task <AliceClient4> > aliceClients = new();

            foreach (var user in inputRegistrationUsers)
            {
                aliceClients.Add(AliceClientBase.CreateNewAsync(round.RoundId, new[] { user.activeOutputAddress }, new[] { round.MixingLevels.GetBaseLevel().SignerKey.PubKey }, new[] { user.requester }, network, user.changeOutputAddress, new[] { user.blinded }, user.inputProofModels, BackendHttpClient));
            }

            long roundId = 0;
            List <(Requester requester, BlindedOutputWithNonceIndex blinded, BitcoinAddress activeOutputAddress, BitcoinAddress changeOutputAddress, IEnumerable <InputProofModel> inputProofModels, List <(Key key, BitcoinAddress address, uint256 txHash, Transaction tx, OutPoint input)> userInputData, AliceClient4 aliceClient, UnblindedSignature unblindedSignature)> users = new();

            for (int i = 0; i < inputRegistrationUsers.Count; i++)
            {
                var user    = inputRegistrationUsers[i];
                var request = aliceClients[i];

                var aliceClient = await request;

                if (roundId == 0)
                {
                    roundId = aliceClient.RoundId;
                }
                else
                {
                    Assert.Equal(roundId, aliceClient.RoundId);
                }

                // Because it's a value tuple.
                users.Add((user.requester, user.blinded, user.activeOutputAddress, user.changeOutputAddress, user.inputProofModels, user.userInputData, aliceClient, null));
            }

            Assert.Equal(users.Count, roundConfig.AnonymitySet);

            List <Task <(RoundPhase currentPhase, IEnumerable <ActiveOutput>)> > confirmationRequests = new();

            foreach (var user in users)
            {
                confirmationRequests.Add(user.aliceClient.PostConfirmationAsync());
            }

            RoundPhase roundPhase = RoundPhase.InputRegistration;
            int        k          = 0;

            foreach (var request in confirmationRequests)
            {
                var resp = await request;
                if (roundPhase == RoundPhase.InputRegistration)
                {
                    roundPhase = resp.currentPhase;
                }
                else
                {
                    Assert.Equal(roundPhase, resp.currentPhase);
                }

                var user = users.ElementAt(k);
                user.unblindedSignature = resp.Item2.First().Signature;
            }

            {
                var times = 0;
                while (!(await SatoshiClient.GetAllRoundStatesAsync()).All(x => x.Phase == RoundPhase.InputRegistration))
                {
                    await Task.Delay(100);

                    if (times > 50)                     // 5 sec, 3 should be enough
                    {
                        throw new TimeoutException("Not all rounds were in InputRegistration.");
                    }
                    times++;
                }
            }

            int bannedCount = coordinator.UtxoReferee.CountBanned(false);

            Assert.Equal(0, bannedCount);

            aliceClients.Clear();
            Assert.True(coordinator.TryGetCurrentInputRegisterableRound(out round));
            foreach (var user in inputRegistrationUsers)
            {
                aliceClients.Add(AliceClientBase.CreateNewAsync(round !.RoundId, new[] { user.activeOutputAddress }, new[] { round.MixingLevels.GetBaseLevel().SignerKey.PubKey }, new[] { user.requester }, network, user.changeOutputAddress, new[] { user.blinded }, user.inputProofModels, BackendHttpClient));
            }

            roundId = 0;
            users   = new List <(Requester requester, BlindedOutputWithNonceIndex blinded, BitcoinAddress activeOutputAddress, BitcoinAddress changeOutputAddress, IEnumerable <InputProofModel> inputProofModels, List <(Key key, BitcoinAddress address, uint256 txHash, Transaction tx, OutPoint input)> userInputData, AliceClient4 aliceClient, UnblindedSignature unblindedSignature)>();
            for (int i = 0; i < inputRegistrationUsers.Count; i++)
            {
                var user    = inputRegistrationUsers[i];
                var request = aliceClients[i];

                var aliceClient = await request;
                if (roundId == 0)
                {
                    roundId = aliceClient.RoundId;
                }
                else
                {
                    Assert.Equal(roundId, aliceClient.RoundId);
                }

                // Because it's a value tuple.
                users.Add((user.requester, user.blinded, user.activeOutputAddress, user.changeOutputAddress, user.inputProofModels, user.userInputData, aliceClient, null));
            }

            Assert.Equal(users.Count, roundConfig.AnonymitySet);

            confirmationRequests = new List <Task <(RoundPhase currentPhase, IEnumerable <ActiveOutput>)> >();

            foreach (var user in users)
            {
                confirmationRequests.Add(user.aliceClient.PostConfirmationAsync());
            }

            {
                var times = 0;
                while (!(await SatoshiClient.GetAllRoundStatesAsync()).All(x => x.Phase == RoundPhase.InputRegistration))
                {
                    await Task.Delay(100);

                    if (times > 50)                     // 5 sec, 3 should be enough
                    {
                        throw new TimeoutException("Not all rounds were in InputRegistration.");
                    }
                    times++;
                }
            }

            bannedCount = coordinator.UtxoReferee.CountBanned(false);
            Assert.True(bannedCount >= roundConfig.AnonymitySet);

            foreach (var aliceClient in aliceClients)
            {
                aliceClient?.Dispose();
            }
        }
Exemplo n.º 30
0
 public SmartCoin GetByOutPoint(OutPoint outpoint)
 {
     return(AsCoinsView().GetByOutPoint(outpoint));
 }
        public void CanSerializeInJson()
        {
            Key k = new Key();

            CanSerializeInJsonCore(DateTimeOffset.UtcNow);
            CanSerializeInJsonCore(new byte[] { 1, 2, 3 });
            CanSerializeInJsonCore(k);
            CanSerializeInJsonCore(Money.Coins(5.0m));
            CanSerializeInJsonCore(k.PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main));
            CanSerializeInJsonCore(new KeyPath("1/2"));
            CanSerializeInJsonCore(RootedKeyPath.Parse("7b09d780/0'/0'/2'"));
            CanSerializeInJsonCore(Network.Main);
            CanSerializeInJsonCore(new uint256(RandomUtils.GetBytes(32)));
            CanSerializeInJsonCore(new uint160(RandomUtils.GetBytes(20)));
            CanSerializeInJsonCore(new AssetId(k.PubKey));
            CanSerializeInJsonCore(k.PubKey.ScriptPubKey);
            CanSerializeInJsonCore(new Key().PubKey.WitHash.GetAddress(Network.Main));
            CanSerializeInJsonCore(new Key().PubKey.WitHash.ScriptPubKey.GetWitScriptAddress(Network.Main));
            var sig = k.Sign(new uint256(RandomUtils.GetBytes(32)));

            CanSerializeInJsonCore(sig);
            CanSerializeInJsonCore(new TransactionSignature(sig, SigHash.All));
            CanSerializeInJsonCore(k.PubKey.Hash);
            CanSerializeInJsonCore(k.PubKey.ScriptPubKey.Hash);
            CanSerializeInJsonCore(k.PubKey.WitHash);
            CanSerializeInJsonCore(k);
            CanSerializeInJsonCore(k.PubKey);
            CanSerializeInJsonCore(new WitScript(new Script(Op.GetPushOp(sig.ToDER()), Op.GetPushOp(sig.ToDER()))));
            CanSerializeInJsonCore(new LockTime(1));
            CanSerializeInJsonCore(new LockTime(130), out var str);
            Assert.Equal("130", str);
            CanSerializeInJsonCore(new LockTime(DateTime.UtcNow));
            CanSerializeInJsonCore(new FeeRate(Money.Satoshis(1), 1000));
            CanSerializeInJsonCore(new FeeRate(Money.Satoshis(1000), 1000));
            CanSerializeInJsonCore(new FeeRate(0.5m));
            CanSerializeInJsonCore(new HDFingerprint(0x0a), out str);
            Assert.Equal("\"0a000000\"", str);
            var print  = Serializer.ToObject <HDFingerprint>("\"0a000000\"");
            var print2 = Serializer.ToObject <HDFingerprint>("10");

            Assert.Equal(print, print2);

            var printn  = Serializer.ToObject <HDFingerprint?>("\"0a000000\"");
            var print2n = Serializer.ToObject <HDFingerprint?>("10");

            Assert.Equal(printn, print2n);
            Assert.Null(Serializer.ToObject <HDFingerprint?>(""));

            var psbt     = PSBT.Parse("70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000002202029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887220203089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000", Network.Main);
            var psbtJson = Serializer.ToString(psbt, Network.Main);
            var psbt2    = Serializer.ToObject <PSBT>(psbtJson, Network.Main);

            Assert.Equal(psbt, psbt2);


            var expectedOutpoint = OutPoint.Parse("44f69ca74088d6d88e30156da85aae54543a87f67cdfdabbe9b53a92d6d7027c01000000");
            var actualOutpoint   = Serializer.ToObject <OutPoint>("\"44f69ca74088d6d88e30156da85aae54543a87f67cdfdabbe9b53a92d6d7027c01000000\"", Network.Main);

            Assert.Equal(expectedOutpoint, actualOutpoint);
            actualOutpoint = Serializer.ToObject <OutPoint>("\"7c02d7d6923ab5e9bbdadf7cf6873a5454ae5aa86d15308ed8d68840a79cf644-1\"", Network.Main);
            Assert.Equal(expectedOutpoint, actualOutpoint);

            CanSerializeInJsonCore(expectedOutpoint, out str);
            Assert.Equal("\"44f69ca74088d6d88e30156da85aae54543a87f67cdfdabbe9b53a92d6d7027c01000000\"", str);
        }
Exemplo n.º 32
0
 public TxoRef(OutPoint outPoint)
 {
     Guard.NotNull(nameof(outPoint), outPoint);
     TransactionId = outPoint.Hash;
     Index         = outPoint.N;
 }
Exemplo n.º 33
0
        /// <inheritdoc/>
        public void CheckKernel(PosRuleContext context, ChainedHeader prevChainedHeader, uint headerBits, long transactionTime, OutPoint prevout)
        {
            FetchCoinsResponse coins = this.coinView.FetchCoinsAsync(new[] { prevout.Hash }).GetAwaiter().GetResult();

            if ((coins == null) || (coins.UnspentOutputs.Length != 1))
            {
                this.logger.LogTrace("(-)[READ_PREV_TX_FAILED]");
                ConsensusErrors.ReadTxPrevFailed.Throw();
            }

            ChainedHeader prevBlock = this.chain.GetBlock(coins.BlockHash);

            if (prevBlock == null)
            {
                this.logger.LogTrace("(-)[REORG]");
                ConsensusErrors.ReadTxPrevFailed.Throw();
            }

            UnspentOutputs prevUtxo = coins.UnspentOutputs[0];

            if (this.IsConfirmedInNPrevBlocks(prevUtxo, prevChainedHeader, ((PosConsensusOptions)this.network.Consensus.Options).GetStakeMinConfirmations(prevChainedHeader.Height + 1, this.network) - 1))
            {
                this.logger.LogTrace("(-)[LOW_COIN_AGE]");
                ConsensusErrors.InvalidStakeDepth.Throw();
            }

            BlockStake prevBlockStake = this.stakeChain.Get(prevChainedHeader.HashBlock);

            if (prevBlockStake == null)
            {
                this.logger.LogTrace("(-)[BAD_STAKE_BLOCK]");
                ConsensusErrors.BadStakeBlock.Throw();
            }

            this.CheckStakeKernelHash(context, headerBits, prevBlockStake.StakeModifierV2, prevUtxo, prevout, (uint)transactionTime);
        }
Exemplo n.º 34
0
 public TransactionOutPointWithValue(OutPoint outpoint, Money value, Script scriptPubKey)
 {
     this.outpoint     = outpoint;
     this.value        = value;
     this.scriptPubKey = scriptPubKey;
 }
Exemplo n.º 35
0
        public bool Remove(OutPoint outPoint)
        {
            var ret = this.sqliteConnection.ExecuteScalar <int>("DELETE FROM TransactionData WHERE OutPoint = @outPoint", new { outPoint });

            return(ret > 0);
        }
Exemplo n.º 36
0
 public static Input CreateFromPrefix(OutPoint previousOutPoint, uint sequence) =>
     new Input(previousOutPoint, sequence, 0, 0, 0, new byte[0]);
 public void UpdateToScriptCoins()
 {
     foreach(var match in MatchedRules)
     {
         var scriptRule = match.Rule as ScriptRule;
         if(scriptRule != null && scriptRule.RedeemScript != null)
         {
             if(match.MatchType == MatchLocation.Output)
             {
                 var outpoint = new OutPoint(TransactionId, match.Index);
                 var coin = ReceivedCoins[outpoint] as Coin;
                 if(coin != null)
                 {
                     ReceivedCoins[outpoint] = coin.ToScriptCoin(scriptRule.RedeemScript);
                 }
             }
             else
             {
                 if(SpentCoins == null)
                     continue;
                 var n = this.SpentIndices.IndexOf(match.Index);
                 var coin = SpentCoins[n] as Coin;
                 if(coin != null)
                 {
                     this.SpentCoins[n] = coin.ToScriptCoin(scriptRule.RedeemScript);
                 }
             }
         }
     }
 }
Exemplo n.º 38
0
		private async Task LockUnspentCoreAsync(bool unlock, OutPoint[] outpoints)
		{
			if (outpoints == null || outpoints.Length == 0)
				return;
			var parameters = new List<object>();
			parameters.Add(unlock);
			var array = new JArray();
			parameters.Add(array);
			foreach (var outp in outpoints)
			{
				var obj = new JObject();
				obj["txid"] = outp.Hash.ToString();
				obj["vout"] = outp.N;
				array.Add(obj);
			}
			await SendCommandAsync("lockunspent", parameters.ToArray()).ConfigureAwait(false);
		}
Exemplo n.º 39
0
			internal static Coin FromJsonCoin(JToken obj)
			{
				OutPoint outpoint = new OutPoint();
				outpoint.FromBytes(Encoders.Hex.DecodeData((string)obj["Outpoint"]));
				TxOut txout = new TxOut();
				txout.FromBytes(Encoders.Hex.DecodeData((string)obj["TxOut"]));
				return new Coin(outpoint, txout);
			}
Exemplo n.º 40
0
        public static Transaction Deserialize(byte[] rawTransaction)
        {
            if (rawTransaction == null)
                throw new ArgumentNullException(nameof(rawTransaction));

            int version;
            Input[] inputs;
            Output[] outputs;
            uint lockTime;

            var cursor = new ByteCursor(rawTransaction);

            try
            {
                version = cursor.ReadInt32();
                var inputCount = cursor.ReadCompact();
                if (inputCount > TransactionRules.MaxInputs)
                {
                    var reason = $"Input count {inputCount} exceeds maximum value {TransactionRules.MaxInputs}";
                    throw new EncodingException(typeof(Transaction), cursor, reason);
                }
                inputs = new Input[inputCount];
                for (int i = 0; i < (int)inputCount; i++)
                {
                    var previousHash = new Sha256Hash(cursor.ReadBytes(Sha256Hash.Length));
                    var previousIndex = cursor.ReadUInt32();
                    var previousOutPoint = new OutPoint(previousHash, previousIndex);
                    var signatureScript = cursor.ReadVarBytes(TransactionRules.MaxPayload);
                    var sequence = cursor.ReadUInt32();
                    inputs[i] = new Input(previousOutPoint, signatureScript, sequence);
                }
                var outputCount = cursor.ReadCompact();
                if (outputCount > TransactionRules.MaxOutputs)
                {
                    var reason = $"Output count {outputCount} exceeds maximum value {TransactionRules.MaxOutputs}";
                    throw new EncodingException(typeof(Transaction), cursor, reason);
                }
                outputs = new Output[outputCount];
                for (int i = 0; i < (int)outputCount; i++)
                {
                    var amount = (Amount)cursor.ReadInt64();
                    var pkScript = cursor.ReadVarBytes(TransactionRules.MaxPayload);
                    outputs[i] = new Output(amount, pkScript);
                }
                lockTime = cursor.ReadUInt32();
            }
            catch (Exception ex) when (!(ex is EncodingException))
            {
                throw new EncodingException(typeof(Transaction), cursor, ex);
            }

            return new Transaction(version, inputs, outputs, lockTime);
        }
Exemplo n.º 41
0
 private void LockUnspentCore(bool unlock, OutPoint[] outpoints)
 {
     if(outpoints == null || outpoints.Length == 0)
         return;
     List<object> parameters = new List<object>();
     parameters.Add(unlock);
     JArray array = new JArray();
     parameters.Add(array);
     foreach(var outp in outpoints)
     {
         var obj = new JObject();
         obj["txid"] = outp.Hash.ToString();
         obj["vout"] = outp.N;
         array.Add(obj);
     }
     SendCommand("lockunspent", parameters.ToArray());
 }
        /// <summary>
        /// Checks that UTXO is valid for staking and then checks kernel hash.
        /// </summary>
        /// <param name="context">Staking context.</param>
        /// <param name="prevChainedBlock">Previous chained block.</param>
        /// <param name="headerBits">Chained block's header bits, which define the difficulty target.</param>
        /// <param name="transactionTime">Transaction time.</param>
        /// <param name="prevout">Information about transaction id and index.</param>
        /// <param name="prevBlockTime">The previous block time.</param>
        public void CheckKernel(ContextStakeInformation context, ChainedBlock prevChainedBlock, uint headerBits, long transactionTime, OutPoint prevout)
        {
            this.logger.LogTrace("({0}:'{1}',{2}:0x{3:X},{4}:{5},{6}:'{7}.{8}')", nameof(prevChainedBlock), prevChainedBlock,
                                 nameof(headerBits), headerBits, nameof(transactionTime), transactionTime, nameof(prevout), prevout.Hash, prevout.N);

            FetchCoinsResponse coins = this.coinView.FetchCoinsAsync(new[] { prevout.Hash }).GetAwaiter().GetResult();

            if ((coins == null) || (coins.UnspentOutputs.Length != 1))
            {
                this.logger.LogTrace("(-)[READ_PREV_TX_FAILED]");
                ConsensusErrors.ReadTxPrevFailed.Throw();
            }

            ChainedBlock prevBlock = this.chain.GetBlock(coins.BlockHash);

            if (prevBlock == null)
            {
                this.logger.LogTrace("(-)[REORG]");
                ConsensusErrors.ReadTxPrevFailed.Throw();
            }

            UnspentOutputs prevUtxo = coins.UnspentOutputs[0];

            if (this.IsConfirmedInNPrevBlocks(prevUtxo, prevChainedBlock, this.consensusOptions.StakeMinConfirmations - 1))
            {
                this.logger.LogTrace("(-)[LOW_COIN_AGE]");
                ConsensusErrors.InvalidStakeDepth.Throw();
            }

            BlockStake prevBlockStake = this.stakeChain.Get(prevChainedBlock.HashBlock);

            if (prevBlockStake == null)
            {
                this.logger.LogTrace("(-)[BAD_STAKE_BLOCK]");
                ConsensusErrors.BadStakeBlock.Throw();
            }

            this.CheckStakeKernelHash(context, prevChainedBlock, headerBits, prevBlock.Header.Time, prevBlockStake, prevUtxo, prevout, (uint)transactionTime);
        }
 public VerboseInputInfo(OutPoint outPoint, VerboseOutputInfo prevOutput)
 {
     OutPoint   = outPoint;
     PrevOutput = prevOutput;
 }
        /// <summary>
        /// Checks that the stake kernel hash satisfies the target difficulty.
        /// </summary>
        /// <param name="context">Staking context.</param>
        /// <param name="prevChainedBlock">Previous chained block.</param>
        /// <param name="headerBits">Chained block's header bits, which define the difficulty target.</param>
        /// <param name="prevBlockTime">The previous block time.</param>
        /// <param name="prevBlockStake">Information about previous staked block.</param>
        /// <param name="stakingCoins">Coins that participate in staking.</param>
        /// <param name="prevout">Information about transaction id and index.</param>
        /// <param name="transactionTime">Transaction time.</param>
        /// <remarks>
        /// Coinstake must meet hash target according to the protocol:
        /// kernel (input 0) must meet the formula
        /// <c>hash(stakeModifierV2 + stakingCoins.Time + prevout.Hash + prevout.N + transactionTime) &lt; target * weight</c>.
        /// This ensures that the chance of getting a coinstake is proportional to the amount of coins one owns.
        /// <para>
        /// The reason this hash is chosen is the following:
        /// <list type="number">
        /// <item><paramref name="prevBlockStake.StakeModifierV2"/>: Scrambles computation to make it very difficult to precompute future proof-of-stake.</item>
        /// <item><paramref name="stakingCoins.Time"/>: Time of the coinstake UTXO. Slightly scrambles computation.</item>
        /// <item><paramref name="prevout.Hash"/> Hash of stakingCoins UTXO, to reduce the chance of nodes generating coinstake at the same time.</item>
        /// <item><paramref name="prevout.N"/>: Output number of stakingCoins UTXO, to reduce the chance of nodes generating coinstake at the same time.</item>
        /// <item><paramref name="transactionTime"/>: Timestamp of the coinstake transaction.</item>
        /// </list>
        /// Block or transaction tx hash should not be used here as they can be generated in vast
        /// quantities so as to generate blocks faster, degrading the system back into a proof-of-work situation.
        /// </para>
        /// </remarks>
        /// <exception cref="ConsensusErrors.StakeTimeViolation">Thrown in case transaction time is lower than it's own UTXO timestamp.</exception>
        /// <exception cref="ConsensusErrors.StakeHashInvalidTarget">Thrown in case PoS hash doesn't meet target protocol.</exception>
        private void CheckStakeKernelHash(ContextStakeInformation context, ChainedBlock prevChainedBlock, uint headerBits, uint prevBlockTime,
                                          BlockStake prevBlockStake, UnspentOutputs stakingCoins, OutPoint prevout, uint transactionTime)
        {
            this.logger.LogTrace("({0}:'{1}/{2}',{3}:{4:X},{5}:{6},{7}.{8}:'{9}',{10}:'{11}/{12}',{13}:'{14}',{15}:{16})",
                                 nameof(prevChainedBlock), prevChainedBlock.HashBlock, prevChainedBlock.Height, nameof(headerBits), headerBits, nameof(prevBlockTime), prevBlockTime,
                                 nameof(prevBlockStake), nameof(prevBlockStake.HashProof), prevBlockStake.HashProof, nameof(stakingCoins), stakingCoins.TransactionId, stakingCoins.Height,
                                 nameof(prevout), prevout, nameof(transactionTime), transactionTime);

            if (transactionTime < stakingCoins.Time)
            {
                this.logger.LogTrace("Coinstake transaction timestamp {0} is lower than it's own UTXO timestamp {1}.", transactionTime, stakingCoins.Time);
                this.logger.LogTrace("(-)[BAD_STAKE_TIME]");
                ConsensusErrors.StakeTimeViolation.Throw();
            }

            // Base target.
            BigInteger target = new Target(headerBits).ToBigInteger();

            // TODO: Investigate:
            // The POS protocol should probably put a limit on the max amount that can be staked
            // not a hard limit but a limit that allow any amount to be staked with a max weight value.
            // the max weight should not exceed the max uint256 array size (array size = 32).

            // Weighted target.
            long       valueIn        = stakingCoins.Outputs[prevout.N].Value.Satoshi;
            BigInteger weight         = BigInteger.ValueOf(valueIn);
            BigInteger weightedTarget = target.Multiply(weight);

            context.TargetProofOfStake = ToUInt256(weightedTarget);
            this.logger.LogTrace("POS target is '{0}', weighted target for {1} coins is '{2}'.", ToUInt256(target), valueIn, context.TargetProofOfStake);

            uint256 stakeModifierV2 = prevBlockStake.StakeModifierV2;

            // Calculate hash.
            using (var ms = new MemoryStream())
            {
                var serializer = new BitcoinStream(ms, true);
                serializer.ReadWrite(stakeModifierV2);
                serializer.ReadWrite(stakingCoins.Time);
                serializer.ReadWrite(prevout.Hash);
                serializer.ReadWrite(prevout.N);
                serializer.ReadWrite(transactionTime);

                context.HashProofOfStake = Hashes.Hash256(ms.ToArray());
            }

            this.logger.LogTrace("Stake modifier V2 is '{0}', hash POS is '{1}'.", stakeModifierV2, context.HashProofOfStake);

            // Now check if proof-of-stake hash meets target protocol.
            BigInteger hashProofOfStakeTarget = new BigInteger(1, context.HashProofOfStake.ToBytes(false));

            if (hashProofOfStakeTarget.CompareTo(weightedTarget) > 0)
            {
                this.logger.LogTrace("(-)[TARGET_MISSED]");
                ConsensusErrors.StakeHashInvalidTarget.Throw();
            }

            this.logger.LogTrace("(-)[OK]");
        }
Exemplo n.º 45
0
		private void LockUnspentCore(bool unlock, OutPoint[] outpoints)
		{
			try
			{
				LockUnspentCoreAsync(unlock, outpoints).Wait();
			}
			catch (AggregateException ex)
			{
				ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
			}
		}
        public CycleProgressInfo Update()
        {
            int             height = Services.BlockExplorerService.GetCurrentHeight();
            CycleParameters cycle;
            CyclePhase      phase;
            CyclePeriod     period;
            bool            isSafetyPeriod = false;

            if (ClientChannelNegotiation == null)
            {
                cycle  = Parameters.CycleGenerator.GetRegisteringCycle(height);
                phase  = CyclePhase.Registration;
                period = cycle.GetPeriods().GetPeriod(phase);
            }
            else
            {
                cycle = ClientChannelNegotiation.GetCycle();
                var phases = new CyclePhase[]
                {
                    CyclePhase.Registration,
                    CyclePhase.ClientChannelEstablishment,
                    CyclePhase.TumblerChannelEstablishment,
                    CyclePhase.PaymentPhase,
                    CyclePhase.TumblerCashoutPhase,
                    CyclePhase.ClientCashoutPhase
                };

                if (cycle.IsComplete(height))
                {
                    return(null);
                }

                //If we are not in any phase we are in the SaftyPeriod.
                if (!phases.Any(p => cycle.IsInPhase(p, height)))
                {
                    //Find last CyclePhase
                    phase = CyclePhase.Registration;
                    for (int i = height - 1; i >= cycle.Start; i--)
                    {
                        if (phases.Any(p => cycle.IsInPhase(p, i)))
                        {
                            phase = phases.First(p => cycle.IsInPhase(p, i));
                            break;
                        }
                    }

                    period         = cycle.GetPeriods().GetPeriod(phase);
                    period.End    += cycle.SafetyPeriodDuration;
                    isSafetyPeriod = true;
                }
                else
                {
                    phase          = phases.First(p => cycle.IsInPhase(p, height));
                    period         = cycle.GetPeriods().GetPeriod(phase);
                    isSafetyPeriod = false;
                }
            }

            var blocksLeft = period.End - height;

            Logs.Client.LogInformation($"Cycle {cycle.Start} ({Status})");
            Logs.Client.LogInformation($"{cycle.ToString(height)} in phase {phase} ({blocksLeft} more blocks)");
            var previousState = Status;

            var progressInfo = new CycleProgressInfo(period, height, blocksLeft, cycle.Start, Status, phase, isSafetyPeriod, $"{cycle.ToString(height)} in phase {phase} ({blocksLeft} more blocks)");

            TumblerClient bob = null, alice = null;

            try
            {
                var correlation = SolverClientSession == null ? CorrelationId.Zero : new CorrelationId(SolverClientSession.Id);

                FeeRate feeRate = null;
                switch (phase)
                {
                case CyclePhase.Registration:
                    if (Status == PaymentStateMachineStatus.New)
                    {
                        bob = Runtime.CreateTumblerClient(cycle.Start, Identity.Bob);
                        //Client asks for voucher
                        var voucherResponse = bob.AskUnsignedVoucher();
                        NeedSave = true;
                        //Client ensures he is in the same cycle as the tumbler (would fail if one tumbler or client's chain isn't sync)
                        var tumblerCycle = Parameters.CycleGenerator.GetCycle(voucherResponse.CycleStart);
                        Assert(tumblerCycle.Start == cycle.Start, "invalid-phase");
                        //Saving the voucher for later
                        StartCycle = cycle.Start;
                        ClientChannelNegotiation = new ClientChannelNegotiation(Parameters, cycle.Start);
                        ClientChannelNegotiation.ReceiveUnsignedVoucher(voucherResponse);
                        Status = PaymentStateMachineStatus.Registered;
                    }
                    break;

                case CyclePhase.ClientChannelEstablishment:
                    if (Status == PaymentStateMachineStatus.Registered)
                    {
                        alice = Runtime.CreateTumblerClient(cycle.Start, Identity.Alice);
                        var key = alice.RequestTumblerEscrowKey();
                        ClientChannelNegotiation.ReceiveTumblerEscrowKey(key.PubKey, key.KeyIndex);
                        //Client create the escrow
                        var escrowTxOut = ClientChannelNegotiation.BuildClientEscrowTxOut();
                        feeRate = GetFeeRate();

                        Transaction clientEscrowTx = null;
                        try
                        {
                            clientEscrowTx = Services.WalletService.FundTransactionAsync(escrowTxOut, feeRate).GetAwaiter().GetResult();
                        }
                        catch (NotEnoughFundsException ex)
                        {
                            Logs.Client.LogInformation($"Not enough funds in the wallet to tumble. Missing about {ex.Missing}. Denomination is {Parameters.Denomination}.");
                            break;
                        }
                        NeedSave = true;
                        var redeemDestination = Services.WalletService.GenerateAddressAsync().GetAwaiter().GetResult().ScriptPubKey;
                        var channelId         = new uint160(RandomUtils.GetBytes(20));
                        SolverClientSession = ClientChannelNegotiation.SetClientSignedTransaction(channelId, clientEscrowTx, redeemDestination);


                        correlation = new CorrelationId(SolverClientSession.Id);

                        Tracker.AddressCreated(cycle.Start, TransactionType.ClientEscrow, escrowTxOut.ScriptPubKey, correlation);
                        Tracker.TransactionCreated(cycle.Start, TransactionType.ClientEscrow, clientEscrowTx.GetHash(), correlation);
                        Services.BlockExplorerService.TrackAsync(escrowTxOut.ScriptPubKey).GetAwaiter().GetResult();


                        var redeemTx = SolverClientSession.CreateRedeemTransaction(feeRate);
                        Tracker.AddressCreated(cycle.Start, TransactionType.ClientRedeem, redeemDestination, correlation);

                        //redeemTx does not be to be recorded to the tracker, this is TrustedBroadcastService job

                        Services.BroadcastService.BroadcastAsync(clientEscrowTx).GetAwaiter().GetResult();

                        Services.TrustedBroadcastService.Broadcast(cycle.Start, TransactionType.ClientRedeem, correlation, redeemTx);
                        Status = PaymentStateMachineStatus.ClientChannelBroadcasted;
                    }
                    else if (Status == PaymentStateMachineStatus.ClientChannelBroadcasted)
                    {
                        alice = Runtime.CreateTumblerClient(cycle.Start, Identity.Alice);
                        TransactionInformation clientTx = GetTransactionInformation(SolverClientSession.EscrowedCoin, true);
                        var state = ClientChannelNegotiation.GetInternalState();
                        if (clientTx != null && clientTx.Confirmations >= cycle.SafetyPeriodDuration)
                        {
                            Logs.Client.LogInformation($"Client escrow reached {cycle.SafetyPeriodDuration} confirmations");
                            //Client asks the public key of the Tumbler and sends its own
                            alice.BeginSignVoucher(new SignVoucherRequest
                            {
                                MerkleProof     = clientTx.MerkleProof,
                                Transaction     = clientTx.Transaction,
                                KeyReference    = state.TumblerEscrowKeyReference,
                                UnsignedVoucher = state.BlindedVoucher,
                                Cycle           = cycle.Start,
                                ClientEscrowKey = state.ClientEscrowKey.PubKey,
                                ChannelId       = SolverClientSession.Id
                            });
                            NeedSave = true;
                            Status   = PaymentStateMachineStatus.TumblerVoucherSigning;
                        }
                    }
                    else if (Status == PaymentStateMachineStatus.TumblerVoucherSigning)
                    {
                        alice = Runtime.CreateTumblerClient(cycle.Start, Identity.Alice);
                        var voucher = alice.EndSignVoucher(SolverClientSession.Id);
                        if (voucher != null)
                        {
                            ClientChannelNegotiation.CheckVoucherSolution(voucher);
                            NeedSave = true;
                            Status   = PaymentStateMachineStatus.TumblerVoucherObtained;
                        }
                    }
                    break;

                case CyclePhase.TumblerChannelEstablishment:

                    if (Status == PaymentStateMachineStatus.TumblerVoucherObtained)
                    {
                        bob = Runtime.CreateTumblerClient(cycle.Start, Identity.Bob);
                        Logs.Client.LogInformation("Begin ask to open the channel...");
                        //Client asks the Tumbler to make a channel
                        var     bobEscrowInformation = ClientChannelNegotiation.GetOpenChannelRequest();
                        uint160 channelId            = null;
                        try
                        {
                            channelId = bob.BeginOpenChannel(bobEscrowInformation);
                            NeedSave  = true;
                        }
                        catch (Exception ex)
                        {
                            if (ex.Message.Contains("tumbler-insufficient-funds"))
                            {
                                Logs.Client.LogWarning("The tumbler server has not enough funds and can't open a channel for now");
                                break;
                            }
                            throw;
                        }
                        ClientChannelNegotiation.SetChannelId(channelId);
                        Status = PaymentStateMachineStatus.TumblerChannelCreating;
                    }
                    else if (Status == PaymentStateMachineStatus.TumblerChannelCreating)
                    {
                        bob = Runtime.CreateTumblerClient(cycle.Start, Identity.Bob);
                        var tumblerEscrow = bob.EndOpenChannel(cycle.Start, ClientChannelNegotiation.GetInternalState().ChannelId);
                        if (tumblerEscrow == null)
                        {
                            Logs.Client.LogInformation("Tumbler escrow still creating...");
                            break;
                        }
                        NeedSave = true;

                        if (tumblerEscrow.OutputIndex >= tumblerEscrow.Transaction.Outputs.Count)
                        {
                            Logs.Client.LogError("Tumbler escrow output out-of-bound");
                            Status = PaymentStateMachineStatus.Wasted;
                            break;
                        }

                        var txOut      = tumblerEscrow.Transaction.Outputs[tumblerEscrow.OutputIndex];
                        var outpoint   = new OutPoint(tumblerEscrow.Transaction.GetHash(), tumblerEscrow.OutputIndex);
                        var escrowCoin = new Coin(outpoint, txOut).ToScriptCoin(ClientChannelNegotiation.GetTumblerEscrowParameters(tumblerEscrow.EscrowInitiatorKey).ToScript());

                        Console.WriteLine("TumblerEscrow hex: " + tumblerEscrow.Transaction.ToHex());

                        PromiseClientSession = ClientChannelNegotiation.ReceiveTumblerEscrowedCoin(escrowCoin);
                        Logs.Client.LogInformation("Tumbler expected escrowed coin received");
                        //Tell to the block explorer we need to track that address (for checking if it is confirmed in payment phase)
                        Services.BlockExplorerService.TrackAsync(PromiseClientSession.EscrowedCoin.ScriptPubKey).GetAwaiter().GetResult();
                        Services.BlockExplorerService.TrackPrunedTransactionAsync(tumblerEscrow.Transaction, tumblerEscrow.MerkleProof).GetAwaiter().GetResult();

                        Tracker.AddressCreated(cycle.Start, TransactionType.TumblerEscrow, PromiseClientSession.EscrowedCoin.ScriptPubKey, correlation);
                        Tracker.TransactionCreated(cycle.Start, TransactionType.TumblerEscrow, PromiseClientSession.EscrowedCoin.Outpoint.Hash, correlation);

                        Services.BroadcastService.BroadcastAsync(tumblerEscrow.Transaction).GetAwaiter().GetResult();
                        //Channel is done, now need to run the promise protocol to get valid puzzle
                        var cashoutDestination = DestinationWallet.GetNewDestination();
                        Tracker.AddressCreated(cycle.Start, TransactionType.TumblerCashout, cashoutDestination, correlation);

                        feeRate = GetFeeRate();
                        var sigReq      = PromiseClientSession.CreateSignatureRequest(cashoutDestination, feeRate);
                        var commitments = bob.SignHashes(PromiseClientSession.Id, sigReq);
                        var revelation  = PromiseClientSession.Reveal(commitments);
                        var proof       = bob.CheckRevelation(PromiseClientSession.Id, revelation);
                        var puzzle      = PromiseClientSession.CheckCommitmentProof(proof);
                        SolverClientSession.AcceptPuzzle(puzzle);
                        Status = PaymentStateMachineStatus.TumblerChannelCreated;
                    }
                    else if (Status == PaymentStateMachineStatus.TumblerChannelCreated)
                    {
                        CheckTumblerChannelSecured(cycle);
                    }
                    break;

                case CyclePhase.PaymentPhase:
                    //Could have confirmed during safe period
                    //Only check for the first block when period start,
                    //else Tumbler can know deanonymize you based on the timing of first Alice request if the transaction was not confirmed previously
                    if (Status == PaymentStateMachineStatus.TumblerChannelCreated && height == period.Start)
                    {
                        CheckTumblerChannelSecured(cycle);
                    }
                    //No "else if" intended
                    if (Status == PaymentStateMachineStatus.TumblerChannelSecured)
                    {
                        alice = Runtime.CreateTumblerClient(cycle.Start, Identity.Alice);
                        Logs.Client.LogDebug("Starting the puzzle solver protocol...");
                        var puzzles = SolverClientSession.GeneratePuzzles();
                        alice.BeginSolvePuzzles(SolverClientSession.Id, puzzles);
                        NeedSave = true;
                        Status   = PaymentStateMachineStatus.ProcessingPayment;
                    }
                    else if (Status == PaymentStateMachineStatus.ProcessingPayment)
                    {
                        feeRate = GetFeeRate();
                        alice   = Runtime.CreateTumblerClient(cycle.Start, Identity.Alice);
                        var commitments = alice.EndSolvePuzzles(SolverClientSession.Id);
                        NeedSave = true;
                        if (commitments == null)
                        {
                            Logs.Client.LogDebug("Still solving puzzles...");
                            break;
                        }
                        var revelation2      = SolverClientSession.Reveal(commitments);
                        var solutionKeys     = alice.CheckRevelation(SolverClientSession.Id, revelation2);
                        var blindFactors     = SolverClientSession.GetBlindFactors(solutionKeys);
                        var offerInformation = alice.CheckBlindFactors(SolverClientSession.Id, blindFactors);

                        var offerSignature = SolverClientSession.SignOffer(offerInformation);

                        var offerRedeem = SolverClientSession.CreateOfferRedeemTransaction(feeRate);
                        Logs.Client.LogDebug("Puzzle solver protocol ended...");

                        //May need to find solution in the fulfillment transaction
                        Services.BlockExplorerService.TrackAsync(offerRedeem.PreviousScriptPubKey).GetAwaiter().GetResult();
                        Tracker.AddressCreated(cycle.Start, TransactionType.ClientOfferRedeem, SolverClientSession.GetInternalState().RedeemDestination, correlation);
                        Services.TrustedBroadcastService.Broadcast(cycle.Start, TransactionType.ClientOfferRedeem, correlation, offerRedeem);
                        try
                        {
                            solutionKeys = alice.FulfillOffer(SolverClientSession.Id, offerSignature);
                            SolverClientSession.CheckSolutions(solutionKeys);
                            var tumblingSolution = SolverClientSession.GetSolution();
                            var transaction      = PromiseClientSession.GetSignedTransaction(tumblingSolution);
                            Logs.Client.LogDebug("Got puzzle solution cooperatively from the tumbler");

                            Logs.Client.LogDebug("TumblerCashOut hex: {0}", transaction.ToHex());

                            Status = PaymentStateMachineStatus.PuzzleSolutionObtained;
                            Services.TrustedBroadcastService.Broadcast(cycle.Start, TransactionType.TumblerCashout, correlation, new TrustedBroadcastRequest()
                            {
                                BroadcastAt = cycle.GetPeriods().ClientCashout.Start,
                                Transaction = transaction
                            });
                            if (Cooperative)
                            {
                                try
                                {
                                    // No need to await for it, it is a just nice for the tumbler (we don't want the underlying socks connection cut before the escape key is sent)
                                    var signature = SolverClientSession.SignEscape();
                                    alice.GiveEscapeKeyAsync(SolverClientSession.Id, signature).GetAwaiter().GetResult();
                                }
                                catch (Exception ex) { Logs.Client.LogDebug(new EventId(), ex, "Exception while giving the escape key"); }
                                Logs.Client.LogInformation("Gave escape signature to the tumbler");
                            }
                        }
                        catch (Exception ex)
                        {
                            Status = PaymentStateMachineStatus.UncooperativeTumbler;
                            Logs.Client.LogWarning("The tumbler did not gave puzzle solution cooperatively");
                            Logs.Client.LogWarning(ex.ToString());
                        }
                    }

                    break;

                case CyclePhase.ClientCashoutPhase:

                    //If the tumbler is uncooperative, he published solutions on the blockchain
                    if (Status == PaymentStateMachineStatus.UncooperativeTumbler)
                    {
                        var transactions = Services.BlockExplorerService.GetTransactionsAsync(SolverClientSession.GetInternalState().OfferCoin.ScriptPubKey, false).GetAwaiter().GetResult();
                        if (transactions.Count != 0)
                        {
                            SolverClientSession.CheckSolutions(transactions.Select(t => t.Transaction).ToArray());
                            Logs.Client.LogInformation("Puzzle solution recovered from tumbler's fulfill transaction");
                            NeedSave = true;
                            Status   = PaymentStateMachineStatus.PuzzleSolutionObtained;
                            var tumblingSolution = SolverClientSession.GetSolution();
                            var transaction      = PromiseClientSession.GetSignedTransaction(tumblingSolution);
                            Tracker.TransactionCreated(cycle.Start, TransactionType.TumblerCashout, transaction.GetHash(), correlation);
                            Services.BroadcastService.BroadcastAsync(transaction).GetAwaiter().GetResult();
                        }
                    }

                    break;
                }
            }
            catch (InvalidStateException ex)
            {
                Logs.Client.LogDebug(new EventId(), ex, "Client side Invalid State, the payment is wasted");
                Status = PaymentStateMachineStatus.Wasted;
            }
            catch (Exception ex) when(ex.Message.IndexOf("invalid-state", StringComparison.OrdinalIgnoreCase) >= 0)
            {
                Logs.Client.LogDebug(new EventId(), ex, "Tumbler side Invalid State, the payment is wasted");
                Status = PaymentStateMachineStatus.Wasted;
            }
            finally
            {
                if (previousState != Status)
                {
                    Logs.Client.LogInformation($"Status changed {previousState} => {Status}");
                }
                if (alice != null && bob != null)
                {
                    throw new InvalidOperationException("Bob and Alice have been both initialized, please report the bug to NTumbleBit developers");
                }
                if (alice != null)
                {
                    alice.Dispose();
                }
                if (bob != null)
                {
                    bob.Dispose();
                }
            }

            return(progressInfo);
        }
Exemplo n.º 47
0
        private void FindTxBtn_ClickAsync(object sender, RoutedEventArgs e)
        {
            ListTxSummary.Items.Clear();
            primaryVM.RecievedCoins = null;
            primaryVM.SpentCoins    = null;
            primaryVM.Block         = 0;
            try
            {
                if (MainTxBtn.IsChecked == true)
                {
                    primaryVM.TxQueryMain(TxInputBox.Text);
                }
                else
                {
                    primaryVM.TxQueryTestNet(TxInputBox.Text);
                }

                if (primaryVM.RecievedCoins != null)
                {
                    primaryVM.RecievedAmount = Money.Zero;
                    ListTxSummary.Items.Add("All Recieved Coins");
                }

                //Sum: Total Recieved
                foreach (var recievedCoin in primaryVM.RecievedCoins)
                {
                    primaryVM.RecievedAmount = (Money)recievedCoin.Amount.Add(primaryVM.RecievedAmount);
                }
                //List: Recieved Coins
                if (MainTxBtn.IsChecked == true)
                {
                    foreach (Coin coin in primaryVM.RecievedCoins)
                    {
                        var amount = coin.Amount;

                        var paymentScript = coin.ScriptPubKey;
                        var address       = paymentScript.GetDestinationAddress(Network.Main);
                        ListTxSummary.Items.Add($"Recieved +{amount.ToDecimal(MoneyUnit.BTC)}BTC To: {address} : ScriptPubKey: {paymentScript}");
                    }
                }
                else
                {
                    foreach (Coin coin in primaryVM.RecievedCoins)
                    {
                        var amount = coin.Amount;

                        var paymentScript = coin.ScriptPubKey;
                        var address       = paymentScript.GetDestinationAddress(Network.TestNet);
                        ListTxSummary.Items.Add($"Recieved +{amount.ToDecimal(MoneyUnit.BTC)}BTC To: {address} : ScriptPubKey: {paymentScript}");
                    }
                }


                primaryVM.SpentAmount = Money.Zero;
                ListTxSummary.Items.Add("All Spent Coins");
                //Sum: Total Spent
                foreach (var spentCoin in primaryVM.SpentCoins)
                {
                    primaryVM.SpentAmount = (Money)spentCoin.Amount.Add(primaryVM.SpentAmount);
                }
                //List: Total Spent
                if (MainTxBtn.IsChecked == true)
                {
                    foreach (Coin coin in primaryVM.SpentCoins)
                    {
                        Money amount = coin.Amount;

                        var paymentScript = coin.ScriptPubKey;
                        var address       = paymentScript.GetDestinationAddress(Network.Main);
                        ListTxSummary.Items.Add($"Spent -{amount.ToDecimal(MoneyUnit.BTC)}BTC From: {address} : ScriptPubKey: {paymentScript}");
                    }
                }
                else
                {
                    foreach (Coin coin in primaryVM.SpentCoins)
                    {
                        Money amount = coin.Amount;

                        var paymentScript = coin.ScriptPubKey;
                        var address       = paymentScript.GetDestinationAddress(Network.TestNet);
                        ListTxSummary.Items.Add($"Spent -{amount.ToDecimal(MoneyUnit.BTC)}BTC From: {address} : ScriptPubKey: {paymentScript}");
                    }
                }


                // Previous Outpoints
                var inputs = primaryVM.Transaction.Inputs;
                primaryVM.OutpointCount = inputs.Count;
                var firstOutpoint    = primaryVM.RecievedCoins.First().Outpoint;
                var firstTransaction = primaryVM.Client.GetTransaction(firstOutpoint.Hash).Result.Transaction;
                ListTxSummary.Items.Add("All Outpoints");
                ListTxSummary.Items.Add($"#({primaryVM.OutpointCount + 1}) Current Outpoint = {firstOutpoint.Hash} , {firstOutpoint.N} , {firstTransaction.IsCoinBase.ToString()}");

                var countOrder = primaryVM.OutpointCount;
                foreach (TxIn input in inputs)
                {
                    OutPoint previousOutpoint    = input.PrevOut;
                    var      previousTransaction = primaryVM.Client.GetTransaction(previousOutpoint.Hash).Result.Transaction;
                    ListTxSummary.Items.Add($"#({countOrder}) Prev Outpoint = {previousOutpoint.Hash} , {previousOutpoint.N} , {previousTransaction.IsCoinBase.ToString()}");
                    countOrder -= 1;
                }
            }
            catch (Exception ex)
            {
                TxInputBox.Text = ex.ToString();
            }
        }
Exemplo n.º 48
0
 public static IBitcoinWriter Add(this IBitcoinWriter w, OutPoint op) => op.AddTo(w);
Exemplo n.º 49
0
		public void bloom_match()
		{
			// Random real transaction (b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b)
			Transaction tx = new Transaction();
			tx.ReadWrite(ParseHex("01000000010b26e9b7735eb6aabdf358bab62f9816a21ba9ebdb719d5299e88607d722c190000000008b4830450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d27d8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a0141046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339ffffffff021bff3d11000000001976a91404943fdd508053c75000106d3bc6e2754dbcff1988ac2f15de00000000001976a914a266436d2965547608b9e15d9032a7b9d64fa43188ac00000000"));


			// and one which spends it (e2769b09e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436)
			var ch = new byte[] { 0x01, 0x00, 0x00, 0x00, 0x01, 0x6b, 0xff, 0x7f, 0xcd, 0x4f, 0x85, 0x65, 0xef, 0x40, 0x6d, 0xd5, 0xd6, 0x3d, 0x4f, 0xf9, 0x4f, 0x31, 0x8f, 0xe8, 0x20, 0x27, 0xfd, 0x4d, 0xc4, 0x51, 0xb0, 0x44, 0x74, 0x01, 0x9f, 0x74, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x49, 0x30, 0x46, 0x02, 0x21, 0x00, 0xda, 0x0d, 0xc6, 0xae, 0xce, 0xfe, 0x1e, 0x06, 0xef, 0xdf, 0x05, 0x77, 0x37, 0x57, 0xde, 0xb1, 0x68, 0x82, 0x09, 0x30, 0xe3, 0xb0, 0xd0, 0x3f, 0x46, 0xf5, 0xfc, 0xf1, 0x50, 0xbf, 0x99, 0x0c, 0x02, 0x21, 0x00, 0xd2, 0x5b, 0x5c, 0x87, 0x04, 0x00, 0x76, 0xe4, 0xf2, 0x53, 0xf8, 0x26, 0x2e, 0x76, 0x3e, 0x2d, 0xd5, 0x1e, 0x7f, 0xf0, 0xbe, 0x15, 0x77, 0x27, 0xc4, 0xbc, 0x42, 0x80, 0x7f, 0x17, 0xbd, 0x39, 0x01, 0x41, 0x04, 0xe6, 0xc2, 0x6e, 0xf6, 0x7d, 0xc6, 0x10, 0xd2, 0xcd, 0x19, 0x24, 0x84, 0x78, 0x9a, 0x6c, 0xf9, 0xae, 0xa9, 0x93, 0x0b, 0x94, 0x4b, 0x7e, 0x2d, 0xb5, 0x34, 0x2b, 0x9d, 0x9e, 0x5b, 0x9f, 0xf7, 0x9a, 0xff, 0x9a, 0x2e, 0xe1, 0x97, 0x8d, 0xd7, 0xfd, 0x01, 0xdf, 0xc5, 0x22, 0xee, 0x02, 0x28, 0x3d, 0x3b, 0x06, 0xa9, 0xd0, 0x3a, 0xcf, 0x80, 0x96, 0x96, 0x8d, 0x7d, 0xbb, 0x0f, 0x91, 0x78, 0xff, 0xff, 0xff, 0xff, 0x02, 0x8b, 0xa7, 0x94, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xba, 0xde, 0xec, 0xfd, 0xef, 0x05, 0x07, 0x24, 0x7f, 0xc8, 0xf7, 0x42, 0x41, 0xd7, 0x3b, 0xc0, 0x39, 0x97, 0x2d, 0x7b, 0x88, 0xac, 0x40, 0x94, 0xa8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xc1, 0x09, 0x32, 0x48, 0x3f, 0xec, 0x93, 0xed, 0x51, 0xf5, 0xfe, 0x95, 0xe7, 0x25, 0x59, 0xf2, 0xcc, 0x70, 0x43, 0xf9, 0x88, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00 };
			var vch = ch.Take(ch.Length - 1).ToArray();

			Transaction spendingTx = new Transaction();
			spendingTx.ReadWrite(vch);

			BloomFilter filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL);
			filter.Insert(uint256.Parse("0xb4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b"));
			Assert.True(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match tx hash");

			filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL);
			// byte-reversed tx hash
			filter.Insert(ParseHex("6bff7fcd4f8565ef406dd5d63d4ff94f318fe82027fd4dc451b04474019f74b4"));
			Assert.True(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match manually serialized tx hash");

			filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL);
			filter.Insert(ParseHex("30450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d27d8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a01"));
			Assert.True(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match input signature");

			filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL);
			filter.Insert(ParseHex("046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339"));
			Assert.True(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match input pub key");

			filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL);
			filter.Insert(ParseHex("04943fdd508053c75000106d3bc6e2754dbcff19"));
			Assert.True(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match output address");
			Assert.True(filter.IsRelevantAndUpdate(spendingTx), "Simple Bloom filter didn't add output");

			filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL);
			filter.Insert(ParseHex("a266436d2965547608b9e15d9032a7b9d64fa431"));
			Assert.True(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match output address");

			filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL);
			filter.Insert(new OutPoint(uint256.Parse("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0));
			Assert.True(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match COutPoint");

			filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL);
			OutPoint prevOutPoint = new OutPoint(uint256.Parse("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0);
			{
				var data = prevOutPoint.ToBytes();
				filter.Insert(data);
			}
			Assert.True(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match manually serialized COutPoint");

			filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL);
			filter.Insert(uint256.Parse("00000009e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436"));
			Assert.True(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched random tx hash");

			filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL);
			filter.Insert(ParseHex("0000006d2965547608b9e15d9032a7b9d64fa431"));
			Assert.True(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched random address");

			filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL);
			filter.Insert(new OutPoint(uint256.Parse("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 1));
			Assert.True(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched COutPoint for an output we didn't care about");

			filter = new BloomFilter(10, 0.000001, 0, BloomFlags.UPDATE_ALL);
			filter.Insert(new OutPoint(uint256.Parse("0x000000d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0));
			Assert.True(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched COutPoint for an output we didn't care about");
		}
Exemplo n.º 50
0
 public TransactionOutPointWithValue(OutPoint outpoint, Money value, Script scriptPubKey)
 {
     this.outpoint = outpoint;
     this.value = value;
     this.scriptPubKey = scriptPubKey;
 }