You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

211 lines
10 KiB
Markdown

2 years ago
# 第35讲 | 搭建你的迷你区块链(设计篇
程序员的天赋技能就是通过代码实践自己的想法,完成一个作品会有相当的成就感。
今天我们终于也来到了实践环节。我将以C++14的代码为例和你分享设计并实现一个迷你区块链的例子。
## 目标和范围
首先我们要知道达成的目标,根据目标划定工作范围。
考虑到我们无法搭建一个类似比特币的庞大P2P网络也没有太多精力实现一个真正意义上的完整功能的全节点钱包而且完整的全节点过于复杂会让学习者迷失在细节中。
所以我们的目标是构建一个包含仅有基础功能的全节点客户端它可能没有太炫酷的UI页面也没有复杂的命令它们可以提供下面的功能。
1. 提供P2P节点发现和同步区块的功能
2. 提供创建公私钥对的功能;
3. 提供发送交易的功能;
4. 提供交易查询的功能;
5. 提供余额查询的功能;
6. 提供挖矿的功能,在任意地址上都可以发起单机挖矿;
7. 提供基础日志,方便跟踪监视。
以上7个功能基本涵盖了一个区块链全节点的主要功能但是由于我的时间有限代码不能全部实现主要是讲解设计和实现思路。后续我会逐渐完善代码你也可以一起参与。
代码开源在:[https://github.com/betachen/tinychain](https://github.com/betachen/tinychain)
## 技术选型
我们在深入区块链技术专题中说到过区块链的四个核心技术概念P2P网络、账户模型与存储、共识、加密模块。
首先P2P网络模块是区块链的最底层模块之一我们主要考虑方便实现和测试可选的方案有轻量级消息队列和WebSocket。考虑到集成的便利性我们首选WebSocket因为至少需要一个HTTP JSON-RPC Server我们可以复用Server中的Websocket服务。
除了通讯协议之外还要考虑数据交换格式我们考虑采用易读通用的JSON格式而不是像比特币一样的数据序列化格式后期更改可以考虑升级到Protobuf后者优势主要体现在性能上。而在我们的例子中性能永远不是首先考虑的更多是它的易读和易调试性。
其次我们来说说账户加密部分由于ECDSA非对称加密模块过于复杂我们选用OpenSSL库中的RSA算法作为加密模块。而交易模型上我们考虑使用UTXO模型因为状态模型需要维护状态可能会带来额外的代码复杂度。
再来说说数据库存储,这个模块需要考虑到易用性和易读性,我们选用 SQLite 3作为持久化存储。
最后我来谈谈共识算法这一模块我们选用PoW作为共识算法这是考虑到PoW实现起来十分简单而且交易和区块的哈希计算会涉及SHA-256使用PoW算法我们就可以复用SHA-256的代码使用SHA-256算法作为挖矿算法会降低我们的工作量。
## 详细功能
有了技术选型之后,我们再对目标功能点进行细分拆解。
1. P2P网络节点发现、节点维护、持久化保存、区块同步。
2. 公私钥对:命令行,创建公私钥对并生成地址,提供私钥存储,公私钥验证。
3. 发送交易:命令行,发送成功验证,输入是交易哈希。
4. 交易查询命令行JSON格式的交易查询返回输入是某个地址。
5. 余额查询命令行JSON格式的余额查询返回输入是某个地址。
6. 挖矿命令行、JSON格式挖矿信息返回输入是某个地址。
7. 区块共识:编织区块链的算法,包含创世区块以及调整全网挖矿难度。
8. 交易共识验证单个交易的算法包含签名验证和UTXO验证。
9. 基础日志:用于监控网络,区块验证等操作。
10. 区块持久化存储:分叉与合并时的一致性,并为查询提供接口。
11. 提供格式化输出交易的功能这里的格式化主要指JSON格式。
12. 有效防止双花交易。
通过详细的功能拆分我们可以发现,功能点多达三十余个,如何设计实现这三十多个功能点是我们接下来首先要解决的问题。问题是这三十多个功能点不是孤立的,而是有相互联系的,我们先从顶层开始设计。
最顶层是一个区块节点一个完整的可执行程序我们命名为Tinychain而对应的命令行客户端为cli-tinychain。
Tinychain的核心程序主要包含以下结构
```
tinychain
├── blockchain
├── consensus
├── database
├── network
├── http-server
└── node
```
我们以node为最顶层那么node会包含其他五个模块node启动就会把其他5个服务启动。
cli-tinychain 主要包含以下结构:
```
cli-tinychain
├── JSON
└── http-client
```
命令行就简单多了我们把命令行的执行和计算全部都扔到tinychian当中命令行只用一个http-client用JSON把API包起来即可。
通过分析我们知道,以下组件是必不可少的,但是我们不必自己开发,可以直接选取一些现成的开发包直接集成即可。
```
基础组件
├── log
├── JSON-paser
├── sha256
└── key-pair
```
## 区块数据结构设计
有了大致的顶层设计已经分类好,那么接下来我们考虑为每个模块填充一些数据结构。一个区块链最重要的是区块,所以我们从区块开始。
一个区块包含两部分,分别是区块头和区块体,区块头是一个区块的元数据,区块体就是包含交易的列表,所以我们直接设计交易体。
### 区块头的设计
我们参照比特币的设计,区块头包含了前向区块哈希、默克尔根哈希、时间戳、难度目标、Nonce值和版本号。
所以我们的结构可能是这样的。
```
{
"target_bits" : "4575460831240",
"hash" :
"4a9169e2f4f8673ac9627be0fa0f9e15a9e3b1bc5cd697d96954d25acacd92df",
"merkle_tree_hash" : "3d228afc50bc52491f5dd8aa8c416da0d9a16bf829790ea0b7635e5b4d44ab4f",
"nonce" : "3852714822920177480",
"height" : 1234567,
"previous_block_hash" : "4d2544e044bfd2f342220a711b10842bb6cfae551b1bc1ed6152ff5c7f3ff654",
"time_stamp" : 1528070857,
"transaction_count" : 1,
"version" : 1
}
```
* target\_bits 表示当前区块的目标值;
* hash 表示这个区块的哈希;
* merkle\_tree\_hash 表示这个区块当中交易列表的默克尔根;
* nonce表示随机数
* height 表示当前区块的高度;
* previous\_block\_hash指向前向区块哈希
* time\_stamp表示生产这个区块时的时间戳
* transaction\_count表示这个区块当中包含多少笔交易
* version表示区块的版本号不代表交易的版本号。
在这里我们的区块头大小不是固定的因为它没有经过序列化完全以JSON表示所以我们这里就不考虑字节印第安序的问题了也不考虑固定长度的问题。
有了区块头我们再看看交易体的设计由于使用UTXO作为交易模型那么我们先考虑一个输入、一个输出的结构。
```
{
"hash": "8c14f0db3df150123e6f3dbbf30f8b955a8249b62ac1d1ff16284aefa3d06d87",
"version": 1,
"input_size": 1,
"output_size": 1,
"size": 135,
"inputs": [{
"prev_out": {
"hash": "0000000000000000000000000000000000000000000000000000000000000000",
"index":0
iq },
}],
"out": [{
"value": "5000000000",
"address": "f3e6066078e815bb2"
}],
}
```
我们可以按照这种结构来设计交易体。
### 地址设计
区块链地址都有通常意义上的地址,我们这里将公钥直接算作地址,不再将公钥进行哈希转换。
### 内存池
内存池是指缓存交易的一块交易缓冲区这里一个节点的主要处理对象所以对内存池的管理是编织区块链的最重要一步。我们这里的内存池使用标准库STL中的容器。
### 哈希计算
区块和交易的哈希计算均使用SHA-256。
## 开发环境搭建
由于选取了C++作为实现方式搭建工程的过程会比较复杂一点。我们用的是Ubuntu 16.04开发环境默认的gcc编译器是gcc-5.4是支持C++14标准的。代码也是全平台可移植的如果你使用Mac也可以尝试搭建。
除了gcc之外我们还需要Cmake来构建工程。我们也许需要Boost库的支持例如Filesystem和Datetime等基础组件。
所以我们的工具链是:
* gcc或clang
* cmake
* boost 1.56+ (datetime)
最后我们还需要一个简单好用的轻量级Httpserver我选取了元界代码中的Mongoose库这里的Mongoose不是MongoDB是由Cesanta开源的一个HTTP Server库支持epoll和select两种网络并发机制也支持WebSocket。
当然除了C++实现之外我们也可以使用Python来实现实际上也有不少Python实现的Demo但我发现用Python实现的例子很多是在单进程中模拟区块链的数据结构并不是真正意义上的分布式节点所以我采取了使用C++实现的策略。
## 测试环境搭建
我们知道区块链是一个分布式网络环境,在开始之前,我们需要构造一个简单且容易测试的分布式网络环境。
我们不可能购买大量的云计算资源所以我们推荐你购买一个基础版的ECS节点2Core 4G就可以性能稍好更好接着我们选用Docker来搭建容器集群在容器中部署节点其中宿主机作为编译环境将编译完成的钱包部署到全部的Docker容器中。
## 总结
今天我大致介绍了实践一个迷你区块链的思路,我们先划定了实践的范围,接着考虑了技术选型,然后细化了详细功能,考虑了一个区块链需要的数据结构,最后考虑了开发环境和测试环境的搭建。今天的问题是,你觉得搭建一个迷你区块链最难的部分是哪一部分呢?
### 链接:
[https://github.com/cesanta/mongoose](https://github.com/cesanta/mongoose)
### 一些Python实现迷你区块链的例子
1. [https://medium.com/crypto-currently/lets-build-the-tiniest-blockchain-e70965a248b](https://medium.com/crypto-currently/lets-build-the-tiniest-blockchain-e70965a248b)
2. [https://hackernoon.com/learn-blockchains-by-building-one-117428612f46](https://hackernoon.com/learn-blockchains-by-building-one-117428612f46)
3. [http://adilmoujahid.com/posts/2018/03/intro-blockchain-bitcoin-python/](http://adilmoujahid.com/posts/2018/03/intro-blockchain-bitcoin-python/)