0%

比特币交易基本问题

经过一段时间的学习,尝试去理解比特币和区块链的概念,也在阅读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的得到的结论,我认为思路是有价值的。

  1. 启动bitcoin节点,或者服务
  2. 根据参数去配置bitcoin network。bitcoin netwrok 主要分为三类
    1. Mainnet 主网,默认端口 8333。
    2. Testnet 测试网,默认端口 18333。
    3. RegTest 私链,单机模式
  3. 节点探测,接入 bitcoin 的p2p网络,进行交互。节点(无论是轻节点还是全节点的一个重要工作就是维护这种p2p链接)
  4. 所有节点都有根据比特币协议所实现的功能,根据这些接口功能,用户组建交易信息,让节点去广播交易

再整理

在此基础上,又根据《精通比特币》,中提到的方法有以下的流程

  1. 编译并启动比特币程序
  2. 使用 bitcore 提供的cli工具去 bitcore 交互,bitcore 本身提供 RPC 服务可以交互,这样就可以完成查询,挖矿,和交易。

根据这两方面的知识,去专门了解RPC服务是什么概念。因为 rust-bitcoin 中直接使用了 bitcoin-rpc 库。按照第一节的原则,整理RPC的用法。

https://github.com/rust-bitcoin/rust-bitcoincore-rpc

可以在以上的链接中查阅 rpc 库的用法

1
2
3
4
5
6
7
8
9
10
11
12
extern crate bitcoincore_rpc;

use bitcoincore_rpc::{Auth, Client, RpcApi};

fn main() {

let rpc = Client::new("http://localhost:8332".to_string(),
Auth::UserPass("<FILL RPC USERNAME>".to_string(),
"<FILL RPC PASSWORD>".to_string())).unwrap();
let best_block_hash = rpc.get_best_block_hash().unwrap();
println!("best block hash: {}", best_block_hash);
}

可以说 rpc 的用法很简单,只需要提供三个参数即可,即 ip,username,password。这下就产生让我最大的疑问

  1. ip从哪里来?
  2. 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 服务在配置中是可以关闭的,比特币节点并不会承诺开放这种服务。

最后结论

通过以上的过程,其实最大的问题就已经明白了。我们要完成交易,有哪些路径?

  1. 使用第三方提供的 api 比如bitchain
  2. 自己维护一个比特币节点,rpc 交互去交易
  3. 维护一个SPV节点

第一点不用说,第二点如果自己维护一个比特币节点,那么ip,username,password都是自己提供的,那只需要进行 rpc 链接。通过 rpc 服务完成交易即可。

维护SPV节点,比特币根据自己协议,轻节点根据自己的验证方式去验证的交易。(轻节点要与别的全节点交互)

最后顺带回答个小问题

整个交易中 RPC 服务在哪里,P2P 服务在哪里

我们把整个比特币的节点拆开看。分成三个部分,钱包,节点核心,别的节点。RPC 服务是节点对外界提供的功能(也可以不开启),用 RPC 的方式和比特币核心进行交互(比特币核心有多种实现,这个交互主要就是交易)。P2P则指的是比特币节点之间的链接方式,dnsseed 或者其他配置的节点链接发生在节点之间。至于为什么要分出钱包?因为我想实现个钱包去比特币核心上做交易。

注:中间那个错误的流程对于eth是可以的,因为 eth 没有轻节点,所以官方承诺提供这种服务,外界是可以连接和交互的。这也是 eth 被诟病不够去中心化的地方,事实上这个可以对外开放的节点也遭受了多次攻击。

完,如有错误,请指正。