Thanks for demonstrating the two different implementations @salvatoshi and @AntoineP. It has gotten me thinking about what would be the best approach, and I see both having their tradeoffs.
I tried implementing both methods in btcd
, and similar to bitcoind
the difference in complexity of the two methods is a result of the attempt of doing input script validation in parallell during tx/block processing (maybe even more so in btcd because of how easy it is to distribute work across goroutines: btcd/blockchain/scriptval.go at 1eb974aab6ef11097571f9517b4b6b0c639ab63b · btcsuite/btcd · GitHub)
Similar to the bitcoind implementations it comes down to either (1) introducing a shared mutable state across the threads, or (2) doing a pre-check of the intended amount flows and then have CCV
assert this data (like in the annex) during script execution.
Or (3) having a deferred check framework in place, performing extra checks after a group of dependent inputs have been validated. The deferred checks framework would actually fit nicely (my hunch, I haven’t actually implemented it) with the btcd implementation.
Shared state (1)
The various alternatives all have their downsides, but generally I would be skeptical to introducing a shared state guarded by a mutex, and “give up” on parallel input validation.
I think it is definitely worth asking the question whether parallel input validation actually has a practical effect in real life (and benchmarks), but at the same time I think Why change it? There be dragons.
Annex hints (2)
The annex approach is nice in the sense that it doesn’t need that much plumbing into the script engine, and the separation of concerns. With this approach you would reduce the CCV opcode to just do “annex assertion” not really caring what the annex means. I believe that would make it easier to add more meaning to the annex at a later point (via a soft fork) and immediately have it available for assertion by CCV without touching the script engine.
I am curios to explore whether the annex hint approach would be useful together with other (future) script primitives. If you can specify for each input how you expect the funds will flow, that feels like it could be useful in itself. If you combine this with SIGHASH_NONE (only sign inputs), maybe that could be used to built partially signed transactions where flow of funds are pre-determined, but outputs are left unspecified (not sure if this is useful at all though).
Batch validation
Cross-input (batch) signature validation is definitely something we should think about supporting down the road, and accompany for that being practical to implement. To me neither of these approaches seems to be in the way of that (though something like deferred checks might be more aligned with that vision).