比特币以前都是1开头的地址,后来出现了3开头的地址,最新的地址格式是bc1开头的。
17mKugcBDEJbu391Fq41AdwLeGHwJLPRDf
35DHm5kjYbvkUwGNc9KR1HywTn4caeWFwL
bc1qfgedw3874f3wkqtkwjm2fawuuwtldvdefh3prc
以1开头的地址是公钥经过Hash160之后的结果,接到BTC的一方使用私钥来解锁。
由于比特币里都是用脚本(Script)来实现锁定和解锁的逻辑,地址可以表示公钥的哈希,也可以表示为脚本的哈希,这就产生了以3开头的地址,准确的名字是
P2SH(Pay to Script Hash)地址
。
回顾一下以前介绍过的两种交易类型:
认真研究一笔交易的执行过程,可以更加准确地理解P2SH的概念,以这笔从3地址转账到1地址的交易为例:
https://nioctib.tech/#/transaction/7edb32d4ffd7a385b763c7a8e56b6358bcd729e747290624e18acdbe6209fc45
下图中的解锁脚本用来使用3地址中的0.0099 BTC。
总共三个参数,第一个参数为0,一个令人费解的参数,也写作OP_0或OP_FALSE,后面再解释。
0 3045...9001 5141...51ae
追查0.0099BTC的来源,可以看到锁定脚本:
HASH160 e9c3...160a EQUAL
debug脚本的验证过程:
前三步把几个操作数压栈,很简单。
取出栈顶端的 0x5141...51ae, 执行HASH160。
再压入一个数 0xe9c3...160a。
执行OP_EQUAL操作。顶端返回一个参数1,说明验证成功。
下面要解开一段脚本,继续验证的过程。
先把这串十六进制数
5141042f90074d7a5bf30c72cf3a8dfd1381bdbd30407010e878f3a11269d5f74a58788505cdca22ea6eab7cfb40dc0e07aba200424ab0d79122a653ad0c7ec9896bdf51ae
到这个网址解码:
http://chainquery.com/bitcoin-api/decodescript
解码得到的脚本是:
OP_1 042f90074d7a5bf30c72cf3a8dfd1381bdbd30407010e878f3a11269d5f74a58788505cdca22ea6eab7cfb40dc0e07aba200424ab0d79122a653ad0c7ec9896bdf OP_1 OP_CHECKMULTISIG
0x51对应OP_1,也写作OP_TRUE,中间一串数字042f...6bdf。
最后的0xae是 OP_CHECKMULTISIG,表示多重签名检查。
这段脚本经过哈希之后,加上前缀05和校验码,生成的地址就是:
3P14159f73E4gFr7JterCCQh9QjiTjiZrG
可以用C#源代码验证上面的结论。
string scriptText = "5141042f90074d7a5bf30c72cf3a8dfd1381bdbd30407010e878f3a11269d5f74a58788505cdca22ea6eab7cfb40dc0e07aba200424ab0d79122a653ad0c7ec9896bdf51ae";
Script script = new Script(Encoders.Hex.DecodeData(scriptText));
Console.WriteLine(script.GetScriptAddress(Network.Main));
// 输出结果:3P14159f73E4gFr7JterCCQh9QjiTjiZrG
堆栈里还有两个数字,再追加上这组代码,准备继续执行。
压入OP_1, 0x042f...6bdf, OP_1,准备执行OP_CHECKMULTISIG。
这里变得非常难于理解,需要提前补一下多重签名的概念,对于一个M-N多重签名(M<=N),表示N个公钥中,只需要提供M个签名就可以花费这笔资金。以2-3多重签名为例(即M=2,N=3),3个公钥要提供2组签名,堆栈里应该有这样一些数。
3
(公钥3)
(公钥2)
(公钥1)
2
(签名2)
(签名1)
0
堆栈最底部的0是一个Bitcoin Core早期实现时的一个BUG,程序员经常会犯的一个"差一BUG",英文为off-by-one。程序在解析script时要多取出一个数,所以就充填了一个OP_FALSE(0)。
比特币最多允许提供16个签名,因为内部只有OP_1, OP_2 ... OP_16,操作数对应着0x51, 0x52 ... 0x60。
现在就可以理解堆栈里的这一串数字的含义,这里利用多重签名的语法来表示一个单一签名,即 1-1 多重签名。
OP_1
0x042f...6bdf 公钥1
OP_1
0x3045...9001 签名1
OP_0
当签名和公钥匹配时,脚本执行结果返回1,表示整个交易通过了有效性验证。
要想了解脚本和签名的生成过程,需要继续学习构建P2SH交易的过程。
参考阅读: