教你用C++搭建一条迷你区块链!

程序员的天赋技能就是通过代码实践自己的想法,完成一个作品会有相当的成就感,所以今天我们以 C++14 的代码为例子,和你分享设计并实现一个迷你区块链例子。

目标和范围

首先我们要知道达成的目标,根据目标划定工作范围。

考虑到我们无法搭建一个类似比特币的庞大 P2P 网络,也没有太多精力实现一个真正意义上的完整功能的全节点钱包,而且完整的全节点过于复杂,会让学习者迷失在细节中。

所以我们的目标是:构建一个包含仅基础功能的全节点客户端,它可能没有太炫酷的 UI 页面,也没有复杂的命令,仅一个命令行工具和一个全节点核心程序,它们提供下面的功能。

提供 P2P 节点发现和同步区块的功能;

提供创建公私钥对的功能;

提供发送交易的功能;

提供交易查询的功能;

提供余额查询的功能;

提供挖矿的功能,在任意地址商都可以发起单机挖矿;

提供基础日志,方便跟踪监视。

以上 7 个功能基本涵盖了一个区块链全节点的主要功能,但是,由于篇幅有限,代码不能全部实现,主要讲解设计和实现思路。后续我会逐渐完善代码,你也可以一起参与,代码开源在:https://github.com/betachen/tinychain

技术选型

我们在「深入浅出区块链」专栏中说到过,区块链的四个核心技术概念:P2P 网络、账户模型与存储、共识、加密模块。

加Q群:731611386可免费获取C语言学习资料

首先,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 算法作为挖矿算法会降低我们的工作量。

详细功能

有了技术选型之后,我们再对目标功能点进行细分拆解。

P2P 网络:节点发现、节点维护、持久化保存、区块同步。

公私钥对:命令行、创建公私钥对、并生成地址、提供私钥存储、公私钥验证。

发送交易:命令行、发送成功验证、输入是交易哈希。

交易查询:命令行、JSON 格式的交易查询返回、输入是某个地址。

余额查询:命令行、JSON 格式的余额查询返回、输入是某个地址。

挖矿:命令行、JSON 格式挖矿信息返回、输入是某个地址。

区块共识:编织区块链的算法,包含创世区块以及调整全网挖矿难度。

交易共识:验证单个交易的算法,包含签名验证和 UTXO 验证。

基础日志:用于监控网络、区块验证等操作。

区块持久化存储:分叉与合并时的一致性,并为查询提供接口。

提供格式化输出交易的功能,这里的格式化主要指 JSON 格式。

有效防止双花交易。

通过详细的功能拆分我们可以发现,功能点多达三十余个,如何设计实现这三十多个功能点是我们接下来首先要解决的问题。问题是这三十多个功能点不是孤立的,而是有相互联系的,我们先从顶层开始设计。

最顶层是一个区块节点,一个完整的可执行程序,我们命名为 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 容器中。

总结

今天大致介绍了实践一个迷你区块链的思路,我们先划定了实践的范围,接着考虑了技术选型,然后细化了详细功能,考虑了一个区块链需要的数据结构,最后考虑了开发环境和测试环境的搭建。
进Q群:可免费领取学习资料

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
课程简介    随着”新基建“的推行,其中涉及到的工业互联网、物联网、人工智能、云计算、区块链,无一不是与安全相关,所有数据的存储、传输、签名认证都涉及到密码学技术,所以在这样的大环境下再结合我多年安全开发经验,设计出这门课程。    因为密码学技术在新基建中的重要性,所以使其成为底层开发人员所必备的技能。特别是现在的区块链技术是全面应用密码学,大数据技术和人工智能技术也要解决隐私安全问题。所以现在学习相关技术是非常必要的技术储备,并且可以改造现有的系统,提升其安全性。课程学习目标了解DES算法原理VS2019创建C++项目,并导入openssl库学会OpenSSL DES算法加解密接口加密文件并做PKCS7 Padding 数据填充解密数据并做数据填充解析课程特点    面向工程应用    市面上的一些密码学课程和密码学的书籍,很多都是从考证出发,讲解算法原理并不面向工程应用,而我们现在缺少的是工程应用相关的知识,本课程从工程应用出发,每种技术都主要讲解其在工程中的使用,并演示工程应用的代码。    从零实现部分算法    课程中实现了base16编解码 ,XOR对称加解密算法,PKCS7 pading数据填充算法,通过对一些简单算法的实现,从而加深对密码学的理解。    理论与实践结合    课程如果只是讲代码,同学并不能理解接口背后的原理,在项目设计中就会留下隐患,出现错误也不容易排查出问题。    如果只讲理论,比如对密码学的一些研究,对于大部分从事工程应用的同学并没有必要,而是理论与实践结合,一切为了工程实践。    代码现场打出    代码不放在ppt而是现场打出,更好的让学员理解代码编写的逻辑,老师现场敲出代码正是展示出了工程项目的思考,每个步骤为什么要这么做,考虑了哪些异常,    易学不枯燥    课程为了确保大部分人开发者都学得会,理解算法原理(才能真正理解算法特性),学会工程应用(接口调用,但不局限接口调用,理解接口背后的机制,并能解决工程中会出现的问题),阅读算法源码但不实现密码算法,,并能将密码学投入到实际工程中,如果是想学习具体的加密算法实现,请关注我后面的课程。课程用到的技术    课程主要演示基于 VS2019 C++,部分演示基于ubuntu 18.04 GCC makefile    如果没有装linux系统,对本课程的学习也没有影响    课程中的OpenSSL基于最新的3.0版本,如果是openss 1.1.1版本也支持,再低的版本不支持国密算法。 课程常见问题课程讲解用的什么平台和工具?    课程演示主要在windows,基于VS2019 ,一些项目会移植到Linux在ubuntu18.04上我不会Linux能否学习本门课程?    可以的,课程主要在Windows上,Linux部分只是移植,可以暂时跳过,熟悉了Linux再过来看我不会C/C++ 语言是否能学习本门课程?    至少要会C语言,C++特性用得不多,但做了一个封装,可以预习一些C++基础。会不会讲算法实现,会不会太难学不会?    课程偏工程应用,具体的AES,椭圆曲线、RSA等算法只通过图示讲原理,一些简单hash算法会读一些源码,并不去实现,课程中会单独实现简洁的XOR对称加密和base16算法(代码量不大易懂)。其他的应用我们都基于OpenSSL3.0的SDK调用算法。课程提供源码和PPT吗?    课程中所有讲解的源码都提供,课程的上课的ppt也提供,PPT提供pdf版,只可以用于学习,不得商用,代码可以用于商用软件项目,涉及到开源系统部分,需要遵守开源的协议,但不得用于网络教学。要观看全部内容请点击c++实战区块链核心密码学-基于opensslhttps://edu.csdn.net/course/play/29593
我很乐意回答您的问题,以下是一段使用 C 语言实现的简单区块链代码: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #define BLOCK_SIZE 512 // 区块大小 #define HASH_SIZE 64 // hash 值大小 typedef struct Block { int index; // 区块链索引 time_t timestamp; // 时间戳 char data[BLOCK_SIZE]; // 数据 char prev_hash[HASH_SIZE]; // 上一个区块的 hash 值 char hash[HASH_SIZE]; // 当前区块的 hash 值 struct Block* next; // 下一个区块的指针 } Block; // 生成 hash 值的函数 char* generate_hash(char* data) { char* hash = (char*)malloc(sizeof(char) * HASH_SIZE); // 实现 hash 算法 // ... return hash; } int main() { Block* head = (Block*)malloc(sizeof(Block)); head->index = 0; head->timestamp = time(0); strcpy(head->data, "This is the first block."); memset(head->prev_hash, 0, HASH_SIZE); strcpy(head->hash, generate_hash(head->data)); head->next = NULL; Block* curr = head; // 添加新的区块 Block* new_block = (Block*)malloc(sizeof(Block)); new_block->index = curr->index + 1; new_block->timestamp = time(0); strcpy(new_block->data, "This is the second block."); strcpy(new_block->prev_hash, curr->hash); strcpy(new_block->hash, generate_hash(new_block->data)); curr->next = new_block; curr = new_block; // 输出区块链的内容 curr = head; while (curr != NULL) { printf("Index: %d\n", curr->index); printf("Timestamp: %ld\n", curr->timestamp); printf("Data: %s\n", curr->data); printf("Prev Hash: %s\n", curr->prev_hash); printf("Hash: %s\n", curr->hash); curr = curr->next; } // 释放内存 curr = head; while (curr != NULL) { Block* temp = curr; curr = curr->next; free(temp); } return 0; } ``` 以上代码实现了一个基本的区块链,包含了区块链的索引、时间戳、数据、上一个区块的 hash 值和当前区块的 hash 值,通过链接不同的区块形成区块链

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值