You perform tweaking at signing time if the key you're signing for was tweaked. That's a decision that was made when the output was constructed, not when it is spent.
Taproot outputs can consist of:
- Just a raw public key directly, with no tweaking. In this case only a key path spend is available, by signing with the corresponding private key. It's just a pay-to-pubkey now, and there is no tweaking at any point. This approach is not advisable for reasons I won't go into, but it is perfectly legal.
- Just an internal public key, with no script tree, which is tweaked to construct the output key using
H_TapTweak(pubkey) as tweak. In this case again only a key path spend is available, but at spending time the private key needs to be tweaked by the same amount or it wouldn't match the published public key. This is what most single-party taproot wallets use.
- The full construction with an internal public key, and a tree of one or more scripts. The output now consists of the internal key tweaked using
H_TapTweak(internal_pubkey || merkleroot), where
merkleroot is the Merkle root of the script tree. Such outputs can be spent in one of two ways:
- The key path spend, which involves signing with the tweaked private key, corresponding to the tweaked public key that was published in the output.
- A script path spend, where one of the script leaves, the internal (untweaked) key, and a Merkle path proving that script occurred in the tree, are revealed, together with whatever that script needs to satisfy it. If that script involves CHECKSIG operations, signatures must be provided for the public keys in the script. If those public keys are tweaked, the signin step will need to tweak the corresponding private keys too, but this is not necessary or advisable. Typical constructions use untweaked keys inside taproot scripts.
So to summarize: you need to tweak private keys at spending time when the public key you're signing for was tweaked. Whether that is the case depends on how the output was constructed. The internal public key (the one intended for key path spends) is usually tweaked (and must be, if scripts are present). The public keys inside those scripts usually aren't tweaked (but in theory they could be).
the privkey_a is used to sign the sighash to use TapScript_A, but nothing involves a tweak when constructing the witness
[signatureA, TapLeafA.script, control_map[TapLeafA.script]].
The control block (taken from the
control_map in the code you cite) contains the tweak that was used. It's just not used to sign the private keys, because the public keys in the script were not tweaked either. But the tweak must still be revealed, or verifiers cannot tell that the script you're revealed actually contributed to the published public key in the output.
My question is, if I want to construct a taproot output just with script spend, what's the point of using tweak?
You can't, really. Every taproot output that permits script path spending is constructed by taking an internal public key and tweaking it with the Merkle root, and you must know what that internal public key and tweak are to spend it, as you need to convince the verifier "wait! that public key you saw in the output, it's actually tweaked using my script here, so I can spend with that instead!". If you don't want the ability to spend with any internal key using a key path, you'll need to use an internal key that nobody knows the private key to; but there still must be one.
The philosophy behind Taproot is that nearly always, outputs can have an internal public key corresponding to some or all of the participants' consent, and heavily optimizes for being able to sign using the key path as a result. If you have a use case with just a script, the result may feel unnatural due to the need for a dummy internal public key, and tweak.