经过一段时间的学习,尝试去理解比特币和区块链的概念,也在阅读rust-bitcoin项目下的代码。现在来提出一个比特币交易的基本问题:
我们怎样去实现比特币交易?
问题的产生
问题的产生很简单,现在只有一个目的,想要在比特币网络上实现一次转账交易。在实现这个目标之前,我做了一些准备工作:
- 阅读了mastering bitcoin
- 参阅了 rust-bitcoin 项目下 rust-wallet的源码
- 阅读了bitcoinJ的源码,根据bitcoinJ的实现梳理出一个我认为的交易实现的过程
- 查阅了 rust-bitcoin 项目的子项目murmel,该项目是一个 SPV 节点的实现,截止目前还暂时无法运行
之前也在博客撰写了 比特币交易过程 的文章,可以在之前的博客看到。其中涉及熵的生成,助记词生成,秘钥的派生等钱包的概念。也在后续的文章中提炼出来交易过程中的脚本构建的过程。初步构建了比特币交易的流程。也有文章描述了SPV节点的概念,简单来讲 SPV 节点不存储完整的区块链信息,使用称为 merkle tree 的数据结构来验证交易信息。
初步思考
第一层的准备工作完成之后,我阅读了 bitcoinj 的代码。bitcoinj 是使用 java 实现的比特币节点,它的功能非常齐全,包含全节点实现和轻节点(SPV)的实现。是受到 bitcoin core 推荐的一种实现。bitcoinj 使用的非常广泛,很多手机上的钱包都是基于bitcoin库的功能实现的。说一个和手机端最贴近的点:直接使用WalletAppkit 组件,他屏蔽了很多实现细节,暴露给开发者非常简洁的开发接口。具体的开发过程可以参考 bitcoinj 的文档,他已经非常简洁清楚了。此处提出一点建议,也是给自己的建议
阅读代码请从开发文档开始。先了解用法,然后再深入源码细节。
这个是最近阅读源码的经验之谈,这个道理也可以通用。
要是没有文档怎么办?
那就请从第二步开始,了解用法,从程序的启动入手。按照程序启动的顺序去梳理出代码的思路。这样也符合代码的开发流程,更容易体会代码开发过程和作者思路。
按照以上原则,我总结出了了 我以为的交易过程(也就是实现本文中目的的过程),注意以下的思路可能是 错误的,但这是我阅读bitcoinj和横向阅读rust-wallet的得到的结论,我认为思路是有价值的。
- 启动bitcoin节点,或者服务
- 根据参数去配置bitcoin network。bitcoin netwrok 主要分为三类
- Mainnet 主网,默认端口 8333。
- Testnet 测试网,默认端口 18333。
- RegTest 私链,单机模式
- 节点探测,接入 bitcoin 的p2p网络,进行交互。节点(无论是轻节点还是全节点的一个重要工作就是维护这种p2p链接)
- 所有节点都有根据比特币协议所实现的功能,根据这些接口功能,用户组建交易信息,让节点去广播交易
再整理
在此基础上,又根据《精通比特币》,中提到的方法有以下的流程
- 编译并启动比特币程序
- 使用 bitcore 提供的cli工具去 bitcore 交互,bitcore 本身提供 RPC 服务可以交互,这样就可以完成查询,挖矿,和交易。
根据这两方面的知识,去专门了解RPC服务是什么概念。因为 rust-bitcoin 中直接使用了 bitcoin-rpc 库。按照第一节的原则,整理RPC的用法。
https://github.com/rust-bitcoin/rust-bitcoincore-rpc
可以在以上的链接中查阅 rpc 库的用法
1 | extern crate bitcoincore_rpc; |
可以说 rpc 的用法很简单,只需要提供三个参数即可,即 ip,username,password。这下就产生让我最大的疑问
- ip从哪里来?
- username 和 password 从哪来?
以下是对问题的思考,他的结论是可能错误的,或者说是不完全的,但也是真实的思考过程
第一个问题:
还记的当初第一部分的思考吗,阅读了 bitcoinj 的代码所总结出的流程。启动节点时刻,本节点是空白的,并不知道该如何去和其他的节点建立 p2p 链接。根据比特币标准,比特币官方维护了 dnsseed,大概是一共就四到六个域名,只要解析这几域名就可以得到ip。事实上我确实使用 dns-resolve 得到了大量ip。
第二个问题:
查阅rpc的用法,username 和 password 是避不开的。在所有的比特币指南的开发中,链接比特币节点,username 和 password 都是需要的。阅读《精通比特币》书中的启动比特币节点,这个用户名是自己在配置文件中配置的。
错误的推理:
既然比特币官方提供dnsseed,确实能够解析出大量dns。而且考虑到比特币数据是公开的,那这些节点是不是开放的?不需要用户名和密码。
错误的流程:
基于以上错误流程,我总结出了要实现流程。去 dnsseed 上解析出ip,然后不用用户名和密码去和这些服务器建立 rpc 服务进行交互。
其实在这个推理过程之后,我还是去阅读了 murmel 的源码 ,跟着他一步步的去实现这个 spv 的节点,但是心中的困惑完全没有解决。再模仿 murmel 写了很多代码之后开始整理思路,这样做到底能否实现我要的交易。
矛盾的思考点出现了,我翻阅了比特币白皮书之类的资料。也对应查阅了很多具体的实现,发现没有一个这个做的。核心在于:比特币官方并不承诺提供RPC服务。也就是说解析出来这些 ip 地址是不可以进行 rpc 服务的。事实上我也尝试去链接这些 ip。确实没法办法链接!并且这些 rpc 服务在配置中是可以关闭的,比特币节点并不会承诺开放这种服务。
最后结论
通过以上的过程,其实最大的问题就已经明白了。我们要完成交易,有哪些路径?
- 使用第三方提供的 api 比如bitchain
- 自己维护一个比特币节点,rpc 交互去交易
- 维护一个SPV节点
第一点不用说,第二点如果自己维护一个比特币节点,那么ip,username,password都是自己提供的,那只需要进行 rpc 链接。通过 rpc 服务完成交易即可。
维护SPV节点,比特币根据自己协议,轻节点根据自己的验证方式去验证的交易。(轻节点要与别的全节点交互)
最后顺带回答个小问题
整个交易中 RPC 服务在哪里,P2P 服务在哪里
我们把整个比特币的节点拆开看。分成三个部分,钱包,节点核心,别的节点。RPC 服务是节点对外界提供的功能(也可以不开启),用 RPC 的方式和比特币核心进行交互(比特币核心有多种实现,这个交互主要就是交易)。P2P则指的是比特币节点之间的链接方式,dnsseed 或者其他配置的节点链接发生在节点之间。至于为什么要分出钱包?因为我想实现个钱包去比特币核心上做交易。
注:中间那个错误的流程对于eth是可以的,因为 eth 没有轻节点,所以官方承诺提供这种服务,外界是可以连接和交互的。这也是 eth 被诟病不够去中心化的地方,事实上这个可以对外开放的节点也遭受了多次攻击。
完,如有错误,请指正。