/// <summary> /// Returns estimated value for the final signed transaction. /// </summary> /// <param name="inputCount">Number of inputs to spend</param> /// <param name="outputCount">Number of outputs to receive</param> /// <returns>Transaction size</returns> public static int GetTransactionSize(int inputCount, int outputCount) { int c1 = NumberConversions.MakeCompactSize((UInt64)inputCount).Length / 2; int c2 = NumberConversions.MakeCompactSize((UInt64)outputCount).Length / 2; int inputSize = 32 //TX hash + 4 //output Index + 1 //scriptSig length + 138 //scriptSig sig + 4; //sequence int outputSize = 8 //Amount + 1 //pk_script length + 25; //pk_script int totalSize = 4 //Version + c1 //TxIn Count + inputSize * inputCount + c2 //TxOut Count + outputSize * outputCount + 4; //LockTime return((totalSize <= 10) ? 0 : totalSize); }
/// <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()); }