예제 #1
0
 public Tx(uint version, TxIn[] inputs, TxOut[] outputs, uint lockTime)
 {
     this.version = version;
     this.inputs = inputs;
     this.outputs = outputs;
     this.lockTime = lockTime;
 }
예제 #2
0
		public void CanExtractTxOutDestinationEasily()
		{
			var secret = new BitcoinSecret("KyJTjvFpPF6DDX4fnT56d2eATPfxjdUPXFFUb85psnCdh34iyXRQ");

			var tx = new Transaction();
			var p2pkh = new TxOut(new Money((UInt64)45000000), secret.GetAddress());
			var p2pk = new TxOut(new Money((UInt64)80000000), secret.PrivateKey.PubKey);

			tx.AddOutput(p2pkh);
			tx.AddOutput(p2pk);

			Assert.False(p2pkh.IsTo(secret.PrivateKey.PubKey));
			Assert.True(p2pkh.IsTo(secret.GetAddress()));
			Assert.True(p2pk.IsTo(secret.PrivateKey.PubKey));
			Assert.False(p2pk.IsTo(secret.GetAddress()));
		}
예제 #3
0
		protected override void BuildTransaction(JObject json, Transaction tx)
		{
			var txid = uint256.Parse((string)json.GetValue("txid"));
			tx.Version = (uint)json.GetValue("version");
			tx.LockTime = (uint)json.GetValue("locktime");

			var vin = (JArray)json.GetValue("vin");
			for(int i = 0 ; i < vin.Count ; i++)
			{
				var jsonIn = (JObject)vin[i];
				var txin = new TxIn();
				tx.Inputs.Add(txin);

				var script = (JObject)jsonIn.GetValue("scriptSig");
				if(script != null)
				{
					txin.ScriptSig = new Script(Encoders.Hex.DecodeData((string)script.GetValue("hex")));
					txin.PrevOut.Hash = uint256.Parse((string)jsonIn.GetValue("txid"));
					txin.PrevOut.N = (uint)jsonIn.GetValue("vout");
				}
				else
				{
					var coinbase = (string)jsonIn.GetValue("coinbase");
					txin.ScriptSig = new Script(Encoders.Hex.DecodeData(coinbase));
				}

				txin.Sequence = (uint)jsonIn.GetValue("sequence");

			}

			var vout = (JArray)json.GetValue("vout");
			for(int i = 0 ; i < vout.Count ; i++)
			{
				var jsonOut = (JObject)vout[i];
				var txout = new TxOut();
				tx.Outputs.Add(txout);

				var btc = (decimal)jsonOut.GetValue("value");
				var satoshis = btc * Money.COIN;
				txout.Value = new Money((long)(satoshis));

				var script = (JObject)jsonOut.GetValue("scriptPubKey");
				txout.ScriptPubKey = new Script(Encoders.Hex.DecodeData((string)script.GetValue("hex")));
			}
		}
예제 #4
0
        private static void Blockchain()
        {
            var bitcoinPrivateKeySecret = new BitcoinSecret("cVX7SpYc8yjNW8WzPpiGTqyWD4eM4BBnfqEm9nwGqJb2QiX9hhdf");
            var network = bitcoinPrivateKeySecret.Network;
            var address = bitcoinPrivateKeySecret.GetAddress();

            Console.WriteLine(bitcoinPrivateKeySecret); // cVX7SpYc8yjNW8WzPpiGTqyWD4eM4BBnfqEm9nwGqJb2QiX9hhdf - this is my private testnet
            Console.WriteLine(address);
            // mtjeFt6dMKqvQmYKcBAkSX9AmX8qdynVKN - THIS is my first public bitcoin testnet address!!!!!!!
            Console.WriteLine(network);

            //tx IDS: 7a73b2fa89315be9a1292bf817c5e97296cdd14019c112ebbdf2cbfeecc60e7d
            // 9ed5bc5b2b8c81bcf6585377fb20b4b5b82eb413981d3e01145c7b1c55c3179c
            // a034c8d913960e1135175f915ad1bb32f2a13270fd73ece1c6d717f2f3d1c2b4
            // e6beb1f58a14d2fc2dc041329cd09cadd86e364dbc46ebd950228a0740c00b9c

            // transaction inspection:
            // for test-net http://tapi.qbit.ninja/transactions/9ed5bc5b2b8c81bcf6585377fb20b4b5b82eb413981d3e01145c7b1c55c3179c
            // or for main-net http://api.qbit.ninja/transactions/....

            var client        = new QBitNinjaClient(network);
            var transactionId = uint256.Parse("e6beb1f58a14d2fc2dc041329cd09cadd86e364dbc46ebd950228a0740c00b9c"); // see this trans above!
                                                                                                                   // from this string we get all transaction info making a web request!
            var transactionResponse = client.GetTransaction(transactionId).Result;

            var      receivedCoins   = transactionResponse.ReceivedCoins;
            OutPoint outPointToSpend = null;

            foreach (var coin in receivedCoins)
            {
                if (coin.TxOut.ScriptPubKey == bitcoinPrivateKeySecret.ScriptPubKey)
                {
                    outPointToSpend = coin.Outpoint;
                }
            }
            if (outPointToSpend == null)
            {
                throw new Exception("TxOut doesn't contain our ScriptPubKey");
            }
            Console.WriteLine("We want to spend {0}. outpoint:", outPointToSpend.N + 1);

            Console.WriteLine(transactionResponse.TransactionId);
            // 9ed5bc5b2b8c81bcf6585377fb20b4b5b82eb413981d3e01145c7b1c55c3179c
            Console.WriteLine(transactionResponse.Block != null
          ? transactionResponse.Block.Confirmations.ToString()
          : "null block");

            var transaction = new Transaction();

            TxIn txIn = new TxIn
            {
                PrevOut = outPointToSpend
            };

            transaction.Inputs.Add(txIn);

            //blockexplorer of addresses or transactions/blocks
            // https://testnet.manu.backend.hamburg/faucet - load test bitcoins into my account - to show this
            // https://testnet.blockexplorer.com/address/mzp4No5cmCXjZUpf112B1XWsvWBfws5bbB
            // https://live.blockcypher.com/btc-testnet/address/mtjeFt6dMKqvQmYKcBAkSX9AmX8qdynVKN/ -  to present this!
            var hallOfTheMakersAddress = BitcoinAddress.Create("mzp4No5cmCXjZUpf112B1XWsvWBfws5bbB");

            TxOut hallOfTheMakersTxOut = new TxOut
            {
                Value        = new Money(7, MoneyUnit.Satoshi),
                ScriptPubKey = hallOfTheMakersAddress.ScriptPubKey
            };
            var   minerFee        = new Money(1, MoneyUnit.Satoshi); // accepts 500! not 100! show example of rejected out-funds!
            TxOut changeBackTxOut = new TxOut
            {
                Value        = (Money)receivedCoins[(int)outPointToSpend.N].Amount - hallOfTheMakersTxOut.Value - minerFee,
                ScriptPubKey = bitcoinPrivateKeySecret.ScriptPubKey
            };

            transaction.Outputs.Add(hallOfTheMakersTxOut);
            transaction.Outputs.Add(changeBackTxOut);

            transaction.Outputs.Add(new TxOut
            {
                Value        = Money.Zero,
                ScriptPubKey =
                    TxNullDataTemplate.Instance.GenerateScriptPubKey(Encoding.UTF8.GetBytes("danson loves NBitcoin!"))
            });

            transaction.Inputs[0].ScriptSig = bitcoinPrivateKeySecret.ScriptPubKey;
            //transaction.Sign(bitcoinPrivateKey, false);
            transaction.Sign(bitcoinPrivateKeySecret, false); // Byzantine generals problem! to show/explain!

            //return;

            // qbit ninja client works
            BroadcastResponse broadcastResponse = client.Broadcast(transaction).Result;

            if (!broadcastResponse.Success)
            {
                Console.Error.WriteLine("ErrorCode: " + broadcastResponse.Error.ErrorCode);
                Console.Error.WriteLine("Error message: " + broadcastResponse.Error.Reason);
            }
            else
            {
                Console.WriteLine("Success! You can check out the hash of the transaction in any block explorer:");
                Console.WriteLine(transaction.GetHash());
                // my first successful transactions on testnet!
                // d9d7e05bf7a1d66bc0324824bf898d2fdd6771b2fc676eaa98efa04bd94312aa
                // 71d6f6193fc0d0959f47e7d05cc2772ad0493298120d0d7d68000a0b231665eb
                // 773d6ca5b18b875602f0058435422ed16a150c05fba8d086a599a361227ad047
                // https://live.blockcypher.com/btc-testnet/tx/d9d7e05bf7a1d66bc0324824bf898d2fdd6771b2fc676eaa98efa04bd94312aa/
                // https://live.blockcypher.com/btc-testnet/address/mtjeFt6dMKqvQmYKcBAkSX9AmX8qdynVKN/
            }

            // much slower version - direct library
            //using (var node = Node.Connect(network)) //Connect to the node
            //{
            //    node.VersionHandshake(); //Say hello
            //                             //Advertize your transaction (send just the hash)
            //    node.SendMessage(new InvPayload(InventoryType.MSG_TX, transaction.GetHash()));
            //    //Send it
            //    node.SendMessage(new TxPayload(transaction));
            //    Thread.Sleep(500); //Wait a bit
            //}
        }
예제 #5
0
        /// <summary>
        /// Creates a cold staking withdrawal <see cref="Transaction"/>.
        /// </summary>
        /// <remarks>
        /// Cold staking withdrawal is performed on the wallet that is in the role of the cold staking cold wallet.
        /// </remarks>
        /// <param name="walletTransactionHandler">The wallet transaction handler used to build the transaction.</param>
        /// <param name="receivingAddress">The address that will receive the withdrawal.</param>
        /// <param name="walletName">The name of the wallet in the role of cold wallet.</param>
        /// <param name="walletPassword">The wallet password.</param>
        /// <param name="amount">The amount to remove from cold staking.</param>
        /// <param name="feeAmount">The fee to pay for cold staking transaction withdrawal.</param>
        /// <returns>The <see cref="Transaction"/> for cold staking withdrawal.</returns>
        /// <exception cref="WalletException">Thrown if the receiving address is in a cold staking account in this wallet.</exception>
        /// <exception cref="ArgumentNullException">Thrown if the receiving address is invalid.</exception>
        internal Transaction GetColdStakingWithdrawalTransaction(IWalletTransactionHandler walletTransactionHandler, string receivingAddress,
                                                                 string walletName, string walletPassword, Money amount, Money feeAmount)
        {
            Guard.NotEmpty(receivingAddress, nameof(receivingAddress));
            Guard.NotEmpty(walletName, nameof(walletName));
            Guard.NotNull(amount, nameof(amount));
            Guard.NotNull(feeAmount, nameof(feeAmount));

            Wallet.Types.Wallet wallet = this.GetWalletByName(walletName);

            // Get the cold staking account.
            HdAccount coldAccount = this.GetColdStakingAccount(wallet, true);

            if (coldAccount == null)
            {
                this.logger.LogTrace("(-)[COLDSTAKE_ACCOUNT_DOES_NOT_EXIST]");
                throw new WalletException("The cold wallet account does not exist.");
            }

            // Prevent reusing cold stake addresses as regular withdrawal addresses.
            if (coldAccount.ExternalAddresses.Concat(coldAccount.InternalAddresses).Any(s => s.Address == receivingAddress || s.Bech32Address == receivingAddress))
            {
                this.logger.LogTrace("(-)[COLDSTAKE_INVALID_COLD_WALLET_ADDRESS_USAGE]");
                throw new WalletException("You can't send the money to a cold staking cold wallet account.");
            }

            HdAccount hotAccount = this.GetColdStakingAccount(wallet, false);

            if (hotAccount != null && hotAccount.ExternalAddresses.Concat(hotAccount.InternalAddresses).Any(s => s.Address == receivingAddress || s.Bech32Address == receivingAddress))
            {
                this.logger.LogTrace("(-)[COLDSTAKE_INVALID_HOT_WALLET_ADDRESS_USAGE]");
                throw new WalletException("You can't send the money to a cold staking hot wallet account.");
            }

            Script destination = null;

            if (BitcoinWitPubKeyAddress.IsValid(receivingAddress, this.network, out Exception _))
            {
                destination = new BitcoinWitPubKeyAddress(receivingAddress, wallet.Network).ScriptPubKey;
            }
            else
            {
                // Send the money to the receiving address.
                destination = BitcoinAddress.Create(receivingAddress, wallet.Network).ScriptPubKey;
            }

            // Create the transaction build context (used in BuildTransaction).
            var accountReference = new WalletAccountReference(walletName, coldAccount.Name);
            var context          = new TransactionBuildContext(wallet.Network)
            {
                AccountReference = accountReference,
                // Specify a dummy change address to prevent a change (internal) address from being created.
                // Will be changed after the transacton is built and before it is signed.
                ChangeAddress    = coldAccount.ExternalAddresses.First(),
                TransactionFee   = feeAmount,
                MinConfirmations = 0,
                Shuffle          = false,
                Sign             = false,
                Recipients       = new[] { new Recipient {
                                               Amount = amount, ScriptPubKey = destination
                                           } }.ToList()
            };

            // Register the cold staking builder extension with the transaction builder.
            context.TransactionBuilder.Extensions.Add(new ColdStakingBuilderExtension(false));

            // Avoid script errors due to missing scriptSig.
            context.TransactionBuilder.StandardTransactionPolicy.ScriptVerify = null;

            // Build the transaction according to the settings recorded in the context.
            Transaction transaction = walletTransactionHandler.BuildTransaction(context);

            // Map OutPoint to UnspentOutputReference.
            Dictionary <OutPoint, UnspentOutputReference> mapOutPointToUnspentOutput = this.GetSpendableTransactionsInAccount(accountReference)
                                                                                       .ToDictionary(unspent => unspent.ToOutPoint(), unspent => unspent);

            // Set the cold staking scriptPubKey on the change output.
            TxOut changeOutput = transaction.Outputs.SingleOrDefault(output => (output.ScriptPubKey != destination) && (output.Value != 0));

            if (changeOutput != null)
            {
                // Find the largest input.
                TxIn largestInput = transaction.Inputs.OrderByDescending(input => mapOutPointToUnspentOutput[input.PrevOut].Transaction.Amount).Take(1).Single();

                // Set the scriptPubKey of the change output to the scriptPubKey of the largest input.
                changeOutput.ScriptPubKey = mapOutPointToUnspentOutput[largestInput.PrevOut].Transaction.ScriptPubKey;
            }

            // Add keys for signing inputs. This takes time so only add keys for distinct addresses.
            foreach (UnspentOutputReference item in transaction.Inputs.Select(i => mapOutPointToUnspentOutput[i.PrevOut]).Distinct())
            {
                Script prevscript = item.Transaction.ScriptPubKey;

                if (prevscript.IsScriptType(ScriptType.P2SH) || prevscript.IsScriptType(ScriptType.P2WSH))
                {
                    if (item.Address.RedeemScripts == null)
                    {
                        throw new WalletException("Wallet has no redeem scripts");
                    }

                    Script redeemScript = item.Address.RedeemScripts.FirstOrDefault(r => r.Hash.ScriptPubKey == item.Transaction.ScriptPubKey || r.WitHash.ScriptPubKey == item.Transaction.ScriptPubKey);

                    if (redeemScript == null)
                    {
                        throw new WalletException($"RedeemScript was not found for address '{item.Address.Address}' with output '{item.Transaction.ScriptPubKey}'");
                    }

                    // Provide the redeem script to the builder
                    var scriptCoin = ScriptCoin.Create(this.network, item.ToOutPoint(), new TxOut(item.Transaction.Amount, prevscript), redeemScript);
                    context.TransactionBuilder.AddCoins(scriptCoin);
                }

                context.TransactionBuilder.AddKeys(wallet.GetExtendedPrivateKeyForAddress(walletPassword, item.Address));
            }

            // Sign the transaction.
            context.TransactionBuilder.SignTransactionInPlace(transaction);

            this.logger.LogTrace("(-):'{0}'", transaction.GetHash());
            return(transaction);
        }
예제 #6
0
        public void RegistrationTest()
        {
            using (NodeBuilder builder = NodeBuilder.Create())
            {
                CoreNode node1 = builder.CreateStratisPosNode(true, fullNodeBuilder =>
                {
                    fullNodeBuilder
                    .UseStratisConsensus()
                    .UseBlockStore()
                    .UseMempool()
                    .UseBlockNotification()
                    .UseTransactionNotification()
                    .UseWallet()
                    .UseWatchOnlyWallet()
                    .AddPowPosMining()
                    //.AddMining()
                    //.UseApi()
                    .AddRPC();
                });

                CoreNode node2 = builder.CreateStratisPosNode(true, fullNodeBuilder =>
                {
                    fullNodeBuilder
                    .UseStratisConsensus()
                    .UseBlockStore()
                    .UseMempool()
                    .UseBlockNotification()
                    .UseTransactionNotification()
                    .UseWallet()
                    .UseWatchOnlyWallet()
                    .AddPowPosMining()
                    //.AddMining()
                    //.UseApi()
                    .AddRPC()
                    .UseRegistration();
                });

                node1.NotInIBD();
                node2.NotInIBD();

                var rpc1 = node1.CreateRPCClient();
                var rpc2 = node2.CreateRPCClient();

                // addnode RPC call does not seem to work, so connect directly
                node1.FullNode.ConnectionManager.AddNodeAddress(node2.Endpoint);

                // Create the originating node's wallet
                var wm1 = node1.FullNode.NodeService <IWalletManager>() as WalletManager;
                wm1.CreateWallet("Registration1", "registration");

                var wallet1  = wm1.GetWalletByName("registration");
                var account1 = wallet1.GetAccountsByCoinType((CoinType)node1.FullNode.Network.Consensus.CoinType).First();
                var address1 = account1.GetFirstUnusedReceivingAddress();
                var secret1  = wallet1.GetExtendedPrivateKeyForAddress("Registration1", address1);
                node1.SetDummyMinerSecret(new BitcoinSecret(secret1.PrivateKey, node1.FullNode.Network));

                // Generate a block so we have some funds to create a transaction with
                node1.GenerateStratisWithMiner(52);

                TestHelper.TriggerSync(node1);
                TestHelper.TriggerSync(node2);

                TestHelper.WaitLoop(() => rpc1.GetBestBlockHash() == rpc2.GetBestBlockHash());

                var rsa           = new RsaKey();
                var ecdsa         = new Key().GetBitcoinSecret(Network.RegTest);
                var serverAddress = ecdsa.GetAddress().ToString();

                var token = new RegistrationToken(1,
                                                  serverAddress,
                                                  IPAddress.Parse("127.0.0.1"),
                                                  IPAddress.Parse("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
                                                  "0123456789ABCDEF",
                                                  "",
                                                  37123,
                                                  ecdsa.PubKey);

                var cryptoUtils = new CryptoUtils(rsa, ecdsa);
                token.RsaSignature   = cryptoUtils.SignDataRSA(token.GetHeaderBytes().ToArray());
                token.EcdsaSignature = cryptoUtils.SignDataECDSA(token.GetHeaderBytes().ToArray());

                byte[] msgBytes = token.GetRegistrationTokenBytes(rsa, ecdsa);

                Transaction sendTx      = new Transaction();
                Money       outputValue = new Money(0.01m, MoneyUnit.BTC);
                Money       feeValue    = new Money(0.01m, MoneyUnit.BTC);

                byte[] bytes = Encoding.UTF8.GetBytes("BREEZE_REGISTRATION_MARKER");
                sendTx.Outputs.Add(new TxOut()
                {
                    Value        = outputValue,
                    ScriptPubKey = TxNullDataTemplate.Instance.GenerateScriptPubKey(bytes)
                });

                foreach (PubKey pubKey in BlockChainDataConversions.BytesToPubKeys(msgBytes))
                {
                    TxOut destTxOut = new TxOut()
                    {
                        Value        = outputValue,
                        ScriptPubKey = pubKey.ScriptPubKey
                    };

                    sendTx.Outputs.Add(destTxOut);
                }

                var wth1 = node1.FullNode.NodeService <IWalletTransactionHandler>() as WalletTransactionHandler;

                List <Recipient> recipients = new List <Recipient>();

                foreach (TxOut txOut in sendTx.Outputs)
                {
                    recipients.Add(new Recipient()
                    {
                        Amount = txOut.Value, ScriptPubKey = txOut.ScriptPubKey
                    });
                }

                var walletReference = new WalletAccountReference()
                {
                    // Default to the first wallet & first account
                    AccountName = wm1.Wallets.First().GetAccountsByCoinType((CoinType)node1.FullNode.Network.Consensus.CoinType).First().Name,
                    WalletName  = wm1.Wallets.First().Name
                };

                var context = new TransactionBuildContext(
                    walletReference,
                    recipients,
                    "Registration1")
                {
                    MinConfirmations = 0,
                    OverrideFeeRate  = new FeeRate(new Money(0.001m, MoneyUnit.BTC)),
                    Shuffle          = false,
                    Sign             = true
                };

                var tx = wth1.BuildTransaction(context);
                node1.AddToStratisPosMempool(tx);

                TestHelper.WaitLoop(() => rpc1.GetRawMempool().Length > 0);

                node1.GenerateStratisWithMiner(1);

                Thread.Sleep(10000);

                TestHelper.WaitLoop(() => rpc1.GetBestBlockHash() == rpc2.GetBestBlockHash());

                Console.WriteLine("Checking if registration was received...");

                var rm2 = node2.FullNode.NodeService <RegistrationManager>();
                var rs2 = rm2.GetRegistrationStore();

                foreach (var record in rs2.GetAll())
                {
                    Console.WriteLine("Received registration: " + record.RecordTxId);
                }

                Console.WriteLine(rs2.GetAll().Count);

                Thread.Sleep(10000);

                node1.Kill();
                node2.Kill();
            }
        }
예제 #7
0
        static void Main()
        {
            // Create a client
            QBitNinjaClient client = new QBitNinjaClient(Network.Main);
            // Parse transaction id to NBitcoin.uint256 so the client can eat it
            var transactionId = uint256.Parse("f13dc48fb035bbf0a6e989a26b3ecb57b84f85e0836e777d6edf60d87a4a2d94");

            // Query the transaction
            QBitNinja.Client.Models.GetTransactionResponse transactionResponse = client.GetTransaction(transactionId).Result;


            NBitcoin.Transaction transaction = transactionResponse.Transaction;

            Console.WriteLine(transactionResponse.TransactionId); // f13dc48fb035bbf0a6e989a26b3ecb57b84f85e0836e777d6edf60d87a4a2d94
            Console.WriteLine(transaction.GetHash());             // f13dc48fb035bbf0a6e989a26b3ecb57b84f85e0836e777d6edf60d87a4a2d94

            // RECEIVED COINS
            List <ICoin> receivedCoins = transactionResponse.ReceivedCoins;

            foreach (Coin coin in receivedCoins)
            {
                Money amount = coin.Amount;

                Console.WriteLine(amount.ToDecimal(MoneyUnit.BTC));
                var paymentScript = coin.ScriptPubKey;
                Console.WriteLine(paymentScript);  // It's the ScriptPubKey
                var address = paymentScript.GetDestinationAddress(Network.Main);
                Console.WriteLine(address);
                Console.WriteLine();
            }

            // RECEIVED COINS
            var outputs = transaction.Outputs;

            foreach (TxOut output in outputs)
            {
                Coin  coin   = new Coin(transaction, output);
                Money amount = coin.Amount;

                Console.WriteLine(amount.ToDecimal(MoneyUnit.BTC));
                var paymentScript = coin.GetScriptCode();
                Console.WriteLine(paymentScript);  // It's the ScriptPubKey
                var address = paymentScript.GetDestinationAddress(Network.Main);
                Console.WriteLine(address);
                Console.WriteLine();
            }

            // RECEIVED COINS
            foreach (TxOut output in outputs)
            {
                Money amount = output.Value;

                Console.WriteLine(amount.ToDecimal(MoneyUnit.BTC));
                var paymentScript = output.ScriptPubKey;
                Console.WriteLine(paymentScript);  // It's the ScriptPubKey
                var address = paymentScript.GetDestinationAddress(Network.Main);
                Console.WriteLine(address);
                Console.WriteLine();
            }

            // SPENT COINS
            List <ICoin> spentCoins = transactionResponse.SpentCoins;

            foreach (Coin coin in spentCoins)
            {
                Money amount = coin.Amount;

                Console.WriteLine(amount.ToDecimal(MoneyUnit.BTC));
                var paymentScript = coin.ScriptPubKey;
                Console.WriteLine(paymentScript);  // It's the ScriptPubKey
                var address = paymentScript.GetDestinationAddress(Network.Main);
                Console.WriteLine(address);
                Console.WriteLine();
            }

            // SPENT COINS
            foreach (Coin coin in spentCoins)
            {
                TxOut previousOutput = coin.TxOut;
                Money amount         = previousOutput.Value;

                Console.WriteLine(amount.ToDecimal(MoneyUnit.BTC));
                var paymentScript = previousOutput.ScriptPubKey;
                Console.WriteLine(paymentScript);  // It's the ScriptPubKey
                var address = paymentScript.GetDestinationAddress(Network.Main);
                Console.WriteLine(address);
                Console.WriteLine();
            }


            var fee = transaction.GetFee(spentCoins.ToArray());

            Console.WriteLine(fee);

            var inputs = transaction.Inputs;

            foreach (TxIn input in inputs)
            {
                OutPoint previousOutpoint = input.PrevOut;
                Console.WriteLine(previousOutpoint.Hash); // hash of prev tx
                Console.WriteLine(previousOutpoint.N);    // idx of out from prev tx, that has been spent in the current tx
                Console.WriteLine();
            }

            // Let's create a txout with 21 bitcoin from the first ScriptPubKey in our current transaction
            Money twentyOneBtc = new Money(21, MoneyUnit.BTC);
            var   scriptPubKey = transaction.Outputs.First().ScriptPubKey;
            TxOut txOut        = new TxOut(twentyOneBtc, scriptPubKey);

            OutPoint firstOutPoint = spentCoins.First().Outpoint;

            Console.WriteLine(firstOutPoint.Hash);       // 4788c5ef8ffd0463422bcafdfab240f5bf0be690482ceccde79c51cfce209edd
            Console.WriteLine(firstOutPoint.N);          // 0

            Console.WriteLine(transaction.Inputs.Count); // 9

            OutPoint firstPreviousOutPoint            = transaction.Inputs.First().PrevOut;
            var      firstPreviousTransactionResponse = client.GetTransaction(firstPreviousOutPoint.Hash).Result;

            Console.WriteLine(firstPreviousTransactionResponse.IsCoinbase); // False
            NBitcoin.Transaction firstPreviousTransaction = firstPreviousTransactionResponse.Transaction;

            //while (firstPreviousTransactionResponse.IsCoinbase == false)
            //{
            //    Console.WriteLine(firstPreviousTransaction.GetHash());

            //    firstPreviousOutPoint = firstPreviousTransaction.Inputs.First().PrevOut;
            //    firstPreviousTransactionResponse = client.GetTransaction(firstPreviousOutPoint.Hash).Result;
            //    firstPreviousTransaction = firstPreviousTransactionResponse.Transaction;
            //}

            Money spentAmount = Money.Zero;

            foreach (var spentCoin in spentCoins)
            {
                spentAmount = (Money)spentCoin.Amount.Add(spentAmount);
            }
            Console.WriteLine(spentAmount.ToDecimal(MoneyUnit.BTC)); // 13.19703492

            Money receivedAmount = Money.Zero;

            foreach (var receivedCoin in receivedCoins)
            {
                receivedAmount = (Money)receivedCoin.Amount.Add(receivedAmount);
            }
            Console.WriteLine(receivedAmount.ToDecimal(MoneyUnit.BTC)); // 13.19683492

            Console.WriteLine((spentAmount - receivedAmount).ToDecimal(MoneyUnit.BTC));

            Console.WriteLine(spentAmount.ToDecimal(MoneyUnit.BTC) - receivedAmount.ToDecimal(MoneyUnit.BTC));

            //var inputs = transaction.Inputs;
            //foreach (TxIn input in inputs)
            //{
            //    uint256 previousTransactionId = input.PrevOut.Hash;
            //    GetTransactionResponse previousTransactionResponse = client.GetTransaction(previousTransactionId).Result;

            //    NBitcoin.Transaction previousTransaction = previousTransactionResponse.Transaction;

            //    var previousTransactionOutputs = previousTransaction.Outputs;
            //    foreach (TxOut previousTransactionOutput in previousTransactionOutputs)
            //    {
            //        Money amount = previousTransactionOutput.Value;

            //        Console.WriteLine(amount.ToDecimal(MoneyUnit.BTC));
            //        var paymentScript = previousTransactionOutput.ScriptPubKey;
            //        Console.WriteLine(paymentScript);  // It's the ScriptPubKey
            //        var address = paymentScript.GetDestinationAddress(Network.Main);
            //        Console.WriteLine(address);
            //        Console.WriteLine();
            //    }
            //}

            Console.ReadLine();
        }
 /// <summary>
 /// Discerns whether an output is a transfer to a destination other than the federation multisig.
 /// </summary>
 private bool IsTargetAddressCandidate(TxOut output)
 {
     return(output.ScriptPubKey != this.multisigAddress.ScriptPubKey && !output.ScriptPubKey.IsUnspendable);
 }
예제 #9
0
		public void MergeFrom(Coins otherCoin)
		{
			var diff = otherCoin.Outputs.Count - this.Outputs.Count;
			if(diff > 0)
			{
				Outputs.Resize(otherCoin.Outputs.Count);
				for(int i = 0 ; i < Outputs.Count ; i++)
				{
					if(Outputs[i] == null)
						Outputs[i] = new TxOut();
				}
			}
			for(int i = 0 ; i < otherCoin.Outputs.Count ; i++)
			{
				if(!otherCoin.Outputs[i].IsNull)
					Outputs[i] = otherCoin.Outputs[i];
			}
			UpdateValue();
		}
 public SerializableCoin(OutPoint outpoint, TxOut txOut)
 {
     Outpoint = outpoint;
     TxOut    = txOut;
 }
        /// <summary>
        /// Adds a transaction that credits the wallet with new coins.
        /// This method is can be called many times for the same transaction (idempotent).
        /// </summary>
        /// <param name="transaction">The transaction from which details are added.</param>
        /// <param name="utxo">The unspent output to add to the wallet.</param>
        /// <param name="blockHeight">Height of the block.</param>
        /// <param name="block">The block containing the transaction to add.</param>
        /// <param name="isPropagated">Propagation state of the transaction.</param>
        private void AddTransactionToWallet(Transaction transaction, TxOut utxo, int?blockHeight = null, Block block = null, bool isPropagated = true)
        {
            Guard.NotNull(transaction, nameof(transaction));
            Guard.NotNull(utxo, nameof(utxo));

            uint256 transactionHash = transaction.GetHash();

            // Get the collection of transactions to add to.
            Script script = utxo.ScriptPubKey;

            // Check if a similar UTXO exists or not (same transaction ID and same index).
            // New UTXOs are added, existing ones are updated.
            int             index            = transaction.Outputs.IndexOf(utxo);
            Money           amount           = utxo.Value;
            TransactionData foundTransaction = this.Wallet.MultiSigAddress.Transactions.FirstOrDefault(t => (t.Id == transactionHash) && (t.Index == index));

            if (foundTransaction == null)
            {
                this.logger.LogTrace("UTXO '{0}-{1}' not found, creating.", transactionHash, index);
                TransactionData newTransaction = new TransactionData
                {
                    Amount       = amount,
                    BlockHeight  = blockHeight,
                    BlockHash    = block?.GetHash(),
                    Id           = transactionHash,
                    CreationTime = DateTimeOffset.FromUnixTimeSeconds(block?.Header.Time ?? transaction.Time),
                    Index        = index,
                    ScriptPubKey = script,
                    Hex          = transaction.ToHex(),
                    IsPropagated = isPropagated
                };

                // Add the Merkle proof to the (non-spending) transaction.
                if (block != null)
                {
                    newTransaction.MerkleProof = new MerkleBlock(block, new[] { transactionHash }).PartialMerkleTree;
                }

                this.Wallet.MultiSigAddress.Transactions.Add(newTransaction);
                this.AddInputKeysLookupLock(newTransaction);
            }
            else
            {
                this.logger.LogTrace("Transaction ID '{0}' found, updating.", transactionHash);

                // Update the block height and block hash.
                if ((foundTransaction.BlockHeight == null) && (blockHeight != null))
                {
                    foundTransaction.BlockHeight = blockHeight;
                    foundTransaction.BlockHash   = block?.GetHash();
                }

                // Update the block time.
                if (block != null)
                {
                    foundTransaction.CreationTime = DateTimeOffset.FromUnixTimeSeconds(block.Header.Time);
                }

                // Add the Merkle proof now that the transaction is confirmed in a block.
                if ((block != null) && (foundTransaction.MerkleProof == null))
                {
                    foundTransaction.MerkleProof = new MerkleBlock(block, new[] { transactionHash }).PartialMerkleTree;
                }

                if (isPropagated)
                {
                    foundTransaction.IsPropagated = true;
                }
            }

            this.TransactionFoundInternal(script);
        }
        private void CheckTransaction(Transaction transaction, Money suppliedBudget)
        {
            if (!transaction.IsSmartContractExecTransaction())
            {
                return;
            }

            TxOut scTxOut = transaction.TryGetSmartContractTxOut();

            if (scTxOut == null)
            {
                new ConsensusError("no-smart-contract-tx-out", "No smart contract TxOut").Throw();
            }

            Result <ContractTxData> callDataDeserializationResult = this.callDataSerializer.Deserialize(scTxOut.ScriptPubKey.ToBytes());

            if (callDataDeserializationResult.IsFailure)
            {
                new ConsensusError("invalid-calldata-format", string.Format("Invalid {0} format", typeof(ContractTxData).Name)).Throw();
            }

            ContractTxData callData = callDataDeserializationResult.Value;

            if (callData.GasPrice < GasPriceMinimum)
            {
                // Supplied gas price is too low.
                this.ThrowGasPriceLessThanMinimum();
            }

            if (callData.GasPrice > GasPriceMaximum)
            {
                // Supplied gas price is too high.
                this.ThrowGasPriceMoreThanMaximum();
            }

            if (callData.IsCreateContract && callData.GasLimit < GasLimitCreateMinimum)
            {
                this.ThrowGasLessThenCreateFee();
            }

            if (!callData.IsCreateContract && callData.GasLimit < GasLimitCallMinimum)
            {
                // Supplied gas limit is too low.
                this.ThrowGasLessThanBaseFee();
            }

            if (callData.GasLimit > GasLimitMaximum)
            {
                // Supplied gas limit is too high - at a certain point we deem that a contract is taking up too much time.
                this.ThrowGasGreaterThanHardLimit();
            }

            // Only measure budget when coming from mempool - this happens inside SmartContractCoinviewRule instead as part of the block.
            if (suppliedBudget != null)
            {
                // Note carrier.GasCostBudget cannot overflow given values are within constraints above.
                if (suppliedBudget < new Money(callData.GasCostBudget))
                {
                    // Supplied satoshis are less than the budget we said we had for the contract execution
                    SmartContractConsensusErrors.FeeTooSmallForGas.Throw();
                }
            }
        }
예제 #13
0
        public uint256 GetSignatureHash(Script scriptCode, int nIn, SigHash nHashType, TxOut spentOutput, HashVersion sigversion, PrecomputedTransactionData precomputedTransactionData)
        {
            if (UsesForkId(nHashType))
            {
                uint nForkHashType = (uint)nHashType;

                nForkHashType |= (uint)ForkID << 8;
                if (spentOutput?.Value == null || spentOutput.Value == TxOut.NullMoney)
                {
                    throw new ArgumentException("The output being signed with the amount must be provided", nameof(spentOutput));
                }

                uint256 hashPrevouts = uint256.Zero;
                uint256 hashSequence = uint256.Zero;
                uint256 hashOutputs  = uint256.Zero;

                if ((nHashType & SigHash.AnyoneCanPay) == 0)
                {
                    hashPrevouts = precomputedTransactionData == null?
                                   GetHashPrevouts() : precomputedTransactionData.HashPrevouts;
                }

                if ((nHashType & SigHash.AnyoneCanPay) == 0 && ((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None)
                {
                    hashSequence = precomputedTransactionData == null?
                                   GetHashSequence() : precomputedTransactionData.HashSequence;
                }

                if (((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None)
                {
                    hashOutputs = precomputedTransactionData == null?
                                  GetHashOutputs() : precomputedTransactionData.HashOutputs;
                }
                else if (((uint)nHashType & 0x1f) == (uint)SigHash.Single && nIn < Outputs.Count)
                {
                    CoinStream ss = CreateHashWriter(sigversion);
                    ss.ReadWrite(Outputs[nIn]);
                    hashOutputs = GetHash(ss);
                }

                CoinStream sss = CreateHashWriter(sigversion);
                // Version
                sss.ReadWrite(Version);
                // Input prevouts/nSequence (none/all, depending on flags)
                sss.ReadWrite(hashPrevouts);
                sss.ReadWrite(hashSequence);
                // The input being signed (replacing the scriptSig with scriptCode + amount)
                // The prevout may already be contained in hashPrevout, and the nSequence
                // may already be contain in hashSequence.
                sss.ReadWrite(Inputs[nIn].PrevOut);
                sss.ReadWrite(scriptCode);
                sss.ReadWrite(spentOutput.Value.Satoshi);
                sss.ReadWrite((uint)Inputs[nIn].Sequence);
                // Outputs (none/one/all, depending on flags)
                sss.ReadWrite(hashOutputs);
                // Locktime
                sss.ReadWriteStruct(LockTime);
                // Sighash type
                sss.ReadWrite(nForkHashType);

                return(GetHash(sss));
            }

            if (nIn >= Inputs.Count)
            {
                return(uint256.One);
            }

            SigHash hashType = nHashType & (SigHash)31;

            // Check for invalid use of SIGHASH_SINGLE
            if (hashType == SigHash.Single)
            {
                if (nIn >= Outputs.Count)
                {
                    return(uint256.One);
                }
            }

            Script scriptCopy = scriptCode.Clone();

            scriptCopy.FindAndDelete(OpcodeType.OP_CODESEPARATOR);

            if (!CustomTransaction.GetCustomTransaction(CurrencyID, out Transaction txCopy))
            {
                txCopy = new Transaction();
            }

            txCopy.FromBytes(this.ToBytes());
            //Set all TxIn script to empty string
            foreach (TxIn txin in txCopy.Inputs)
            {
                txin.ScriptSig = new Script();
            }
            //Copy subscript into the txin script you are checking
            txCopy.Inputs[nIn].ScriptSig = scriptCopy;

            if (hashType == SigHash.None)
            {
                //The output of txCopy is set to a vector of zero size.
                txCopy.Outputs.Clear();

                //All other inputs aside from the current input in txCopy have their nSequence index set to zero
                foreach (TxIn input in txCopy.Inputs.Where((x, i) => i != nIn))
                {
                    input.Sequence = 0;
                }
            }
            else if (hashType == SigHash.Single)
            {
                //The output of txCopy is resized to the size of the current input index+1.
                txCopy.Outputs.RemoveRange(nIn + 1, txCopy.Outputs.Count - (nIn + 1));
                //All other txCopy outputs aside from the output that is the same as the current input index are set to a blank script and a value of (long) -1.
                for (int i = 0; i < txCopy.Outputs.Count; i++)
                {
                    if (i == nIn)
                    {
                        continue;
                    }

                    txCopy.Outputs[i] = new TxOut();
                }
                //All other txCopy inputs aside from the current input are set to have an nSequence index of zero.
                foreach (TxIn input in txCopy.Inputs.Where((x, i) => i != nIn))
                {
                    input.Sequence = 0;
                }
            }

            if ((nHashType & SigHash.AnyoneCanPay) != 0)
            {
                //The txCopy input vector is resized to a length of one.
                TxIn script = txCopy.Inputs[nIn];
                txCopy.Inputs.Clear();
                txCopy.Inputs.Add(script);
                //The subScript (lead in by its length as a var-integer encoded!) is set as the first and only member of this vector.
                txCopy.Inputs[0].ScriptSig = scriptCopy;
            }

            //Serialize TxCopy, append 4 byte hashtypecode
            CoinStream stream = CreateHashWriter(sigversion);

            txCopy.ReadWrite(stream);
            //stream.ReadWrite(nForkHashType);
            return(GetHash(stream));
        }
예제 #14
0
파일: Coins.cs 프로젝트: MakoLab/blockgraph
 private bool IsNull(TxOut o) => o.Value.Satoshi == -1;
예제 #15
0
        static void GetTransaction(Network network, string btcPrivateKey, string transId, bool spending)
        {
            var client              = new QBitNinjaClient(network);
            var transactionId       = uint256.Parse(transId);
            var transactionResponse = client.GetTransaction(transactionId).Result;
            var privateKey          = new BitcoinSecret(btcPrivateKey);

            Console.WriteLine(transactionResponse.TransactionId);

            if (null != transactionResponse.Block)
            {
                Console.WriteLine("Confirmations: " + transactionResponse.Block.Confirmations);
            }
            else
            {
                Console.WriteLine("Block is not yet set.");
            }

            var message = "Asemco leaves their record on the blockchain.";
            var bytes   = Encoding.UTF8.GetBytes(message);

            //foreach (var output in transactionResponse.Transaction.Outputs)
            //{

            //    Console.WriteLine(output.Value);
            //}

            if (spending)
            {
                var      receivedCoins   = transactionResponse.ReceivedCoins;
                OutPoint outPointToSpend = null;
                foreach (var coin in receivedCoins)
                {
                    if (coin.TxOut.ScriptPubKey == privateKey.ScriptPubKey)
                    {
                        outPointToSpend = coin.Outpoint;
                    }
                }

                if (outPointToSpend == null)
                {
                    throw new Exception("TxOut doesn't contain our ScriptPubKey");
                }
                Console.WriteLine("We want to spend {0}. outpoint:", outPointToSpend.N + 1);

                var transaction = new Transaction();
                transaction.Inputs.Add(new TxIn()
                {
                    PrevOut = outPointToSpend
                });

                var receiverAddress = BitcoinAddress.Create("2N8hwP1WmJrFF5QWABn38y63uYLhnJYJYTF", network);
                var outgoingAmount  = new Money(0.0004m, MoneyUnit.BTC);
                var minerFee        = new Money(0.00007m, MoneyUnit.BTC);

                var txInAmount   = (Money)receivedCoins[(int)outPointToSpend.N].Amount;
                var changeAmount = txInAmount - outgoingAmount - minerFee;

                TxOut testBTCOut = new TxOut()
                {
                    Value        = outgoingAmount,
                    ScriptPubKey = receiverAddress.ScriptPubKey
                };

                TxOut changeBackTxOut = new TxOut()
                {
                    Value        = changeAmount,
                    ScriptPubKey = privateKey.ScriptPubKey
                };

                //var message = "Asemco leaves their record on the blockchain.";
                //var bytes = Encoding.UTF8.GetBytes(message);
                TxOut transMessage = new TxOut()
                {
                    Value        = Money.Zero,
                    ScriptPubKey = TxNullDataTemplate.Instance.GenerateScriptPubKey(bytes)
                };

                transaction.Outputs.Add(testBTCOut);
                transaction.Outputs.Add(changeBackTxOut);
                transaction.Outputs.Add(transMessage);
                transaction.Inputs[0].ScriptSig = privateKey.ScriptPubKey;
                transaction.Sign(privateKey, false);

                BroadcastResponse broadcastResponse = client.Broadcast(transaction).Result;

                if (!broadcastResponse.Success)
                {
                    Console.Error.WriteLine("ErrorCode: " + broadcastResponse.Error.ErrorCode);
                    Console.Error.WriteLine("Error message: " + broadcastResponse.Error.Reason);
                }
                else
                {
                    Console.WriteLine("Success!  You've hit the blockchain!  The transaction hash is below.");
                    Console.WriteLine(transaction.GetHash());
                }
            }
        }
예제 #16
0
        public async Task <IActionResult> PostInputsAsync([FromBody, Required] InputsRequest4 request)
        {
            // Validate request.
            if (request.RoundId < 0)
            {
                return(BadRequest("Invalid request."));
            }

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

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

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

                // Do more checks.
                try
                {
                    var 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.Select(x => x.BlindedOutput)))
                    {
                        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 <OutPoint>();
                    foreach (InputProofModel inputProof in request.Inputs)
                    {
                        var outpoint = inputProof.Input;
                        if (uniqueInputs.Contains(outpoint))
                        {
                            return(BadRequest("Cannot register an input twice."));
                        }
                        uniqueInputs.Add(outpoint);
                    }

                    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, 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, out List <Alice> tnr))
                        {
                            if (tr.Union(tnr).Count() > tr.Count)
                            {
                                return(BadRequest("Input is already registered in another round."));
                            }
                        }

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

                        if (bannedElem is { })
                        {
                            return(BadRequest($"Input is banned from participation for {(int)bannedElem.BannedRemaining.TotalMinutes} minutes: {inputProof.Input.N}:{inputProof.Input.Hash}."));
                        }

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

                    // Perform all RPC request at once
                    await batch.SendBatchAsync();

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

                    var inputs = new HashSet <Coin>();

                    var allInputsConfirmed = true;
                    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.N}:{inputProof.Input.Hash}."));
                        }

                        // Check if unconfirmed.
                        if (getTxOutResponse.Confirmations <= 0)
                        {
                            return(BadRequest("Provided input is unconfirmed."));
                        }

                        // Check if immature.
                        if (getTxOutResponse.IsCoinBase && getTxOutResponse.Confirmations <= 100)
                        {
                            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, txOut));
                    }

                    if (!allInputsConfirmed)
                    {
                        // Check if mempool would accept a fake transaction created with the registered inputs.
                        // Fake outputs: mixlevels + 1 maximum, +1 because there can be a change.
                        var result = await RpcClient.TestMempoolAcceptAsync(inputs, fakeOutputCount : round.MixingLevels.Count() + 1, round.FeePerInputs, round.FeePerOutputs);

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

                    var acceptedBlindedOutputScripts = new List <BlindedOutputWithNonceIndex>();

                    // 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.Select(x => x.BlindedOutput));

                    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.BlindedOutput, round.NonceProvider.GetNonceKeyForIndex(blindedOutput.N));
                        blindSignatures.Add(blindSignature);
                    }
                    alice.BlindedOutputSignatures = blindSignatures.ToArray();

                    // Check if phase changed since.
                    if (round.Status != CoordinatorRoundStatus.Running || round.Phase != RoundPhase.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.ExecuteNextPhaseAsync(RoundPhase.ConnectionConfirmation);
                    }

                    var resp = new InputsResponse
                    {
                        UniqueId = alice.UniqueId,
                        RoundId  = round.RoundId
                    };
                    return(Ok(resp));
                }
        /// <summary>
        /// Whether transaction is witness standard.
        /// <seealso cref="https://github.com/bitcoin/bitcoin/blob/aa624b61c928295c27ffbb4d27be582f5aa31b56/src/policy/policy.cpp#L196"/>
        /// </summary>
        /// <param name="tx">Transaction to verify.</param>
        /// <param name="mapInputs">Map of previous transactions that have outputs we're spending.</param>
        /// <returns>Whether transaction is witness standard.</returns>
        private bool IsWitnessStandard(Transaction tx, MempoolCoinView mapInputs)
        {
            if (tx.IsCoinBase)
            {
                this.logger.LogTrace("(-)[IS_COINBASE]:true");
                return(true); // Coinbases are skipped.
            }

            foreach (TxIn input in tx.Inputs)
            {
                // We don't care if witness for this input is empty, since it must not be bloated.
                // If the script is invalid without witness, it would be caught sooner or later during validation.
                if (input.WitScriptEmpty)
                {
                    continue;
                }

                TxOut prev = mapInputs.GetOutputFor(input);

                // Get the scriptPubKey corresponding to this input.
                Script prevScript = prev.ScriptPubKey;
                if (prevScript.IsPayToScriptHash(this.network))
                {
                    // If the scriptPubKey is P2SH, we try to extract the redeemScript casually by converting the scriptSig
                    // into a stack. We do not check IsPushOnly nor compare the hash as these will be done later anyway.
                    // If the check fails at this stage, we know that this txid must be a bad one.
                    PayToScriptHashSigParameters sigParams = PayToScriptHashTemplate.Instance.ExtractScriptSigParameters(this.network, input.ScriptSig);
                    if (sigParams == null || sigParams.RedeemScript == null)
                    {
                        this.logger.LogTrace("(-)[BAD_TXID]:false");
                        return(false);
                    }

                    prevScript = sigParams.RedeemScript;
                }

                // Non-witness program must not be associated with any witness.
                if (!prevScript.IsWitness(this.network))
                {
                    this.logger.LogTrace("(-)[WITNESS_MISMATCH]:false");
                    return(false);
                }

                // Check P2WSH standard limits.
                WitProgramParameters wit = PayToWitTemplate.Instance.ExtractScriptPubKeyParameters2(this.chainIndexer.Network, prevScript);
                if (wit == null)
                {
                    this.logger.LogTrace("(-)[BAD_WITNESS_PARAMS]:false");
                    return(false);
                }

                // Version 0 segregated witness program validation.
                if (wit.Version == 0 && wit.Program.Length == WitnessV0ScriptHashSize)
                {
                    WitScript witness = input.WitScript;

                    // Get P2WSH script from top of stack.
                    Script scriptPubKey = Script.FromBytesUnsafe(witness.GetUnsafePush(witness.PushCount - 1));

                    // Stack items are remainder of stack.
                    int sizeWitnessStack = witness.PushCount - 1;

                    // Get the witness stack items.
                    var stack = new List <byte[]>();
                    for (int i = 0; i < sizeWitnessStack; i++)
                    {
                        stack.Add(witness.GetUnsafePush(i));
                    }

                    // Validate P2WSH script isn't larger than max length.
                    if (scriptPubKey.ToBytes(true).Length > MaxStandardP2wshScriptSize)
                    {
                        this.logger.LogTrace("(-)[P2WSH_SCRIPT_SIZE]:false");
                        return(false);
                    }

                    // Validate number items in witness stack isn't larger than max.
                    if (sizeWitnessStack > MaxStandardP2wshStackItems)
                    {
                        this.logger.LogTrace("(-)[P2WSH_STACK_ITEMS]:false");
                        return(false);
                    }

                    // Validate size of each of the witness stack items.
                    for (int j = 0; j < sizeWitnessStack; j++)
                    {
                        if (stack[j].Length > MaxStandardP2wshStackItemSize)
                        {
                            this.logger.LogTrace("(-)[P2WSH_STACK_ITEM_SIZE]:false");
                            return(false);
                        }
                    }
                }
            }

            return(true);
        }
 public abstract void RecordReceipt(HashHeightPair block, Script pubKeyScript, TxOut txOut, bool isCoinBase, long creationTime, uint256 outputTxId, int outputIndex, bool isChange);
예제 #19
0
		public void ReadWrite(BitcoinStream stream)
		{
			if(stream.Serializing)
			{
				uint o = (uint)(nHeight * 2 + (fCoinBase ? 1 : 0));
				stream.ReadWriteAsCompactVarInt(ref o);
				if(nHeight > 0)
					stream.ReadWriteAsCompactVarInt(ref nVersion);
				TxOutCompressor compressor = new TxOutCompressor(txout);
				stream.ReadWrite(ref compressor);
			}
			else
			{
				uint nCode = 0;
				stream.ReadWriteAsCompactVarInt(ref nCode);
				nHeight = nCode / 2;
				fCoinBase = (nCode & 1) != 0;
				if(nHeight > 0)
					stream.ReadWriteAsCompactVarInt(ref nVersion);
				TxOutCompressor compressor = new TxOutCompressor();
				stream.ReadWrite(ref compressor);
				txout = compressor.TxOut;
			}
		}
        public bool ProcessTransactions(IEnumerable <Transaction> transactions, HashHeightPair block, uint256 fixedTxId = null)
        {
            bool additions = false;

            // Convert relevant information in the block to information that can be joined to the wallet tables.
            IWalletTransactionLookup transactionsOfInterest = this.transactionsOfInterest;
            IWalletAddressLookup     addressesOfInterest    = this.addressesOfInterest;

            // Used for tracking address top-up requirements.
            var trackers = new Dictionary <TopUpTracker, TopUpTracker>();

            foreach (Transaction tx in transactions)
            {
                // Build temp.PrevOuts
                uint256 txId       = fixedTxId ?? tx.GetHash();
                bool    addSpendTx = false;

                for (int i = 0; i < tx.Inputs.Count; i++)
                {
                    TxIn txIn = tx.Inputs[i];

                    if (!transactionsOfInterest.Contains(txIn.PrevOut, out HashSet <AddressIdentifier> addresses))
                    {
                        continue;
                    }

                    // Record our outputs that are being spent.
                    foreach (AddressIdentifier address in addresses)
                    {
                        // TODO: Probably need to derive time explicitly from the block header. How were PoW transactions being handled here?
                        long time = 0;
                        if (tx is IPosTransactionWithTime posTx)
                        {
                            time = posTx.Time;
                        }

                        RecordSpend(block, txIn, address.ScriptPubKey, tx.IsCoinBase | tx.IsCoinStake, time, tx.TotalOut, txId, i);
                    }

                    additions  = true;
                    addSpendTx = true;
                }

                // Build temp.Outputs.
                for (int i = 0; i < tx.Outputs.Count; i++)
                {
                    TxOut txOut = tx.Outputs[i];

                    if (txOut.IsEmpty)
                    {
                        continue;
                    }

                    if (txOut.ScriptPubKey.ToBytes(true)[0] == (byte)OpcodeType.OP_RETURN)
                    {
                        continue;
                    }

                    foreach (Script pubKeyScript in this.GetDestinations(txOut.ScriptPubKey))
                    {
                        bool containsAddress = addressesOfInterest.Contains(pubKeyScript, out AddressIdentifier address);

                        // Paying to one of our addresses?
                        if (!addSpendTx && !containsAddress)
                        {
                            continue;
                        }

                        // Check if top-up is required.
                        if (containsAddress && address != null)
                        {
                            // Get the top-up tracker that applies to this account and address type.
                            ITopUpTracker tracker = this.GetTopUpTracker(address);
                            if (!tracker.IsWatchOnlyAccount)
                            {
                                // If an address inside the address buffer is being used then top-up the buffer.
                                while (address.AddressIndex >= tracker.NextAddressIndex)
                                {
                                    AddressIdentifier newAddress = tracker.CreateAddress();

                                    // Add the new address to our addresses of interest.
                                    addressesOfInterest.AddTentative(Script.FromHex(newAddress.ScriptPubKey),
                                                                     new AddressIdentifier()
                                    {
                                        WalletId     = newAddress.WalletId,
                                        AccountIndex = newAddress.AccountIndex,
                                        AddressType  = newAddress.AddressType,
                                        AddressIndex = newAddress.AddressIndex
                                    });
                                }
                            }
                        }

                        // TODO: Probably need to derive time explicitly from the block header. How were PoW transactions being handled here?
                        long time = 0;
                        if (tx is IPosTransactionWithTime posTx)
                        {
                            time = posTx.Time;
                        }

                        // Record outputs received by our wallets.
                        this.RecordReceipt(block, pubKeyScript, txOut, tx.IsCoinBase | tx.IsCoinStake, time, txId, i, containsAddress && address.AddressType == 1);

                        additions = true;

                        if (containsAddress)
                        {
                            transactionsOfInterest.AddTentative(new OutPoint(txId, i), address);
                        }
                    }
                }
            }

            return(additions);
        }
예제 #21
0
 public bool MatchPubKeyHash(TxOut output)
 {
     var id = template.ExtractScriptPubKeyParameters(output.ScriptPubKey);
     return (id == KeyId);
 }
예제 #22
0
 /// <summary>
 /// Initializes an instance of the <see cref="Vout"/> class.
 /// </summary>
 /// <param name="n">The index of the output.</param>
 /// <param name="txout">A <see cref="TxOut"/></param>
 /// <param name="network">The network where the transaction occured.</param>
 public Vout(int n, TxOut txout, Network network)
 {
     this.N            = n;
     this.Value        = txout.Value.ToDecimal(MoneyUnit.BTC);
     this.ScriptPubKey = new ScriptPubKey(txout.ScriptPubKey, network);
 }
예제 #23
0
        /// <summary>
        /// Creates a cold staking withdrawal <see cref="Transaction"/>.
        /// </summary>
        /// <remarks>
        /// Cold staking withdrawal is performed on the wallet that is in the role of the cold staking cold wallet.
        /// </remarks>
        /// <param name="walletTransactionHandler">The wallet transaction handler used to build the transaction.</param>
        /// <param name="receivingAddress">The address that will receive the withdrawal.</param>
        /// <param name="walletName">The name of the wallet in the role of cold wallet.</param>
        /// <param name="walletPassword">The wallet password.</param>
        /// <param name="amount">The amount to remove from cold staking.</param>
        /// <param name="feeAmount">The fee to pay for cold staking transaction withdrawal.</param>
        /// <returns>The <see cref="Transaction"/> for cold staking withdrawal.</returns>
        /// <exception cref="WalletException">Thrown if the receiving address is in a cold staking account in this wallet.</exception>
        /// <exception cref="ArgumentNullException">Thrown if the receiving address is invalid.</exception>
        internal Transaction GetColdStakingWithdrawalTransaction(IWalletTransactionHandler walletTransactionHandler, string receivingAddress,
                                                                 string walletName, string walletPassword, Money amount, Money feeAmount)
        {
            Guard.NotEmpty(receivingAddress, nameof(receivingAddress));
            Guard.NotEmpty(walletName, nameof(walletName));
            Guard.NotNull(amount, nameof(amount));
            Guard.NotNull(feeAmount, nameof(feeAmount));

            this.logger.LogTrace("({0}:'{1}',{2}:'{3}',{4}:'{5}',{6}:'{7}'",
                                 nameof(receivingAddress), receivingAddress,
                                 nameof(walletName), walletName,
                                 nameof(amount), amount,
                                 nameof(feeAmount), feeAmount
                                 );

            Wallet.Wallet wallet = this.GetWalletByName(walletName);

            // Get the cold staking account.
            HdAccount coldAccount = this.GetColdStakingAccount(wallet, true);

            if (coldAccount == null)
            {
                this.logger.LogTrace("(-)[COLDSTAKE_ACCOUNT_DOES_NOT_EXIST]");
                throw new WalletException("The cold wallet account does not exist.");
            }

            // Prevent reusing cold stake addresses as regular withdrawal addresses.
            if (coldAccount.ExternalAddresses.Concat(coldAccount.InternalAddresses).Select(a => a.Address.ToString()).Contains(receivingAddress))
            {
                this.logger.LogTrace("(-)[COLDSTAKE_INVALID_COLD_WALLET_ADDRESS_USAGE]");
                throw new WalletException("You can't send the money to a cold staking cold wallet account.");
            }

            HdAccount hotAccount = this.GetColdStakingAccount(wallet, false);

            if (hotAccount != null && hotAccount.ExternalAddresses.Concat(hotAccount.InternalAddresses).Select(a => a.Address.ToString()).Contains(receivingAddress))
            {
                this.logger.LogTrace("(-)[COLDSTAKE_INVALID_HOT_WALLET_ADDRESS_USAGE]");
                throw new WalletException("You can't send the money to a cold staking hot wallet account.");
            }

            // Send the money to the receiving address.
            Script destination = BitcoinAddress.Create(receivingAddress, wallet.Network).ScriptPubKey;

            // Create the transaction build context (used in BuildTransaction).
            var accountReference = new WalletAccountReference(walletName, coldAccount.Name);
            var context          = new TransactionBuildContext(wallet.Network)
            {
                AccountReference = accountReference,
                // Specify a dummy change address to prevent a change (internal) address from being created.
                // Will be changed after the transacton is built and before it is signed.
                ChangeAddress    = coldAccount.ExternalAddresses.First(),
                TransactionFee   = feeAmount,
                MinConfirmations = 0,
                Shuffle          = false,
                Recipients       = new[] { new Recipient {
                                               Amount = amount, ScriptPubKey = destination
                                           } }.ToList()
            };

            // Register the cold staking builder extension with the transaction builder.
            context.TransactionBuilder.Extensions.Add(new ColdStakingBuilderExtension(false));

            // Avoid script errors due to missing scriptSig.
            context.TransactionBuilder.StandardTransactionPolicy.ScriptVerify = null;

            // Build the transaction according to the settings recorded in the context.
            Transaction transaction = walletTransactionHandler.BuildTransaction(context);

            // Map OutPoint to UnspentOutputReference.
            Dictionary <OutPoint, UnspentOutputReference> mapOutPointToUnspentOutput = this.GetSpendableTransactionsInAccount(accountReference)
                                                                                       .ToDictionary(unspent => unspent.ToOutPoint(), unspent => unspent);

            // Set the cold staking scriptPubKey on the change output.
            TxOut changeOutput = transaction.Outputs.SingleOrDefault(output => (output.ScriptPubKey != destination) && (output.Value != 0));

            if (changeOutput != null)
            {
                // Find the largest input.
                TxIn largestInput = transaction.Inputs.OrderByDescending(input => mapOutPointToUnspentOutput[input.PrevOut].Transaction.Amount).Take(1).Single();

                // Set the scriptPubKey of the change output to the scriptPubKey of the largest input.
                changeOutput.ScriptPubKey = mapOutPointToUnspentOutput[largestInput.PrevOut].Transaction.ScriptPubKey;
            }

            // Add keys for signing inputs.
            foreach (TxIn input in transaction.Inputs)
            {
                UnspentOutputReference unspent = mapOutPointToUnspentOutput[input.PrevOut];
                context.TransactionBuilder.AddKeys(wallet.GetExtendedPrivateKeyForAddress(walletPassword, unspent.Address));
            }

            // Sign the transaction.
            context.TransactionBuilder.SignTransactionInPlace(transaction);

            this.logger.LogTrace("(-):'{0}'", transaction.GetHash());
            return(transaction);
        }
예제 #24
0
        public async Task <IActionResult> PostSignaturesAsync([FromQuery] string uniqueId, [FromQuery] long roundId, [FromBody] IDictionary <int, string> signatures)
        {
            if (roundId <= 0 ||
                signatures == null ||
                !signatures.Any() ||
                signatures.Any(x => x.Key < 0 || string.IsNullOrWhiteSpace(x.Value)) ||
                !ModelState.IsValid)
            {
                return(BadRequest());
            }

            (CcjRound round, Alice alice) = GetRunningRoundAndAliceOrFailureResponse(roundId, uniqueId, out IActionResult returnFailureResponse);
            if (returnFailureResponse != null)
            {
                return(returnFailureResponse);
            }

            // Check if Alice provided signature to all her inputs.
            if (signatures.Count != alice.Inputs.Count())
            {
                return(BadRequest("Alice did not provide enough witnesses."));
            }

            CcjRoundPhase phase = round.Phase;

            switch (phase)
            {
            case CcjRoundPhase.Signing:
            {
                using (await SigningLock.LockAsync())
                {
                    foreach (var signaturePair in signatures)
                    {
                        int       index   = signaturePair.Key;
                        WitScript witness = null;
                        try
                        {
                            witness = new WitScript(signaturePair.Value);
                        }
                        catch (Exception ex)
                        {
                            return(BadRequest($"Malformed witness is provided. Details: {ex.Message}"));
                        }
                        int maxIndex = round.UnsignedCoinJoin.Inputs.Count - 1;
                        if (maxIndex < index)
                        {
                            return(BadRequest($"Index out of range. Maximum value: {maxIndex}. Provided value: {index}"));
                        }

                        // Check duplicates.
                        if (round.SignedCoinJoin.Inputs[index].HasWitness())
                        {
                            return(BadRequest($"Input is already signed."));
                        }

                        // Verify witness.
                        var cjCopy = new Transaction(round.UnsignedCoinJoin.ToHex());
                        cjCopy.Inputs[index].WitScript = witness;
                        TxOut output = alice.Inputs.Single(x => x.OutPoint == cjCopy.Inputs[index].PrevOut).Output;
                        if (!Script.VerifyScript(output.ScriptPubKey, cjCopy, index, output.Value, ScriptVerify.Standard, SigHash.All))
                        {
                            return(BadRequest($"Invalid witness is provided."));
                        }

                        // Finally add it to our CJ.
                        round.SignedCoinJoin.Inputs[index].WitScript = witness;
                    }

                    alice.State = AliceState.SignedCoinJoin;

                    await round.BroadcastCoinJoinIfFullySignedAsync();
                }

                return(NoContent());
            }

            default:
            {
                return(Conflict($"CoinJoin can only be requested from Signing phase. Current phase: {phase}."));
            }
            }
        }
예제 #25
0
        public bool gastar(string txIdOrigem, string enderecoDestino)
        {
            // Private key da transação "Enviando pra mim mesmo1" que gastarei: mthg7QduM2ba3AJ5UwvQwvkuJDjLJsu9Rt
            string chavePrivada      = "cTQA9XcNUcS7CVtvAvz6BipKn5xzWTn3ppFTGCkEwe8QS9dVZPDw";
            var    bitcoinPrivateKey = new BitcoinSecret(chavePrivada);
            var    rede    = bitcoinPrivateKey.Network;
            var    address = bitcoinPrivateKey.GetAddress();

            Console.WriteLine("Endereço de origem dos fundos: " + address);

            //Consulta transação que gastarei (Um output de troco e um "Enviando pra mim mesmo1" que gastarei
            var client        = new QBitNinjaClient(rede);
            var transactionId = uint256.Parse(txIdOrigem);

            Console.WriteLine("Endereço direto da rede: " + transactionId);
            var transactionResponse = client.GetTransaction(transactionId).Result; //?



            //Verifica outpoints da transação que gastarei
            var      receivedCoins   = transactionResponse.ReceivedCoins;
            OutPoint outPointToSpend = null;

            foreach (var coin in receivedCoins)
            {
                Console.WriteLine(coin.TxOut.ScriptPubKey);
                Console.WriteLine(bitcoinPrivateKey.ScriptPubKey);
                if (coin.TxOut.ScriptPubKey == bitcoinPrivateKey.ScriptPubKey)
                {
                    outPointToSpend = coin.Outpoint;
                }
            }
            if (outPointToSpend == null)
            {
                throw new Exception("TxOut doesn't contain our ScriptPubKey");
            }
            Console.WriteLine("We want to spend {0}. outpoint:", outPointToSpend.N + 1);

            //Criando transação e adicionando o output que quero gastar no input da transação
            transacao = Transaction.Create(rede);
            transacao.Inputs.Add(new TxIn()
            {
                PrevOut = outPointToSpend
            });

            //Destinação
            var destino = BitcoinAddress.Create(enderecoDestino, rede);


            //Criação do Output
            TxOut txOut = new TxOut()
            {
                Value        = new Money(0.0004m, MoneyUnit.BTC),
                ScriptPubKey = destino.ScriptPubKey
            };

            TxOut trocoTxOut = new TxOut()
            {
                Value        = new Money(0.00053m, MoneyUnit.BTC),
                ScriptPubKey = bitcoinPrivateKey.ScriptPubKey
            };

            transacao.Outputs.Add(txOut);
            transacao.Outputs.Add(trocoTxOut);

            //Mensagem na transação
            var mensagem = "Olá Mundo!";
            var bytes    = Encoding.UTF8.GetBytes(mensagem);

            transacao.Outputs.Add(new TxOut()
            {
                Value        = Money.Zero,
                ScriptPubKey = TxNullDataTemplate.Instance.GenerateScriptPubKey(bytes)
            });

            Console.WriteLine("\n" + transacao);

            //Assinando a transação

            //Pegando pubkey script do endereço de origem
            transacao.Inputs[0].ScriptSig = bitcoinPrivateKey.ScriptPubKey;

            //assinando com a private key referente ao pubkey script da origem
            transacao.Sign(bitcoinPrivateKey, receivedCoins.ToArray());

            Console.WriteLine("\n" + transacao);


            //Propagar a transação...

            BroadcastResponse broadcastResponse = client.Broadcast(transacao).Result;

            if (!broadcastResponse.Success)
            {
                Console.Error.WriteLine("ErrorCode: " + broadcastResponse.Error.ErrorCode);
                Console.Error.WriteLine("Error message: " + broadcastResponse.Error.Reason);
            }
            else
            {
                Console.WriteLine("Success! You can check out the hash of the transaciton in any block explorer:");
                Console.WriteLine(transacao.GetHash());
                return(true);
            }

            return(false);
        }
예제 #26
0
        public async Task <IActionResult> PostInputsAsync([FromBody] InputsRequest request)
        {
            // Validate request.
            if (!ModelState.IsValid ||
                request == null ||
                string.IsNullOrWhiteSpace(request.BlindedOutputScriptHex) ||
                string.IsNullOrWhiteSpace(request.ChangeOutputAddress) ||
                request.Inputs == null ||
                !request.Inputs.Any() ||
                request.Inputs.Any(x => x.Input == default(TxoRef) ||
                                   x.Input.TransactionId == null ||
                                   string.IsNullOrWhiteSpace(x.Proof)))
            {
                return(BadRequest("Invalid request."));
            }

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

            using (await InputsLock.LockAsync())
            {
                CcjRound round = Coordinator.GetCurrentInputRegisterableRound();

                // Do more checks.
                try
                {
                    if (round.ContainsBlindedOutputScriptHex(request.BlindedOutputScriptHex, out _))
                    {
                        return(BadRequest("Blinded output has already been registered."));
                    }

                    BitcoinAddress changeOutputAddress;
                    try
                    {
                        changeOutputAddress = BitcoinAddress.Create(request.ChangeOutputAddress, Network);
                    }
                    catch (FormatException ex)
                    {
                        return(BadRequest($"Invalid ChangeOutputAddress. Details: {ex.Message}"));
                    }

                    var inputs = new HashSet <(OutPoint OutPoint, TxOut Output)>();

                    var alicesToRemove = new HashSet <Guid>();

                    foreach (InputProofModel inputProof in request.Inputs)
                    {
                        if (inputs.Any(x => x.OutPoint == inputProof.Input))
                        {
                            return(BadRequest("Cannot register an input twice."));
                        }
                        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();
                        if (Coordinator.UtxoReferee.BannedUtxos.TryGetValue(outpoint, out (int severity, DateTimeOffset timeOfBan)bannedElem))
                        {
                            int maxBan  = (int)TimeSpan.FromDays(30).TotalMinutes;
                            int banLeft = maxBan - (int)((DateTimeOffset.UtcNow - bannedElem.timeOfBan).TotalMinutes);
                            if (banLeft > 0)
                            {
                                return(BadRequest($"Input is banned from participation for {banLeft} minutes: {inputProof.Input.Index}:{inputProof.Input.TransactionId}."));
                            }
                            await Coordinator.UtxoReferee.UnbanAsync(outpoint);
                        }

                        GetTxOutResponse getTxOutResponse = await RpcClient.GetTxOutAsync(inputProof.Input.TransactionId, (int)inputProof.Input.Index, includeMempool : true);

                        // Check if inputs are unspent.
                        if (getTxOutResponse == null)
                        {
                            return(BadRequest("Provided input is not unspent."));
                        }

                        // Check if unconfirmed.
                        if (getTxOutResponse.Confirmations <= 0)
                        {
                            // If it spends a CJ then it may be acceptable to register.
                            if (!Coordinator.ContainsCoinJoin(inputProof.Input.TransactionId))
                            {
                                return(BadRequest("Provided input is neither confirmed, nor is from an unconfirmed coinjoin."));
                            }
                            // After 24 unconfirmed cj in the mempool dont't let unconfirmed coinjoin to be registered.
                            if (await Coordinator.IsUnconfirmedCoinJoinLimitReachedAsync())
                            {
                                return(BadRequest("Provided input is from an unconfirmed coinjoin, but the maximum number of unconfirmed coinjoins is reached."));
                            }
                        }

                        // 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.
                        bool validProof;
                        try
                        {
                            validProof = address.VerifyMessage(request.BlindedOutputScriptHex, inputProof.Proof);
                        }
                        catch (FormatException ex)
                        {
                            return(BadRequest($"Provided proof is invalid: {ex.Message}"));
                        }
                        if (!validProof)
                        {
                            return(BadRequest("Provided proof is invalid."));
                        }

                        inputs.Add((inputProof.Input.ToOutPoint(), txout));
                    }

                    // Check if inputs have enough coins.
                    Money inputSum        = inputs.Select(x => x.Output.Value).Sum();
                    Money networkFeeToPay = (inputs.Count() * round.FeePerInputs) + (2 * round.FeePerOutputs);
                    Money changeAmount    = inputSum - (round.Denomination + networkFeeToPay);
                    if (changeAmount < Money.Zero)
                    {
                        return(BadRequest($"Not enough inputs are provided. Fee to pay: {networkFeeToPay.ToString(false, true)} BTC. Round denomination: {round.Denomination.ToString(false, true)} BTC. Only provided: {inputSum.ToString(false, true)} BTC."));
                    }

                    // Make sure Alice checks work.
                    var alice = new Alice(inputs, networkFeeToPay, changeOutputAddress, request.BlindedOutputScriptHex);

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

                    // All checks are good. Sign.
                    byte[] blindedData;
                    try
                    {
                        blindedData = ByteHelpers.FromHex(request.BlindedOutputScriptHex);
                    }
                    catch
                    {
                        return(BadRequest("Invalid blinded output hex."));
                    }

                    byte[] signature = RsaKey.SignBlindedData(blindedData);

                    // Check if phase changed since.
                    if (round.Status != CcjRoundStatus.Running || round.Phase != CcjRoundPhase.InputRegistration)
                    {
                        return(base.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.RemoveAlicesIfInputsSpentAsync();

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

                    var resp = new InputsResponse
                    {
                        UniqueId = alice.UniqueId,
                        BlindedOutputSignature = signature,
                        RoundId = round.RoundId
                    };
                    return(Ok(resp));
                }
                catch (Exception ex)
                {
                    Logger.LogDebug <ChaumianCoinJoinController>(ex);
                    return(BadRequest(ex.Message));
                }
            }
        }
예제 #27
0
파일: Tracker.cs 프로젝트: xcrash/NBitcoin
			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);
			}
예제 #28
0
        public OfferInformation CheckBlindedFactors(BlindFactor[] blindFactors, FeeRate feeRate)
        {
            if (blindFactors == null)
            {
                throw new ArgumentNullException(nameof(blindFactors));
            }
            if (blindFactors.Length != Parameters.RealPuzzleCount)
            {
                throw new ArgumentException("Expecting " + Parameters.RealPuzzleCount + " blind factors");
            }
            AssertState(SolverServerStates.WaitingBlindFactor);
            Puzzle unblindedPuzzle = null;
            int    y = 0;

            for (int i = 0; i < Parameters.RealPuzzleCount; i++)
            {
                var solvedPuzzle = InternalState.SolvedPuzzles[i];
                var unblinded    = new Puzzle(Parameters.ServerKey, solvedPuzzle.Puzzle).Unblind(blindFactors[i]);
                if (unblindedPuzzle == null)
                {
                    unblindedPuzzle = unblinded;
                }
                else if (unblinded != unblindedPuzzle)
                {
                    throw new PuzzleException("Invalid blind factor");
                }
                y++;
            }

            InternalState.FulfillKey = new Key();

            Transaction dummy = new Transaction();

            dummy.AddInput(new TxIn(InternalState.EscrowedCoin.Outpoint));
            dummy.Inputs[0].ScriptSig = new Script(
                Op.GetPushOp(TrustedBroadcastRequest.PlaceholderSignature),
                Op.GetPushOp(TrustedBroadcastRequest.PlaceholderSignature),
                Op.GetPushOp(InternalState.EscrowedCoin.Redeem.ToBytes())
                );
            dummy.Inputs[0].Witnessify();
            dummy.AddOutput(new TxOut(InternalState.EscrowedCoin.Amount, new Key().ScriptPubKey.Hash));

            var offerTransactionFee = feeRate.GetFee(dummy.GetVirtualSize());


            var escrow            = InternalState.EscrowedCoin;
            var escrowInformation = EscrowScriptPubKeyParameters.GetFromCoin(InternalState.EscrowedCoin);
            var redeem            = new OfferScriptPubKeyParameters
            {
                Hashes     = InternalState.SolvedPuzzles.Select(p => p.SolutionKey.GetHash()).ToArray(),
                FulfillKey = InternalState.FulfillKey.PubKey,
                Expiration = escrowInformation.LockTime,
                RedeemKey  = escrowInformation.Initiator
            }.ToScript();
            var txOut = new TxOut(escrow.Amount - offerTransactionFee, redeem.WitHash.ScriptPubKey.Hash);

            InternalState.OfferCoin = new Coin(escrow.Outpoint, txOut).ToScriptCoin(redeem);
            InternalState.Status    = SolverServerStates.WaitingFulfillment;
            return(new OfferInformation
            {
                FulfillKey = InternalState.FulfillKey.PubKey,
                Fee = offerTransactionFee
            });
        }
        /// <inheritdoc />
        public override async Task RunAsync(RuleContext context)
        {
            this.Logger.LogTrace("()");

            Block            block = context.ValidationContext.BlockToValidate;
            ChainedHeader    index = context.ValidationContext.ChainedHeaderToValidate;
            DeploymentFlags  flags = context.Flags;
            UnspentOutputSet view  = (context as UtxoRuleContext).UnspentOutputSet;

            long  sigOpsCost  = 0;
            Money fees        = Money.Zero;
            var   checkInputs = new List <Task <bool> >();

            for (int txIndex = 0; txIndex < block.Transactions.Count; txIndex++)
            {
                Transaction tx = block.Transactions[txIndex];

                if (!context.SkipValidation)
                {
                    if (!this.IsProtocolTransaction(tx))
                    {
                        if (!view.HaveInputs(tx))
                        {
                            this.Logger.LogTrace("(-)[BAD_TX_NO_INPUT]");
                            ConsensusErrors.BadTransactionMissingInput.Throw();
                        }

                        var prevheights = new int[tx.Inputs.Count];
                        // Check that transaction is BIP68 final.
                        // BIP68 lock checks (as opposed to nLockTime checks) must
                        // be in ConnectBlock because they require the UTXO set.
                        for (int j = 0; j < tx.Inputs.Count; j++)
                        {
                            prevheights[j] = (int)view.AccessCoins(tx.Inputs[j].PrevOut.Hash).Height;
                        }

                        if (!tx.CheckSequenceLocks(prevheights, index, flags.LockTimeFlags))
                        {
                            this.Logger.LogTrace("(-)[BAD_TX_NON_FINAL]");
                            ConsensusErrors.BadTransactionNonFinal.Throw();
                        }
                    }

                    // GetTransactionSignatureOperationCost counts 3 types of sigops:
                    // * legacy (always),
                    // * p2sh (when P2SH enabled in flags and excludes coinbase),
                    // * witness (when witness enabled in flags and excludes coinbase).
                    sigOpsCost += this.GetTransactionSignatureOperationCost(tx, view, flags);
                    if (sigOpsCost > this.ConsensusOptions.MaxBlockSigopsCost)
                    {
                        this.Logger.LogTrace("(-)[BAD_BLOCK_SIG_OPS]");
                        ConsensusErrors.BadBlockSigOps.Throw();
                    }

                    if (!this.IsProtocolTransaction(tx))
                    {
                        this.CheckInputs(tx, view, index.Height);
                        fees += view.GetValueIn(tx) - tx.TotalOut;
                        var txData = new PrecomputedTransactionData(tx);
                        for (int inputIndex = 0; inputIndex < tx.Inputs.Count; inputIndex++)
                        {
                            TxIn  input          = tx.Inputs[inputIndex];
                            int   inputIndexCopy = inputIndex;
                            TxOut txout          = view.GetOutputFor(input);
                            var   checkInput     = new Task <bool>(() =>
                            {
                                var checker             = new TransactionChecker(tx, inputIndexCopy, txout.Value, txData);
                                var ctx                 = new ScriptEvaluationContext(this.Parent.Network);
                                ctx.ScriptVerify        = flags.ScriptFlags;
                                bool verifyScriptResult = ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker);

                                if (verifyScriptResult == false)
                                {
                                    this.Logger.LogTrace("Verify script for transaction '{0}' failed, ScriptSig = '{1}', ScriptPubKey = '{2}', script evaluation error = '{3}'", tx.GetHash(), input.ScriptSig, txout.ScriptPubKey, ctx.Error);
                                }

                                return(verifyScriptResult);
                            });
                            checkInput.Start();
                            checkInputs.Add(checkInput);
                        }
                    }
                }

                this.UpdateCoinView(context, tx);
            }

            if (!context.SkipValidation)
            {
                this.CheckBlockReward(context, fees, index.Height, block);

                foreach (Task <bool> checkInput in checkInputs)
                {
                    if (await checkInput.ConfigureAwait(false))
                    {
                        continue;
                    }

                    this.Logger.LogTrace("(-)[BAD_TX_SCRIPT]");
                    ConsensusErrors.BadTransactionScriptError.Throw();
                }
            }
            else
            {
                this.Logger.LogTrace("BIP68, SigOp cost, and block reward validation skipped for block at height {0}.", index.Height);
            }

            this.Logger.LogTrace("(-)");
        }
예제 #30
0
        static void Main()
        {
            //========================================================================================
            //Section. Transaction

            //(Mastering Bitcoin) Transactions are the most important part of the bitcoin system. Everything else in bitcoin is designed to ensure that transactions can be created, propagated on the network, validated, and finally added to the global ledger of transactions(the blockchain).Transactions are data structures that encode the transfer of value between participants in the bitcoin system.Each transaction is a public entry in bitcoin’s blockchain, the global double-entry bookkeeping ledger.
            //A transaction may have no recipient, or it may have several.The same can be said for senders! On the Blockchain, the sender and recipient are always abstracted with a ScriptPubKey, as we demonstrated in previous chapters.
            //If you use Bitcoin Core, your Transactions tab will show the transaction, like this:
            //Picture depiction:



            //For now we are interested in the Transaction ID. In this case, it is:
            //f13dc48fb035bbf0a6e989a26b3ecb57b84f85e0836e777d6edf60d87a4a2d94


            //Note: The Transaction ID is defined by SHA256(SHA256(txbytes))


            //Note: Do NOT use the Transaction ID to handle unconfirmed transactions. The Transaction ID can be manipulated before it is confirmed.This is known as “Transaction Malleability.”


            //You can review or exmine a specify transaction on a block explorer like Blockchain.info:
            //https://blockchain.info/tx/f13dc48fb035bbf0a6e989a26b3ecb57b84f85e0836e777d6edf60d87a4a2d94


            //But as a developer you will probably want a service that is easier to query and parse.
            //As a C# developer and an NBitcoin user Nicolas Dorier's QBit Ninja will definitely be your best choice. It is an open source web service API to query the blockchain and for tracking wallets.
            //QBit Ninja depends on NBitcoin.Indexer which relies on Microsoft Azure Storage.C# developers are expected to use the NuGet client package instead of developing a wrapper around this API.

            //If you go to:
            //http://api.qbit.ninja/transactions/f13dc48fb035bbf0a6e989a26b3ecb57b84f85e0836e777d6edf60d87a4a2d94
            //you will see the raw bytes of your transaction.
            //Picture depiction:



            //You can parse the transaction from a Hexadecimal with the following code:
            //Transaction tx = new Transaction("0100000...");



            //Quickly close the tab, before it scares you away. QBit Ninja queries the API and parses the information so go ahead and install QBitNinja.Client NuGet package.
            //Picture depiction:



            //Query the transaction by ID:
            //Create a client.
            QBitNinjaClient client = new QBitNinjaClient(Network.Main);
            //Parse transaction ID to NBitcoin.uint256 so the client can eat it.
            var transactionId = uint256.Parse("f13dc48fb035bbf0a6e989a26b3ecb57b84f85e0836e777d6edf60d87a4a2d94");

            Console.WriteLine($"transactionId: {transactionId}");
            //Query the transaction.
            QBitNinja.Client.Models.GetTransactionResponse transactionResponse = client.GetTransaction(transactionId).Result;
            Console.WriteLine($"transactionResponse: {transactionResponse}");
            //The type of transactionResponse is GetTransactionResponse. It lives under QBitNinja.Client.Models namespace.

            //You can get NBitcoin.Transaction type from it:
            NBitcoin.Transaction transaction = transactionResponse.Transaction;
            Console.WriteLine($"transaction: {transaction}");

            //Let's see an example getting back the transaction ID with both classes(transactionResponse, transaction):
            Console.WriteLine($"transactionResponse.TransactionId: {transactionResponse.TransactionId}");
            //Output:
            //f13dc48fb035bbf0a6e989a26b3ecb57b84f85e0836e777d6edf60d87a4a2d94
            Console.WriteLine($"transaction.GetHash(): {transaction.GetHash()}");
            //Output:
            //f13dc48fb035bbf0a6e989a26b3ecb57b84f85e0836e777d6edf60d87a4a2d94



            //            GetTransactionResponse has additional information about the transaction like the value and scriptPubKey of the inputs being spent in the transaction.
            //The relevant parts for now are the inputs and outputs.
            //You can see there is only one output in our transaction. 13.19683492 bitcoins are sent to that ScriptPubKey.



            //The relevant parts for now are the inputs and outputs.
            //You can see there is only one output in our transaction.
            //13.19683492 bitcoins are sent to that ScriptPubKey.



            //Examine the RECEIVED COINS by using QBitNinja's GetTransactionResponse class
            List <ICoin> receivedCoins = transactionResponse.ReceivedCoins;

            Console.WriteLine("=====Examine the RECEIVED COINS by using QBitNinja's GetTransactionResponse class=====");
            foreach (var coin in receivedCoins)
            {
                Money amount = (Money)coin.Amount;

                Console.WriteLine($"amount.ToDecimal(MoneyUnit.BTC): {amount.ToDecimal(MoneyUnit.BTC)}");
                var paymentScript = coin.TxOut.ScriptPubKey;
                //Print each ScriptPubKey by executing foreach loop.
                Console.WriteLine(paymentScript);
                //Get a Bitcoin address.
                //Recall we can get a Bitcoin address from a ScriptPubKey by specifying a network type by processing backwards.
                var bitcoinAddressWithQG = paymentScript.GetDestinationAddress(Network.Main);
                Console.WriteLine($"bitcoinAddressWithQG: {bitcoinAddressWithQG}");
                //Output:
                //1HfbwN6Lvma9eDsv7mdwp529tgiyfNr7jc
            }

            //Examine the RECEIVED COINS by using NBitcoin's Transaction class
            var outputs = transaction.Outputs;

            Console.WriteLine("=====Examine the received coins by using NBitcoin's Transaction class=====");
            foreach (TxOut output in outputs)
            {
                Coin  coin   = new Coin(transaction, output);
                Money amount = coin.Amount;

                Console.WriteLine(amount.ToDecimal(MoneyUnit.BTC));
                var paymentScript = coin.GetScriptCode();
                //It's the ScriptPubKey.
                Console.WriteLine(paymentScript);
                var bitcoinAddressWithNT = paymentScript.GetDestinationAddress(Network.Main);
                Console.WriteLine($"bitcoinAddressWithNT: {bitcoinAddressWithNT}");
                Console.WriteLine();
            }

            //We have written out some informations about the RECEIVED COINS using QBitNinja's GetTransactionResponse class and NBitcoins's Transaction class.



            //Exercise: Write out the same informations about the SPENT COINS using QBitNinja's GetTransactionResponse class!
            //Examine the SPENT COINS by using QBitNinja's GetTransactionResponse class
            List <ICoin> spentCoins = transactionResponse.SpentCoins;

            Console.WriteLine("=====Examine the SPENT COINS by using QBitNinja's GetTransactionResponse class=====");
            foreach (var coin in spentCoins)
            {
                Money amount = (Money)coin.Amount;

                Console.WriteLine(amount.ToDecimal(MoneyUnit.BTC));
                var paymentScript = coin.TxOut.ScriptPubKey;
                Console.WriteLine(paymentScript);  // It's the ScriptPubKey
                var address = paymentScript.GetDestinationAddress(Network.Main);
                Console.WriteLine(address);
                Console.WriteLine();
            }

            //Examine the SPENT COINS by using NBitcoin's Transaction class
            Console.WriteLine("=====Examine the SPENT COINS by using NBitcoin's Transaction class=====");
            foreach (var coin in spentCoins)
            {
                TxOut previousOutput = coin.TxOut;
                Money amount         = previousOutput.Value;

                Console.WriteLine(amount.ToDecimal(MoneyUnit.BTC));
                var paymentScript = previousOutput.ScriptPubKey;
                Console.WriteLine(paymentScript);  // It's the ScriptPubKey
                var address = paymentScript.GetDestinationAddress(Network.Main);
                Console.WriteLine(address);
                Console.WriteLine();
            }



            //===============================================================================
            //Examine the inputs.
            //Now let's examine the inputs. If you look at them, you will notice a previous output is referenced. Each input shows you which previous out has been spent in order to fund this transaction.

            Console.WriteLine("=====Examine the inputs=====");
            var inputs = transaction.Inputs;

            foreach (TxIn input in inputs)
            {
                //Get each previous output which is referenced to another transaction, from each input.
                OutPoint previousOutpoint = input.PrevOut;
                Console.WriteLine($"previousOutpoint: {previousOutpoint}");
                //Hash of the previous OutPoint.
                Console.WriteLine($"previousOutpoint.Hash: {previousOutpoint.Hash}");
                //Index number of the previous OutPoint that will be spent in the current transaction.
                Console.WriteLine($"previousOutpoint.N: {previousOutpoint.N}");
                Console.WriteLine();
            }

            //The terms TxOut, Output and out are synonymous.
            //Not to be confused with OutPoint, but more on this later.

            //In summary, the TxOut represents an amount of bitcoin and a ScriptPubKey representing a recipient.



            //As above illustration, let's create a TxOut with 21 bitcoins from the first ScriptPubKey in our current transaction:
            Money twentyOneBtc = new Money(21, MoneyUnit.BTC);
            //Get the first ScriptPubKey to specify a recipient.
            var scriptPubKey = transaction.Outputs.First().ScriptPubKey;
            //Create a new TxOut with passing twentyOneBtc, scriptPubKey into a constructor arguments.
            TxOut txOut = new TxOut(twentyOneBtc, scriptPubKey);

            Console.WriteLine($"Value in txOut: {txOut.Value}");
            //Output:
            //21.00000000
            Console.WriteLine($"ScriptPubKey in txOut: {txOut.ScriptPubKey}");
            //Output:
            //OP_DUP OP_HASH160 b6cefbb855cabf6ee45598f518a98011c22961aa OP_EQUALVERIFY OP_CHECKSIG



            //Every TxOut is uniquely addressed at the blockchain level by the ID of the transaction which includes TxOut(s) and TxOut(s) index number inside this transaction. We call such reference an Outpoint.
            //Picture depiction:
            //Transaction ID + Index number of TxOut => OutPoint


            //For example, the OutPoint of the TxOut with 13.19683492 BTC in our transaction is:
            //(f13dc48fb035bbf0a6e989a26b3ecb57b84f85e0836e777d6edf60d87a4a2d94, 0).
            //As you can see, OutPoint is composed of 2 values, the transaction ID including a TxOut and index number of the TxOut in the transaction.


            OutPoint firstOutPoint = receivedCoins.First().Outpoint;

            Console.WriteLine($"firstOutPoint.Hash {firstOutPoint.Hash}");
            //Output:
            //f13dc48fb035bbf0a6e989a26b3ecb57b84f85e0836e777d6edf60d87a4a2d94
            Console.WriteLine($"firstOutPoint.N: {firstOutPoint.N}");
            //Output:
            //0


            //Now let’s take a closer look at the inputs(aka TxIn) of the transaction:
            //Picture depiction:
            //OutPoint + ScriptSig => TxIn



            //The TxIn is composed of the "OutPoint" of the TxOut being spent in the current transaction including this TxIn, which originates in another previous transaction and the "ScriptSig". We can see the ScriptSig as the “Proof of Ownership”. In our transaction there are actually 9 inputs.

            Console.WriteLine(transaction.Inputs.Count);
            //Output:
            //9


            //With a transaction ID of the previous transaction including OutPoint, we can review the information associated with that transaction.
            OutPoint firstPreviousOutPoint = transaction.Inputs.First().PrevOut;

            Console.WriteLine($"firstPreviousOutPoint {firstPreviousOutPoint}");
            //Output:
            //4788c5ef8ffd0463422bcafdfab240f5bf0be690482ceccde79c51cfce209edd-0
            var firstPreviousTransactionResponse =
                client.GetTransaction(firstPreviousOutPoint.Hash).Result;

            Console.WriteLine($"firstPreviousTransactionResponse.TransactionId {firstPreviousTransactionResponse.TransactionId}");
            //Output:
            //4788c5ef8ffd0463422bcafdfab240f5bf0be690482ceccde79c51cfce209edd
            Console.WriteLine($"firstPreviousTransactionResponse.IsCoinbase: {firstPreviousTransactionResponse.IsCoinbase}");
            //Output:
            //False
            NBitcoin.Transaction firstPreviousTransaction = firstPreviousTransactionResponse.Transaction;
            Console.WriteLine($"firstPreviousTransaction {firstPreviousTransaction}");

            //CoinBase transaction is located in the first transaction, also known as Tx0, in a block. And it includes the newly mined coins and a transaction that the newly mied coins are sent to the miner.


            //We could continue to trace the transaction IDs back in this manner until we reach a coinbase transaction, the transaction including the newly mined coin by a miner.

            //Exercise: Follow the first input of this transaction and its ancestor transactions until you find a coinbase transaction!
            //Hint: After a few minutes and 30-40 transactions, I gave up tracing back.
            //Yes, you've guessed right, it is not the most efficient way to do this, but a good exercise.


            //while (firstPreviousTransactionResponse.IsCoinbase == false)
            //{
            //    Console.WriteLine(firstPreviousTransaction.GetHash());

            //    firstPreviousOutPoint = firstPreviousTransaction.Inputs.First().PrevOut;
            //    firstPreviousTransactionResponse = client.GetTransaction(firstPreviousOutPoint.Hash).Result;
            //    firstPreviousTransaction = firstPreviousTransactionResponse.Transaction;
            //}



            //In our example, the outputs were for a total of 13.19703492 BTC.
            Money spentAmount = Money.Zero;

            foreach (var spentCoin in spentCoins)
            {
                spentAmount = (Money)spentCoin.Amount.Add(spentAmount);
            }
            Console.WriteLine($"spentAmount.ToDecimal(MoneyUnit.BTC): {spentAmount.ToDecimal(MoneyUnit.BTC)}");
            //Output:
            //13.19703492

            //In this transaction, 13.19683492 BTC were received.
            Money receivedAmount = Money.Zero;

            foreach (var receivedCoin in receivedCoins)
            {
                receivedAmount = (Money)receivedCoin.Amount.Add(receivedAmount);
            }
            Console.WriteLine($"receivedAmount.ToDecimal(MoneyUnit.BTC): {receivedAmount.ToDecimal(MoneyUnit.BTC)}");
            //Output:
            //13.19683492



            //Exercise: Get the total received amount, as I have been done with the spent amount.

            //var inputs = transaction.Inputs;
            //foreach (TxIn input in inputs)
            //{
            //    uint256 previousTransactionId = input.PrevOut.Hash;
            //    GetTransactionResponse previousTransactionResponse = client.GetTransaction(previousTransactionId).Result;

            //    NBitcoin.Transaction previousTransaction = previousTransactionResponse.Transaction;

            //    var previousTransactionOutputs = previousTransaction.Outputs;
            //    foreach (TxOut previousTransactionOutput in previousTransactionOutputs)
            //    {
            //        Money amount = previousTransactionOutput.Value;

            //        Console.WriteLine(amount.ToDecimal(MoneyUnit.BTC));
            //        var paymentScript = previousTransactionOutput.ScriptPubKey;
            //        Console.WriteLine(paymentScript);  // It's the ScriptPubKey
            //        var address = paymentScript.GetDestinationAddress(Network.Main);
            //        Console.WriteLine(address);
            //        Console.WriteLine();
            //    }
            //}



            //That means 0.0002 BTC(or 13.19703492 - 13.19683492) is not accounted for!The difference between the inputs and outputs are called Transaction Fees or Miner’s Fees.This is the money that the miner collects for including a given transaction in a block.

            Console.WriteLine((spentAmount - receivedAmount).ToDecimal(MoneyUnit.BTC));
            Console.WriteLine(spentAmount.ToDecimal(MoneyUnit.BTC) - receivedAmount.ToDecimal(MoneyUnit.BTC));
            var fee = transaction.GetFee(spentCoins.ToArray());

            Console.WriteLine($"fee : {fee}");



            //You should note that a coinbase transaction is the only transaction whose value of output are superior to the value of input. This effectively corresponds to a coin creation. So by definition there is no fee in a coinbase transaction.The coinbase transaction is the first transaction of every block.
            //The consensus rules enforce that the sum of output's value in the coinbase transaction does not exceed the sum of transaction fees in the block plus the mining reward.

            Console.ReadLine();
        }
예제 #31
0
        public static void BitcoinPractice()
        {
            Key privateKey = new Key();

            //PubKey publicKey = privateKey.PubKey;
            //Console.WriteLine(publicKey);

            //Console.WriteLine(publicKey.GetAddress(Network.Main));
            //Console.WriteLine(publicKey.GetAddress(Network.TestNet));

            //var publicKeyHash = publicKey.Hash;
            //Console.WriteLine(publicKeyHash);

            //var mainNetAddress = publicKeyHash.GetAddress(Network.Main);
            //var testNetAddress = publicKeyHash.GetAddress(Network.TestNet);
            //Console.WriteLine(mainNetAddress);
            //Console.WriteLine(testNetAddress);

            //var publicKeyHash2 = new KeyId("14836dbe7f38c5ac3d49e8d790af808a4ee9edcf");  // I don't get what's going on here; where does this string come from, what does KeyId do??

            //var mainNetAddress2 = publicKeyHash2.GetAddress(Network.Main);
            //var testNetAddress2 = publicKeyHash2.GetAddress(Network.TestNet);
            //Console.WriteLine(mainNetAddress2.ScriptPubKey);
            //Console.WriteLine(testNetAddress2.ScriptPubKey);

            //var paymentScript = publicKeyHash2.ScriptPubKey;
            //var sameMainNetAddress = paymentScript.GetDestinationAddress(Network.Main); // going backwards from script and network to get address
            //Console.WriteLine(sameMainNetAddress);
            //Console.WriteLine(mainNetAddress2 == sameMainNetAddress);

            //var samePublicKeyHash = (KeyId)paymentScript.GetDestination(); // going backwards to get same result as line 32 from the script
            //Console.WriteLine(publicKeyHash2 == samePublicKeyHash);
            //var sameMainNetAddress2 = new BitcoinPubKeyAddress(samePublicKeyHash, Network.Main); //
            //Console.WriteLine(mainNetAddress2 == sameMainNetAddress2);

            //BitcoinSecret mainNetPrivateKey = privateKey.GetBitcoinSecret(Network.Main); // generate WIF Wallet Import Format of privKey
            //Console.WriteLine(mainNetPrivateKey);
            //bool wifIsBitcoinSecret = mainNetPrivateKey == privateKey.GetWif(Network.Main);
            //Console.WriteLine(wifIsBitcoinSecret);


            //// Exercise:
            //// 1. Generate a private key on mainnet and note it
            //Key myKey = new Key();
            //BitcoinSecret myKeyWIF = myKey.GetBitcoinSecret(Network.Main);
            //Console.WriteLine(myKeyWIF); // saved offline so it isn't pushed to github

            //// 2. Get the corresponding address
            //PubKey myPubKey = myKey.PubKey;
            //var myBitcoinAddress = myPubKey.GetAddress(Network.Main);
            //Console.WriteLine(myBitcoinAddress);

            //// or...
            //var myPubKeyHash = myPubKey.Hash;
            //var myBitcoinAddress2 = myPubKeyHash.GetAddress(Network.Main);
            //Console.WriteLine(myBitcoinAddress2); // saved offline for privacy

            // 3. Send bitcoins to it. As much as you can afford to lose, so it will keep you focused and motivated to get them back
            // during the following lessons.
            // DONE!!!

            // Transaction tx = new Transaction("");
            QBitNinjaClient        client              = new QBitNinjaClient(Network.Main);
            var                    transactionId       = NBitcoin.uint256.Parse(""); // remove tx id and hex before push
            GetTransactionResponse transactionResponse = client.GetTransaction(transactionId).Result;

            NBitcoin.Transaction transaction = transactionResponse.Transaction;

            Console.WriteLine(transactionResponse.TransactionId);
            Console.WriteLine(transaction.GetHash());

            List <ICoin> receivedCoins = transactionResponse.ReceivedCoins;

            foreach (var coin in receivedCoins)
            {
                Money amount = (Money)coin.Amount; // amount of each output in the tx to the address we created above (RECEIVED COINS)

                Console.WriteLine(amount.ToDecimal(MoneyUnit.BTC));
                var paymentScript = coin.TxOut.ScriptPubKey;
                Console.WriteLine(paymentScript); // ScriptPubKey of each output in the tx
                var address = paymentScript.GetDestinationAddress(Network.Main);
                Console.WriteLine(address);       // address of each output in the tx
            }

            // Exercise: Write out the same info about the SPENT COINS using QBitNinja's GetTransactionResponse class
            List <ICoin> spentCoins = transactionResponse.SpentCoins;

            foreach (var spentCoin in spentCoins)
            {
                Money amountSpent = (Money)spentCoin.Amount;

                Console.WriteLine(amountSpent.ToDecimal(MoneyUnit.BTC));
                var paymentScript = spentCoin.TxOut.ScriptPubKey; // not sure if this is working... doesn't look like a script, see below
                Console.WriteLine(paymentScript);                 // 0 367003b...  // ??? '[index of output from prev tx spent in current tx] [hash of prev tx]' == 'Outpoint' ???
                var address = paymentScript.GetDestinationAddress(Network.Main);
                Console.WriteLine(address);                       //
            }

            var outputs = transaction.Outputs;  // this does same thing as ReceivedCoins foreach loop above

            foreach (TxOut output in outputs)
            {
                Money amount = output.Value;

                Console.WriteLine(amount.ToDecimal(MoneyUnit.BTC));
                var paymentScript = output.ScriptPubKey;
                Console.WriteLine(paymentScript);
                var address = paymentScript.GetDestinationAddress(Network.Main);
                Console.WriteLine(address);
            }

            var inputs = transaction.Inputs;

            foreach (TxIn input in inputs)
            {
                OutPoint previousOutpoint = input.PrevOut;
                Console.WriteLine(previousOutpoint.Hash);
                Console.WriteLine(previousOutpoint.N);
            }

            Money twentyOneBitcoin = new Money(21, MoneyUnit.BTC);
            var   scriptPubKey     = transaction.Outputs.First().ScriptPubKey;
            TxOut txOut            = new TxOut(twentyOneBitcoin, scriptPubKey);

            OutPoint firstOutPoint = receivedCoins.First().Outpoint;

            Console.WriteLine(firstOutPoint.Hash);
            Console.WriteLine(firstOutPoint.N);

            Console.WriteLine(transaction.Inputs.Count);

            OutPoint firstPreviousOutPoint    = transaction.Inputs.First().PrevOut;
            var      firstPreviousTransaction = client.GetTransaction(firstPreviousOutPoint.Hash).Result.Transaction;

            Console.WriteLine(firstPreviousTransaction.IsCoinBase);

            Money spentAmount = Money.Zero;

            foreach (var spentCoin in spentCoins)
            {
                spentAmount = (Money)spentCoin.Amount.Add(spentAmount);
            }
            Console.WriteLine(spentAmount.ToDecimal(MoneyUnit.BTC));

            // Exercise: Get the total received amount
            Money receivedAmount = Money.Zero;

            foreach (var receivedCoin in receivedCoins)
            {
                receivedAmount = (Money)receivedCoin.Amount.Add(receivedAmount);
            }
            Console.WriteLine(receivedAmount.ToDecimal(MoneyUnit.BTC));

            var fee = transaction.GetFee(spentCoins.ToArray());

            Console.WriteLine(fee);
            Console.WriteLine(fee == (spentAmount - receivedAmount)); // True
        }
예제 #32
0
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public BuildTransactionResult BuildTransaction(
            PaymentIntent payments,
            Func <FeeRate> feeRateFetcher,
            IEnumerable <TxoRef> allowedInputs = null)
        {
            payments = Guard.NotNull(nameof(payments), payments);

            long totalAmount = payments.TotalAmount.Satoshi;

            if (totalAmount < 0 || totalAmount > Constants.MaximumNumberOfSatoshis)
            {
                throw new ArgumentOutOfRangeException($"{nameof(payments)}.{nameof(payments.TotalAmount)} sum cannot be smaller than 0 or greater than {Constants.MaximumNumberOfSatoshis}.");
            }

            // Get allowed coins to spend.
            List <SmartCoin> allowedSmartCoinInputs; // Inputs that can be used to build the transaction.

            if (allowedInputs != null)               // If allowedInputs are specified then select the coins from them.
            {
                if (!allowedInputs.Any())
                {
                    throw new ArgumentException($"{nameof(allowedInputs)} is not null, but empty.");
                }

                allowedSmartCoinInputs = AllowUnconfirmed
                                        ? Coins.Where(x => !x.Unavailable && allowedInputs.Any(y => y.TransactionId == x.TransactionId && y.Index == x.Index)).ToList()
                                        : Coins.Where(x => !x.Unavailable && x.Confirmed && allowedInputs.Any(y => y.TransactionId == x.TransactionId && y.Index == x.Index)).ToList();

                // Add those that have the same script, because common ownership is already exposed.
                // But only if the user didn't click the "max" button. In this case he'd send more money than what he'd think.
                if (payments.ChangeStrategy != ChangeStrategy.AllRemainingCustom)
                {
                    var allScripts = allowedSmartCoinInputs.Select(x => x.ScriptPubKey).ToHashSet();
                    foreach (var coin in Coins.Where(x => !x.Unavailable && !allowedSmartCoinInputs.Any(y => x.TransactionId == y.TransactionId && x.Index == y.Index)))
                    {
                        if (!(AllowUnconfirmed || coin.Confirmed))
                        {
                            continue;
                        }

                        if (allScripts.Contains(coin.ScriptPubKey))
                        {
                            allowedSmartCoinInputs.Add(coin);
                        }
                    }
                }
            }
            else
            {
                allowedSmartCoinInputs = AllowUnconfirmed ? Coins.Where(x => !x.Unavailable).ToList() : Coins.Where(x => !x.Unavailable && x.Confirmed).ToList();
            }

            // Get and calculate fee
            Logger.LogInfo("Calculating dynamic transaction fee...");

            TransactionBuilder builder = Network.CreateTransactionBuilder();

            builder.SetCoinSelector(new SmartCoinSelector(allowedSmartCoinInputs));
            builder.AddCoins(allowedSmartCoinInputs.Select(c => c.GetCoin()));

            foreach (var request in payments.Requests.Where(x => x.Amount.Type == MoneyRequestType.Value))
            {
                var amountRequest = request.Amount;

                builder.Send(request.Destination, amountRequest.Amount);
                if (amountRequest.SubtractFee)
                {
                    builder.SubtractFees();
                }
            }

            HdPubKey changeHdPubKey = null;

            if (payments.TryGetCustomRequest(out DestinationRequest custChange))
            {
                var changeScript = custChange.Destination.ScriptPubKey;
                changeHdPubKey = KeyManager.GetKeyForScriptPubKey(changeScript);

                var changeStrategy = payments.ChangeStrategy;
                if (changeStrategy == ChangeStrategy.Custom)
                {
                    builder.SetChange(changeScript);
                }
                else if (changeStrategy == ChangeStrategy.AllRemainingCustom)
                {
                    builder.SendAllRemaining(changeScript);
                }
                else
                {
                    throw new NotSupportedException(payments.ChangeStrategy.ToString());
                }
            }
            else
            {
                KeyManager.AssertCleanKeysIndexed(isInternal: true);
                KeyManager.AssertLockedInternalKeysIndexed(14);
                changeHdPubKey = KeyManager.GetKeys(KeyState.Clean, true).RandomElement();

                builder.SetChange(changeHdPubKey.P2wpkhScript);
            }

            FeeRate feeRate = feeRateFetcher();

            builder.SendEstimatedFees(feeRate);

            var psbt = builder.BuildPSBT(false);

            var spentCoins = psbt.Inputs.Select(txin => allowedSmartCoinInputs.First(y => y.GetOutPoint() == txin.PrevOut)).ToArray();

            var realToSend = payments.Requests
                             .Select(t =>
                                     (label: t.Label,
                                      destination: t.Destination,
                                      amount: psbt.Outputs.FirstOrDefault(o => o.ScriptPubKey == t.Destination.ScriptPubKey)?.Value))
                             .Where(i => i.amount != null);

            if (!psbt.TryGetFee(out var fee))
            {
                throw new InvalidOperationException("Impossible to get the fees of the PSBT, this should never happen.");
            }
            Logger.LogInfo($"Fee: {fee.Satoshi} Satoshi.");

            var vSize = builder.EstimateSize(psbt.GetOriginalTransaction(), true);

            Logger.LogInfo($"Estimated tx size: {vSize} vbytes.");

            // Do some checks
            Money totalSendAmountNoFee = realToSend.Sum(x => x.amount);

            if (totalSendAmountNoFee == Money.Zero)
            {
                throw new InvalidOperationException("The amount after subtracting the fee is too small to be sent.");
            }
            Money totalSendAmount = totalSendAmountNoFee + fee;

            Money totalOutgoingAmountNoFee;

            if (changeHdPubKey is null)
            {
                totalOutgoingAmountNoFee = totalSendAmountNoFee;
            }
            else
            {
                totalOutgoingAmountNoFee = realToSend.Where(x => !changeHdPubKey.ContainsScript(x.destination.ScriptPubKey)).Sum(x => x.amount);
            }
            decimal totalOutgoingAmountNoFeeDecimal = totalOutgoingAmountNoFee.ToDecimal(MoneyUnit.BTC);
            // Cannot divide by zero, so use the closest number we have to zero.
            decimal totalOutgoingAmountNoFeeDecimalDivisor = totalOutgoingAmountNoFeeDecimal == 0 ? decimal.MinValue : totalOutgoingAmountNoFeeDecimal;
            decimal feePc = (100 * fee.ToDecimal(MoneyUnit.BTC)) / totalOutgoingAmountNoFeeDecimalDivisor;

            if (feePc > 1)
            {
                Logger.LogInfo($"The transaction fee is {totalOutgoingAmountNoFee:0.#}% of your transaction amount.{Environment.NewLine}"
                               + $"Sending:\t {totalSendAmount.ToString(fplus: false, trimExcessZero: true)} BTC.{Environment.NewLine}"
                               + $"Fee:\t\t {fee.Satoshi} Satoshi.");
            }
            if (feePc > 100)
            {
                throw new InvalidOperationException($"The transaction fee is more than twice the transaction amount: {feePc:0.#}%.");
            }

            if (spentCoins.Any(u => !u.Confirmed))
            {
                Logger.LogInfo("Unconfirmed transaction is spent.");
            }

            // Build the transaction
            Logger.LogInfo("Signing transaction...");
            // It must be watch only, too, because if we have the key and also hardware wallet, we do not care we can sign.

            Transaction tx = null;

            if (KeyManager.IsWatchOnly)
            {
                tx = psbt.GetGlobalTransaction();
            }
            else
            {
                IEnumerable <ExtKey> signingKeys = KeyManager.GetSecrets(Password, spentCoins.Select(x => x.ScriptPubKey).ToArray());
                builder = builder.AddKeys(signingKeys.ToArray());
                builder.SignPSBT(psbt);
                psbt.Finalize();
                tx = psbt.ExtractTransaction();

                var checkResults = builder.Check(tx).ToList();
                if (!psbt.TryGetEstimatedFeeRate(out FeeRate actualFeeRate))
                {
                    throw new InvalidOperationException("Impossible to get the fee rate of the PSBT, this should never happen.");
                }

                // Manually check the feerate, because some inaccuracy is possible.
                var sb1 = feeRate.SatoshiPerByte;
                var sb2 = actualFeeRate.SatoshiPerByte;
                if (Math.Abs(sb1 - sb2) > 2)                 // 2s/b inaccuracy ok.
                {
                    // So it'll generate a transactionpolicy error thrown below.
                    checkResults.Add(new NotEnoughFundsPolicyError("Fees different than expected"));
                }
                if (checkResults.Count > 0)
                {
                    throw new InvalidTxException(tx, checkResults);
                }
            }

            if (KeyManager.MasterFingerprint is HDFingerprint fp)
            {
                foreach (var coin in spentCoins)
                {
                    var rootKeyPath = new RootedKeyPath(fp, coin.HdPubKey.FullKeyPath);
                    psbt.AddKeyPath(coin.HdPubKey.PubKey, rootKeyPath, coin.ScriptPubKey);
                }
            }

            var label = SmartLabel.Merge(payments.Requests.Select(x => x.Label).Concat(spentCoins.Select(x => x.Label)));
            var outerWalletOutputs = new List <SmartCoin>();
            var innerWalletOutputs = new List <SmartCoin>();

            for (var i = 0U; i < tx.Outputs.Count; i++)
            {
                TxOut output   = tx.Outputs[i];
                var   anonset  = (tx.GetAnonymitySet(i) + spentCoins.Min(x => x.AnonymitySet)) - 1;              // Minus 1, because count own only once.
                var   foundKey = KeyManager.GetKeyForScriptPubKey(output.ScriptPubKey);
                var   coin     = new SmartCoin(tx.GetHash(), i, output.ScriptPubKey, output.Value, tx.Inputs.ToTxoRefs().ToArray(), Height.Unknown, tx.RBF, anonset, isLikelyCoinJoinOutput: false, pubKey: foundKey);
                label = SmartLabel.Merge(label, coin.Label);                 // foundKey's label is already added to the coinlabel.

                if (foundKey is null)
                {
                    outerWalletOutputs.Add(coin);
                }
                else
                {
                    innerWalletOutputs.Add(coin);
                }
            }

            foreach (var coin in outerWalletOutputs.Concat(innerWalletOutputs))
            {
                var foundPaymentRequest = payments.Requests.FirstOrDefault(x => x.Destination.ScriptPubKey == coin.ScriptPubKey);

                // If change then we concatenate all the labels.
                if (foundPaymentRequest is null)                 // Then it's autochange.
                {
                    coin.Label = label;
                }
                else
                {
                    coin.Label = SmartLabel.Merge(coin.Label, foundPaymentRequest.Label);
                }

                var foundKey = KeyManager.GetKeyForScriptPubKey(coin.ScriptPubKey);
                foundKey?.SetLabel(coin.Label);                 // The foundkeylabel has already been added previously, so no need to concatenate.
            }

            Logger.LogInfo($"Transaction is successfully built: {tx.GetHash()}.");
            var sign = !KeyManager.IsWatchOnly;
            var spendsUnconfirmed = spentCoins.Any(c => !c.Confirmed);

            return(new BuildTransactionResult(new SmartTransaction(tx, Height.Unknown), psbt, spendsUnconfirmed, sign, fee, feePc, outerWalletOutputs, innerWalletOutputs, spentCoins));
        }
        /// <summary>
        /// Whether transaction inputs are standard.
        /// Check for standard transaction types.
        /// </summary>
        /// <seealso>https://github.com/bitcoin/bitcoin/blob/febf3a856bcfb8fef2cb4ddcb8d1e0cab8a22580/src/policy/policy.cpp#L156</seealso>
        /// <param name="tx">Transaction to verify.</param>
        /// <param name="mapInputs">Map of previous transactions that have outputs we're spending.</param>
        /// <returns>Whether all inputs (scriptSigs) use only standard transaction forms.</returns>
        private bool AreInputsStandard(Network network, Transaction tx, MempoolCoinView mapInputs)
        {
            if (tx.IsCoinBase)
            {
                this.logger.LogTrace("(-)[IS_COINBASE]:true");
                return(true); // Coinbases don't use vin normally.
            }

            for (int i = 0; i < tx.Inputs.Count; i++)
            {
                TxIn           txin     = tx.Inputs[i];
                TxOut          prev     = mapInputs.GetOutputFor(txin);
                ScriptTemplate template = network.StandardScriptsRegistry.GetTemplateFromScriptPubKey(prev.ScriptPubKey);
                if (template == null) // i.e. the TX_NONSTANDARD case
                {
                    this.logger.LogTrace("(-)[BAD_SCRIPT_TEMPLATE]:false");
                    return(false);
                }

                /* Check transaction inputs to mitigate two potential denial-of-service attacks:
                 *
                 * 1. scriptSigs with extra data stuffed into them, not consumed by scriptPubKey (or P2SH script)
                 * 2. P2SH scripts with a crazy number of expensive CHECKSIG/CHECKMULTISIG operations
                 *
                 * Why bother? To avoid denial-of-service attacks; an attacker can submit a standard HASH... OP_EQUAL transaction,
                 * which will get accepted into blocks. The redemption script can be anything; an attacker could use a very
                 * expensive-to-check-upon-redemption script like:
                 *   DUP CHECKSIG DROP ... repeated 100 times... OP_1
                 */
                if (template.Type == TxOutType.TX_SCRIPTHASH)
                {
                    // Convert the scriptSig into a stack, so we can inspect the redeemScript.
                    var ctx = new ScriptEvaluationContext(this.network)
                    {
                        ScriptVerify = ScriptVerify.None
                    };

                    if (!ctx.EvalScript(txin.ScriptSig, tx, i)) // TODO: Check the semantics of SigVersion::BASE from original code
                    {
                        return(false);
                    }

                    // TODO: Investigate why IsEmpty is failing to return true when there is nothing on the stack. It is possible that nowhere else in the codebase is using IsEmpty on an IEnumerable
                    if (ctx.Stack.IsEmpty() || ctx.Stack.Count == 0)
                    {
                        return(false);
                    }

                    // Get redeemScript from stack.
                    var redeemScript = new Script(ctx.Stack.Top(-1));

                    // TODO: Move this into a network-specific rule so that it only applies to Strax (the Cirrus validator already allows non-standard transactions)
                    if (!redeemScript.ToOps().Select(o => o.Code).Contains(OpcodeType.OP_FEDERATION))
                    {
                        if (redeemScript.GetSigOpCount(true) > MaxP2SHSigOps)
                        {
                            this.logger.LogTrace("(-)[SIG_OP_MAX]:false");
                            return(false);
                        }
                    }
                }
            }

            return(true);
        }
        /// <summary>
        /// Checks if block signature is valid.
        /// </summary>
        /// <param name="block">The block.</param>
        /// <returns><c>true</c> if the signature is valid, <c>false</c> otherwise.</returns>
        private bool CheckBlockSignature(SmartContractPosBlock block)
        {
            this.Logger.LogTrace("()");

            if (BlockStake.IsProofOfWork(block))
            {
                bool res = block.BlockSignature.IsEmpty();
                this.Logger.LogTrace("(-)[POW]:{0}", res);
                return(res);
            }

            if (block.BlockSignature.IsEmpty())
            {
                this.Logger.LogTrace("(-)[EMPTY]:false");
                return(false);
            }

            TxOut txout = block.Transactions[1].Outputs[1];

            if (PayToPubkeyTemplate.Instance.CheckScriptPubKey(txout.ScriptPubKey))
            {
                PubKey pubKey = PayToPubkeyTemplate.Instance.ExtractScriptPubKeyParameters(txout.ScriptPubKey);
                bool   res    = pubKey.Verify(block.GetHash(), new ECDSASignature(block.BlockSignature.Signature));
                this.Logger.LogTrace("(-)[P2PK]:{0}", res);
                return(res);
            }

            // Block signing key also can be encoded in the nonspendable output.
            // This allows to not pollute UTXO set with useless outputs e.g. in case of multisig staking.

            List <Op> ops = txout.ScriptPubKey.ToOps().ToList();

            if (!ops.Any()) // script.GetOp(pc, opcode, vchPushValue))
            {
                this.Logger.LogTrace("(-)[NO_OPS]:false");
                return(false);
            }

            if (ops.ElementAt(0).Code != OpcodeType.OP_RETURN) // OP_RETURN)
            {
                this.Logger.LogTrace("(-)[NO_OP_RETURN]:false");
                return(false);
            }

            if (ops.Count < 2) // script.GetOp(pc, opcode, vchPushValue)
            {
                this.Logger.LogTrace("(-)[NO_SECOND_OP]:false");
                return(false);
            }

            byte[] data = ops.ElementAt(1).PushData;
            if (!ScriptEvaluationContext.IsCompressedOrUncompressedPubKey(data))
            {
                this.Logger.LogTrace("(-)[NO_PUSH_DATA]:false");
                return(false);
            }

            bool verifyRes = new PubKey(data).Verify(block.GetHash(), new ECDSASignature(block.BlockSignature.Signature));

            this.Logger.LogTrace("(-):{0}", verifyRes);
            return(verifyRes);
        }
예제 #35
0
        private static void PlaySplit()
        {
            var scan = new Key(TestUtils.ParseHex("cc411aab02edcd3bccf484a9ba5280d4a774e6f81eac8ebec9cb1c2e8f73020a"));
            var addr = new BitcoinStealthAddress("waPYjXyrTrvXjZHmMGdqs9YTegpRDpx97H5G3xqLehkgyrrZKsxGCmnwKexpZjXTCskUWwYywdUvrZK7L2vejeVZSYHVns61gm8VfU", Network.TestNet);

            var sender = new BitcoinSecret("cRjSUV1LqN2F8MsGnLE2JKfCP75kbWGFRroNQeXHC429jqVFgmW3", Network.TestNet);
            var tx = new Transaction();
            tx.Version = 1;

            RPCClient client = RPCClientTests.CreateRPCClient();
            var coins = client.ListUnspent();
            foreach(var unspent in coins)
            {
                tx.Inputs.Add(new TxIn(unspent.OutPoint));
            }
            var amount = coins.Select(c => c.Amount).Sum();

            var perOut = (long)(amount.Satoshi / 13);

            while(amount > 0)
            {
                var toSend = Math.Min(perOut, (long)amount);
                amount -= toSend;

                var tout = new TxOut(toSend, sender.GetAddress());
                if(!tout.IsDust)
                    tx.Outputs.Add(tout);
            }

            tx.SignAll(sender);
            client.SendRawTransaction(tx);
        }
예제 #36
0
 public PaymentOutput(TxOut txOut)
 {
     Amount = txOut.Value;
     Script = txOut.ScriptPubKey;
 }
예제 #37
0
 public bool MatchScriptHash(TxOut output)
 {
     ScriptId id = template.ExtractScriptPubKeyParameters(output.ScriptPubKey);
     return (id == ScriptId);
 }
예제 #38
0
        public PuzzleSolution SignVoucher(
            [ModelBinder(BinderType = typeof(TumblerParametersModelBinder))]
            ClassicTumblerParameters tumblerId,
            [FromBody] SignVoucherRequest request)
        {
            if (tumblerId == null)
            {
                throw new ArgumentNullException("tumblerId");
            }
            if (request.UnsignedVoucher == null)
            {
                throw new ActionResultException(BadRequest("Missing UnsignedVoucher"));
            }
            if (request.MerkleProof == null)
            {
                throw new ActionResultException(BadRequest("Missing MerkleProof"));
            }
            if (request.Transaction == null)
            {
                throw new ActionResultException(BadRequest("Missing Transaction"));
            }
            if (request.ClientEscrowKey == null)
            {
                throw new ActionResultException(BadRequest("Missing ClientEscrowKey"));
            }

            if (request.MerkleProof.PartialMerkleTree
                .GetMatchedTransactions()
                .FirstOrDefault() != request.Transaction.GetHash() || !request.MerkleProof.Header.CheckProofOfWork())
            {
                throw new ActionResultException(BadRequest("invalid-merkleproof"));
            }

            var confirmations = Services.BlockExplorerService.GetBlockConfirmations(request.MerkleProof.Header.GetHash());

            if ((confirmations < Parameters.CycleGenerator.FirstCycle.SafetyPeriodDuration))
            {
                throw new ActionResultException(BadRequest("not-enough-confirmation"));
            }

            var transaction = request.Transaction;

            if (transaction.Outputs.Count > 2)
            {
                throw new ActionResultException(BadRequest("invalid-transaction"));
            }

            var cycle  = GetCycle(request.Cycle);
            var height = Services.BlockExplorerService.GetCurrentHeight();

            if (!cycle.IsInPhase(CyclePhase.ClientChannelEstablishment, height))
            {
                throw new ActionResultException(BadRequest("invalid-phase"));
            }


            var key = Repository.GetKey(cycle.Start, request.KeyReference);

            var expectedEscrow = new EscrowScriptPubKeyParameters(request.ClientEscrowKey, key.PubKey, cycle.GetClientLockTime());

            var expectedTxOut = new TxOut(Parameters.Denomination + Parameters.Fee, expectedEscrow.ToScript().Hash);
            var escrowedCoin  =
                transaction
                .Outputs
                .AsCoins()
                .Where(c => c.TxOut.Value == expectedTxOut.Value &&
                       c.TxOut.ScriptPubKey == expectedTxOut.ScriptPubKey)
                .Select(c => c.ToScriptCoin(expectedEscrow.ToScript()))
                .FirstOrDefault();

            if (escrowedCoin == null)
            {
                throw new ActionResultException(BadRequest("invalid-transaction"));
            }

            try
            {
                var solverServerSession = new SolverServerSession(Runtime.TumblerKey, Parameters.CreateSolverParamaters());
                solverServerSession.ConfigureEscrowedCoin(escrowedCoin, key);

                Services.BlockExplorerService.Track(escrowedCoin.ScriptPubKey);
                if (!Services.BlockExplorerService.TrackPrunedTransaction(request.Transaction, request.MerkleProof))
                {
                    throw new ActionResultException(BadRequest("invalid-merkleproof"));
                }

                if (!Repository.MarkUsedNonce(cycle.Start, new uint160(key.PubKey.Hash.ToBytes())))
                {
                    throw new ActionResultException(BadRequest("invalid-transaction"));
                }
                Repository.Save(cycle.Start, solverServerSession);
                Logs.Tumbler.LogInformation($"Cycle {cycle.Start} Proof of Escrow signed for " + transaction.GetHash());

                var correlation = GetCorrelation(solverServerSession);
                Tracker.AddressCreated(cycle.Start, TransactionType.ClientEscrow, escrowedCoin.ScriptPubKey, correlation);
                Tracker.TransactionCreated(cycle.Start, TransactionType.ClientEscrow, request.Transaction.GetHash(), correlation);
                var solution = request.UnsignedVoucher.WithRsaKey(Runtime.VoucherKey.PubKey).Solve(Runtime.VoucherKey);
                return(solution);
            }
            catch (PuzzleException)
            {
                throw new ActionResultException(BadRequest("invalid-transaction"));
            }
        }
예제 #39
0
		private void ClearUnused(Func<TxOut, bool> belongsToCoins)
		{
			for(int i = 0 ; i < vout.Count ; i++)
			{
				var o = vout[i];
				if(o.ScriptPubKey.IsUnspendable || !belongsToCoins(o))
				{
					vout[i] = new TxOut();
				}
			}
			Cleanup();
		}
예제 #40
0
        public ScriptCoinModel OpenChannel(
            [ModelBinder(BinderType = typeof(TumblerParametersModelBinder))]
            ClassicTumblerParameters tumblerId,
            [FromBody] OpenChannelRequest request)
        {
            if (tumblerId == null)
            {
                throw new ArgumentNullException("tumblerId");
            }
            var height = Services.BlockExplorerService.GetCurrentHeight();
            var cycle  = GetCycle(request.CycleStart);

            if (!cycle.IsInPhase(CyclePhase.TumblerChannelEstablishment, height))
            {
                throw new ActionResultException(BadRequest("invalid-phase"));
            }
            var fee = Services.FeeService.GetFeeRate();

            try
            {
                if (!Parameters.VoucherKey.Verify(request.Signature, NBitcoin.Utils.ToBytes((uint)request.CycleStart, true), request.Nonce))
                {
                    throw new ActionResultException(BadRequest("incorrect-voucher"));
                }
                if (!Repository.MarkUsedNonce(request.CycleStart, request.Nonce))
                {
                    throw new ActionResultException(BadRequest("nonce-already-used"));
                }

                var escrowKey = new Key();

                var escrow = new EscrowScriptPubKeyParameters();
                escrow.LockTime  = cycle.GetTumblerLockTime();
                escrow.Receiver  = request.EscrowKey;
                escrow.Initiator = escrowKey.PubKey;

                Logs.Tumbler.LogInformation($"Cycle {cycle.Start} Asked to open channel");
                var txOut              = new TxOut(Parameters.Denomination, escrow.ToScript().Hash);
                var tx                 = Services.WalletService.FundTransaction(txOut, fee);
                var correlation        = escrow.GetCorrelation();
                var escrowTumblerLabel = $"Cycle {cycle.Start} Tumbler Escrow";
                Services.BlockExplorerService.Track(txOut.ScriptPubKey);

                Tracker.AddressCreated(cycle.Start, TransactionType.TumblerEscrow, txOut.ScriptPubKey, correlation);
                Tracker.TransactionCreated(cycle.Start, TransactionType.TumblerEscrow, tx.GetHash(), correlation);
                Logs.Tumbler.LogInformation($"Cycle {cycle.Start} Channel created " + tx.GetHash());

                var coin = tx.Outputs.AsCoins().First(o => o.ScriptPubKey == txOut.ScriptPubKey && o.TxOut.Value == txOut.Value);

                var session = new PromiseServerSession(Parameters.CreatePromiseParamaters());
                var redeem  = Services.WalletService.GenerateAddress();
                session.ConfigureEscrowedCoin(coin.ToScriptCoin(escrow.ToScript()), escrowKey, redeem.ScriptPubKey);
                Repository.Save(cycle.Start, session);

                Services.BroadcastService.Broadcast(tx);

                var redeemTx = session.CreateRedeemTransaction(fee);
                Tracker.AddressCreated(cycle.Start, TransactionType.TumblerRedeem, redeem.ScriptPubKey, correlation);
                Services.TrustedBroadcastService.Broadcast(cycle.Start, TransactionType.TumblerRedeem, correlation, redeemTx);
                return(new ScriptCoinModel(session.EscrowedCoin));
            }
            catch (PuzzleException)
            {
                throw new ActionResultException(BadRequest("incorrect-voucher"));
            }
            catch (NotEnoughFundsException ex)
            {
                Logs.Tumbler.LogInformation(ex.Message);
                throw new ActionResultException(BadRequest("tumbler-insufficient-funds"));
            }
        }
예제 #41
0
 private bool Match(TxOut txout, StealthPayment[] payments)
 {
     return payments.Any(p=>p.SpendableScript.Same(txout.ScriptPubKey) && !txout.IsDust);
 }
예제 #42
0
 public bool MatchScriptHash(TxOut output)
 {
     var key = template.ExtractScriptPubKeyParameters(output.ScriptPubKey);
     return key != null && (key.ID == PubKey.ID);
 }