第二章 工作量證明和挖礦

2022-03-29 06:13:39 字數 4584 閱讀 2366

本章節我們將會在我們的玩具版區塊鏈的基礎上加入工作量證明(pow)的支援。在第一章節的版本中, 任何人都都可以在沒有任何工作量證明的情況下新增乙個區塊到區塊鏈中。 當我們引入工作量證明機制之後,乙個節點必須要解開乙個有相當計算量的拼圖(pow puzzle)之後,才能往區塊鏈上新增乙個新的區塊。而去解開該拼圖,通常就被稱為挖礦。

引入工作量證明機制之後,我們還可以對乙個新區塊的產出時間作出大致的控制。大概的做法就是動態的改變拼圖的難易程度來達到控制的效果:如果最近的區塊產生的太快了,那麼就將拼圖的難度提公升,反之,則將拼圖的難度降低。

需要點出來的時,本章節中我們還沒有引入交易(transaction)這個概念。這就意味著礦工挖出乙個區塊後,並不會獲得相應的獎勵。 一般來說,在加密貨幣中,如果礦工挖到了乙個區塊,是應該獲得一定量的幣作為激勵的。

工作量證明拼圖是乙個什麼樣的任務呢?其實就是去計算出乙個滿足條件的區塊雜湊。怎麼才算滿足條件呢?如果這個計算出來的雜湊的前面的0的數目滿足指定的個數,那麼就算滿足條件。那麼這個個數又是誰指定的呢?對,就是上面的這個difficulty指定的。如果difficulty指定說雜湊前面要有4個0,但你計算出來的雜湊前面只有3個0,那麼這個雜湊就不是個有效的雜湊。

下圖展示的就是不同難易程度的情況下,雜湊是否有效:

以下**用來檢查指定difficulty下雜湊是否有效:

const hashmatchesdifficulty = (hash: string, difficulty: number): boolean => ;
區塊的雜湊是通過對區塊的內容算sha256來獲得的,通過相同的區塊內容做雜湊,我們是無法算出符合指定difficulty的雜湊, 因為內容一直沒有變,雜湊也就一直不變, 這就是為什麼我們引入了nonce這個屬性到區塊中,我們可以控制nonce的改變來獲得不同的雜湊值。只要我們的內容有任何一點點的改變,算出來的雜湊就肯定是不一樣的。一旦相應的nonce修改後讓我們獲得到指定difficulty的雜湊,我們挖礦也就成功了!

既然加入了difficulty和nonce到區塊,我們還是先看看區塊結構現在長什麼樣吧:

class block 

}

當然,我們的創世區塊是硬編碼的,記得把它也給更新成相應的結構哦。

如上所述, 為了解開我們的工作量證明拼圖這個任務,我們需要不停的修改區塊中的nonce然後計算修改後的雜湊,直到算出滿足條件的雜湊。這個滿足條件的雜湊什麼時候才會跑出來則完全是個隨機的事情。所以我們要做的就是給nonce乙個初始值,然後不停的迴圈,修改,計算雜湊,直到滿足條件的心儀的它的出現。

const findblock = (index: number, previoushash: string, timestamp: number, data: string, difficulty: number): block => 

nonce++;}};

一旦滿足條件的區塊的雜湊給找到了,挖礦成功!然後我們就需要將該區塊廣播到網路上,讓其他節點接受我們的區塊,並更新最新的賬本。這個和第一章節的情況並無二致。

我們現在已經擁有找出和驗證滿足指定難易度的區塊的雜湊的手段了,但是,這個難易程度是如何決定的呢?必須要有乙個方法讓全網各個節點一致認同這個決定。不然我的難易度是要挖半天才能出來,別人的是幾毫秒就出來,那這個區塊鏈網路就有問題。

所以,這裡必須要有乙個方法來讓所有的節點都一致認同當前挖礦的難易度。為了做到這一點,我們首先引入一些用於計算當前難易度的規則。

先定義以下的一些常量:

這裡我們將會把區塊產出間隔設定成10秒,難易度調整間隔設定成10個區塊。這些常量是不會隨著時間而改變的,所以我們將其硬編碼如下:

// in seconds

const block_generation_interval: number = 10;

// in blocks

const difficulty_adjustment_interval: number = 10;

有了這些規則,我們就可以在我們的網路上達成難易度的一致性了。每產出10個區塊之後,我們就去檢查生成所有這10個區塊所消耗的時間,然後和預期時間進行對比,然後對難易度進行動態的調整。

這裡的預期時間是這樣指定的:block_generation_interval * difficulty_adjustment_interval。 該預期時間代表了當前網路的算力剛剛好和當前的難易度吻合。

如果耗時超過或者不足預期時間的兩倍,那麼我們就會將difficulty進行對應的減1或者加1。該演算法如下:

const getdifficulty = (ablockchain: block): number =>  else 

};const getadjusteddifficulty = (latestblock: block, ablockchain: block) => else if (timetaken > timeexpected * 2) else

};

第一章節中的區塊鏈版本中,區塊結構中的時間戳屬性是沒有任何意義的,因為我們不會對其作任何校驗的工作,也就是說,我們其實可以給該時間戳賦以任何內容。 但現在情況變了,因為我們這裡引入了難易度的動態調整,而該調整是基於前10個區塊產出的耗時總長度的。所以時間戳我們就不能像之前一樣隨意賦值了。

既然時間戳的大小會影響區塊產出難易度的調整,所以別有用心的人就會考慮去惡意設定乙個錯誤的時間戳來嘗試操縱我們的難易度,以實現對我們的區塊鏈網路進行攻擊。為了規避這種風險,我們需要引入以下的規則:

const isvalidtimestamp = (newblock: block, previousblock: block): boolean => ;
天地會珠海分舵注:這裡為什麼要有一分鐘的緩衝呢?估計是為了既考慮一定程度的容錯,也減緩了時間戳惡意修改的攻擊。如果還有其他原因的,請不吝指教。

還記得第一章節的區塊鏈的版本中,我們定的規則是:一旦發生衝突,我們總是選擇最長的區塊鏈作為有效的鏈進行更新,以示對更多區塊生產者的努力的肯定。因為我們現在引入了挖礦的難易度,所以我們不應該再這樣子做了,我們更應該對投入資源更多計算而出的那條鏈進行肯定。也就是說,現在正確的鏈,不是最長的那條鏈,而是累積難易度最大的那條鏈。什麼意思呢?換個說法就是,正確的鏈,應該是那條消耗了最多計算資源(網路算力*耗時)而產生出來的鏈。

那麼怎麼算出一條鏈的累積難易度呢?我們首先對區塊鏈中每個區塊的difficulty進行2^difficulty計算,然後將每個區塊的計算結果加起來,最終就是這條鏈的累積難易度。

這裡為什麼我們用2difficulty來計算呢?因為difficulty代表了雜湊的二進位制表示中前面的0的位數。試想下,乙個difficulty是11的塊和乙個difficulty是5的塊相比,總的來說,我們需要多計算2(11-5) = 2^6 次雜湊才能獲得想要的結果。因為每一位在二進位制中都有0和1兩種變化,他們之間相差6個位,固有2^6個變化。

下圖中,雖然鏈a比鏈b長,但因為鏈b的累積難易度比鏈a大,所以鏈b才是正確的鏈.

同時我們還要注意的是,我們累積難易度只和區塊的difficulty屬性有關係,和區塊的真實雜湊即其前面的位數是沒有任何關係的。 拿乙個difficulty為4的區塊來說,假如它的雜湊是000000a34c...(該雜湊同時也滿足difficulty為5和6的情況),我們計算累積難易度是還是會以24來算,而不是25或者2^6, 即使它前面有6個0.

這種根據難易度選擇正確的鏈的策略也叫做中本聰共識(nakamoto consensus), 這也是中本聰的位元幣中最重要的乙個發明之一。一旦賬本出現分叉,礦工們就必須選擇一條鏈來投入資源繼續進行挖礦,因為這個關係到礦工挖礦後的激勵,所以選擇正確的鏈必須全網達成共識。

npm install

npm run node1

npm run node2

我們可以嘗試通過curl或者postman呼叫/mineblock這個api介面來進行挖礦,並檢視對應的輸出.

在通過不同的時間間隔呼叫完10次/mineblock這個介面之後,難易度就會相應的進行變化

這個真實情況不好模擬出來。 但是我們可以去驗證對應的演算法。我們可以先開乙個節點,建立3個以上的區塊之後,再開第二個節點。這時節點2就會去獲取節點1的所有區塊,然後執行對應的邏輯去選擇最大累積難易度的鏈。

工作量證明拼圖的乙個重要特點就是難以解開但易於驗證。所以不斷調整nonce以算出乙個滿足一定難度的sha256雜湊,然後簡單的驗證前面幾位是否是0,往往就是這種問題最簡單的解決方案。

有了工作量證明機制後,節點現在就需要挖礦,也就是解決工作量證明拼圖,才能往區塊鏈中新增加乙個區塊了。在下一章節中,我們將會為我們的區塊鏈引入交易(transaction)功能。

本章節**請檢視這裡

第三章

位元幣如何挖礦(挖礦原理) 工作量證明

在區塊鏈記賬原理 一篇,我們了解到記賬是把交易記錄 交易時間 賬本序號 上乙個hash值等資訊計算hash打包的過程。我們知道所有的計算和存貯是需要消耗計算機資源的,既然要付出成本,那節點為什麼還要參與記賬呢?在中本聰 位元幣之父 的設計裡,完成記賬的節點可以獲得系統給與的一定數量的位元幣獎勵,這個...

第二章 訊號量

整形訊號量 記錄型訊號量 這些wait 都是 1 signal都是 1 不同的是 整型訊號量wait先判斷小於0然後再 1 signal沒有判斷直接 1 記錄型訊號量wait先 1 再判斷小於0 如果小於0 就從這個小於0的地方鎖上 block 然後signal的時候先 1 然後再從鎖住的地方解鎖。...

工作量證明(POW)和股權證明(POS)共識機制

一 pow proof of work 工作量證明機制。基本原理 第一代共識機制,位元幣的基礎。理解起來,很簡單,就是 按勞取酬 你付出多少工作量,就會獲得多少報酬 位元幣等加密貨幣 在網路世界裡,這裡的勞動就是你為網路提供的計算服務 算力x時長 提供這種服務的過程就是 挖礦 假如是真的礦場,顯然在...