I'd like to specifically talk about the requirement of specifying an exact number of inputs that are required to spend the output. I understand the necessity of specifying exact inputs, but I don't understand the use case for specifying a number of inputs without specifying the exact inputs to spend.
The reason that we want to specify the number of inputs but not the exact inputs is because if we were to specify the exact inputs it creates a hash cycle. This then prevents the opcode from actually being used. I recommend reviewing the RPCs branch -- I'm currently in the process of doing a lot of refactoring, so this is a temporary branch --
https://github.com/JeremyRubin/bitcoin/commits/checktemplateverify-v2-rpcs-rb, which shows how templates are constructed with a bottom-up pass and then a top-down pass.
In addition, the BIP recognizes that committing to the sequences hash makes committing to a number of inputs "strictly redundant", but says doing so makes it easier to construct StandardTemplateHashes from the script.
With respect to constructing via script, the reason that separately committing the number of inputs from the sequences makes it possible to write a script that allows the spender to pass in *any* sequences they like, while still be restricted to a specific number of inputs, with the addition of OP_CAT (or similar). E.g.:
OP_SWAP OP_CAT OP_CAT OP_SHA256 OP_CTV
It's nice to be able to constrain the passed in hash to be any set of 7 sequences.
Can anyone expand on what is said in the "Rationale: Committing to the number of inputs" section in the BIP?
I think the above answers your question? But more specifically, the half spend problem crops up if I created 2 CTV outputs with the same script (with no # inputs restriction) they could be spent in the same transaction, creating half the outputs as if spent separately. Restricting the number of inputs to 1 prevents any sort of half-spend issue. The other protection from this is the commitment to the currently executing input index, which means identical basic CTV scripts cannot be spent in the same transaction.
The use case I'm concerned with is creating a timelocked cold wallet where arbitrary funds can be spent, but within some time-period the transaction can be reversed (for example, by a different higher priority key or by a multisig wallet with more keys than were used by the transaction being reversed). Requiring that op_ctv specify a specific number of inputs makes that use-case not generally possible or at best not efficient, since the wallet can contain many inputs, and in order to spend, you'd have to either have to spend each input one at a time, or you would have to have a large script that specifies optional op_ctv spends for every possible number of inputs you expect the wallet to have, which is obviously a bit of a pain and can go wrong if the wallet ends up having more inputs than you built the script for.
So I think that your vault use case is too general. If you refine it a little, you'll see that CTV works pretty well. You can see code for this in the repo above. Essentially you spend into a single output that can be either withdrawn 1 step at a time or sent back to cold storage. I don't have code for it at writing, but you can also set up a larger script (maybe using taproot) which allows for bigger withdrawals after bigger timeouts, but I think that simple is better as a first pass.
It's also possible to set up these kind of cold vaults which by default leave the coins at rest, but it's a bit more difficult to reason about so I've focused on the simple ones first.
Why not make specifying the number of inputs an optional thing so that some people can use it when its necessary and some can omit it when its not?
It keeps the BIP and implementation simple and can be added later with OP_CAT or a new template type. Most of the multi-input use cases can be done pretty easily with Taproot. I view OP_CTV as a first, and major, step towards multi-transaction programmability, and as we see the successes of that we can expand to more general mechanisms if the tooling that emerges around OP_CTV is insufficient for a desired use case. But I personally think that CTV is flexible enough to support most of the really compelling use cases in straightforward ways.