从二进制开始一步一步构建比特币
很多人了解比特币,但不是真正了解比特币的内部原理。也许对于普通人不需要了解这些,但是如果你对区块链这项新技术感兴趣,并志愿加入这一领域的开发,我觉得你还是很有必要了解比特币实现的细节。所以我写了这篇文章,帮助大家入门或打好基础。文章参考了较多的外文资料,以及书籍,但是仍就不能保证所有内容都是准确无误的,欢迎大家勘误。
github源代码地址:https://github.com/john-shine/build-bitcoin-sbs
全文未整理完毕,待续……
比特币基本概念
比特币不是被用户所有,而是被地址所有;
比特币地址
典型的比特币地址是P2PKH
P2SH地址完全不同
比特币交易
交易中,比特币被转移到新的地址当中
比特币革命性在于交易如何通过挖矿记录在分布式的数据库中。交易被一起些入到区块中,约每十分钟一个新的区块挖掘出来并传播到网络之中,从未交易日志的一部分,也就是众所周知的区块链。
比特币挖矿的过程实际上就是将交易记录写入区块,让所有人都能够公开访问的过程。
挖矿能够产生合格的区块不只一个,实际理论上可以产生无数个满足条件的区块。
p2p网络
天网不能被关闭,因为没有中心
如果区块没有验证通过,那么这个区块就不会被继续传播了
1、生成比特币私钥和地址
2、生成一笔交易,转账少量比特币到上述地址
3、对交易签名(这个花费了我很多的时间)
4、广播交易到p2p网络,等待区块被开采出来
比特币协议乱糟糟的:big-endian numbers little-endian numbers fixed-length numbers variable-length numbers custom encodings DER encoding cryptographic algorithms
另一个让人感到恼火的是,协议使用加密的数据直接操作,而且只要错了一个位的数据,交易就会被拒绝,而你却不知道错在哪里
最后难点在于,签名版本的交易是和实际使用版本的交易有非常大的区别的
在交易被签名之前,公钥实际上是不会被公开的。不想大多数公钥私钥系统里面,公钥是公开的
比特币脚本语言(script)
你可能觉得比特币签名机制是简单地加上交易签名,那么你想得太简单了,实际要复杂得多。实际上每笔交易都包含一小段程序。
比特币脚本语言非常的复杂,大概有80中不同的opcodes,包括算数运算、位运算、字符串操作、条件判断、堆栈操作。语言还包括必须的加密算法(sha-256、RIPEMD等等。为保证脚本一定会退出,语言内没有设定循环语句,因而比特币脚本语言是非图灵完备的。然而,实际上只有少数几种交易是被支持的。
PUSHDATA将签名压入堆栈
PUSHDATA将公钥压入堆栈
OP_DUP复制公钥到堆栈
OP_HASH160算出160位的公钥的hash
PUSHDATA把比特币地址压入堆栈
OP_EQUALVERIFY验证堆栈顶部两个数据的值是非相等=>交易的公钥的hash=交易输出比特币地址,证明公钥是合法的
OP_CHECKSIG检查交易的签名和公钥以及堆栈中的签名
上述过程保证了签名的合法性
对交易签名
交易签名算是手动创建比特币交易最复杂的一部分了,过程非常的困难而且很容易出错。基本思想是利用ECDSA椭圆曲线算法和私钥来生成交易的数字签名,但是细节却很复杂。签名过程可以详细的分为19步:
1、4字节version字段:01000000
2、1字节输入长度字段:01
3、32字节交易hash的输出
4、4字节
如果输入有多个的话,交易签名也将更加的复杂,因为每个输入都需要被签名
签名之前需要加上hash type常量后缀,普通交易是SIGHASH_ALL 即 0×00000001。签名之后,hash tyoe从交易后端去除,然后后缀到scriptSig
另外一个比较容易混淆的是,比特币协议的签名和公钥转换都是使用的是512位椭圆曲线值,但是却完全是不同的方式:签名使用DER编码,而公钥使用的是纯文本。另外,两者的值都有额外的比特位,但是添加的位置不一样:SIGHASH_ALL是添加到签名的后面,而type 04是添加到公钥的前面。
因为椭圆曲线算法使用随机数字,调试签名变得更加复杂了。因此签名是随着每次的计算而不同的,你就没办法和已知正确的签名进行对比,判断哪里出错了。
另外一个重要的签名每次不断变化带来的影响是:如果你重新签名交易,交易的hash会改变。这就是众所周知的Transaction Malleability。就这是为什么第三方随便就可以修改交易数据,但是不改变交易的实质。Transaction Malleability也曾经带来了大麻烦 MtGox事件
https://www.mtgox.com/press_release_20140210.html
最终的scriptPubKey必须包含成功的脚本以完成比特币的花费,注意脚本是在将来不确定的一个时间执行的(当比特币被花费的时候),而不是理解执行。scriptPubKey包含十六进制表示的目标地址,而不是Base58Check编码得到的。
结果是只有私钥的所有者能够花费这笔比特币,其他人能看到但是不能消费。
交易的最后
一旦所拥有必须的方法就绪,最后交易被打包起来。
理解椭圆曲线算法
比特币使用椭圆曲线作为签名算法,椭圆曲线算法的名称其实有点误导人:实际上椭圆曲线不是一个椭圆,一点也不像椭圆,也和椭圆的关系不大。
比特币使用的椭圆曲线是secp256k1,方程式y^2=x^3+7
G是基点,私钥是阶数,secrect_key * G = public_key
拥有私钥的人员可以签名,但是任何拥有公钥的人只能认证,不能够签名
发送交易到p2p网络
第1步,找到peer。这里面会遇到一个“先有鸡还是先有蛋”的问题。服务端可信赖的peers注册在bitseed.xf2.org中,dns查询,客户端得到一批peers的ip地址,并且验证它们是非能够正常。
如果这一步没有找到合适的peer,就会去查找代码中写死的peers,定义在bitcoin/src/chainparamsseeds.h的pnSeed中
static SeedSpec6 pnSeed6_main[] = {}
static SeedSpec6 pnSeed6_test[] = {}
每当用户开启Bitcoin客户端时,就会加入peer,退出bitcoin客户端时就会退出peer。因此peer的流动性很高。
第2步,广播到peers,使用p2p协议的过程倒是很直截了当,不绕弯子。首先发送TCP请求到任意一个peer的8333端口,开始发送消息, 接受消息返回。p2p协议的容错能力很强,及时请求发生故障,依然可以保持通讯。
这里要说得是,大家编写代码的时候,最好使用测试网络Testnet。现在已经升级到Testnet3,这里的比特币没有价值,你可以随便搞,不然把真的比特币弄丢了,那就损失大了。比方说如果忘记找零给自己的比特币地址,那么多余的比特币将会成为矿工费了。
p2p协议由24中不同的消息类型组成,每种消息都是简单直接的将二进制数据包含在ASCII命令之中
协议详细内容参见:https://en.bitcoin.it/wiki/Protocol_specification
1、交换version信息
2、verack消息
Victory:交易被挖矿挖出来
10分钟后,我的脚本收到了包含inv消息的区块,里面就包含了我的交易,说明我的交易成功了!!也可以通过比特币钱包或者在线校验网站看看是不是真的成功了
参考资料:
- 《Mastering Bitcoin》(《精通比特币》)— Andreas M. Antonopoulos
- Bitcoins the hard way: Using the raw Bitcoin protocol
- Bitcoin multisig the hard way: Understanding raw P2SH multisig transactions
- Bitcoin Wiki
- Bitcoin Stack Exchange
本文采用CC BY-NC-ND协议进行许可,传播时请保留链接:http://www.john-shine.com/skill/build-bitcoin-sbs.html
目前还没有评论
目前还没有trackbacks.
Trackbacks被禁用了