Bitcoin Explained (VIII)

By | 2014-12-09

Last time we saw how once you are the “owner” of some bitcoins, that gives you the magical power of being able to specify what conditions the next claimant must meet before they become the “owner”

As flexible as it is, there is a problem with this arrangement. Every client has to be taught how to manufacture every special condition script. Let’s say we want to do an M-of-N payout to implement an escrow transaction. I have my mobile wallet, and you have a web wallet, and we have some arbiter who is using a desktop client. We have to find some way of generating a script, then each of us providing signatures of it, and inputs to it with our coins. There is no standard protocol for doing that, and if there were, it would be a lot of work for each of the wallet developers to implement.

Then what if someone comes up with a new type of condition script: a time-locked M-of-N with trusted party abort?

Finally, this ever-more complicated script has to go in the spending transaction – the person doing the spending then is faced with the miner’s per-byte cost of the transaction, but the spender doesn’t care what complicated measures the next owner wants to impose on their newly owned coins, why should they pay the higher fee?

The solution is pay to script hash or P2SH as it’s often abbreviated.

The idea is that instead of paying to a Bitcoin address, or rather, instead of specifying a condition in the transaction output, the spender specifies nothing other than the hash of the script that is allowed to make the claim. Now, think back to our previous standard claim script:

{
   "lock_time":0,
   "size":341,
   "inputs":[
      {
         "prev_out":{
            "index":0,
            "hash":"ac13d28fd26c1b7e88083c63859d0010919c5b037676f1b424807674f6f54377"
         },
         "script":"00483045022100c99f1a41b28693087617fb03c45e7504654f251423a4c5e2b5b9f4ee8ff04482022027b5f55c36507167ef24f109e5ff44e279447ec80641bdf21299d971035e2fa10148304502200acf7136240358e010f2b09a5818068a00169667218763ae698d4d118751a73c022100abe7a4984cc312ac28ea69c69636f4801b05bc952221342f875eeea237bca8d6014c6952210397c34bc26fd4dc5523c83b0027c18a7505c2668ff8d6ec8834b08074d80bd8f42102f8cc6389cbfac80ee9432a0e2e370b0c0f52b1359df3faddae266a966b11bb712103c072355c488cf8a064ba9d6c2255d7a4ac06b0de4d637870541a815e8f5ce7db53ae"
      }
   ],
   "version":1,
   "vin_sz":1,
   "hash":"f84007fb6b7faba3c5de7fb30526744f9b4e8e3d8841d5d4416c5c8a3d632648",
   "vout_sz":1,
   "out":[
      {
         "script_string":"OP_DUP OP_HASH160 af5cda336bfb8c7543d40e65ae1c3acab95549bf OP_EQUALVERIFY OP_CHECKSIG",
         "address":"1GzEV4jzdR1A715J1rvU5GomW3a5UJZqaZ",
         "value":4672558,
         "script":"76a914af5cda336bfb8c7543d40e65ae1c3acab95549bf88ac"
      }
   ]
}

Implementing this feature required a change to the transaction format in core Bitcoin.

Let’s have a look at a P2SH address in the wild. This is a P2SH address with one transaction paying it, and one transaction paying out from it. First the (abridged) transaction that funds it.

{
   "lock_time":0,
   "size":224,
   "version":1,
   "vin_sz":1,
   "hash":"ac13d28fd26c1b7e88083c63859d0010919c5b037676f1b424807674f6f54377",
   "vout_sz":2,
   "out":[
      {
         "script_string":"OP_HASH160 c57a2d34802679c0916cdb447d128c1708844354 OP_EQUAL",
         "address":"3KhBQK4BzGcYEyi6gxnumVYYDcr9EZY4Qj",
         "value":4673558,
         "script":"a914c57a2d34802679c0916cdb447d128c170884435487"
      },
   ]
}

Then the (abridged) transaction that spends from the P2SH address:

{
   "lock_time":0,
   "size":341,
   "inputs":[
      {
         "prev_out":{
            "index":0,
            "hash":"ac13d28fd26c1b7e88083c63859d0010919c5b037676f1b424807674f6f54377"
         },
         "script":"00483045022100c99f1a41b28693087617fb03c45e7504654f251423a4c5e2b5b9f4ee8ff04482022027b5f55c36507167ef24f109e5ff44e279447ec80641bdf21299d971035e2fa10148304502200acf7136240358e010f2b09a5818068a00169667218763ae698d4d118751a73c022100abe7a4984cc312ac28ea69c69636f4801b05bc952221342f875eeea237bca8d6014c6952210397c34bc26fd4dc5523c83b0027c18a7505c2668ff8d6ec8834b08074d80bd8f42102f8cc6389cbfac80ee9432a0e2e370b0c0f52b1359df3faddae266a966b11bb712103c072355c488cf8a064ba9d6c2255d7a4ac06b0de4d637870541a815e8f5ce7db53ae"
      }
   ],
   "version":1,
   "vin_sz":1,
   "hash":"f84007fb6b7faba3c5de7fb30526744f9b4e8e3d8841d5d4416c5c8a3d632648",
   "vout_sz":1,
}

My “abridgements” in these cases are to remove the inputs and outputs that aren’t under discussion – hopefully that let’s you see the pieces that are relevant, without having so many impenetrable long hex numbers to sift through.

So you can see that the spending transaction’s input references the funding transaction’s output (funding_hash:index). Next notice that the condition script is smaller than the standard one we examined previously. The funding (output) script is:

OP_HASH160
PUSH_20 c57a2d34802679c0916cdb447d128c1708844354
OP_EQUAL

BIP16 describes how a P2SH output may be “spotted” and it is this form of address that does it OP_HASH160, PUSH_20, OP_EQUAL). It’s not just a script, it’s a marker to tell the Bitcoin core that this script is not, in fact, to be used as a script; rather that this is a P2SH output. The fact that it is a P2SH-patterned output is also what allows anyone displaying this output to show it as a class-5 address (which is a “3” in bas58).

I think, if the Bitcoin developers had known what they know now, there would never have been anything but P2SH – the output “scripts” would have been only a hash, and nothing else. The three extra bytes here are not the most painful of overheads, so it’s not the end of the world.

Running this as if it were a script using pre-P2SH rules, will always fail. An OP_HASH160 is 33 bytes, so the OP_EQUAL operation will never return true if this is not identified as a P2SH marker.

It’s important to remember that this is no longer a script. It is a backward-compatible way of switching bitcoin to P2SH mode and supply the hash of the only claimant script that is allowed to claim it. The claimant script must not only successfully complete, but it must first match this hash before bitcoin will even attempt to execute it. Let’s look in more detail then at the claim (input) script:

00 (OP_FALSE)
48 (PUSH_72)
   30 45 02 21 00 c9 9f 1a   41 b2 86 93 08 76 17 fb
   03 c4 5e 75 04 65 4f 25   14 23 a4 c5 e2 b5 b9 f4
   ee 8f f0 44 82 02 20 27   b5 f5 5c 36 50 71 67 ef
   24 f1 09 e5 ff 44 e2 79   44 7e c8 06 41 bd f2 12
   99 d9 71 03 5e 2f a1 01
48 (PUSH_72)
   30 45 02 20 0a cf 71 36   24 03 58 e0 10 f2 b0 9a
   58 18 06 8a 00 16 96 67   21 87 63 ae 69 8d 4d 11
   87 51 a7 3c 02 21 00 ab   e7 a4 98 4c c3 12 ac 28
   ea 69 c6 96 36 f4 80 1b   05 bc 95 22 21 34 2f 87
   5e ee a2 37 bc a8 d6 01
4c 69 (OP_PUSHDATA1(105))
   52 21 03 97 c3 4b c2 6f   d4 dc 55 23 c8 3b 00 27
   c1 8a 75 05 c2 66 8f f8   d6 ec 88 34 b0 80 74 d8
   0b d8 f4 21 02 f8 cc 63   89 cb fa c8 0e e9 43 2a
   0e 2e 37 0b 0c 0f 52 b1   35 9d f3 fa dd ae 26 6a
   96 6b 11 bb 71 21 03 c0   72 35 5c 48 8c f8 a0 64
   ba 9d 6c 22 55 d7 a4 ac   06 b0 de 4d 63 78 70 54
   1a 81 5e 8f 5c e7 db 53   ae

This, again, is a P2SH-specific layout. If run as a script directly, OP_FALSE would simply cause it to fail instantly. Remember though that the funding “script” is known at this point, and we’ve already seen that we can detect the funding script as a P2SH hash, and hence know that the claiming “script” should be interpreted as a P2SH claim. These have some special rules:

  • Must begin with OP_FALSE.
  • Must then only contain PUSH operations.

Other than this the script is simply run. The magic of a P2SH script then happens: the last item on the stack is popped, and de-serialized to form the condition script, its hash validated against the P2SH we got from the funding transaction, then the stack that remains is used as the initial stack for that condition script. The equivalent of running the following combined script:

00 (OP_FALSE)
48 (PUSH_72)
   30 45 02 21 00 c9 9f 1a   41 b2 86 93 08 76 17 fb
   03 c4 5e 75 04 65 4f 25   14 23 a4 c5 e2 b5 b9 f4
   ee 8f f0 44 82 02 20 27   b5 f5 5c 36 50 71 67 ef
   24 f1 09 e5 ff 44 e2 79   44 7e c8 06 41 bd f2 12
   99 d9 71 03 5e 2f a1 01
48 (PUSH_72)
   30 45 02 20 0a cf 71 36   24 03 58 e0 10 f2 b0 9a
   58 18 06 8a 00 16 96 67   21 87 63 ae 69 8d 4d 11
   87 51 a7 3c 02 21 00 ab   e7 a4 98 4c c3 12 ac 28
   ea 69 c6 96 36 f4 80 1b   05 bc 95 22 21 34 2f 87
   5e ee a2 37 bc a8 d6 01
52 (OP_2)
21 (PUSH_33)
   03 97 c3 4b c2 6f d4 dc   55 23 c8 3b 00 27 c1 8a
   75 05 c2 66 8f f8 d6 ec   88 34 b0 80 74 d8 0b d8
   f4
21 (PUSH_33)
   02 f8 cc 63 89 cb fa c8   0e e9 43 2a 0e 2e 37 0b
   0c 0f 52 b1 35 9d f3 fa   dd ae 26 6a 96 6b 11 bb
   71
21 (PUSH_33)
   03 c0 72 35 5c 48 8c f8   a0 64 ba 9d 6c 22 55 d7
   a4 ac 06 b0 de 4d 63 78   70 54 1a 81 5e 8f 5c e7
   db
53 (OP_3)
ae (OP_CHECKMULTISIG)

This (as we’d expect for Brawker’s usage) is a 2-of-3 multisig condition script. All the cryptographic work is done in OP_CHECKMULTISIG, which I won’t describe in detail here – suffice to say it’s even more complex than OP_CHECKSIG that we saw previously. Let me simplify this by making it more symbolic:

PUSH_TRANSACTION_SIGNATURE#1
PUSH_TRANSACTION_SIGNATURE#2
PUSH_2
PUSH_DESTINATION_PUBLIC_KEY#1
PUSH_DESTINATION_PUBLIC_KEY#2
PUSH_DESTINATION_PUBLIC_KEY#3
PUSH_3
OP_CHECKMULTISIG

In a higher level language, we might have written this as

OP_CHECKMULTSIG([KEY#1, KEY#2, KEY#3], [SIG#1, SIG#2])

Its job is to check that the signatures are valid for two of the three keys. The signatures, as for OP_CHECKSIG we saw before, are signatures of the transaction outputs (which aren’t shown, as we’re not concerning ourselves with were the money goes next).

It’s important to note the similarity to the standard bitcoin transaction form.

PUSH_TRANSACTION_SIGNATURE      )  from input script in claiming
PUSH_CLAIM_PUBLIC_KEY           )  transaction

OP_DUP                          )
OP_HASH160                      )  from output script in
PUSH_NEW_OWNER_PUBLIC_KEY       )  funding transaction
OP_EQUALVERIFY                  )
OP_CHECKSIG                     )

And our P2SH script:

PUSH_TRANSACTION_SIGNATURE#1    )  from input script in claiming
PUSH_TRANSACTION_SIGNATURE#2    )  transaction

PUSH_2                          )
PUSH_DESTINATION_PUBLIC_KEY#1   )  from input script in claiming
PUSH_DESTINATION_PUBLIC_KEY#2   )  transaction, but must have a
PUSH_DESTINATION_PUBLIC_KEY#3   )  hash equal to the hash in the
PUSH_3                          )  funding transaction output
OP_CHECKMULTISIG                )

What’s important is not the change from an OP_CHECKSIG to OP_CHECKMULTISIG (that’s just because multisig-in-claimer is one key use of P2SH, so that’s were we’re first seeing it used), rather it’s that the condition script and the claim script were both stored in the claiming transaction; and yet, the funder was able to ensure that the claim script was the one they intended by requiring a particular hash-of-condition-script.

Note that nothing would stop us supplying a single-pay transaction of exactly the same form in a P2SH script – making P2SH able to do all that the traditional form do. It’s this property that made me say “I think, if the Bitcoin developers had known what they know now, there would never have been anything but P2SH”.

A particularly interesting result is that there is a one-to-one correspondence between a particular bitcoin address and the standard-form script that pays it – meaning wallets can easily detect payments to your addresses regardless of how they are paid.

The advantages then:

  • It may seem convoluted, but a lot of that convolution is because of the need to remain backward compatible. Fundamentally, P2SH is simpler than traditional transactions because the script is no longer split across transactions.

  • It becomes possible to have many payments to a single P2SH hash, without every single one needing to contain a copy of the condition script. This is a far more satisfying arrangement, it’s the equivalent of us identifying bank accounts by account number rather than the name of the bank, the address of the bank, and the name of the account holder. This has the additional advantage that storage in the blockchain is not wasted with needless duplication.

  • Wallets do not need to implement a convoluted condition script negotiation protocol. The single hash, of the same form as any other Bitcoin address, is all that’s needed. Wallets need only be able to support pay-to-hash and any complexity of target is supported.

  • The receiver of the funds gets to choose the level of complexity of the usage conditions. The payer doesn’t need to be involved in whatever internal arrangements the receiver is using for managing their funds. For example, an e-commerce site might want to operate a 2-of-2 validation on all their funds – why should every customer, when they pay them have to go through the inconvenience of getting a copy of the script that implements that requirement? They do not care at all, and P2SH lets them do that.

  • Bitcoin transaction fees contain a charge per byte of transaction. Since it is the receiver who is imposing complicated conditions on use of the funds, it is they who should pay the fee. P2SH funding transactions are always the same size, regardless of the complexity of the claiming conditions.

Leave a Reply