Skip to content

Commit a5e8ff1

Browse files
committed
Merge branch 'main' of github.com:Dapp-Learning-DAO/Dapp-Learning
2 parents 382f437 + 5162477 commit a5e8ff1

File tree

312 files changed

+10914
-6019
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

312 files changed

+10914
-6019
lines changed
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
## OP_CHECKMULTISIG
2+
3+
`OP_CHECKMULTISIG` 是比特币脚本语言中的一个操作码(opcode),用于实现多重签名验证。在比特币交易中,`OP_CHECKMULTISIG` 允许指定多个公钥,并要求至少有一定数量(M)的签名来验证交易。此功能广泛应用于多重签名钱包和联合管理的比特币地址,旨在增强比特币网络的安全性和灵活性。
4+
5+
### 背景
6+
7+
1. **早期比特币设计**
8+
- 比特币最初设计主要关注点是点对点的单一用户交易。
9+
- 简单的签名机制足以支持基本的比特币转账功能。
10+
11+
2. **安全需求增加**
12+
- 随着比特币的普及,越来越多的用户和组织开始使用比特币。
13+
- 对于大额资金或共享账户,单一签名的安全性不足以抵御潜在风险。
14+
15+
3. **多重签名需求**
16+
- 多重签名(multisig)技术允许多个签名者共同管理一个比特币地址,提高了安全性。
17+
- 特别适用于企业账户、家庭共享账户和其他需要联合管理的情境。
18+
19+
### 具体的 BIP
20+
21+
1. **BIP 11**
22+
- 标题:`M-of-N Standard Transactions`
23+
- 提案作者:Gavin Andresen
24+
- 提出时间:2012 年 3 月
25+
- 内容:描述了多重签名交易的标准格式,即 M 个签名中的 N 个签名验证通过即可执行交易。
26+
27+
2. **BIP 16**
28+
- 标题:`Pay to Script Hash`
29+
- 提案作者:Gavin Andresen
30+
- 提出时间:2012 年 1 月
31+
- 内容:定义了支付到脚本哈希(P2SH)的机制,使得复杂的脚本可以更简洁地在链上表示,从而增强了多重签名的可用性。
32+
33+
### 时间线
34+
35+
1. **2011 年至 2012 年初**
36+
- 多重签名的需求逐渐显现,社区开始讨论如何实现更安全的交易机制。
37+
38+
2. **2012 年 1 月**
39+
- Gavin Andresen 提出 BIP 16,描述了 P2SH 的机制,为多重签名交易的简化提供了基础。
40+
41+
3. **2012 年 3 月**
42+
- Gavin Andresen 提出 BIP 11,明确了 M-of-N 多重签名交易的标准格式,具体描述了如何在比特币脚本中实现多重签名验证。
43+
44+
4. **2012 年 5 月**
45+
- 比特币客户端(Bitcoin Core)集成了 P2SH 和多重签名功能,`OP_CHECKMULTISIG` 操作码开始被正式使用。
46+
47+
### 关键人物
48+
49+
1. **Gavin Andresen**
50+
- 比特币基金会的首席科学家,最早提出并推动了 P2SH 和多重签名交易的标准化。
51+
- 他是 BIP 11 和 BIP 16 的主要作者,对 `OP_CHECKMULTISIG` 的引入和实施起到了关键作用。
52+
53+
2. **比特币开发社区**
54+
- 包括许多开发者和贡献者,他们共同讨论、审查和改进了多重签名机制。
55+
- 社区的参与和支持确保了这些改进提案的顺利实施和推广。
56+
57+
### 工作原理
58+
59+
`OP_CHECKMULTISIG` 的基本原理是检查一组公钥和签名,确认至少有 M 个签名是有效的。具体来说,这个操作码会验证堆栈上的公钥和签名,并确保至少有 M 个签名与对应的公钥匹配。
60+
61+
### 操作步骤
62+
63+
1. **堆栈输入**
64+
- 签名数量(N)
65+
- N 个签名
66+
- 公钥数量(M)
67+
- M 个公钥
68+
69+
2. **执行过程**
70+
- 从堆栈中弹出签名数量(N)。
71+
- 弹出 N 个签名并存储。
72+
- 弹出公钥数量(M)。
73+
- 弹出 M 个公钥并存储。
74+
- 验证至少有 M 个签名能匹配对应的公钥。
75+
- 如果验证成功,脚本继续执行;否则,脚本失败。
76+
77+
### 示例脚本
78+
79+
假设我们有三个公钥,需要至少两个签名来验证交易,脚本如下:
80+
81+
```
82+
2 <PubKey1> <PubKey2> <PubKey3> 3 OP_CHECKMULTISIG
83+
```
84+
85+
在这个脚本中:
86+
- `2` 表示需要两个有效签名。
87+
- `<PubKey1> <PubKey2> <PubKey3>` 是三个公钥。
88+
- `3` 表示总共有三个公钥。
89+
- `OP_CHECKMULTISIG` 执行多重签名验证。
90+
91+
### 例子详解
92+
93+
假设 Alice、Bob 和 Charlie 共同管理一个地址,任何交易需要至少两人签名。我们生成一个多重签名地址,并创建一个 P2SH(Pay to Script Hash)交易。
94+
95+
1. **创建多重签名地址**
96+
97+
```
98+
2 <AlicePubKey> <BobPubKey> <CharliePubKey> 3 OP_CHECKMULTISIG
99+
```
100+
101+
2. **生成 P2SH 地址**
102+
将上述脚本的哈希作为地址,这样可以简化交易脚本并提高安全性。
103+
104+
3. **花费多重签名地址中的资金**
105+
- Alice 和 Bob 签名交易。
106+
- 将签名和原始脚本放入输入脚本中:
107+
108+
```
109+
0 <AliceSignature> <BobSignature> 2 <AlicePubKey> <BobPubKey> <CharliePubKey> 3 OP_CHECKMULTISIG
110+
```
111+
112+
### 注意事项
113+
114+
- **漏洞**:早期版本的 `OP_CHECKMULTISIG` 有一个“off-by-one”漏洞,要求在签名数量前添加一个额外的 `OP_0`
115+
- **效率**:验证签名和公钥需要计算资源,在高频交易场景中需要考虑其效率。
116+
117+
## 链上交易
118+
### P2SH-P2MS
119+
链上交易:https://mempool.space/zh/tx/45f862a3c72b659406c38f606e2bcc73145241c3bf5c42ded4d4737af8d401ad
120+
```js
121+
// ScriptSig
122+
<empty>
123+
OP_0
124+
OP_PUSHBYTES_71 3044022018a750079fc75b68f5208f86b146821d5f3acb41f68195f8b4cbf1012cba2cbb02200c3ddd0dc271342f62222603356dd7c713cf387718e57ec2644f7811295d03ea01
125+
OP_PUSHBYTES_72 30450221009a5914969675238ddfb9c1475d2888b3a5a507c6d13c35ac13a6f97a5c3dec97022055587f56408ba7abdc48d637d3eb03a3c29f05d7c22df9a20fd4bea739a00b2601
126+
OP_PUSHDATA1 52210231d0ed51696dff4a25015f2a8330d07b81761eda22cae558da1086b5d084e1922102e142c9123d48ce4c0b32403b19403e41129f1d37de852d9bbc95165af6960e3b2102207620c770dc24eb02edc6fba51b70fd0c04a2772842d808188b835a8f73832853ae
127+
128+
// P2SH redeem script
129+
OP_PUSHNUM_2
130+
OP_PUSHBYTES_33 0231d0ed51696dff4a25015f2a8330d07b81761eda22cae558da1086b5d084e192
131+
OP_PUSHBYTES_33 02e142c9123d48ce4c0b32403b19403e41129f1d37de852d9bbc95165af6960e3b
132+
OP_PUSHBYTES_33 02207620c770dc24eb02edc6fba51b70fd0c04a2772842d808188b835a8f738328
133+
OP_PUSHNUM_3
134+
OP_CHECKMULTISIG
135+
```
136+
137+
### P2WSH-P2MS
138+
链上交易:https://mempool.space/zh/tx/a085e629f5932e729f54f174c936f015a10bb7935db66d5803e81dbff0ee15cc
139+
```js
140+
// Witness
141+
<empty>
142+
304402204f63983ae7cdb18fc3b75d4acc8e74d35816d4a8660d4296da4c0f6a0206fdd90220347e8317c686aa3785905eb1ff1cf32c4ce7ef019678fdd9ad00cfceedd74c2e01
143+
30440220055d4a41afa73ef449b18add7748b054eec768a8350c5095b4f622546eb55a3f022073cea484b99331cc8ae1ebf6b1698d07b560ad01ee63e3a4a739684a0408aec601
144+
52210279d1f38c1c80d47cb00ddbbe2915a60d5706e1ef66056a169150f083b288eb952102cb7d02b654f8616bfc5ab017b7a3ec9092e466381af0f552b7efcd8d920453672103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae
145+
146+
// P2WSH witness script
147+
OP_PUSHNUM_2
148+
OP_PUSHBYTES_33 0279d1f38c1c80d47cb00ddbbe2915a60d5706e1ef66056a169150f083b288eb95
149+
OP_PUSHBYTES_33 02cb7d02b654f8616bfc5ab017b7a3ec9092e466381af0f552b7efcd8d92045367
150+
OP_PUSHBYTES_33 03c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f880
151+
OP_PUSHNUM_3
152+
OP_CHECKMULTISIG
153+
```
154+
155+
### P2TR-P2MS
156+
157+
## 代码示例
158+
159+
完整的示例可以在[bitcoinjs-lib](https://github.com/bitcoinjs/bitcoinjs-lib/)里找到,这里逐步拆解分析。
160+
161+
### P2SH-P2MS
162+
163+
示例代码参考[bitcoinjs-lib](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.spec.ts#L204-L250)
164+
165+
166+
1. **构建锁定脚本**
167+
以上面的示例脚本为例:
168+
169+
```javascript
170+
2 <AlicePubKey> <BobPubKey> <CharliePubKey> 3 OP_CHECKMULTISIG
171+
172+
const output = bscript.compile(
173+
([] as Stack).concat(
174+
OP_INT_BASE + a.m,
175+
a.pubkeys,
176+
OP_INT_BASE + o.n,
177+
OPS.OP_CHECKMULTISIG,
178+
)
179+
);
180+
181+
// 其中
182+
m = 2;
183+
pubkeys = [<AlicePubKey>, <BobPubKey>, <CharliePubKey>];
184+
n = 3;
185+
```
186+
187+
2. **签名过程**
188+
189+
```javascript
190+
const multisig = createPayment('p2sh-p2ms(2 of 4)');
191+
const inputData1 = await getInputData(2e4, multisig.payment, false, 'p2sh');
192+
193+
const psbt = new bitcoin.Psbt({ network: regtest })
194+
.addInput(inputData1)
195+
.addOutput({
196+
address: regtestUtils.RANDOM_ADDRESS,
197+
value: 1e4,
198+
})
199+
.signInput(0, multisig.keys[0])
200+
.signInput(0, multisig.keys[2]);
201+
```
202+
203+
签名之后会得到签名信息,多签对应多个元素的签名数组:
204+
205+
```javascript
206+
const partialSig = [
207+
{
208+
pubkey: AlicePubKey,
209+
signature: bscript.signature.encode(Alice.sign(hash), sighashType),
210+
},
211+
{
212+
pubkey: BobPubKey,
213+
signature: bscript.signature.encode(Bob.sign(hash), sighashType),
214+
},
215+
];
216+
```
217+
218+
3. **排序签名**
219+
220+
```javascript
221+
function getSortedSigs(script: Buffer, partialSig: PartialSig[]): Buffer[] {
222+
const p2ms = payments.p2ms({ output: script });
223+
// for each pubkey in order of p2ms script
224+
return p2ms
225+
.pubkeys!.map(pk => {
226+
// filter partialSig array by pubkey being equal
227+
return (
228+
partialSig.filter(ps => {
229+
return ps.pubkey.equals(pk);
230+
})[0] || {}
231+
).signature;
232+
// Any pubkey without a match will return undefined
233+
// this last filter removes all the undefined items in the array.
234+
})
235+
.filter(v => !!v);
236+
}
237+
```
238+
239+
4. **构建解锁脚本**
240+
241+
```javascript
242+
bscript.compile(([OPS.OP_0] as Stack).concat(a.signatures))
243+
```
244+
245+
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
## OP_CHECKMULTISIGVERIFY
2+
3+
`OP_CHECKMULTISIGVERIFY` 并没有通过单独的 BIP 提出,但其背景和实现与 BIP 16 的提出密切相关。BIP 16 提出了支付到脚本哈希(P2SH)的机制,使得复杂的脚本可以更简洁地在链上表示,并且增强了多重签名的可用性。
4+
5+
### 详细背景
6+
7+
#### BIP 16 提出
8+
9+
1. **BIP 16**
10+
- 标题:`Pay to Script Hash`
11+
- 提案作者:Gavin Andresen
12+
- 提出时间:2012 年 1 月
13+
- 内容:定义了支付到脚本哈希(P2SH)的机制。P2SH 允许复杂的脚本在链上以更简洁的形式表示,并在解锁时提供完整的脚本内容。这极大地简化了多重签名等复杂交易的实现。
14+
15+
#### OP_CHECKMULTISIGVERIFY 的引入
16+
17+
在 BIP 16 提出并实现后,比特币开发社区继续寻找优化和简化脚本的方法。`OP_CHECKMULTISIGVERIFY` 作为 `OP_CHECKMULTISIG``OP_VERIFY` 的组合,自然而然地被引入,以简化多重签名验证的脚本编写和执行过程。
18+
19+
### OP_CHECKMULTISIGVERIFY 的工作原理
20+
21+
`OP_CHECKMULTISIGVERIFY` 结合了 `OP_CHECKMULTISIG``OP_VERIFY` 的功能。其主要目的是在验证多重签名后,立即判断验证结果并返回执行状态。
22+
23+
脚本构建和签名过程可以参考[OP_CHECKMULTISIG](../OP_CHECKMULTISIG/README.md),这里不再赘述。
24+
25+
### 具体案例
26+
在babylon业务中,会把用户的资产质押到一个Taproot地址里。根据官方披露的三条解锁路径都需要OP_CHECKSIGVERIFY来验证签名然后执行后续逻辑
27+
1. 时间锁路径,质押者到期可以赎回
28+
```js
29+
<Staker_PK> OP_CHECKSIGVERIFY <Timelock_Blocks> OP_CHECKSEQUENCEVERIFY
30+
```
31+
32+
2. 解绑路径,质押者提前赎回
33+
```
34+
<StakerPk> OP_CHECKSIGVERIFY
35+
<CovenantPk1> OP_CHECKSIG
36+
<CovenantPk1> OP_CHECKSIGADD ... <CovenantPkN> OP_CHECKSIGADD
37+
<CovenantThreshold> OP_GREATERTHANOREQUAL
38+
```
39+
40+
3. 质押者作恶,惩罚路径
41+
```js
42+
<StakerPk> OP_CHECKSIGVERIFY
43+
<FinalityProviderPk> OP_CHECKSIGVERIFY
44+
<CovenantPk1> OP_CHECKSIG <CovenantPk1> OP_CHECKSIGADD ... <CovenantPkN> OP_CHECKSIGADD
45+
<CovenantThreshold> OP_GREATERTHANOREQUAL
46+
```
47+
48+
相关交易可以查看:https://mempool.space/zh/signet/tx/ceb126550481ecb69b45929b2b5869fd3975a707e6100b368d6cc15e4434ad9d

0 commit comments

Comments
 (0)