From feb62236fd39924998e695785c3fc1f5bb45e5c7 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 15 Dec 2021 19:20:54 -0800 Subject: [PATCH 1/2] bip-taro: initial version --- bip-taro-addr.mediawiki | 101 ++++ bip-taro-ms-smt.mediawiki | 273 +++++++++++ bip-taro-proof-file.mediawiki | 188 ++++++++ bip-taro-universe.mediawiki | 209 ++++++++ bip-taro-vm.mediawiki | 119 +++++ bip-taro.mediawiki | 864 ++++++++++++++++++++++++++++++++++ 6 files changed, 1754 insertions(+) create mode 100644 bip-taro-addr.mediawiki create mode 100644 bip-taro-ms-smt.mediawiki create mode 100644 bip-taro-proof-file.mediawiki create mode 100644 bip-taro-universe.mediawiki create mode 100644 bip-taro-vm.mediawiki create mode 100644 bip-taro.mediawiki diff --git a/bip-taro-addr.mediawiki b/bip-taro-addr.mediawiki new file mode 100644 index 0000000000..52143e2f4f --- /dev/null +++ b/bip-taro-addr.mediawiki @@ -0,0 +1,101 @@ +
+ BIP: ???
+  Layer: Applications
+  Title: Taro On Chain Addresses
+  Author: Olaoluwa Osuntokun 
+  Comments-Summary: No comments yet.
+  Comments-URI: https://git
+  Status: Draft
+  Type: Standards Track
+  Created: 2021-12-10
+  License: BSD-2-Clause
+
+ +==Abstract== + +This document describes a way to map a single-asset Taro send to a familiar +bech32 address, as well as a way to map that address into a valid +Taro script tree that can be included in a broadcast transaction to complete a +transfer. Once the transaction has been broadcast, the receiver can use the +previous outpoint of the confirmed transaction to lookup the complete asset +proof in their chosen Universe. + +==Copyright== + +This document is licensed under the 2-clause BSD license. + +==Motivation== + +The Taro protocol needs an easy way to allow users to send each other assets +on-chain, without requiring several rounds of interaction to exchange and +validate proofs. By using the existing bech32 address +serialization standard, such addresses look distinct, while also looking +familiar enough based on the character set encoding. The described address +format also addresses a number of possible foot guns, by making it impossible +to send the wrong asset (based on an address) amongst other protections. + +==Specification== + +A Taro asset is uniquely defined by its asset_id as well as the +asset_script_hash that serves as a predicate that must be +satisfied for transfers. These values, along with an internal Taproot key used +when creating the Bitcoin output that holds the Taro asset, are encoded into a +single address. + +===Encoding an Address=== + +Let the human readable prefix (as specified by BIP 173) be: + +* taro for mainnet +* tarot for testnet + +Given the 32-byte asset_id, 32-byte +asset_script_hash, and 32-byte x-only BIP 340/341 internal public +key, 8-byte amount to send, an address is encoded as: +* bech32(hrp=taroHrp, asset_id || asset_script_hash || internal_key || amt) + +===Decoding and Sending To An Address=== + +Given a valid Taro address, decompose the contents into the referenced +asset_id, asset_script_hash, and +internal_key. + +Construct a new blank Taro leaf with the following TLV values: +* taro_version: 0 +* asset_id: asset_id +* asset_type: 0 +* amt: amt_to_send +* taro_version: 0 +* asset_script_version: 0 +* asset_script_hash: asset_script_hash + +Create a valid tapscript root, using leaf version 0x0c with the +sole leaf being the serialized TLV blob specified above. + +Create the top-level taproot public key script, as a segwit v1 witness +program, as specified in BIP 341, using the included key as the internal key. + +With the target taproot public key script constructed, the asset is sent to the +receiver with the execution of the following steps: +# Construct a valid transaction that spends an input that holds the referenced asset_id and ''at least'' amt units of the asset. +# Create a new Taro output commitment based on the input commitment (this will be the change output), that now only commits to S-A units of asset_id, where S is the input amount, and A is the amount specified in the encoded Taro address. +## This new leaf MUST have a split_commitment specified that commits to the position (keyed by sha256(output_index || asset_id || asset_script_hash) within the transaction of the newly created asset leaf for the receiver. +## Add an additional output that sends a de minimis (in practice this MUST be above dust) amount to the top-level taproot public key computed earlier. +## Broadcast and sign the transaction, submitting the resulting Taro state transition proof to a Universe of choice, also known by the receiver. +# Post the resulting state transition proof to the specified Universe. The submitted proof ''must'' contain the optional auxiliary value of the full split_commitment the receiver requires to spend the asset. + +===Spending The Received Asset=== + +In order to spend (or simply confirm receipt) of the received asset, the +receiver should: +# Re-derive the taproot public key script created above that sends to their specified Taro asset leaf. +# Wait for a transaction creating the output to be confirmed in the blockchain. +## In practice this may be via light client protocols such as BIP 157/158, or simply a full node with an address index, or import public key. +# For each previous outpoint referenced in the transaction: +## Look up the previous outpoint as a key into the chosen canonical Universe/Multiverse. +### If the key is found, verify the inclusion proof of the value (as described in [[../master/bip-cmyk-proof-file.mediawiki|bip-cmyk-proof-file]]), and extract the split_commitment inclusion proof for the output. +# Walk the Universe tree backwards in time to incrementally construct the full provenance proof needed to spend the asset. + +==Test Vectors== + +==Reference Implementation== diff --git a/bip-taro-ms-smt.mediawiki b/bip-taro-ms-smt.mediawiki new file mode 100644 index 0000000000..1904213770 --- /dev/null +++ b/bip-taro-ms-smt.mediawiki @@ -0,0 +1,273 @@ +
+ BIP: ???
+  Layer: Applications
+  Title: Merkle Sum Sparse Merkle Trees
+  Author: Olaoluwa Osuntokun 
+  Comments-Summary: No comments yet.
+  Comments-URI: https://git
+  Status: Draft
+  Type: Standards Track
+  Created: 2021-12-10
+  License: BSD-2-Clause
+
+ +==Abstract== + +This document describes a merkle-sum sparse merkle tree (MS-SMT) data +structure. This is an augmented version of a sparse merkle tree that includes +a sum value which is combined during the internal branch hashing operation. +Such trees permit efficient proofs of non-inclusion, while also supporting +efficient fault proofs of invalid merkle sum commitments. + +==Copyright== + +This document is licensed under the 2-clause BSD license. + +==Motivation== + +Taro is a Taproot-native asset overlay protocol. Rather than post all asset +related data on-chain in OP_RETURN outputs, the protocol instead +uses a series of commitments anchored within the Taproot script tree. When +handling unique assets, it's important to be able to prove that the former +owner (or seller) of the asset is no longer committing to it within their tree. +Additionally, when carrying out multi-asset swaps, verifiers need to be able to +efficiently verify that no new assets are being created (inflation check). The +MS-SMT supports both non-inclusion proofs, and non-inflation proofs. + +==Design== + +A merkle sum tree is a merkalized key-value map simulated over a particia +merkle tree of depth 256 (as we use sha256 as our hash function). The "base" +state of the tree, is a merkle tree with 256 leaves, storing an "empty hash". +Within this tree, the digests of an empty leaf, and empty internal nodes for +each level can be computed ahead of time. The "value" of an empty leaf is zero. + +In addition to storing the hash digest of a leaf/branch, an 8-byte value is +also stored along side the entry, making each entry 40 bytes in length. The +root hash therefore commits to the digest of all items in the leaf, as well as +the sum of all the "sum values" in the set of leaves. When combining two +branches/leaves, the sum of the left and right leaf/branch is serialized along +with the hash digest of the nodes. + +When inserting a new key into the tree, at each level, the ith bit of the key +is used to traverse left or right down the tree. Due to this traversal, every +possible key has a unique location (position wise) within the set of leaves. A +non-inclusion proof is the proof that the value at the unique position for a +key is empty. + +Due to the nature of the mapping, sparse merkle trees are ''history +independent'' meaning no matter the inserting order, given the same set of keys +and values, the same root hash will always be produced. As the size of the +tree is intractable, a series of techniques are used to maintain a relevant set +of branches and leaves in memory, using a persistent key-value store to store +the relevant unique items of the tree. Proofs can be compressed by using a +bitmap to indicate if the next node in the proof is an empty hash for that +level, or the parent of the item being proved. + +===Specification=== + +We use sha256 as our hash function, and 8-byte sum values. + +====Building the Empty Hash Map==== + +The map of all empty hashes by level empty_hashes can be +pre-computed ahead of time, as: +* The hash of an empty leaf is empty_hash_1 = sha256(nil, nil) +* The hash of an empty branch at the second level is empty_hash_2 = sha256(empty_hash_1, empty_hash_1) +* and so on... + +We refer to the map resulting from this route as the +empty_hash_map: + +build_empty_hash_map() -> map[int][32]byte: + + empty_hash_map = make(map[int][32]byte) + prior_level_hash = None + for i in range(256): + if prior_level_hash is None: + prior_level_hash = sha256(nil, nil, 0) + empty_hash_map[i] = prior_level_hash + continue + + empty_hash_map[i] = sha256(prior_level_hash, prior_level_hash, 0) + + return empty_hash_map + + + +====Looking Up Items==== + +Looking up an item in the tree requires traversal down the tree based on the +next bit position of the key itself. We assume the existence of a persistent +key-value store that maps the hash of a node to the left and right digests of +its children. + +The following routine specifies the insertion algorithm: + +lookup_item(key [32]byte, db KVStore) -> MerkleSumLeaf: + + root_hash, _ = db.fetch_root_hash() + current_branch = root_hash + + value_hash, value_sum = None + for i in range(256): + if bit_index(i, key) == 0: + current_branch, _ = db.get_children(current_branch) + else: + _, current_branch = db.get_children(current_branch) + + return MerkleSumLeaf(current_branch.hash, current_branch) + + + +====Inserting Items==== + +Inserting items into the tree entails traversing the tree until we arrive at +the position for the leaf, then bubbling up (hashing and summing) the change +all the way up the tree. + + +insert_item(key [32]byte, value []byte, sum_value int64, db KVStore) -> None: + root_hash, _ = db.fetch_root_hash() + current_branch = root_hash + + insertion_path = [] + value_hash, value_sum = None + for i in range(256): + if bit_index(i, key) == 0: + current_branch, sibling = db.get_children(current_branch) + insertion_path.append(sibling) + else: + sibling, current_branch, = db.get_children(current_branch) + insertion_path.append(sibling) + + db.insert(current_branch.parent_hash, MerkleSumLeaf(key, value, sum_value)) + + for i in range(256): + updated_sum = sum_value + inclusion_path[-1].value + + sibling_node = insertion_path[-1] + if bit_index(i, key) == 0: + updated_value = sha256(value, sibling_node.sum_value, updated_sum) + + db.insert(key=updated_value, value=(sibling_node, value)) + else: + updated_value = sha256(insertion_path[-1].hash, value, updated_sum) + + db.insert(key=updated_value, value=(value, sibling_node)) + + value = updated_value + sum_value = updated_sum + + insertion_path.pop() + + return None + + + +====Deleting Items==== + +Deleting an item is identical to insertion, but we delete the item in the tree +by setting its value to the empty hash. + +delete_item(key [32]byte, db KVStore) -> None: + return insert_item(key, nil, 0, db) + + +====Creating Inclusion & Non-Inclusion Proofs==== + +An inclusion proof of an item proves that the item is found in the tree, and +has a certain sum value. A non-inclusion tree proves the opposite: that an item +is not found within the tree. + +Generating an inclusion or non inclusion proof entails walking down the tree +and obtaining all the sibling hashes and their sum values: + +gen_merkle_proof(key [32]byte, db KVStore) -> []MerkleSumNode + root_hash, _ = db.fetch_root_hash() + current_branch = root_hash + + proof_nodes = [] + value_hash, value_sum = None + for i in range(256): + if bit_index(i, key) == 0: + current_branch, sibling = db.get_children(current_branch) + proof_nodes.append(sibling) + else: + sibling, current_branch, = db.get_children(current_branch) + proof_nodes.append(sibling) + + return proof_nodes + + +A plain proof is always a series of 256 merkle sum elements. However we can +compress proofs by using an extra bitmap that indicates if the proof contents +are an empty hash or not. + +compress_merkle_proof(proof []MerkleSumNode) -> CompressedProof: + compressed_proof = new_compressed_proof( + compression_bits=new_bit_vector(256), + proof=[]MerkleSumNode{}, + ) + + for i, proof_node in proof: + if proof_node == empty_hash_map[i]: + compressed_proof.compression_bits.append(1) + else: + compressed_proof.proof.append(proof_node) + + return compressed_proof + + +====Verifying Inclusion & Non-Inclusion Proofs==== + +In order to verify a proof, we need to confirm that if starting at the proof, +if we hash and sum up the tree, then we'll end up at the same root hash and sum +value. + +Before proofs are verified, the proof should first be decompressed: + +decompress_merkle_proof(compressed_proof CompressedProof) -> []MerkleSumNode: + proof = []MerkleSumNode{} + + for i in range(256): + if compressed_proof.bit_at(index=i) == 1: + proof.append(empty_hash_map[i]) + else: + proof.append(compressed_proof.proof) + compressed_proof = drop_1(compressed_proof.proof) + + return proof + + +With the proof decompressed, we verify the proof by hashing and summing up each +level. + +verify_merkle_proof(proof []MerkleSumNode, root MerkleSumNode, + key [32]byte, value []byte, value_sum int64) -> bool: + + for i in range(256): + if bit_index(i, key) == 0: + value = sha256(proof[-1-i], value, proof[1-i].sum, value_sum) + else: + value = sha256(value, proof[-1-i], value.sum, value_sum) + + return root.hash == value && root.sum == value_sum + + + + +====Caching Optimizations==== + +TODO(roasbeef): + + +==Test Vectors== + +TBD + +==Backwards Compatibility== + +==Reference Implementation== + +github.com/lightninglabs/taro diff --git a/bip-taro-proof-file.mediawiki b/bip-taro-proof-file.mediawiki new file mode 100644 index 0000000000..72301a0721 --- /dev/null +++ b/bip-taro-proof-file.mediawiki @@ -0,0 +1,188 @@ +
+ BIP: ???
+  Layer: Applications
+  Title: Taro Flat File Proof Format
+  Author: Olaoluwa Osuntokun 
+  Comments-Summary: No comments yet.
+  Comments-URI: https://git
+  Status: Draft
+  Type: Standards Track
+  Created: 2021-12-10
+  License: BSD-2-Clause
+
+ +==Abstract== + +This document defines a flat file proof format as a standardized way to package +Taro asset proofs. The proof format itself is an append-only log of the prior +lineage of a given asset. Proofs are anchored at the initial "genesis output" +for a given asset. A proof of a single Taro state transition includes a Bitcoin +merkle proof, a CMYK merkle-sum sparse merkle tree (MS-SMT) inclusion proof, and +finally a set of valid witnesses for the state transition. + +==Copyright== + +This document is licensed under the 2-clause BSD license. + +==Motivation== + +The Taro protocol is an overlay protocol that enables the representation and +transfer of assets on the base Bitcoin blockchain. As Bitcoin is a UTXO-based +system each asset is itself rooted at an initial "genesis" transaction, which +marks the creation of said asset. An asset is therefore defined by its genesis +output, as this marks its lineage. To ensure implementations are able to verify +provenance proofs across the ecosystem, a standardized proof format is +proposed. The proof format is a linear log of state transitions, allowing new +transfers/transition to simply be append to the end of the fail. + +==Design== + +Proving provenance of an asset requires the following arguments at each point +in the past history of the asset: +* The very first snapshot of an asset is rooted at the genesis outpoint as dictated by the canonical Universe. +* A valid merkle proof that proves the inclusion of the genesis outpoint and resulting created asset. +* At each step/transition beyond the genesis outpoint: +** A valid merkle proof of a transaction which spends the outpoint referenced in the prior step. +** A valid MS-SMT opening proving the commitment of the new location of the asset. +** A valid asset witness state transition from the prior outpoint to the new location. + +===Specification=== + +The Taro proof file is a flat file that records each relevant state transition +for a given asset to be verified. The file is verified incrementally, with +verification halting if an invalid state transition or proof is encountered. + +A file is a series of inclusion and state transition proofs rooted at a given +genesis outpoint. The very first transition requires now witness validation as +its the genesis outpoint. + +====File Serialization==== + +A single inclusion and state transition proof has the following format: +* previous_outpoint || block_header || merkle_inclusion_proof || anchor_transaction || tlv_proof_map + +where: +* previous_outpoint: is the 36-byte outpoint of the Taro-committed output being spent. If this is the very first proof, then this value will be the "genesis outpoint" for the given asset. +* block_header: is the 80-byte block header that includes a spend of the above outpoint. +* merkle_inclusion_proof: is the merkle inclusion proof of the transaction spending the previous_outpoint. This is serialized with a BigSize length prefix as: +** length_of_proof || serialized_proof +** The serialized_proof in BIP 37 format. (TODO(roasbeef): only ever proving one at a time, can roll something new here?) +* anchor_transaction: is the transaction spending the previous_outpoint. This transaction commits to at least a single Taro asset tree within one of its outputs. +* tlv_proof_map: stores the Taro-specific information needed to verify the structure of commitments as well as Taro-level state transitions. + +The tlv_proof_map has the following defined types: +* type: 0 (asset_output_pos) +** value: +*** [uint32:output_index] +* type: 1 (internal_key) +** value: +*** [32*byte:internal_key_schnorr] +* type: 2 (taro_asset_leaf) +** value: +*** [tlv_blob:serialized Taro asset leaf] +* type: 3 (asset_leaf_proof) +** value: +*** [...*byte:serialized_ms_smt_proof] +* type: 4 (split_commitment_opening) +** value: +*** [...*byte:serialized_ms_smt_proof] + +where: +* asset_output_pos: is the output index that holds the Taro asset commitment that spends the prior asset commitment . +* taro_asset_leaf: is the serialized TLV asset leaf resulting from a valid spend of the asset held at the previous_outpoint. +* asset_leaf_proof: is the revealed proof of the Taro asset leaf above, anchored in the anchor_transaction at the taproot output specified by asset_output_pos. +* split_commitment_opening: is a valid split commitment opening for the taro_asset_leaf when the asset leaf was the output of the split of the asset spent at previous_outpoint. + +The final flat proof file has the following format: +* file_sum: 32-bytes - 256 sum over the remaining contents of the file +* file_version: 4-bytes - version of proof file +* taro_proofs: BigSize length prefixed serialized proofs + +// TODO(roasbeef): make the file_sum instead an MMR over the set? allows for probabilistic verification of file, prove certain transition fragments + +====Proof Verification==== + +Verification of a proof file starts at the first entry (the genesis output +creation) and walks forward, validating each state transition and inclusion +proof in series. If any state transition is found to be invalid, then the asset +proof is invalid. Otherwise, if the file is consumed in full without any +violations, the proof is said to be valid. + +Given a proof file for a given asset f_proof, genesis outpoint +g verification is defined as follows: +# Verify the integrity of the proof file: +## Extract the 32-byte file_sum prefix. +## Compute the sha256 over the remainder of the file. +## If this computed value doesn't match file_sum, verification fails. +# Verify each inclusion proof and state transition: +## Parse the next proof block from the flat file. +## If this is the first proof to be verified: +### Store the previous_outpoint as the genesis outpoint. +## Otherwise, verify that the anchor_transaction has an inputs that spends the ''prior'' previous_outpoint +## Given the anchor_transaction verify that the included merkle_inclusion_proof rooted at the merkle root of the block_headertlv_proof_map. +## If the anchor_transaction does not have ''at least'' asset_output_pos outputs, verification fails. +## Verify that the asset_leaf_proof embeds the taro_asset_leaf at the outpout rooted at the asset_output_pos using the specified internal_key to compute the taproot commitment. +## Verify that the asset witness included at the prev_asset_witness field of the taro_asset_leaf is valid based on the specific asset_script_version +## If a split_commitment_opening is present, verify that the included leaf is a valid opening rooted at the taro_asset_leaf's split_commitment_root field. + +A psuedo-code routine for flat file verification follows: + +verify_asset_file_proof(file_proof []byte, genesis_outpoint OutPoint, + assetID [32]byte) -> bool + + file_sum = read(num_bytes=32, file_proof) + sha_sum = sh256(drop(bytes_to_drop=32, file_proof)) + if file_sum != sha_sum: + return false + + genesis_outpoint, prev_outpoint = None + file_reader = new_bytes_reader(file_proof) + while file_reader.len() != 0: + proof_block = parse_proof_block(file_reader) + + if genesis_outpoint is None: + genesis_outpoint = proof_block.previous_outpoint + + txn = proof_block.txn + if genesis_outpoint is not None: + if !spends_prev_out(txn): + return false + + if !verify_merkle_proof( + proof_block.block_header, proof_block.merkle_inclusion_proof, txn, + ): + return false + + proof_tlv_map = proof_block.tlv_map + + if len(txn.outputs) < proof_tlv_map.asset_output_pos: + return false + + if !verify_asset_tree_proof( + txn, proof_tlv_map.taro_asset_leaf, proof_tlv_map.asset_leaf_proof, + ): + return false + + if !verify_taro_state_transition(proof_tlv_map.taro_asset_leaf): + return false + + if proof_tlv_map.split_commitment_opening is not None: + if !verify_split_commitment( + proof_tlv_map.taro_asset_leaf, + proof_tlv_map.split_commitment_opening, + ): + return false + + return true + + +==Test Vectors== + +TBD + +==Backwards Compatibility== + +==Reference Implementation== + +github.com/lightninglabs/taro diff --git a/bip-taro-universe.mediawiki b/bip-taro-universe.mediawiki new file mode 100644 index 0000000000..3eef2c8d65 --- /dev/null +++ b/bip-taro-universe.mediawiki @@ -0,0 +1,209 @@ +
+ BIP: ???
+  Layer: Applications
+  Title: Taro Asset Universes
+  Author: Olaoluwa Osuntokun 
+  Comments-Summary: No comments yet.
+  Comments-URI: https://git
+  Status: Draft
+  Type: Standards Track
+  Created: 2021-12-10
+  License: BSD-2-Clause
+
+ +==Abstract== + +Taro asset provenance is defined by the lineage of an asset all the way back to +the genesis point, which is the outpoint that the unique asset identifier is +derived from. A Taro Universe is proposed as a way for users/holders of an +asset to easily bootstrap their recognition of a given genesis point as the +root of an asset. A Universe is an MS-SMT that indexes into the set of spent +outpoints that track asset movement/transfer. A Universe can contain only the +set of genesis outpoints for an asset, several assets, track individual +transactions, and also be used as an aggregation layer. + +==Copyright== + +This document is licensed under the 2-clause BSD license. + +==Motivation== + +In order to give users/holders of an asset an easy way to bootstrap provenance, +as well as track the total amount of units issued for a given asset, an +on-chain merkalized indexing structure is necessary. Further, if we define +constraints w.r.t how a "canonical" Universe can be updated, then users are +able to watch a set of on-chain outputs to be notified of further chain +issuance. Continuing to build off this structure, if users are able to maintain +a trust relationship with the issuer of an asset (say the asset belongs to a +closed source game), then they can delegate update rights to a single or +federated set of parties, allowing them to bundle several asset updates in a +single transaction, thereby scaling on-chain transfers. + +==Design== + +A Taro asset Universe MS-SMT differs from the normal MS-SMT in that the key +index of the lowest tree is an ''outpoint'' rather than an asset script key as +given an outpoint where an asset ''was'' present, the asset Universe maps to +the Taro transaction+spending meta data. Given this outpoint indexing +structure, if we create a new "re genesis" outpoint, then we can construct a +new virtual Taro transaction graph which provably tracks the movement of assets +in an off-chain manner, relying on a single or federated party to handle +updates. + +===Specification=== + +====Asset Universes==== + +An Asset Universe is a publicly audit able merkle-sum sparse merkle tree +(MS-SMT) over one or several assets. Unlike the normal Taro asset tree, a +Universe doesn't itself live within a solely controlled output. Instead a +Universe may exist on its own as a distinct output. A close analogue to a +public Universe is a block explorer. A Universe can be used to: +* bootstrap proof verification by committing to the set of genesis outputs +* generate more compact proofs w/ an additional trust assumption +* audit to the total amount of units in existence for a given asset +* track new asset issuance for a given asset ID + +Universes can also be used to essentially compress the history of a series of +transfers into a single on-chain transaction. From this PoV, a Universe can be +seen as a sort of Taro asset channel wherein one party (or a +consortium/threshold of them) can batch update a tree to clear countless +transfers in a single on-chain transaction. Such a Universe can also be created +over multiple assets, effectively becoming a Universe of Universes, or a +''Multiverse''. + +During asset creation, the party creating the asset (identified by it's all +zero prev_asset_id value) MAY also specify a +cannonical_universe field which specifies additional constraints +on the set of outputs produced. Namely, if this field is specified during asset +creation, then the ''second'' output of the next spend after the genesis +outpoint MUST commit to an updated base Universe that indexes into the prior +genesis outpoint spend (asset creation). In addition, the internal key used for +the output MUST be the asset_key_family filed specified during +asset creation. After each subsequent asset issuance event, this output SHOULD +be updated to commit to the new updated base Universe that indexes into all +asset issuance transactions on chain. + +Specifying a key effectively blesses a public key on-chain, allowing it to be +used to commit to the "canonical" history of an asset. In addition, those +wishing to be "notified" of new asset issuance can watch this output on-chain +to track any modifications. + +====Primordial Universes & Genesis Asset Verification==== + +Unlike a normal Taro asset tree, a base Universe for a given asset only commits +to the set of ''genesis outpoints'' for an asset. As this type of Universe only +commits to the set of constituent assets present at the Beginning, that all other +transfers depend on, we call this a ''Primordial'' Universe. + +Such a Universe can be used to bootstrap provenance and proof verification, as +assuming a party knows which Universe to query, they're able to verify the +provenance of a purported valid asset. In addition to bootstrapping provenance +verification, as Universe trees are themselves an MS-SMT, they can be used to +audit the total amount of a given asset in existence. + +A Primordial Universe, is an MS-SMT with the following structure: +* The MS-SMT root commits to the sum of the total set of issued assets for a given genesisAssetID +* key: an outpoint, serialized in a txid:vout structure as we find in Bitcoin. +* value: the initial Taro asset leaf ''created'' by that outpoint (which will always be a genesisOutpoint) along with a merkle-proof which proves the existence a transaction that ''spent'' that output. +* sum_value: the value of the leaf itself. + +As the Primordial Universe for a given asset can be known at the initial asset +creation time, based on the referenced universeKey those wishing +to track any new asset issuance related to a given genesisAssetID +can watch the output on chain. Each time the output is spent indicates a new +minting event. As a result, clients are able to watch a select set of outputs +on-chain, one for each genesisAssetID they care about, effectively +using the blockchain to be notified each time the total amount of issued assets +changes. + +As the MS-SMT is keyed by the genesisOutpoint, it can be used to +bootstrap any proof verification of a purported asset, as the initial linkage +is dependent on the provenance of the referenced genesisOutpoint +(verification starts at the Beginning and works backwards). + +====Asset Multiverses==== + +An Asset Multiverse is a Universe of Universes. Rather than just storing the +set of constituent assets (the set of genesisOutpoints), a +Multiverse commits to several root assetIDs, as well as additional +proofs of asset transfers (including splits+merges). A Multiverse is therefore +effectively a commitment to every asset transfer that may have ever happened. +Importantly, one cannot prove that a Multiverse has complete history, as a +Multiverse can only commit to what it directly observed, or was shown to it. + +A Multiverse has the following structure: +* The MS-SMT root commits to the sum, of the sum of the total number of asset units committed to. +** Notice that this will commit to the same sum as the set of Primordial Universes for each given asset (if no additional history is included). +* The top-level key for the MS-SMT is the asset_id, with the value being the MS-SMT root for the given asset root. +** The sum value here is the sum value carried up from the sub-tree. +** The key of this sub MS-SMT is similar to that of a Primordial Universe: a txid:vout outpoint. +*** The value stored is the asset leaf, along with a merkle-proof that proves the existence of the transaction that ''spent'' that output. + +With this structure, it's possible for the maintainers of the Multiverse to +also store subjectively complete history of the set of transfers. In addition, +this structure can be used to trace the set of transfers/lineage for a given +asset. Notice that we effectively commit to the set of all spent outputs +associated with an asset, with the very first spend being the +genesisOutpoint spend. As a result, a full proof of an asset's +provenance is simply a series of keys stored at the lowest level of an SMT, +with verifies following the transfer from outpoint to outpoint within a tree. + +Using the trait presented above, one can create a flat file that proves the +provenance of an asset simply by extracting select branches from a Multiverse +tree, and enumerating the set of keys one needs to assert for validation. + +====Pocket Universes: Off-Chain Taro Transfer Compression==== + +All leaves within a Multiverse are themselves a commitment to an event that +happened on chain: A Taro asset transfer. Proofs for unique assets have a nice +property that they scale linearly in the number of asset transfers (you can't +split/merge so the same unit is being transferred to a differing set of +owners). Normal assets give a greater degree of flexibility, but scale worse +as a single asset held might actually be the merging of several Taro asset +UTXOs, thereby increasing proofs size as a function of the number of +splits/merges in an asset's history. Pocket Universes are an off-chain transfer +compression system that allows a consortium to stamp asset transfers that take +place in an "imaginary" universe. + +To further reduce validation costs, verifiers can chose to only verify a single +input split all the way back to the genesis outpoint. This implements a naive +form of probabilistic validation, as the probability that each unverified split + +A Pocket Universe is similar to a commit chain. A single party, or a set of +parties, commits to a set of transfers within the main chain, which themselves +are anchored to an initial verifiable genesisOutpoint. A Pocket +Universe is therefore a scaling tool, as with a single new commitment on-chain, +an essentially unbounded amount of transfers can be timestamped within the +chain. Pocket Universes may be useful in cases where a party has issued +assets, that can only be used with the aide of the issuer, for example in-game +assets. + +=====Taro Virtual Transaction Graphs===== + +Assets can be moved into a pocket Universe which is another commitment in the +main Tapscript tree by "sending" the set of outpoints to a special NUMs asset +key which is derived from the genesisOutpoint of the given asset. +The traditional "hash and increment" approach to generating NUMs points can be +used, or any other variant. As performance isn't a concern, the naive approach +will likely be used in practice. + +Once the assets have been from the PoV of the base Universe, a new parallel +pocket Universe commitment can be created, which uses the ''new'' outpoint +created as a result of the above transfer transaction as the very first +spending input. From here, new transfers can be created, refreezing the +created outpoints of the virtual Taro VM validation transaction. The result is +an effective ''freezing'' of assets anchored in the main chain, which then +permits them to be batched and transferred in the maintained Pocket Universe. + +====Asset Universe APIs & Federated Sync==== + +==Test Vectors== + +TBD + +==Backwards Compatibility== + +==Reference Implementation== + +github.com/lightninglabs/taro diff --git a/bip-taro-vm.mediawiki b/bip-taro-vm.mediawiki new file mode 100644 index 0000000000..669e9ab6c9 --- /dev/null +++ b/bip-taro-vm.mediawiki @@ -0,0 +1,119 @@ +
+ BIP: ???
+  Layer: Applications
+  Title: Taro Asset Script v1
+  Author: Olaoluwa Osuntokun 
+  Comments-Summary: No comments yet.
+  Comments-URI: https://git
+  Status: Draft
+  Type: Standards Track
+  Created: 2021-12-10
+  License: BSD-2-Clause
+
+ +==Abstract== + +This document describes the virtual machine execution environment used to +validate Taro transfers that utilize an
asset_script_version
of +1. The execution environment described in this document is a slight twist on +the taproot validation rules defined in BIPs 341 and 342. Given a Taro asset +one or more Taro asset leaves to be spent (inputs) and asset leaves to be +created, a "virtual" taproot Bitcoin transaction is created. This transaction +is a 1-input-1-output transaction that commits to the inputs and output set +using a merkle sum tree. With this mapping complete, validation takes place as +normal. + +==Copyright== + +This document is licensed under the 2-clause BSD license. + +==Motivation== + +The Taro asset overlay permits the usage of a nearly arbitrary virtual machine +for validation of transfers within the system. In order to reduce the scope of +the initial version of the protocol, we describe a way to leverage the existing +Bitcoin Script virtual machine, allowing us to inherit a baseline set of +expressibility, while allowing implementers re-use existing tools and +libraries. + +==Design== + +The Taro asset_script_version 1 maps a Taro input and output set +to a "virtual" Bitcoin transaction. The input and output sets are committed to +within a single 1-input-1-output transaction using a normal merkle sum tree +(TODO(roasbeef): non-inclusion useful at all here??). Via the merkle-sum +invariant of the augmented merkle tree, a validator is able to enforce +non-inflation of assets by asserting that the committed input sum is ''equal'' +to the committed output sum. Once this invariant is verified, execution resumes +as normal using the BIP 341+342 validation rules, with additional pre-execution +checks that may fail validation early. + +===Specification=== + +A single 1-input-1-output transaction is used to compress the Taro state +transition state into a constant size transaction. Given a Taro asset +commitment (which lives in a taproot output), and its valid opening, the set +the previous asset ID are compressed into a single input, and the present +split_commitment is used to compress the output state. + +====Mapping Inputs==== + +Given a set of inputs, each identified by a prev_asset_id, the +input commitment (which is used as the previous output) is constructed as +follows: + +# Initialize a new empty MS-SMT tree as specified in [[../master/bip-taro-ms-smt.mediawiki|bip-taro-ms-smt]]. +# If the Taro output to be validated only specifies a split_commitment, then the prev_asset_witnesses of the referenced root output are used in place. +# For each Taro input ''c_i'', identified in the prev_asset_witnesses field: +## Serialize the referenced previous asset leaf (identified by prev_outpoint || asset_id || asset_script_hash) in TLV format. +## Insert this leaf into the MS-SMT tree, with a key of the prev_id_identifier, a value of the serialized leaf, and sum value of the asset amount contained in the leaf. +# Obtain the root hash input_root and sum value input_asset_sum resulting from the tree creation and root digest computation. +# Let the hash of the serialized 36-byte MS-SMT root be the sole previous output of the virtual execution transaction. +# A value of zero is used for the output index of the virtual execution transaction. + +With the above routine, we map the input set into a MS-SMT tree, which also +commits to the total amount being spent of any given asset. During +verification, as there may be multiple input witnesses, during validation, the +asset_witness for each input is used as the initial witness stack. + +(TODO(roasbeef): need to map the squence number as well?) + +====Mapping Outputs==== + +Given a Taro output, and any associated outputs contained within its +split_commitment_root, the output commitment is constructed as +follows: + +# If the Taro output to be validated includes a +# Let the output value be the sum of all the amt fields on the top level as well as the split commitment cohort set, in other words the last 4-bytes of the split_commitment_root. +# Let the output script be the first 32-bytes of the split_commitment_root value converted to a segwit v1 witness program (taproot). + +====Validating a State Transition==== + +Once the set of inputs and outputs have been mapped to our virtual Bitcoin +transaction (creating a v2 Bitcoin transaction with a single input and output), +validation proceeds as normal according to BIP 341+342 with the following +modifications: + +# If the input_asset_sum is not exactly equal to the output_asset_sum validation MUST fail. +# If the Taro output to be validated only specifies a split_commitment_root and no explicit inputs, then a valid inclusion proof for the output MUST be presented and valid. +# The previous public key script for each input is to be the asset_script_hash for each previous input, mapped to a v1 segwit witness program (taproot). +# The input value for each included input is to be the amt field of the previous Taro output being spent. +# All signatures included in the witness MUST be exactly 64-bytes in length, which triggers SIGHASH_DEFAULT evaluation. +# If the prev_asset_id is blank, then ALL witnesses MUST be blank as well and the prev_outpoint values as well. In this case, verification succeeds as this is only a creation/minting transaction. +# If the asset_id value is NOT the same for each Taro input and output, validation MUST fail. +## Alternatively, if each input and output has the same referenced asset_family_key, then only the asset_tags MUST match. + +(TODO(roasbeef): lift the sighash requirement here? useful for swappy stuff??) + +(TODO(roasbeef): split commitment root as another control block??) + +==Test Vectors== + +TBD + +==Backwards Compatibility== + +==Reference Implementation== + +github.com/lightninglabs/taro diff --git a/bip-taro.mediawiki b/bip-taro.mediawiki new file mode 100644 index 0000000000..e976a85909 --- /dev/null +++ b/bip-taro.mediawiki @@ -0,0 +1,864 @@ +
+ BIP: ???
+  Layer: Applications
+  Title: Taro: A Taproot Asset Representation Overlay
+  Author: Olaoluwa Osuntokun 
+  Comments-Summary: No comments yet.
+  Comments-URI: https://git
+  Status: Draft
+  Type: Standards Track
+  Created: 2021-12-10
+  License: BSD-2-Clause
+
+ +==Abstract== + +This document describes Taro, a Taproot-native asset overlay built on top of +the base Bitcoin blockchain. Taro enables the representation of arbitrary +(normal and collectibles) assets on top of Bitcoin without bloating the base +chain. The protocol is designed such that transactions interacting with Taro +assets are indistinguishable from regular transactions from the PoV of the +Bitcoin blockchain. Taro extends the base Taproot script tree with a nested +''asset script'' tree (a merkle-sum sparse merkle tree, or MS-SMT) that commits +to valid witnesses as structured metadata that allow for proofs of the movement +of assets across the transaction graph. The provenance of transfers of a Taro +asset can be verified using a hermetic proof transmitted as flat file, or using +the aide of an externally maintained Universe, which is a MS-SMT that indexes +on-chain asset issuance+transfers, which natively supports +proof-of-reserves/supply system. + +Taro supports off-chain single and multi-hop transfers over Lightning channels +(based on the BOLT protocol suite), with the latter manifested using Taro's +unique embedded asset script system. Light client verification of on-chain +Taro transfers is facilitated by a series of on and off-chain merkle proofs, +which can be compressed by delegating a trust relationship to an active +Universe instance. A variant on off-chain multi-party channels are proposed to +support off-chain transfer of normal assets. In addition, a special type of +Universe, dubbed a Pocket Universe, which is based on an exit-only +commit-chain design can be used to aggregate transfers on-chain in a +trust-minimized manner. + +==Copyright== + +This document is licensed under the 2-clause BSD license. + +==Motivation== + +Bitcoin, the first decentralized blockchain, realized early in its life +several systems that attempted to represent arbitrary assets within the +constraints of the Bitcoin system itself. Amongst the earliest of these systems +was Counterparty (now known as OMNI), a meta-token protocol on top of Bitcoin +that used OP_RETURN to commit the raw representations and transfers +of assets within the system. Several years after the creation and deployment of +Omni and other related systems very little activity takes place within these +systems, and instead has been dispersed to the dozens of new blockchains with +no direct lineage back to Bitcoin, the chain that started it all. + +The goal of Taro is to carry the torch and realize a system for representing and +manipulating arbitrary assets on Bitcoin that reflects modern blockchain design +principles, and offers a Taproot-inspired data structure and protocol flow that +Bitcoin developers will find familiar. Importantly, such a system must be +designed from the ground up to work in an off-chain setting to ensure the +base chain isn't bloated by an unscalable overlay protocol. With a clear +specification, we aim to encourage widespread ecosystems adoption of the +system, galvanizing existing underallocated developer excitement and +resources. + +By allowing Bitcoin developers to easily express and manipulate arbitrary +assets in a private, scalable manner, we aim to increase the utility of the +Bitcoin system, thereby generating additional demand for blockspace, which is a +necessary to ensure the system is able to flourish in an environment where the +block subsidy asymptotes to zero. + +==Design== + +In this section we provide a high-level overview of the fundamental data +structures and concepts that the Taro protocol is comprised of. Taro starts +with a simple idea of committing to arbitrary non-Script data within the +taproot script tree, and then builds upon this idea to create an overlay +protocol for arbitrary assets anchored in the Bitcoin chain. + +===Merkle-Sum Sparse Merkle Trees=== + +A merkle tree is an authenticated data structure over a ''list'' of elements. +It lets us do things like take the contents of a directory and create a root +hash that allows us to prove that a given file belongs to the directory in +question. Each element is hashed pair-wise, starting at the bottom, until only +a single hash exists. We refer to this hash as the root hash. + +A merkle-sum tree is a variant on a merkle-sum tree where our hash operation +also commits to the ''sum'' of a given attribute. We can extend the example +above by committing to the size of each file. The root hash then commits to the +left sub-tree, the right-sub tree, and also the sum of the sizes of the left +and right sub-tree. This new variant is useful as we can prove both the existence +of a file, as well as its size. We can also do things like generate a proof of +the size of a sub-directory. + +One thing we can't easily do with a merkle-sum tree is prove non-existence, as +if items are not canonically sorted, then proving non-existence requires +revealing all the leaves in the tree. If we choose to order the set of leaves in +the merkle tree, then we gain the ability to more easily prove non-existence, +but we introduce the overhead of needing to re-balance the tree each time new +items are inserted. Proving non-existence is useful in the Taro setting, as we +may want the seller of an asset to prove that they're no longer committing to +the asset in question. + +A Sparse Merkle Tree is merkle-tree "simulation" over a series of key-value +pairs, that supports efficient proofs of non-inclusion. An SMT is actually +composed of the entire keyspace (all 256-bit values), namely it is a tree with +2^256 leaves (thereby having 256 levels). The representation of the structure +is made tractable by observing that we know the hash of an empty element, the +hash of a branch with two empty sub-trees, and so on. In addition, a series of +caching strategies can be used to make the representation even more efficient. +Proofs can also be further compressed with the addition of a bitmap that +signals if an intermediate branch in the proof commits to an empty sub-tree. + +A Merkle-Sum Sparse Merkle Tree, is an SMT, that also inherits the sum-combiner +trait. We use this data structure to permit easy verification of the splits of +a divisible asset, support proofs of non-inclusion, and also to serve a +merkalized look up table to facilitate the verification of Taro asset proofs. + +The MS-SMT we refer to in this document is fully specified in BIP +bip-taro-smt.mediawiki. + +===Taro Asset Trees=== + +Taro is a Taproot-native asset overlay that can be used to create and transfer +collectibles or normal assets using the base Bitcoin blockchain. Assets +are presented as commitments to structured data ''within'' the existing Taproot +script tree. This structured data is never written in plain to the base chain +itself, but instead maintained on a higher layer by the Taro overlay protocol. +Assets themselves are represented by a series of MS-SMTs (one each each asset +ID/type) within the main asset tree. Each asset (in addition to a number of +other attributes) commits to an ''asset script hash'', that commits to an +''asset script'' that restricts exactly ''how'' an asset is permitted to be +transferred from the PoV of the Taro overlay protocol. The initial version of +the system uses a sub-set of Bitcoin script to allow assets to express arbitrary +conditions on the valid transfer of an asset. + +Taro asset leaves thereby mirror the programmatically of UTXOs on the base +Bitcoin blockchain. Verifiers of a given Taro asset will reject any invalid +state transitions. As a result, we inherit a level of programmability on par +with (and also beyond, via new asset script versions) the base scripting +system, which is a key requirement for facilitating multi-hop off-chain +transfers over Lightning (via HTLCs ''embedded'' in the asset script). + +===Asset Provenance=== + +Provenance of an asset is proved using one of two mechanisms. The first of +which, hence referred to as a "full proof" is a structured file that contains +serialized proofs from an initial ''genesis output'', with a series of valid +asset witnesses verifiably transferring ownership of an asset across taproot +outputs. Full proofs are typically repented as a flat file for consumption, +verification, display by the Taro protocol. + +The second method of proving/verifying provenance is an MS-SMT of a slightly +different structure, which is also committed to within the Taproot script tree. +We refer to this meta-structure as a Universe, as it commits to Taro +commitments within the chain along with additional proof data. A Universe is a +key-value store that can be used to bootstrap provenance verification +(maintained by those that created the set of assets of interest). Beyond +initial genesis output verification, a Universe can also serve as a scalability +layer by recording all valid Taro asset transfers within the MS-SMT, that maps +an ''asset identifier'' to the last known valid transfer of the asset. + +===Merkle-Sum based Proof of Reserves=== + +The usage of the MS-SMT data structure enables the system to support efficient +proofs of supply/non-inflation, allowing participants to easily verify the +total amount of assets committed to within a given taproot output, as well as +the total set of issued assets by a given Universe. The merkle-sum augment of +the SMT allows assets to be divisible, while allowing verifiers to assert that +similar to regular Bitcoin transactions, no new assets are created during +transfers outside of specially crafted asset genesis transactions. + +===Splits, Merges, collectibles and Normal Divisible Assets=== + +Taro is able to represent collectibles, and normal divisible assets. Normal +assets commit to a total value of held assets, which can be split up ''within'' a +tree similar to normal Bitcoin UTXOs, as well as across top-level Taproot +outputs. We refer to these as internal and external splits. Normal assets can +also be merged, in a similar process as UTXO consolidation on the base layer. +During transfer of assets, the holder of an asset proves that they hold a valid +split (via a merkle-sum proof), with each created asset committing to a new +merkle-sum output split set to enforce non-inflation during transfers. + +Collectibles on the other hand cannot be split, nor merged. They're typically +created in a batch, and typically represent a claim/credentials on a +chain-level or real-world asset/functionality (assuming an existing trust +relationship). An example of a collection would be a series of limited edition +digital collectables such as baseball cards, or trading card games. + +Normal assets can be transferred off-chain in a multi-hop fashion using the +Lightning Network, while collectibles assets must either be transferred +off-chain or lifted into a multi-party channel to permit transfers amongst a +known set of participants. + +===Specification=== + +====Asset Tree Representation==== + +=====Asset Root Commitment===== + +The Taro asset tree is a core component of the system. An asset tree is an +MS-SMT commitment to a series of asset identifiers, grouped by their genesis +asset ID. A given Taro asset tree is composed of two nested MS-SMT instances: + +# The first level maps an asset_id or asset_key_family to a sub-tree root hash of a given asset. +# The second level maps an asset_script_key or asset_id || asset_script_key to a serialized Taro asset leaf. + +The root hash of an asset leaf, observing [[../master/bip-0341.mediawiki|BIP-341]] is +represented as a Tapscript tree with a single leaf: +* tagged_hash("TapLeaf", leaf_version || taro_version || asset_tree_root) + +A leaf_version of ??? is selected. From the PoV of the Bitcoin +system, we've simply committed to a tapscript leaf with an unparseable Script. +In the future, if the Bitcoin system is soft forked to gain awayness of the +Taro-specific commitments, then the same or different leaf version can be used +to gate verification of the new behavior. + +An asset_id is the 32-byte hash of: +* sha256(genesis_outpoint || asset_tag || asset_meta) + +where: +* genesis_outpoint is the first previous input outpoint used in the asset genesis transaction serialized in Bitcoin wire format. +* asset_tag is a random 32-byte value that represents a given asset, and can be used to link a series of discrete assets into a single asset family. In practice, this will typically be the hash of a human readable asset name. +* asset_meta is an opaque 32-byte value that can be used to commit to various metadata including external links, documents, stats, attributes, images, etc. Importantly, this field is considered to be ''immutable''. + +Given the above structure, the asset_id is guaranteed to be +globally unique from the PoV of the chain, as thanks to +[[../master/bip-0034.mediawiki|BIP 34]] +as outpoints can never repeat once serialized within the block chain. + +In addition, we enforce a rule that a given output MUST only have a ''single'' +root Taro asset commitment (any other duplicates cannot be spent due to the way +asset witnesses bind to the input outpoint). + +The asset_tag field cannot be enforced to be ''globally'' unique. +Instead to ensure locally unique asset_id instances within initial +asset creation, each asset_tag and asset_meta value +MUST only appear once during asset creation. + +The top-level MS-SMT commits to set of all held defined assets. The root hash +of this MS-SMT tree is referred to as the asset_tree_root. The +MS-SMT is structured as follows: +* key: asset_id or asset_key_family +* value: taro_version || asset_id_tree_root || asset_sum +* sum_value: asset_sum + +Similar to the asset_id, the asset_key_family is +derived using the first input outpoint as a sort of chain randomness to ensure +uniqueness within the system. Given a genesis_outpoint and a +public key asset_key_internal, then asset_key_family +is derived as follows: +* asset_key_family = asset_key_internal + sha256(asset_key_internal || genesis_outpoint) + +The top level tree can be easily used to prove the existence of a set of given +assets, the total amount of asset units held, as well as the sum of a given +asset held. + +The asset_id_tree_root value is itself another nested MS-SMT with +the following structure: +* key: asset_script_key or asset_id || asset_script_key +* value: asset_leaf || leaf_sum +* sum_value: leaf_sum + +Within the MS-SMT held by the tree rooted at a given +asset_id_tree_root, a new Taro MS-SMT is created for each +asset_id or asset_key_familyan output holds. The +leaves of the MS-SMT are keyed by the asset_script_key or +asset_id || asset_script_key unlocking script. The root hash of +the asset tree is calculated as either: +* asset_tree_root = sha256(asset_id || left_hash || right_hash || sum_value) +or +* asset_tree_root = sha256(asset_key_family || left_hash || right_hash || sum_value) + +where: +* asset_key_family is the 32-byte hash of a public key derived from the genesis outpoint which can be used to collocate assets intended to be fungible with one another (issuing multiple tranches of the same asset) or collectibles. +* asset_id is the 32-byte asset ID specified above +* left_hash is the hash of the left sub-tree. +* right_hash is the hash of the right sub-tree. +* amt_sum is the sum of the amt values of each of the asset leaves (essentially asset UTXOs). + +Committing to the total sum of the asset values in a given asset sub-tree for a +given asset allows asset holders to easily prove how much of a given asset they +own, which when combined with the asset creation and "Universe" concept, allow +for a built-in mechanism of proof of reserves. + +The usage of the asset_key_family (as we'll see below) allows +issuers to collocate assets intended to be fungible with each other within the +same asset sub tree. The derivation of the asset_key_family +ensures that similar to the asset_id this value is globally unique +within the chain. Assets that don't specify an asset_key_family +use a slightly different schema to key into the main MS-SMT, and can only be +issued in a single batch. + +When an asset_key_family is specified during asset +issuance/minting, similar to the top level-SMT, the internal MS-SMT modifies +its internal key to also factor in the derived asset_id for each +asset. As a result, the lowest level key factors in the asset_id +as well as the asset_script_key. This again ensures that all +assets and script keys are placed within a unique location within the MS-SMt. + +Note that due to the structure of the tree, for a given Taro asset sub-tree +within a valid taproot output, all asset_script_key values MUST +be unique. + +=====Asset Leaf Format===== + +The leaves of an asset tree for a given asset_id commit to a blob +of metadata expressed in the +[https://github.com/lightning/bolts/blob/master/01-messaging.md#type-length-value-format +TLV] format used by the Lightning Network's wire protocol. The TLV structure is +canonical and deterministic, making it suitable to be used for cryptographic +commitments and signed digests. In addition, the TLV format supports both +backwards and forwards compatibility, making the underlying structure of the +asset commitments highly extensible. + +An asset leaf is a serialized TLV blob, with the following key-value mappings: +* type: 0 (taro_version) +** value: +*** [u8:version] +* type: 1 (asset_id) +** value: +*** [32*byte:id_bytes] +* type: 2 (asset_type) +** value: +*** [u8:type] +* type: 3 (amt) +** value: +*** [BigSize:amt] +* type: 3 (lock_time) +** value: +*** [BigSize:block_height] +* type: 4 (prev_asset_witnesses) +** value: +*** [u16:num_inputs][asset_witnesses...], where: +**** [...*byte:asset_witnesses]: +***** [asset_witness]: +****** type: 0 (prev_asset_id) +******* value: [prev_outpoint || asset_id || asset_script_key] +****** type: 1 (asset_witness) +******* value: [...*byte:asset_witness] +****** type: 2 (split_commitment_proof) +******* value: [...*byte:split_proof] +* type: 1 (split_commitment) +** value: [32*byte:split_commitment_root] +* type: 5 (asset_script_version) +** value: +*** [u16:script_version] +* type: 6 (asset_script_key) +** value: +*** [33*byte:pub_key] +* type: 7 (asset_family_key) +** value: +*** [96*byte:pub_key || schnorr_sig] + +where: + +* taro_version: is a single byte that denotes the version of Taro being used, which allows a client to determine which of the below TLV values to expect. +* asset_id: is the asset_id defined above +* asset_type: is a single byte representing the type of the asset, two starting asset types are defined: +** 0: a normal asset +** 1: a collectible asset +* amt: is the amount of the asset held in this leaf position +* lock_time: is a field that restricts ''when'' an asset can be moved based on the included block height. +* prev_asset_witnesses: is a ''nested'' TLV that contains the asset witnesses needed to verify the merging into the target asset leaf +** prev_asset_id: references the previous asset input by the input position within the transaction, the asset ID of the previous asset tree, and the asset script hash. +*** This value is included in any signatures generated within the asset witness itself, serving a purpose similar to the previous outpoint in vanilla Bitcoin. +*** If this field is all zeroes, then that indicates a ''new'' asset is being created. +*** For ''internal'' split verification, this field can be used to save space by including only a single signature/witness that covers all other new splits. +*** If this type isn't present, then a split_commitment MUST be present. +** split_commitment_proof: is used to permit the spending of an asset UTXO created as a result of an asset split. When an asset is split, the non-change UTXO commits to the location of all other splits within an MS-SMT tree. When spending a change UTXO resulting from an asset_split, a normal asset_witness isn't required, instead the owner of the change asset UTXO must prove that it holds a valid split which was authorized by the main transfer transaction. +*** Outputs with the same split_commitment are said to share a single asset_witness as such outputs are the result of a new asset split. Therefore we only need a single witness and the resulting merkle-sum asset tree to verify a transfer. +** asset_witness: is a serialized witness in an identical format as Bitcoin's Segwit witness field. This field can only be blank if prev_asset_id is blank. +* split_commitment: is used to commit to, and permit verification of the new output split distribution for normal assets. The split_commitment_root is an MS-SMT with a key of sha256(output_index || asset_id || asset_script_key), and the value being the new Taro asset leaf. This is an optional field and is only specified wehn an asset value is split during transfer. +*** Outputs with the same split_commitment are said to share a single asset_witness as such outputs are the result of a new asset split. Therefore we only need a single witness and the resulting merkle-sum asset tree to verify a transfer. +* asset_script_version: is a 2 byte asset script version that governs how the following TLV value is to be validated. +* asset_script_key: is the external public key derived in a BIP 341 manner which may commit to an asset script that encumbers this asset leaf. +* asset_family_key: is a 32-byte public key as defined by [[../master/bip-0340.mediawiki|BIP-340]] followed by a 64-byte BIP 340 signature over the asset_id. This key can be used to associate distinct assets as identified by their asset_id, effectively allowing further issuance of a base asset. Assets that references the same asset_family_key are to be considered the same asset. This is an optional field, and assets that don't contain this field are effectively considered to be a one-time only issuance event, meaning no further assets related to the derived asset_id can be created. +* cannonical_universe: is a key (with no corresponding value) which if specified, designates the existence of an on-chain canonical Universe. Specified further in bip-taro-universe.mediawiki, a canonical Universe is maintained by the issuer of an asset and can be used to allow 3rd parties to easily audit the total asset supply and also track future issuance. In short, if this is specified, ''second'' output of the next spend of the outpoint created during asset issuance MUST commit to the root hash of a base Universe and all subsequent spends must only happen after later asset issuance events, and MUST commit to a new valid Universe root. This feature allows light clients to watch a set of outputs on chain to be notified of future asset issuance. + +(TODO(roasbeef): merkle sum commitment over input set as well?) + * enables probabilistic validation? + +An asset leaf serves to store structured data related to an asset, as well as +the series of previous input asset witnesses needed to verify the proper +transfer of an asset leaf. The input structure here resembles the normal +Bitcoin input and output scheme, with the addition of the +split_commitment which is needed to allow verifiers to reject +invalid splits, thereby preventing asset inflation. + +The asset_script_version is a key design element of the Taro asset +leaf as it allows for future upgrades in the scripting system used to +lock/unlock asset leaves. Initially, a version 0 asset leaf is +used which denotes that the asset_script_key be a ''valid'' +output_key as specified in BIP 341 and BIP 342. As a result, the +version 0 Taro VM is an instance of Taproot ''within'' an outer instance. +Verification logic for asset_script_version is fully defined in +BIP ???. In the future new asset script versions can be introduced to further +increase the expressibility of the embedded asset script. + +TLV types below 2^16-1 are reserved for use by additional `taro_version` +interations. Types with a numerical value above this reserved range can be used +TLV types below 2^16-1 are reserved for use by additional +taro_version iterations. Types with a numerical value above this +reserved range can be used to store arbitrary attributes to assets. An eaxmple +of such an attribute would be storing the _mutable_ stats of an in-game asset. +Any immutable fields for normal or collectible assets should be instead +committed to within the `asset_meta` be storing the ''mutable'' stats of an +in-game asset. Any immutable fields for normal or unique assets should be +instead committed to within the asset_meta field. + +The usage of an MS-SMT for the Taro asset tree itself permits parties to easily +verify that no new assets are created when inputs are referenced, and also that +each new split/merge results in a valid split set (via the +split_commitment_root which is included in the computed witness +sighash). + +===Asset Creation=== + +The creation of an asset resembles any other Bitcoin transaction, albeit it +will typically be a 1-input-1-output transaction. An asset creation transaction +spends an arbitrary set of inputs, and produces one or many outputs which may +commit to a set of newly created assets. As a simplifying mechanism, we require +that a given Taro transaction can only create new assets, or transfer existing, +but not both. + +(TODO(roasbeef): does not allowing transfer+creation actually make anything simpler?) + +The following function creates a new valid taproot public key script whose +internal tapscript tree commits to the representation of the new asset: + +create_new_asset_output(total_units: uint64, asset_tag: [32]byte, + genesis_point: [36]byte, asset_meta: [32]byte, asset_type: uint8, + asset_script_key: [32]byte, taro_attrs: TLV) -> PkScript: + + asset_id = sha256(genesis_point || asset_tag || asset_meta) + + tlv_leaf = new_taro_tlv_leaf( + taro_version=0, asset_id, asset_type, total_units, asset_script_version=0, + asset_script_key, attrs=taro_attrs, + ) + + inner_smt_leaf = new_ms_smt_leaf( + key=asset_script_key, value=tlv_encode(tlv_leaf), sum_val=total_units, + ) + inner_smt_root = ms_smt_root_hash(new_ms_smt(inner_smt_leaf)) + + outer_smt_leaf = new_ms_smt_leaf( + key=asset_id, value=inner_smt_root, sum_val=total_units, + ) + outer_smt_root = mew_smt_root_hash(new_ms_smt(outer_smt_leaf)) + + internal_key = new_internal_key() + + return taproot_output_script(key=internal_key, leaves=[taproot_smt_leaf]) + + +Note that we require the genesis_point to be known ahead of time +in order to enforce the uniqueness constraint of the asset_id as +is defined. + +The taro_attrs field can be used to commit to a set of arbitrary, +and potentially mutable fields associated with an asset. + +In the above example, the resulting taproot tree we create only commits to a +single leaf, which is the Taro asset root. In practice, one will likely have +other normal script-path scripts within the tapscript tree. + +The above example creates only a single asset. It's possible to also create +several assets within a single output, thereby batching asset creation. + +===Asset Transfers=== + +Across normal and asset collectibles, two types of transfers are defined: swaps and +normal sends. + +Asset swap transfers take place over multiple rounds of interaction, using +PSBT extensions (as defined in bip-taro-psbt.mediawiki) to collaboratively +create a Multi-Input-Multi-Output (MIMO) transaction that spends one or more +distinct +assets as inputs, and creates one or more new asset owners as outputs. + +Normal sends on the other hand, involve only a single round of interaction, as +only one party is spending an asset to the wallet of the other party. Compared +to interactive transfers, only a single Taro asset witness needs to be +specified. + +Constructing and verifying a transfer of an item part of an asset collection +entails the owner of the asset transferring ownership from their input, into a +new output under control by the receiver. The set of merkle-sum commitments as +well as, asset witness validity are used to verify and authenticate asset +transfers. + +Swaps of regular assets involve additional verification steps, as both sides +need to ensure that no new assets are inadvertently being created (resulting in +invalid transaction). In order to accomplish this, the transaction must be +formed in such a way that 3rd party verifiers are able to verify both splits +(splitting a single asset UTXO multiple instances) and merges (combining +outputs of the same asset type into one). We use the +split_commitment field to accomplish this, effectively forcing +what can be considered a change output (in the Taro domain), to commit to a +merkle-sum tree of the other created splits. + +====Asset Swap Transactions==== + +In this section, we specify interactive asset transfers wherein two (or more +parties) collaborate to create a MIMO transaction that transfers one or more +assets amongst the set of participating parties. We describe the core +interaction, verification of exchanged proofs, in this setting. We leave further +details concerning how such a protocol would be mapped to a PSBT-based signing +ceremony to BIP bip-taro-psbt-mediawiki. + +=====Collectible Asset Transfers===== + +Transfers of asset collectibles are simpler than normal asset transfers, in that +they don't require the verification of splitting or merging multiple asset +UTXOS. Transfer of assets collectibles happen on two layers: + +* First, asset witness and script hash information are exchanged allowing the receiving party (of each input asset) to verify the provenance of the inputs assets, with the sending party delegating ownership to the script hash of the receiving party. +** We call this the ''internal'' transfer process. +* Next, a set of normal Bitcoin signatures/witnesses are exchanged on the lower layer, which spend all input assets (effectively destroying them) and re-create the assets as new commitments in the resulting MIMO transaction. +** We call this the ''external'' transfer process. + +Note that not all inputs need to be inputs that hold Taro assets. This +naturally lends to new sub-protocols executing batched MIMO atomic swaps in a +single transaction across a number of parties. + +As verification is a cornerstone of the Taro protocol, during the interactive +transfer process each party carries out the following set of verification assertions: +* Verify the provenance of any input assets. +* Verify that a party can generate valid input asset witnesses for each asset. +* Verify that the new set of Taro asset script outputs created either no longer commits to a transferred asset, or now properly commits to a newly received asset. + +The first verification step verifies that an asset has valid lineage. The +second step verifies that an asset owner can actually spend the asset if they +choose. The final step verifies that no new assets are created, and instead +only existing assets with verified provenance are transferred. + +Transfers are specified as follows: +# For each input (which may hold one or more asset collectibles to be transferred), the owner transmits: +## An opening of the MS-SMT commitment stored in the previous output of each input, transmitted as the identifier of a Universe (to extract a compressed proof) or full proof file. +### Using this, the receiver(s) of the asset collectibles verify that: +#### Each proof is a valid opening of the asset tree commitment, with a valid leaf path down to the asset to be transferred. +#### The merkle sum commitment for each asset to be transferred is valid given the amt (should be 1 for all asset collectibles) of the desired asset. +#### Given the asset_id, a valid opening revealing the genesisOutpoint, assetTag and assetMeta. +#### The supplied genesisOutpoint is present in the Universe maintained for the set of unique assets collectibles. A merkle proof of the Universe's current MS-SMT, or a full proof may be used. +# With initial verification complete, the internal transfer process begins: +## For each asset collection, ''i'' to be exchanged: +### The receiver creates a new asset script key commitment, asset_script_key_i (which will be used to delegate ownership of the input asset), and sends this to the owner. +### The owner takes the raw serialized asset script and closes a new asset witness over the asset_script_key_i script (as part of the serialized asset leaf). This witness is then transmitted to the receiver. +### The receiver can now construct a new valid leaf (with a valid witness delegating ownership), either creating a new rooted asset tree (if they didn't own any instances of the asset collection lineage) or add it to an existing tree. Call this asset_leaf_i. This new leaf MUST properly reference a valid prev_asset_id to be a valid witness. +# With internal transfer complete, the final external transfer is executed: +## The set of inputs (which may be heterogeneous w.r.t asset type) are added to a new version 2 Bitcoin transaction. +## The set of new ownership output are exchanged and added to the MIMO transfer transaction. Minimally each party MUST have a new ownership output present in the transaction. +## For each Taro tagged output ''i'', ''transferring'' an asset: +### The sender transmits the receiver a non-inclusion proof rooted at the new output, proving that the asset in question in no longer committed to within their asset tree. +### The sender constructs a new valid taproot output script, including the update asset tree root, and sends this to the receiver for verification. +## For each ownership output ''receiving'' an asset: +### The receiver constructs a new valid taproot output script, which provably commits to the new asset_leaf_i fragment. +# The external transfer is ratified by exchange of a valid set of ''Bitcoin'' input witnesses for each input. + +After the final step has been executed, one or both sides can broadcast the +transaction ratifying it within the main chain. In addition, each party can +also append to their respective asset proof files (which were transmitted in +full during the verification process), or delete them if they've transferred an +asset. + +In the case where an active Universe is being used for transfers, then only the +internal transfer process needs to take place, with the new proofs being +uploaded to a Universe to be stamped within the chain. + +The transfer process above can alternatively be expressed via the following +fragment of psuedo-code, that specifies a transfer (a full swap) of a series of +assets owned by two parties, Alice and Bob: + +verify_asset_input_proofs(asset_proofs: map[AssetPrevID]AssetLeafProof) -> bool + + for prev_asset_id, asset_leaf in asset_proofs + prev_outpoint, asset_id, asset_script_key = prev_asset_id + + match asset_leaf.proof_type: + case FullProof: + + for i in range(len(asset_leaf.inclusion_proofs)): + asset_leaf_tlv = asset_leaf.raw_leaf[i] + leaf_merkle_proof = asset_leaf.inclusion_proof2[i] + + if i != 0 and asset_leaf_tlv.prev_input.prev_outpoint != + asset_leaf[i-1].leaf_merkle_proof.outpoint: + fail + + if not verify_inclusion_proof(leaf_merkle_proof, asset_leaf): + fail + + if not verify_witness(asset_leaf.asset_witness, asset_leaf): + fail + + case CompactProof: + if not verify_universe_proof(asset_leaf.inclusion_proofs, asset_id): + fail + +transfer_assets(sender_assets: map[AssetID]AssetLeaf, + receiver_scripts: [[32]byte]) -> map[AssetID]AssetLeaf + + new_assets = {} + for prev_asset_id, asset_leaf in sender_assets: + asset_script_key = asset_script_keyes[i] + + new_leaf = clone_unique_leaf(asset_leaf) + new_leaf.asset_script_key = asset_script_key + new_leaf.prev_asset_id = prev_asset_id + new_leaf.asset_witness = gen_witness(tlv_encode(new_leaf)) + + new_assets[prev_asset_id.asset_id] = new_leaf + + return new_assets + +taro_interactive_transfer(bob_inputs_assets: map[AssetID]AssetLeafProof, + alice_input_assets: map[AssetID]AssetLeafProof, alice_internal_key: PublicKey, + bob_internal_key: PublicKey, alice_asset_script_keyes: [[32]byte], + bob_asset_script_keyes: [[32]byte]) -> Tx: + + if !verify_asset_input_proofs(list(chain(alice_new_assets, bob_input_assets))): + fail + + bob_new_assets = transfer_assets(alice_input_assets, bob_asset_script_keyes) + alice_new_assets = transfer_assets(bob_input_assets, alice_asset_script_keyes) + + alice_new_taro_root = alice_compute_root(remove=alice_input_assets) + if not verify_non_inclusion(alice_new_taro_root, bob_new_assets): + fail + + bob_new_taro_root = bob_compute_root(remove=bob_input_assets) + if not verify_non_inclusion(bob_new_taro_root, alice_new_assets): + fail + + transfer_tx = new_tx() + for prev_asset_id, _ in list(chain(alice_input_assets, bob_inputs_assets)): + transfer_tx.add_txin(prev_asset_id.prev_outpoint) + + transfer_tx.add_output(taproot_output_script(key=alice_internal_key, leaves=[alice_new_taro_root])) + transfer_tx.add_output(taproot_output_script(key=bob_internal_key, leaves=[bob_new_taro_root])) + + return transfer_tx + + + +The method above produces a fully signed Bitcoin transaction, that when +broadcast, will ''atomically'' trade Alice's set of collectible assets for +Bob's set of collectible assets, with Alice paying an additional 1 BTC to Bob +to satisfy the conditions of their transfer. + +=====Normal Asset Transfers===== + +Transfers of normal assets are nearly identical to transfers of asset +collectibles. The main difference is that along the way, both sides also need +to verify the proper splitting and merging of any input assets. + +As an example, let's say Alice owns 10 units of asset Foo and wishes to +transfer 9 units of them them to Bob. When generating the asset witness to +transfer the asset, Alice's witness commits to a ''new'' merkle-sum split tree, +proving that her new leaf is a member of that tree, and the tree still commits +to only 10 units of the asset. Alice may not know the final structure, but +adds an additional constraint that this commitment exists. A valid transfer of +normal assets is only valid if this condition is upheld. + +The example above demonstrates how ''external splits'' (splits across distinct +taproot outputs) are verified. In addition to this, we also need to verify that +no new assets have been created within an asset commitment. We use the +merkle-sum trait of the SMT to verify this. + +With that said, transfers of normal assets are identical to transfers of +collectibles assets with the following additions: +# Internal transfer verification: +## For each asset, ''i'', and amount ''n'', to be exchanged: +### The sender transmits a valid MS-SMT merkle proof proving the existence of the asset, and also the authenticity of the amount to be transferred. +### The receiver creates a new asset_script_key_i and asset_leaf_i as normal, with the added constraint that the amt field of the new asset MUST match the amount to be transferred. +### When generating the valid asset_witness for a given asset input, the sender MUST also construct a new MS-SMT tree with a key of sha256(output_index || asset_id || asset_script_key), and a value of the asset leaf being created (serialized without this field). This will be the split_commitment_root of the change output. +# External transfer: +## For each ownership output ''i'', transferring ''n'' units of asset ''y'': +### The sender no longer needs to transmit a full non-inclusion proof, but instead opens the Taro root commitment of their change output, allowing the receiver to verify that amount t-n is committed to, with t being the prior asset root sum. +### For each new asset leaf associated with the input split, verify that the serialized leaf is a member of the split_commitment_root within the change output. + +Note that for a given input asset_id in a transaction, any +resulting internal splits will share the same input witness (referenced +indirectly) as well as split_commitment_root. + +The following pseudo-code routines define new methods to create and verify the +split_commitment_root: + +create_split_commit_root(asset_splits: map[SplitLocator]SplitLeaf) -> [32]byte: + + split_tree = new_mt_smt() + for split_locator, split_leaf in range asset_splits: + output_index, asset_id, asset_script_key, split_amt = split_locator + split_key = sha256(output_index || asset_id || asset_script_key) + + split_tree.insert(key=split_tree, value=split_leaf, sum_val=split_amt) + + return split_tree.root_hash() + +verify_split_commit_root(split_loc: SplitLocator, split: SplitLeaf, + split_root: [32]byte, audit_path: [[32]byte]) -> bool: + + output_index, asset_id, asset_script_key, split_amt = split_locator + split_key = sha256(output_index || asset_id || asset_script_key) + + split_leaf = new_mt_smt_leaf(key=split_leaf, val=split, sum_val=split_amt) + + hash_val = split_leaf + for branch in audit_path: + branch_sum = split_leaf.sum_val + branch.sum_val + + hash_val = sha256(hash_val || branch.hash || branch+sum) + + return hash_val == split_root + + + +====Normal Asset Transfers==== + +Normal active asset transfers are single sided transfers where only a single +party is transmitting a collection or normal asset to another party. As a +result, a MIMO transaction isn't necessary as given the asset in question, +amount to be transferred, and the desired asset_script_key along +with a public key, a new asset root and its corresponding taproot output can be +constructed. + +Non-interactive transfers introduce the concept of a on-chain Taro ''address''. +See [[../master/bip-taro-addr.mediawiki|BIP-???]] for further details on the +address format. + +Given a valid Taro address, if Alice wishes to transfer to Bob ''N'' units of +asset ''Y'', using Taro address ''C'': +* Alice uses the Taro address to derive construct a valid leaf, and a new external taproot output as dictated by [[../master/bip-0341.mediawiki|BIP-341]]. +* Alice constructs a new sub-commitment in her asset tree that now instead commits to T-N units of the asset (in the case of a normal asset). +* Alice signs and broadcasts a new transaction including the two outputs, with a necessary deposit amount of ''K'' satoshis. + +Alices internal transfer doesn't include a prev_asset_id nor asset +witness for Bob's new asset leaf. Instead, Bob will use the referenced +split_commitment_root to create a +split_commitment_proof that asserts the existence of the new split +of amount N. This transfer is valid as the "sig hash" of Alice's +asset witness also covers the structure of Bob's new asset commitment. + +For non-interactive transfers Bob needs to obtain this proof from somewhere, +but is able to reconstruct it entirely given the latest proof for Alice's +non-change output. If Alice wishes to transmit this data on-chain to Bob, then +she can place the data in Taproot's currently unused annex field. + +That's it. Alice can construct the expected root Taro commitment that Bob is +looking for in the chain. This makes the scheme light client friendly, as +neutrino nodes can simply look for the resulting taproot output within the +filters. The "deposit" amount is necessary as Bitcoin doesn't permit zero +valued outputs. This amount only needs to be slightly above dust, and can be +seen as a sort of fixed transfer fee. + +In order to be able to ''spend'' the asset, Bob needs to obtain the full asset +proof that was used as an input, which can be obtained from the relevant +Universe, or from Alice directly. + + +non_interactive_send(receiver_script_key: [32]byte, receiver_internal_key: PublicKey, + input_asset: AssetLeaf, amt: uint64) -> Tx + + receiver_leaf = clone_leaf(input_asset.leaf) + receiver_leaf.asset_script_key = receiver_script_key + receiver_leaf.prev_input = nil + receiver_leaf.amt = amt + + inner_smt_leaf = new_ms_smt_leaf( + key=receiver_script_key, value=tlv_encode(receiver_leaf), sum_val=amt, + ) + inner_smt_root = ms_smt_root_hash(new_ms_smt(inner_smt_leaf)) + outer_smt_leaf = new_ms_smt_leaf( + key=input_asset.asset_id, value=inner_smt_root, sum_val=amt, + ) + outer_smt_root = mew_smt_root_hash(new_ms_smt(outer_smt_leaf)) + + internal_key = new_internal_key() + + receiver_output = taproot_output_script(key=internal_key, leaves=[taproot_smt_leaf]) + + + +In order to be able to ''spend'' this new asset, Bob needs to obtain the full +provenance proof for the asset in question. Given that Bob is able to locate +the transfer transaction on-chain (using systems such as BIP 157/158), Bob +knows the previous input where the asset was stored. Given a known Universe, +Bob can look up the previous outpoint, then append his new leaf information to +the end of the file. + +====Taro Asset Files & Leaf Verification==== + +Taro asset files can be used to hermetically store and transmit the proofs of +provenance of a given asset. The flat file is a series of merkle proofs within +the main Bitcoin chain and also within Taproot outputs that hold asset +commitments. In order to verify the validity and provenance of an asset, a +verifier walks backwards (or forwards) in the asset transaction graph verifying +each commitment and asset witness state transition along the way. + +The Taro proof file format is specified in bip-taro-proof-file.mediawiki. +Future iterations of the proof file may also commit to a root append-only +merkle tree root of each individual proof segment. This would allow for +probabilistic verification of the provenance of an asset, reducing verification +costs for 3rd parties and potential receivers of the asset. + +===Asset Universes=== + + +Verification of an asset must also verify the provenance of the asset, namely +that the asset is an indirect descendent of the initial genesis outpoint that +created the asset itself. As all assets are defined by their provenance, +failure to verify the history of an asset defeats the entire purpose of the +system. As an example, the Bitcoin chain is defined as all the blocks that +ultimately link back up to the main genesis block, if a chain doesn't include +the genesis block, then it isn't Bitcoin. + +The Universe concept is an on-chain (and also off-chain) MS-SMT index into the main +chain, which indexes the set of revealed proofs, anchoring them back to the main +chain. Universes are used to bootstrap the provenance of an asset, and can also +be iterated to (re)construct the proof file for an asset. + +Universes are fully specified in bip-taro-universe.mediawiki. + +===Multi-Hop Taro Asset Transfer=== + +Leveraging the embedded asset HTLC/PTLC construct, we can extend the Lightning +Network to support multi-hop transfers of arbitrary assets and use the Bitcoin +backbone of the network as an asset agnostic monetary transport network. Only +the sender and receiver of a multi-hop payment need to be aware of the assets +being transferred. The internal backbone of the network only sees the +equivalent resulting bitcoin flows. + +Assuming Bob has N beefbux of outbound liquidity, and Alice has +M beefbux of inbound liquidity (where N>M) then Bob +can send M beefbux to Alice. The first hop of the transfer takes +in M+f beefbux (where f is their fee) and sends out +K = M/B BTC, where code is the agreed/advertised +beefbux/BTC exchange rate. All final hop of the transfer takes in +K BTC and sends M beefbuf (in the actual route this +would be less due to fees) to Alice. + +With the above construction, the Lightning Network can be extended to support +the transfer of arbitrary assets with BTC effectively serving as the "gas +asset". + +The full details of the Lightning Network (BOLT) extensions are specified in +bLIP ???. + +==Applications== + +==Test Vectors== + +TBD + +==Backwards Compatibility== + +==Acknowledgement== + +==Reference Implementation== + +github.com/lightninglabs/taro From 5339ffda33d22dfeab38b45e319ccda4cca12335 Mon Sep 17 00:00:00 2001 From: Christian Moss Date: Tue, 5 Apr 2022 16:27:30 +0100 Subject: [PATCH 2/2] Update bip-taro.mediawiki Mastercoin became OMNI, not counterparty which is a seperate protocol but still op_return based --- bip-taro.mediawiki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bip-taro.mediawiki b/bip-taro.mediawiki index e976a85909..c9efaadf1f 100644 --- a/bip-taro.mediawiki +++ b/bip-taro.mediawiki @@ -47,7 +47,7 @@ This document is licensed under the 2-clause BSD license. Bitcoin, the first decentralized blockchain, realized early in its life several systems that attempted to represent arbitrary assets within the constraints of the Bitcoin system itself. Amongst the earliest of these systems -was Counterparty (now known as OMNI), a meta-token protocol on top of Bitcoin +was Mastercoin (now known as OMNI), a meta-token protocol on top of Bitcoin that used OP_RETURN to commit the raw representations and transfers of assets within the system. Several years after the creation and deployment of Omni and other related systems very little activity takes place within these