Subject: Re: [bitcoin-dev] Taro: A Taproot Asset Representation Overlay

From: Olaoluwa Osuntokun <[email-masked]>
Date: Fri, 15 Apr 2022 19:43:08 -0700

Hi y'all,

> In terms of achieving this level of binding within the Taro tree itself, I
> can think of three options:

The earlier BIP draft just sort of said "the commitment should be unique"
and hand waved away the exact algorithm used to verify this key property. I
thought about a few ways to do this, but most of them can't work, as the
taproot tree is always sorting the siblings before hashing them into a
parent.
This sorting means that all ordering information is lost, and can't be
obtained again afaict.

To get around this limitation, I've proposed a concrete scheme here to both
verify that the commitment is in a unique place within the tree, and also
that it doesn't exist anywhere else in the transaction (assumes widespread
usage of BIP 86): https://github.com/Roasbeef/bips/pull/21.

A series of inclusion and non-inclusion proofs are used to construct+verify
this property. At a high level the scheme takes advantage of the tagged hash
scheme used in taproot: leaves use "TapLeaf" as the tag, and branches use
"TapBranch" as the tag. Building upon this, we then require the Taro
commitment to be in the leftmost/rightmost (remember ordering info is lost)
of the tree. From here we can decompose things into a few different cases:

  * The control block proof is 32 bytes, meaning there's only a single
    element, so verify the taro commitment and the taproot commitment is
    correct.

  * The proof is instead 64 bytes, meaning there's a leaf at depth 1 with a
    sibling:
    * If the sibling is a leaf, then verify the pre-image is 32 bytes and
      the tagged hash calc matches up.
    * If the sibling is a branch, then verify that hashing the two opaque
      siblings that make it up gives the same branch (tap hash branch).

>From the PoV of wallets, this isn't too hard to manage as a Taro library can
just take the existing root of the wallet's scripts, and merge that into a
new root with the Taro leaf hanging off to the side.

As an aside, one thing that's missing in the ecosystem today is a
standardized algorithm for constructing a taproot tree given a set of script
leaves. The tree constructor has a lot of freedom to do things like put more
common things higher up in the tree, or always try to make the tree uniform,
etc, etc. The btcsuite libraries use a simple algo [1] I came up with that
just merges leaves into branches until there're no leaves left (even number)
or there's one leaf, with that last leaf being merged with the final branch.
After that you just keep on merging. It's not the most optimized/efficient
routine but it's simple which counts for a lot IMO.

Admittedly as is defined in my PR above, Taro is a bit demanding w.r.t
commitment space: it wants the highest position in the tree as that's easy
to verify and makes a smaller proof. The impact for items in the script tree
itself isn't too bad as it just means an extra 32 byte hash in the control
block proof when it comes to reveal time, and that's witness data which is
discounted at the block size level.

Zooming out a bit, assuming that applications/protocols start making more
structured commitments in the tapscript tree, it may make sense to roll out
a distinct BIP that carves out an explicit structure/split. As an example, a
new standard could be created that pushes all the actual scripts to the
"left" and everything else to the "right". In the "right" part of the tree,
we can use w/e tree structure we want, so we aren't bound by the sorting
quirk. If each project picks some 32-byte value (a hash of the name or w/e),
then another SMT (or w/e other merklalized k-v map) can be used to place
each root commitment in a unique location in the tree. Maybe something like
this also becomes the basis of future consensus-critical commitments (utxo
commitments, etc, etc)?

-- Laolu

[1]:
https://github.com/btcsuite/btcd/blob/99e4e00345017a70eadc4e1d06353c56b23bb15c/txscript/taproot.go#L618