Understanding Taproot Assets Protocol

Hiroki Gondo
Nayuta Engineering Blog
7 min readJun 9, 2023

--

The Taproot Assets Protocol (formerly Taro, hereafter referred to as TAP) is a protocol for representing UTXO-based assets on top of Bitcoin. This article aims to understand how TAP creates and transfers assets.

The specifications are as follows. Here I will mainly explain bip-tap and bip-tap-ms-smt.

The implementation by Lightning Labs is here.

What Is Taproot?

Taproot is a new type of Bitcoin output that allows two types of payment conditions to be specified simultaneously: Key Pathand Script Path.

Key Path, like traditional P2PKH, is spent by the signature of a public key. Key Aggregation (multi-sig) with Schnorr signatures can also be used.

Script Path, like traditional P2SH, allows complex payment conditions by a scripting language. It also allows multiple scripts to be specified simultaneously. These scripts are not serialized directly into the Taproot output but are structured by a Merkle tree and compressed into its root hash. Individual scripts are published on-chain only if the output is spent under their conditions.

TAP embeds its data (Asset Tree) in Script Path. This data is uninterpretable and undetectable from a Bitcoin perspective since it is hashed.

Taproot Asset Representation

Asset Tree

An Asset Tree, a two-level Merkle Sum Sparse Merkle Tree structure, represents Taproot Assets. The lower represents the set of UTXOs of a single asset identified by asset_id. The upper aggregates the lowers.

The value of the upper root hash (asset_tree_root) is embedded in Script Pathof Taproot outputs, and the tree state is committed.

An asset transfer creates a new Asset Tree, and the previous one is updated. These are accompanied by the issuance of a new Bitcoin transaction that spends the previous Taproot output; unauthorized transfers that do not meet the specifications (e.g., inflation, double-spending) are considered invalid. *

* This is the so-called Client Side Validation concept; those familiar with Bitcoin’s emergent consensus may be curious about the basis for the correctness of the specifications, but I’ll put that issue on hold here.

Merkle Sum Sparse Merkle Tree

Merkle Sum Sparse Merkle Tree (hereafter referred to as MS-SMT) is a variant of the Markle tree defined by bip-tap-ms-smt. Since the key is 256 bits, it has 2 ^ 256 leaves, most of which are empty.

Each leaf contains an amount, and each branch commits the total amount in the leaves in the subtree. Even if the contents of a subtree are not known, the total amount contained therein can be known just by looking at the branch. The root commits the total amount in the tree.

Like the Markle Tree in general, a pruned tree containing any leaf can offer the inclusion proof of these leaves (Merkle Proof), but MS-SMT also supports non-inclusion proofs. This is accomplished by the restriction that a non-existent key’s leaf must be explicitly set to a value indicating its non-existence (None) (inclusion proof of “None” = non-inclusion proof). Thus the default tree has 2 ^ 256 “None” leaves.

Asset Leaf(Asset UTXO)

The lower MS-SMTs of the Asset Tree have asset_script_key as the key, and the Asset Leaf as the value. Each Asset Leaf represents a UTXO of the asset (hereafter referred to as UTXO for simplicity *), and the following items, including optional ones, are serialized in the Type-length-value format.

* Not that of Bitcoin.

  • taproot_asset_version
  • asset_genesis
  • asset_id
  • asset_type
  • amt
  • lock_time
  • relative_lock_time
  • prev_asset_witnesses
    - prev_asset_id
    - asset_witness
    - split_commitment_proof
  • split_commitment
  • asset_script_version
  • asset_script_key
  • asset_group_key
  • canonical_universe

asset_genesis is a preimage of the data from which asset_idis derived.

asset_script_key is both the key of the Asset Leaf and the public key of Taproot type (on a virtual tap-vm, separate from Bitcoin) and is the payment condition for spending the UTXO represented by this Asset Leaf.

When spending a UTXO, the (external) Bitcoin payment condition must be met as well, as the (internal) TAP payment condition specified in asset_script_key must also be met by prev_asset_id and asset_witness. For example, asset_witness is a signature by asset_script_key of the spent UTXO.

If a UTXO is split in a transfer, split_commitment and split_commitment_proof are needed.

split_commitment is an MS-SMT that refers to all the UTXOs after the split (In this case, Asset Tree has three layers), and the sum value of the root is the total amount of assets transferred.

split_commitment_proof is a Merkle Proof of split_commitment which proves the existence of a split.

Only one of all splits has prev_asset_id, asset_witness, and split_commitment. All other splits have onlysplit_commitment_proof. All splits share prev_asset_id and asset_witness.

Asset Creation

An asset is created by embedding a new Asset Tree containing new asset UTXOs into a Taproot output of a Bitcoin transaction (the genesis transaction of the asset). The ID of the asset (asset_id) is defined by the following formula.

asset_id := sha256(genesis_outpoint || sha256(asset_tag) || asset_meta_hash || output_index || asset_type)

genesis_outpoint is the output of the previous transaction spent by the transaction, and output_index is the output position of the transaction containing the Asset Tree. These ensure that asset_id is globally unique.

The newly created UTXO omitsprev_asset_id and asset_witness.

Asset Transfer

Like Bitcoin UTXOs, a merger and split of UTXOs may occur in a transfer of assets. But we begin with a simple case where none of the above occurs *.

* Some simplifications have been made in the following explanation. For example, The change outputs of Bitcoin are omitted.

Alice has 10 and transfers all of them to Bob without splitting.

Alice creates and broadcasts a Bitcoin transaction that spends the Taproot output where the UTXO is embedded. This has two new Taproot outputs. One contains a new Asset Tree containing a new UTXO with 10 that Bob can control (e.g., using Bob’s Bitcoin key). The other includes an Asset Tree from which the asset UTXO has been removed from the original Asset Tree, which Alice can control.

The new UTXO with 10 for Bob refers to Alice’s previous UTXO by prev_asset_id. In asset_witness, the signature by the previous asset_script_key is placed. In asset_script_key of the new UTXO, a new one given by Bob in advance is placed.

Bob needs to verify that payment conditions have been met and that the asset has not been inflated after the transfer to confirm receipt of the asset.

  • Does the newly created Asset Tree for Bob contain a new UTXO that meets the payment conditions?
  • Has the input UTXO been removed from the updated Asset Tree for Alice?
  • If there are other Taproot outputs in the transaction, Don’t they contain another Asset Tree? *
  • and so on.

These are proven/verified by the inclusion/non-inclusion proofs of MS-SMT and the preimages and proofs of the Taproot outputs.

* If a different UTXO is added to such an Asset Tree than the one for Bob that spends the input UTXO, it would result in double-spending.

Merger UTXO

Alice has 3 and 7, transferring all of them to Bob.

Each input UTXO may belong separately to a different Asset Tree or different keys (asset_script_key) of the same tree. The new UTXO for Bob should include prev_asset_id and asset_witness for each of the two UTXOs spent.

Split UTXO

Alice has 10 and transfers 7 of them to Bob. As a result, Alice has 3 for change, and Bob has 7.

Alice’s change UTXO has prev_asset_id, asset_witness, and split_commitment; Bob’s new UTXO only has split_commitment_proof *.

I will not explain when to merge and split UTXOs simultaneously.

* This is slightly different from what the specification says, but the current implementation probably looks like this.

Asset Provenance

Although not mentioned previously, each input UTXO must be verified as legitimate before the asset transfer can be verified. For each input UTXO, it must be proved/verified that all transfers along the routes from the genesis transaction of the asset, on which the asset is based, to the new UTXO have been performed correctly.

The routes are a complex graph from the genesis transaction to the most recent transaction. In the above, at the Tx A, it needs to validate transfers on all blue transactions of its ancestors, and at the Tx B, it needs to validate transfers on all of the blue and red ones. This is a significant scalability challenge because history grows quasi-exponentially.

Also, since the preimages and proofs required for verification are not published on-chain, how they are passed from the sender to the receiver during asset transfer is an issue.

Scalability

Increased history is a significant scalability challenge. Several solutions have been proposed, but support for Lightning Network is one of the most promising. Off-chain transactions do not increase history.

This protocol is still new and the specifications are incomplete. For example, there is this issue I pointed out before.

--

--