Learning-Bitcoin-from-the-C.../09_4_Scripting_a_P2PKH.md
2020-07-15 13:33:01 -10:00

22 KiB
Raw Blame History

9.4: Scripting a P2PKH

NOTE: This is a draft in progress, so that I can get some feedback from early reviewers. It is not yet ready for learning.

P2PKH addresses are quickly fading in popularity due to the advent of SegWit, but nonetheless they remain a great building block for understanding Bitcoin, and especially for understanding Bitcoin scripts. (We'll take a quick look at how Segwit-native P2WPKH scripts work differently in the next section.)

Understand the Unlocking Script

We've long said that when funds are sent to a Bitcoin address, they're locked to the private key associated with that address. This is managed through the scriptPubKey of a P2PKH transaction, which is designed such that it requires the recipient to have the private key associated with the the P2PKH Bitcoin address. To be precise, the recipient must supply both the public key linked to the private key and a signature generated by the private key.

That's what the scriptSig unlocking script seen in §9.1 showed: a <signature> and a <pubKey>:

04402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c9dfba68b92cbab7d1022066f273178febc7a37568e2e9f4dec980a2e9a95441abe838c7ef64c39d85849c[ALL] 0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b

Understand the Locking Script

The associated scriptPubKey locking script from the previous section was OP_DUP OP_HASH160 06b5c6ba5330cdf738a2ce91152bfd0e71f9ec39 OP_EQUALVERIFY OP_CHECKSIG, which is the standard locking methodology used for an older P2PKH address. That long string in the middle is a <pubKeyHash>.

Run a P2PKH Script

When you unlock a P2PKH UTXO, you (effectively) concatenate the unlocking and locking scripts, producing:

Script: <signature> <pubKey> OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

Now, you can evaluate how the P2PKH UTXO is unlocked.

First, you put the initial constants on the stack, then make a duplicate of the pubKey with OP_DUP:

Script: <signature> <pubKey> OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
Stack: [ ]

Script: <pubKey> OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
Stack: [ <signature> ]

Script: OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
Stack: [ <signature> <pubKey> ]

Script: OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
Running: <pubKey> OP_DUP
Stack: [ <signature> <pubKey> <pubKey> ]

Why the duplicate? Because that's what the script says to do!

Next, OP_HASH160 pops the <pubKey> off the stack, hashes it, and puts the result back on the stack.

Script: <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
Running: <pubKey> OP_HASH160
Stack: [ <signature> <pubKey> <pubKeyHash> ]

Then, you place the <pubKeyHash> that was in the locking script on the stack:

Script: OP_EQUALVERIFY OP_CHECKSIG
Stack: [ <signature> <pubKey> <pubKeyHash> <pubKeyHash> ]

OP_EQUALVERIFY is effectively two opcodes: OP_EQUAL, which pops two items from the stack and pushes True or False based on the comparison; and OP_VERIFY which pops that result and immediately marks the transaction as invalid if it's False. (Chapter 12 talks more about the use of OP_VERIFY as a conditional.)

Assuming the two <pubKeyHash>es are equal, you will have the following result:

Script: OP_CHECKSIG
Running: <pubKeyHash> <pubKeyHash> OP_EQUALVERIFY
Stack: [ <signature> <pubKey> ]

At this point you've proven that the <pubKey> supplied in the scriptSig hashes to the Bitcoin address in question, so you know that the redeemer knew the public key. But, they also need to prove knowledge of the private key, which is done with OP_CHECKSIG, which confirms that the unlocking script's signature matches that public key.

Script:
Running: <signature> <pubKey> OP_CHECKSIG
Stack: [ True ]

The Script now ends and the transaction is allowed to respend the UTXO in question.

Use btcdeb for a P2PKH Example

Testing out actual Bitcoin transactions with btcdeb is a bit trickier, because you need to know the public key and a signature to make everything work, and generating the latter is somewhat difficult. However, one way to test things is to let Bitcoin do the work for you in generating a transaction that will unlock a UTXO. And, we already did that back in §9.1.

Remember that each unlocking script fits the locking script of the previous UTXO, so in that section we were building an unlocking script for a transaction to unlock the UTXO with a txid of bb4362dec15e67d366088f5493c789f22fb4a604e767dae1f6a631687e2784aa and a vout of 0. To fully test things out we need to examine the previous UTXO.

$ bitcoin-cli gettransaction "bb4362dec15e67d366088f5493c789f22fb4a604e767dae1f6a631687e2784aa"
{
  "amount": 0.00095000,
  "confirmations": 12,
  "blockhash": "0000000075a4c1519da5e671b15064734c42784eab723530a6ace83ca1e66d3f",
  "blockheight": 1780789,
  "blockindex": 132,
  "blocktime": 1594841768,
  "txid": "bb4362dec15e67d366088f5493c789f22fb4a604e767dae1f6a631687e2784aa",
  "walletconflicts": [
  ],
  "time": 1594841108,
  "timereceived": 1594841108,
  "bip125-replaceable": "no",
  "details": [
    {
      "address": "mmX7GUoXq2wVcbnrnFJrGKsGR14fXiGbD9",
      "category": "receive",
      "amount": 0.00095000,
      "label": "",
      "vout": 0
    }
  ],
  "hex": "020000000001011efcc3bf9950ac2ea08c53b43a0f8cc21e4b5564e205f996f7cadb7d13bb79470000000017160014c4ea10874ae77d957e170bd43f2ee828a8e3bc71feffffff0218730100000000001976a91441d83eaffbf80f82dee4c152de59a38ffd0b602188ac713b10000000000017a914b780fc2e945bea71b9ee2d8d2901f00914a25fbd8702473044022025ee4fd38e6865125f7c315406c0b3a8139d482e3be333727d38868baa656d3d02204b35d9b5812cb85894541da611d5cec14c374ae7a7b8ba14bb44495747b571530121033cae26cb3fa063c95e2c55a94bd04ab9cf173104555efe448b1bfc3a68c8f873342c1b00"
}

That hex is the raw transaction for the UTXO, so if we save that and decode it, we can see the scriptPubKey information.

$ hex=$(bitcoin-cli gettransaction "bb4362dec15e67d366088f5493c789f22fb4a604e767dae1f6a631687e2784aa" | jq -r '.hex')
$ bitcoin-cli decoderawtransaction $hex
{
  "txid": "bb4362dec15e67d366088f5493c789f22fb4a604e767dae1f6a631687e2784aa",
  "hash": "6866490b16a92d68179e1cf04380fd08f16ec80bf66469af8d5e78ae624ff202",
  "version": 2,
  "size": 249,
  "vsize": 168,
  "weight": 669,
  "locktime": 1780788,
  "vin": [
    {
      "txid": "4779bb137ddbcaf796f905e264554b1ec28c0f3ab4538ca02eac5099bfc3fc1e",
      "vout": 0,
      "scriptSig": {
        "asm": "0014c4ea10874ae77d957e170bd43f2ee828a8e3bc71",
        "hex": "160014c4ea10874ae77d957e170bd43f2ee828a8e3bc71"
      },
      "txinwitness": [
        "3044022025ee4fd38e6865125f7c315406c0b3a8139d482e3be333727d38868baa656d3d02204b35d9b5812cb85894541da611d5cec14c374ae7a7b8ba14bb44495747b5715301",
        "033cae26cb3fa063c95e2c55a94bd04ab9cf173104555efe448b1bfc3a68c8f873"
      ],
      "sequence": 4294967294
    }
  ],
  "vout": [
    {
      "value": 0.00095000,
      "n": 0,
      "scriptPubKey": {
        "asm": "OP_DUP OP_HASH160 41d83eaffbf80f82dee4c152de59a38ffd0b6021 OP_EQUALVERIFY OP_CHECKSIG",
        "hex": "76a91441d83eaffbf80f82dee4c152de59a38ffd0b602188ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
          "mmX7GUoXq2wVcbnrnFJrGKsGR14fXiGbD9"
        ]
      }
    },
    {
      "value": 0.01063793,
      "n": 1,
      "scriptPubKey": {
        "asm": "OP_HASH160 b780fc2e945bea71b9ee2d8d2901f00914a25fbd OP_EQUAL",
        "hex": "a914b780fc2e945bea71b9ee2d8d2901f00914a25fbd87",
        "reqSigs": 1,
        "type": "scripthash",
        "addresses": [
          "2N9yWARt5E3TQsX2RjsauxSZaEZVhinAS4h"
        ]
      }
    }
  ]
}

So, OP_DUP OP_HASH160 41d83eaffbf80f82dee4c152de59a38ffd0b6021 OP_EQUALVERIFY OP_CHECKSIG was the locking script for the UTXO, and as we already know from looking at the scriptSig, the unlocking script was 304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c9dfba68b92cbab7d1022066f273178febc7a37568e2e9f4dec980a2e9a95441abe838c7ef64c39d85849c[ALL] 0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b.

Note that we could find that public key ourselves by using getaddressinfo:

$ bitcoin-cli getaddressinfo mmX7GUoXq2wVcbnrnFJrGKsGR14fXiGbD9
{
  "address": "mmX7GUoXq2wVcbnrnFJrGKsGR14fXiGbD9",
  "scriptPubKey": "76a91441d83eaffbf80f82dee4c152de59a38ffd0b602188ac",
  "ismine": true,
  "solvable": true,
  "desc": "pkh([f004311c/0'/0'/2']0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b)#t3g5mjk9",
  "iswatchonly": false,
  "isscript": false,
  "iswitness": false,
  "pubkey": "0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b",
  "iscompressed": true,
  "ischange": false,
  "timestamp": 1594835792,
  "hdkeypath": "m/0'/0'/2'",
  "hdseedid": "f058372260f71fea37f7ecab9e4c5dc25dc11eac",
  "hdmasterfingerprint": "f004311c",
  "labels": [
    ""
  ]
}

Figuring out that signature, however, requires generating a transaction and signing it (or really understanding the nuts and bolts of how Bitcoin transactions are created).

We can put those together the locking script, the signature, and the pubkey using btcdeb, showing how simple a P2PKH script is.

$ btcdeb '[304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c9dfba68b92cbab7d1022066f273178febc7a37568e2e9f4dec980a2e9a95441abe838c7ef64c39d85849c 0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b OP_DUP OP_HASH160 41d83eaffbf80f82dee4c152de59a38ffd0b6021 OP_EQUALVERIFY OP_CHECKSIG]'
btcdeb 0.2.19 -- type `btcdeb -h` for start up options
unknown key ID 41d83eaffbf80f82dee4c152de59a38ffd0b6021: returning fake key
valid script
7 op script loaded. type `help` for usage information
script                                                             |                                                             stack 
-------------------------------------------------------------------+-------------------------------------------------------------------
304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c9dfba68b... | and_v(
0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b |     sig(304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c...
OP_DUP                                                             |     and_v(
OP_HASH160                                                         |         pk(0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3...
41d83eaffbf80f82dee4c152de59a38ffd0b6021                           |         c:pk_h(030500000000000000000000000000000000000000000000...
OP_EQUALVERIFY                                                     |     )
OP_CHECKSIG                                                        | 
                                                                   | )
                                                                   | 
#0000 304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c9dfba68b92cbab7d1022066f273178febc7a37568e2e9f4dec980a2e9a95441abe838c7ef64c39d85849c
btcdeb> step
		<> PUSH stack 304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c9dfba68b92cbab7d1022066f273178febc7a37568e2e9f4dec980a2e9a95441abe838c7ef64c39d85849c
script                                                             |                                                             stack 
-------------------------------------------------------------------+-------------------------------------------------------------------
0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b | 304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c9dfba68b...
OP_DUP                                                             | 
OP_HASH160                                                         | and_v(
41d83eaffbf80f82dee4c152de59a38ffd0b6021                           |     sig(304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c...
OP_EQUALVERIFY                                                     |     and_v(
OP_CHECKSIG                                                        |         pk(0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3...
                                                                   |         c:pk_h(030500000000000000000000000000000000000000000000...
                                                                   |     )
                                                                   | 
                                                                   | )
                                                                   | 
#0001 0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b
btcdeb> step
		<> PUSH stack 0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b
script                                                             |                                                             stack 
-------------------------------------------------------------------+-------------------------------------------------------------------
OP_DUP                                                             | 0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b
OP_HASH160                                                         | 304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c9dfba68b...
41d83eaffbf80f82dee4c152de59a38ffd0b6021                           | 
OP_EQUALVERIFY                                                     | and_v(
OP_CHECKSIG                                                        |     sig(304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c...
                                                                   |     and_v(
                                                                   |         pk(0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3...
                                                                   |         c:pk_h(030500000000000000000000000000000000000000000000...
                                                                   |     )
                                                                   | 
                                                                   | )
                                                                   | 
#0002 OP_DUP
btcdeb> step
		<> PUSH stack 0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b
script                                                             |                                                             stack 
-------------------------------------------------------------------+-------------------------------------------------------------------
OP_HASH160                                                         | 0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b
41d83eaffbf80f82dee4c152de59a38ffd0b6021                           | 0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b
OP_EQUALVERIFY                                                     | 304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c9dfba68b...
OP_CHECKSIG                                                        | 
                                                                   | and_v(
                                                                   |     sig(304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c...
                                                                   |     and_v(
                                                                   |         pk(0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3...
                                                                   |         c:pk_h(030500000000000000000000000000000000000000000000...
                                                                   |     )
                                                                   | 
                                                                   | )
                                                                   | 
#0003 OP_HASH160
btcdeb> step
		<> POP  stack
		<> PUSH stack 41d83eaffbf80f82dee4c152de59a38ffd0b6021
script                                                             |                                                             stack 
-------------------------------------------------------------------+-------------------------------------------------------------------
41d83eaffbf80f82dee4c152de59a38ffd0b6021                           |                           41d83eaffbf80f82dee4c152de59a38ffd0b6021
OP_EQUALVERIFY                                                     | 0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b
OP_CHECKSIG                                                        | 304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c9dfba68b...
                                                                   | 
                                                                   | and_v(
                                                                   |     sig(304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c...
                                                                   |     and_v(
                                                                   |         pk(0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3...
                                                                   |         c:pk_h(030500000000000000000000000000000000000000000000...
                                                                   |     )
                                                                   | 
                                                                   | )
                                                                   | 
#0004 41d83eaffbf80f82dee4c152de59a38ffd0b6021
btcdeb> step
		<> PUSH stack 41d83eaffbf80f82dee4c152de59a38ffd0b6021
script                                                             |                                                             stack 
-------------------------------------------------------------------+-------------------------------------------------------------------
OP_EQUALVERIFY                                                     |                           41d83eaffbf80f82dee4c152de59a38ffd0b6021
OP_CHECKSIG                                                        |                           41d83eaffbf80f82dee4c152de59a38ffd0b6021
                                                                   | 0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b
                                                                   | 304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c9dfba68b...
                                                                   | 
                                                                   | and_v(
                                                                   |     sig(304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c...
                                                                   |     and_v(
                                                                   |         pk(0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3...
                                                                   |         c:pk_h(030500000000000000000000000000000000000000000000...
                                                                   |     )
                                                                   | 
                                                                   | )
                                                                   | 
#0005 OP_EQUALVERIFY
btcdeb> step
		<> POP  stack
		<> POP  stack
		<> PUSH stack 01
		<> POP  stack
script                                                             |                                                             stack 
-------------------------------------------------------------------+-------------------------------------------------------------------
OP_CHECKSIG                                                        | 0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b
                                                                   | 304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c9dfba68b...
                                                                   | 
                                                                   | and_v(
                                                                   |     sig(304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c...
                                                                   |     and_v(
                                                                   |         pk(0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3...
                                                                   |         c:pk_h(030500000000000000000000000000000000000000000000...
                                                                   |     )
                                                                   | 
                                                                   | )
                                                                   | 
#0006 OP_CHECKSIG
btcdeb> step
error: Signature is found in scriptCode
[[NOTE: THIS CURRENTLY HAS SOME ERRORS IN THE OUTPUT AND THE CHECKSUM]]

If you read through that you can see the usage of elements like the OP_DUP to duplicate the hash for testing, and how that result is then pushed out of the way to test out the signature. As is shown, a P2PKH is quite simple: its protection comes about those the strength of its cryptography.

Summary: Scripting a Pay to Public Key Hash

Sending to a P2PKH address was relatively easy when you were just using bitcoin-cli. Examining the Bitcoin Script underlying it lays bare the cryptographic functions that were implicit in funding that transaction: how the UTXO was unlocked with a signature and a public key.

What's Next?

Continue "Introducing Bitcoin Scripts" with §9.5: Scripting a P2WPKH.