有了区块和区块链的基本结构,有了工做量证实,咱们已经能够开始挖矿了。剩下就是最核心的功能-交易,可是在开始实现交易这一重大功能以前,咱们还要预先作一些铺垫,好比数据的序列化和启动命令解析。c++
根据《用 Go 构建一个区块链》的目录, 本章节的区块数据的序列化存储会使用一款KV数据库。其中比特币中是使用的是谷歌出品、c++编写的 LevelDB数据库,go语言示例中使用的是BoltDB。git
我原本考虑使用redis和json来进行咱们的数据序列化存储。使用boost库的program_options 解析命令行参数。可是考虑代码过于复杂也许会偏离演示区块链的属性这一目的。最后进行了精简,最终的方案是舍去命令行参数解析,数据序列化改成使用map容器做为哈希与区块block指针的映射记录。github
咱们代替序列化的数据结构为map<string, Block*> g_db;redis
该结构是一个哈希值与区块指针的映射,因为每一个区块的哈希值的都是独一无二的,必定程度上哈希就至关于KV数据库中KEY。数据库
咱们经过独一无二的哈希值能够快速的map容器中查找的到区块指针,这一过程与使用kv数据的增删改查接口是基本相同的。因此使用map能达到使用kv数据同样的演示效果。json
让咱们从 NewBlockchain
函数开始。在以前的实现中,它会建立一个新的 Blockchain
实例,并向其中加入创世块。而如今,咱们但愿它作的事情有数组
Blockchain
实例,其 tip 指向创世块(tip 有尾部,尖端的意思,在这里 tip 存储的是最后一个块的哈希1 Blockchain* NewBlockchain() { 2 string tip; 3 Block* genesis = NewGenesisBlock(); 4 g_db[genesis->hash] = genesis; 5 g_db["l"] = genesis; 6 tip = genesis->hash; 7 8 Blockchain* bc = new Blockchain{ tip, &g_db }; 9 10 return bc; 11 }
咱们建立了一个创世块,而且记录到全局map中,创始块哈希映射创始块的指针。 “l”字符串映射目前最后一个区块指针,刚好也是目前惟一的一个区块-创世块。 tp记录最后一个区块的哈希,也是目前惟一的一个区块-创世块的哈希数据结构
Blockchain
的结构如今看起来是这样:函数
typedef struct blockchain { string tip; map<string, Block*>* db; }Blockchain;
接下来咱们想要更新的是 AddBlock
方法:如今向链中加入区块,就不是像以前向一个数组中加入一个元素那么简单了。从如今开始,咱们会将区块存储在数据库里面:post
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 0; }
运行结果以下:
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/