lll pay 0.1BTC if someone can explain & show some good code that explains the following (python, c++, c#)Quick question about signing transactions. I've written a method to sign transactions using bytes. Mostly just to understand the scrypt part and inputs and outputs better.
All works well with one input and 2 outputs (change) Eg, this transaction worked
http://blockexplorer.com/tx/852cb5b462de0fc0d67d14618945e854822e649098242577a363bd1d2caa9c7a#o0What im struggling with is multiple inputs. The below artcile lists an input as this. Which makes sense. If input is a collection, the below is just repeated? Then the whole structure is signed?
previous output hash
(reversed) 48 4d 40 d4 5b 9e a0 d6 52 fc a8 25 8a b7 ca a4 25 41 eb 52 97 58 57 f9 6f b5 0c d7 32 c8 b4 81
previous output index 00 00 00 00
script length
scriptSig script containing signature
sequence ff ff ff ff
I have been following this tut, and the python example off it.
http://www.righto.com/2014/02/bitcoins-hard-way-using-raw-bitcoin.htmlc# code: NB, DTO classes not in sample
public class Transaction
{
public Byte[] Version
{
get
{
return new Byte[4] { 0x01, 0x00, 0x00, 0x00 };
}
}
public Byte[] LockTime
{
get
{
return new Byte[4] { 0x00, 0x00, 0x00, 0x00 };
}
}
public Byte[] HashType
{
get
{
return new Byte[4] { 0x01, 0x00, 0x00, 0x00 };
}
}
public Byte[] Change
{
get;
private set;
}
public Byte[] Value
{
get;
private set;
}
public Byte NumTxIn
{
get
{
if (_outputsToSpend != null)
{
return Convert.ToByte(_outputsToSpend.Count); //new Byte[1] { 0x01 };
}
else
{
return Convert.ToByte(0);
}
}
}
public Byte NumTxOut
{
get
{
return Convert.ToByte(2);
}
}
public Byte[] OutPuts
{
get
{
return null;
}
}
public Byte[] Sequence
{
get
{
return new Byte[4] { 0xFF, 0xFF, 0xFF, 0xFF }; //FF FF FF FF
}
}
private IList<DC.Common.Models.UnspentOutput> _outputsToSpend;
public Transaction(IList<DC.Common.Models.UnspentOutput> outputsToSpend, DC.Common.Models.Address sourceAddress, String destinationAddress, String destinationHash160, Decimal amount, Decimal fee = 0.0001M)
{
//amount = amount * DC.Common.Math.SATOSHI;
//fee = fee * DC.Common.Math.SATOSHI;
//Amount to send + miners fee
Decimal change = (amount + fee) - outputsToSpend.Sum(o => o.Value) / DC.Common.Math.SATOSHI;
//CHANGE
this.Change = DC.Common.Math.GetValueAsBytes(change);
this.Value = DC.Common.Math.GetValueAsBytes(amount);
_outputsToSpend = outputsToSpend;
}
public byte[] CreateAndSignTransaction(DC.Common.Models.Address sourceAddress, String destinationAddress, String destinationHash160, Decimal amount, Decimal fee = 0.0001M)
{
if (!DC.Common.BitcoinHelper.IsValidAddress(destinationAddress))
throw new ArgumentException("Destination address is not valid");
//Int32 OUTPUT_INDEX = 1;
Int32 SIGHASH_ALL = 1;
//1
Byte[] version = this.Version;
Debug.Assert(version[0] == 1);
Debug.Assert(version.Length == 4);
//2
//Byte num_txin = Convert.ToByte(outputsToSpend.Count); //new Byte[1] { 0x01 };
Byte[] tx_fields = version.Concat(this.NumTxIn);
Debug.Assert(tx_fields.Length == 5);
foreach (DC.Common.Models.UnspentOutput outputToSpend in _outputsToSpend)
{
//THIS IS HASH OF THE TRANSACTION THAT CONTAINS THE UNSPENT OUTPUT
//NB, ALREADY REVERSED!
Byte[] incomingTxHash = DC.Common.StringHelper.HexStringToByte(outputToSpend.TxHash);
//Array.Reverse(incomingTxHash);
//3
Byte[] prevout_hash = DC.Common.StringHelper.HexStringToByte(outputToSpend.TxHash);
//Array.Reverse(prevout_hash);
tx_fields = tx_fields.Concat(prevout_hash);
//4 (index 0)
//TODO: CONFIRM THIS. 4BYTE ARRAY, WIHT PREVIOUS INDEX EG 00 00 00 01
Byte[] output_index = BitConverter.GetBytes(outputToSpend.TxOutputN);
Array.Reverse(output_index);
//Byte[] output_index = new Byte[4] { 0x00, 0x00, 0x00, 0x00 };
//Array.Copy(outputIndexTemp, 1, output_index, 4 - outputIndexTemp.Length, outputIndexTemp.Length);
tx_fields = tx_fields.Concat(output_index);
//SWAP. NB WE HAVE THE SCRYPT, JUST CONVERT TO BYTES
//##next comes the part of the transaction input. here we place the script of the *output* that we want to redeem
//String sourceOutputPublicKey = DC.Common.Script.ParseScript(outputsToSpend[0].Script);
Byte[] tempScript = DC.Common.StringHelper.HexStringToByte(outputToSpend.Script);
Byte scriptSighHashLength = Convert.ToByte(tempScript.Length);
tx_fields = tx_fields.Concat(scriptSighHashLength);
Byte[] scriptSigHash = DC.Common.StringHelper.HexStringToByte(outputToSpend.Script);
tx_fields = tx_fields.Concat(scriptSigHash);
//7
tx_fields = tx_fields.Concat(this.Sequence);
}
//8
//Byte[] num_txout = new Byte[1] { 0x02 }; //1
tx_fields = tx_fields.Concat(this.NumTxOut);
//9
//Byte[] value = DC.Common.Math.GetValueAsBytes(amount);
tx_fields = tx_fields.Concat(this.Value);
//10
//http://bitcoin.stackexchange.com/questions/3374/how-to-redeem-a-basic-tx
Byte[] scriptPubKey = DC.Common.Script.CreateScriptPubKey(destinationHash160);
Byte scriptLen = Convert.ToByte(scriptPubKey.Length);
tx_fields = tx_fields.Concat(scriptLen);
tx_fields = tx_fields.Concat(scriptPubKey);
//Amount to send + miners fee
//Decimal change = (amount + fee) - outputsToSpend.Sum(o => o.Value) / DC.Common.Math.SATOSHI;
//CHANGE
tx_fields = tx_fields.Concat(this.Change);
//http://bitcoin.stackexchange.com/questions/3374/how-to-redeem-a-basic-tx
Byte[] changeScriptPubKey = DC.Common.Script.CreateScriptPubKey(sourceAddress.PublicKeyHash160); //SELF
Byte changeScriptLen = Convert.ToByte(changeScriptPubKey.Length);
tx_fields = tx_fields.Concat(changeScriptLen);
tx_fields = tx_fields.Concat(changeScriptPubKey);
//END CHANGE
//12
tx_fields = tx_fields.Concat(this.LockTime);
//13
tx_fields = tx_fields.Concat(this.HashType);
//DOUBLE HASH
Byte[] hash_scriptless = Crypto.DoubleSHA256(tx_fields);
//SIGN DATA
Byte[] sig_data = Crypto.Sign(hash_scriptless, sourceAddress.d, DC.Common.Crypto.GetDomainParams());
sig_data = sig_data.Concat(Convert.ToByte(SIGHASH_ALL));
//final_tx.write_int32(tx_fields['version'])
//final_tx.write_compact_size(tx_fields['num_txin'])
//final_tx.write(tx_fields['prevout_hash'])
//final_tx.write_uint32(tx_fields['output_index'])
Byte[] final_tx = this.Version; //new Byte[4] { 0x01, 0x00, 0x00, 0x00 }; //version
Debug.Assert(final_tx[0] == 1);
final_tx = final_tx.Concat(NumTxIn); //count
foreach (DC.Common.Models.UnspentOutput outputToSpend in _outputsToSpend)
{
//final_tx = final_tx.Concat(prevout_hash);
Byte[] previousTxHash = StringHelper.HexStringToByte(outputToSpend.TxHash);
final_tx = final_tx.Concat(previousTxHash);
Byte[] previousOutputIndex = new Byte[4];
previousOutputIndex = BitConverter.GetBytes(outputToSpend.TxOutputN); //TODO
Array.Reverse(previousOutputIndex);
final_tx = final_tx.Concat(previousOutputIndex);
}
Byte[] pub_key = StringHelper.HexStringToByte(sourceAddress.PublicKeyAsHex);
//scriptSig = chr(len(sig_data)) + sig_data + chr(len(pubkey_data)) + pubkey_data
Byte[] scriptSig = new Byte[1]; //Length
scriptSig[0] = Convert.ToByte(sig_data.Length); //Length
scriptSig = scriptSig.Concat(sig_data); //data
scriptSig = scriptSig.Concat(Convert.ToByte(pub_key.Length)); //Length pub key
scriptSig = scriptSig.Concat(pub_key); //pub key
final_tx = final_tx.Concat(Convert.ToByte(scriptSig.Length)); //Length of script sig
final_tx = final_tx.Concat(scriptSig);
final_tx = final_tx.Concat(this.Sequence);
final_tx = final_tx.Concat(this.NumTxOut);
final_tx = final_tx.Concat(this.Value);
final_tx = final_tx.Concat(Convert.ToByte(scriptPubKey.Length));
final_tx = final_tx.Concat(scriptPubKey);
final_tx = final_tx.Concat(this.Change);
final_tx = final_tx.Concat(Convert.ToByte(changeScriptPubKey.Length));
final_tx = final_tx.Concat(changeScriptPubKey);
final_tx = final_tx.Concat(this.LockTime);
return final_tx;
}
}