比特币 - bips/bip-tap.mediawiki,位于Roasbeef/bips - Roasbeef

  • Roasbeef
  • 发布于 2025-03-05 14:51
  • 阅读 26

该文档描述了TAP,一种构建在比特币区块链之上的Taproot原生资产协议。Taproot Assets允许在比特币上表示任意资产,而不会使基础链膨胀。该协议设计使得与Taproot Assets交互的交易与普通交易无区别。Taproot Assets 通过一个嵌套的资产脚本树扩展了基础 Taproot 脚本树,资产脚本树提交到有效的见证作为结构化元数据,允许跨交易图移动资产的证明。

BIP: ??? Layer: Applications Title: TAP: Taproot Assets Protocol Author: Olaoluwa Osuntokun <laolu32@gmail.com> Comments-Summary: No comments yet. Comments-URI: https://git Status: Draft Type: Standards Track Created: 2021-12-10 License: BSD-2-Clause


|     |
| --- |
| ## Table of Contents&lt;br>[Permalink: Table of Contents](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#table-of-contents)&lt;br>- [Abstract](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Abstract)&lt;br>- [Copyright](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Copyright)&lt;br>- [Motivation](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Motivation)&lt;br>- [Design](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Design) &lt;br>  - [Merkle-Sum Sparse Merkle Trees](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-MerkleSum_Sparse_Merkle_Trees)&lt;br>  - [Taproot Asset Trees](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Taproot_Asset_Trees)&lt;br>  - [Asset Provenance](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Asset_Provenance)&lt;br>  - [Merkle-Sum based Proof of Reserves](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-MerkleSum_based_Proof_of_Reserves)&lt;br>  - [Splits, Merges, collectibles and Normal Divisible Assets](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Splits_Merges_collectibles_and_Normal_Divisible_Assets)&lt;br>  - [Specification](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Specification) &lt;br>    - [Asset Tree Representation](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Asset_Tree_Representation) &lt;br>      - [Asset Root Commitment](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Asset_Root_Commitment)&lt;br>      - [Asset Leaf Format](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Asset_Leaf_Format)&lt;br>  - [Asset Creation](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Asset_Creation)&lt;br>  - [Asset Burning](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Asset_Burning)&lt;br>  - [Asset Transfers](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Asset_Transfers) &lt;br>    - [Asset Swap Transactions](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Asset_Swap_Transactions) &lt;br>      - [Collectible Asset Transfers](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Collectible_Asset_Transfers)&lt;br>      - [Normal Asset Transfers](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Normal_Asset_Transfers)&lt;br>    - [Normal Asset Transfers](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Normal_Asset_Transfers-2)&lt;br>    - [Taproot Asset Files & Leaf Verification](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Taproot_Asset_Files__Leaf_Verification)&lt;br>  - [Asset Universes](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Asset_Universes)&lt;br>  - [Multi-Hop Taproot Asset Transfer](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-MultiHop_Taproot_Asset_Transfer)&lt;br>- [Applications](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Applications)&lt;br>- [Test Vectors](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Test_Vectors)&lt;br>- [Backwards Compatibility](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Backwards_Compatibility)&lt;br>- [Acknowledgement](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Acknowledgement)&lt;br>- [Reference Implementation](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#user-content-Reference_Implementation) |

### Abstract

[Permalink: Abstract](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#abstract)

本文档描述了 TAP,一个构建在基础比特币区块链之上的,Taproot 原生资产覆盖网络。
Taproot Assets 使得在比特币之上表示任意的(普通和收藏品)资产成为可能,而不会使基础链膨胀。
该协议的设计使得与 Taproot Assets 交互的交易,从比特币区块链的 PoV 来看,与常规交易无法区分。
Taproot Assets 使用嵌套的**资产脚本**树(一个 merkle-sum sparse merkle tree,或者 MS-SMT)扩展了基础的 Taproot 脚本树,该树将有效的见证作为结构化元数据进行提交,从而允许跨交易图的资产转移的证明。
可以使用作为扁平文件传输的密封证明来验证 Taproot Asset 转移的来源,或者借助外部维护的 Universe,它是一个 MS-SMT,索引链上资产发行 + 转移,原生支持储备金/供应量证明系统。

Taproot Assets 支持基于 BOLT 协议套件的闪电通道上的链下单跳和多跳传输,后者使用 Taproot Assets 独特的嵌入式资产脚本系统实现。
一系列链上和链下的 merkle 证明促进了链上 Taproot Asset 转移的轻客户端验证,这些证明可以通过将信任关系委托给活跃的 Universe 实例来压缩。
提出了一种链下多方通道的变体,以支持普通资产的链下转移。
此外,一种特殊类型的 Universe,称为 Pocket Universe,它基于仅退出的 commit-chain 设计,可以以一种最小化信任的方式来聚合链上的转移。

### Copyright

[Permalink: Copyright](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#copyright)

本文档采用 2-clause BSD 许可。

### Motivation

[Permalink: Motivation](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#motivation)

比特币,第一个去中心化区块链,在其早期就实现了几个试图在比特币系统本身的约束内表示任意资产的系统。
在这些系统中最早期的是 Mastercoin(现在称为 OMNI)和 Counterparty,它们是构建在比特币之上的元代币协议,它们使用 `OP_RETURN` 来提交系统中资产的原始表示和转移。
在 Omni 和其他相关系统创建和部署几年后,这些系统内的活动很少,而是分散到数十个与比特币没有直接联系的新区块链,而比特币才是启动这一切的链。

Taproot Assets 的目标是接过火炬,并实现一个在比特币上表示和操作任意资产的系统,该系统反映了现代区块链的设计原则,并提供了一个 Taproot 风格的数据结构和协议流程,比特币开发者会觉得熟悉。
重要的是,这样一个系统必须从一开始就被设计为在链下环境中工作,以确保基础链不会被不可扩展的覆盖协议所膨胀。
通过一个清晰的规范,我们旨在鼓励该系统被广泛的生态系统所采用,从而激发现有未充分配置的开发者热情和资源。

通过允许比特币开发者以私有、可扩展的方式轻松地表达和操作任意资产,我们的目标是增加比特币系统的效用,从而产生对区块空间的额外需求,这对于确保该系统能够在区块补贴渐近线变为零的环境中蓬勃发展是必要的。

### Design

[Permalink: Design](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#design)

在本节中,我们将提供 Taproot Assets 协议所包含的基本数据结构和概念的概述。
Taproot Assets 从在 taproot 脚本树中提交任意非脚本数据的简单想法开始,然后在此想法的基础上创建一个锚定在比特币链中的任意资产的覆盖协议。

#### Merkle-Sum Sparse Merkle Trees

[Permalink: Merkle-Sum Sparse Merkle Trees](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#merkle-sum-sparse-merkle-trees)

Merkle 树是元素**列表**上的经过身份验证的数据结构。
它允许我们做一些事情,比如获取目录的内容并创建一个根哈希,使我们能够证明给定的文件属于所讨论的目录。
每个元素都成对地进行哈希处理,从底部开始,直到只存在一个哈希。
我们将此哈希称为根哈希。

Merkle-sum 树是 merkle 树的一种变体,我们的哈希操作也提交给给定属性的**总和**。
我们可以通过提交每个文件的大小来扩展上面的示例。
然后,根哈希提交到左子树、右子树,以及左子树和右子树大小的总和。
这种新的变体很有用,因为我们可以证明文件的存在及其大小。
我们还可以做一些事情,比如生成子目录大小的证明。

我们不能轻易地用 merkle-sum 树做的一件事是证明不存在,因为如果项目没有规范排序,那么证明不存在就需要显示树中的所有叶子。
如果我们选择对 merkle 树中的叶子集进行排序,那么我们可以更容易地证明不存在,但是我们会引入每次插入新项目时都需要重新平衡树的开销。
证明不存在在 Taproot Asset 设置中很有用,因为我们可能希望资产的卖方证明他们不再提交所讨论的资产。

Sparse Merkle Tree 是通过一系列键值对进行的 merkle 树“模拟”,它支持有效的非包含证明。
SMT 实际上由整个键空间(所有 256 位值)组成,即它是一棵具有 2^256 个叶子(因此具有 256 个级别)的树。
通过观察我们知道一个空元素的哈希值、一个具有两个空子树的分支的哈希值等等,就可以使该结构的表示易于处理。
此外,一系列缓存策略可用于使表示更有效。
通过添加一个位图,用于指示证明中的中间分支是否提交给一个空子树,可以进一步压缩证明。

Merkle-Sum Sparse Merkle Tree 是一个 SMT,它也继承了 sum-combiner 特性。
我们使用此数据结构来允许轻松验证可分割资产的分割,支持非包含证明,并用作 merkalize 查找表,以方便验证 Taproot Asset 证明。

本文档中提到的 MS-SMT 在 BIP [bip-tap-smt](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap-smt.mediawiki) 中有完整的规范。

#### Taproot Asset Trees

[Permalink: Taproot Asset Trees](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#taproot-asset-trees)

Taproot Assets 协议是一个 Taproot 原生的资产覆盖网络,可用于使用基础比特币区块链创建和转移收藏品或普通资产。
资产表示为**在**现有 Taproot 脚本树**内**对结构化数据的提交。
此结构化数据永远不会以明文形式写入基础链本身,而是由 Taproot Assets 覆盖协议在更高层维护。
资产本身由主资产树内的一系列 MS-SMT(每个资产 ID/类型一个)表示。
每个资产(除了许多其他属性之外)还提交到一个**资产脚本哈希**,该哈希提交到一个**资产脚本**,该脚本从 Taproot Assets 覆盖协议的 PoV 限制了资产被允许转移的 _方式 _。
该系统的初始版本使用比特币脚本的一个子集,以允许资产表达关于资产有效转移的任意条件。

因此,Taproot Asset 叶子反映了基础比特币区块链上 UTXO 的可编程性。
给定 Taproot Asset 的验证者将拒绝任何无效的状态转换。
因此,我们继承了与基础脚本系统相当(并且通过新的资产脚本版本也超越了)的可编程性,这是促进通过闪电网络进行多跳链下转移(通过**嵌入**在资产脚本中的 HTLC)的关键要求。

#### Asset Provenance

[Permalink: Asset Provenance](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#asset-provenance)

资产的来源使用两种机制之一来证明。
第一种机制,因此被称为“完整证明”,是一个结构化文件,其中包含来自初始**创世输出**的序列化证明,以及一系列可验证地转移 taproot 输出之间资产所有权的有效资产见证。
完整证明通常表示为扁平文件,供 Taproot Assets 协议使用、验证和显示。

第二种证明/验证来源的方法是一种结构略有不同的 MS-SMT,它也在 Taproot 脚本树中提交。
我们将这种元结构称为 Universe,因为它提交到链中的 Taproot Asset 承诺以及额外的证明数据。
Universe 是一个键值存储,可用于引导来源验证(由那些创建感兴趣的资产集的人维护)。
除了初始创世输出验证之外,Universe 还可以通过在 MS-SMT 中记录所有有效的 Taproot Asset 转移来充当可扩展性层,该 MS-SMT 将**资产标识符**映射到资产的最后已知的有效转移。

#### Merkle-Sum based Proof of Reserves

[Permalink: Merkle-Sum based Proof of Reserves](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#merkle-sum-based-proof-of-reserves)

MS-SMT 数据结构的使用使系统能够支持有效的供应/非通货膨胀证明,允许参与者轻松验证在给定 taproot 输出中提交的资产总额,以及给定 Universe 发行的资产总集。
SMT 的 merkle-sum 增强功能允许资产可分割,同时允许验证者断言,与常规比特币交易类似,在专门设计的资产创世交易之外,转移期间不会创建新资产。

#### Splits, Merges, collectibles and Normal Divisible Assets

[Permalink: Splits, Merges, collectibles and Normal Divisible Assets](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#splits-merges-collectibles-and-normal-divisible-assets)

Taproot Assets 能够表示收藏品和普通可分割资产。
普通资产承诺持有资产的总价值,这些资产可以像普通比特币 UTXO 一样在树**内**分割,也可以在顶级 Taproot 输出之间分割。
我们称这些为内部和外部拆分。
普通资产也可以合并,其过程与基础层上的 UTXO 合并类似。
在转移资产期间,资产持有人证明他们持有有效的拆分(通过 merkle-sum 证明),并且每个创建的资产都承诺一个新的 merkle-sum 输出拆分集,以在转移期间强制执行非通货膨胀。

另一方面,收藏品既不能拆分也不能合并。
它们通常批量创建,并且通常表示对链级或现实世界资产/功能的索赔/凭据(假设存在现有的信任关系)。
收藏品的一个例子是一系列限量版数字收藏品,如棒球卡或集换式卡牌游戏。

普通资产可以使用闪电网络以多跳方式链下转移,而收藏品资产必须在链上转移或提升到多方通道中,以允许在已知的一组参与者之间进行转移。

#### Specification

[Permalink: Specification](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#specification)

##### Asset Tree Representation

[Permalink: Asset Tree Representation](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#asset-tree-representation)

###### Asset Root Commitment

[Permalink: Asset Root Commitment](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#asset-root-commitment)

Taproot Asset 树是系统的核心组件。
资产树是对一系列资产标识符的 MS-SMT 承诺,这些资产标识符按其创世资产 ID 分组。
给定的 Taproot Asset 树由两个嵌套的 MS-SMT 实例组成:

1. 第一层将 `asset_id` 或 `asset_group_key` 映射到给定资产的子树根哈希。
2. 第二层将 `asset_script_key` 或 `asset_id || asset_script_key` 映射到序列化的 Taproot Asset 叶子。

观察 [BIP-341](https://github.com/Roasbeef/bips/blob/master/bip-0341.mediawiki),资产树的根哈希表示为一个 Tapscript 树,该树提交给一个唯一的叶子:

- `tagged_hash("TapLeaf", leaf_version || taproot_asset_marker || taproot_asset_version || asset_tree_root)`

其中:

- `taproot_asset_marker` 是 ascii 字符串 "taproot-assets" 的 `sha256` 哈希。
- `taproot_asset_version` 是 Taproot Assets 系统的版本,它定义了如何解释剩余的承诺值。
- `asset_tree_root` 是一个 40 字节(32 字节用于哈希,8 字节用于值)MS-SMT 根。

选择 ??? 的 `leaf_version`。
从比特币系统的 PoV 来看,我们只是提交给了一个带有不可解析脚本的 tapscript 叶子。
未来,如果比特币系统被软分叉以了解 Taproot Asset 特定的承诺,那么可以使用相同或不同的叶子版本来控制新行为的验证。

`asset_id` 是以下内容的 32 字节哈希:

- `sha256(genesis_outpoint || sha256(asset_tag) || asset_meta_hash || output_index || asset_type)`

其中:

- `genesis_outpoint` 是资产创世交易中使用的第一个先前输入输出点,以比特币 wire 格式序列化。
- `asset_tag` 是一个随机的 32 字节值,表示给定的资产,可用于将一系列离散资产链接到单个资产组中。 在实践中,这通常是人类可读资产名称的哈希。
- `asset_meta_hash` 是一个不透明的 32 字节值,可用于提交各种元数据,包括外部链接、文档、统计数据、属性、图像等。重要的是,此字段被认为是_不可变的_。
  - 创世资产证明必须包含 `asset_meta_hash` 的原像,这允许验证者获取已提交的数据并验证 sha2 原像。
- `output_index` (4 字节,大端) 是创世交易中包含唯一 Taproot Asset 承诺的输出的索引。
- `asset_type` (1 字节) 是正在铸造的资产的类型。

鉴于上述结构,从链的 PoV 来看,`asset_id` 保证是全局唯一的,这要归功于 [BIP 34](https://github.com/Roasbeef/bips/blob/master/bip-0034.mediawiki),因为输出点一旦在区块链内序列化就永远不会重复。

此外,我们强制执行一个规则,即给定的输出必须只包含 _单个_ 根 Taproot Asset 承诺。为了验证此属性,我们对根 Taproot Asset 树承诺的位置强制执行以下约束:

- 资产根承诺必须是 tapscript 树中最左侧或最右侧的叶子:
  - 我们通过要求在脚本树中显示深度为 0 或 1 的 Taproot Asset 承诺来强制执行这一点。
- 对于深度为 0(32 字节控制块)的显示证明,Taproot Asset 承诺是控制块证明中唯一的元素,因为它直接成为根哈希。
- 对于深度为 1(64 字节控制块)的显示证明,我们要求证明者还显示同级哈希的原像。
  - 对于深度为 1 的根位置,Taproot Asset 承诺是完全排序的 tapscript 树的最左侧或最右侧元素。
  - 要成为有效的 Taproot Asset 根承诺,同级哈希原像必须:
    - 恰好为 64 字节,这意味着该同级本身是一个 tap 分支(Taproot Asset 根原像本身为 76 字节:2 字节版本,32 字节 Taproot Asset 标记,2 字节 Taproot Asset 版本,32 字节哈希,8 字节总和值),受其 BIP-340 标记哈希标记的约束。
    - 或者,是一个 tap 叶子,可以通过唯一的 BIP-340 标记哈希标记进行验证。
      - 在这种情况下,必须对照原始消息摘要验证叶子,以检测并拒绝 tapscript 树中重复包含的 Taproot Asset 承诺(请参阅 `taproot_asset_marker`)。

由于在哈希之前对 tap 分支/叶子进行词典排序,因此我们丢失了 merkle 树中的排序信息。
因此,我们被迫要求显示同级原像。

以下算法可用于声明 tapscript 树中 Taproot Asset 承诺的唯一性:

taproot_asset_commitment_is_valid(commitment_output: TxOut, control_block: ControlBlock, sibling_preimage: TaprootAssetTapReveal, taproot_asset_root: []byte) -> bool

taproot_asset_root_hash = tagged_hash("TapLeaf", taproot_asset_root)
internal_key = parse_key(control_block.internal_key_bytes)

match len(control_block.size):
   # Just the internal public key, so we can just verify the commitment below.
   case 32:
       expected_output_key = internal_key +
           tagged_hash("TapTweak", internal_key || taproot_asset_root_hash)*G

       return expected_output_key == witness_program(commitment_output.pk_script)

   # More than one element in the tree, so need to verify each sub-case.
   case 65:

       match sibling_preimage:
           # The pre-image is a non Taproot Asset leaf, so verify the hashes
           # match up.
           case LeafReveal(leaf_bytes):
               # The leaf can't share the Taproot Asset marker bytes,
               # otherwise it may be a legitimate commitment.
               if leaf_bytes[2:].starts_with(taproot_asset_marker):
                   return false

               leaf_hash = tagged_hash("TapLeaf", leaf_bytes)

               if leaf_hash != control_block.sibling_hash:
                   return false

           # The pre-image is itself a branch, so we need the two siblings
           # used to construct the branch.
           case BranchReveal(sibling_1, sibling_2):
               expected_branch_hash = tagged_hash("TapBranch", sort(sibling_1, sibling_2))

               if expected_branch_hash != construct.sibling_hash:
                   return false

           # We know the tree is well formed now, so we'll verify the root
           # commitment.
           root_hash = tagged_hash("TapBranch", sort(taproot_asset_root_hash, control_block.sibling_hash))

           expected_output_key = internal_key + tagged_hash("TapTweak", internal_key || root_hash)*G

           return expected_output_key == witness_program(commitment_output.pk_script)

   default:
       return false

除了验证承诺在单个 taproot 树的约束内是唯一的之外,我们还需要验证在创世交易的其余输出中没有其他**重复**或意外的承诺。我们可以通过断言以下内容来实现这一点:

- 对于创世交易中没有创建资产的每个输出:
  - 如果顶级密钥是使用 BIP 86 派生的,则验证密钥派生。
  - 如果密钥提交到脚本路径:
    - 如果树中只有一个元素,请验证它不是重复的承诺。
    - 如果树包含多个元素,则验证两个分支的原像不是重复的承诺。

在典型的创世交易(1 个输入,1 个输出)的情况下,无需交换其他信息。但是,如果交易也有其他输出,则需要一个最小的控制块显示(32 或 65 字节)。

以下算法可用于验证交易中其他位置的输出是否没有提交到重复的 Taproot Asset 承诺:

verify_no_taproot_asset_up_my_sleeves(genesis_tx: Tx, taproot_asset_root: []byte) -> bool

for tx_out in genesis_tx.tx_outs:
    # We can ignore any non-P2TR output, as they can't commit to Taproot
    # Assets.
    if !commitment_expected(tx_out):
        continue

    # We're expecting a commitment at this point, so we'll walk through
    # our two cases.
    match tx_out.taproot_commitment:
        # A single leaf, so we just verify it isn't the Taproot Asset
        # commitment.
        case SingleLeaf(leaf_bytes, internal_key):
           # Someone has some 'splaining to do...
           if leaf_bytes == taproot_asset_root:
               return false

           leaf_hash = tagged_hash("TapLeaf", leaf_bytes)
           expected_output_key = internal_key + tagged_hash("TapTweak", internal_key || leaf_hash)*G

           if expected_output_key != witness_program(tx_out.pk_script):
               return false

       # More than one element, so we verify that neither of the pre-image
       # are a duplicate commitment.
       case MultiLeaf(branch_1_bytes, branch_2_bytes):
           if branch_1_bytes == taproot_asset_root:
               return false

           if branch_2_bytes == taproot_asset_root:
               return false

           branch_hash = tagged_hash("TapBranch", sort(branch_1_bytes, branch_2_bytes))

           expected_output_key = internal_key + tagged_hash("TapTweak", internal_key || leaf_hash)*G

           if expected_output_key != witness_program(tx_out.pk_script):
               return false
return true

BIP [bip-tap-smt](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap-smt.mediawiki) 中描述了一种确定性算法,该算法在给定一组单独的叶子或完整的 tapscript 树(根哈希)的情况下,构造最终的 taproot merkle 树哈希。

无法强制 `asset_tag` 字段在_全局_上是唯一的。
相反,为了确保在初始资产创建中本地唯一的 `asset_id` 实例,每个 `asset_tag` 和 `asset_meta_hash` 值必须仅在资产创建期间出现一次。此外,`asset_meta_hash` 的_原像_(称为 `meta_reveal`)可以与创世资产创建的初始证明一起打包,以确保验证者可以获取给定资产的所有相关数据。

顶层 MS-SMT 提交给所有持有的已定义资产集。此 MS-SMT 树的根哈希称为 `asset_tree_root`。MS-SMT 的结构如下:

- key: `asset_id` 或 `asset_group_key`
- value: `taproot_asset_version || asset_id_tree_root || asset_sum`
- sum\_value: `asset_sum`

与 `asset_id` 类似,`asset_group_key` 的派生方式确保了系统内的唯一性,但可以在多个资产铸造之间共享。`asset_group_key` 的派生方式如下:

- `asset_group_key = TapTweak(asset_internal_group_key, asset_group_script_root)`

其中:

- `asset_raw_group_key` 是 [BIP-340](https://github.com/Roasbeef/bips/blob/master/bip-0340.mediawiki) 定义的 32 字节公钥
- `asset_internal_group_key = SingleTweak(asset_raw_group_key, asset_id)`
- `asset_group_script_root` 是一个 taproot,可以容纳任意资产脚本,从而强制执行铸造资产的条件。
  - `asset_raw_group_key` 和 `asset_group_script_root` 必须在资产创世时显示,以便验证组密钥,因此将与资产证明一起提供。

并且

- `TapTweak` 是 [BIP-341](https://github.com/Roasbeef/bips/blob/master/bip-0341.mediawiki) 中定义的 taproot 调整函数。
- `SingleTweak` 是一个调整函数,与 `TapTweak` 不同,它使用未标记的哈希:

`tweaked_pubkey = pubkey * sha256(tweak || pubkey) * G`
顶层树可以很容易地用于证明给定资产集合的存在、所持有的资产单位总数以及所持有的给定资产的总和。

`asset_id_tree_root` 值本身是另一个嵌套的 MS-SMT,具有以下结构:

- key: `asset_script_key` 或 `asset_id || asset_script_key`
- value: `asset_leaf || leaf_sum`
- sum\_value: `leaf_sum`

在由给定 `asset_id_tree_root` 根植的树所持有的 MS-SMT 中,会为输出所持有的每个 `asset_id` 或 `asset_group_key` 创建一个新的 Taproot Asset MS-SMT。 MS-SMT 的叶子由解锁脚本的 `asset_script_key` 或 `asset_id || asset_script_key` 键控。 资产树的根哈希计算为以下之一:

- `asset_tree_root = sha256(asset_id || left_hash || right_hash || sum_value)`

或者

- `asset_tree_root = sha256(asset_group_key || left_hash || right_hash || sum_value)`

其中:

- `asset_group_key` 是从
genesis **输出点**、输出索引和资产类型派生的公钥的 32 字节哈希值,可用于将旨在彼此同质化(发行同一资产的多个批次)或收藏品的资产并置。

- `asset_id` 是上面指定的 32 字节资产 ID
- `left_hash` 是左子树的哈希值。
- `right_hash` 是右子树的哈希值。
- `amt_sum` 是每个资产叶子(本质上是资产 UTXO)的 `amt` 值的总和。

提交给定资产子树中给定资产的资产值的总和,允许资产持有者轻松证明他们拥有的给定资产的数量,当与资产创建和“Universe”概念相结合时,允许一种内置的储备证明机制。

`asset_group_key` 的使用(我们将在下面看到)允许发行者在同一资产子树中并置旨在彼此同质化的资产。 `asset_group_key` 的派生确保类似于 `asset_id`,此值在链中是全局唯一的。 未指定 `asset_group_key` 的
资产使用略有不同的模式来键入主 MS-SMT,并且只能以单个批次发行。

当在资产发行/铸造期间指定 `asset_group_key` 时,类似于顶级 SMT,内部 MS-SMT 会修改其内部密钥,以便也考虑每个资产的派生 `asset_id`。 因此,最低级别的密钥会考虑 `asset_id` 以及 `asset_script_key`。 再次,这确保了所有资产和脚本密钥都放置在 MS-SMT 中唯一的**位置**。

请注意,由于树的结构,对于有效 taproot 输出中的给定 Taproot Asset 子树,所有 `asset_script_key` 值**必须**是唯一的。

###### 资产叶子格式

[Permalink: 资产叶子格式](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#asset-leaf-format)

给定 `asset_id` 的资产树的叶子提交到以
[https://learnblockchain.cn/article/18232#type-length-value-formatTLVTLV](https://learnblockchain.cn/article/18232#type-length-value-formatTLVTLV)
格式表示的元数据 blob,该**格式**供闪电网络的线路协议使用。 TLV 结构是规范的和确定性的,使其适合用于加密承诺和签名摘要。 此外,TLV 格式支持向后和向前兼容,从而使资产承诺的基础结构具有高度可扩展性。

资产叶子是一个序列化的 TLV blob,具有以下键值映射:

- type: 0 ( `taproot_asset_version`)
  - value:
    - \[ `u8`: `version`\]
- type: 1 ( `asset_id`)
  - value:
    - \[ `32*byte`: `asset_id`\]
- type: 2 ( `asset_type`)
  - value:
    - \[ `u8`: `type`\]
- type: 3 ( `amt`)
  - value:
    - \[ `BigSize`: `amt`\]
- type: 4 ( `lock_time`)
  - value:
    - \[ `BigSize`: `block_height`\]
- type: 5 ( `relative_lock_time`)
  - value:
    - \[ `BigSize`: `num_relative_blocks`\]
- type: 6 ( `prev_asset_witnesses`)
  - value:
    - \[ `u16`: `num_inputs`\]\[ `asset_witnesses...`\], 其中:
      - \[ `...*byte`: `asset_witnesses`\]:
        - \[ `asset_witness`\]:
          - type: 0 ( `prev_asset_input`)
            - value: \[ `prev_outpoint || prev_asset_id || prev_asset_script_key`\]
          - type: 1 ( `asset_witness`)
            - value: \[ `...*byte`: `asset_witness`\]
          - type: 2 ( `split_commitment`)
            - value: \[ `...*byte`: `split_commitment_proof`\]
            - value: \[ `...*byte`: `root_asset`\]
- type: 7 ( `split_commitment_root`)
  - value: \[ `32*byte`: `split_commitment_root`\]
- type: 8 ( `asset_script_version`)
  - value:
    - \[ `u16`: `script_version`\]
- type: 9 ( `asset_script_key`)
  - value:
    - \[ `33*byte`: `pub_key`\]
- type: 10 ( `asset_group_key`)
  - value:
    - \[ `32*byte`: `pub_key`\]

其中:

- `taproot_asset_version`: 是一个单字节,表示正在使用的 Taproot Asset 的版本,它允许客户端确定要期望哪些下面的 TLV 值。
- `asset_type`: 是一个单字节,表示资产的类型,定义了两个起始的资产类型:
  - `0`: 普通资产
  - `1`: 收藏品资产
- `amt`: 是此叶子**位置**持有的资产数量
- `lock_time`: 是一个字段,根据包含的区块高度限制**何时**可以移动资产。
- `relative_lock_time`: 是一个字段,根据从外部交易的挖矿中需要传递的区块数来限制**何时**可以移动资产。
- `prev_asset_witnesses`: 是一个**嵌套**的 TLV,包含验证合并到目标资产叶子中所需的资产 witness
  - `prev_asset_input`: 通过交易中的输入**位置**、先前资产树的资产 ID 和资产脚本哈希来引用先前的资产输入。
    - 此值包含在资产 witness 本身内生成的任何签名中,其作用类似于普通比特币中的先前输出点。
    - 如果此字段全部为零,则表示正在创建**新**资产。
    - 对于**内部**拆分验证,此字段可用于通过仅包含涵盖所有其他新拆分的一个签名/witness 来节省空间。
    - 如果此类型不存在,则**必须**存在 `split_commitment`。
  - `split_commitment_proof`: 用于允许花费因资产拆分而创建的资产 UTXO。 当资产被拆分时,非变更 UTXO 会提交到 MS-SMT 树中所有其他拆分的**位置**。 当花费由 asset\_split 产生的变更资产 UTXO 时,不需要普通的 `asset_witness`,而是变更资产 UTXO 的所有者必须证明它持有由主转移交易(`root_asset`)授权的有效拆分。
    - 具有相同 `split_commitment` 的输出被称为共享单个 `asset_witness`,因为这样的输出是新的资产拆分的结果。 因此,我们只需要一个 witness 和生成的 merkle-sum 资产树来验证转移。
  - `asset_witness`: 是与比特币的 Segwit witness 字段格式相同的序列化 witness。
    - 如果 `prev_asset_input` 全部为零,则 witness 将是以下之一:
      - 空,表示此资产不支持进一步发行。 在这种情况下,**不得**存在 `asset_group_key`。
      - 非空,在这种情况下,将根据 [BIP-341](https://github.com/Roasbeef/bips/blob/master/bip-0340.mediawiki#specification) 验证规则使用 `asset_group_key` 代替 `asset_script_key` 来解释 witness。 在这种情况下,**必须**存在 `asset_group_key`。
- `split_commitment_root`: 用于提交并允许验证普通资产的新输出拆分分配。 `split_commitment_root` 是一个 MS-SMT,其密钥为 `sha256(output_index || asset_id || asset_script_key)`,值为新的 Taproot Asset 叶子。 这是一个可选字段,仅在转移期间拆分资产价值时指定。
  - 具有相同 `split_commitment_root` 的输出被称为共享单个 `asset_witness`,因为这样的输出是新的资产拆分的结果。 因此,我们只需要一个 witness 和生成的 merkle-sum 资产树来验证转移。
- `asset_script_version`: 是一个 2 字节的资产脚本版本,它控制如何验证以下 TLV 值。
- `asset_script_key`: 是以 BIP 341 方式派生的外部公钥,它可以提交到限制此资产叶子的资产脚本。
- `asset_group_key`: 是 [BIP-340](https://github.com/Roasbeef/bips/blob/master/bip-0340.mediawiki) 定义的 32 字节公钥。 此密钥可用于关联由其 `asset_id` 标识的不同资产,从而有效地允许进一步发行基础资产。 引用相同 `asset_group_key` 的资产将被视为相同的资产。 这是一个可选字段,不包含此字段的资产实际上被认为是仅一次性发行事件,这意味着无法创建与派生 `asset_id` 相关的其他资产。
- `canonical_universe`: 是一个密钥(没有相应的值),如果指定,则表示链上规范 Universe 的存在。 在 [./bip-tap-universe.mediawiki](https://learnblockchain.cn/article/18213) 中进一步指定,规范 Universe 由资产发行者维护,可用于允许第三方轻松审计总资产供应并跟踪未来发行。 简而言之,如果指定了此项,则资产发行期间创建的**输出点**的下一次花费的**第二个**输出**必须**提交到基础 Universe 的根哈希,并且所有后续花费只能在以后的资产发行事件之后发生,并且**必须**提交到新的有效 Universe 根。 此功能允许轻客户端在链上观察一组输出,以便在未来资产发行时收到通知。

(TODO(roasbeef): 输入集合的 merkle sum 承诺? 启用概率验证?)

资产叶子用于存储与资产相关的结构化数据,以及验证资产叶子正确转移所需的一系列先前输入资产 witness。 此处的输入结构类似于普通的比特币输入和输出方案,此外还添加了 `split_commitment`,这是允许验证者拒绝无效拆分,从而防止资产膨胀所必需的。

`asset_script_version` 是 Taproot Asset 叶子的一个关键设计元素,因为它允许将来升级用于锁定/解锁资产叶子的脚本系统。 最初,使用版本 `0` 资产叶子,表示 `asset_script_key` 是 BIP 341 和 BIP 342 中指定的**有效** `output_key`。 因此,版本 0 Taproot Asset VM 是 Taproot 在外部实例**内**的一个实例。
`asset_script_version` 的验证逻辑在 BIP ??? 中完全定义。 将来,可以引入新的资产脚本版本,以进一步提高嵌入式资产脚本的可表达性。

数值高于此保留范围的类型可以用于额外的 `taproot_asset_version` 迭代,低于 2^16-1 的 TLV 类型被保留用于使用。 数值高于此保留范围的类型可用于存储资产的任意属性。 这样的属性的一个示例是存储游戏中资产的 **可变** 统计信息。 普通资产或收藏品资产的任何不可变字段都应改为提交到 `asset_meta_hash` 中,将游戏中资产的**可变**统计信息存储在其中。

MS-SMT 用于 Taproot Asset 树本身允许各方轻松验证在引用输入时是否未创建任何新资产,并且每个新的拆分/合并都会产生有效的拆分集合(通过包含在计算的 witness sighash 中的 `split_commitment_root`)。

#### 资产创建

[Permalink: 资产创建](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#asset-creation)

资产的创建类似于任何其他比特币交易,但它通常是 1 输入 1 输出的交易。 资产创建交易花费任意一组输入,并产生一个或多个可能提交到一组新创建资产的输出。 作为一种简化机制,我们要求给定的 Taproot Asset 交易只能创建新资产或转移现有资产,但不能同时进行。

(TODO(roasbeef): 不允许转移+创建实际上会使任何事情更简单吗?)

以下函数创建一个新的有效 taproot 公钥脚本,其内部 tapscript 树提交到新资产的表示:

create_new_asset_output(total_units: uint64, asset_tag: [32]byte, genesis_point: [36]byte, asset_meta_hash: [32]byte, output_index uint32, asset_type: uint8, asset_script_key: [32]byte, taproot_asset_attrs: TLV) -> PkScript:

asset_id = sha256(
    genesis_point || sha256(asset_tag) || asset_meta_hash || output_index ||
    asset_type
)

tlv_leaf = new_taproot_asset_tlv_leaf(
    taproot_asset_version=0, asset_id, asset_type, total_units, asset_script_version=0,
    asset_script_key, attrs=taproot_asset_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])

请注意,我们要求提前知道 `genesis_point`,以便强制执行定义的 `asset_id` 的唯一性约束。

`taproot_asset_attrs` 字段可用于提交到与资产关联的一组任意的和可能可变的字段。

在上面的示例中,我们创建的生成的 taproot 树仅提交到单个叶子,即 Taproot Asset 根。 实际上,在 tapscript 树中可能还有其他正常的脚本路径脚本。

上面的示例仅创建一个资产。 也可以在单个输出中创建多个资产,从而批量创建资产。

#### 资产销毁

[Permalink: 资产销毁](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#asset-burning)

鉴于 Taproot Asset 承诺在 tapscript 承诺的上下文中是唯一的要求,Taproot Assets 也可以被**可证明地烧毁**。 这可以通过简单地从资产树中**删除** Taproot Asset 叶子并使用排除证明来证明来实现。

为了使销毁更明确且更易于识别和验证,选择了“通过不可花费密钥证明销毁”方法:要销毁一定数量的资产,只需将它们转移到可证明的不可花费脚本密钥即可。 然后,销毁证明看起来像任何其他状态转换证明,唯一的区别是脚本密钥可以被识别为不可花费。

这种资产销毁转换可以与同一树中的其他资产输出(例如,来自部分销毁的变更或同一承诺中的其他资产)合并,同时进行销毁。 在任何后续转移中,都可以通过简单地从树中省略销毁的叶子来修剪它们,此时不需要任何额外的证明。

可以通过以下方式派生资产销毁的唯一、可证明的不可花费脚本密钥:

burn_tweak = tagged_hash("TapTweak", nums_key || prev_outpoint || prev_asset_id || prev_script_key) burn_key = nums_key + burn_tweak * G


其中:

- `nums_key` 是众所周知的 NUMS 点(使用字符串“taproot-assets”和传统的“哈希和增量”方法来生成点)
- `prev_outpoint` 是根资产 witness 的第一个 `prev_input` 的 `outpoint`(见下文)
- `prev_asset_id` 是根资产 witness 的第一个 `prev_input` 的 `asset_id`(见下文)
- `prev_script_key` 是根资产 witness 的第一个 `prev_input` 的 `script_key`(见下文)

根资产 witness 的第一个 `prev_input` 定义为资产中携带先前资产 witness 的第一个输入。 如果资产有拆分承诺,那么这是
`prev_asset_witnesses[0].split_commitment.root_asset.prev_asset_witnesses[0].prev_asset_id`,否则它只是 `prev_asset_witnesses[0].prev_asset_id`。

以下算法可用于识别资产转移中的可证明的不可花费脚本密钥,以将其标记为资产销毁:

derive_burn_key(first_prev_id: PrevID) -> SchnorrKey burn_tweak = tagged_hash("TapTweak", NUMS_key || first_prev_id.outpoint || first_prev_id.asset_id || first_prev_id.script_key) return = NUMS_key + burn_tweak*G

get_first_prev_id(witness: AssetWitness) -> PrevID if is_split_commit_witness(witness): return witness.split_commitment.root_asset.prev_asset_witnesses[0].prev_asset_id

return witness.prev_asset_id

is_burn(asset: Asset) -> bool: first_prev_id = get_first_prev_id(asset.prev_asset_witnesses[0]) burn_key = derive_burn_key(first_prev_id)

return asset.ScriptKey == burn_key

#### 资产转移

[Permalink: 资产转移](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#asset-transfers)

在普通资产和资产收藏品中,定义了两种类型的转移:交换和普通发送。

资产交换转移需要经过多轮交互,使用 PSBT 扩展(如 [./bip-tap-psbt.mediawiki](https://learnblockchain.cn/article/18231) 中定义)来协作创建多输入多输出 (MIMO) 交易,该交易花费一个或多个不同的资产作为输入,并创建一个或多个新的资产所有者作为输出。

另一方面,普通发送仅涉及一轮交互,因为只有一方将资产发送到另一方的钱包。 与交互式转移相比,只需要指定一个 Taproot Asset witness。

构建和验证资产集合中某个**项目**的转移需要资产的所有者将其所有权从其输入转移到接收者控制下的新输出中。 一组 merkle-sum 承诺以及资产 witness 有效性用于验证和验证资产转移。

常规资产的交换涉及额外的验证步骤,因为双方都需要确保没有无意中创建新资产(导致无效交易)。 为了完成此操作,必须以这样一种方式形成交易,即第三方验证者能够验证拆分(将单个资产 UTXO 拆分为多个实例)和合并(将相同资产类型的输出合并为一个)。 我们使用 `split_commitment` 字段来实现此目的,有效地强制执行可以被视为变更输出(在 Taproot Asset 领域中)的内容,以提交到其他创建的拆分的 merkle-sum 树。

##### 资产交换交易

[Permalink: 资产交换交易](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#asset-swap-transactions)

在本节中,我们指定交互式资产转移,其中两个(或更多方)协作创建 MIMO 交易,该交易在一组参与方之间转移一个或多个资产。 我们描述了在此设置中的核心交互、交换证明的验证。 我们将有关如何将此类协议映射到基于 PSBT 的签名仪式的更多详细信息留给 BIP [./bip-tap-psbt-mediawiki](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap-psbt-mediawiki)。

###### 收藏品资产转移

[Permalink: 收藏品资产转移](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#collectible-asset-transfers)

资产收藏品的转移比普通资产转移更简单,因为它们不需要验证拆分或合并多个资产 UTXO。 资产收藏品的转移发生在两个层面上:

- 首先,交换资产 witness 和脚本哈希信息,允许接收方(每个输入资产)验证输入资产的来源,发送方将所有权委托给接收方的脚本哈希。
  - 我们称之为**内部**转移过程。
- 接下来,在一层交换一组普通的比特币签名/witness,这些签名/witness 花费所有输入资产(有效地销毁它们)并在生成的 MIMO 交易中将资产重新创建为新的承诺。
  - 我们称之为**外部**转移过程。

请注意,并非所有输入都需要是持有 Taproot Asset 的输入。 这自然会导致新的子协议在单个交易中跨多个方执行批处理的 MIMO 原子交换。

由于验证是 Taproot Asset 协议的基石,因此在交互式转移过程中,每个参与方都会执行以下一组验证断言:

- 验证任何输入资产的来源。
- 验证一方可以为每个资产生成有效的输入资产 witness。
- 验证创建的新 Taproot Asset 脚本输出要么不再提交到转移的资产,要么现在正确地提交到新接收的资产。

第一个验证步骤验证资产是否具有有效的沿袭。 第二个步骤验证资产所有者是否实际上可以选择花费该资产。 最后一个步骤验证是否未创建任何新资产,而是仅转移了具有已验证来源的现有资产。

转移指定如下:

1. 对于每个输入(可能持有一个或多个要转移的资产收藏品),所有者传输:
   1. 存储在每个输入的先前输出中的 MS-SMT 承诺的打开,作为 Universe 的标识符(以提取压缩证明)或完整证明文件传输。
      1. 使用此方法,资产收藏品的接收者验证:
         1. 每个证明都是资产树承诺的有效打开,具有通向要转移的资产的有效叶子路径。
         2. 对于每个要转移的资产,给定所需资产的 amt(对于所有资产收藏品,应为 1),merkle sum 承诺是有效的。
         3. 给定 `asset_id`,打开显示 `genesisOutpoint`、`assetTag` 和 `assetMeta` 的有效打开。
         4. 提供的 `genesisOutpoint` 存在于为一组唯一资产收藏品维护的 Universe 中。 可以使用 Universe 的当前 MS-SMT 的 merkle 证明或完整证明。
2. 初始验证完成后,开始内部转移过程:
   1. 对于要交换的每个资产集合 _i_:
      1. 接收者创建一个新的资产脚本密钥承诺 `asset_script_key_i`(将用于委托输入资产的所有权),并将其发送给所有者。
      2. 所有者获取原始序列化的资产脚本,并在 `asset_script_key_i` 脚本上关闭一个新的资产 witness(作为序列化资产叶子的一部分)。 然后将此 witness 传输给接收者。
      3. 接收者现在可以构建一个新的有效叶子(具有委托所有权的有效 witness),要么创建一个新的根资产树(如果他们不拥有资产集合沿袭的任何实例),要么将其添加到现有树中。 将此称为 `asset_leaf_i`。 这个新的叶子**必须**正确引用一个有效的 `prev_asset_input` 才能成为一个有效的 witness。
3. 内部转移完成后,执行最终的外部转移:
   1. 将一组输入(可能在资产类型方面是异构的)添加到新的版本 `2` 比特币交易。
   2. 交换一组新的所有权输出并将其添加到 MIMO 转移交易中。 每个参与方必须至少在交易中存在一个新的所有权输出。
   3. 对于每个 Taproot Asset 标记的输出 _i_,_转移_资产:
      1. 发送者向接收者传输一个新的输出中根植的非包含证明,证明所讨论的资产不再在其资产树中承诺。
      2. 发送者构建一个新的有效 taproot 输出脚本,包括更新的资产树根,并将其发送给接收者进行验证。
   4. 对于每个_接收_资产的所有权输出:
      1. 接收者构建一个新的有效 taproot 输出脚本,该脚本可证明地提交到新的 `asset_leaf_i` 片段。
4. 外部转移通过交换每个输入的有效比特币输入 witness 集合来批准。

在执行最后一步之后,一方或双方都可以广播交易,以在主链中批准该交易。 此外,每个参与方还可以附加到其各自的资产证明文件(在验证过程中已完整传输),或者如果在他们转移了资产,则将其删除。

在使用用于转移的活动 Universe 的情况下,则只需要进行内部转移过程,并将新的证明上传到 Universe,以便在链中盖章。

上面的转移过程也可以通过以下伪代码片段来表示,该片段指定由 Alice 和 Bob 两个参与方拥有的一系列资产的转移(完整交换):

verify_asset_input_proofs(asset_proofs: map[AssetPrevID]AssetLeafProof) -> bool

for prev_asset_input, asset_leaf in asset_proofs
    prev_outpoint, prev_asset_id, prev_asset_script_key = prev_asset_input

    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, prev_asset_id):
                fail

transfer_assets(sender_assets: map[AssetID]AssetLeaf, receiver_scripts: [[32]byte]) -> map[AssetID]AssetLeaf

new_assets = {}
for prev_asset_input, 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_input = prev_asset_input
    new_leaf.asset_witness = gen_witness(tlv_encode(new_leaf))

    new_assets[prev_asset_input.prev_asset_id] = new_leaf

return new_assets

taproot_asset_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_taproot_asset_root = alice_compute_root(remove=alice_input_assets)
if not verify_non_inclusion(alice_new_taproot_asset_root, bob_new_assets):
  fail

bob_new_taproot_asset_root = bob_compute_root(remove=bob_input_assets)
if not verify_non_inclusion(bob_new_taproot_asset_root, alice_new_assets):
  fail

transfer_tx = new_tx()
for prev_asset_input, _ in list(chain(alice_input_assets, bob_inputs_assets)):
    transfer_tx.add_txin(prev_asset_input.prev_outpoint)

transfer_tx.add_output(taproot_output_script(key=alice_internal_key, leaves=[alice_new_taproot_asset_root]))
transfer_tx.add_output(taproot_output_script(key=bob_internal_key, leaves=[bob_new_taproot_asset_root]))

return transfer_tx

上面的方法生成一个完全签名的比特币交易,当广播时,将**原子地**将 Alice 的一组收藏品资产换成 Bob 的一组收藏品资产,Alice 额外支付 1 BTC 给 Bob 以满足其转移的条件。

###### 普通资产转移

[Permalink: 普通资产转移](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#normal-asset-transfers)

普通资产的转移与资产收藏品的转移几乎相同。 主要区别在于,在此过程中,双方还需要验证任何输入资产的正确拆分和合并。

例如,假设 Alice 拥有 10 个单位的资产 Foo,并希望将其中的 9 个单位转移给 Bob。 当生成资产 witness 以转移资产时,Alice 的 witness 会提交到一个**新的** merkle-sum 拆分树,证明她的新叶子是该树的成员,并且该树仍然只提交 10 个单位的资产。 Alice 可能不知道最终结构,但添加了一个额外的约束,即存在此承诺。 只有满足此条件,普通资产的有效转移才是有效的。

上面的示例演示了如何验证**外部拆分**(跨不同 taproot 输出的拆分)。 除此之外,我们还需要验证是否未在资产承诺中创建任何新资产。 我们使用 SMT 的 merkle-sum 特征来验证这一点。

话虽如此,普通资产的转移与收藏品资产的转移相同,但有以下补充:

1. 内部转移验证:
   1. 对于每个资产 _i_ 和要交换的数量 _n_:
      1. 发送者传输一个有效的 MS-SMT merkle 证明,证明资产的存在,以及要转移的数量的真实性。
      2. 接收者创建一个新的 `asset_script_key_i` 和 `asset_leaf_i`,与通常一样,但添加了一个约束,即新资产的 `amt` 字段**必须**与要转移的数量匹配。
      3. 当为给定的资产输入生成有效的 `asset_witness` 时,发送者**必须**还构造一个新的 MS-SMT 树,其密钥为 `sha256(output_index || asset_id || asset_script_key)`,其值为正在创建的资产叶子(序列化时没有此字段)。 这将是变更输出的 `split_commitment_root`。
2. 外部转移:
   1. 对于每个所有权输出 _i_,转移 _n_ 个单位的资产 _y_:
      1. 发送者不再需要传输完整的非包含证明,而是打开其变更输出的 Taproot Asset 根承诺,从而允许接收者验证承诺了数量 `t-n`,其中 `t` 是先前的资产根总和。
      2. 对于与输入拆分关联的每个新资产叶子,请验证序列化的叶子是否是变更输出中 `split_commitment_root` 的成员。

请注意,对于交易中的给定输入 `asset_id`,任何生成的内部拆分都将共享相同的输入 witness(间接引用)以及 `split_commitment_root`。
以下伪代码例程定义了创建和验证 `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

##### 常规资产转移

[固定链接:常规资产转移](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#normal-asset-transfers-1)

常规主动资产转移是单边转移,其中只有一方将集合或常规资产传输给另一方。因此,不需要 MIMO 交易,因为给定有问题的资产、要转移的金额以及所需的 `asset_script_key` 以及公钥,可以构造一个新的资产根及其对应的 Taproot 输出。

非交互式转移引入了链上 Taproot Asset **地址**的概念。有关地址格式的更多详细信息,请参见 [bip-tap-addr](https://github.com/Roasbeef/bips/blob/bip-tap/bip-bap-addr.mediawiki)。

给定一个有效的 Taproot Asset 地址,如果 Alice 希望使用 Taproot Asset 地址 _C_ 将 _N_ 个单位的资产 _Y_ 转移给 Bob:

- Alice 使用 Taproot Asset 地址来推导构造一个有效的叶子,以及一个新的外部 Taproot 输出,如 [BIP-341](https://github.com/Roasbeef/bips/blob/master/bip-0341.mediawiki) 所规定的。
- Alice 在她的资产树中构造一个新的子承诺,现在提交给 `T-N` 个单位的资产(在正常资产的情况下)。
- Alice 签名并广播包含两个输出的新交易,必要的 _K_ satoshis 的存款金额。

Alice 的内部转移不包括 Bob 的新资产叶子的 `prev_asset_input` 也不包括资产见证。相反,Bob 将使用引用的 `split_commitment_root` 创建一个 `split_commitment_proof`,以断言金额 `N` 的新拆分的存在。此转移有效,因为 Alice 的资产见证的“sig hash”也涵盖了 Bob 的新资产承诺的结构。

对于非交互式转移,Bob 需要从某个地方获得此证明,但可以完全根据 Alice 的非变更输出的最新证明来重建它。如果 Alice 希望在链上将此数据传输给 Bob,则她可以将数据放置在 Taproot 当前未使用的 annex 字段中。

就这样。Alice 可以构造 Bob 在链中寻找的预期根 Taproot Asset 承诺。这使得该方案对轻客户端友好,因为 neutrino 节点可以简单地在过滤器中查找生成的 Taproot 输出。“存款”金额是必要的,因为 Bitcoin 不允许零值输出。这个金额只需要略高于 dust,并且可以被视为一种固定的转账费用。

为了能够**花费**该资产,Bob 需要获得用作输入的完整资产证明,该证明可以从相关的 Universe 或直接从 Alice 那里获得。

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])


为了能够**花费**这个新资产,Bob 需要获得有关该资产的完整来源证明。假设 Bob 能够找到链上的转移交易(使用诸如 BIP 157/158 之类的系统),Bob 知道先前存储资产的输入。给定一个已知的 Universe,Bob 可以查找先前的 outpoint,然后将他的新叶子信息附加到文件的末尾。

##### Taproot 资产文件 & 叶子验证

[固定链接:Taproot 资产文件 & 叶子验证](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#taproot-asset-files--leaf-verification)

Taproot 资产文件可用于密封地存储和传输给定资产的来源证明。平面文件是主 Bitcoin 链内以及保存资产承诺的 Taproot 输出内的一系列 Merkle 证明。为了验证资产的有效性和来源,验证者在资产交易图中向后(或向前)移动,沿途验证每个承诺和资产见证状态转换。

Taproot 资产证明文件格式在 [./bip-tap-proof-file.mediawiki](https://learnblockchain.cn/article/18218) 中指定。证明文件的未来迭代也可能提交到每个单独证明段的根仅附加 Merkle 树根。这将允许对资产的来源进行概率验证,从而降低第三方和潜在资产接收者的验证成本。

#### 资产 Universes

[固定链接:资产 Universes](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#asset-universes)

资产的验证还必须验证资产的来源,即该资产是创建资产本身的初始 genesis outpoint 的间接后代。由于所有资产都由其来源定义,因此未能验证资产的历史会使整个系统的目的失效。例如,Bitcoin 链被定义为最终链接回主 genesis 区块的所有区块,如果链不包括 genesis 区块,那么它就不是 Bitcoin。

Universe 概念是一个链上(以及链下)的 MS-SMT 索引,它索引到主链中,索引揭示的证明集,将其锚定回主链。Universes 用于引导资产的来源,也可以迭代以(重新)构建资产的证明文件。

Universes 在 [./bip-tap-universe.mediawiki](https://learnblockchain.cn/article/18213) 中完全指定。

#### 多跳 Taproot 资产转移

[固定链接:多跳 Taproot 资产转移](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#multi-hop-taproot-asset-transfer)

利用嵌入式资产 HTLC/PTLC 构造,我们可以扩展 Lightning Network 以支持任意资产的多跳转移,并将网络的 Bitcoin 主干用作与资产无关的货币传输网络。多跳支付的发送者和接收者只需要了解正在转移的资产。网络的内部主干只看到等效的产生的 bitcoin 流动。

假设 Bob 拥有 `N` beefbux 的出站流动性,而 Alice 拥有 `M` beefbux 的入站流动性(其中 `N>M`),那么 Bob 可以向 Alice 发送 `M` beefbux。转移的第一跳接收 `M+f` beefbux(其中 `f` 是他们的费用),并发送 `K = M/B` BTC,其中 `B` 是商定的/广告的 beefbux/BTC 汇率。转移的所有最后一跳都接收 `K` BTC 并向 Alice 发送 `M` beefbuf(在实际路由中,由于费用,这将更少)。

通过上述构建,可以扩展闪电网络以支持任意资产的转移,BTC 有效地充当“gas 资产”。

闪电网络 (BOLT) 扩展的完整详细信息在 bLIP ??? 中指定。

### 应用

[固定链接:应用](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#applications)

### 测试向量

[固定链接:测试向量](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#test-vectors)

[资产叶格式](https://github.com/Roasbeef/bips/blob/bip-tap/Asset%20Leaf%20Format) 的测试向量可以在这里找到:

- [资产 TLV 编码测试向量](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap/asset_tlv_encoding_generated.json)
- [资产 TLV 编码错误测试向量](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap/asset_tlv_encoding_error_cases.json)
- [资产销毁密钥测试向量](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap/asset_burn_key_generated.json)

测试向量由 **Taproot Assets GitHub 存储库中的单元测试** 自动生成。

### 向后兼容性

[固定链接:向后兼容性](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#backwards-compatibility)

### 致谢

[固定链接:致谢](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#acknowledgement)

感谢 Peter Todd 多年来推广了 Taproot 资产设计中使用的许多想法和技术。该系统设计的组成部分受到了他早期在 proofchains 等系统上的工作的启发。

感谢 Giacomo Zucco、Alekos Filini 和 Maxim Orlovsky(以及 RGB 设计师的其他成员)开创了使用客户端验证技术来创建资产发行协议的概念。

感谢 Daniel Stadulis 阅读了本草案的早期版本,并在早期的设计讨论中塑造了设计的许多组成部分。

### 参考实现

[固定链接:参考实现](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki#reference-implementation)

github.com/lightninglabs/taproot-assets

>- 原文链接: [github.com/Roasbeef/bips...](https://github.com/Roasbeef/bips/blob/bip-tap/bip-tap.mediawiki)
>- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Roasbeef
Roasbeef
江湖只有他的大名,没有他的介绍。