从二进制开始一步一步构建比特币

很多人了解比特币,但不是真正了解比特币的内部原理。也许对于普通人不需要了解这些,但是如果你对区块链这项新技术感兴趣,并志愿加入这一领域的开发,我觉得你还是很有必要了解比特币实现的细节。所以我写了这篇文章,帮助大家入门或打好基础。文章参考了较多的外文资料,以及书籍,但是仍就不能保证所有内容都是准确无误的,欢迎大家勘误。

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消息的区块,里面就包含了我的交易,说明我的交易成功了!!也可以通过比特币钱包或者在线校验网站看看是不是真的成功了

参考资料:

本文采用CC BY-NC-ND协议进行许可,传播时请保留链接:http://www.john-shine.com/skill/build-bitcoin-sbs.html

51 次阅读
  1. 目前还没有评论

发表评论

× 一 = 四

注意: 评论者允许使用'@user空格'的方式将自己的评论通知另外评论者。例如, ABC是本文的评论者之一,则使用'@ABC '(不包括单引号)将会自动将您的评论发送给ABC。使用'@all ',将会将评论发送给之前所有其它评论者。请务必注意user必须和评论者名相匹配(大小写一致)。

疑惑 调皮 伤心 抠鼻 惊讶 微笑 脸红 坏笑 惊讶 发呆 撇嘴 酷 阴险 咒骂 愤怒 白眼 鼓掌 得意 汗 打呵欠 大哭 憨笑

评论