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

根据《用 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. IIS7.5 PHP环境HTTP经常500错误处理方法

    IIS使用FastCGI方式配置PHP以后,在编写PHP程序时,经常会出现HTTP 500错误 HTTP 500(Internal Server Error):服务器尝试执行请求时遇到了意外情况.研究 ...

  2. mysql中的锁

    MYSQL不同的存储引擎支持不同的锁的机制 MyISAM 支持表锁,InnoDB支持表锁和行锁 表锁,行锁比较 表锁:开销小,加锁快:不会出现死锁:锁定力度大,发生锁冲突概率高,并发度最低 行锁:开销 ...

  3. 华硕飞马3S,日常使用续航测试

    最近爱机荣耀6的电池1天2充,无奈换台新机,华为系列没大电池且价格贵,小米红米系列品控呵呵,其他品牌无小屏幕大容量电池: 然后换了台华硕飞马3S:5.2英寸 5000ma电池,日常工作娱乐使用1天半多 ...

  4. linux git 保存用户名和密码

    一.通过文件方式 1.在~/下, touch创建文件 .git-credentials, 用vim编辑此文件,输入内容格式: touch .git-credentials vim .git-crede ...

  5. 在高并发情况nginx的作用

    1 场景一:如图 在单机的情况下例如:单个tomcat 有100w条请求的时候,而默认tomcat支持的并发数量并不能达到要求,所所以单台服务器 扛不住 容易宕机,瘫痪 2 高并发的情况下要让服务器不 ...

  6. Deployment Descriptor Web.xml

    Deployment Descriptor部署描述符: - 部署描述符是要部署到Web容器或EJB容器的Web应用程序或EJB应用程序的配置文件. - 部署描述符应包含EJB应用程序中所有企业bean ...

  7. 03-封装Response响应

    package com.day5; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputSt ...

  8. JS高级-异步

    单线程 只有一个线程,同一时间只能做一件事 原因:避免DOM渲染的冲突 浏览器需要渲染DOM JS可以修改DOM结果 JS执行的时候,浏览器DOM渲染会暂停 两段JS也不能同时执行(修改DOM就冲突) ...

  9. kubernetes学习笔记之十:RBAC

    第一章.RBAC介绍 在Kubernetes中,授权有ABAC(基于属性的访问控制).RBAC(基于角色的访问控制).Webhook.Node.AlwaysDeny(一直拒绝)和AlwaysAllow ...

  10. ubuntu vsftp

    转自:https://www.cnblogs.com/java-synchronized/p/6935711.html 12点多了,擦!做个码农真不容易呀! 系统:Ubuntu16.04 安装:FTP ...