Remember that this PR uses an updated version of libsecp256k1 that requires
the --enable-module-schnorrsig and --enable-experimental options. You may
need to make clean && ./configure && make for the build to succeed.
This commit implements the script validation
rules
from BIP 341. Itāll help to refer back to that specification as you review the
code.
There are several script verification
flags
defined in the software. These are passed to the script interpreter and specify
which rules the interpreter should enforce. Each of these verification flags
should be a tightening of the rules (adding a new script verification flag can
make a previously succeeding script fail, but cannot make a previously failing
script succeed).
This commit adds two new script verification flags: SCRIPT_VERIFY_TAPROOT
and SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION.
Pay-to-contract is a method of hiding a commitment inside a public key,
which can be selectively revealed later. The idea has been around for many
years, with the original taproot
post
citing a paper by Ilja Gerhardt and Timo
Hanke from 2012. Taproot uses pay-to-contract
to commit to a script (or scripts) inside a public key.
This commit adds a
CheckPayToContract()
function, which calls through to the secp256k1_xonly_pubkey_tweak_add_check()
function.
Everything in the if (stack.size == 1)
branch
corresponds to the āIf there is exactly one element left in the witness stack,
key path spending is usedā section of the BIP. Everything in the else
branch
corresponds to the āIf there are at least two witness elements left, script
path spending is usedā part of the BIP.
This commit does not add any script verification for the taproot script path
spending (that comes in a later commit). Once the taproot commitment has been
verified, we either fail validation if the
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION is set, or succeed if it
isnāt
(code).
Questions
Will the new SCRIPT_VERIFY_TAPROOT and
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION script verification
flags be used for consensus checks or policy checks?
What is the maximum permitted size of the control block?
What happens if a segwit v1 output has a witness program that isnāt 32
bytes? What happens if thereās a P2SH-wrapped segwit v1 output?
Explain this line: if (!(flags & SCRIPT_VERIFY_TAPROOT)) return set_success(serror);
What error is given if VerifyTaprootCommitment() fails? Why?
<jnewbery> Brief reminder of the format: I have some questions prepared that we'll go through, but feel free to jump in at any point. If you have a question, just ask at any time!
<robot-dreams> initial thoughts: I expected it'd be a really scary change about elliptic curves, but it turned out to be slightly more approachable and focused on soft forks :)
<gzhao408> It taught me about script upgradeability using witness versions š¤was wondering, if there was another script upgrade we wanted to do using v2 for example, would it be incompatible with taproot?
<pinheadmz> yeah its simpler than i expected -- checkTaprootCommitmenet in particualr, the way the control block is merkleized is a lot simpler than i imagined
<jnewbery> robot-dreams: probably good to review the last taproot PR review club to see how we use the new libsecp interface: https://bitcoincore.reviews/17977-2
<gzhao408> right, but if they had distinct features that were both really nice, would we be able to have both in the same script? or would there be a better way?
<michaelfolkson> It gets messy when you start wanting to take an old version (say 1) when you have a new version (say 2) and add new features to that old version to make say 3
<jnewbery> ok, first question: Will the new SCRIPT_VERIFY_TAPROOT and SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION script verification flags be used for consensus checks or policy checks?
<gzhao408> thanks for answering my question <jnewbery> <pinheadmz>, I'll think on it more - just want to feel out what the boundaries of compatibility are
<robot-dreams> On the other hand, I think `SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION` is for policy: it might prevent some transactions from being signed or added to the mempool, but it won't cause blocks to be rejected
<gzhao408> +1 robot-dreams, and I wanna check my understanding of the DISCOURAGE flags, I think itās: before, v1 would have been WITNESS_UNKNOWN, so no requirementsā¦ and now there are (stricter) defined taproot spending rules. So if we didnāt discourage, itās possible for us to accept an old v1 to mempool, but then it wouldnāt pass consensus after the soft fork?
<pinheadmz> and then you might see in the future say if ANNEX gets a function, that DISCOURAGE ANNEX will be removed and the new annex rules enforced by soft fork
<gzhao408> AND Itās not possible to add a script verification flag that makes spending rules less strict, right? i.e. they are all backwards compatible
<gzhao408> because... otherwise you might accept to mempool (if you didn't discourage in policy), and then there's a spending rule activated, and then it no longer passes consensus?
<jnewbery> pinheadmz: I don't think the mempool would do anything at activation time, but policy should mean that there aren't any of those transaction in there
<gzhao408> ! so at what point do we start applying SCRIPT_VERIFY_DISCOURAGE_UPGRADEABLE_TAPROOT_VERSION, would that theoretically be well before we start enforcing taproot in consensus?
<jnewbery> pinheadmz: i believe you're right. So the miner needs to ensure that they're enforcing the new consensus rules on their mempool well before activation
<robot-dreams> this is for the miner's benefit, right? (they don't want to put in the work to mine a block and then have it be rejected cause of upgraded VERIFY rules)
<unweave> >that transaction probably won't propogate [...] because it's policy-invalid I've had transactions confirm that violated policy and I didn't put any extra effort into shepherding them to a miner FWIW
<jnewbery> Next question (although feel free to continue asking questions about flags/policy if there's anything unanswered): What is the maximum permitted size of the control block?
<jnewbery> Next: What happens if a segwit v1 output has a witness program that isnāt 32 bytes? What happens if thereās a P2SH-wrapped segwit v1 output?
<pinheadmz> jnewbery yes but if we enforce v1 programs to be exactly 32 bytes then we can mitigate a user error where an address has extra qqqq's or whatever it was
<Paul_R> Can a full-node be adversarial by not-listening to policy? could it then spread undesirable txs to miners & possibly blocks? will this nodes peers ban it?
<robot-dreams> Just confirming, "unencumbered" means it's a transaction output that anyone can figure out how to spend just by looking at it (no private keys / etc. neeeded)?
<robot-dreams> jnewbery: Taproot validation is a soft fork: if the VERIFY flag is not set, it's unencumbered (similar to legacy client behavior for P2SH and Segwit)
<sosthene> I mean, it seems that last year there was discussion about the benefits of leaving this door open, but maybe there are other arguiments since I'm not aware of
<Paul_R> because then it seems like there would be some parties competing to do so, which would be a weird industry... yeah pinheadmz shows it is a very slow race so far haha
<pinheadmz> this is the function that checks that the control block crap and witness data match the output, aka witness program aka the adress coin is sent to
<fjahr> michaelfolkson: it's not really a race between the entire bitcoin world though, only the question which miner mines the next block. If you send a miner a anyonecanspend why would they mine it for you? They will just replace it with their own tx.
<jnewbery> but yes, it's the function that checks that the witness program has commited to the witness, so if it fails, then it's a witness program mismatch
<unweave> [off topic] Paul_R see this presentation for examples of automated sweeping of low-entropy bitcoin funds https://www.youtube.com/watch?v=foil0hzl4Pg "DEF CON 23 - Ryan Castellucci - Cracking CryptoCurrency Brainwallets"
<robot-dreams> was the conclusion "we're not going to support this because the benefit (allowing very old wallets to send to a taproot output) isn't worth the multitude of added complexity, privacy implications, etc."?