richardkiss / pycoin

Python-based Bitcoin and alt-coin utility library.
MIT License
1.4k stars 497 forks source link

Question: how to sign serialisation of unsigned tx #23

Closed adamkrellenstein closed 10 years ago

adamkrellenstein commented 10 years ago

What's the best way to use pycoin to sign the hexadecimal serialisation of an unsigned transaction, such as may be passed to Bitcoin Core's signrawtransaction API method?

Example input: 01000000014e40a06a3e19165e71351e287346a46f9e768677e64d667bef2857ae175c4b63020000001976a914a0e8d5fe498e6ca2c2000de13fc9cc470bd9b87088acffffffff036c2a0000000000001976a914a0e8d5fe498e6ca2c2000de13fc9cc470bd9b87088ac6c2a00000000000047512103f62c74a1686c41e2639c2c6e9ab336856a405f7aba0b753af6002ce709daf55d211c434e5452505254590000000000000000000000010000000001312d000000000052aededb2b01000000001976a914a0e8d5fe498e6ca2c2000de13fc9cc470bd9b87088ac00000000

Desired output: 01000000014e40a06a3e19165e71351e287346a46f9e768677e64d667bef2857ae175c4b63020000006b48304502210080d73fe780f385c9f1461e62494f4feee39b4c43202a34c4c85471803568489b02206de0ebd1fda2098561e4b64ee8baee1a16a7344e5ab862e332a0c60f8810bd08012103f62c74a1686c41e2639c2c6e9ab336856a405f7aba0b753af6002ce709daf55dffffffff036c2a0000000000001976a914a0e8d5fe498e6ca2c2000de13fc9cc470bd9b87088ac6c2a00000000000047512103f62c74a1686c41e2639c2c6e9ab336856a405f7aba0b753af6002ce709daf55d211c434e5452505254590000000000000000000000010000000001312d000000000052aededb2b01000000001976a914a0e8d5fe498e6ca2c2000de13fc9cc470bd9b87088ac00000000

Thanks for your help.

adamkrellenstein commented 10 years ago

I can get a Tx object from this, but not an UnsignedTx. >>> pycoin.tx.Tx.parse(io.BytesIO(h2b('01000000016a26923302377f548764ba5d28d75ff89405f08dbd626876c5e8359ae84955e1010000001976a914a0e8d5fe498e6ca2c2000de13fc9cc470bd9b87088acffffffff036c2a0000000000001976a914f9200a96db875f8906ad34a1f46de4ad0626d4c488ac6c2a00000000000047512103f62c74a1686c41e2639c2c6e9ab336856a405f7aba0b753af6002ce709daf55d211c434e5452505254590000000000000000000000010000000001312d000000000052aec6572c01000000001976a914a0e8d5fe498e6ca2c2000de13fc9cc470bd9b87088ac00000000'))) Tx [3fc4e9b1d3882f86145993ed314229be3d0693165718201499ee2c2f461dd017] (v:1) [TxIn<e15549e89a35e8c5766862bd8df00594f85fd7285dba6487547f37023392266a[1] "OP_DUP OP_HASH160 a0e8d5fe498e6ca2c2000de13fc9cc470bd9b870 OP_EQUALVERIFY OP_CHECKSIG">] [TxOut<0.0001086 "OP_DUP OP_HASH160 f9200a96db875f8906ad34a1f46de4ad0626d4c4 OP_EQUALVERIFY OP_CHECKSIG">, TxOut<0.0001086 "OP_1 03f62c74a1686c41e2639c2c6e9ab336856a405f7aba0b753af6002ce709daf55d 1c434e5452505254590000000000000000000000010000000001312d0000000000 OP_2 OP_CHECKMULTISIG">, TxOut<0.1968327 "OP_DUP OP_HASH160 a0e8d5fe498e6ca2c2000de13fc9cc470bd9b870 OP_EQUALVERIFY OP_CHECKSIG">]

>>> pycoin.tx.UnsignedTx.parse(io.BytesIO(h2b('01000000016a26923302377f548764ba5d28d75ff89405f08dbd626876c5e8359ae84955e1010000001976a914a0e8d5fe498e6ca2c2000de13fc9cc470bd9b87088acffffffff036c2a0000000000001976a914f9200a96db875f8906ad34a1f46de4ad0626d4c488ac6c2a00000000000047512103f62c74a1686c41e2639c2c6e9ab336856a405f7aba0b753af6002ce709daf55d211c434e5452505254590000000000000000000000010000000001312d000000000052aec6572c01000000001976a914a0e8d5fe498e6ca2c2000de13fc9cc470bd9b87088ac00000000'))) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/python3.4/site-packages/pycoin/tx/UnsignedTx.py", line 65, in parse new_txs_out.append(TxOut.parse(f)) File "/usr/lib/python3.4/site-packages/pycoin/tx/TxOut.py", line 51, in parse return self(*parse_struct("QS", f)) File "/usr/lib/python3.4/site-packages/pycoin/serialize/streamer.py", line 39, in parse_struct l.append(self.parse_lookup[c](f)) File "/usr/lib/python3.4/site-packages/pycoin/serialize/bitcoin_streamer.py", line 39, in <lambda> "Q" : (lambda f: struct.unpack("<Q", f.read(8))[0], lambda f, v: f.write(struct.pack("<Q", v))), struct.error: unpack requires a bytes object of length 8

richardkiss commented 10 years ago

UnsignedTx is a custom type that I made up, so it won't be easy to use that type. No matter what you do, you're going to have to get the "Spendable" TxOut outputs that correspond to this transaction's TxIn inputs.

I'm working on a rewrite of how signing works (sponsored by coinsafe.com) that should make things easier. Although not API-stable, take a look at https://github.com/richardkiss/pycoin/tree/offline_tx. As of 19fa9c203e26aeff7cf25c633e3f5a1d812ed497 you can do the following:

  1. Load the input Txs into a dictionary tx_db using tx_cache.tx_for_hash (this will fetch the Txs from blockchain.info, although it can be configured to use blockr.io).
  2. For your parsed Tx tx, call tx.use unspents_from_db(tx_db) using the tx_db.
  3. Build hash160_lookup using tx/script/solvers/build_hash160_lookup_db
  4. Call tx.sign(hash160_lookup)

I believe this will work.

This could be made easier by combining steps 1 and 2 with an adaptor but I haven't done that work yet. (It's more of an organization problem than a programming one.)

adamkrellenstein commented 10 years ago

Got it. Thanks!