Bitcoin Forum

Bitcoin => Development & Technical Discussion => Topic started by: HeadsOrTails on July 16, 2015, 05:25:34 AM



Title: Using Pycoin to spend non-standard scripts
Post by: HeadsOrTails on July 16, 2015, 05:25:34 AM
Let's take http://test.webbtc.com/tx/76cf175637ee40281b9893e691113a9b5c835d8eb44875954a68b9d582b14a9e (http://test.webbtc.com/tx/76cf175637ee40281b9893e691113a9b5c835d8eb44875954a68b9d582b14a9e) as an example (Testnet, TxID: 76cf175637ee40281b9893e691113a9b5c835d8eb44875954a68b9d582b14a9e (http://test.webbtc.com/tx/76cf175637ee40281b9893e691113a9b5c835d8eb44875954a68b9d582b14a9e.json))

(I found this through the Unknown P2SH Scripts page of webbtc.com)

Code:
2
OP_PICK
1
OP_PICK
OP_NOT
OP_BOOLOR
OP_VERIFY
1
OP_PICK
OP_VERIFY
OP_2DROP
OP_DROP
1

How would I use Pycoin to play around with non-standard scripts like this? Alternatively, pybitcointools.


Title: Re: Using Pycoin to spend non-standard scripts
Post by: d4n13 on July 17, 2015, 06:17:43 PM
How would I use Pycoin to play around with non-standard scripts like this? Alternatively, pybitcointools.
Writing a Script "compiler" in python should be very straight forward.  PyBitcoinTools will parse out the scripts with deserialize, then you take the Script 'asm' and write a decompiler.

If you then put the 'asm' data back in the dict and serialize with pybitcointools you should be good to go.

I'll give it a shot next week.


Title: Re: Using Pycoin to spend non-standard scripts
Post by: HeadsOrTails on July 18, 2015, 06:41:04 AM
How would I use Pycoin to play around with non-standard scripts like this? Alternatively, pybitcointools.
Writing a Script "compiler" in python should be very straight forward.  PyBitcoinTools will parse out the scripts with deserialize, then you take the Script 'asm' and write a decompiler.

If you then put the 'asm' data back in the dict and serialize with pybitcointools you should be good to go.

Thanks for the reply!

I'm really familiar with pybitcointools, so I can certainly see how your suggestion works. However, the issue is when using serialize_script; namely, if you've got objects (like a pubkey), the serialize_script method doesn't add push codes for the size of the object.

Ie:

Code:
my_script = serialize_script([OP_foo, OP_bar, "20byte_pubkey_hash", OP_spam])

myscript will return 11 22 01234567890123456789 33 instead of 11 22 14 01234567890123456789 33


Title: Re: Using Pycoin to spend non-standard scripts
Post by: d4n13 on July 18, 2015, 06:53:46 AM
I'm really familiar with pybitcointools, so I can certainly see how your suggestion works. However, the issue is when using serialize_script; namely, if you've got objects (like a pubkey), the serialize_script method doesn't add push codes for the size of the object.

Ie:

Code:
my_script = serialize_script([OP_foo, OP_bar, "20byte_pubkey_hash", OP_spam])

myscript will return 11 22 01234567890123456789 33 instead of 11 22 14 01234567890123456789 33
LOL...

I'm terribly unfamiliar with PyBitcoinTools, as my 'Wow there is a PyBitcoinTools module" post from today will likely attest.

I never realized there was a serialize_script method.  I was simply thinking of the serialize(tx_dict).  I was thinking you would right your own (or overwrite) the serialize_script method.  I mean there are less than 100 op codes right?


Title: Re: Using Pycoin to spend non-standard scripts
Post by: HeadsOrTails on July 18, 2015, 08:49:12 AM
I never realized there was a serialize_script method.  I was simply thinking of the serialize(tx_dict).  I was thinking you would right your own (or overwrite) the serialize_script method.  I mean there are less than 100 op codes right?[/size]

Yeah, that's certainly do-able. I'll fork the pybitcointools library and see how I go with it.

I've been looking at Peter Todd's python-bitcoinlib (https://github.com/petertodd/python-bitcoinlib/) and Richard Kiss' Pycoin (https://github.com/richardkiss/pycoin), mainly because the classes are much more powerful for playing around with scripting, SIGHASH, etc. That being said, the OOP for python-bitcoinlib tries to emulate the Core software naming conventions, and it's really complicated.

If anyone else can provide a single example of a non-standard script Tx using Pycoin/python-bitcoinlib, I'd really appreciate it.


d4n13: Thanks for the input, I'll look at trying this with pybitcointools


Title: Re: Using Pycoin to spend non-standard scripts
Post by: d4n13 on July 19, 2015, 06:28:34 AM
Yep... there is a bug in there somewhere...

Try the following
Code:
from bitcoin import *
serialize_script(deserialize_script('ac'))
---
RuntimeError: maximum recursion depth exceeded while calling a Python object


Title: Re: Using Pycoin to spend non-standard scripts
Post by: d4n13 on July 20, 2015, 07:13:30 AM
I was simply thinking of the serialize(tx_dict).  I was thinking you would right your own (or overwrite) the serialize_script method.  I mean there are less than 100 op codes right?

It worked....

https://i.imgur.com/hW6pjGW.png

Transaction 5e4cf125218fcab2746303485ce3a2a74dc12271f678e851fc95fd97cd0153e4 (http://test.webbtc.com/tx/5e4cf125218fcab2746303485ce3a2a74dc12271f678e851fc95fd97cd0153e4) puts "Hello World" on the blockchain
Transaction 602c4d614351ee615ee1dc280c16a4cf962ccc928a8c3a0dca6f9ccd46104976 (http://test.webbtc.com/script/602c4d614351ee615ee1dc280c16a4cf962ccc928a8c3a0dca6f9ccd46104976:0) spends "Hello World" on the blockchain.

Interestingly... the script debugger says OP_SIGCHECK failed... but it didn't.  Also, the funky output script does an interesting job of making the coins quasi-anonomized.


Title: Re: Using Pycoin to spend non-standard scripts
Post by: HeadsOrTails on July 20, 2015, 09:09:31 AM
Yep... there is a bug in there somewhere...

Try the following
Code:
from bitcoin import *
serialize_script(deserialize_script('ac'))
---
RuntimeError: maximum recursion depth exceeded while calling a Python object

Yeh, I often run scripts on iOS Pythonista, and there's a lot of recursion errors that come up since by default the recursion depth is 256; so setting sys.setrecursiondepth(512) often works in that environment.

The pybitcointools bug is strange because the code serializes multisig scripts, but there's a bug with the CHECKMULTISIG; so instead of serializing the 'ae', it just appends 'ae' to the end of the returned string.

I am looking at using this code:

Code:
def mk_script(*args):
    # lst = ['76', 'a9', '14', 'dd6cce9f255a8cc17bda8ba0373df8e861cb866e', '88', 'ac']
    if len(args) == 1 and isinstance(args[0], (list, tuple))
        lst = list(args[0])
    elif len(args) > 1 and all(map(lambda o: isinstance(o, str), args)):
        lst = [args]
    else:
        lst = [changebase(str(x), 10, 16, 2) if isinstance(x, (int, long)) else x for x in args]
   
    llens = [len(changebase(x, 16, 256, 1)) for x in lst]    # byte lengths
    lint = map(lambda h: decode(h, 16), lst)                 # list as ints
   
    asm = 0xff
    for i in range(len(lint)):
        asm = asm << (8*llens[i]) | lint[i]
   
    asmhex = "0x" + encode(asm, 16, (sum(llens) + 1)*2)
    final = asmhex.partition('0xff')[-1]
    return final

How would I go about tweaking this code to avoid the manual addition of push20?


ie. I want to use
Code:
mk_script(['76', 'a9', 'dd6cce9f255a8cc17bda8ba0373df8e861cb866e', '88', 'ac'])
... instead of
Code:
mk_script(['76', 'a9', '14', 'dd6cce9f255a8cc17bda8ba0373df8e861cb866e', '88', 'ac'])
(note the "14"  preceding the pubkeyhash, which acts as push 20 bytes).

I'd prefer to avoid using the push bytes


Title: Re: Using Pycoin to spend non-standard scripts
Post by: d4n13 on July 20, 2015, 09:39:45 AM
How would I go about tweaking this code to avoid the manual addition of push20?


ie. I want to use
Code:
mk_script(['76', 'a9', 'dd6cce9f255a8cc17bda8ba0373df8e861cb866e', '88', 'ac'])
... instead of
Code:
mk_script(['76', 'a9', '14', 'dd6cce9f255a8cc17bda8ba0373df8e861cb866e', '88', 'ac'])
(note the "14"  preceding the pubkeyhash, which acts as push 20 bytes).

I'd prefer to avoid using the push bytes
Yeah, the bit about push20 is clean... no bug.  The fact that you have a list item of 40 hex digits is proof that deserialize consumed a push20.  If it consumed a push10 you would have a list item of 20 hex digits instead..

get it?


Title: Re: Using Pycoin to spend non-standard scripts
Post by: edmundedgar on July 20, 2015, 12:41:57 PM
I don't know if this is related but something seems to have changed in the way pybitcointool serializes scripts since I was working with this around:
https://github.com/vbuterin/pybitcointools/commit/87aaf6dc3f38d853dd8cb324a4eaf72ae309d322
...which I think is still the version you get if you do
pip install pybitcointool
(This is before the name changed to "bitcoin".)

I haven't had a chance to get to the bottom of this and it may be that the old version is broken and the new one is right, but you might like to try comparing what you get with the old version.


Title: Re: Using Pycoin to spend non-standard scripts
Post by: d4n13 on July 20, 2015, 11:26:09 PM
Yep... there is a bug in there somewhere...

Try the following
Code:
from bitcoin import *
serialize_script(deserialize_script('ac'))
---
RuntimeError: maximum recursion depth exceeded while calling a Python object

Submitted issue #104 (https://github.com/vbuterin/pybitcointools/issues/104)


Title: Re: Using Pycoin to spend non-standard scripts
Post by: HeadsOrTails on July 21, 2015, 03:06:08 AM
Yep... there is a bug in there somewhere...

Try the following
Code:
from bitcoin import *
serialize_script(deserialize_script('ac'))
---
RuntimeError: maximum recursion depth exceeded while calling a Python object

Submitted issue #104 (https://github.com/vbuterin/pybitcointools/issues/104)

I fixed it (https://github.com/vbuterin/pybitcointools/pull/103)


Title: Re: Using Pycoin to spend non-standard scripts
Post by: HeadsOrTails on July 21, 2015, 03:15:40 AM
Maybe we can work on this Tx: http://test.webbtc.com/tx/2e7f518ce5ab61c1c959d25e396bc9d3d684d22ea86dc477b1a90329c6ca354f (http://test.webbtc.com/tx/2e7f518ce5ab61c1c959d25e396bc9d3d684d22ea86dc477b1a90329c6ca354f)

I've set up the script like this:

Code:
OP_IF
0330ed33784ee1891122bc608b89da2da45194efaca68564051e5a7be9bee7f63f
OP_CHECKSIGVERIFY
OP_ELSE
80bf07
OP_NOP2
OP_DROP
OP_ENDIF
042daa93315eebbe2cb9b5c3505df4c6fb6caca8b756786098567550d4820c09db988fe9997d049d687292f815ccd6e7fb5c1b1a91137999818d17c73d0f80aef9
OP_CHECKSIG

So its a master key, which is sha256("master"*42)

How would I use pybitcointools to spend this script?

Nb script:
Code:
myscript = "63210330ed33784ee1891122bc608b89da2da45194efaca68564051e5a7be9bee7f63fad670380bf07b1756841042daa93315eebbe2cb9b5c3505df4c6fb6caca8b756786098567550d4820c09db988fe9997d049d687292f815ccd6e7fb5c1b1a91137999818d17c73d0f80aef9ac"


Title: Re: Using Pycoin to spend non-standard scripts
Post by: d4n13 on July 21, 2015, 04:07:50 AM
Maybe we can work on this Tx: http://test.webbtc.com/tx/2e7f518ce5ab61c1c959d25e396bc9d3d684d22ea86dc477b1a90329c6ca354f (http://test.webbtc.com/tx/2e7f518ce5ab61c1c959d25e396bc9d3d684d22ea86dc477b1a90329c6ca354f)

I've set up the script like this:

Code:
OP_IF
0330ed33784ee1891122bc608b89da2da45194efaca68564051e5a7be9bee7f63f
OP_CHECKSIGVERIFY
OP_ELSE
80bf07
OP_NOP2
OP_DROP
OP_ENDIF
042daa93315eebbe2cb9b5c3505df4c6fb6caca8b756786098567550d4820c09db988fe9997d049d687292f815ccd6e7fb5c1b1a91137999818d17c73d0f80aef9
OP_CHECKSIG

So its a master key, which is sha256("master"*42)

How would I use pybitcointools to spend this script?

Nb script:
Code:
myscript = "63210330ed33784ee1891122bc608b89da2da45194efaca68564051e5a7be9bee7f63fad670380bf07b1756841042daa93315eebbe2cb9b5c3505df4c6fb6caca8b756786098567550d4820c09db988fe9997d049d687292f815ccd6e7fb5c1b1a91137999818d17c73d0f80aef9ac"

I was working on a cleaner implementation... but got pulled away tonight...

Basically you have to override
Code:
sign(tx,i,priv,hashcode)
You want to change line 341 of transaction.py (https://github.com/vbuterin/pybitcointools/blob/master/bitcoin/transaction.py#L341) so that it accepts an input script.  You need to feed it the scriptPubKey of the UTXO you are spending.
Code:
    signing_tx = signature_form(tx, i, '<utxo_scriptPubKey>', hashcode)

Then you need to sign the transaction with pubkey 042d...d17c, and copy the sig into "sig1", then sign the transaction with pubkey 0330...f63f, and copy the sig into "sig2".  

Finally your non-standard scriptSig is:
Code:
<sig1>
<sig2>
I've done it on the "Hello World" example I did above, but the code is kinda ugly.

Let me clean it up and I'll write a pybitcointools implementation to spend the coin.


Title: Re: Using Pycoin to spend non-standard scripts
Post by: HeadsOrTails on July 21, 2015, 04:15:28 AM

Basically you have to override
Code:
sign(tx,i,priv,hashcode)
You want to change line 341 of transaction.py (https://github.com/vbuterin/pybitcointools/blob/master/bitcoin/transaction.py#L341) so that it accepts an input script.  You need to feed it the scriptPubKey of the UTXO you are spending.
Code:
    signing_tx = signature_form(tx, i, '<utxo_scriptPubKey>', hashcode)

Then you need to sign the transaction with pubkey 042d...d17c, and copy the sig into "sig1", then sign the transaction with pubkey 0330...f63f, and copy the sig into "sig2".  

Finally your non-standard scriptSig is:
Code:
<sig1>
<sig2>
I've done it on the "Hello World" example I did above, but the code is kinda ugly.

Let me clean it up and I'll write a pybitcointools implementation to spend the coin.

Ah ha! OK, that makes perfect sense. I'll try it out.

There's a fork of pybitcointools which is updated more often here (https://github.com/simcity4242/pybitcointools). One of the issues I've encountered with pybitcointools is the DER encoding; the fork checks for DER encoding, whereas the original pybitcointools isn't even BER in some instances (eg if the r or s value has the leading bit set and is less than 2**255, the encoding doesn't prepend nullbytes)