The PR branch HEAD was 905d672b74 at the time of this review club meeting.
Notes
Bitcoin transactions encode spending conditions through a scriptPubKey in outputs and a witness
and scriptSig in the inputs. You can read more about Bitcoin Script
here. In the functional test framework, scripts are represented
using the
CScript
class, and can be initialized using an array of opcodes and byte-encoded data.
PR #22363 replaces many manually-constructed
default scripts in the functional tests with helper functions provided in script_util.py.
It also corrects an error in the helper function,
get_multisig,
in which the P2SH-wrapped P2WSH script hadn’t hashed the witness script before putting it into the
scriptSig. We’ll use this opportunity to review script and output types.
To test your understanding of scripts and output types, you can try to fill out this table (Hint:
a few cells have been pre-filled, and some cells should remain blank):
What do key_to_p2pkh_script, script_to_p2sh_script, key_to_p2wpkh_script and
script_to_p2wsh_script in script_util.py do? In what cases would we want to use or not use them?
Review of Terminology: Let’s define script code, witness script, redeem script, scriptPubKey,
scriptSig, witness, and witness program (some of these terms are synonymous).
What does the operation OP_HASH160 do? (Hint: what does the script
interpreter
do when it sees this opcode? What are the differences between the
hashers?)
Review of P2PKH: to send coins to someone by public key hash (pre-segwit), what is included in
the scriptPubKey of the output? What needs to be provided in the input when the coin is spent?
Review of P2SH: to send coins to someone with spending conditions encoded in a script, what is
included in the scriptPubKey of the output? What needs to be provided in the input when the coin is
spent? Why do we use Pay-To-Script-Hash instead of Pay-To-Script?
Review of P2SH-P2WSH: What is the purpose of “P2SH wrapped segwit” outputs? When a non-segwit
node validates a P2SH-P2WSH input, what does it do?
Review of P2SH-P2WSH: When a node with segwit enabled validates a P2SH-P2WSH input, what does it
do in addition to the procedure performed by a non-segwit node?
What is wrong with the P2SH-P2WSH script
here?
(Hint: which variable holds the 2-of-3 multisig script itself? Which variable holds the scriptSig
which will be included in the input?)
How would you verify the correctness of helper functions like get_multisig()? Can we add tests
for them?
Can you find any other places in functional tests that could use the script_util.py helper
functions instead of manually creating scripts?
<glozow> First question: What do `key_to_p2pkh_script`, `script_to_p2sh_script`, `key_to_p2wpkh_script` and `script_to_p2wsh_script` in wallet\_util.py do?
<sipa> not "placed before" since somewhere in 2010; it's evaluated first, and the resulting stack is fed as initial state for the scriptPubKey is evaluated
<glozow> LarryRuane: I suppose we could use "script code" colloquially to mean the "code" evaluated in scripts, but scriptCode also has a meaning defined in BIP143
<LarryRuane> Witness script is part of the tx input (but not included in the txid hash), and it's placed into the execution to-do list (probably using the wrong terms here) after the special segwit pattern is seen, 0,32-byte-hash
<LarryRuane> I'm confused about this part of p2wpkh: once the stack has the special pattern 20-byte-hash,0, then magically the command set (to-do list) becomes the standard p2pkh sequence, sig, pubkey, OP_DUP, OP_HASH160, 20-byte-hash, OP_EQUALVERIFY, OP_CHECKSIG .... my question is, is THAT the witness? Or is this sequence "manufactured" on the fly, and the
<glozow> and the interpreter sees that pattern and knows to use the script code OP_DUP OP_HASH160 20Bhash OP_EQUALVERIFY OP_CHECKSIG with the witness yeah?
<glozow> Review of P2PKH: to send coins to someone by public key hash (pre-segwit), what is included in the scriptPubKey of the output? What is included in the scriptSig?
<glozow> Same question for P2SH: to send coins to someone with spending conditions encoded in a script, what is included in the scriptPubKey of the output? What needs to be provided in the scriptSig when the coin is spent?
<LarryRuane> I think the TXO is smaller (and when it's still a UTXO, that's very helpful for resource use), and also it's more secure (in some future where ECDSA is broken)
<jnewbery> The "motivation" section for BIP16 is very short, but it contains the key point: "The purpose of pay-to-script-hash is to move the responsibility for supplying the conditions to redeem a transaction from the sender of the funds to the redeemer."
<jnewbery> And the second point: "The benefit is allowing a sender to fund any arbitrary transaction, no matter how complicated, using a fixed-length 20-byte hash that is short enough to scan from a QR code or easily copied and pasted."
<stickies-v> jnewbery: arguably it's still good for privacy though? e.g. not exposing that you have timelocks in your script until after the outputs are spent is a privacy benefit, no?
<glozow> Review of P2SH-P2WSH: What is the purpose of “P2SH wrapped segwit” outputs? When a non-segwit node validates a P2SH-P2WSH input, what does it do?
<glozow> And the other part of the question is: When a node with segwit enabled validates a P2SH-P2WSH input, what does it do in addition to the procedure performed by a non-segwit node?
<LarryRuane> purpose is, in case you are asking for a payment from someone with an old wallet, so the segwit address you'd like to give the person won't work ... so you can give the payer what looks exactly like a P2SH address
<jnewbery> stickies-v: I'm not sure that's how we usually think about privacy. If it needs to be revealed in future, then you could argue that it's not really private.
<glozow> So we have a scriptPubKey that looks like a P2SH, and both a scriptSig and a witness. What does a nonsegwit node do to validate it? And what does a segwit node do?
<LarryRuane> the nonsegwit node verifies that the redeem script hash is correct, then runs the redeem script, however, it's just OP_0 and a 20-byte-hash, so push those on the stack, and since top element is nonzero, done, success
<glozow> yep! we've run out of time for the last 2 questions, but they'd be good to include in your review (hopefully everyone will be posting a review after this!)
<glozow> yep! we've run out of time for the last 2 questions, but they'd be good to include in your review (hopefully everyone will be posting a review after this!)
<glozow> #11: Can you find any other places in functional tests that could use the script_util.py helper functions instead of manually creating scripts?