mirror of
https://github.com/ChristopherA/Learning-Bitcoin-from-the-Command-Line.git
synced 2025-06-07 07:56:31 +00:00
390 lines
22 KiB
Markdown
390 lines
22 KiB
Markdown
# 9.4: Scripting a P2PKH
|
|
|
|
> :information_source: **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.
|
|
|
|
Take a look again at the transaction you created in [§9.1](09_1_Understanding_the_Foundation_of_Transactions.md):
|
|
```
|
|
$ bitcoin-cli -named decoderawtransaction hexstring=$signedtx
|
|
{
|
|
"txid": "34151dac704d94a269cd33f80be34c122152edc9bfbb9323852966bf0ce937ed",
|
|
"hash": "34151dac704d94a269cd33f80be34c122152edc9bfbb9323852966bf0ce937ed",
|
|
"version": 2,
|
|
"size": 191,
|
|
"vsize": 191,
|
|
"weight": 764,
|
|
"locktime": 0,
|
|
"vin": [
|
|
{
|
|
"txid": "bb4362dec15e67d366088f5493c789f22fb4a604e767dae1f6a631687e2784aa",
|
|
"vout": 0,
|
|
"scriptSig": {
|
|
"asm": "304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c9dfba68b92cbab7d1022066f273178febc7a37568e2e9f4dec980a2e9a95441abe838c7ef64c39d85849c[ALL] 0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b",
|
|
"hex": "47304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c9dfba68b92cbab7d1022066f273178febc7a37568e2e9f4dec980a2e9a95441abe838c7ef64c39d85849c01210315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b"
|
|
},
|
|
"sequence": 4294967295
|
|
}
|
|
],
|
|
"vout": [
|
|
{
|
|
"value": 0.00090000,
|
|
"n": 0,
|
|
"scriptPubKey": {
|
|
"asm": "OP_DUP OP_HASH160 06b5c6ba5330cdf738a2ce91152bfd0e71f9ec39 OP_EQUALVERIFY OP_CHECKSIG",
|
|
"hex": "76a91406b5c6ba5330cdf738a2ce91152bfd0e71f9ec3988ac",
|
|
"reqSigs": 1,
|
|
"type": "pubkeyhash",
|
|
"addresses": [
|
|
"mg8S7F1gY3ivV9M9GrWwe6ziWvK2MFquCf"
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
You can see that its `scriptSig` unlocking script has two values. That's a `<signature>` (and an `[all]`) and a `<pubKey>`:
|
|
```
|
|
304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c9dfba68b92cbab7d1022066f273178febc7a37568e2e9f4dec980a2e9a95441abe838c7ef64c39d85849c[ALL] 0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b
|
|
```
|
|
That's all an unlock script is! (For a P2PKH.)
|
|
|
|
## Understand the Locking Script
|
|
|
|
Remember that each unlocking script unlocks a previous UTXO. In the above example, the `vin` reveals that it's actually unlocking vout `0` of txid `bb4362dec15e67d366088f5493c789f22fb4a604e767dae1f6a631687e2784aa`.
|
|
|
|
You can examine that UTXO with `gettransaction`.
|
|
```
|
|
$ 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"
|
|
}
|
|
```
|
|
But as you can see, you didn't get the `scriptPubKey` with `gettransaction`. You need to take an additional step to retrieve that by examining the raw transaction info (that's the `hex`) with `decoderawtransaction`:
|
|
```
|
|
$ 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"
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
You can now look at `vout` `0` and see it was locked with the `scriptPubKey` of `OP_DUP OP_HASH160 41d83eaffbf80f82dee4c152de59a38ffd0b6021 OP_EQUALVERIFY OP_CHECKSIG`. That's the standard locking methodology used for an older P2PKH address with the `<pubKeyHash>` stuck in the middle.
|
|
|
|
Running it will demonstrate how it works.
|
|
|
|
## Run a P2PKH Script
|
|
|
|
When you unlock a P2PKH UTXO, you (effectively) concatenate the unlocking and locking scripts. For a P2PKH address, like the example used in this chapter, that produces:
|
|
```
|
|
Script: <signature> <pubKey> OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
|
|
```
|
|
With that put together, you can examinine how a 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 it's needed to check the two unlocking elements: the public key and the signature.
|
|
|
|
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 if it was successful, 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 _would_ unlock a UTXO. That's what you've done above: generating the transaction to spend the UTXO caused `bitcoin-cli` to calculate the `<signature>` and `<pubKey>`. You then look at the raw transaction information of the UTXO to learn the locking script including the `<pubKeyHash>`
|
|
|
|
You can put 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... |
|
|
0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b |
|
|
OP_DUP |
|
|
OP_HASH160 |
|
|
41d83eaffbf80f82dee4c152de59a38ffd0b6021 |
|
|
OP_EQUALVERIFY |
|
|
OP_CHECKSIG |
|
|
|
|
|
|
|
|
#0000 304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c9dfba68b92cbab7d1022066f273178febc7a37568e2e9f4dec980a2e9a95441abe838c7ef64c39d85849c
|
|
```
|
|
You push the `<signature>` and `<pubKey>` onto the stack:
|
|
```
|
|
btcdeb> step
|
|
<> PUSH stack 304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c9dfba68b92cbab7d1022066f273178febc7a37568e2e9f4dec980a2e9a95441abe838c7ef64c39d85849c
|
|
script | stack
|
|
-------------------------------------------------------------------+-------------------------------------------------------------------
|
|
0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b | 304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c9dfba68b...
|
|
OP_DUP |
|
|
OP_HASH160 |
|
|
41d83eaffbf80f82dee4c152de59a38ffd0b6021 |
|
|
OP_EQUALVERIFY |
|
|
OP_CHECKSIG |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#0001 0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b
|
|
btcdeb> step
|
|
<> PUSH stack 0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b
|
|
script | stack
|
|
-------------------------------------------------------------------+-------------------------------------------------------------------
|
|
OP_DUP | 0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b
|
|
OP_HASH160 | 304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c9dfba68b...
|
|
41d83eaffbf80f82dee4c152de59a38ffd0b6021 |
|
|
OP_EQUALVERIFY |
|
|
OP_CHECKSIG |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
You `OP_DUP` and `OP_HASH` the `<pubKey>`:
|
|
```
|
|
#0002 OP_DUP
|
|
btcdeb> step
|
|
<> PUSH stack 0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b
|
|
script | stack
|
|
-------------------------------------------------------------------+-------------------------------------------------------------------
|
|
OP_HASH160 | 0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b
|
|
41d83eaffbf80f82dee4c152de59a38ffd0b6021 | 0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b
|
|
OP_EQUALVERIFY | 304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c9dfba68b...
|
|
OP_CHECKSIG |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#0003 OP_HASH160
|
|
btcdeb> step
|
|
<> POP stack
|
|
<> PUSH stack 41d83eaffbf80f82dee4c152de59a38ffd0b6021
|
|
script | stack
|
|
-------------------------------------------------------------------+-------------------------------------------------------------------
|
|
41d83eaffbf80f82dee4c152de59a38ffd0b6021 | 41d83eaffbf80f82dee4c152de59a38ffd0b6021
|
|
OP_EQUALVERIFY | 0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b
|
|
OP_CHECKSIG | 304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c9dfba68b...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
You push the `<pubKeyHash>` from the locking script onto the stack and verify it:
|
|
```
|
|
#0004 41d83eaffbf80f82dee4c152de59a38ffd0b6021
|
|
btcdeb> step
|
|
<> PUSH stack 41d83eaffbf80f82dee4c152de59a38ffd0b6021
|
|
script | stack
|
|
-------------------------------------------------------------------+-------------------------------------------------------------------
|
|
OP_EQUALVERIFY | 41d83eaffbf80f82dee4c152de59a38ffd0b6021
|
|
OP_CHECKSIG | 41d83eaffbf80f82dee4c152de59a38ffd0b6021
|
|
| 0315a0aeb37634a71ede72d903acae4c6efa77f3423dcbcd6de3e13d9fd989438b
|
|
| 304402201cc39005b076cb06534cd084fcc522e7bf937c4c9654c1c9dfba68b...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#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...
|
|
| )
|
|
|
|
|
| )
|
|
|
|
|
|
|
```
|
|
And that point, all that's required is the `OP_CHECKSIG`:
|
|
```
|
|
#0006 OP_CHECKSIG
|
|
btcdeb> step
|
|
error: Signature is found in scriptCode
|
|
```
|
|
(Unfortunately this checking may or may not be working at any point due to vagaries of the Bitcoin Core and `btcdeb` code.)
|
|
|
|
As is shown, a P2PKH is quite simple: its protection comes about those the strength of its cryptography.
|
|
|
|
### How to Look Up a Pub Key & Signature by Hand
|
|
|
|
What if you wanted to generate the `<signature>` and `<PubKey>` information needed to unlock a UTXO yourself, without leaning on `bitcoin-cli` to create a transaction?
|
|
|
|
It turns out that it's pretty easy to get a `<pubKey>` You just need to use `getaddressinfo` to examine the address where the UTXO is currently sitting:
|
|
```
|
|
$ 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 really understanding the nuts and bolts of how Bitcoin transactions are created. So we leave that as advanced study for the reader: creating a `bitcoin-cli` transaction to "solve" a UTXO is the best solution to that for the moment.
|
|
|
|
## 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](09_5_Scripting_a_P2WPKH.md).
|