有了區塊和區塊鏈的基本結構,有了工做量證實,咱們已經能夠開始挖礦了。剩下就是最核心的功能-交易,可是在開始實現交易這一重大功能以前,咱們還要預先作一些鋪墊,好比數據的序列化和啓動命令解析。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/