Bitcoin Explained (VII)

This series of articles is an extension to my earlier Bitcoin Explained series. It absolutely isn’t necessary for understanding bitcoin, the previous articles are still valid. However, I recently made use of the Brawker website, and wanted to understand their use of P2SH more fully. This new series of articles is the result of me trying to gain that understanding, and on the way explaining transactions in more detail.

We’ll begin with the idea of bitcoin scripts.

First let me repeat the diagram I gave in Part VI, as to the contents of a particular transaction (#5004 in this case):

TX#5000:out#1 = ...
TX#5000:out#2 = ...
TX#5000:out#3 =  50 BTC -
                         \
       ...                \
TX#5001:out#3 = ...        \             /-- 80 BTC -> PUBKEY#9292
TX#5001:out#5 = 150 BTC ----+- TX#5004 -+
TX#5001:out#6 = ...        /  (155 BTC)  \-- 70 BTC -> PUBKEY#8755
                          /
TX#5002:out#1 = ...      /
TX#5002:out#2 =   5 BTC -
       ...

Earlier we found that the inputs were really references to earlier outputs; and in fact were “claims” on those outputs. The outputs of the transaction aren’t really outputs, as they don’t go anywhere, rather they are “conditions of claim”. Bitcoin transactions have their own, highly specific, scripting language that lets us describe the output conditions, and then meet those conditions with claims.

Let me relabel the above diagram with that in mind:

TX#5000:CONDITION#3
CLAIM()  -> 50 BTC -
                     \
                      \
TX#5001:CONDITION#5    \             /-- 80 BTC -> CONDITION#0
CLAIM()  -> 150 BTC ----+- TX#5004 -+
                       /  (155 BTC)  \-- 70 BTC -> CONDITION#1
                      /
TX#5002:CONDITION#2  /
CLAIM()  ->   5 BTC -

Our “inputs” then, aren’t really inputs, they are a reference to an earlier transaction’s condition script, and a claim script that validates the claim.

These “scripts” are a series of operators that perform actions on a stack. Not unlike Postscript or Forth.

Let’s look first at a typical transaction; here’s one I picked at random with a few clicks on . We can get the raw hex of the transaction by appending “?format=hex” to the transaction display URI.

01 00 00 00 01 66 01 62   1c 41 a6 dc a0 38 1e 17
3b 9a 8d 89 35 64 e9 3d   ea 95 9b 51 04 16 4c 82
09 9e 34 d2 3c 06 00 00   00 6b 48 30 45 02 21 00
a7 55 54 4e 79 a1 d3 ff   9e 68 3e e5 34 cb a7 1e
2a 7d b5 78 dd 39 2f 6f   67 d1 03 e9 7e 40 80 3f
02 20 53 be 17 c6 dd f5   f7 b6 b1 64 44 a8 08 9f
ad 94 3d f0 1b 17 a3 08   89 96 c1 90 76 69 54 88
ba 4a 01 21 03 74 51 ce   c1 99 e4 6e cb 33 49 11
4d 67 48 cc 4f 12 b9 40   63 2c b2 a3 73 92 83 63
fe 1a fa 10 f8 ff ff ff   ff 02 10 ae 93 03 00 00
00 00 19 76 a9 14 d7 c5   3f 1e d6 9e 35 f2 4b 3a
c1 f0 22 7f 4f 17 73 dd   35 01 88 ac 80 d9 e9 02
00 00 00 00 19 76 a9 14   38 d4 68 61 53 49 86 69
4f b6 a0 b6 98 c0 88 a1   56 9d 44 15 88 ac 00 00
00 00 

Remember that the bitcoin formats are pretty dense binary as every byte counts when we’re storing billions of these things. We could decode this by hand for our edification (I’d encourage you to do so if you are interested in programming, it’s good for the soul), but http://blockchain.info also have a very nice raw decoder interface we can make use of.

{
   "lock_time":0,
   "size":226,
   "inputs":[
      {
         "prev_out":{
            "index":6,
            "hash":"3cd2349e09824c1604519b95ea3de96435898d9a3b171e38a0dca6411c620166"
         },
         "script":"483045022100a755544e79a1d3ff9e683ee534cba71e2a7db578dd392f6f67d103e97e40803f022053be17c6ddf5f7b6b16444a8089fad943df01b17a3088996c19076695488ba4a0121037451cec199e46ecb3349114d6748cc4f12b940632cb2a373928363fe1afa10f8"
      }
   ],
   "version":1,
   "vin_sz":1,
   "hash":"85de8fa55135a4a0b126cafabe30b0b9b6034121cb7439740a30aaf34c25d127",
   "vout_sz":2,
   "out":[
      {
         "script_string":"OP_DUP OP_HASH160 d7c53f1ed69e35f24b3ac1f0227f4f1773dd3501 OP_EQUALVERIFY OP_CHECKSIG",
         "address":"1LftaBA1WVJCPKen9tmrb8g5DgXAVghZKy",
         "value":60010000,
         "script":"76a914d7c53f1ed69e35f24b3ac1f0227f4f1773dd350188ac"
      },
      {
         "script_string":"OP_DUP OP_HASH160 38d46861534986694fb6a0b698c088a1569d4415 OP_EQUALVERIFY OP_CHECKSIG",
         "address":"16BVJ6G1FdocrgQ5kAQHLwxrA9gxfftXRX",
         "value":48880000,
         "script":"76a91438d46861534986694fb6a0b698c088a1569d441588ac"
      }
   ]
}

This is the same information as was in the block of hex bytes, but decoded into more readable fields for us – there is actually a little more than that here, as some additional hashes are calculated for convenience, but I’ll not go into them now. Notice one input and two outputs. We’ll ignore the outputs of this transaction, and instead focus on this transaction’s single input.

   "inputs":[
      {
         "prev_out":{
            "index":6,
            "hash":"3cd2349e09824c1604519b95ea3de96435898d9a3b171e38a0dca6411c620166"
         },
         "script":"483045022100a755544e79a1d3ff9e683ee534cba71e2a7db578dd392f6f67d103e97e40803f022053be17c6ddf5f7b6b16444a8089fad943df01b17a3088996c19076695488ba4a0121037451cec199e46ecb3349114d6748cc4f12b940632cb2a373928363fe1afa10f8"
      }
   ],

The script itself, needs a little bit of decoding, we will do this one by hand:

48 (PUSH_72)
   30 45 02 21 00 a7 55 54   4e 79 a1 d3 ff 9e 68 3e
   e5 34 cb a7 1e 2a 7d b5   78 dd 39 2f 6f 67 d1 03
   e9 7e 40 80 3f 02 20 53   be 17 c6 dd f5 f7 b6 b1
   64 44 a8 08 9f ad 94 3d   f0 1b 17 a3 08 89 96 c1
   90 76 69 54 88 ba 4a 01
21 (PUSH_33)
   03 74 51 ce c1 99 e4 6e   cb 33 49 11 4d 67 48 cc
   4f 12 b9 40 63 2c b2 a3   73 92 83 63 fe 1a fa 10
   f8 

The PUSH_nn bitcoin script simply pushes the following nn bytes of script onto the execution stack. To make this more straight forward to understand, I’m going to write this as:

PUSH_TRANSACTION_SIGNATURE
PUSH_CLAIM_PUBLIC_KEY

Moving on to the other fields in the input array, we can see that this script is claiming index 6 of previous transaction 3cd2349e09824c1604519b95ea3de96435898d9a3b171e38a0dca6411c620166. We can use http://blockchain.info to look that transaction up too. I won’t repeat the whole hex and decode, you can look at them yourself should you be interested. Instead, let’s just look at output 6, which is what our original transaction is claiming.

{
   "script_string":"OP_DUP OP_HASH160 b442e3f5440fa5858807b18a4c9aff6c773648df OP_EQUALVERIFY OP_CHECKSIG",
   "address":"1HS8mAHuiemhxNdw7gGFYJTY8ioa3MYL4N",
   "value":108900000,
   "script":"76a914b442e3f5440fa5858807b18a4c9aff6c773648df88ac"
},

We see then that our transaction is claiming 108900000 satoshis. The address and script_string fields have been added for convenience, consider them informational for now. What we’re interested in is the script.

OP_DUP
OP_HASH160
b442e3f5440fa5858807b18a4c9aff6c773648df
OP_EQUALVERIFY
OP_CHECKSIG

The vast majority (at present) of bitcoin outputs are condition scripts of this form – and in truth, Bitcoin core maintains a list of what it calls “standard” scripts – so entirely arbitrary scripts are not allowed in the interests of not ending up with abuse of all the full node’s CPUs. A claim is tested for validity by prepending the claim script to it, then running the whole. The hex in the middle is the hex equivalent of the payee’s bitcoin address. Bitcoin addresses are usually presented in base-58 and with a type-code and checksum, this is the raw hex form. A bitcoin “address” is in truth, the RIPE-160 hash of the public key in an ECDSA public/private key pair.

Let’s combine the claim and the condition, and give the hex bytes symbolic names.

PUSH_TRANSACTION_SIGNATURE
PUSH_CLAIM_PUBLIC_KEY
OP_DUP
OP_HASH160
PUSH_NEW_OWNER_PUBLIC_KEY
OP_EQUALVERIFY
OP_CHECKSIG

Now we’re in a position to “run” this script. Remember that the script operates on a stack. That stack starts out empty, PUSH_TRANSACTION_SIGNATURE leaves it as:

TRANSACTION_SIGNATURE

Then PUSH_CLAIM_PUBLIC_KEY adds its data (I’ll draw the stack growing downwards):

TRANSACTION_SIGNATURE
CLAIM_PUBLIC_KEY

OP_DUP copies the last item on the stack.

TRANSACTION_SIGNATURE
CLAIM_PUBLIC_KEY
CLAIM_PUBLIC_KEY

OP_HASH160 calculates the RIPE-160 hash of the last stack item. Recall that a RIPE-160 hash of a public key is also a bitcoin address. So this operation in fact converts a public key into a bitcoin address:

TRANSACTION_SIGNATURE
CLAIM_PUBLIC_KEY
CLAIM_BITCOIN_ADDRESS

PUSH_NEW_OWNER_PUBLIC_KEY pushes its data onto the stack:

TRANSACTION_SIGNATURE
CLAIM_PUBLIC_KEY
CLAIM_BITCOIN_ADDRESS
NEW_OWNER_PUBLIC_KEY

OP_EQUALVERIFY pops the last two items and compares them for equality; aborting in failure if they are not. Remember that PAYEE_ADDRESS was part of the condition script, and CLAIM_PUBLIC_KEY was from the claim script. So this operation is verifying that the person claiming this output is the owner of the public key referenced in the claimed transactions condition script.

TRANSACTION_SIGNATURE
OWNER_PUBLIC_KEY

And we’re back to were we began, but this time so CLAIM_PUBLIC_KEY becomes OWNER_PUBLIC_KEY. We’ve now got one operation left in the script, OP_CHECKSIG. This is a biggie. OP_CHECKSIG is a complex, compound operator, and it’s shortness is deceptive. (If I’m honest, I find OP_CHECKSIG one of the smelliest bits of bitcoin precisely because it’s operations are so complex, it relies on a very particular set of byte formats and memory operations, and you’d be basically very worried about reimplementing it yourself in case you differed even slightly from the bitcoin-core implementation).

What it does is take the rest of the claiming transaction and verify that the signature of it, TRANSACTION_SIGNATURE, was generated by OWNER_PUBLIC_KEY. Phew. “The rest of the claiming transaction” is important – what’s being signed is the output condition section of the new transaction – i.e. the conditions that the next claimant must meet. That means that only the owner of OWNER_PUBLIC_KEY can specify how his property can next be claimed.

Let’s simplify this: if I want to pay you, I must claim some other coins that have previously specified that only I can claim them. I must then specify who is the next claimant – you. I do that by saying (in script form) “only a signature generated by PAYEE_ADDRESS can specify the next condition script for these coins”. Since I do not own the private key for PAYEE_ADDRESS, even I cannot meet my own condition – only you can.

That’s the current standard bitcoin transaction that every client and every node will use by default. It should be clear though, that the claim script we specified was entirely arbitrary. Nothing, in fact, stops us from specifying the following condition script:

OP_TRUE

Anyone would be able to meet this condition; all they need do is create a transaction that claims to meet it, and it’s theirs. It will become a “first to pick up the money I found lying around” situation. You can imagine then that the variety of possible condition scripts is infinite.

Next time, I’ll discuss the limitations of this arrangement, and how bitcoin has addressed those limitations with P2SH (pay to script hash).

This entry was posted in FussyLogic and tagged . Bookmark the permalink. Trackbacks are closed, but you can post a comment.

Post a Comment

You must be logged in to post a comment.