private string GetAddressFromScript(string script) { if (string.IsNullOrEmpty(script)) { return(string.Empty); } string firstOP = script.Substring(0, 2); string remove = string.Empty; if (firstOP == NumberConversions.IntToHex((int)OPCodes.opcodetype.OP_DUP, 1)) { remove = NumberConversions.IntToHex((int)OPCodes.opcodetype.OP_DUP, 1) + NumberConversions.IntToHex((int)OPCodes.opcodetype.OP_HASH160, 1) + NumberConversions.IntToHex(40 / 2, 1); } else if (firstOP == NumberConversions.IntToHex(0x01, 1)) { remove = NumberConversions.IntToHex(0x01, 1) + NumberConversions.IntToHex((int)OPCodes.opcodetype.OP_INVALIDOPCODE, 1) + NumberConversions.IntToHex(0x16, 1) + NumberConversions.IntToHex((int)OPCodes.opcodetype.OP_PUBKEYHASH, 1) + NumberConversions.IntToHex((int)OPCodes.opcodetype.OP_0, 1); } string addressHash = script.Substring(remove.Length, 40); return(BitcoinConversions.Hash160ToBase58(addressHash)); }
public override async Task <Response <List <UTXO> > > GetUTXO(List <SendingAddress> addrList) { Response <List <UTXO> > resp = new Response <List <UTXO> >(); string addresses = string.Join("|", addrList.Select(b => b.Address).ToArray()); string url = "https://blockchain.info/unspent?active=" + addresses; Response <JObject> apiResp = await SendApiRequestAsync(url); if (apiResp.Errors.Any()) { resp.Errors.AddRange(apiResp.Errors); return(resp); } resp.Result = new List <UTXO>(); foreach (var item in apiResp.Result["unspent_outputs"]) { UTXO u = new UTXO(); string script = item["script"].ToString(); u.AddressHash160 = script.Substring("76a914".Length, script.Length - "76a91488ac".Length); u.Address = BitcoinConversions.Hash160ToBase58(u.AddressHash160); u.TxHash = item["tx_hash_big_endian"].ToString(); u.Amount = (ulong)item["value"]; u.Confirmation = (int)item["confirmations"]; u.OutIndex = (uint)item["tx_output_n"]; resp.Result.Add(u); } return(resp); }
private static string BuildScriptPub(string addr) { string hash160 = BitcoinConversions.Base58ToHash160(addr); return(ByteArray.ConcatArrays( new byte[] { (byte)OPCodes.OP_DUP, (byte)OPCodes.OP_HASH160 }, new byte[] { (byte)(hash160.Length / 2) }, Base16.ToByteArray(hash160), new byte[] { (byte)OPCodes.OP_EQUALVERIFY, (byte)OPCodes.OP_CHECKSIG }).ToBase16()); }
private void MakeTx() { List <UTXO> uList = new List <UTXO>(); foreach (var item in TxInList) { UTXO u = new UTXO(); u.TxHash = item.TxId; u.OutIndex = item.OutIndex; if (BTx.Status == BitcoinTransaction.TxStatus.Signed) { int pubKeyLength = 65; string pubKey = item.ScriptSig.Substring((item.ScriptSigLength * 2) - (pubKeyLength + 1)); u.Address = BitcoinConversions.PubKeyToBase58(pubKey); u.AddressHash160 = BitcoinConversions.ByteArrayToHex(BitcoinConversions.PubKeyToHash160(pubKey)); } else { var addr = GetAddressFromScript(item.ScriptSig); if (string.IsNullOrEmpty(addr)) { u.Address = string.Empty; u.AddressHash160 = string.Empty; } else { u.Address = addr; u.AddressHash160 = BitcoinConversions.Base58ToHash160(addr); } } uList.Add(u); } try { UInt32 lockTime = 0; RawTx = Transaction.CreateRawTx(uList, ReceiveList.ToList(), lockTime, SelectedWalletType); } catch (Exception ex) { RawTx = string.Empty; MessageBox.Show(ex.Message); } }
/// <summary> /// Makes ScriptSig based on wallet type. /// <para/> INCOMPLETE! /// </summary> /// <param name="addr">Hes string to put inside of ScriptSig.</param> /// <param name="type">Type of wallet that is supposed to sign this transaction.</param> /// <returns>ScriptSig</returns> public static string BuildScriptSig(string addr, WalletType type) { string hash160 = BitcoinConversions.Base58ToHash160(addr); switch (type) { case WalletType.Electrum: //01 ff 16 fd 00 return(ByteArray.ConcatArrays( new byte[] { 1, (byte)OPCodes.OP_INVALIDOPCODE, 16, (byte)OPCodes.OP_PUBKEYHASH, 0 }, Base16.ToByteArray(hash160) ).ToBase16()); case WalletType.Normal: case WalletType.Core: default: return(BuildScriptPub(addr)); } }
public async Task <List <UTXO> > GetUTXO(List <SendingAddress> addrList) { using (var client = new HttpClient()) { // Make sure all balances are 0 foreach (var ad in addrList) { ad.BalanceSatoshi = 0; } var addresses = string.Join("|", addrList.Select(b => b.Address).ToArray()); string url = "https://blockchain.info/unspent?active=" + addresses; string result = await client.GetStringAsync(url); JObject jResult = JObject.Parse(result); List <UTXO> utxList = new List <UTXO>(); foreach (var item in jResult["unspent_outputs"]) { UTXO u = new UTXO(); string script = item["script"].ToString(); u.AddressHash160 = script.Substring("76a914".Length, script.Length - "76a91488ac".Length); u.Address = BitcoinConversions.Hash160ToBase58(u.AddressHash160); u.TxHash = item["tx_hash_big_endian"].ToString(); u.Amount = (int)item["value"]; u.Confirmation = (int)item["confirmations"]; u.OutIndex = (UInt32)item["tx_output_n"]; utxList.Add(u); // Update address balance foreach (var ad in addrList) { if (ad.Address == u.Address) { ad.BalanceSatoshi += u.Amount; } } } return(utxList); } }
/// <summary> /// Creates a Raw Unsigned bitcoin transaction Transaction. /// <para/> Sources: /// <para/> 1. https://bitcoin.stackexchange.com/questions/32628/redeeming-a-raw-transaction-step-by-step-example-required /// <para/> 2. https://bitcoin.org/en/developer-reference#raw-transaction-format /// <para/> 3. https://bitcoin.org/en/developer-examples#simple-raw-transaction /// </summary> /// <param name="txToSpend">List of UTXOs to spend.</param> /// <param name="receiveAddr">List of receiving addresses and the amount to be paid to each.</param> /// <param name="wallet">Type of the wallet (Electrum does not recognize normal type of scriptSig placeholder).</param> /// <returns>Rat unsigned transaction string.</returns> public static string CreateRawTx(List <UTXO> txToSpend, List <ReceivingAddress> receiveAddr, UInt32 lockTime, WalletType wallet) { StringBuilder rawTx = new StringBuilder(); // 1) 4 byte - version UInt32 version = 1; rawTx.Append(NumberConversions.IntToHex(version, 4)); // 2) ? byte - tx_in count (compactSize uint) int txInCount = txToSpend.Count; rawTx.Append(NumberConversions.MakeCompactSize((UInt64)txInCount)); for (int i = 0; i < txInCount; i++) { // 3) 32 byte - TX hash (reverse) string txToSend = txToSpend[i].TxHash; rawTx.Append(ReverseTx(txToSend)); // 4) 4 byte - output Index UInt32 outputIndex = txToSpend[i].OutIndex; rawTx.Append(NumberConversions.IntToHex(outputIndex, 4)); // 5) ? byte - scriptSig length (compactSize uint) (Maximum value is 10,000 bytes) // Can be from 0 and up. Will be replaced by actual scriptSig with another length. // Format can be different based on the client which will be used for signing. if (string.IsNullOrEmpty(txToSpend[i].AddressHash160) && wallet != WalletType.Core) { throw new Exception(string.Format("No address was found. Set wallet type to Core.")); } string scriptSig = BuildScript(txToSpend[i].AddressHash160, wallet); rawTx.Append(NumberConversions.IntToHex((UInt64)(scriptSig.Length / 2), 1)); // 6) ? byte - scriptSig which is filled with scriptPubkey temporarily (20 is half the length of address hash160) rawTx.Append(scriptSig); //7) 4 byte - sequence - max is 0xffffffff - can change for RBF transactions // Ref: // https://bitcoin.stackexchange.com/questions/2025/what-is-txins-sequence UInt32 sequence = UInt32.MaxValue; rawTx.Append(NumberConversions.IntToHex(sequence, 4)); } //8) ? byte - tx_out count (compactSize uint) int txOutCount = receiveAddr.Count; rawTx.Append(NumberConversions.MakeCompactSize((UInt64)txOutCount)); foreach (var item in receiveAddr) { //9) 8 byte - amout to transfer UInt64 amount = item.PaymentSatoshi; rawTx.Append(NumberConversions.IntToHex(amount, 8)); //10) ? byte - pk_script length (compactSize uint) string itemHash = BitcoinConversions.Base58ToHash160(item.Address); string outputScript = BuildScript(itemHash, WalletType.Normal); rawTx.Append(NumberConversions.MakeCompactSize((UInt64)outputScript.Length / 2)); //11) ? byte - pk_script rawTx.Append(outputScript); } //12) 4 byte - lock time // * If less than 500 million, locktime is parsed as a block height. The transaction can be added to any block which has this height or higher. // * If greater than or equal to 500 million, locktime is parsed using the Unix epoch time format (the number of seconds elapsed since 1970-01-01T00:00 UTC—currently over 1.395 billion). The transaction can be added to any block whose block time is greater than the locktime. rawTx.Append(NumberConversions.IntToHex(lockTime, 4)); return(rawTx.ToString()); }
/// <summary> /// Deserialize the transaction hex string. /// </summary> /// <param name="tx">Transaction hex string</param> /// <returns>Bitcoin Transaction</returns> public static BitcoinTransaction DecodeRawTx(string tx) { BitcoinTransaction btx = new BitcoinTransaction(); int index = 0; // 1) 4 byte - version string version = tx.Substring(index, 8); btx.Version = (UInt32)NumberConversions.HexToUInt(version); index += 8; // 2) ? byte - tx_in count (CompactSize uint) btx.TxInCount = NumberConversions.ReadCompactSize(tx, ref index); bool isSigned = true; bool isRbf = false; // Initialize the array btx.TxInList = new TxIn[btx.TxInCount]; for (UInt64 i = 0; i < btx.TxInCount; i++) { TxIn temp = new TxIn(); // 3) 32 byte - TX hash (reverse) temp.TxId = tx.Substring(index, 64); temp.TxId = ReverseTx(temp.TxId); index += 64; // 4) 4 byte - output Index string outIndex = tx.Substring(index, 8); temp.OutIndex = (UInt32)NumberConversions.HexToUInt(outIndex); index += 8; // 5) ? byte - scriptSig length (CompactSize uint) (Maximum value is 10,000 bytes) string scriptSigLength = tx.Substring(index, 2); temp.ScriptSigLength = (int)NumberConversions.ReadCompactSize(tx, ref index); // 6) ? byte - scriptSig or a placeholder for unsigned (can be empty too) temp.ScriptSig = tx.Substring(index, temp.ScriptSigLength * 2); index += temp.ScriptSigLength * 2; //7) 4 byte - sequence - max is 0xffffffff - can change for RBF transactions string sequence = tx.Substring(index, 8); temp.Sequence = (UInt32)NumberConversions.HexToUInt(sequence); index += 8; btx.TxInList[i] = temp; // Check to see if all the inputs are signed if (temp.ScriptSigLength <= 25) { isSigned = false; } // Check for opt-in Replace By Fee if (temp.Sequence != UInt32.MaxValue) { isRbf = true; } } // Set transaction sign and RBF status. btx.Status = (isSigned) ? BitcoinTransaction.TxStatus.Signed : BitcoinTransaction.TxStatus.Unsigned; btx.IsRbf = isRbf; //8) ? byte - tx_out count (compactSize uint) btx.TxOutCount = NumberConversions.ReadCompactSize(tx, ref index); // Initialize the array btx.TxOutList = new TxOut[btx.TxOutCount]; for (UInt64 i = 0; i < btx.TxOutCount; i++) { TxOut temp = new TxOut(); //9) 8 byte - amout to transfer string amount = tx.Substring(index, 16); temp.Amount = NumberConversions.HexToUInt(amount); index += 16; //10) ? byte - pk_script length (compactSize uint) string pkScriptLength = tx.Substring(index, 2); temp.PkScriptLength = (Int32)NumberConversions.HexToUInt(pkScriptLength); index += 2; //11) ? byte - pk_script temp.PkScript = tx.Substring(index, temp.PkScriptLength * 2); index += temp.PkScriptLength * 2; btx.TxOutList[i] = temp; } //12) 4 byte - lock time string lockTime = tx.Substring(index, 8); btx.LockTime = (UInt32)NumberConversions.HexToUInt(lockTime); index += 8; // If the transaction is signed, then it has a TxId if (isSigned) { btx.TxId = BitcoinConversions.GetTxId(tx); } return(btx); }