BIP174 定义了 PSBT
标准, 全称是 Partially Signed Bitcoin Transaction Format
, 是一种用于交易的部分签名格式, 便于在不同参与者之间传递和更新比特币交易信息, 而无需将私钥暴露给其他参与者。
PSBT 结构
PSBT 结构由 4 部分组成:
Magic
- 固定为 0x70736274FF
Global Map
- 全局信息, 用于存储交易的全局信息, 如版本号、锁定时间等
Input Map
- 多个输入信息
Output Map
- 多个输出信息
<psbt> := <magic> <global-map> <input-map>* <output-map>*
<magic> := 0x70 0x73 0x62 0x74 0xFF
<global-map> := <keypair>* 0x00
<input-map> := <keypair>* 0x00
<output-map> := <keypair>* 0x00
除 Magic
外, 其他部分都是以 keypair
开头, 0x00
结尾。
keypair
是键值对字节数据, 由 key
和 value
组成。
<keypair> := <key> <value>
<key> := <keylen> <keytype> <keydata>
<value> := <valuelen> <valuedata>
key
是一个字节序列, 包括
keylen
- 格式为 Compact Size
类型, 表示 keytype
和 keydata
总字节长度
keytype
- 表示 key
的类型
keydata
- key
数据
value
是一个字节序列, 包括
valuelen
- 格式为 Compact Size
类型, 表示 valuedata
的长度
valuedata
- valuelen
字节的数据
每一部分具体的 key-value
见下表:
PSBT结构
类别 | 描述 |
---|
magic | 0x70736274FF , 0x70736274 转换成字符串为PSBT , 表示 PSBT 的开始, 0xFF 为分隔符 |
Global | 字段名 | keytype | keydata | keydata 描述 | valuedata | valuedata 描述 |
---|
Unsigned Transaction | 0x00 | - | - | - | 原始交易数据, 每个输入中的 scriptSig 和 witness 必须为空 | Extended Public Key | 0x01 | xpub | | 4字节主拓展秘钥指纹 + 32 字节路径 | BIP 32 定义的主拓展密钥指纹和公钥的导出路径 | Transaction Version | 0x02 | - | - | 4字节小端序 | 交易版本号 | Locktime | 0x03 | - | - | 4字节小端序 | 交易 Locktime | Input Count | 0x04 | - | - | Compact Size | 交易输入数量 | Output Count | 0x05 | - | - | Compact Size | 交易输出数量 |
|
Inputs | 字段名 | keytype | keydata | keydata 描述 | valuedata | valuedata 描述 |
---|
Non-Witness UTXO | 0x00 | - | - | 交易数据 | 花费非隔离见证 UTXO 时, 提供的 UTXO 所在交易的完整交易数据 | Witness UTXO | 0x01 | - | - | 8字节金额 + 锁定脚本长度 + 锁定脚本 | | Partial Signature | 0x02 | 公钥 | 与签名对应的公钥 | 签名数据 | 部分签名 | Sighash Type | 0x03 | - | - | 4字节小端序 | Sighash 类型 | Redeem Script | 0x04 | - | - | 赎回脚本 | P2SH 自定义的赎回脚本, 位于交易输入的 scriptSig 字段中 | Witness Script | 0x05 | - | - | 赎回脚本 | P2WSH 自定义的赎回脚本, 位于交易输入对应的 witness 字段中 | Final ScriptSig | 0x07 | - | - | 解锁脚本 | 最终的 scriptSig 解锁脚本 | Final ScriptWitness | 0x08 | - | - | 解锁脚本 | 最终的 scriptWitness 解锁脚本 | Previous TXID | 0x0e | - | - | 32字节 TXID | 花费的UTXO所在的交易id | Spent Output Index | 0x0f | - | - | 4字节小端序 | 花费的UTXO在其交易中的索引 | Sequence Number | 0x10 | - | - | 4字节小端序 | 交易输入的序列号 | Taproot Key Spend Signature | 0x13 | - | - | 签名数据 | Taproot 秘钥路径花费的签名 | Taproot Script Spend Signature | 0x14 | 32字节 Taproot 公钥 X 坐标 + leafhash | - | 签名数据 | Taproot 脚本路径花费的签名 | Taproot Leaf Script | 0x15 | 控制块 | BIP341 规定的脚步路径花费时, 提供的控制块是内部公钥和默克尔证明的组合。 | 脚本 | 花费的叶子的完整脚本 | Taproot Key BIP 32 Derivation Path | 0x16 | 32字节 Taproot 公钥 X 坐标 | - | - | - | Taproot Internal Key | 0x17 | - | - | 32字节内部公钥的X坐标 | 内部公钥 | Taproot Merkle Root | 0x18 | - | - | 32字节默克尔根 | |
|
Outputs | 字段名 | keytype | keydata | keydata 描述 | valuedata | valuedata 描述 |
---|
Redeem Script | 0x00 | - | - | 赎回脚本 | P2SH 自定义脚本 | Witness Script | 0x01 | - | - | 赎回脚本 | P2WSH 自定义脚本 | Output Amount | 0x03 | - | - | 8字节金额 | 输出金额 | Output Script | 0x04 | - | - | 输出脚本 | 输出脚本 | Taproot Internal Key | 0x05 | - | - | 32字节公钥 | Taproot 内部公钥 | Taproot Tree | 0x06 | - | - | (1字节深度 + 1字节叶子版本 + 脚本长度 + 脚本)* | Taproot 脚本树, 按照深度构建脚本树 |
|
流程
假设现在有三个用户, 分别为 Alice, Bob 和 Carol。
Alice 创建了一个 PSBT
Alice 创建的 PSBT
交易中, 包含了两个输入和两个输出, 这两个输入分别只能由 Bob 和 Carol 可以生成有效签名
之后 Alice 将 PSBT
序列化后的数据同时传递给 Bob 和 Carol, 如果他们同意该花费可以使用对应的私钥对交易输入生成有效的签名。
Bob 和 Carol 分别完成对 PSBT
交易的第 0 和第 1 个输入签名, 并将签名后的 PSBT
发送给 Alice, Alice 接收到后可以将两个签名合并到一起。
在调用 finalizeAllInputs
前, 如果打印交易输入信息, 可以看到 PSBT
交易中的签名信息。
partialSig
用于存储部分签名信息, 为数组结构, 每个元素包含 pubkey
和 signature
字段。
对于多签的交易输入, 则 partialSig
会有多个元素。
而在调用 finalizeAllInputs
后, PSBT
交易中的签名信息会被最终定稿, 此时打印交易输入信息。
finalScriptSig
和 finalScriptWitness
为最终的解锁脚本, 被放置在交易输入中的 scriptSig
和 witness
字段中。
PSBT 为需要多方参与的交易提供了一种标准的数据交换格式。但同样也可以用于单方签名的交易。
多签
假设 Alice 将一个 2-of-3 P2MS
作为交易输入。要使交易有效, 则至少需要 Alice、Bob 和 Carol 中的两个人签名。
在 Alice 创建完 PSBT
后并签名后, 将其序列化数据传递给 Bob
和 Carol
。
Bob
和 Carol
接收到 PSBT
数据后, 只要有一方同意花费, 则可以签名。
最后 Alice 合并签名, 并提取交易, 最终发送到比特币网络。