Closed Be-P closed 6 months ago
The example is mainly meant to demonstrate the syntax of creating a taproot tree. You can put any pubkey that you like in any of the scripts. It doesn't have to match the pubkey used to create the taproot output, and in fact you can use an unspendable pubkey to create the output if you want to disable key-spending as an option.
It may be a good idea to use such a key in the example, to demonstrate a script-only taproot output and clarify that the pubkey used in the script path does not need to equal the internal pubkey.
The script is indeed a "key-spending" one right? it is not an example about "mast path spending". If I change the pubkey to an unspendable one the tx wouldn't be verified anymore, am I correct? The witness and the control block seem related to the pubkey spending not to the mast path spending approach
In taproot a key-spend is when you provide a signature for the taproot key itself. If you are spending via script, you can still include an OP_CHECKSIG in the script and provide a signature to satisfy the script (along with the script and cblock to verify the script path in the taproot).
I can totally see where this gets confusing since I have (lazily) recycled the pubkey in the example, and then created a signature with it:
const sig = Signer.taproot.sign(seckey, txdata, 0, { extension: target })
Since we are recycling the pubkey for the script path, this is a valid way to produce a signature for the script spend.
Also, this can be confusing as well:
const isValid = await Signer.taproot.verify(txdata, 0, { pubkey })
The signer allows you to specify which pubkey to use for validating a signature on the witness stack, which is useful for validating an OP_CHECKSIG script that includes a pubkey.
I agree that all-together this makes the example seem confusing and it should be updated to be more clear. I hope this explanation helps.
I would be really happy to add a PR that shows how to spend using a script path. I'm having trouble understanding how to generate a valid witness for the transaction to be spent, and how to verify it. Probably I'm also confusing the underlying concepts.
Is it correct that the line const [ tpubkey, cblock ] = Tap.getPubKey(pubkey, { tree, target })
can be used to just get the cblock and later use it to compose the witness? without having to use the tweaked pubkey anywhere
I would be really happy to add a PR that shows how to spend using a script path. I'm having trouble understanding how to generate a valid witness for the transaction to be spent, and how to verify it. Probably I'm also confusing the underlying concepts.
That would be great!
Is it correct that the line
const [ tpubkey, cblock ] = Tap.getPubKey(pubkey, { tree, target })
can be used to just get the cblock and later use it to compose the witness? without having to use the tweaked pubkey anywhere
The tweaked pubkey is used as the P2TR address, so for your script spending to work you need to send funds to the tweaked pubkey. If you want to disable key spending, then you would use a provably unspendable pubkey as the internal key. But you need to spend to the tweaked pubkey because that is what verifies the script + cblock.
If you have already computed the tweaked pubkey elsewhere, or are just grabbing it from a transaction, then yes you can just ignore it and grab the cblock for verification.
It seems that the example is generating a public key (to which the funds need to be sent) in such a way that the pubkey depends on the target script that we want the spender to verify.
If I'm not mistaken the power of Taproot is to enable the commitment of funds to a not-pubic unlocking MAST (the whole Tree) and allow the spender to satisfy just one of the possible alternatives, inside the MAST. that way we are actually not using the MAST feature. The pubkey actually depends on the unlock script