比特幣有關
在現代社會中,一種大量流通的貨幣一般都有非常高級的加密技術,例如人民幣上就有水印、熒光光圈等一系列技術來提高偽造的難度。數字貨幣在本質上是網絡上的數據,非常容易受到攻擊,所以需要一套完善的加密體系。
就像我們在銀行有賬戶密碼一樣,在比特幣中我們也有一些相似的東西來決定一個賬戶的所有權。具體來說有三個與賬戶相關的概念:私鑰、公鑰和比特幣地址。
他們之間的關系如上圖所示,私鑰可以通過橢圓加密算法生成公鑰,公鑰可以通過哈希函數生成比特幣地址。反過來看,有了比特幣地址是無法生成公鑰的,有了公鑰也無法生成私鑰,這是由后面要介紹的加密算法所實現的。
掌握私鑰就能生成相應的公鑰和比特幣地址,相當於掌握了整個賬戶,所以我們一定要保管好自己的私鑰。
橢圓加密算法
從私鑰到公鑰所用的是橢圓加密算法,它有一個簡單的數學形式,即
其中 K
代表公鑰,k
代表私鑰,而 G
代表橢圓曲線上面的一點,這個乘法不是自然數的乘法,而是橢圓乘法。
橢圓曲線的加法如下所示:
得到
\(x_3 = \lambda^2-x_1-x_2\)
\(y_3 = \lambda(x_3-x_1)+y_1\)
通過計算 G+G 算出 2G,繼而算出 4G,最終通過大約 log2(k)
次算出 k * G,由於每次的計算量不大,所以總體計算量也不大。
要根據 K
和 G
得到 k
的問題叫做離散對數問題,計算復雜度為根號 p
,其中 p
為整個群的大小,(如果 n
位二進制那么復雜度就是 2n/2)這個復雜度是非常可怕的,幾乎沒有可能求解出來。換句話說,根據公鑰反向計算私鑰基本上是不可能的。
哈希算法
哈希函數有很多種,一般有如下定義:
- 輸入是任意長度的字符串。
- 輸出是固定長度的數字(例如比特幣一般用
256
位)。 - 計算非常簡單。
從安全角度來看,它有下面的特征:
- 沒有碰撞:對於
x
和y
,如果x
不等於y
,那么我們可以保證H(x)
不等於H(y)
。這條性質是概率上的保證,因為我們的輸入集合是一個無限的集合,而輸出是一個只有 2 的 256 次方的集合,根據抽屜原理必然存在x1
與x2
不相等但是H(x1)
等於H(x2)
的情況,但是 Hash 函數的設計會讓這個概率非常非常小,小到幾乎不可能發生。 - 隱藏性質:從
x
可以生成H(x)
,但是幾乎無法從H(x)
生成x
,這條性質保證我們無法由比特幣地址得到公鑰。
交易與數字簽名
一個方框代表一筆交易,例如中間的方塊代表的是由 owner1
支付給 owner2
的一次交易,我們可以看到在方框下方有一個簽名,正是這個簽名讓這次交易有效。
這個簽名簽署用到的是 owner1
的私鑰和與這次交易的相關信息(包括前一次交易,owner2
公鑰,金額,時間戳等信息),其中:
其中 sig
代表簽名,sign
是簽名函數,sk
代表私鑰,message
代表交易相關信息。
而簽名完成后,其他人要有辦法驗證這筆交易是否有效,所以它們要用 owner1
的公鑰,交易相關信息以及簽名完成驗證。用 api
的形式來表達就是
這里的 pk
代表公鑰,isValid
是一個布爾變量,其值為真或假。
從上面的兩個 api
我們可以看到數字簽名有一系列好的性質,一個性質是我們在簽名的時候用到私鑰,但是驗證時只用公鑰,這保證了私鑰的安全性。第二個是簽名與每次交易對應,我們不能把簽名遷移到另一次交易上,只要私鑰不泄露別人就無法偽造簽名。
哈希指針
首先我們要引入一個叫哈希指針的概念。一般來說一個普通指針可以告訴你數據存儲的位置,哈希指針除了能夠告訴你數據存儲的位置,由於它還存儲了對應數據的哈希值,還能告訴你對應數據是否被篡改過。
在區塊鏈中,如果我們假設一個區塊被篡改了:
那么如果我們對這個區塊的數據計算哈希值,就會跟后面一個區塊中存儲的哈希值對不上,就會發生驗證的問題。那么如果要掩蓋這個問題,就必須把再后面一個區塊的哈希值給改掉,這是后面的也出現哈希值改變的問題,這里就需要一直改到最近的一個區塊。有一個特殊的哈希指針指向最近的區塊,由於這個哈希指針無法被黑客篡改,所以黑客沒有辦法通過篡改區塊的方式對區塊鏈進行攻擊。
默克爾樹
其實所有帶有指針的數據結構都可以把普通指針換為哈希指針,例如我們可以把二叉樹的指針也換成哈希指針,然后就可以得到默克爾樹。
默克爾樹底部的樹葉是我們的交易數據,而每一個父節點是兩個子節點哈希值之和的哈希值。
樹這種數據結構為我們快速驗證一個交易是否存在提供了便利,在比特幣中我們為每個交易存儲了一個默克爾路徑,比如我們要驗證 K
這個交易,我們只用存儲下圖中藍色的哈希值就能把根節點給重建出來,驗證是否存在這筆交易。而不需要整棵樹的數據,這大大節約了空間和時間,在比特幣的 SPV
輕錢包認證中默克爾樹起到非常重要的作用。
由默克爾樹和區塊鏈組成的區塊鏈的架構
真實的比特幣系統中,外層是一個區塊鏈,而每個區塊內部則是用一個默克爾樹來存儲交易。外層的區塊鏈保證了結構的簡單,為后面將要介紹的挖礦與共識打下了基礎,而默克爾樹則為快速驗證交易提供了基礎。
挖礦
作為一種貨幣,中本聰在設計之初就對比特幣的分發做了周詳的設計,總共 2100 萬枚比特幣將按照一個遞減的速率的分發,具體是每四年速率減緩一次。最初發現每個區塊會獎勵 50 個比特幣,到了2017年,發現新區塊的獎勵已經降低到了 12.5 個比特幣。
新區塊是如何被發現的呢?這就涉及到了比特幣的 “挖礦”,所有的“礦工” 會競爭解決一個問題來獲得記賬權並獲得相應的新幣獎勵。
它們解決的問題是一個與哈希加密算法有關的數學難題,具體來說就是對於一個字符串,尋找一個后綴來使哈希值小於某一特定值。
對於字符串,尋找后綴使得哈希值小於某一閾值是隨機的。
如果得到的哈希值是完全隨機的(近似均勻抽樣),那么我們可以通過設置一個閾值,讓競爭者找到一個后綴,使其對應的哈希值小於這個值。我們可以通過調整這個值的大小來改變期望的計算次數,我們的哈希函數得到的結果的最大值是 2 的 256 次方,所以如果我們把閾值設為 2 的 240 次方,那得到小於這個值的概率是 1 除以 2 的 16 次方。我們可以通過調整閾值來控制難度,閾值越小,難度越高。在比特幣系統中,難度會隨着前面發現區塊的時間動態調整,使得發現每個區塊的平均時間為 10 分鍾。
雙重支付
比如使用飯卡的支付,如果系統響應減慢,完全可以以五塊的余額做出十塊的東西。方法就是在剛用五塊錢,當系統沒有確認支付,你刷卡顯示的錢還是五塊錢。可以利用這個時間差進行雙重支付。
我們可以看上面這幅圖,A 支付給了 B,這項交易被寫入了區塊,假設這個區塊的名字是 M,然后 A 馬上開始從 M 的前一個區塊開始挖新的區塊,構成一條新的鏈條,在這個新的鏈條中 A 把比特幣支付給了自己的地址,如果這條新的鏈條的長度超過了原來的鏈條,那么所有人會接受這條最長鏈條,拋棄原來的鏈條(這是因為在比特幣中所有人都遵循將最長鏈條視為有效鏈條的規則)。如此一來,M 及 M 以后的區塊交易都會變得無效,A 轉出去的比特幣又變回來了。
通過“挖礦”我們可以給予人們動機去當誠實的節點。
我們先從數學角度分析一下這個問題:
\(p = 誠實節點發現下一節點的概率\)
$q = 欺詐節點發現下一節點的概率 $
\(qz = 欺詐節點從落后 z 個節點到追上誠實節點的概率\)
我們假設在 A 支付給 B 后就馬上開始准備新的鏈條 ,如果在 z 個區塊后 B 確認了交易,那么在這段時間內 A 生成區塊的數量應該是一個均值為 z * (q/p) 的泊松分布,那么 A 能夠完成雙重支付攻擊的概率就是:
對泊松分布的解釋:
生成區塊數量的期望是z*(q/p)
那么如果 q > p ,幾乎一定會追上誠實節點,這也是 51%算力攻擊這種說法的來源(更詳細的介紹51%算力攻擊)
如果 q < p 的話,壞人能夠追上的概率隨着 z 的增加呈現出指數型衰減 。由於比特幣的交易需要 6 次確認, 並且在現實中已經幾乎沒有機構能夠壟斷算力,所以雙重支付攻擊基本是不可能實現的。
從另一方面來看,就算有人有能力能夠追上誠實節點並發動 51%算力攻擊,他可以選擇當一個誠實的節點,從挖到的新區塊的獎勵和比特幣價值的增長中獲得收益。也可以選擇對系統進行攻擊,損害整個社區使比特幣失去價值。如果作為誠實節點的經濟激勵大於作惡,他就會有經濟動機去保持誠實。
從上面的分析來看,工作量證明通過用算力投票的方式讓節點自發地選擇做誠實的節點從而讓雙重支付幾乎不會發生,這樣的機制非常巧妙,在比特幣后很多新幣也發明了如 POS , DPOS 等新的共識機制來解決雙重支付的問題。
內容系:藍橋雲課 《比特幣基礎概念入門》 整理筆記