public string DepositBtc(string btcPubKey, string recipient, decimal amount) { var address = new BitcoinPubKeyAddress(btcPubKey, Network.TestNet); //var address = new BitcoinSecret(btcPubKey); var txOperations = QClient.GetBalance(dest: address, unspentOnly: true).Result.Operations; var coins = new List <Coin>(); foreach (var op in txOperations) { op.ReceivedCoins.ForEach(c => coins.Add((Coin)c)); } coins.Sort(delegate(Coin x, Coin y) { return(-x.Amount.CompareTo(y.Amount)); }); var coinSum = 0m; for (int i = 0; coinSum < amount; i++) { coinSum += coins[i].Amount.ToDecimal(MoneyUnit.BTC); if (coinSum >= amount) { coins.RemoveRange(i + 1, coins.Count - (i + 1)); } } var builder = new TransactionBuilder(); var destination = new BitcoinPubKeyAddress(recipient, Network.TestNet); NBitcoin.Transaction tx = builder .AddCoins(coins) //.AddKeys(testAddress) .Send(destination, Money.Coins(amount)) .SetChange(address) .SendFees(Money.Coins(0.0001m)) .BuildTransaction(sign: false); tx.Outputs.Add(new TxOut { Value = Money.Zero, ScriptPubKey = TxNullDataTemplate.Instance.GenerateScriptPubKey(Encoding.UTF8.GetBytes(recipient)) }); foreach (var input in tx.Inputs) { input.ScriptSig = address.ScriptPubKey; } var txhash = tx.ToHex(); if (builder.Verify(tx)) { var broadcastResult = QClient.Broadcast(tx).Result; } return(tx.ToHex()); }
/// <inheritdoc /> public void ProcessTransaction(Transaction transaction, int?blockHeight = null, Block block = null) { Guard.NotNull(transaction, nameof(transaction)); var hash = transaction.GetHash(); this.logger.LogTrace($"transaction received - hash: {hash}, coin: {this.coinType}"); // load the keys for lookup if they are not loaded yet. if (this.keysLookup == null) { this.LoadKeysLookup(); } // check the outputs foreach (TxOut utxo in transaction.Outputs) { // check if the outputs contain one of our addresses if (this.keysLookup.TryGetValue(utxo.ScriptPubKey, out HdAddress pubKey)) { this.AddTransactionToWallet(transaction.ToHex(), hash, transaction.Time, transaction.Outputs.IndexOf(utxo), utxo.Value, utxo.ScriptPubKey, blockHeight, block); } } // check the inputs - include those that have a reference to a transaction containing one of our scripts and the same index foreach (TxIn input in transaction.Inputs.Where(txIn => this.keysLookup.Values.Distinct().SelectMany(v => v.Transactions).Any(trackedTx => trackedTx.Id == txIn.PrevOut.Hash && trackedTx.Index == txIn.PrevOut.N))) { TransactionData tTx = this.keysLookup.Values.Distinct().SelectMany(v => v.Transactions).Single(trackedTx => trackedTx.Id == input.PrevOut.Hash && trackedTx.Index == input.PrevOut.N); // find the script this input references var keyToSpend = this.keysLookup.First(v => v.Value.Transactions.Contains(tTx)).Key; // get the details of the outputs paid out. IEnumerable <TxOut> paidoutto = transaction.Outputs.Where(o => { // if script is empty ignore it if (o.IsEmpty) { return(false); } var found = this.keysLookup.TryGetValue(o.ScriptPubKey, out HdAddress addr); // include the keys we don't hold if (!found) { return(true); } // include the keys we do hold but that are for receiving // addresses (which would mean the user paid itself). return(!addr.IsChangeAddress()); }); this.AddSpendingTransactionToWallet(transaction.ToHex(), hash, transaction.Time, paidoutto, tTx.Id, tTx.Index, blockHeight, block); } }
public static void Spend(this Transaction txn, Action <string> a) { var txnReportRequest = new HTTPRequest( new Uri(TxnBroadcastEndpoint), HTTPMethods.Post, (txnR, txnResponse) => { try { var response = JsonConvert.DeserializeAnonymousType(txnResponse.DataAsText, new { Success = false, R = "", Message = "" }); if (!response.Success) { Debug.LogError("Spend response was unsuccessful - " + response.Message); Debug.LogError(txnResponse.DataAsText); throw new SpendUTXOException(response.Message); } var hashRegex = new Regex(@"^([A-Fa-f0-9]{64})$"); if (hashRegex.IsMatch(response.R)) { a(response.R); } else { Debug.LogError("Failed spending utxo, return hash wasn't an hash"); Debug.LogError(txnResponse.DataAsText); throw new SpendUTXOException(txnResponse.DataAsText); } } catch (Exception e) { if (txnResponse != null) { Debug.LogError("Fatal error: " + txnResponse.DataAsText); } else { Debug.LogError("Failed to spend transaction"); Debug.LogError(txnR.Exception.Message); } Debug.LogError($"{e.GetType()}: {e.Message}"); ModalDialog.Instance.CallbackYes.AddListener(() => { SceneManager.LoadScene("Main"); }); ModalDialog.Instance.Show("Failed to spend UTXO", $"{e.GetType()}: {e.Message}", "Ok"); //Debug.LogError(JsonConvert.SerializeObject(txn)); //Debug.LogError($"{{\"tx\":\"{txn.ToHex()}\"}}"); } }); txnReportRequest.AddHeader("Content-Type", "application/json"); txnReportRequest.RawData = Encoding.UTF8.GetBytes($"{{\"tx\":\"{txn.ToHex()}\"}}"); txnReportRequest.Send(); }
public async Task BroadcastAsync(Transaction tx) { if(tx == null) throw new ArgumentNullException("tx"); var jsonTx = new JObject(); jsonTx["hex"] = tx.ToHex(); var content = new StringContent(jsonTx.ToString(), Encoding.UTF8, "application/json"); using(var response = await Client.PostAsync(BlockrAddress + BroadcastPath, content).ConfigureAwait(false)) { var result = await response.Content.ReadAsStringAsync().ConfigureAwait(false); var json = JObject.Parse(result); var status = json["status"]; var code = json["code"]; if(status != null && (status.ToString() == "error" || status.ToString() == "fail")) { throw new BlockrException(json); } } }
public UnsignedTransaction SendTransactionToExchage(dynamic transactionDto, string toAddress, string privateKey, string network, string token) { try { var btcnetwork = GetNetwork(network); Endpoint endpoint = Endpoint.BtcTest3; if (network.ToUpper() == "MAIN") { endpoint = Endpoint.BtcMain; } Blockcypher blockCypherLib = new Blockcypher(token, endpoint); BitcoinSecret secret = new BitcoinSecret(privateKey, btcnetwork); NBitcoin.Transaction tx = NBitcoin.Transaction.Create(btcnetwork); TxIn input = new TxIn { PrevOut = new OutPoint(new uint256(transactionDto.TransactionID), transactionDto.Vout), ScriptSig = secret.GetAddress(ScriptPubKeyType.Legacy).ScriptPubKey }; tx.Inputs.Add(input); TxOut output = new TxOut(); Money fee = Money.Coins(transactionDto.DefaultFees.GetValueOrDefault()); Money totalAmount = Money.Coins(transactionDto.Amount); output.Value = totalAmount - fee; BitcoinAddress toaddress = BitcoinAddress.Create(toAddress, btcnetwork); output.ScriptPubKey = toaddress.ScriptPubKey; tx.Outputs.Add(output); var coins = tx.Inputs.Select(txin => new Coin(txin.PrevOut, new TxOut { ScriptPubKey = txin.ScriptSig })); tx.Sign(secret, coins.ToArray()); var response = blockCypherLib.PushTransaction(tx.ToHex()).Result; return(response); } catch (Exception ex) { throw new Exception(); } }
public static string GetHexSignedTransaction(TransactionToSign txToSign) { var walletPrivateKey = ConfigurationManager.AppSettings[METACO_ENV_WALLET_PRIVATE_KEY_HEX_NAME]; var key = new Key(Encoders.Hex.DecodeData(walletPrivateKey)); var scriptPubKey = PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(key.PubKey); var tx = new Transaction(Encoders.Hex.DecodeData(txToSign.Raw)); foreach(var inputsToSign in txToSign.InputsToSign) { var sigHash = tx.GetSignatureHash(scriptPubKey, inputsToSign.Index); var sig = key.Sign(sigHash); var txSign = new TransactionSignature(sig, SigHash.All); var inputScript = PayToPubkeyHashTemplate.Instance.GenerateScriptSig(txSign, key.PubKey); tx.Inputs[inputsToSign.Index].ScriptSig = inputScript; Assert.True(Script.VerifyScript(scriptPubKey, tx, inputsToSign.Index)); } return tx.ToHex(); }
public TransactionModel(NBitcoin.Transaction trx) { hex = trx?.ToHex(); }
public string getHexTransaction() { return(transaction.ToHex()); }
/// <inheritdoc /> public (string hex, uint256 transactionId, Money fee) BuildTransaction(string walletName, string accountName, string password, string destinationAddress, Money amount, string feeType, int minConfirmations) { if (amount == Money.Zero) { throw new WalletException($"Cannot send transaction with 0 {this.coinType}"); } // get the wallet and the account Wallet wallet = this.GetWalletByName(walletName); HdAccount account = wallet.AccountsRoot.Single(a => a.CoinType == this.coinType).GetAccountByName(accountName); // get a list of transactions outputs that have not been spent var spendableTransactions = account.GetSpendableTransactions().ToList(); // remove whats under min confirmations var currentHeight = this.chain.Height; spendableTransactions = spendableTransactions.Where(s => currentHeight - s.BlockHeight >= minConfirmations).ToList(); // get total spendable balance in the account. var balance = spendableTransactions.Sum(t => t.Amount); // make sure we have enough funds if (balance < amount) { throw new WalletException("Not enough funds."); } // calculate which addresses needs to be used as well as the fee to be charged var calculationResult = this.CalculateFees(spendableTransactions, amount); // get extended private key var privateKey = Key.Parse(wallet.EncryptedSeed, password, wallet.Network); var seedExtKey = new ExtKey(privateKey, wallet.ChainCode); var signingKeys = new HashSet <ISecret>(); var coins = new List <Coin>(); foreach (var transactionToUse in calculationResult.transactionsToUse) { var address = account.FindAddressesForTransaction(t => t.Id == transactionToUse.Id && t.Index == transactionToUse.Index && t.Amount > 0).Single(); ExtKey addressExtKey = seedExtKey.Derive(new KeyPath(address.HdPath)); BitcoinExtKey addressPrivateKey = addressExtKey.GetWif(wallet.Network); signingKeys.Add(addressPrivateKey); coins.Add(new Coin(transactionToUse.Id, (uint)transactionToUse.Index, transactionToUse.Amount, transactionToUse.ScriptPubKey)); } // get address to send the change to var changeAddress = account.GetFirstUnusedChangeAddress(); // get script destination address Script destinationScript = PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(new BitcoinPubKeyAddress(destinationAddress, wallet.Network)); // build transaction var builder = new TransactionBuilder(); Transaction tx = builder .AddCoins(coins) .AddKeys(signingKeys.ToArray()) .Send(destinationScript, amount) .SetChange(changeAddress.ScriptPubKey) .SendFees(calculationResult.fee) .BuildTransaction(true); if (!builder.Verify(tx)) { throw new WalletException("Could not build transaction, please make sure you entered the correct data."); } return(tx.ToHex(), tx.GetHash(), calculationResult.fee); }
public async Task <string> WithdrawBtc(string withdrawHash, string recipient, decimal amount) { var coins = new List <UTXO>(); var coinsUsed = new List <UTXO>(); var estimatedFee = await client.GetFeeRateAsync(3); try { await locker.WaitAsync(); while (coinsUsed.Sum(c => c.AsCoin().Amount.ToDecimal(MoneyUnit.BTC)) <= amount) { var txOperations = await client.GetUTXOsAsync(TrackedSource.Create(pubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main))); foreach (var op in txOperations.Confirmed.UTXOs) { if (!usedInputs.ContainsKey(op.TransactionHash.ToString() + op.Value.Satoshi.ToString())) { coins.Add(op); } } coins.Sort(delegate(UTXO x, UTXO y) { return(-x.AsCoin().Amount.CompareTo(y.AsCoin().Amount)); }); foreach (var item in coins) { if (coinsUsed.Sum(c => c.AsCoin().Amount.ToDecimal(MoneyUnit.BTC)) <= amount) { coinsUsed.Add(item); usedInputs.Add(item.TransactionHash.ToString() + item.Value.Satoshi.ToString(), item); } else { break; } } if (coinsUsed.Sum(c => c.AsCoin().Amount.ToDecimal(MoneyUnit.BTC)) < amount) { await Task.Delay(5000); } } TransactionBuilder builder = null; BitcoinPubKeyAddress destination = null; if (network == NetworkType.Testnet) { builder = Network.TestNet.CreateTransactionBuilder(); destination = new BitcoinPubKeyAddress(recipient, Network.TestNet); } else { builder = Network.Main.CreateTransactionBuilder(); destination = new BitcoinPubKeyAddress(recipient, Network.Main); } NBitcoin.Transaction tx = builder .AddCoins(coinsUsed.Select(c => c.AsCoin() /* .ScriptPubKey.ToBytes()*/)) .Send(destination, Money.Coins(amount)) .Send(TxNullDataTemplate.Instance.GenerateScriptPubKey(Encoding.UTF8.GetBytes(withdrawHash)), Money.Zero) .SetChange(pubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main)) .SendEstimatedFees((await client.GetFeeRateAsync(3)).FeeRate) .BuildTransaction(sign: false); // Specify the path to unmanaged PKCS#11 library provided by the cryptographic device vendor string pkcs11LibraryPath = @"/opt/cloudhsm/lib/libcloudhsm_pkcs11_standard.so"; // Create factories used by Pkcs11Interop library Net.Pkcs11Interop.HighLevelAPI.Pkcs11InteropFactories factories = new Net.Pkcs11Interop.HighLevelAPI.Pkcs11InteropFactories(); // Load unmanaged PKCS#11 library using (Net.Pkcs11Interop.HighLevelAPI.IPkcs11Library pkcs11Library = factories.Pkcs11LibraryFactory.LoadPkcs11Library(factories, pkcs11LibraryPath, AppType.MultiThreaded)) { // Show general information about loaded library Net.Pkcs11Interop.HighLevelAPI.ILibraryInfo libraryInfo = pkcs11Library.GetInfo(); Console.WriteLine("Library"); Console.WriteLine(" Manufacturer: " + libraryInfo.ManufacturerId); Console.WriteLine(" Description: " + libraryInfo.LibraryDescription); Console.WriteLine(" Version: " + libraryInfo.LibraryVersion); // Get list of all available slots foreach (Net.Pkcs11Interop.HighLevelAPI.ISlot slot in pkcs11Library.GetSlotList(SlotsType.WithOrWithoutTokenPresent)) { // Show basic information about slot Net.Pkcs11Interop.HighLevelAPI.ISlotInfo slotInfo = slot.GetSlotInfo(); Console.WriteLine(); Console.WriteLine("Slot"); Console.WriteLine(" Manufacturer: " + slotInfo.ManufacturerId); Console.WriteLine(" Description: " + slotInfo.SlotDescription); Console.WriteLine(" Token present: " + slotInfo.SlotFlags.TokenPresent); if (slotInfo.SlotFlags.TokenPresent) { // Show basic information about token present in the slot Net.Pkcs11Interop.HighLevelAPI.ITokenInfo tokenInfo = slot.GetTokenInfo(); Console.WriteLine("Token"); Console.WriteLine(" Manufacturer: " + tokenInfo.ManufacturerId); Console.WriteLine(" Model: " + tokenInfo.Model); Console.WriteLine(" Serial number: " + tokenInfo.SerialNumber); Console.WriteLine(" Label: " + tokenInfo.Label); // Show list of mechanisms (algorithms) supported by the token Console.WriteLine("Supported mechanisms: "); foreach (CKM mechanism in slot.GetMechanismList()) { Console.WriteLine(" " + mechanism); } } using (Net.Pkcs11Interop.HighLevelAPI.ISession session = slot.OpenSession(SessionType.ReadWrite)) { session.Login(CKU.CKU_USER, pkcsUser); // Specify signing mechanism Net.Pkcs11Interop.HighLevelAPI.IMechanism mechanism = session.Factories.MechanismFactory.Create(CKM.CKM_ECDSA); List <Net.Pkcs11Interop.HighLevelAPI.IObjectAttribute> publicKeyAttributes = new List <Net.Pkcs11Interop.HighLevelAPI.IObjectAttribute>(); publicKeyAttributes.Add(new Net.Pkcs11Interop.HighLevelAPI80.ObjectAttribute(CKA.CKA_LABEL, hsmKey)); publicKeyAttributes.Add(new Net.Pkcs11Interop.HighLevelAPI80.ObjectAttribute(CKA.CKA_SIGN, true)); Net.Pkcs11Interop.HighLevelAPI.IObjectHandle key = session.FindAllObjects(publicKeyAttributes).FirstOrDefault(); uint i = 0; foreach (var c in tx.Inputs.AsIndexedInputs()) { byte[] sourceData = c.GetSignatureHash(coinsUsed.First(cu => cu.Outpoint.Hash == c.PrevOut.Hash).AsCoin()).ToBytes(); Console.WriteLine("sourceData: " + tx.ToHex()); byte[] signature = session.Sign(mechanism, key, sourceData); Console.WriteLine("signature: " + BitConverter.ToString(signature)); var canSig = ECDSASignatureFactory.FromComponents(signature).MakeCanonical(); var sig = new NBitcoin.TransactionSignature(new NBitcoin.Crypto.ECDSASignature(new NBitcoin.BouncyCastle.Math.BigInteger(canSig.R.ToByteArray()), new NBitcoin.BouncyCastle.Math.BigInteger(canSig.S.ToByteArray())), SigHash.Single); builder = builder.AddKnownSignature(pubKey, sig, c.PrevOut); TransactionSignature sig2 = null; if (builder.TrySignInput(tx, i, SigHash.Single, out sig2)) { Console.WriteLine("Input Signed"); Console.WriteLine(BitConverter.ToString(sig2.ToBytes())); } else { Console.WriteLine("Input Not Signed"); } Console.WriteLine("tx: " + tx); i++; tx = builder.SignTransactionInPlace(tx); } Console.WriteLine("tx: " + tx); TransactionPolicyError[] errors = null; if (builder.Verify(tx, out errors)) { var broadcastResult = await client.BroadcastAsync(tx); broadcastResult.ToString(); Console.WriteLine("broadcast: " + tx.GetHash().ToString()); session.Logout(); return(tx.GetHash().ToString()); } else { Console.WriteLine("Verify transaction failed"); } if (errors != null) { foreach (var e in errors) { Console.WriteLine(e.ToString()); } } session.Logout(); } } } return(null); } catch (Exception e) { Log.Error("Failed to send BTC Withdraw" + e.ToString()); throw e; } finally { locker.Release(); } }
public dynamic SendToTestNet(dynamic transactionDto, string toAddress, string privateKey, string network, string token) { try { var btcnetwork = GetNetwork(network); BitcoinSecret secret = new BitcoinSecret(privateKey, btcnetwork); NBitcoin.Transaction tx = NBitcoin.Transaction.Create(btcnetwork); TxIn input = new TxIn { PrevOut = new OutPoint(new uint256(transactionDto.TransactionID), transactionDto.Vout), ScriptSig = secret.GetAddress(ScriptPubKeyType.Legacy).ScriptPubKey }; tx.Inputs.Add(input); TxOut output = new TxOut(); Money fee = Money.Coins(transactionDto.DefaultFees); Money totalAmount = Money.Coins(transactionDto.Amount); output.Value = totalAmount - fee; BitcoinAddress toaddress = BitcoinAddress.Create(toAddress, btcnetwork); output.ScriptPubKey = toaddress.ScriptPubKey; tx.Outputs.Add(output); var coins = tx.Inputs.Select(txin => new Coin(txin.PrevOut, new TxOut { ScriptPubKey = txin.ScriptSig })); tx.Sign(secret, coins.ToArray()); /* Push Data to So Chain*/ string api = "https://chain.so/api/v2/send_tx/LTCTEST"; var httpWebRequest = (HttpWebRequest)WebRequest.Create(api); httpWebRequest.ContentType = "application/json; charset=utf-8"; httpWebRequest.Method = HttpMethod.Post.Method; dynamic reqObj = new ExpandoObject(); reqObj.tx_hex = tx.ToHex(); string payload = JsonConvert.SerializeObject(reqObj); using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream())) { streamWriter.Write(payload); var ss = streamWriter.ToString(); streamWriter.Flush(); } string responseData = string.Empty; var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse(); using (var streamReader = new StreamReader(httpResponse.GetResponseStream())) { responseData = streamReader.ReadToEnd(); } dynamic response = new ExpandoObject(); if (!string.IsNullOrWhiteSpace(responseData)) { response = JsonConvert.DeserializeObject(responseData); } return(response); } catch (Exception ex) { throw new Exception(); } }
public static String Submit(Transaction tx) { //Info.Blockchain.API.PushTx.PushTx bcInfo = new Info.Blockchain.API.PushTx.PushTx(); //http://btc.blockr.io/api/v1/tx/push var cli = new WebClient(); cli.Headers[HttpRequestHeader.ContentType] = "application/json"; RawTx t = new RawTx(); t.hex = tx.ToHex(); String json = Newtonsoft.Json.JsonConvert.SerializeObject(t); String response = cli.UploadString("http://tbtc.blockr.io/api/v1/tx/push", json); //IList<String> nodes = new List<String>(3); //nodes.Add("54.149.133.4:18333"); ////SOME TEST NET NODES ////54.149.133.4:18333 ////52.69.206.155:18333 ////93.114.160.222:18333 //string url = "93.114.160.222:18333"; //nic ////url = "52.4.156.236:18333"; ////string url = "54.149.133.4:18333"; //using (var node = NBitcoin.Protocol.Node.ConnectToLocal(NBitcoin.Network.TestNet)) //{ // node.VersionHandshake(); // NBitcoin.Transaction[] transactions = new Transaction[1]; // transactions[0] = tx; // node.SendMessage(new NBitcoin.Protocol.InvPayload(transactions)); // node.SendMessage(new NBitcoin.Protocol.TxPayload(tx)); //} //using(var node = NBitcoin.Protocol.Node.Connect(NBitcoin.Network.TestNet, url)) //{ // node.VersionHandshake(); // System.Threading.Thread.Sleep(100); // NBitcoin.Transaction[] transactions = new Transaction[1]; // transactions[0] = tx; // node.SendMessage(new NBitcoin.Protocol.InvPayload(transactions)); // node.SendMessage(new NBitcoin.Protocol.TxPayload(tx)); //} var r = Newtonsoft.Json.JsonConvert.DeserializeObject<Response>(response); return r.data; }
private async Task <BtcTxnSignature> getBtcTxnSignaturesAsync( ECurrency currency, string address, string amount, string fee, string target, string reserve, string reservedFundsRedeemScript, string privateKey ) { GluwaClient client = new GluwaClient(mEnv); Result <BalanceResponse, ErrorResponse> getUnspentOutput = await client.GetBalanceAsync(currency, address, true); List <UnspentOutput> unspentOutputs = getUnspentOutput.Data.UnspentOutputs.OrderByDescending(u => u.Amount).ToList(); Money amountValue = Money.Parse(amount); Money feeValue = Money.Parse(fee); Money reserveAmount = amountValue + feeValue; Money totalRequiredAmountMoney = reserveAmount + feeValue; BigInteger totalRequiredAmount = new BigInteger(totalRequiredAmountMoney.Satoshi); BitcoinAddress sourceAddress = BitcoinAddress.Create(address, mEnv.Network); BitcoinAddress targetAddress = BitcoinAddress.Create(target, mEnv.Network); BitcoinAddress reserveAddress = BitcoinAddress.Create(reserve, mEnv.Network); BitcoinSecret secret = new BitcoinSecret(privateKey, mEnv.Network); List <UnspentOutput> usingUnspentOutputs = new List <UnspentOutput>(); BigInteger unspentOutputTotalAmount = BigInteger.Zero; for (int i = 0; i < unspentOutputs.Count; i++) { if (unspentOutputTotalAmount < totalRequiredAmount && i >= MAX_UNSPENTOUTPUTS_COUNT) { throw new InvalidOperationException($"Could not find up to {MAX_UNSPENTOUTPUTS_COUNT} BTC unspent outputs that can cover the amount and fee."); } if (unspentOutputTotalAmount >= totalRequiredAmount) { break; } usingUnspentOutputs.Add(unspentOutputs[i]); Money sumAmount = Money.Parse(unspentOutputs[i].Amount); unspentOutputTotalAmount += new BigInteger(sumAmount.Satoshi); } List <Coin> coins = new List <Coin>(); for (int i = 0; i < usingUnspentOutputs.Count; i++) { coins.Add(new Coin( fromTxHash: new uint256(usingUnspentOutputs[i].TxHash), fromOutputIndex: (uint)usingUnspentOutputs[i].Index, amount: usingUnspentOutputs[i].Amount, scriptPubKey: Script.FromHex(sourceAddress.ScriptPubKey.ToHex()) )); } TransactionBuilder builder = mEnv.Network.CreateTransactionBuilder(); NBitcoin.Transaction reserveTxSignature = builder .AddKeys(secret) .AddCoins(coins) .Send(reserveAddress, reserveAmount) .SetChange(sourceAddress) .SendFees(fee) .BuildTransaction(true); IEnumerable <Coin> reserveTxCoins = reserveTxSignature.Outputs.AsCoins(); Coin reserveTxCoin = reserveTxCoins.First( c => c.TxOut.ScriptPubKey.GetDestinationAddress(mEnv.Network) == reserveAddress); Script reservedFundsRedeemScriptValue = new Script(reservedFundsRedeemScript); ScriptCoin reservedCoin = new ScriptCoin(reserveTxCoin, reservedFundsRedeemScriptValue); builder = mEnv.Network.CreateTransactionBuilder(); PSBT executePsbt = builder .AddKeys(secret) .AddCoins(reservedCoin) .Send(targetAddress, amount) .SendFees(feeValue) .SetChange(reserveAddress) .BuildPSBT(true); builder = mEnv.Network.CreateTransactionBuilder(); PSBT reclaimPsbt = builder .AddKeys(secret) .AddCoins(reservedCoin) .Send(sourceAddress, amount) .SendFees(feeValue) .SetChange(reserveAddress) .BuildPSBT(true); BtcTxnSignature bTCTxnSignature = new BtcTxnSignature() { ReserveTxnSignature = reserveTxSignature.ToHex(), ExecuteTxnSignature = executePsbt.ToHex(), ReclaimTxnSignature = reclaimPsbt.ToHex() }; return(bTCTxnSignature); }
private async Task <string> getBtcTransactionSignatureAsync(ECurrency currency, string address, string amount, string fee, string target, string privateKey) { Result <BalanceResponse, ErrorResponse> getUnspentOutput = await GetBalanceAsync(currency, address, true); List <UnspentOutput> unspentOutputs = getUnspentOutput.Data.UnspentOutputs.OrderByDescending(u => u.Amount).ToList(); Money amountValue = Money.Parse(amount); Money feeValue = Money.Parse(fee); Money totalAmountAndFeeValue = amountValue + feeValue; BigInteger totalAmountAndFee = new BigInteger(totalAmountAndFeeValue.Satoshi); BitcoinAddress sourceAddress = BitcoinAddress.Create(address, mEnv.Network); BitcoinAddress targetAddress = BitcoinAddress.Create(target, mEnv.Network); BitcoinSecret secret = new BitcoinSecret(privateKey, mEnv.Network); List <UnspentOutput> usingUnspentOutputs = new List <UnspentOutput>(); BigInteger unspentOutputTotalAmount = BigInteger.Zero; for (int i = 0; i < unspentOutputs.Count; i++) { if (unspentOutputTotalAmount < totalAmountAndFee && i >= MAX_UNSPENTOUTPUTS_COUNT) { throw new InvalidOperationException($"Could not find up to {MAX_UNSPENTOUTPUTS_COUNT} BTC unspent outputs that can cover the amount and fee."); } if (unspentOutputTotalAmount >= totalAmountAndFee) { break; } usingUnspentOutputs.Add(unspentOutputs[i]); Money sumAmount = Money.Parse(unspentOutputs[i].Amount); unspentOutputTotalAmount += new BigInteger(sumAmount.Satoshi); } List <Coin> coins = new List <Coin>(); for (int i = 0; i < usingUnspentOutputs.Count; i++) { coins.Add(new Coin( fromTxHash: new uint256(usingUnspentOutputs[i].TxHash), fromOutputIndex: (uint)usingUnspentOutputs[i].Index, amount: usingUnspentOutputs[i].Amount, scriptPubKey: Script.FromHex(sourceAddress.ScriptPubKey.ToHex()) )); } TransactionBuilder builder = mEnv.Network.CreateTransactionBuilder(); NBitcoin.Transaction txn = builder .AddKeys(secret) .AddCoins(coins) .Send(targetAddress, amount) .SetChange(sourceAddress) .SendFees(fee) .BuildTransaction(true); if (!builder.Verify(txn, out NBitcoin.Policy.TransactionPolicyError[] error)) { throw new InvalidOperationException(string.Join(System.Environment.NewLine, error.Select(e => e.ToString()))); } string signature = txn.ToHex(); return(signature); }