gossip 区块链_源代码: 一个最小化的区块链系统
近期有個國內(nèi)著名技術(shù)協(xié)會的約稿,正好向技術(shù)圈分享一下我對區(qū)塊鏈系統(tǒng)的拙見。我發(fā)現(xiàn)一件有趣的事情,即使是有計算機背景,懂編程的同學(xué),都也不怎么清楚區(qū)塊鏈到底是怎么回事。今天這里,我打算用計算機語言和大家溝通,爭取可以至少讓計算機背景的同學(xué),徹底弄明白區(qū)塊鏈?zhǔn)钦厥?#xff0c;是怎么工作的。
不過在開始之前,需要明確的一件事情是,同之前的計算機技術(shù)不同,區(qū)塊鏈技術(shù)核心關(guān)乎的是一個計算系統(tǒng)的自動化監(jiān)管和治理,而不是為了讓計算更高效或更大規(guī)模地發(fā)生。需要明確這個期望,才方便我們?nèi)ダ斫?#xff0c;為什么區(qū)塊鏈?zhǔn)沁@樣設(shè)計的,這樣工作的。
偽代碼雜糅了C++和Javascript的語法,一點亂,歡迎大家來改進(jìn) (逃 ...
================= 預(yù)警分割線 ==============
好吧,這里開始,前方高能。
我們將以最簡化的加密數(shù)字貨幣為例介紹區(qū)塊鏈的精確工作原理,為了便于理解將省略手續(xù)費,大部分優(yōu)化,互操作性等層面的東西。這里會用到強類型的偽代碼,來精確定義其數(shù)據(jù)結(jié)構(gòu)和執(zhí)行邏輯。這里我們將從零開始實現(xiàn)一個類似以太坊數(shù)字貨幣那樣的區(qū)塊鏈系統(tǒng),為了便于理解,我們將采用以太坊所采用的賬戶-狀態(tài)模型來表示賬簿,而不是比特幣的那種UTXO。
我們先從一系列基礎(chǔ)實體和原語的定義開始:基礎(chǔ)數(shù)據(jù)類型
數(shù)字簽名原語標(biāo)準(zhǔn)的非對稱加密系統(tǒng)里面的函數(shù),公私鑰對可以在不聯(lián)網(wǎng)的情況下,任意生成,并且全球唯一。通常為32到64字節(jié)的無結(jié)構(gòu)二進(jìn)制數(shù)據(jù)。其中公鑰會公開,在區(qū)塊鏈系統(tǒng)中用來表明特定身份,供他人驗證其對特定賬戶的控制權(quán)。而私鑰則用來通過數(shù)字簽名來證明其對賬戶的控制。VerifySignature原語,用來對于給定數(shù)據(jù)和簽名,驗證是不是對應(yīng)的簽名者簽署的。
賬戶地址
在我們這里的例子中,所有哈希函數(shù)都采用SHA256,其將產(chǎn)生一個32字節(jié)的哈希值。地址是賬戶的標(biāo)識符,是一個32字節(jié)的無結(jié)構(gòu)二進(jìn)制數(shù)據(jù),由公鑰的哈希值 SHA256(PublicKey) 得到。那么也就是說每個公鑰,對應(yīng)一個唯一的地址,對應(yīng)一個唯一的賬戶。
智能合約 (Smart Contract)這個有點像一個 C++的類,定義了一些狀態(tài),以及修改這些狀態(tài)的函數(shù)。一個區(qū)塊鏈系統(tǒng)中,可以有多個智能合約同時存在,但是每個僅會有一個實例。這里我們就數(shù)字貨幣給出一個極度簡化的智能合約的例子:
class MyCoin {// internal statehash_map<Address, BigInt> _Ledger;// internal functionBigInt _GetBalance(Address addr){ if(_Ledger.has(addr))return _Ledger[addr];else return 0;}// 轉(zhuǎn)賬函數(shù)void Transfer(Address signer, Address from, Address to, BigInt amount){if(signer != from)return;if(amount > 0 && _GetBalance(from) >= amount){ _Ledger[from] -= amount;amount += _GetBalance(to);_Ledger[to] = amount;}}// 挖礦獎勵函數(shù)void CoinBase(int height, Address miner){BigInt reward = 5000000000; // 這里簡化為,每次獎勵50個幣if(reward > 0){reward += _GetBalance(miner);_Ledger[miner] = reward;}} };交易 (Transaction)
一個交易表示對特定相關(guān)賬戶一次狀態(tài)修改請求。交易中不攜帶任何邏輯代碼,僅僅是指定這個交易將調(diào)用智能合約里面的哪個公開函數(shù)及其調(diào)用參數(shù)。當(dāng)然在我們這個極度簡化的系統(tǒng)中,只有一種交易,即前面的轉(zhuǎn)賬(Transfer)。交易的發(fā)起方必須為扣款方(from),并且整個交易攜帶對交易內(nèi)容的數(shù)字簽名,以確信該交易由扣款方發(fā)起。基于我們這里的例子,一個交易至少含有以下結(jié)構(gòu):
區(qū)塊 (Block)
一個區(qū)塊表示區(qū)塊鏈接力執(zhí)行中的一步,里面主要包含這一步中確認(rèn)的一批交易,以及共識機制驗證數(shù)據(jù)和塊頭元數(shù)據(jù)。一個最簡化的定義可以是這樣:
這里我們給出了最簡化的工作量證明(Proof-of-Work)的驗證數(shù)據(jù)結(jié)構(gòu),如果采用其他共識算法,這個部分會有變化。從這個結(jié)構(gòu)可以看出,區(qū)塊鏈之所以稱為鏈,就是因為區(qū)塊結(jié)構(gòu)中包含一個指向上一個區(qū)塊的"指針",PrevBlock。任何一個被確認(rèn)的區(qū)塊,同時也意味著承認(rèn)其全部的前驅(qū)區(qū)塊,以及這些區(qū)塊所攜帶的全部交易。一個區(qū)塊被確認(rèn)有三個條件:
1. 這個區(qū)塊的共識驗證要滿足其特定共識算法的要求。在工作量證明算法中,PowTarget必須小于當(dāng)前挖礦難度的要求,同時 ((BigInt&)SHA256(Block)) < Block::PowTarget。
2. 這個塊所包含的交易必須沒有被之前的區(qū)塊包含過,并且每個交易必須能夠保證其數(shù)字簽名能夠被其Signer的公鑰正確驗證。至于交易所執(zhí)行的邏輯是否正確,是否出錯則無關(guān)緊要。
3. 在所有分叉塊中,即具有相同PrevBlock的塊,只有優(yōu)先的塊會被確認(rèn)。這一點不同的共識算法有不同的情況。P2P通訊原語
區(qū)塊鏈的網(wǎng)絡(luò)層僅用到了P2P網(wǎng)絡(luò)技術(shù)中簡單的部分,用基于TCP長連接的Gossip協(xié)議實現(xiàn)一個數(shù)據(jù)塊的全網(wǎng)廣播(Flooding)。我們這里將其抽象下面的通訊原語:
內(nèi)存池(Mempool)原語
內(nèi)存池在區(qū)塊鏈系統(tǒng)中用來記錄尚未被確認(rèn)的交易,很容易用比如哈希表來實現(xiàn)。
interface Mempool {bool Has(Transaction txn);void Insert(Transaction new_txn);void Remove(Transaction txns[count]);int Collect(Transaction txns[max_count]); };其中Collect原語用于挖礦時合成新的區(qū)塊,從mempool中挑出一系列交易來填充Txns數(shù)組,最多挑TxnMaxCount個,并返回實際填充的個數(shù)。
區(qū)塊歸檔數(shù)據(jù)庫原語
區(qū)塊鏈系統(tǒng)中的區(qū)塊以及交易,在被確認(rèn)之后,將從內(nèi)存中移除,并寫入歸檔數(shù)據(jù)庫中。這個部分很容易用一個Key-value storage系統(tǒng)來實現(xiàn),當(dāng)然用SQL數(shù)據(jù)可也是可以的,就是效率低一些。
interface ArchiveDatabase {void Archive(Transactiontxns[count]);void Archive(Block blk);void Has(Transaction txn);void Has(Block blk); }有了這些定義之后,我們可以給出一個不考慮分叉情況下最簡單的基于工作量證明的區(qū)塊鏈系統(tǒng)的偽代碼:
static const int TARGET_ADJUST_INTERVAL = 256; // 每隔256個塊調(diào)整一次算力難度 static const int BLOCK_CREATION_INTERVAL = 600*1000; //每十分鐘出一個塊 static const int TRANSCATION_PERBLOCK_MAX = 1024; // 每塊最多包含1024個交易BroadcastNetwork* g_pNet = BroadcastNetwork::Create(...); Mempool* g_pMempool = Mempool::Create(...); ArchiveDatabase* g_pArchiDB = ArchiveDatabase::Create(...);MyCoin g_MyLedger; // 賬簿// 當(dāng)前區(qū)塊鏈的頭 Block g_BlockHead = Block::GenesisBlock(6); // 初始化為創(chuàng)始區(qū)塊 HashValue g_BlockHeadHash = SHA256(g_BlockHead); int g_BlockNextHeight = 1; CriticalSection g_BlockHeadCS;// 下一個塊的共識相關(guān)信息 (工作量證明) PowTarget g_NextPowTarget = Block::InitialPowTarget(); // 初始挖礦難度 int g_LastTargetAdjustedTime;// 收到來自網(wǎng)絡(luò)廣播的交易 g_pNet-> OnRecvTransaction = [](Transaction txn) {if(g_pMempool->Has(txn) || g_pArchiDB->Has(txn))return; // 忽略已經(jīng)存在的交易if(!VerifySignature(txn.InvokeFunctionName + txn.InvokeArguments +txn.Signer,txn.Signer, txn.Signature))return;// 驗證簽名是否正確g_pNet->Broadcast(txn); // 基本驗證合法之后,接力這個交易的廣播g_pMempool->Insert(txn); };// 收到來自網(wǎng)絡(luò)廣播的區(qū)塊 g_pNet-> OnRecvBlock = [](Block blk) {if(blk.PrevBlock != g_BlockHeadHash)return; // 忽略亂序到達(dá)的塊,忽略分叉塊if(blk.PowTarget > g_NextPowTarget)return; // 忽略不滿足當(dāng)前算力要求的塊if(blk.TxnCount > TRANSCATION_PERBLOCK_MAX)return; // 忽略過于大的塊HashValue h = SHA256(blk);if( ((BigInt&)h) >= blk.PowTarget )return; // 忽略未達(dá)到當(dāng)前標(biāo)稱算力要求的塊// 校驗全部塊中的交易for(Int32 i=0; i<blk.TxnsCount; i++){auto& txn = blk.Txns[i];if( g_pArchiDB->Has(txn) || // 包含之前已經(jīng)被確認(rèn)過的交易!VerifySignature(txn.InvokeFunctionName + txn.InvokeArguments +txn.Signer,txn.Signer, txn.Signature) // 包含驗簽失敗的交易)return;}// 至此這個區(qū)塊被確認(rèn)g_pNet->Broadcast(txn); // 確認(rèn)之后,盡快接力這個區(qū)塊的廣播g_MyLedger.CoinBase(g_BlockNextHeight, Miner); // 執(zhí)行出塊獎勵for(auto& txn : blk.Txns) // 執(zhí)行每一條交易,然后歸檔{// 調(diào)用交易中指定的函數(shù)g_MyLedger[txn.InvokeFunctionName](txn.Signer, txn.InvokeArguments…);g_pArchiDB->Archive(txn);g_pMempool->Remove(txn); // 從內(nèi)存池中刪除,如果存在的話}g_pArchiDB->Archive(g_BlockHead); // 歸檔上一個區(qū)塊// 更新區(qū)塊鏈頭,這部分代碼需要和挖礦過程中構(gòu)造新的塊的步驟互斥g_BlockHeadCS.Lock();{if(g_BlockNextHeight%TARGET_ADJUST_INTERVAL == 1){// 進(jìn)行算力調(diào)整,周期為 TARGET_ADJUST_INTERVAL 個區(qū)塊if(g_BlockNextHeight > 1){ g_NextPowTarget = PowTargetAdjustment(g_NextPowTarget, blk.Timestamp - g_LastTargetAdjustedTime);}g_LastTargetAdjustedTime = blk.Timestamp;}// 更新區(qū)塊鏈頭在最新的這個塊g_BlockHeadHash = h;g_BlockHead = blk;g_BlockNextHeight++;}g_BlockHeadCS.Unlock(); };這里涉及到一個上面沒有定義的算法,PowTargetAdjustment是用來根據(jù)近期出塊速度來調(diào)整出塊算力難度要求,從而使得出塊的平均間隔的期望可以大體穩(wěn)定在一個預(yù)先設(shè)定的值(BLOCK_CREATION_INTERVAL)。這是一個和工作量證明共識算法有關(guān)的算法,并不是所有區(qū)塊鏈系統(tǒng)都有。這個算法的一個最簡化定義如下:
算力難度調(diào)整
BigInt PowTargetAdjustment(BigInt cur_target, int nth_block_interval) {return cur_target*nth_block_interval/(BLOCK_CREATION_INTERVAL*TARGET_ADJUST_INTERVAL); }到這里一個不出塊的區(qū)塊鏈節(jié)點,即全節(jié)點就可以工作了。全節(jié)點是區(qū)塊鏈網(wǎng)絡(luò)中的大多數(shù)節(jié)點,是區(qū)塊鏈底層P2P網(wǎng)絡(luò)得以穩(wěn)定魯棒運行的保障,同時也實現(xiàn)了區(qū)塊數(shù)據(jù)和交易數(shù)據(jù)的高度冗余的全網(wǎng)存儲。雖然不出塊,全節(jié)點不同于互聯(lián)網(wǎng)架構(gòu)的客戶端。一個全節(jié)點不需要信賴其他節(jié)點,更不存在一個服務(wù)器。全節(jié)點能夠獨立自主地驗證區(qū)塊鏈完整的歷史演進(jìn)過程,進(jìn)而重構(gòu)其上的狀態(tài) (例如一個賬戶的余額),而不是去向一個需要信賴的服務(wù)器查詢。
當(dāng)然,區(qū)塊鏈網(wǎng)絡(luò)計算接力過程是由出塊節(jié)點完成了,也就是所謂的礦工節(jié)點。這些少數(shù)節(jié)點,和大量的全節(jié)點混在一起,大部分節(jié)點收到最新的區(qū)塊是來自于其他全節(jié)點的接力廣播,而不是直接來自于一個出塊節(jié)點。當(dāng)然,作為接受方,也無從判斷發(fā)送方是中繼的全節(jié)點,還是剛剛出塊的礦工節(jié)點。這也有效地保護(hù)了真正出塊節(jié)點的安全性,避免暴露礦工節(jié)點的物理IP地址。
一個出塊節(jié)點,首先是一個全節(jié)點,除了上面定義的這些行為之外,還需要一個額外的過程,運行在一個或者多個線程上。我們定義最簡化的出塊過程如下:
void Mining() {while(g_KeepMining){// 構(gòu)造新的塊,這個部分需要和區(qū)塊鏈頭更新代碼互斥g_BlockHeadCS.Lock();{int next_height = g_BlockNextHeight;Block new_block;new_block.Timestamp = os::GetCurrentTime();new_block.PrevBlock = g_BlockHeadHash; // 指向最新的塊new_block.Miner = g_MyAddress;new_block.TxnCount = g_pMempool->Collect(new_block.Txns[TRANSCATION_PERBLOCK_MAX]);new_block.PowTarget = g_NextPowTarget;new_block.PowNonce = os::Random<Int64>(); // 隨機初始值}g_BlockHeadCS.Unlock();// 開始挖礦while(next_height == g_BlockNextHeight){if( ((BigInt64&)SHA256(new_block)) < new_block.PowTarget ){// 挖礦成功g_pNet->Broadcast(new_block); // 立即廣播出去g_pNet->OnRecvBlock(new_block); // 更新本節(jié)點的區(qū)塊鏈頭break; // 開始去挖下一個塊}new_block.PowNonce++; // 嘗試下一個Nonce}// 大多數(shù)情況下,其他節(jié)點出了新的塊,并更新了區(qū)塊高度// 則挖礦被打斷,去挖更新之后的下一個塊} }總結(jié)
以上是生活随笔為你收集整理的gossip 区块链_源代码: 一个最小化的区块链系统的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: update yum 到指定版本_yum
- 下一篇: dft变换的两幅图_快速傅里叶变换FFT