一步步教小白使用C++构建区块链

        最近你可能听说过很多关于区块链的消息,想知道大家都在谈论什么。 区块链是一种分类账本,它的实现原理使得很难改变它所包含的数据;有人说区块链是不可改变的,它的目的是保持其正确的,不变性意味着永久性;但是硬盘上没有任何东西 永远可以被认为是永久性不变的;我们把这些哲学辩论留给非技术人员。而我现在要做的是向您展示如何使用C ++编写区块链。

免责声明

  • 本教程是参考Savjee使用NodeJS编写一篇文章;
  • 有一段时间没有编写任何C ++代码,代码可能会有点生疏。(译者注:运行该C++程序,可以生成区块并组成区块链,也具备工作量证明POW功能
    一步步教小白使用C++构建区块链

首先要做的是打开一个新的C++项目,我使用JetBrains中的CLion,但任何C++ IDE甚至文本编辑器都可以(译者注:译者使用的是VIM)。 我们将创建TestChain项目,继续下一步创建项目。
如果您使用CLion,您会看到main.cpp文件已经创建并打开; 我们暂时不使用该主函数文件。
在主项目文件夹中创建一个名为Block.h的文件,您应该可以通过右键单击项目工具窗口中的TestChain目录并选择:New>C/C ++ Header File来新建。
在文件内部,添加下面的代码(如果您使用CLion将所有代码放在#define TESTCHAIN_BLOCK_H和#endif的行之间)(译者注:防止头文件被重复调用)


  1. #include <cstdint>
  2. #include <iostream>

上面语句的意思是告诉编译器包含cstdint和iostream库。在它下面添加这一行:


  1. using namespace std;

上面语句是为std命名空间创建了一个快捷方式。其作用是不需要通过其使用全名引用std命名空间中的声明,例如,如果有上面语句则书写string就可以使用字符串,否则需要使用全名访问字符串,如std::string。

到现在为止感觉还挺好; 让我们进一步完善充实。

区块链由一系列包含数据的区块组成,每个区块都包含前一区块的加密HASH,这就表示很难改变某一个区块的内容,而后面的区块内容也同样发生改变(译者注:如果改变中间某个区块的内容,则这个区块之后的所有区块的内容都需要被改变才有效); 因此区块链被赋予了不变的特性。

区块头结构

接下来我们创建我们的块类,将以下几行添加到Block.h头文件中:


  1. class Block {
  2. public:
  3. string sPrevHash;
  4. Block(uint32_t nIndexIn, const string &sDataIn);
  5. string GetHash();
  6. void MineBlock(uint32_t nDifficulty);
  7. private:
  8. uint32_t _nIndex;
  9. int64_t _nNonce;
  10. string _sData;
  11. string _sHash;
  12. time_t _tTime;
  13. string _CalculateHash() const;
  14. };

不出所料,第1行 类Block第2行 公有修饰符 public第3行 公有变量sPrevHash,保存区块的前一个区块第5行 构造函数,需要的参数为nIndexIn和sDataInconst和&一起使用,表示参数为引用传递,其值不可被修改,其目的是提高效率并且节省内存第7行 GetHash()函数,获得HASH值第9行 MineBlock()函数,挖矿,其参数nDifficulty表示指定的难度值第11行 私有修饰符private,表示后面的变量为私有变量,不能被其他类访问第12-16行 私有变量

  • _nIndex 区块索引值,第几个区块,从0开始计算,
  • _nNonce 区块随机数,
  • _sData 区块描述字符
  • _sHash 区块HASH值
  • _tTime 区块生成时间

第18行 _CalculateHash(),计算HASH值。使用const关键字,是为了确保函数的输出不能被修改,这在处理区块链时非常有用。

现在是在主项目文件夹中创建Blockchain.h头文件的时候了。让我们开始添加这些行(如果您使用CLion将所有代码#define TESTCHAIN_BLOCKCHAIN_H 和 #endif之间)


  1. #include <cstdint>
  2. #include <vector>
  3. #include "Block.h"
  4. using namespace std;

上面语句告知编译器包含cstdint和vector向量库,以及调用我们刚创建的Block.h头文件,并创建std namespace快捷方式。

区块链头结构

现在让我们创建区块链类,将以下代码行添加到Blockchain.h头文件中:


  1. class Blockchain {
  2. public:
  3. Blockchain();
  4. void AddBlock(Block bNew);
  5. private:
  6. uint32_t _nDifficulty;
  7. vector<Block> _vChain;
  8. Block _GetLastBlock() const;
  9. };

第1行 创建区块链类第2行 public修饰符第3行 默认构造函数第5行 AddBlock() 增加区块函数,参数为我们前面创建的Block类的对象bNew第7行 private修饰符,后面的变量和方法是私有的第8-9行 私有变量

  • _nDifficulty难度值
  • _vChain保存区块的变量第11行 _GetLastBlock()获取最新的区块,由const关键字,表示输出的内容不可更改

因为区块链使用密码学,所以我们区块链需要一些加密函数。 我们将使用SHA256哈希来创建块的哈希值,(我们可以编写自己的SHA256函数,但是没必要重复造轮子),为了让编码更加简单,引用了来自Zedwood的C++ sha256函数,该链接中有sha256.h,sha256.cpp和LICENSE.txt文件,我们将它们保存在项目文件夹中。(译者注:可以使用译者提供的centos系统可以变编译运行的代码

好的,让我们继续

接下来我们的区块创建一个源文件Block.cpp并保存到主项目文件夹中首先添加这些行,告诉编译器包含我们之前添加的Block.h和sha256.h文件。


  1. #include "Block.h"
  2. #include "sha256.h"

下面实现区块构造函数:


  1. Block::Block(uint32_t nIndexIn, const string &sDataIn) : _nIndex(nIndexIn), _sData(sDataIn) {
  2. _nNonce = -1;
  3. _tTime = time(nullptr);
  4. }

第1行 将参数的内容赋值给变量_nIndex和_sData中。第2行 _nNonce变量设置为-1。第3行 _tTime变量设置为当前时间。

实现访问区块的HASH函数:


  1. string Block::GetHash() {
  2. return _sHash;
  3. }

第2行 返回私有变量_sHash的值

您可能已经清楚,比特币数字货币中设计区块链技术非常受欢迎,使得分类账不能被修改或者保密;这意味着,当一个用户将比特币发送给另一个用户时,比特币网络上的节点将转账交易写到区块链中的一个区块中。另一台计算机作为一个节点运行比特币软件(因为比特币网络是P2P,所以该节点可以是世界上任何一个人运行);这个过程被称为“挖矿”,每当他们在区块链上成功创建一个有效的区块时,该节点将获得相应的比特币奖励。

如果想成功创建一个有效的区块并因此获得奖励,矿工必须创建一个有效的区块加密HASH添加到区块链中。 有效区块HASH是通过计算HASH开头零的数量来实现的;如果零的数量等于或大于网络设置的难度,则表示有效,否则将增加随机数后重新创建散列;直到生成一个有效的散列。 这个过程称为工作量证明(Proof of Work,PoW)。

挖矿过程-工作量证明

接下来,我们实现MineBlock方法来复现上述过程


  1. void Block::MineBlock(uint32_t nDifficulty) {
  2. char cstr[nDifficulty + 1];
  3. for (uint32_t i = 0; i < nDifficulty; ++i) {
  4. cstr[i] = '0';
  5. }
  6. cstr[nDifficulty] = '\0';
  7. string str(cstr);
  8. do {
  9. _nNonce++;
  10. _sHash = _CalculateHash();
  11. } while (_sHash.substr(0, nDifficulty) != str);
  12. cout << "Block mined: " << _sHash << endl;
  13. }

第1行 MineBlock函数在block.h文件中声明,参数nDifficulty为难度第2行 创建一个难度大于nDifficulty的值的字符数据第3-6行 用0填充数组,最后一个字符填充为终止符\0第8行 将字符数组转换为string字符串第10-13行 循环增加随机数,并且计算HASH值保存到_sHash中。循环结束的条件是比较生成的HASH值和刚刚创建的零串,一旦匹配则跳出循环,否则继续生成HASH第15行 一旦找到匹配,就会向输出发送该区块的HASH值,表示该块已成功开采。

区块HASH计算

我们前面已经看到它好几次,所以接下来添加_CalculateHash方法:


  1. inline string Block::_CalculateHash() const {
  2. stringstream ss;
  3. ss << _nIndex << _tTime << _sData << _nNonce << sPrevHash;
  4. return sha256(ss.str());
  5. }

第1行 该函数在Block.h头文件中声明,inline关键字修饰,表示编译器编译时将该函数代码嵌入到其他函数中,减少了函数的调用开销(译者注:inline通常用在循环体内)
第2行 创建一个字符串流ss。
第3行 将_nIndex,_tTime,_sData,_nNonce和sPrevHash的值附加到流字符串流ss中。
第5行 将字符串流ss的sha256值输出

增加区块链文件Blockchain.cpp到主项目文件夹中。

下面的代码编译器包含添加的Blockchain.h文件。


  1. #include "Blockchain.h"

创世块的产生

接下来,我们实现区块链构造函数


  1. Blockchain::Blockchain() {
  2. _vChain.emplace_back(Block(0, "Genesis Block"));
  3. _nDifficulty = 3;
  4. }

第1行 区块链构造函数,生成“创世块”。
因为将区块添加到区块链中时,需要添加前一个区块HASH值,所以区块链必须从某处开始,我们称最开始的那个区块为“创世块”。
第2行 将生成的区块放在向量_vChain中。
emplace_back()函数,在容器_vChain尾部添加一个元素,这个元素原地构造,不需要触发拷贝构造和转移构造。
第3行 设置工作量POW的难度值(即区块HASH前面有多少个零)

添加区块

现在,我们实现区块增加函数


  1. void Blockchain::AddBlock(Block bNew) {
  2. bNew.sPrevHash = _GetLastBlock().GetHash();
  3. bNew.MineBlock(_nDifficulty);
  4. _vChain.push_back(bNew);
  5. }

第1行 该函数是在Blockchain.h文件中申明,参数为区块对象第2行 将上一区块的hash值(_vChain的最后一个区块HASH值)保存到sPrevHash中第3行 挖矿,参数为_nDifficulty难度值第4行 将新挖的区块保存到_vChain向量的末尾

让我们实现区块链的最后一个方法


  1. Block Blockchain::_GetLastBlock() const {
  2. return _vChain.back();
  3. }

第1行 该函数是在Blockchain.h头文件中声明
第2行 返回向量_vChain的最后一个元素

主函数

新建main.cpp(挖掘三个区块后退出程序)


  1. #include "Blockchain.h"
  2. int main() {
  3. Blockchain bChain = Blockchain();
  4. cout << "Mining block 1..." << endl;
  5. bChain.AddBlock(Block(1, "Block 1 Data"));
  6. cout << "Mining block 2..." << endl;
  7. bChain.AddBlock(Block(2, "Block 2 Data"));
  8. cout << "Mining block 3..." << endl;
  9. bChain.AddBlock(Block(3, "Block 3 Data"));
  10. return 0;
  11. }

源码目录结构以及编译情况
一步步教小白使用C++构建区块链
挖矿过程
一步步教小白使用C++构建区块链

请点击下载源码

源码介绍文档请阅读《一步步教小白使用C++构建区块链》

作者:davenash
链接:http://davenash.com/2017/10/build-a-blockchain-with-c/
版权声明:作者保留权利。文章为作者独立观点,不代表B链网立场。严禁修改,转载请注明原文链接