Implement BIP 340-342 validation - Implement Taproot validation (BIP 341) (
consensus, taproot) Sep 2, 2020
The PR branch HEAD was 84ec87085 at the time of this review club meeting.
This is the fifth in
a series of review club
meetings on the (work in progress)
implementation of BIP 340-342.
This week, we’ll look at another commit from PR 17977 -
. Implement Taproot
Remember that this PR uses an updated version of libsecp256k1 that requires
--enable-experimental options. You may
make clean && ./configure && make for the build to succeed.
This commit implements the
from BIP 341. It’ll help to refer back to that specification as you review the
code. script validation
There are several
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
This commit adds two new script verification flags:
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
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
function, which calls through to the
The main code changes in this commit are in
is modified, and a new
Everything in the
if (stack.size == 1)
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
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
Will the new
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?
1 17:00 <jnewbery> #startmeeting
2 17:00 <jnewbery> Hi folks. Welcome to PR Review club. Feel free to say hi!
7 17:00 <Paul_R> hi everyone
11 17:00 <jnewbery> HI GZHAO!
12 17:00 <michaelfolkson> WHAT
13 17:00 <jnewbery> And let us know if it's your first time here. We love new participants.
14 17:00 <michaelfolkson> hi
15 17:00 <kimgnome> First time
17 17:01 <jnewbery> welcome kimgnome!
19 17:01 <jonatack> hi. i see some people are wide awake at this hour ;)
20 17:01 <kimgnome> Thanks
22 17:01 <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!
23 17:01 <sosthene> not really my first time, but it has been a while, a year I think :)
25 17:01 <Paul_R> also my first time
26 17:01 <jnewbery> welcome back sosthene :)
27 17:01 <jnewbery> welcome Paul_R
29 17:02 <jnewbery> other thing to remember: We're all here to learn, so there's no such thing as a stupid question.
30 17:02 <jonatack> great to see new people :D
31 17:02 <jnewbery> ok, let's get started!
33 17:02 <jnewbery> Who had a chance to review this week's commit? (y/n)
43 17:02 <jnewbery> wow that's a lot of review
45 17:02 <jnewbery> Any initial thoughts from those who reviewed it? Is it what you expected taproot to look like?
46 17:03 <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 :)
47 17:04 <jnewbery> robot-dreams: good! Yes, all the EC stuff is hidden away in libsecp
48 17:04 <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?
49 17:04 <nehan> i am super confused and am looking forward to learning more about what's going on!
50 17:04 <pinheadmz> yeah its simpler than i expected -- checkTaprootCommitmenet in particualr, the way the control block is merkleized is a lot simpler than i imagined
52 17:05 <pinheadmz> gzhao408 v2 wouldnt be inompatible with taproot kinda like how taproot (v1) isnt incompatible with segwit v0
53 17:05 <pinheadmz> i mean, they are incompatible but they dont interfere with each other
55 17:06 <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?
56 17:06 <unweave> pinheadmz would it be accurate to say "wouldn't *need to be* incompatible" ?
57 17:06 <pinheadmz> i see - well there are some logic in the code where its like (if WITNESSV0 || TAPROOT)
58 17:06 <pinheadmz> so you could add another (...|| V2) in there to share logic from taproot with v2
59 17:06 <gzhao408> my guess is no but I can't think of any concrete examples where it'd be necessary so 😅
60 17:07 <jnewbery> gzhao408: it's difficult for me to imagine something that abstract
61 17:07 <sosthene> But do we still need V2 anyway? :p
62 17:07 <jnewbery> but if there were eventually some segwit v2 I imagine it might have a superset of taproot's functionality maybe(?)
63 17:08 <pinheadmz> true also with leaf versions you could have a new script language inside a v1 taproot
64 17:08 <jnewbery> pinheadmz: yep, I expect you can do almost everything you want with script versions inside taproot
65 17:09 <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
66 17:09 <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?
67 17:09 <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
68 17:09 <robot-dreams> I think `SCRIPT_VERIFY_TAPROOT` is for consensus: it might cause some blocks to be rejected
69 17:09 <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
70 17:09 <pinheadmz> robot-dreams i think youre right, and "DISCOURAGE..." anything is just policy
71 17:09 <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?
72 17:09 <gzhao408> yeah, discourage but not enforce
73 17:10 <gzhao408> er - enforce in block validation*
74 17:10 <pinheadmz> discourage means: reject from mempool, still valid in block
75 17:10 <jonatack> counterargument to sow doubt: the flags are in protocol.h
76 17:11 <gzhao408> pinheadmz 👍
77 17:11 <Paul_R> therefore it's a policy, not consensus rule
79 17:11 <Paul_R> pinheadmz
80 17:11 <gzhao408> :jonatack: well, policy flags is a superset of consensus flags
81 17:11 <jonatack> gzhao408: stirring things up
82 17:11 <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
83 17:11 <gzhao408> u jonatack i counteratack
84 17:12 <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
85 17:12 <pinheadmz> less strict rules -> hard fork :-(
86 17:12 <jnewbery> jonatack: the flags are in script/interpreter.h
87 17:13 <jnewbery> gzhao408: yes, adding a new flag should only make things more strict
88 17:13 <gzhao408> I think jonatack is referring to the STANDARD_SCRIPT_VERIFY_FLAGS in policy.h
89 17:14 <jonatack> i wondered if the "answer" might be "both"
90 17:14 <robot-dreams> Yes, STANDARD_SCRIPT_VERIFY_FLAGS is in policy.h but I think it's also worth noting GetBlockScriptFlags
91 17:15 <jnewbery> my answer is: after the taproot softfork (however it gets activated), SCRIPT_VERIFY_TAPROOT will be a consensus rule
92 17:15 <jnewbery> SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION is intended to be a policy rule
93 17:15 <jnewbery> why do we discourage future taproot versions?
94 17:16 <michaelfolkson> Because they are unencumbered?
95 17:16 <sosthene> To discourage people using future versions while there are still not defined?
96 17:17 <robot-dreams> to communicate something like "this is a nonstandard transaction, don't do this"?
97 17:17 <pinheadmz> some of these policy rules are because they are anyone can spend, but a lot of policy rules are DDoS mitigation as well
98 17:17 <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?
99 17:17 <gzhao408> (is that scenario possible?)
100 17:18 <pinheadmz> gzhao408 youd have to get the timing just right :-)
101 17:18 <gzhao408> <pinheadmz> couldn't it hang out in your mempool for a while if it was full?
102 17:18 <pinheadmz> get a tx in the mempool right on activation
103 17:18 <jnewbery> gzhao408: you certainly need to be careful around activation time with your mempool and propagating transactions
104 17:19 <pinheadmz> i wonder actually if the mempool evicts invalid txs on the edge of activation
105 17:19 <jnewbery> you're right that having a policy rule well in advance of activation prevents us from having this problem
106 17:19 <pinheadmz> I actually sent btc to a v1 address, that is an anyonecanspend output and its in the utxo set right now
107 17:19 <pinheadmz> someone could try to spend it on the block before taproot is enforced
108 17:19 <Paul_R> recently?
109 17:20 <pinheadmz> a few months ago, after bitcoin core eased the restriciton on DISCOURAGE UNKOWN WITNESS version
110 17:20 <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
111 17:21 <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?
112 17:21 <jnewbery> right, sending _to_ a v1 address is policy-valid. Spending _from_ v1 is policy-invalid
113 17:21 <pinheadmz> ah ok
114 17:21 <pinheadmz> what if a node / miner had requireStandard: false ?
115 17:21 <jnewbery> so someone could theoretically spend pinheadmz's output if they could get their transaction to a miner that isn't enforcing that policy
116 17:22 <jnewbery> but that transaction probably won't propogate through the network otherwise because it's policy-invalid for most nodes
117 17:22 <pinheadmz> but is there any chance that (lets say with standard: false) the spend enters a miner's mempool?
118 17:22 <pinheadmz> then the next block, taproot is enforecd
119 17:22 <pinheadmz> the mining code assumes the entire mempool is valid for the next block, i think ?
120 17:23 <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
121 17:23 <pinheadmz> interesting
122 17:23 <pinheadmz> theres no check mempool and evict function outside of a reorg ?
123 17:24 <Paul_R> jnewbery: are miner's generally this responsible?
124 17:24 <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)
125 17:24 <jnewbery> gzhao: SCRIPT_VERIFY_DISCOURAGED_UPGRADEABLE_TAPROOT_VERSION would be enforced in policy from when taproot is activated
126 17:24 <gzhao408> <jnewbery> ohhh i see, thanks!
127 17:25 <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
128 17:25 <jnewbery> Paul_R: if they're running Bitcoin Core then any v1 spends are already policy-invalid and not in their mempool
129 17:25 <jonatack> pinheadmz: i've wondered when/why someone would set -acceptnonstdtxn
131 17:26 <pinheadmz> 5,431 free satoshis for someone
132 17:26 <jonatack> it's testing only
133 17:26 <jnewbery> unweave: interesting
134 17:26 <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?
135 17:27 <unweave> If I recall correctly, it was a txn which had sub 1 sat/byte fee rate
136 17:27 <michaelfolkson> 33+32*128
137 17:27 <jonatack> static_assert(TAPROOT_CONTROL_MAX_SIZE == 4129); // 33 + (32 * 128)
138 17:28 <jnewbery> unweave: that is interesting. Most nodes won't propogate transactions with a feerate under 1 sat/byte
139 17:28 <jnewbery> michaelfolkson jonatack: exactly
140 17:28 <jnewbery> that was an easy one though :)
141 17:28 <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?
142 17:29 <pinheadmz> jnewbery these are unencumbered
143 17:29 <jnewbery> pinheadmz: yes!
144 17:29 <pinheadmz> although - there was the bech32 issue which could be mitigated if program length was more strictly enforced
145 17:29 <pinheadmz> lost track of that thread, i guess we are not doing anything about in taproot?
146 17:29 <robot-dreams> but I believe if the `SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM` flag is set, we will fail validation for these cases
147 17:30 <jnewbery> bech32 is an address format. It doesn't impact consensus or validity of transactions.
149 17:30 * michaelfolkson shrugs
150 17:30 <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
151 17:31 <jnewbery> robot-dreams: right, exactly. So these transactions would be consensus valid, but would fail policy
152 17:31 <pinheadmz> robot-dreams good point
153 17:31 <jnewbery> pinheadmz: when you say "we enforce" are you talking about in the wallet software?
154 17:32 <pinheadmz> hm. i suppose yeah - but consensus doesn't allow witness v0 programs to be != 20 or 32
155 17:32 <pinheadmz> so even broken wallet software cant lose money sent to a v0 program with 33 bytes
156 17:33 <pinheadmz> a broken v1 wallet could, although tht point about policy i guess steps in there
157 17:33 <jnewbery> pinheadmz: yes, an OP_0 then a push that isn't 20 or 32 bytes is always invalid.
158 17:33 <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?
159 17:34 <jnewbery> does anyone have any questions about 'P2SH-wrapped'?
160 17:34 <pinheadmz> Paul_R you cant force a peer to accept a tx. so *their* mempool polocy protects them, including miners
161 17:34 <jnewbery> Like for example "what is P2SH-wrapped?"? Does anyone have that question?
162 17:35 <robot-dreams> jnewbery: I do have a question, why don't we support that for Taproot?
163 17:35 <jnewbery> good question robot-dreams!
164 17:35 <robot-dreams> I'd imagine it'd be a convenient way to let legacy wallets pay to a new address
165 17:35 <jnewbery> anyone got a good answer for that?
166 17:35 <pinheadmz> i think its bc it turns into a complexity nightmare
167 17:35 <pinheadmz> nested was essential for the transition to segwit
168 17:36 <michaelfolkson> Because we were introducing bech32 right?
169 17:36 <pinheadmz> this just means that blockchain.com wallets cant send to taproot address :-P
170 17:37 <michaelfolkson> There is no new address format with Taproot
171 17:37 <pinheadmz> michaelfolkson right I mean, if you only want to accept taproot ouputs there is no address you can give to a legacy-legacy wallet
172 17:37 <pinheadmz> with segwit v0 you had the option of nested addresses
173 17:37 <michaelfolkson> So a P2SH wrapped Taproot won't be able to be spent
174 17:38 <jnewbery> Right exactly, bech32 has been around for several years, so there's nothing new for wallets to do to be able to send to a taproot output
175 17:38 <pinheadmz> michaelfolkson there's just no such thing
176 17:38 <pinheadmz> its anyone can spend
178 17:38 <jnewbery> pinheadmz: exactly - it's unencumbered
179 17:38 <michaelfolkson> Ah ok
180 17:39 <nehan> what exactly does unencumbered mean?
181 17:39 <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)?
182 17:39 <robot-dreams> nehan: thanks for asking :)
184 17:39 <jnewbery> yes that. It's anyone-can-spend
185 17:40 <michaelfolkson> There's no reason for wanting P2SH wrapped Taproot as far as I know. Other than for avoiding mistakes with anyone can spend
186 17:40 <jnewbery> Of course, you'd need the preimage, so maybe unencumbered is maybe the wrong word
187 17:40 <jonatack> unencumbered by rules
188 17:40 <jnewbery> but if you have the preimage, it's anyone can spend (eg a miner could steal it from you if you created a transaction spending it)
189 17:41 <jnewbery> does that make sense nehan?
190 17:41 <nehan> jnewbery: yes thanks
191 17:41 <jonatack> unencumbered by s/rules/script validation rules/
192 17:42 <jnewbery> good! Everyone else happy with that? It's a little bit subtle
193 17:42 <michaelfolkson> encumber -to burden with obligations (dictionary)
194 17:42 <jnewbery> ok, next question: Explain this line: if (!(flags & SCRIPT_VERIFY_TAPROOT)) return set_success(serror);
195 17:43 <nehan> why are v1 outputs with lengths other than 32 bytes unencumbered instead of unspendable?
196 17:43 <michaelfolkson> For future upgradability
197 17:43 <sosthene> nehan: iirc, it is to allow them being used in the future
198 17:43 <nehan> ah, ok thanks!
199 17:43 <pinheadmz> yeah, an interesting design charactersitic of taproot is ALLL the upgradeablity
200 17:43 <jonatack> The bitwise AND of (flags & SCRIPT_VERIFY_TAPROOT) equalling 1 (true) means we need to verify SCRIPT_VERIFY_TAPROOT?
201 17:43 <jonatack> so if bitwise AND evaluates to 0 (false) then no need to verify and we can return with success
202 17:43 <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)
203 17:44 <pinheadmz> i think the authors might have felt like segwit v0 was too restricvitve perhaps, that theyd painted themself into a corner
204 17:44 <fjahr> Checks that the SCRIPT_VERIFY_TAPROOT flag is set for this script check. If not it skips verification and returns with success immediately.
205 17:44 <jnewbery> michaelfolkson sosthene: potentially, but it seems unlikely we'd ever use non-20/32-byte v1 outputs for anything
206 17:44 <michaelfolkson> No loss though right? If there is even a chance we might use them, keep them upgradable?
207 17:45 <michaelfolkson> Apart from anyone can spend mistakes by people playing around with this stuff
208 17:45 <jnewbery> michaelfolkson: is that any worse than unspendable?
209 17:45 <nehan> well, anyone can spend seems always better than unspendable
210 17:45 <sosthene> jnewbery: why?
211 17:46 <michaelfolkson> Fair point. If you put something with anyone can spend on the blockchain you aren't getting it back :)
212 17:46 <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
213 17:46 <michaelfolkson> But you aren't getting it back if it is unspendable either so...
214 17:46 * michaelfolkson shrugs
215 17:46 <nehan> unspendable = money burnt forever
216 17:47 <michaelfolkson> anyone-can-spend = enter into a race with the entire Bitcoin world
217 17:47 <Paul_R> could someone create a 'chain-analysis' of sorts, to automatically detect unencumbered coins and sweep them?
218 17:47 <jnewbery> robot-dreams: that's right, if the flag isn't set, then a v1 segwit output is unencumbered
219 17:47 <jnewbery> Paul_R: I'm sure they already have
220 17:47 <unweave> michaelfolkson and yet as pinheadmz 's txn shows, it's a very slow race, no?
221 17:48 <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
222 17:48 <unweave> Paul_R this already happens with low entropy keys
223 17:48 <michaelfolkson> Oh no that actually is unspendable isn't it?
224 17:48 <pinheadmz> that and as discussed, policy rejects spending from anyonecanspend
225 17:48 <pinheadmz> so itd have to be a clever miner
226 17:48 <michaelfolkson> Wasn't that a Taproot output?
227 17:48 <michaelfolkson> Before Taproot is activated?
228 17:48 <pinheadmz> and yes, the taproot address i used is just 32 0x01 bytes -- so this output is actually UNSPENDABLE once taproot activates!
229 17:49 <pinheadmz> (bc there is no private key for that witness program)
230 17:49 <Paul_R> pinheadmz all for 5000sats // .50 euros
231 17:49 <unweave> ah I think I misunderstood
233 17:49 <jnewbery> Final question: What error is given if VerifyTaprootCommitment() fails? Why?
234 17:49 <michaelfolkson> Oh there's that too pinheadmz but it is currently unspendable now too right?
235 17:49 <pinheadmz> witness program mismatch
236 17:50 <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
237 17:50 <pinheadmz> oops didnt mean to type crap
238 17:50 <michaelfolkson> banned
239 17:50 <jonatack> no more review club for you
240 17:50 <jnewbery> :shocked:
241 17:50 <pinheadmz> damn
242 17:51 <pinheadmz> starting my own ##PR-club-with-cuss-words
243 17:51 <robot-dreams> This means the witness program (Schnorr public key) didn't commit to the Merkle root demonstrated by the script
244 17:51 <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.
245 17:51 <robot-dreams> (though I don't yet know the mechanism by which a Schnorr public key commits to a hash)
246 17:51 * michaelfolkson checks code of conduct
247 17:52 <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
248 17:53 <jnewbery> ok, we have a few minutes left. Did anyone have any other questions or observations they wanted to share?
249 17:53 <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"
250 17:53 <Paul_R> unweave thx!
251 17:54 <michaelfolkson> Yeah I guess its worse than I said. Race in which you can only win if miner is dumb
252 17:54 <robot-dreams> to followup on P2SH wrapping:
253 17:54 <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."?
254 17:56 <jnewbery> robot-dreams: yes, those are good arguments against
256 17:57 <jnewbery> ok, just about time to wrap up. Any final thoughts? How are y'all enjoying taproot review?
257 17:57 <jonatack> we lost gzhao408
258 17:58 <Paul_R> that was a great first experience, thanks everyone
259 17:58 <Paul_R> i wish i had come sooner
260 17:58 * gzhao408 was afk because of work meeting
261 17:58 <robot-dreams> very exciting taproot review, this was a great entry point into "what is taproot"
262 17:58 <michaelfolkson> I'd like to suggest added comments to Taproot PR but I guess it isn't a good time
263 17:58 <jnewbery> ok, let's call it. Thanks everyone. See you all next week!
264 17:58 <sosthene> thanks, that was very interesting
265 17:59 <gzhao408> jnewbery i am loving taproot review, script is very exciting stuff
266 17:59 <jonatack> Paul_R: great 🚀
267 17:59 <jnewbery> #endmeeting