banner
leaf

leaf

It is better to manage the army than to manage the people. And the enemy.
follow
substack
tg_channel

區塊鏈技術與應用-肖臻

1.1 密碼學基礎
哈希

範圍足夠大 2**256

需要符合以下三條性質:

collision resistance 抗哈希碰撞能力  
hiding 能夠隱藏輸入信息,也就是沒法從哈希結果反向推導出輸入或者輸入的規律  
puzzle friendly 保證挖礦的難度,除了暴力窮舉沒有捷徑,pow 工作量證明  

非對稱加密

交易時 私鑰簽名,公鑰驗證
1.2 私鑰和地址

隨機生成私鑰,然後通過橢圓曲線乘法可以生成一系列公鑰。

比特幣不直接用公鑰作為地址,而是做了一系列變換:

A = RIPEMD160(SHA256(K))

A: 地址
K: 公鑰

後續會再進行 base58 之類的編碼。總之公鑰和地址是一對一的,從公鑰能夠計算出地址,反之不行。
1.3 核心數據結構

哈希指針: H 不光保存指向結構的地址,還需要保存目標的哈希值。
區塊鏈

就是使用哈希指針的區塊組成的鏈表。

image

image

image

區塊頭是 80 字節,而區塊要包含交易,所以一般來說區塊比區塊頭大小大成百上千倍。

可以發現每個區塊頭總是包含上一個區塊的哈希值,所以任何區塊的修改都會導致後續區塊的哈希值改變,所以只要檢查最新塊哈希值就可以確定之前所有區塊數據有沒有被篡改。

當前塊的哈希就是礦工計算出來的,不存在區塊鏈系統內部,節點接到新區塊廣播時,只需要計算出當前塊哈希然後驗證是否符合難度就行,不需要礦工提供。但是應用層為了能夠高效查詢數據,會在應用層維護區塊哈希到區塊數據的索引。

創世塊:第一個區塊,區塊鏈的頭,寫死在代碼中

區塊高度:創世塊為 0,後續每產生一個新區塊高度加一

挖礦:簡單來說就是通過嘗試不同的 Nonce (Timestamp 和 Merkle Root 也可以用,後續說明),使得當前區塊哈希 H (block header) <= target,即前面必須有一定數目個 0 (例如 000000000000000000040b38097e0a61ef1ad31b184c908a738cfff013c094b2)。

merkle tree

保存區塊中的交易數據。

和 binary tree 類似,但是有兩點不同:

使用哈希指針  
只有葉節點存儲交易信息,中間節點存儲左右子節點哈希的哈希  

網課知識點:

第二節:

比特幣被稱為加密貨幣 crypto-currency
區塊鏈上內容都是公開的,包括區塊的地址,轉帳的金額。

比特幣主要用了密碼學中的兩個功能:1. 哈希 2. 簽名

image

  1. 密碼學中用到的哈希函數被稱為 cryptographic hash function: 它有兩個重要的性質:
    ①collision (這裡指哈希碰撞) resistance : 例如 x≠y H (x)=H (y) 兩個不同的輸入,輸出卻是相等的,這就稱哈希碰撞。它是不可避免的,因為輸入空間總大於輸出空間。給出 x,很難找到 y,除非蠻力求解 (brute-force)。
    該性質的作用:對一個 message 求 digest
    比如 message 取 m m 的哈希值是 H (m)=digest 如果有人想篡改 m 值而 H (m) 不變,則無法做到。
    哈希碰撞無法人為製造,無法驗證,是根據實踐經驗得來的。
    ②hiding 哈希函數的計算過程是單向的,不可逆的。(從 H (x) 無法推導出 x) hiding 性質前提是輸入空間足夠大,分佈比較均勻。如果不是足夠大,一般在 x 後面拼接一個隨機數,如 H (x||nonce)。
    該性質的作用:和 collision resistance 結合在一起,用來實現 digital commitment (又稱為 digital equivalent of a sealed envelope)
    把預測結果作為輸入 x,算出一個哈希值,講哈希值公布,hiding 讓人們知道哈希值而不知道預測值,最後再將 x 公布,因為有 collision resistance 的性質,預測結果是不可篡改的。

除了密碼學中要求的這兩個性質外,比特幣中用到的哈希函數還有第三個性質:
③puzzle friendly 指哈希值的預算事先是不可預測的。假如哈希值是 00...0XX...X,一樣事先無法知道哪個值更容易算出這個結果,還是要一個一個帶入。

比特幣挖礦的過程中實際就是找一個 nonce,nonce 跟區塊的塊頭裡的其他信息合一起作為輸入,得出的哈希值要小於等於某個指定的目標預值。H (block header)≤target。block header 指塊頭,塊頭裡有很多域,其中一個域是我們可以設置的隨機數 nonce,挖礦的過程是不停的試隨機數,使得 block header 取哈希後落在指定的範圍之內。

puzzle friendly 是指挖礦過程中沒有捷徑,為了使輸出值落在指定範圍,只能一個一個去試。所以這個過程還可以作為工作量證明 (proof of work)。

image

挖礦很難,驗證很容易。(difficult to solve ,but easy to verify)

比特幣中用的哈希函數叫作 SHA-256 (secure hash algorithm) 以上三個性質它都是滿足的。

在比特幣系統中開賬戶:
在本地創立一個公私鑰匙對 (public key ,private key),這就是一個賬戶。公私鑰匙對是來自於非對稱的加密技術 (asymmetric encryption algorithm)。

兩人之間信息的交流可以利用密鑰 (encryption key),A 將信息加密後發給 B,B 收到後用密鑰解密,因為加密和解密用的是同一個密鑰,所以叫對稱加密。前提是有渠道可以安全地把密鑰分發給通訊的雙方。因此對稱加密的缺點就是密鑰的分發不方便,因為在網絡上很容易被竊聽。非對稱密鑰是用一對密鑰而不是一個,加密用公鑰,解密用私鑰,加密和解密用的都是接收方的公鑰和私鑰。公鑰是不用保密的,私鑰要保密但是私鑰只要保存在本地就行,不用傳給對方。公鑰相當於銀行賬號,別人轉帳只要知道公鑰就行,私鑰相當於賬戶密碼,知道私鑰可以把賬戶上錢轉走。公鑰和私鑰是用來簽名。

假如 A 想向 B 轉 10 個比特幣,A 把交易放在區塊鏈上,別人怎麼知道這筆交易是 A 發起的呢?這就需要 A 要用自己的私鑰給交易簽名,其他人收到這筆交易後,要用 A 的公鑰去驗證簽名。簽名用私鑰,驗證用公鑰,用的仍然是同一個人的。創建賬戶產生相同公私鑰的可能性微乎其微,所以大量創建賬戶來竊取其他人賬戶是不可行的。

我們假設產生公私鑰時有一個好的隨機源 (a good source of randomness),產生公私鑰是隨機的,如果隨機源不好,就有可能產生相同的公私鑰。比特幣中用的簽名算法,不僅是生成公私鑰的時候要有好的隨機源,之後每一次簽名時也要有好的隨機源。只要有一次簽名用的隨機源不好的話,就有可能洩露私鑰。

第三節:比特幣的數據結構

普通指針存儲的是某個結構體在內存中的地址。假如 P 是指向一結構體的指針,那麼 P 裡面存放的就是該結構體在內存中的起始位置。而哈希指針除了要存地址之外,還要保存該結構體的哈希值 H ()。好處是:從哈希值這個哈希指針,不僅可以找到該結構體的位置,同時還能夠檢測出該結構體的內容有沒有被篡改,因為我們保存了它的哈希值。

比特幣中最基本的結構就是區塊鏈,區塊鏈就是一個一個區塊組成的鏈表。區塊鏈和普通的鏈表相比有什麼區別:
①用哈希指針代替了普通指針 (B block chain is a linked list using hash pointers)

區塊鏈第一個區塊叫作創世塊 (genesis block) 最後一個區塊 是最近產生的區塊 (most recent block) 每一個區塊都包含指向前一個區塊的哈希指針
一個區塊的哈希指針怎麼算:是把前面整個區塊的內容,包括裡面的 hash pointer,合在一起取哈希值。通過這種結構,可以實現 tamper-evident log。如果有人改變了一個區塊的內容,後面一個區塊的哈希指針就對不上,因為後一個區塊哈希指針是根據前一個區塊的內容算出來的,所以後一個哈希指針也得改,以此類推,我們保留的是最後一個哈希值也會變化。(見拍的圖①)

②普通鏈表可以改變任意一個元素,對鏈表中其他元素是沒有影響的。而區塊鏈是牽一發而動全身,因為只需要保存最後一個哈希值,就可以判斷區塊鏈有沒有改變,在哪裡改變了。
因此比特幣沒有要保存所有區塊的內容,可以只保留最近的幾千個區塊。如果要用到以前的區塊,可以向系統中其他節點要這個區塊。有些節點是有惡意的,怎麼判斷?這裡要用到哈希值一個性質,如下:
其他節點給你一個區塊,如何判斷它是正確的?算出它的哈希值,與保留的區塊的哈希值對比,即可。

比特幣中的另外一個結構是 tree。(見拍的圖②,其中最下面一層是數據塊 (data blocks),上面三層內部節點都是哈希指針 (hash pointers),第一層是根節點,根節點的區塊也可以取個哈希,叫根哈希 (root hash))
另外一個概念 tree。

這種結構的好處:只要記住根哈希值,就能檢測出對樹中任何部位的修改。
它們的區別:①用哈希指針代替了普通指針。

比特幣當中各區塊之間用哈希指針連接在一起,每個區塊所包含的交易組織成一個 merkle tree 的形式,最下面一行 data blocks 每個區塊實際上是一個交易,每個區塊分為兩部分,分別是塊頭和塊身 (block header ,block body)。塊頭裡面有根哈希值,每個區塊所包含的所有交易組成的 merkle tree 的根哈希值存在於區塊的塊頭裡面,但是,塊頭裡沒有交易的具體內

容,只有一個根哈希值,塊身裡面是有交易的列表的。

merkle tree 的作用:

①提供 merkle proof
比特幣中的節點分為兩類:全節點 (保存整個區塊的內容,即塊頭塊身都有,有交易的具體信息) 和輕節點 (例如手機上的比特幣錢包)(只有塊頭)

這時存在一個問題:如何向一個輕節點證明某個交易是寫入區塊鏈的?
這時需要用到 merkle proof : 找到交易所在的位置 (最底行的其中一個區塊),這時該區塊一直往上到根節點的路徑就叫 merkle proof。

拍的圖三:最上面一行是小型的區塊鏈,該圖展現的是一個區塊的 merkle tree,最下面一行是包含的交易。假設某個輕節點想知道圖中黃色的交易,是否包含在了 merkle tree 裡面。該輕節點沒有包含交易列表,沒有這棵 merkle tree 的具體內容,只有一個根哈希值。這時輕節點向一個全節點發出請求,請求證明黃色的交易被包含在這棵 merkle tree 裡面的 merkle proof。全節點收到這個請求之後,只需要將圖中標為紅色的這三個哈希值發給輕節點即可。有了這些哈希值之後,輕節點可以在本地計算出圖中標為綠色三個哈希值。首先算出黃色交易的哈希值,即它正上方的那個綠的哈希值,然後跟旁邊紅色的哈希值拼接起來,可以算出上層節點綠色的哈希值。然後再拼接,再算出上層綠色哈希值,再拼接,就可以算出整棵樹的根哈希值。輕節點把這個根哈希值和 block header 裡的根哈希值比較一下,就能知道黃色的交易是否在這棵 merkle tree 裡。

全節點在 merkle proof 裡提供的這幾個哈希值,就是從黃色的交易所在的節點的位置到樹根的路徑上用到的這些哈希值。輕節點收到這樣一個 merkle proof 之後,只要從下往上驗證,沿途的哈希值都是正確的即可。(驗證時只能驗證該路徑的哈希值,其他路徑是驗證不了的,即該圖中紅色的哈希值是驗證不了的)

這樣是否不安全呢?假如黃色交易被篡改,它的哈希值發生了變化,那能不能調整旁邊紅色的哈希值,使得它們拼接起來的哈希值是不變的呢?不行,根據 collision resistance,這是不可行的。

merkle proof 可以證明 merkle tree 裡面包含了某個交易,所以這種證明又叫 proof of membership 或 proof of inclusion。
對於一個輕節點來說,驗證一個 merkle proof 複雜度是多少?假設最底層有 n 個交易,則 merkle proof 複雜程度是 θ(log (n))。

如何證明 merkle tree 裡面沒有包含某個交易?即 proof of non-membership。可以把整棵樹傳給輕節點,輕節點收到後驗證樹的構造都是對的,每一層用到的哈希值都是正確的,說明樹裡只有這些葉節點,要找的交易不在裡面,就證明了 proof of non-membership。問題在於,它的複雜度是線性的 θ(n),是比較笨的方法。

如果對葉節點的排列順序做一些要求,比如按照交易的哈希值排序。每一個葉節點都是一次交易,對交易的內容取一次哈希,按照哈希值從小到大排列。要查的交易先算出一個哈希值,看看如果它在裡面該是在哪個位置。比如說在第三個第四個之間,這時提供的 proof 是第三個第四個葉節點都要往上到根節點。如果其中哈希值都是正確的,最後根節點算出的哈希值也

是沒有被改過的,說明第三、四個節點在原來的 merkle tree 裡面,確實是相鄰的點。要找的交易如果存在的話,應該在這兩個節點中間。但是它沒有出現,所以就不存在。其複雜度也是 log 形式,代價是要排序。排好序的叫作 sorted merkle tree。比特幣中沒有用到這種排好序的 merkle tree,因為比特幣中不需要做不存在證明。

這節講了比特幣中兩種最基本的結構:區塊鏈和 merkle tree,都是用哈希指針來構造的。除了這兩種之外,哈希指針還能用另一個方面。

只要一個數據結構是無環的 (非循環鏈表),都能用哈希指針代替普通指針。有環的話存在一個問題,他們的哈希值沒法計算,沒法確定一個哈希值固定的區塊。

比特幣的共識協議#

數字貨幣和紙質貨幣區別是可以複製,叫作雙花攻擊 即 double spending attack。
去中心化貨幣要解決兩個問題:①數字貨幣的發行②怎麼驗證交易的有效性,防止 double spending attack。

答案:①比特幣的發行是由挖礦決定的
②依靠區塊鏈的數據結構
比特幣的發行者 A 擁有鑄幣權 (createcoin) 假如發行 10 個比特幣 A (10) 分別給 B 和 C 各五個 → B (5) C (5) 該交易需要有 A 的簽名,證明經 A 同意。(designed by A) 同時還要說明花掉的 10 個比特幣從哪來的。
參考拍的圖四 第二個方框中的錢是從第一個框內鑄幣交易中來的。

比特幣系統中每個交易都包含輸入和輸出兩部分。輸入部分要說明幣的來源,輸出部分要給出收款人公鍵的哈希。
有的交易部分比較複雜,如 C 的貨幣來源是第二第三個方框,要標識清楚。

圖四就構成了一個小型的區塊鏈,這裡有兩種哈希指針,一種哈希指針是連接在各個區塊之間的,把它們串起來構成一個鏈表,前面學的就是這種哈希指針。而在該圖中還有第二種哈希指針,是指向前面某個交易的指針,用來指明幣的來源。為什麼要說明幣的來源:證明幣不是憑空捏造的是有記錄的,同時也是防範 double spending。

現在來看第二個方框裡 A 向 B 的轉帳,該交易需要 A 的簽名和 B 的地址。比特幣系統裡收款的地址是通過公鍵推算出來的。比如 B 的地址就是 B 的公鍵取哈希然後經過一些轉換得到的。

A 如何知道 B 的地址?比特幣系統中沒有查詢對方地址的功能,必須通過其他渠道。比如某個電商網站,接受比特幣支付,就可以公開它的地址或公鍵。

A 需要知道 B 的地址,B 需要知道 A 的什麼信息嗎?B 其實也要知道 A 的公鍵,這代表 A 的身份。不僅是 B,所有節點都需要知道 A 的公鍵。而簽名是用私鑰簽名公鑰驗證 (注意不要跟前面知識弄混了,加密是用接收人的公鑰加密私鑰解密),所以區塊鏈上每個節點都要獨立驗證。

那如何才能知道 A 的公鑰?實際上交易裡就包含了。輸入時不僅要輸入幣的來源,還要輸入公鑰。那就存在了安全漏洞,假如 B 的同夥偽造了這次交易呢?其實第一個方框裡鑄幣交易的輸出就有 A 的公鍵的哈希,所以第二個方框交易裡 A 的公鍵要跟前面哈希對的上。

在比特幣系統當中,前面這些驗證過程,是通過執行腳本來實現的。每個交易的輸入提一段腳本,包括給出公鍵的過程,公鍵也是在輸入的腳本裡指定的。每個交易的輸出也是一段腳本,驗證其的合法性,就需要把當前交易的輸入腳本跟前面交易 (提供幣來源的交易) 的輸出腳本拼在一起,然後看看能不能順利執行,如果能執行說明是合法的。比特幣腳本 (BitCoin Script)。

image

該圖對交易系統進行了簡化,實際上每個區塊 (對應圖中的每個方框) 可以有很多交易,這些交易就組成 merkle tree。每個區塊分為塊頭和塊身。

塊頭包含的是區塊的宏觀信息,比如:用的是比特幣哪個版本 (version) 的協議,區塊鏈當中指向前一個區塊的指針 (hash of previous block header),整顆 merkle tree 的根哈希值 (merkle root hash),還有兩個域是跟挖礦相關的,一個是挖礦的難度目標預值 (target),另一個是隨機數 nonce。

這裡的 target,就是前面講到的,整個塊頭的哈希要小於這個預值,即 H (block header)≤target。block header 裡存的就是這個目標預值的編碼 (nBits)。這裡需要注意,前一個區塊的哈希只算的是前一個區塊的塊頭,所以前面畫的,一個區塊引出一個剪頭指向另一個區塊中間,是不正確的,所以有的書剪頭是指向一個區塊的上面。取哈希時是把塊頭的所有部分都取哈希。

image

image

塊身裡有交易列表 (transaction list)。

前面還有一個內容講的時候簡化了:每個節點都需要驗證所有的交易,實際上系統中的節點分全節點 (full node) 和輕節點 (light node),全節點是保存區塊鏈所有的信息的,驗證每一個交易,所以全節點又叫 fully validating node。輕節點只保存 block header 的信息,一般來說輕節點沒法獨立驗證交易的合法性。

比如一個交易是不是 double spending,輕節點沒有存以前的交易信息所以它沒法驗證。系統中大多數節點是輕節點,這節課內容主要針對全節點,因為輕節點沒有參與區塊鏈的構造和維護,只是利用了區塊鏈的一些信息做一些查詢。

區塊鏈裡的內容是如何寫到區塊鏈裡面的呢:每個節點,每個賬戶都可以發布交易,交易是廣播給所有節點的。有些交易是合法的,有些是非法的。誰來決定哪些交易應該被寫入下一個區塊中呢?按照什麼順序寫呢?如果每個節點自己決定可以嗎?如果每個人都在本地維護一個區塊鏈,那區塊鏈的統一性得不到保證,而賬本的內容是要取得分佈式的共識 (distributed consensus)。

下面的筆記跟比特幣的應用關係不大,可以作為了解:
分佈式的共識一個簡單的例子就是分佈式的哈希表 (distributed hash table),比如系統裡有很多台機器,共同維護一個全局的哈希表。

這裡需要取得共識的內容是什麼?哈希表中包含了哪些鍵值對 key valve pair。假如有人在自己電腦上插入一個鍵值對,'xiao' 這個 pair 對應的是 12345,即 'xiao'→12345。那麼別人在另一台讀的時候也要能把這個讀出來,這就叫一個全局的哈希表。

關於分佈式系統有很多不可能結論 (impossibility result),其中最著名的是 FLP。這三個字母是三個專家的名字縮寫,他們的結論是:在一個異步的 (asynchronous) 系統裡,(網絡傳輸遲延沒有上限就叫異步系統),即使只有一個成員是有問題的 (faulty),也不可能取得共識。

還有一個著名結論 Theorem。(CAP 是指分佈式系統的三個我們想要的性質,Consistency【系統狀態的一致性】 Availability【別人都可以用】 Partition tolerance)。該理論內容是:任何一個分佈式系統,比如分佈式哈希表,這三個性質中,最多只能滿足兩個,假如想要前兩個性質,那麼就不會得到第三個性質。

分佈式共識一個著名的協議是 Paxos,該協議能夠保證一致性,即第一個性質。如果該協議達成了共識,那麼這個共識一定是一致的,即每個成員所認為的共識都是相同的。但是,某些情況下,該協議可能永遠無法達成共識,這種可能性比較小但是客觀存在的。

比特幣中的共識協議 (consensus in BitCoin):
比特幣中共識要解決的一個問題是,有些節點可能是有惡意的。我們假設系統中大多數節點是好的,那麼該如何取得共識協議?

第一種方案是投票,首先應該確定哪些區塊有投票權,有些 membership 是有嚴格要求的,這種情況下基於投票的方案是可行的。但比特幣系統創建賬戶是很容易的,甚至一個人產生了公私鑰對別人都無法得知,只有轉帳時別人才知道。所以有些人可以不停的創建賬戶,當超過賬戶總數的一半時就有了控制權,這種稱為女巫攻擊 (sybil attack)。因此投票方法不可取。

比特幣賬戶巧妙的解決了這個問題,不是按照賬戶數目投票,而是按照計算力來投票。每個節點都可以在本地組裝出一個候選區塊,把它認為合法的交易放在裡面,然後開始嘗試各種 nonce 值 (佔 4 byte),看哪一個能滿足不等式 H (block header)≤target 的要求。如果某個節點找到了符合要求的 nonce,它就獲得了記帳權。

所謂的記帳權,就是往比特幣賬本裡寫入下一個區塊的權利。只有找到這個 nonce,獲得記帳權的節點才有權利發布下一個區塊。其他節點收到這個區塊之後,要驗證這個區塊的合法性。

比如括號裡 block header 的內容填的對不對;block header 裡面有一個域,叫 nBits 域,實際上它是目標預值的一個編碼檢查一下 nBits 域設置的是不是符合比特幣協議中規定的難度要求;該不等式是否成立。假設都符合要求,然後檢查 block body 裡面的交易列表,驗證一下每個交易都是合法的:①要有合法的簽名②以前沒有被花過。如果有一項不符合要求,這個區塊就是不能被接受的。如果所有條件都符合,也不一定接受。

假如生成了一個新區塊,怎麼知道新區塊插在了哪裡呢?根據生成區塊的指針。有可能就存在一個問題,如圖 5 (第四個視頻第 65 分鐘) ,這兩個交易指 A 轉帳給 B,以及 A 轉帳給自己。這種情況不是 double spending,判斷一個交易是不是 double spending,是看這個區塊所在的分支上幣又沒有被花掉。如圖,一直到第三個區塊,幣都沒有花過,所以這個交易是合法的。雖然該交易是合法的,但是它不在最長合法鏈 (longest valid chain) 上。這種稱為分叉攻擊 (forking attack)。所以接收的區塊應該是擴展最長合法鏈。

區塊鏈在正常情況下也可能出現分叉:兩個節點同時獲得記帳權。每個節點在本地自己組裝一個它認為合適的區塊,然後去試各種 nonce,如果兩個節點在差不多同一時間找到了符合要求的 nonce,就都可以把區塊發布,這時會出現兩個等長的分叉。這兩條都是最長合法鏈,那該接受那條呢?比特幣協議當中,在缺省 (默認的意思) 情況下,每個節點是接受它最早收到的那個。所以不同節點根據在網絡上的位置不同,有的節點先聽到新生成的其中一個區塊,那就接受這個區塊;有些節點先聽到另一個區塊,那就接受另一個區塊。

如何判斷接收了一個區塊?比特幣協議中用到了 implicit consign,如果沿著這個區塊往下繼續擴展,就算認可了這個發布的區塊。比如在新生成的其中一個區塊後面又拓展一個區塊,表明就認可了這個新區塊。

等長的臨時性的分叉會維持一段時間,直到一個分叉勝出。也就是哪一個鏈搶先一步生成了新的區塊,哪一條就是最長合法鏈。另一個作廢的就叫 orphan block。這兩個新區塊有可能會各自拉攏,兩個區塊鏈看誰的算力強,有時候也是看誰的運氣好,就會勝出。

競爭記帳權的好處:首先獲得記帳權的節點本身有一定的權力,可以決定哪些交易寫到下一個區塊裡。但這些不應該被設定為競爭記帳權的動力,所以巧妙地建立了一個機制:區塊獎勵 (block reward)。

比特幣協議中規定獲得記帳權的節點在發布的區塊裡可以有一個特殊的交易:鑄幣交易。在這個交易裡可以發布一定數量的比特幣。

這裡要回到前面問題①,誰來決定貨幣的發行?coinbase transaction 幣基交易是比特幣系統中發行新的比特幣的唯一方法,後面的交易都是比特幣的轉移。這個交易不用指出幣的來源。

那麼能造多少幣呢?開始時比特幣剛上線的時候,每一個發布的區塊可以產生 50BTC (BTC 就是比特幣的符號)。協議中規定,21 萬個區塊以後,初塊獎勵就要減半,就變成了 25BTC。再過 21 萬個區塊,又要減半。

因此當一個區塊勝出後,另一個作廢的區塊得到的比特幣是沒有作用的,其他誠實的區塊是不會承認的。

比特幣系統中要取得什麼共識?去中心化的賬本要取得共識。誰又能決定賬本的內容呢?只有獲得記帳權的節點才能寫東西。怎麼獲得記帳權呢?就是解 pow (挖礦)。按照算力記票,算力可以用每秒能試多少 nonce 數值表示。那怎樣防範女巫攻擊呢?按算力記票,即使創建再多的賬戶,也無法使算力增強。

比特幣爭奪記帳權的過程叫作挖礦 (mining),比特幣被稱為數字黃金 (digital gold),爭奪記帳權的節點被稱為礦工 (miner)。

比特幣系統的實現#

區塊鏈是去中心化的賬本,比特幣使用的是基於交易的這種賬本模式 (transaction [交易]-based ledger [賬本])。系統當中並不會顯示每個賬戶有多少錢。

比特幣系統的全節點要維護一個叫 UTXO (unspent transaction output)(還沒有被花出去的交易的輸出) 的數據結構。區塊鏈上有很多交易,有些交易的輸出可能已經被花掉,有些還沒有被花掉。所有沒有被花掉的輸出的集合就叫做 UTXO。

一個交易可能有多個輸出。假如 A 給 B5 個比特幣,B 花掉了。A 也給了 C3 個比特幣,C 沒有花掉。這時 5 個比特幣就不算 UTXO,而 3 個比特幣算。UTXO 集合當中的每個元素要給出產生輸出的交易的哈希值,以及它在這個交易裡是第幾個輸出。這兩個信息就可以定位到 UTXO 中的輸出。

要 UTXO 集合有什麼作用?
為了檢測 double spending。即檢測新發布的交易是否合法。因此全節點要在內存中維護 UTXO 這樣一個數據結構,以便快速檢測 double spending。

每個交易要消耗掉一部分輸出,也會產生新的輸出。還看上面的例子,B 花掉的 5 個比特幣雖然不在 UTXO 裡面,但如果他轉帳給 D,而 D 沒有花掉,那麼這 5 個比特幣又要保存在 UTXO 裡面。如果 D 始終不花,那麼這個信息要永久保存在 UTXO 裡面。有可能是不想花,也有可能是把密鑰丟了。

每個交易可以有多個輸入,也可以有多個輸出,所有輸入金額之和要等於輸出金額之和。即 total inputs=total outputs。因此一個交易可能來自多個地址,可能有多個簽名。

有些交易 total inputs 略微大於 total outputs。
假如輸入 1 比特幣,輸出 0.99 比特幣,另外 0.01 比特幣作為交易費給獲得記帳權發布區塊的節點。

區塊獎勵也不能完全作為挖礦的獎勵,發布區塊的節點為什麼一定要把你的交易打包在區塊呢?他們還要驗證你的交易的合法性,如果交易較多佔用的帶寬會比較大,網絡傳播速度也會更慢。所以只有區塊獎勵是不夠的。

因此比特幣系統設計了第二個激勵機制:交易費 (transaction fee)。也就是你把我的交易打包在區塊裡,我給你一些小費。交易費一般很小,也有一些簡單的交易沒有交易費。

21 萬個區塊大概要挖多長時間呢?大約是 4 年。比特幣系統設計的平均出塊時間是 10 分鐘,就是整個系統平均 10 分鐘會產生一個新的區塊。

除了比特幣這種基於交易的模式,與之對應的還有基於賬戶的模式 (account-based ledger),比如以太坊系統。在這種模式中,系統是要顯示的記錄每個賬戶上有多少幣。

比特幣基於交易的模式,隱私保護性較好。缺點是比特幣當中的轉帳交易要說明幣的來源,而基於賬戶的模式就不用。

如圖⑥(第五節視頻 16 分鐘處)
一個區塊的例子
第一行表明:該區塊包含了 686 個交易
第二行:總輸出 XXX 個比特幣
第四行:總交易費 (686 個交易的交易費之和)
最下面一行:區塊獎勵 (礦工挖礦的主要動力)
第五行:區塊的序號
第六行:區塊的時間戳
第九行:挖礦的難度 (每隔 2016 個區塊要調整挖礦的難度,保持出塊時間在 10 分鐘左右)
倒數第二行:挖礦時嘗試的隨機數

右邊:第一行:該區塊塊頭的哈希值
第二行:前一個區塊塊頭的哈希值
(注意:計算哈希值只算塊頭)
兩個哈希值的共同點:前面都有一串 0。是因為,設置的目標預值,表示成 16 進制,就是前面一長串的 0。所以凡是符合難度要求的區塊,塊頭的哈希值算出來都是要有一長串的 0。
第四行 root 是該區塊中包含的那些交易構成的 merkle tree 的根哈希值。

如圖⑥(見第五節視頻 第 20 分鐘) 塊頭的數據結構
最後一行:是 32 位的無符號整數。nonce 只有 2 的 32 次方個可能的取值。按照比特幣現在的挖礦情況來說,很可能把 2 的 32 次方個取值都驗了一遍也找不到合適的。那怎麼辦呢?block header 的數據結構裡還有哪些域是可以調整的呢?

如圖⑦ 塊頭裡各個域的描述 (見第五個視頻 第 21 分鐘)
第一行:比特幣協議的版本號 (無法更改的)
第二行:前一個區塊的塊頭的哈希值 (無法更改)
第三行 tree 的根哈希值 (可以更改)
第四行:區塊產生的時間 (可以調整) 比特幣系統不要求特別精確的時間,可以在一定範圍內調整。
第五行:目標預值 (編碼後的版本)(只能按協議中的要求定期調整)
第六行:隨機數

挖礦時只改隨機數不夠,還可以更改根哈希值。

如圖⑧(見第五節視頻 第 23 分鐘)
鑄幣交易沒有輸入,它有一個 coinbase,可以寫入任何的內容。也可以把 digital commitment 裡的 commit 的哈希值寫入裡面。也可以把第一節講到的預測股市的內容寫入裡面,coinbase 的內容是沒人會檢查的,甚至可以寫你的心情。

那這個域對我們有什麼用呢?

如圖⑨(見第五節視頻 第 24 分鐘)
對應的是最後一個 block header 裡的根哈希值對應的 merkle tree,左下角的交易是 coinbase,把它的域改了之後,其上的哈希值就發生了變化,然後沿著 merkle tree 的結構往上傳遞。最後導致 block header 裡的根哈希值發生變化 (merkle root 是 block header 的一部分)。塊頭裡 4 個字節的 nonce 不夠用,還有其他字節可以用,比如 coinbase 域的前八個字節當做 extra nonce 來用,這樣子搜索空間就增大到了 2 的 96 次方。

所以真正挖礦的時候只有兩層循環,外層循環調整 coinbase 域的 extra nonce。算出 block header 裡的根哈希值之後,內層循環再調整 header 裡的 nonce。

如圖⑩ 普通的轉帳交易的例子 (見第五節視頻 第 26 分鐘)
該交易有兩個輸入和兩個輸出。
左上角:這裡的 output 其實是輸入,指的是之前交易的 output。
右上角:這裡的 output 都是 unspent,都沒有被花掉,會保存在 UTXO 裡面。
右邊表格第一行:輸入的總金額。
依次往下:輸出總金額、兩者之間的差值。
兩表格下面:可以看出輸入和輸出都是用腳本的形式來指定的。

比特幣系統中驗證交易的合法性,就是把 input scripts 和 output script 配對後執行來完成的。注意:不是把圖中的 input scripts
和 output scripts 配對,因為這兩個腳本是一個交易中的腳本。不是把同一個交易裡的輸入腳本和輸出腳本配對,而是把這裡的輸入腳本和前面提供幣來源的交易的輸出腳本配對。如果輸入輸出腳本拼接在一起,能順利執行不出現錯誤,那麼該交易就是合法的。

如圖十一,是在求解 puzzle 的過程。
注意:求哈希時只用到了 block header 的內容,而交易的具體信息在 block header 裡面是沒有的。block header 裡面只有 merkle tree 的根哈希值,這個就已經能保證交易是沒有被篡改的。

挖礦過程每次嘗試一個 nonce 可以看作是一個 Bernoulli trial (伯努利實驗)。每一個隨機的伯努利實驗就構成了一個伯努利過程。它的一個性質是:無記憶性。

每嘗試一個 nonce 成功的概率是很小的,要進行大量的實驗。這時可以用泊松過程來代替伯努利過程。我們真正關心的是系統出塊時間,出塊時間是服從指數分佈。可以畫出一個坐標軸,縱軸表示概率密度,橫軸表示出塊時間 (整個系統的出塊時間,並不是每個礦工的出塊時間)。具體到每一個礦工,他能挖到下個區塊的時間取決於礦工的算力佔系統算力的百分比。

假如一個人的算力佔系統總算力的 1%,那麼系統出 100 個區塊,就有一個區塊是這個人挖的。

指數分佈也是無記憶性的。因為概率分佈曲線的特點是:隨便從一個地方截斷,剩下一部分曲線跟原來是一樣的。比如:已經等十分鐘了,還沒有找到合法的區塊,那麼還需要等多久呢?仍然參考概率密度函數分佈,平均仍然要等十分鐘。將來還要挖多長時間,跟過去已經挖了多長時間是沒有關係的。這個過程也叫 free。

如果沒有 progress free,會出現什麼現象:算力強的礦工會有不成比例的優勢。因為算力強的礦工過去做的工作是更多的,過去嘗試了那麼多不成功的 nonce 之後,後面 nonce 成功的概率就會增大。以此 progress free 是挖礦公平性的保證。

出塊獎勵是系統中產生新的比特幣的唯一途徑。產生的比特幣構成的一個幾何序列。21 萬*50+21 萬*25+21 萬*12.5+......=21 萬*50*(1+1/2+1/4+......)=2100 萬

比特幣求解的 puzzle,除了比拼算力之外,沒有其他實際意義。比特幣的稀缺性是人為造成的。

雖然挖礦求解 puzzle 本身沒有實際意義,但是挖礦的過程對於維護比特幣系統的安全性是至關重要的。挖礦提供一種憑藉算力投票的有效手段,只要大部分算力是掌握在誠實的節點手裡,系統的安全性就能夠得到保證。

雖然挖礦獎勵越來越小,難度越來越大,但這幾年挖礦的競爭是越來越激烈的,因為比特幣的價格是飆升的。最終區塊獎勵為 0 了,是不是就沒有動力挖礦了呢?不是的,因為還有交易費激勵機制。

假設大部分算力是掌握在誠實的礦工手裡,我們能得到什麼樣的安全保證?能不能保證寫入區塊鏈的交易都是合法的。挖礦給出的只是概率上的保證,只能說有比較大的概率下個區塊是由誠實的礦工發布的,但是不能保證記帳權不會落到有惡意的節點手裡。

比如好的礦工佔 90% 的算力,壞的礦工佔 10% 的算力。那麼 10% 的概率下記帳權會落在有惡意的礦工手裡,這時候會出現什麼情況?

先考慮第一個問題:他能不能偷幣?能不能把別人賬上的錢轉給自己?不能,因為他沒有辦法偽造別人的簽名。

假設 M 是有惡意的,他想把 A 賬上的錢轉走,所以他發布一個 A 轉給 M 的交易,但這個交易需要有 A 的簽名,M 雖然獲得記帳權,但他不知道 A 的私鑰,所以偽造不了簽名。

如果 M 把交易硬寫在區塊鏈上,誠實的節點不會接受這個區塊,因為它包含有非法的交易。所以誠實的節點會繼續沿前一個區塊挖,生成新的區塊代替非法的區塊,其他誠實的區塊會沿著這個合法的區塊繼續挖。比特幣要求是擴展正常合法鏈,M 生成的不是合法區塊,所以該區塊作廢。這對他造成的代價是很大的,因為沒有了區塊獎勵,又沒有偷到錢。

第二個問題:他能不能把已經花了的幣再花一遍 (即 double spending)? 假如他把 M→A 的交易寫在了一個區塊裡面,現在他獲得了記帳權,他又發布另一個交易,把這個錢轉回給自己,即 M→M'。同樣,這很明顯是 double spending,只要是誠實的節點都不會接受這個區塊。

他如果想發布這個區塊,只能連在寫了 M→A 交易區塊的前一個區塊。注意:區塊插在哪個位置,在剛挖礦時就是要決定的,因為設置的 block header 裡要填上前一個 block header 的哈希。所以他想插到那個區塊的話,一開始就要認定,而不是等獲得記帳權以後再認定。

這樣生成的兩條區塊鏈,都是合法的。參考圖十二 (第五節視頻 第 56 分鐘處)。要看其他節點沿著哪一個鏈往下擴展,最後一個勝出一個作廢。

這種攻擊的目的是什麼?如果 M→A 的交易,產生了某種不可逆的外部效果,然後 M→M' 再把 M→A 的交易回滾了,那麼 M 就可以從中不當獲利。

比如:網上購物時,M 購買一些商品,然後該網站接受比特幣支付,M 發起一個交易把賬轉給網站。網站監聽到交易寫入了區塊鏈裡,以為支付成功了,所以就把商品給了 M。M 拿到商品之後,又發起一個交易,把支出的钱轉給自己,然後把下面的鏈拓展成最長合法鏈。這樣的結果是:既得到了商品,又收回了花掉的錢,就達到了 double spending 的目的。

如何防範這種攻擊呢?如果 M→A 的交易所在的區塊不是最後一個區塊,那麼這種攻擊的難度就會大大增加。要是想回滾 M→A 的交易,還是要插在它之前的某個區塊,然後想辦法成為最長合法鏈。這個難度是很大的。因為誠實的節點,不會沿著它生成的區塊往下擴展,因為它不是最長合法鏈。因此防範這種攻擊的方法就是多等幾個區塊,或者叫多等幾個確認 confirmation。

M→A 交易剛剛寫入區塊裡時,我們把它叫作 one confirmation。這時後面加的區塊,依次叫 two confirmation、three confirmation... 比特幣協議當中,缺省 (系統默認) 的是要等六個 confirmation。有了六個 confirmation,才認定 M→A 的交易是不可篡改的。這需要等多長時間呢?平均出塊時間是 10 分鐘,因此要等一個小時。

區塊鏈是不可篡改的賬本,那是不是意味著凡是寫入區塊鏈中的內容就永遠改不了呢?經上述分析可以看出,這種分析只是一種概率上的保證。剛剛寫入區塊鏈的內容,還是比較容易被改動的。經過一段等待時間之後,或者後面幾個區塊被確認之後,被篡改的概率就大幅度下降 (指數級別的下降)。

其實還有一種,叫零確認 (其具體位置可見第五節視頻 第 62 分第 26 秒)。意思是說,這個轉帳交易發布出去了,但還沒又被寫入區塊鏈裡。即 M→A 的交易已經發布,但下面包含 M→M' 的區塊還沒有被挖出。

這個概念相當於電商購物的例子中,在支付時你發布一個轉帳交易,告訴電商自己已經轉過錢了。電商運行一個全節點或委託一個全節點監聽區塊鏈上的交易,他收到轉帳交易之後要驗證該交易的合法性 (有合法的簽名,以前沒有被花過),甚至不用等到該交易寫入區塊鏈裡。這種操作聽起來風險很大,交易剛發布出去,都沒往區塊鏈裡寫呢。其實,零確認在實際當中,用的還是比較普遍的。為什麼呢?

這其中有兩個原因:

①比特幣協議缺省的設置是節點接收最先聽到的那個交易。所以在零確認的位置,M→A 的節點收到後,再發 M→M' 的交易,有比較大的概率誠實的節點是不會接受的。
②很多購物網站,從支付成功,到發貨,是有一定的時間間隔的,即有一定的處理時間。

回到前面問題:假設某個有惡意的節點獲得記帳權,它還能做什麼壞事?能不能故意不把某些合法的交易寫入區塊鏈裡?即發布的區塊故意不包含某些交易。這是可以的。

比特幣協議並沒有規定獲得記帳權的節點一定要把那些交易發布到區塊裡。但出現這種情況問題也不大,因為這些合法的交易一定會被寫入下一個區塊裡,總有誠實的節點願意發布這些交易。

其實,區塊鏈在正常工作下,也會出現合法的交易沒有被包含進去的情況,可能就是這段時間交易的數目太多了。比特幣協議中規定,每個區塊的大小是有限制的,最多不能超過一兆字節。所以如果交易的數目太多了,那麼有些交易可能就只能等到下一個區塊再發布。

會不會出現這種情況?M→M' 的交易所在的區塊所在的鏈條雖然短,但是先偷偷的生成比上面更多的區塊,然後等上面的鏈條公布後再公布,就能夠勝過上面的幾個區塊了?這種方法叫作 selfish mining。

正常情況下挖到一個區塊馬上就發布,原因是你不發布別人可能就發布了,那樣就拿不到區塊獎勵了。而 selfish mining 是先藏著不急著發布,這是分叉工具的一種手段。

但這樣成功的概率並不大,因為有惡意的節點本來算力佔比就不高,還要生成更多的區塊,就非常困難。

以上是 selfish mining 的其中一個目的,它還有另一個目的。假如 A 挖了兩個區塊都沒有發布,而在 B 挖到一個區塊公布後立馬公布,這樣 B 挖的區塊就作廢了。這樣的好處就是減少競爭,因為 A 在挖第二個區塊時,別人還在挖第一個區塊 (前提是 A 算力足夠強)。

但這樣也有不好的地方,假如 A 挖出一個區塊,A 以為他能趕在別人面前再挖一個區塊,結果這時有人挖出了第一個區塊,那這樣的話 A 就要在別人發布之後立馬發布,去爭取區塊獎勵。

比特幣網絡#

比特幣工作在應用層 (application layer block chain),它的底層是一個網絡層 (network layer overlay network)。

比特幣的 P2P 網絡是非常簡單的,所有節點都是對等的。不像有的 P2P 網絡有所謂的超級節點、紙節點。

要加入 P2P 網絡首先得知道至少有一個種子節點,然後你要跟種子節點聯繫,它會告訴你它所知道的網絡中的其他節點,節點之間是通過 TCP 通信的,這樣有利於穿透防火牆。當你要離開時不需要做任何操作,不用通知其他節點,退出應用程序就行了。別的節點沒有聽到你的信息,過一段時間之後就會把你刪掉。

比特幣網絡的設計原則是:簡單、魯棒,而不是高效。每個節點維護一個零度節點的集合,消息傳播在網絡中採取 flooding 的方式。節點第一次聽到某個消息的時候,把它傳播給去他所有的零度節點,同時記錄一下這個消息我已經收到過了。下次再收到這個消息的時候,就不用轉發給零度節點了。

零度節點的選取是隨機的,沒有考慮底層的拓撲結構。比如一個在加利福尼亞的節點,它選的零度節點可能是在阿根廷的。這樣設計的好處是增強魯棒性,它沒有考慮底層的拓撲結構,但是犧牲的是效率,你向身邊的人轉帳和向美國的人轉帳速度是差不多的。

比特幣系統中,每個節點要維護一個等待上鏈的交易的集合。假如一個集合的交易都是等待寫入區塊鏈裡的,那麼第一次聽到某個交易的時候,把這個交易加入這個集合,並且轉發這個交易給節點,以後再收到這個交易就不用轉發了,這樣避免交易會在網絡上無線的傳播下去。轉發的前提是該交易是合法的。

這裡有衝突的情況,有可能你會有兩個有衝突的交易,差不多同時被廣播到網絡上。比如說 A→B 和 A→C,這兩個如果同時廣播在網絡上,那麼每個節點根據在網絡中的位置的不同,收到兩個交易的先後順序不同。

比如一個人先收到第一個交易,就寫入到集合裡,再收到第二個交易的時候就不會寫入集合,因為跟上個交易有衝突,就認定是非法的。假設這兩個交易花的是同一個幣,那麼寫入集合的交易就會被刪掉。

比如說節點聽到一個新發布的區塊,裡面包含了 A→B 的交易,那麼這個交易就可以刪掉了,因為已經寫入到了區塊鏈裡。如果節點又聽到了 A→C 的交易,那該怎麼辦?這時也要把 A→B 刪掉。因為 A→C 如果已經被寫入到了區塊裡,那麼 A→B 就變成了非法交易,就變成了 double spending,這就是衝突的情況。可能某個先收到 A→C 的節點,搶先挖到了礦,發布了區塊。

新發布的區塊在網絡上的傳播有很多方式,跟新發布的交易是類似的。每個節點除了要檢查區塊的內容合法性之外,還要查它是不是在最長合法鏈裡。越是大的區塊,在網絡上傳播速度越慢。

比特幣協議對區塊的大小有 1M 字節的限制。比特幣系統採用的傳播方式是非常耗費帶寬的,帶寬是瓶頸。按 1M 的區塊大小限制來算的話,一個新發布的區塊有可能需要幾十秒,才能傳輸到網絡大部分境地,這已經是挺長時間了,所以這個限制值不算小。

還需要注意的一點:我們講的比特幣網絡的傳播屬於 best effort 。一個交易發布到比特幣網絡上,不一定所以的節點都能收到,而且不同的節點收到這個交易的順序也不一定是相同的。網絡傳播存在延遲,而且這個延遲有的時候可能會很長,有的節點也不一定按照比特幣協議的要求進行轉發。

可能有的該轉發的不轉發,導致某些合法的交易收不到,也有的節點可能轉發一些不該轉發發的消息,比如說有些不合法的交易也被轉發了。這就是我們面臨的一個實際問題。

比特幣的挖礦難度調整

目標預值越小,挖礦的難度越大。調整挖礦的難度就是調整目標空間在整個輸出空間中所占的比例。

比特幣用的哈希算法是 SHA-256,這個產生的哈希值是 256 位。所以整個輸出空間是 2 的 256 次方。調整這個比例,即目標空間占輸出空間的比例,通俗的說,就是哈希值前面要有多少個 0。比如說 256 位的哈希值,要是合法的區塊,要求算出來的哈希,前面至少有 70 個 0。當然這只是通俗的說法,因為這個目標預值,並不是說前面都是 0,從某一個位置開始,後面都變成了 1。

挖礦的難度跟目標預值是成反比的,公式是=difficulty 1 target /target。上面是指挖礦難度等於 1 的時候所對應的目標預值,挖礦難度最小就是 1,這個時候對應的目標預值是個非常大的數。

即 target 越大,挖礦是越容易的。所以公式裡很大的一个數,除以當前的目標預值,得到的就是當前的挖礦難度。所以 difficulty 和 target 大小是成反比的。

為什麼要調整挖礦難度呢?如果不調會有什麼問題呢?系統裡的總算力越來越強,挖礦難度保持不變的話,出塊時間是越來越短的。

出塊時間越來越短,會有什麼問題嗎?
比如說不到一秒就出一個區塊,區塊在網絡上傳播的時間可能需要幾十秒,底層的比特幣網絡可能需要幾十秒才能讓其他節點都收到。別的節點沒有收到這個區塊之前還是繼續沿著已有的區塊鏈往下擴展。如果有兩個節點同時都發布一個區塊,這個時候就會出現分叉。

出塊時間如果越來越短的話,這種分叉會成為常態,而且不僅會出現二分叉,可能會出現很多的分叉。比如 10 個區塊同時被挖出來,系統可能會出現 10 分叉。

分叉如果過多,對於系統達成共識是沒有好處的,而且危害了系統的安全性。比特幣協議是假設大部分算力掌握在誠實的礦工手裡。系統當中的總算力越強,安全性就越好,因為有惡意的節點想掌控 51% 的算力就越難。如果掌握了 51% 的算力,它就可以幹很多壞事,比如分叉攻擊。

如果後面分叉多的話,前面某個區塊裡的某個交易,很可能就遭受分叉攻擊,惡意節點會試圖回滾。因為後面分叉多,算力就會分散,惡意節點得逞的概率更大。這個時候惡意節點就不需要 51% 的算力了,可能 10% 的算力就夠了,因此出塊時間不是越短越好。

那 10 分鐘的出塊時間是不是最優的呢?不一定。改成其他值也可以,有間隔只是說應該有個常數範圍。以太坊系統出塊時間就降低到了 15s,所以以太坊的出塊速度是比特幣的 40 倍。

出塊時間大幅度下降之後,以太坊就要設計新的協議,叫 ghost。在該協議中,這些分叉,產生的 orphan block (即產生最長合法鏈後另一個要被丟棄的區塊) 就不能丟棄掉了,而是也要給它們一些獎勵,這叫 uncle reward。以太坊也要調整挖礦難度,使出塊時間保持在 15s。

講完了為什麼要調整挖礦難度,現在講一下怎麼調整挖礦難度。比特幣協議中規定,每 2016 個區塊後就要調整目標預值,這大概是每兩個星期調整一次。

具體的調整公式 =target×(actual time/expected time)。actual time 指產生 2016 個區塊實際花費的時間,expected time 指產生 2016 個區塊應用的時間,即 2016×10min。

如果實際花費時間超過了兩周,即平均出塊時間超過了 10min。那麼這時候挖礦難度要調的低一點,應該讓出塊更容易。因此該公式算出來的 target 會變大,則難度會下降。

實際上,上調和下調都有四倍的限制。假如實際時間超過了 8 個星期,那麼我們計算公式時也只能按 4 倍算,目標預值增大最多只能增大 4 倍。

那怎麼才能讓所有的礦工同時調整目標預值呢?計算 target 的方法寫在比特幣系統的代碼裡,每挖到 2016 個區塊會自動進行調整。如果有有惡意的節點故意不調,會怎麼樣?

如果一個節點不調,

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。