public static string SignTx(string TxJSON, string extPrivateKey, bool testnet) { try { Network ntwk = testnet ? Network.TestNet : Network.Main; TxSerial txs = Newtonsoft.Json.JsonConvert.DeserializeObject <TxSerial>(TxJSON); ExtKey p2 = ExtKey.Parse(extPrivateKey); ExtKey RootKey = p2; TransactionBuilder bldr = new TransactionBuilder(); bldr.Send(new BitcoinPubKeyAddress(txs.ToAddress), Money.Satoshis(txs.SendAmt)); bldr.SetChange(new BitcoinPubKeyAddress(txs.ChgAddress)); bldr.SendFees(Money.Satoshis(txs.Fee)); //re-add coins List <Coin> lstCoin = new List <Coin>(); foreach (UTXO u in txs.InputUTXOs) { Coin cn; bldr.AddCoins(cn = new Coin( new OutPoint(new uint256(u.Tx_hash), u.Tx_pos), //tx that funded wallet, spend this coin new TxOut(Money.Satoshis(u.Value), new BitcoinPubKeyAddress(u.Address)))); lstCoin.Add(cn); } Transaction tx = bldr.BuildTransaction(false); //will select coins foreach (UTXO u in txs.InputUTXOs) { Coin cn; bldr.AddCoins(cn = new Coin( new OutPoint(new uint256(u.Tx_hash), u.Tx_pos), //tx that funded wallet, spend this coin new TxOut(Money.Satoshis(u.Value), new BitcoinPubKeyAddress(u.Address)))); //check if coin in spend list, else error (should already be done in CreateTx) foreach (TxIn i in tx.Inputs) { if (i.PrevOut == cn.Outpoint) //this coin is in tx //sign utxo with corresponding private key { bldr.AddKnownSignature(new PubKey(u.PublicKey), tx.SignInput(new BitcoinSecret(RootKey.Derive((uint)u.AddrType).Derive(u.AddrIndex).PrivateKey, ntwk), cn)); } } } Transaction txSigned = bldr.BuildTransaction(true); NBitcoin.Policy.TransactionPolicyError[] pe; bool verify = bldr.Verify(txSigned, out pe); string hx = txSigned.ToHex(); return(hx); } catch (Exception ex) { string result = "ERROR: " + ex.Message; return(result); } }
//take in tx parameters, return tx object public static TxSerial CreateTx(string extPubKey, string pubToAddr, string chgAddr, int walletId, long satToSend, long fee, bool testnet) { CheckNullOrEmpty(new object[] { extPubKey, pubToAddr, ElectrumXhost }, new string[] { "extPubKey", "pubToAddr", "ElectrumXhost" }); string err = ""; if (satToSend == 0) { err += "satoshiToSend = 0, "; } if (fee == 0) { err += "satoshiFee = 0, "; } if (err != "") { throw new Exception("[CreateTx] " + err); } //get first 100+100 child address from ext pub key List <Tuple <string, string> > recAddrList = GetDerivedKeys(extPubKey, 0, 20, false, testnet); //receive addresses List <Tuple <string, string> > chgAddrList = GetDerivedKeys(extPubKey, 0, 20, true, testnet); //change addresses //TODO - create process for getting next change address, so address never used twice if (chgAddr == null || chgAddr == "") //get first chg addr for extPubKey { chgAddr = chgAddrList.First().Item1; } //server status check string info = ElectrumX.GetServerInfo(ElectrumXhost, ElectrumXport); if (info == null) { throw new Exception("[CreateTx] ElectrumX Server Check Failed"); } string[] recAddrListAddr = new string[recAddrList.Count]; //short address string[] recAddrListExt = new string[recAddrList.Count]; //long address int ctr = 0; foreach (var t in recAddrList) { recAddrListAddr[ctr++] = t.Item1; //short } ctr = 0; foreach (var t in recAddrList) { recAddrListExt[ctr++] = t.Item2; //long - hash } string[] chgAddrListAddr = new string[recAddrList.Count]; string[] chgAddrListExt = new string[recAddrList.Count]; ctr = 0; foreach (var t in chgAddrList) { chgAddrListAddr[ctr++] = t.Item1; } ctr = 0; foreach (var t in chgAddrList) { chgAddrListExt[ctr++] = t.Item2; } //get all UTXOs (unspent inputs) from receive addresses UTXO[] recUTXOs = ElectrumX.GetUTXOs(recAddrList, ElectrumXhost, ElectrumXport); UTXO.SetAddressType(recUTXOs, 0); //receiver //get all UTXOs (unspent inputs) from change addresses UTXO[] chgUTXOs = ElectrumX.GetUTXOs(chgAddrList, ElectrumXhost, ElectrumXport); UTXO.SetAddressType(chgUTXOs, 1); //change //start new tx TransactionBuilder bldr = new TransactionBuilder(); bldr.Send(new BitcoinPubKeyAddress(pubToAddr), Money.Satoshis(satToSend)); //amount to send to recipient bldr.SetChange(new BitcoinPubKeyAddress(chgAddr)); //send change to this address bldr.SendFees(Money.Satoshis(fee)); //miner (tx) fee //collect all UTXOs List <UTXO> allUTXOs = new List <UTXO>(); allUTXOs.AddRange(recUTXOs); allUTXOs.AddRange(chgUTXOs); List <ICoin> lstTxCoins = new List <ICoin>(); //Coin is a UTXO //add new coin for each UTXO foreach (UTXO x in allUTXOs) //tx builder will select coins from this list { BitcoinPubKeyAddress fromAddr = new BitcoinPubKeyAddress(x.Address); NBitcoin.Coin cn = null; //create new coin from UTXO bldr.AddCoins(cn = new NBitcoin.Coin( new OutPoint(new uint256(x.Tx_hash), x.Tx_pos), //tx that funded wallet, spend this coin new TxOut(Money.Satoshis(x.Value), fromAddr.ScriptPubKey))); //specify full coin amount, else SetChange ignored lstTxCoins.Add(cn); //add coin to transaction, may not be used x.tmp = cn; //link UTXO with coin } List <UTXO> usedUTXOs = new List <UTXO>(); //coins actually used in tx NBitcoin.Transaction tx = bldr.BuildTransaction(false); //sort\filter coins, some coins will not be needed\used //coin objects not stored in tx, so we need to determine which coins were used //scan tx inputs for matching coins, ignore other coins foreach (UTXO u in allUTXOs) { foreach (TxIn i in tx.Inputs) { if (i.PrevOut == ((NBitcoin.Coin)u.tmp).Outpoint) //this coin in tx { usedUTXOs.Add(u); //this UTXO will be used\spent in tx } } } //populate return object TxSerial txs = new TxSerial() { SendAmt = satToSend, Fee = fee, ExtPublicKey = extPubKey, ToAddress = pubToAddr, ChgAddress = chgAddr, WalletId = walletId }; txs.ExtPublicKey = extPubKey; foreach (UTXO u in usedUTXOs) { u.tmp = null; //don't serialize coin object, will rebuild coins in signing process } txs.InputUTXOs = new List <UTXO>(); txs.InputUTXOs.AddRange(usedUTXOs); //string jsn = Newtonsoft.Json.JsonConvert.SerializeObject(txs, Newtonsoft.Json.Formatting.Indented); return(txs); }
public static string CreateTxJSON(string extPubKey, string pubToAddr, string chgAddr, int walletId, long satToSend, long fee, bool testnet) { TxSerial ts = CreateTx(extPubKey, pubToAddr, chgAddr, walletId, satToSend, fee, testnet); return(Newtonsoft.Json.JsonConvert.SerializeObject(ts, Newtonsoft.Json.Formatting.Indented)); }