[bitcoin-dev] TXHASH + CHECKSIGFROMSTACKVERIFY in lieu of CTV and ANYPREVOUT

Anthony Towns aj at erisian.com.au
Fri Jan 28 01:34:36 UTC 2022


On Wed, Jan 26, 2022 at 12:20:10PM -0500, Russell O'Connor via bitcoin-dev wrote:
> Recapping the relationship between CTV and ANYPREVOUT::

> While this is a pretty neat feature,
> something that ANYPREVOUT cannot mimic, the main application for it is
> listed as using congestion control to fund lightning channels, fixing their
> TXIDs in advance of them being placed on chain.  However, if ANYPREVOUT
> were used to mimic CTV, then likely it would be eltoo channels that would
> be funded, and it isn't necessary to know the TXIDs of eltoo channels in
> advance in order to use them.

Even if they weren't eltoo channels, they could be updated lightning penalty
channels signed with APO signatures so that the txid wasn't crucial. So
I don't think this would require all the work to update to eltoo just to
have this feature, if APO were available without CTV per se.

> An Alternative Proposal::
>  ...

> For similar reasons, TXHASH is not amenable to extending the set of txflags
> at a later date.

> I believe the difficulties with upgrading TXHASH can be mitigated by
> designing a robust set of TXHASH flags from the start.  For example having
> bits to control whether [...]

I don't think that's really feasible -- eg, what you propose don't cover
SIGHASH_GROUP: 

 https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2021-July/019243.html

> That all said, even if other txhash flag modes are needed in the future,
> adding TXHASH2 always remains an option.

I think baking this in from day 0 might be better: make TXHASH be
a multibyte opcode, so that when you decode "0xBB" on the stack,
you also decode a serialize.h:VarInt as the version number. Version 0
(0xBB00) gives hashes corresponding to bip342, version 1 (0xBB01) gives
hashes corresponding to bip118 (anyprevout), anything else remains as
OP_SUCCESS behaviour, and you retain a pretty compact encoding even if
we somehow eventually end up needing hundreds or thousands of different
TXHASH versions.

Because the version here is part of the opcode rather than pulled from
the stack, I think this preserves any benefits related to composition
or analysis, but is otherwise still pretty general. I'm imagining that
the idea would be to be consistent between CHECKSIG key versions and
TXHASH versions.

So I think just designing it this way means TXHASH *would* be "amenable
to extending the set of txflags at a later date."

> '<anyprevout-pubkey> CHECKSIGVERIFY can be simulated by '<apo_style_flag> TXHASH <pubkey> CHECKSIGFROMSTACKVERIFY'. 

I don't think that's quite right. BIP 118 anyprevout is done by taking
the pubkey "P", marking it as "APO-capable" (by prefixing it with 0x01),
and then getting a sighash and sig from the witness. Doing the same
with TXHASH/CSFSV would just be replacing "<APO:P> CHECKSIGVERIFY" with
"TXHASH <P> CSFSV" with the witness providing both the signature and
txhash flag, just as separate elements rather than concatenated. (The
"APO-capable" part is implicit in the "TXHASH" opcode)

> In addition to the CTV and ANYPREVOUT applications, with
> CHECKSIGFROMSTACKVERIFY we can verify signatures on arbitrary messages
> signed by oracles for oracle applications.  This is where we see the
> benefit of decomposing operations into primitive pieces.  By giving users
> the ability to program their own use cases from components, we get more
> applications out of fewer op codes!

While I see the appeal of this from a language design perspective;
I'm not sure it's really the goal we want. When I look at bitcoin's
existing script, I see a lot of basic opcodes to do simple arithmetic and
manipulate the stack in various ways, but the opcodes that are actually
useful are more "do everything at once" things like check(multi)sig or
sha256. It seems like what's most useful on the blockchain is a higher
level language, rather than more of blockchain assembly language made
up of small generic pieces. I guess "program their own use cases from
components" seems to be coming pretty close to "write your own crypto
algorithms" here...

I'm not really sure what the dividing line there is, or even which side
TXHASH would be on. I'm not even totally convinced that the "high level
language" should be describing what consensus provides rather than some
layer on top that people compile (a la miniscript). Just trying to put
into words why I'm not 100% comfortable with the principle per se.


One thing I've thought about is an opcode like "POP_SIGDATA" which would
populate a new "register" called "sigdata", which would then be added
to the message being signed. That's a generalisation of tapscript's
behaviour for "codeseparator" essentially. That is,

   x POP_SIGDATA p CHECKSIG

would be roughly the same as

   TXHASH x CAT SHA256SUM p CHECKSIGFROMSTACK

I think "POP_SIGDATA" makes for an interesting counterpart to
"PUSH_ANNEXITEM" -- we implicitly commit to all the annex items in
signatures, so PUSH_ANNEXITEM would give a way to use signed data that's
given verbatim in the witness in further calculations; but POP_SIGDATA
would do the opposite, allowing you to require data that's the result
of calculations and not explicitly spelled out in the witness be signed.

You could implement CHECKSIGFROMSTACK using that, ie:

    sig x p CHECKSIGFROMSTACK

is the same as:

    sig' x POP_SIGDATA p CHECKSIG

provided sig' applies a new "SIGHASH_NO_TX_DATA_AT_ALL" sighash flag to
"sig" that just does what it says.

You could likewise implement CTV as an extension to CHECKSIG -- define a
new pubkey type that's just the constant "0x0000" and have the "signature"
be valid if it's an exact match for the corresponding message hash. You
could bump the key to "0x0001" to introduce new hashes; and include a
"sighash" with the "signature" as well perhaps. (Apart from reusing an
existing opcode instead of introducing a new one, and costing some
additional witnss bytes, I don't think that makes much difference
eithr way)

I think the key tradeoff between "x POP_SIGDATA p CHECKSIG" and
"CHECKSIGFROMSTACK" isn't so much that one approach is a couple of bytes
more or less or one claims two opcodes vs just one for the other, but
whether it's common to want to commit to some extra random data alongside
the tx itself, and in the cases where that's desirable, if we can have
a standard way of constructing that and assume everyone will use it; or
if it's important that wallets can design their own way of committing to
the extra data more manually, because it's impotant to support different
approaches in different circumstances.


If we had CTV, POP_SIGDATA, and SIGHASH_NO_TX_DATA_AT_ALL but no OP_CAT,
are there any practical use cases that wouldn't be covered that having
TXHASH/CAT/CHECKSIGFROMSTACK instead would allow? Or where those would
be significantly more convenient/efficient?

(Assume "y x POP_SIGDATA POP_SIGDATA p CHECKSIGVERIFY q CHECKSIG"
commits to a vector [x,y] via p but does not commit to either via q so
that there's some "CAT"-like behaviour available)


I think a difference between "TXHASH EQUALVERIFY" and "CTV" is that
because the idea for TXHASH is to be compatible with CHECKSIGFROMSTACK,
then the messages it hashes should be distinct from anything else you
might ever sign. But for CTV that doesn't matter, because there's no
signature to be reused; so as a result, how the message is hashed can
be simpler, and that in turn may make it easier to do the "subtractive
covenants" and similar.

I guess I don't find that super important -- if you're manually
constructing covenants in script by putting together various bits of
data about a tx, then I guess I think you've already lost the game, and
having to have your script be a little more complicated in order to to
tagged hashes and the like is no big deal.


Concretely:

 - I think TXHASH needs to be designed to be upgradable; but I think
   that's solvable

 - I think it makes sense for TXHASH and CHECKSIG to be synchronised;
   so any message digest you can hash via txhash should be signable via
   CHECKSIG and vice-versa. Given that, I don't think this approach
   replaces APO, just adds to it.

 - I think I'd prefer having a single set of message digests shared
   between TXHASH and CHECKSIG, than having one set of message digests
   for CHECKSIG and a different set for CTV. But that's a design choice
   for CTV rather than an advantage of TXHASH over CTV.

 - I think defining some OP_NOPx in terms of TXHASH so that it can
   be made available without p2sh/segwit/tapscript wrapping would work
   fine, if that optimisation is worthwhile

 - Even if we favoured CTV over TXHASH for consensus implementation,
   I think "TXHASH" seems like a good primitive to use when talking
   about script language design...

Cheers,
aj



More information about the bitcoin-dev mailing list