Author

Topic: BOUNTY 0.1BTC signing question with multiple inputs (Read 964 times)

hero member
Activity: 765
Merit: 503
No worries apxu.  Thanks for the help.
member
Activity: 229
Merit: 13
Got it. Thanks.
newbie
Activity: 25
Merit: 0
Thanks for posting.  I have received an answer via PM.  Im just verifying that first.

Nice trick. No need to pay any bounty there...
Shut up you muppet.  Ill let you contact the apxu who helped me directly, and wasn't too interested in the bounty.

http://s30.postimg.org/ylo9navy9/fuckoff.png

Just saying that (IMHO) if you offer a bounty in public, you should pay in public. Anyone could forge this images, it says nothing. No need to get grumpy, unless indeed you have something to hide.
hero member
Activity: 765
Merit: 503
Paid to apxu f2efa958f00d6d88cdcf5110b2bfba42585d09415da5f1fac6ad5a6eca9457a7-000

hero member
Activity: 765
Merit: 503
Thanks for posting.  I have received an answer via PM.  Im just verifying that first.

Nice trick. No need to pay any bounty there...
Shut up you muppet.  Ill let you contact the apxu who helped me directly, and wasn't too interested in the bounty.

newbie
Activity: 25
Merit: 0
Thanks for posting.  I have received an answer via PM.  Im just verifying that first.

Nice trick. No need to pay any bounty there...
hero member
Activity: 765
Merit: 503
Thanks for posting.  I have received an answer via PM.  Im just verifying that first.
hero member
Activity: 714
Merit: 662
Take a look at my code

https://github.com/NicolasDorier/NBitcoin/blob/master/NBitcoin/Script.cs#L358

Response : It depends on the type of signature.

A signature in a script is in two part the DER + One SigHash type :

"All" sign the hash of all the transaction (No output, no input can be added)
"Anyone can pay" same thing, but without the other inputs, (other inputs can be added or removed)
"None" will not sign output (other output can be added or removed)
"Single" you only care about your input, and the output of the same index.

You are using C#, so feel free to take my code, and run the unit tests. (Or make your own)

I ported the C++ unit test suite.
https://github.com/NicolasDorier/NBitcoin/blob/master/NBitcoin.Tests/transaction_tests.cs

Just take my code, put a break point in the SignatureHash method, run tests, and compare the difference with your code.

In your case though, it appears you only want to support the "All" type. So you can freely skip if case statements in my code.
hero member
Activity: 765
Merit: 503
Bounty added.
hero member
Activity: 765
Merit: 503
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#o0

What 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.html


c# 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 _outputsToSpend;

        public Transaction(IList 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;
        }
    }
Jump to: