有了区块和区块链的基本结构,有了工作量证明,我们已经可以开始挖矿了。剩下就是最核心的功能-交易,但是在开始实现交易这一重大功能之前,我们还要预先做一些铺垫,比如数据的序列化和启动命令解析。

根据《用 Go 构建一个区块链》的目录, 本章节的区块数据的序列化存储会使用一款KV数据库。其中比特币中是使用的是谷歌出品、c++编写的 LevelDB数据库,go语言示例中使用的是BoltDB。

我本来考虑使用redis和json来进行我们的数据序列化存储。使用boost库的program_options 解析命令行参数。但是考虑代码过于复杂也许会偏离演示区块链的属性这一目的。最后进行了精简,最终的方案是舍去命令行参数解析,数据序列化改为使用map容器作为哈希与区块block指针的映射记录。

我们代替序列化的数据结构为map<string, Block*> g_db;

该结构是一个哈希值与区块指针的映射,由于每个区块的哈希值的都是独一无二的,一定程度上哈希就相当于KV数据库中KEY。

我们通过独一无二的哈希值可以快速的map容器中查找的到区块指针,这一过程与使用kv数据的增删改查接口是基本相同的。所以使用map能达到使用kv数据一样的演示效果。

一 创建区块链与加入创世块

让我们从 NewBlockchain 函数开始。在之前的实现中,它会创建一个新的 Blockchain 实例,并向其中加入创世块。而现在,我们希望它做的事情有

  1. 创建创世块
  2. 存储到map中
  3. 将创世块哈希保存为最后一个块的哈希
  4. 创建一个新的 Blockchain 实例,其 tip 指向创世块(tip 有尾部,尖端的意思,在这里 tip 存储的是最后一个块的哈希
    代码大概是这样:
 Blockchain* NewBlockchain() {
string tip;
Block* genesis = NewGenesisBlock();
g_db[genesis->hash] = genesis;
g_db["l"] = genesis;
tip = genesis->hash; Blockchain* bc = new Blockchain{ tip, &g_db }; return bc;
}

我们创建了一个创世块,并且记录到全局map中,创始块哈希映射创始块的指针。 “l”字符串映射目前最后一个区块指针,恰好也是目前唯一的一个区块-创世块。 tp记录最后一个区块的哈希,也是目前唯一的一个区块-创世块的哈希

Blockchain 的结构现在看起来是这样:

typedef struct blockchain {
string tip;
map<string, Block*>* db;
}Blockchain;

接下来我们想要更新的是 AddBlock 方法:现在向链中加入区块,就不是像之前向一个数组中加入一个元素那么简单了。从现在开始,我们会将区块存储在数据库里面:

void AddBlock(string  data, Blockchain* bc) {
string lastHash; Block* p = g_db["l"];
if (p == NULL)
return;
lastHash = p->hash; Block* newBlock = NewBlock(data, lastHash); (*bc->db)[newBlock->hash] = newBlock;
(*bc->db)["l"] = newBlock; bc->tip = newBlock->hash;
}

二检查区块链

现在,产生的所有块都会被保存到一个数据库里面,所以我们可以重新打开一个链,然后向里面加入新块。但是在实现这一点后,我们失去了之前一个非常好的特性:我们再也无法打印区块链的区块了,因为现在不是将区块存储在一个数组,而是放到了数据库里面。让我们来解决这个问题!

BoltDB 允许对一个 bucket 里面的所有 key 进行迭代,但是所有的 key 都以字节序进行存储,而且我们想要以区块能够进入区块链中的顺序进行打印。此外,因为我们不想将所有的块都加载到内存中(因为我们的区块链数据库可能很大!或者现在可以假装它可能很大),我们将会一个一个地读取它们。故而,我们需要一个区块链迭代器(BlockchainIterator):

typedef struct blockchainiterator {
string currentHash;
map<string, Block*>* db;
}BlockchainIterator;

每当要对链中的块进行迭代时,我们就会创建一个迭代器,里面存储了当前迭代的块哈希(currentHash)和数据库的连接(db)。通过 db,迭代器逻辑上被附属到一个区块链上(这里的区块链指的是存储了一个数据库连接的 Blockchain 实例),并且通过 Blockchain 方法进行创建:

BlockchainIterator* Iterator(Blockchain* bc) {
BlockchainIterator* bci = new BlockchainIterator{ bc->tip,bc->db };
return bci;
}

注意,迭代器的初始状态为链中的 tip,因此区块将从头到尾,也就是从最新的到最旧的进行获取。实际上,选择一个 tip 就是意味着给一条链“投票”。一条链可能有多个分支,最长的那条链会被认为是主分支。在获得一个 tip (可以是链中的任意一个块)之后,我们就可以重新构造整条链,找到它的长度和需要构建它的工作。这同样也意味着,一个 tip 也就是区块链的一种标识符。

BlockchainIterator 只会做一件事情:返回链中的下一个块

Block* Next(BlockchainIterator* i){
Block* block;
if (i->currentHash == "")
return NULL;
block = (*i->db)[i->currentHash];
i->currentHash = block->prevBlockHash;
return block;
}

这就是数据库部分的内容了!

我们在main函数中测试创建区块链和添加区块 并且打印结果 代码如下

int main()
{
Blockchain* bc = NewBlockchain();
printChain(bc); AddBlock("Send 1 BTC to Ivan", bc);
printChain(bc); AddBlock("Pay 0.31337 BTC for a coffee", bc);
printChain(bc); return ;
}

运行结果如下:

Mining the block containing Genesis Block

Prev. hash:
Data: Genesis Block
Hash: 000000e8b0be7b9518b23c6cfbfc7ff19ec8395141e37cfdb87e7e448cf1d8c0

Mining the block containing Send 1 BTC to Ivan

Prev. hash: 000000e8b0be7b9518b23c6cfbfc7ff19ec8395141e37cfdb87e7e448cf1d8c0
Data: Send 1 BTC to Ivan
Hash: 0000005abaa800aba85a0d0ae8bd11077af9bfdf41cbc96c217638b6990988aa

Prev. hash:
Data: Genesis Block
Hash: 000000e8b0be7b9518b23c6cfbfc7ff19ec8395141e37cfdb87e7e448cf1d8c0

Mining the block containing Pay 0.31337 BTC for a coffee

Prev. hash: 0000005abaa800aba85a0d0ae8bd11077af9bfdf41cbc96c217638b6990988aa
Data: Pay 0.31337 BTC for a coffee
Hash: 000000401cdc481e2c6698a801917e65dbc1ab0168aed077feef04623f8e1280

Prev. hash: 000000e8b0be7b9518b23c6cfbfc7ff19ec8395141e37cfdb87e7e448cf1d8c0
Data: Send 1 BTC to Ivan
Hash: 0000005abaa800aba85a0d0ae8bd11077af9bfdf41cbc96c217638b6990988aa

Prev. hash:
Data: Genesis Block
Hash: 000000e8b0be7b9518b23c6cfbfc7ff19ec8395141e37cfdb87e7e448cf1d8c0

请按任意键继续. . .

工程代码见群下载  文件名为CppBlockchain_part3.zip

参考博文:

https://blog.csdn.net/simple_the_best/article/details/78157303

https://jeiwan.cc/posts/building-blockchain-in-go-part-3/

cpp 区块链模拟示例(五) 序列化的更多相关文章

  1. cpp 区块链模拟示例(一)工程建立

    /* 作 者: itdef 欢迎转帖 请保持文本完整并注明出处 技术博客 http://www.cnblogs.com/itdef/ 技术交流群 群号码:432336863欢迎c c++ window ...

  2. cpp 区块链模拟示例(四) 区块链工作量证明

    本文主要在之前的区块链原形上添加了工作量证明,并且为后继的交易功能做好准备. 上一个章节我们已经创建了区块链的基本原形,但是区块的哈希计算和加入太过于简单,如果按照这种速度添加区块那么区块链估计一个小 ...

  3. cpp 区块链模拟示例(二)工程代码解析

    /* 作 者: itdef 欢迎转帖 请保持文本完整并注明出处 技术博客 http://www.cnblogs.com/itdef/ 技术交流群 群号码:432336863欢迎c c++ window ...

  4. cpp 区块链模拟示例(三)新基本原形工程的建立

    /* 作 者: itdef 欢迎转帖 请保持文本完整并注明出处 技术博客 http://www.cnblogs.com/itdef/ 技术交流群 群号码:432336863欢迎c c++ window ...

  5. cpp 区块链模拟示例(七) 补充 Merkle树

    Merkle 树 完整的比特币数据库(也就是区块链)需要超过 140 Gb 的磁盘空间.因为比特币的去中心化特性,网络中的每个节点必须是独立,自给自足的,也就是每个节点必须存储一个区块链的完整副本.随 ...

  6. cpp 区块链模拟示例(六) 交易

    交易(transaction)是比特币的核心所在,而区块链的唯一目的,也正是为了能够安全可靠地存储交易.在区块链中,交易一旦被创建,就没有任何人能够再去修改或是删除它.在今天的文章中,我们会实现交易的 ...

  7. 区块链开发(五)git、truffle安装

    truffle是以太坊最受欢迎的一个开发框架,本篇博客介绍truffle的下载安装过程. git安装 在安装truffle之前需要核实一下本机是否安装git程序.后面的程序安装需要依赖git. 输入以 ...

  8. 3星|《IBM商业价值报告:区块链》:一些重要行业对区块链的态度和已经发生的区块链的应用

    区块链项目开发指南 (区块链技术丛书) 介绍IBM的专家们调研许多重要行业与组织后总结的各行业对区块链的态度和实际的应用.看起来有点意思,不过有两个缺点: 1:这些实际已经发生的应用基本没看到相关的新 ...

  9. 用Java为Hyperledger Fabric(超级账本)开发区块链智能合约链代码之部署与运行示例代码

    部署并运行 Java 链代码示例 您已经定义并启动了本地区块链网络,而且已构建 Java shim 客户端 JAR 并安装到本地 Maven 存储库中,现在已准备好在之前下载的 Hyperledger ...

随机推荐

  1. 在Release版本下使用VLD

    前提 同Debug版本在VC中配置好VLD的相关信息,拷贝 Visual Leak Detector\bin\Win32目录下所有的文件和vld.ini到工程目标路径下. 强制检测 在程序入口处的cp ...

  2. 解决Ubuntn安装中文语言包却不能切换

    记一次奇葩的经历吧,第一次在VM中安装Ubuntn16.04安装完成后的确出现了语言包安装提示,就这样毫无压力的一直用着中文版的Ubuntn. 习惯了一段时间后第二次安装一样的安装方式却始终是英文界面 ...

  3. centos7-软件安装-jdk1.8

    JDK1.8下载地址: http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 安装目录 ...

  4. Linux Bash Shell字符串分割substring等(转)

    原文https://blog.csdn.net/wuyinggui10000/article/details/52779364 脚本开发中遇到的问题是:原java中的字符串分割操作(substring ...

  5. [SQL]某数据库中查出包含 字段名 的所有表名

    --利用SQL语句来查询字段所在的表 --从某数据库中查出包含 字段名 字段的所有表名 SELECT TABLE_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE ...

  6. CentOS7 安装kafka集群

    1. 环境准备 JDK1.8 ZooKeeper集群(参见本人博文) Scala2.12(如果需要做scala开发的话,安装方法参见本人博文) 本次安装的kafka和zookeeper集群在同一套物理 ...

  7. redis集群相关

    1.主从数据库配置 为master数据库添加slave数据库只需要在从数据库的配置中添加配置: slaveof 主数据库地址 主数据库端口 当然,也可以通过命令: redis-server --por ...

  8. 多端统一框架尝试--Taro

    参考资料 Taro官网Taro GitHubTaro资源汇总Taro-UI 我的demo代码 github地址 Taro介绍和尝试心得 Taro是基于React语法规范开发的多端统一的框架,一套代码可 ...

  9. volatile适用场景之二

    1.volatile最适用一个线程写,多个线程读的场合. 如果有多个线程并发写操作,仍然需要使用锁或者线程安全的容器或者原子变量来代替.(摘自Netty权威指南) 疑问:如果只是赋值的原子操作,是否可 ...

  10. python自带的进程池及线程池

    进程池 """ python自带的进程池 """ from multiprocessing import Pool from time im ...