Taproot Asset 状态转换的 PSBT 格式

本文档描述了在Taproot Asset状态转换交易的[[https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki|Partially Signed Bitcoin Transaction (PSBT, BIP-0174)]]格式中使用的自定义字段。

<pre> BIP: ??? Layer: Applications Title: Taproot Assets PSBT Author: Oliver Gugger <gugger@gmail.com> Olaoluwa Osuntokun <laolu32@gmail.com> Comments-Summary: No comments yet. Comments-URI: https://git Status: Draft Type: Standards Track Created: 2023-02-24 License: BSD-2-Clause </pre>

==摘要==

本文档描述了用于 Taproot Asset 状态转换交易的 [[https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki|Partially\ Signed Bitcoin Transaction (PSBT, BIP-0174)]] 格式中的自定义字段。

==版权==

本文档基于 2-clause BSD 许可。

==设计==

Taproot Asset 状态转换交易也称为“虚拟交易”或资产转移交易。“虚拟”一词用于区分这些仅发生在链下资产覆盖上下文中的资产转移,而不是“锚定”交易,后者是将多个资产级转移提交到比特币链的 BTC 级别链上交易。

虚拟交易与其 Bitcoin wire transaction(比特币链上交易)对应项有许多相似之处,因为它花费一个或多个资产输入(资产 UTXO 或资产“币”),并创建一个或多个新的资产输出。

与比特币交易的主要区别在于,虚拟资产交易不会将满足每个输入的前一个输出脚本的见证放在输入本身中,而是使用专门输出的 <code>prev_asset_witnesses</code> 字段,该输出容纳 split root asset(拆分根资产)。这使得一个 many-in-many-out(多入多出)虚拟交易可以被压缩成一个 1-in-1-out(单入单出)交易(如在 [[./bip-tap-vm.mediawiki|bip-tap-vm]] 中所述),以便在 Taproot Asset Virtual Machine(Taproot 资产虚拟机)中进行验证。

为了组装满足每个输入的前一个输出脚本的完整见证堆栈,交易可能需要由多个参与者和/或设备签名。这要求虚拟交易在多个参与者之间传递,每个参与者都向其中添加自己的部分(例如,签名)。鉴于虚拟交易与现有比特币交易的相似性,PSBT 格式被选为虚拟资源状态转移的交换格式,并且具有一组新的 PSBT <code><keytype></code>,如本文档中所定义。

==规范==

一个虚拟交易只能包含彼此之间可互换的资产的输入和输出。如果资产具有相同的 genesis ID(在同一 tranche 中铸造)或引用相同的 <code>group_key</code>(在不同的 tranche 中铸造),则认为资产是可互换的。

在一个虚拟交易中,具有相同 genesis ID的多个输入(资产币)可以像合并和拆分比特币输入一样进行合并和拆分。具有不同 genesis ID(但相同的 <code>group_key</code>)的可互换资产可以在同一个虚拟交易中一起使用,以满足付款请求,但是不允许将两个可互换的资产(具有不同的资产 ID)合并到 同一个 资产 UTXO 中。有关更多详细信息,请参见 [[./bip-tap-vm.mediawiki|bip-tap-vm]]。

虚拟交易输出的构造必须遵循不同的规则,具体取决于接收方是以交互方式还是非交互方式接收输出: <ul> <li> 交互式:接收方将看到完整的虚拟交易(例如,作为 PSBT),包括完全签名的新资产叶。他们拥有所有必需的信息,能够完全构造 BTC 级别的锚定输出 Taproot 密钥(包括完整的资产见证以及可能在单个 BTC 级别的锚定输出中提交的多个资产叶),以观察链上的入站转移。因此,在完全发送叶的场景中(完全价值发送),不需要“拆分墓碑”(见下文)。 </li> <li> 非交互式:接收方已创建 Taproot Asset 地址,并且仅在链上监视该 Taproot 输出密钥。资产必须作为拆分资产提交到树中,并在序列化之前修剪拆分承诺,以使接收方可以完全预测生成的 Taproot Asset 树。拆分承诺和根资产证明将在证明文件中交付。完全价值发送也必须创建为拆分,发送者创建一个零价值的“拆分墓碑”输出,并将 NUMS 点作为脚本密钥(见下文)。 </li> </ul>

TODO(guggero): 一旦定义了,请描述如何以非交互方式处理可互换的资产。

在拆分资产 UTXO 时,会创建一个拆分承诺(如 [[./bip-tap-vm.mediawiki|bip-tap-vm]] 中所述)。拆分根放置在其中一个输出中(通常是返回给资产发送者的找零输出),并带有 IsSplitRoot 标志。如果拆分的剩余找零为 0,则拆分根资源输出的 <code>script_key</code> 应该是众所周知的 NUMS 点(使用字符串“taproot-assets”和传统的“hash and increment”方法来生成该点),以证明该输出无法进一步花费。这种所谓的“拆分墓碑”是完整发送币值的非互动式发送(发送到 Taproot Asset 地址)所必需的。在交互式场景中,可以修剪这些零值墓碑输出,在这种情况下,接收人知道完整的根资产叶(包括 TX 见证),并且可以正确构造承诺树根。

==Taproot Asset 虚拟交易的自定义 PSBT 字段==

[[https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki|BIP-0174]] 大致定义了 6 个全局、24 个输入和 7 个输出 <code>keytype</code>。这为新的 BIP 指定其他类型留出了足够的空间,而不会造成很大的冲突风险。专有类型 <code>0xFX</code> 不适用于这种情况,因为它适用于应用程序/供应商特定的数据,而不是 BIP 中声明的字段。

为了进一步降低与其他(正在进行的)BIP 的密钥类型冲突的风险,我们从每个新密钥类型部分中的(任意选择的)值 <code>0x70</code> 开始。

===全局类型===

{ ! 名称 ! <tt><keytype></tt> ! <tt><keydata></tt> ! <tt><keydata></tt> 描述 ! <tt><valuedata></tt> ! <tt><valuedata></tt> 描述
虚拟交易标记
<tt>PSBT_GLOBAL_TAP_IS_VIRTUAL_TX = 0x70</tt>
无密钥数据
<tt><byte 0x01></tt>
<code>0x01</code> 的静态标记,用于将此交易标识为 Taproot Asset 虚拟交易。
-
Taproot Asset 链 HRP
<tt>PSBT_GLOBAL_TAP_CHAIN_HRP = 0x71</tt>
无密钥数据
<tt><string HRP></tt>
Taproot Asset 链标识符的人类可读前缀,如 [[./bip-tap-addr.mediawiki bip-tap-addr]] 中所指定。
-
Taproot Asset PSBT 版本
<tt>PSBT_GLOBAL_TAP_PSBT_VERSION = 0x72</tt>
无密钥数据
<tt><byte version></tt>
Taproot Asset PSBT 格式的版本。目前,<code>0x00</code> 是唯一已知和支持的版本。
}

===输入类型===

{ ! 名称 ! <tt><keytype></tt> ! <tt><keydata></tt> ! <tt><keydata></tt> 描述 ! <tt><valuedata></tt> ! <tt><valuedata></tt> 描述
前一个资产叶
<tt>PSBT_IN_TAP_PREV_ID = 0x70</tt>
无密钥数据
<tt><tlv_blob prev_asset_id></tt>
前一个资产叶(由 <code>prev_outpoint asset_id asset_script_hash</code> 标识),采用 TLV 格式,如 [[./bip-tap.mediawiki#asset-leaf-format bip-tap 资产叶格式]] 中所定义。
-
锚定输出值
<tt>PSBT_IN_TAP_ANCHOR_VALUE = 0x71</tt>
无密钥数据
<tt><64-bit big endian int value></tt>
BTC 级别的锚定输出的 satoshis 值,该锚定输出提交了要花费的资产输入。
-
锚定输出 <code>pkScript</code>
<tt>PSBT_IN_TAP_ANCHOR_PK_SCRIPT = 0x72</tt>
无密钥数据
<tt><bytes pkScript></tt>
BTC 级别的锚定输出的 <code>pkScript</code>,该锚定输出提交了要花费的资产输入。
-
锚定输出 Sighash 类型
<tt>PSBT_IN_TAP_ANCHOR_SIGHASH_TYPE = 0x73</tt>
无密钥数据
<tt><64-bit big endian int sighash type></tt>
指定用于 BTC 级别锚定输出的 sighash 类型的 64 位大端无符号整数,该锚定输出提交了要花费的资产输入。
-
锚定输出 Taproot 内部密钥
<tt>PSBT_IN_TAP_ANCHOR_TAP_INTERNAL_KEY = 0x74</tt>
无密钥数据
<tt><32-byte xonlypubkey></tt>
用作 BTC 级别锚定输出的内部密钥的 X-only pubkey,该锚定输出提交了要花费的资产输入。
-
锚定输出 Taproot Merkle 根
<tt>PSBT_IN_TAP_ANCHOR_TAP_MERKLE_ROOT = 0x75</tt>
无密钥数据
<tt><32-byte hash></tt>
BTC 级别锚定输出的 32 字节 Merkle 根哈希,该锚定输出提交了要花费的资产输入。
-
锚定输出 BIP-0032 派生路径
<tt>PSBT_IN_TAP_ANCHOR_BIP32_DERIVATION = 0x76</tt>
<tt><bytes pubkey></tt>
公钥
<tt><4 byte fingerprint> <32-bit little endian uint path element>*</tt>
主密钥指纹,如 BIP-0032 所定义,与用于 BTC 级别锚定输出的公钥的派生路径连接,该锚定输出提交了要花费的资产输入。派生路径表示为彼此连接的 32 位无符号整数索引。公钥是签署此输入所需的公钥。
-
锚定输出 Taproot 密钥 BIP 32 派生路径
<tt>PSBT_IN_TAP_ANCHOR_TAP_BIP32_DERIVATION = 0x77</tt>
<tt><32 byte xonlypubkey></tt>
此输入中涉及的 32 字节 X-only 公钥。它可以是输出密钥、内部密钥或叶脚本中存在的密钥。
<tt><compact size uint number of hashes> <32 byte leaf hash>*
<4 byte fingerprint> <32-bit little endian uint path element>*</tt> 一个压缩大小的无符号整数,表示叶哈希的数量,后跟叶哈希列表,后跟 4 字节主密钥指纹与公钥的派生路径连接。派生路径表示为彼此连接的 32 位小端无符号整数索引。公钥是花费此输出所需的公钥。叶哈希是涉及此公钥的叶的哈希。内部密钥没有叶哈希,因此可以用 <tt>hashes len</tt> 为 0 表示。在构造 <tt>PSBT_IN_FINAL_SCRIPTWITNESS</tt> 后,Finalizers 应删除此字段。
锚定输出 Tapscript 同级
<tt>PSBT_IN_TAP_ANCHOR_TAPSCRIPT_SIBLING = 0x78</tt>
无密钥数据
<tt><byte sibling_type><compact size num_bytes><bytes tapscript sibling preimage></tt>
与 Taproot Asset 承诺位于同一级别的 tapscript 同级的原像,该承诺已提交到锚定。如果不存在,则 Taproot Asset 承诺是树中唯一的脚本叶。
-
Taproot Asset 资产
<tt>PSBT_IN_TAP_ASSET = 0x79</tt>
无密钥数据
<tt><tlv_blob asset></tt>
要花费的完整输入资产叶,采用 TLV 格式,如 [[./bip-tap.mediawiki#asset-leaf-format bip-tap 资产叶格式]] 中所定义。
-
Taproot Asset 证明
<tt>PSBT_IN_TAP_ASSET_PROOF = 0x7a</tt>
无密钥数据
<tt><tlv_blob proof></tt>
要花费的输入资产的最后一个证明,采用 TLV 格式,如 [[./bip-tap-proof-file.mediawiki#file-serialization \

bip-tap-proof-file 文件序列化]] 中所定义。 |}

===输出类型===

{ ! 名称 ! <tt><keytype></tt> ! <tt><keydata></tt> ! <tt><keydata></tt> 描述 ! <tt><valuedata></tt> ! <tt><valuedata></tt> 描述
类型
<tt>PSBT_OUT_TAP_TYPE = 0x70</tt>
无密钥数据
<tt><byte output_type></tt>
一个 <code>uint8</code> 值,指示虚拟输出的类型。有效值为:0x00 (<tt>Simple</tt>)、0x01 (<tt>SplitRoot</tt>)、0x02 (<tt>PassiveAssetsOnly</tt>) 和 0x03 (<tt>PassiveSplitRoot</tt>),请参见下面的描述。
-
是交互式的
<tt>PSBT_OUT_TAP_IS_INTERACTIVE = 0x71</tt>
无密钥数据
<tt><byte 0x00/0x01></tt>
一个布尔值,指示输出的接收者是否知道他们正在接收的完整资产叶 (=交互式),还是不知道 (=非交互式)。在非交互式情况下,接收者将期望此输出为拆分输出。
-
锚点输出索引
<tt>PSBT_OUT_TAP_ANCHOR_OUTPUT_INDEX = 0x72</tt>
无密钥数据
<tt><64-bit big endian int value></tt>
此资产输出将要提交到的比特币级别锚定交易输出索引。
-
锚定输出 Taproot 内部密钥
<tt>PSBT_OUT_TAP_ANCHOR_TAP_INTERNAL_KEY = 0x73</tt>
无密钥数据
<tt><32-byte xonlypubkey></tt>
用作将提交到资产输出的 BTC 级别锚定输出的内部密钥的 X-only pubkey。
-
锚定输出 BIP-0032 派生路径
<tt>PSBT_OUT_TAP_ANCHOR_BIP32_DERIVATION = 0x74</tt>
<tt><bytes pubkey></tt>
公钥
<tt><4 byte fingerprint> <32-bit little endian uint path element>*</tt>
主密钥指纹,如 BIP-0032 所定义,与将用于 BTC 级别锚定输出的公钥的派生路径连接,该锚定输出将提交到资产输出。派生路径表示为彼此连接的 32 位无符号整数索引。公钥是签署此输入所需的公钥。
-
锚定输出 Taproot 密钥 BIP-0032 派生路径
<tt>PSBT_OUT_TAP_ANCHOR_TAP_BIP32_DERIVATION = 0x75</tt>
<tt><32 byte xonlypubkey></tt>
此输入中涉及的 32 字节 X-only 公钥。它可以是输出密钥、内部密钥或叶脚本中存在的密钥。
<tt><compact size uint number of hashes> <32 byte leaf hash>*
<4 byte fingerprint> <32-bit little endian uint path element>*</tt> 一个压缩大小的无符号整数,表示叶哈希的数量,后跟叶哈希列表,后跟 4 字节主密钥指纹与公钥的派生路径连接。派生路径表示为彼此连接的 32 位小端无符号整数索引。公钥是花费此输出所需的公钥。叶哈希是涉及此公钥的叶的哈希。内部密钥没有叶哈希,因此可以用 <tt>hashes len</tt> 为 0 表示。在构造 <tt>PSBT_IN_FINAL_SCRIPTWITNESS</tt> 后,Finalizers 应删除此字段。
Taproot Asset
<tt>PSBT_OUT_TAP_ASSET = 0x76</tt>
无密钥数据
<tt><tlv_blob asset></tt>
要创建的完整输出资产叶,采用 TLV 格式,如 [[./bip-tap.mediawiki#asset-leaf-format bip-tap 资产叶格式]] 中所定义。
-
Taproot Asset 拆分资产
<tt>PSBT_OUT_TAP_SPLIT_ASSET = 0x77</tt>
无密钥数据
<tt><tlv_blob split_asset></tt>
如果在 <tt>PSBT_OUT_TAP_ASSET</tt> 中序列化的资产是拆分根 (<tt>PSBT_OUT_TAP_IS_SPLIT_ROOT=0x01</tt>),则此字段包含在包含拆分承诺见证的根定位符处创建的拆分资产。这仅用于验证,并且未在任何树中提交。如果存在,则拆分资产以 TLV 格式编码,如 [[./bip-tap.mediawiki#asset-leaf-format bip-tap 资产叶格式]] 中所定义。
-
锚定输出 Tapscript 同级
<tt>PSBT_OUT_TAP_ANCHOR_TAPSCRIPT_SIBLING = 0x78</tt>
无密钥数据
<tt><byte sibling_type><compact size num_bytes><bytes tapscript sibling preimage></tt>
与 Taproot Asset 承诺位于同一级别的 tapscript 同级的原像,该承诺已提交到锚定。如果不存在,则 Taproot Asset 承诺是树中唯一的脚本叶。
}

===<tt>PSBT_OUT_TAP_TYPE</tt> 的值===

<tt>PSBT_OUT_TAP_TYPE</tt> 字段描述了虚拟输出的类型。此类型会影响资产的签名方式,以及输出中是否存在资产。 定义了以下值: <ul> <li><b>Simple</b> (<tt>0x00</tt>) 是一个纯粹的完整值或拆分输出,它不是拆分根,也不携带被动资产。如果是拆分,则此输出的资产具有拆分承诺。 </li> <li><b>SplitRoot</b> (<tt>0x01</tt>) 是一个拆分根输出,它携带来自拆分的找零或来自非互动式全值发送输出的墓碑。在任何一种情况下,此输出的资产都具有 tx 见证。 </li> <li><b>PassiveAssetsOnly</b> (<tt>0x02</tt>) 表示此输出仅携带被动资产,因此此输出中的资产为 nil。被动资产本身在其自己的虚拟交易中签名,并且不在此数据包中。 </li> <li><b>PassiveSplitRoot</b> (<tt>0x03</tt>) 是一个拆分根输出,它携带来自拆分的找零或来自非互动式全值发送输出的墓碑,以及被动资产。 </li> </ul>

==将 Taproot Asset 虚拟交易提交到 BTC 级别的锚定交易中==

以上部分定义了表示虚拟资产交易的新字段。本节说明如何将一个或多个虚拟资产交易映射到单个比特币级别的链上“锚定”交易中并提交到其中。

应采取以下步骤,将一个或多个 Taproot Asset 虚拟交易提交到单个 BTC 锚定交易中:

按照 [[./bip-tap.mediawiki|bip-tap]] 中的描述签署虚拟交易。

验证每个虚拟交易中的每个输入是否已设置必要的锚定信息 (<code>PSBT_IN_TAPANCHOR*</code>)。

验证每个虚拟交易中的每个输出是否已设置必要的锚定信息 (<code>PSBT_OUT_TAPANCHOR*</code>)。

验证每个虚拟输入的锚定信息引用了同一个前一个输出点 (<code>PSBT_IN_TAP_PREV_ID.prev_outpoint</code>) 与所有字段相同,因为所有字段都应引用同一个链上的未花费输出。

验证每个虚拟输出的锚定信息引用了同一个锚定输出索引 (<code>PSBT_OUT_TAP_ANCHOR_OUTPUT_INDEX</code>) 与所有字段相同,因为所有字段都应引用同一个正在创建的链上输出。

对于每个输入,将输入资产的最后一个转换证明添加到 BTC 锚定 PSBT 输入 (字段 <code>PSBT_IN_TAP_PROOF</code>)。

对于每个唯一的锚定输出索引,在 BTC 锚定交易上创建一个输出:

将所有资产合并到单个 Taproot Asset 树中。

如果 BTC 锚定输出存在 Tapscript 同级,请验证它不是 Taproot Asset 承诺。

从合并的 Taproot Asset 树和可选的 Tapscript 同级计算 Merkle 根哈希。

从内部密钥和 Merkle 根哈希计算 Taproot 输出密钥,更新 BTC 锚定输出的 <code>pkScript</code>。

为每个虚拟交易输出创建单个转换证明,并将它们添加到 BTC 锚定输出,并按每个资产的 <code>script_key</code> 键控 (字段 <code>PSBT_OUT_TAP_PROOF</code>)。

一旦 BTC 级别的锚定交易附加了额外的信息,就可以将其传递给所有签名者,以生成必要的签名以使其最终确定。

==BTC 级别锚定交易的自定义 PSBT 字段==

===输入类型===

{ ! 名称 ! <tt><keytype></tt> ! <tt><keydata></tt> ! <tt><keydata></tt> 描述 ! <tt><valuedata></tt> ! <tt><valuedata></tt> 描述
Taproot Asset 证明
<tt>PSBT_IN_TAP_PROOF = 0x70</tt>
<tt><32 byte xonlyscriptkey></tt>
要花费的输入资产的 32 字节 X-only 脚本密钥。
<tt><tlv_blob proof></tt>
要花费的输入资产的最后一个证明,采用 TLV 格式,如 [[./bip-tap-proof-file.mediawiki#file-serialization bip-tap-proof-file 文件序列化]] 中所定义。
}

===输出类型===

{ ! 名称 ! <tt><keytype></tt> ! <tt><keydata></tt> ! <tt><keydata></tt> 描述 ! <tt><valuedata></tt> ! <tt><valuedata></tt> 描述
Taproot Asset 证明
<tt>PSBT_OUT_TAP_PROOF = 0x70</tt>
<tt><32 byte xonlyscriptkey></tt>
要花费的输入资产的 32 字节 X-only 脚本密钥。
<tt><tlv_blob proof></tt>
在此输出中创建的新资产的新转换证明。
}

==测试向量==

[[Custom PSBT fields for Taproot Asset virtual transactions]] 的测试向量可以在这里找到:

  • [[bip-tap-psbt/psbt_encoding_generated.json|PSBT 编码测试向量]]
  • [[bip-tap-psbt/psbt_encoding_error_cases.json|PSBT 编码错误测试向量]]

测试向量由 [https://github.com/lightninglabs/taproot-assets/tree/main/tappsbt Taproot Assets GitHub 存储库中的单元测试] 自动生成。

==参考实现==

github.com/lightninglabs/taproot-assets/tree/main/tappsbt

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

0 条评论

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