比特幣背後的算法與數學
比特幣實現中的哈希算法
可以說比特幣的整個實現就是建立在已有的甚至存在多年的計算機科學領域裏的技術或概念的整合,其中哈希算法在比特幣中的應用幾乎是方方面面,主要包括SHA256和RIPEMD160,比特幣將這兩個哈希算法的應用組合成兩個函數:hash256(d)=sha256(sha256(d))和hash160(d)=ripemd160(sha256(d)),其中d為待哈希的字節數組,兩者分別生成256位(32字節)和160位(20字節)的16進制數值。hash256主要用於生成標誌符,如區塊ID,交易ID等,而hash160主要用於生成比特幣地址。
值得一提的是,為什麽兩個函數都是做兩次哈希呢?對於hash160比較認同的答案是ripemd160可以使得生成的地址更短,但是只做ripemd160一次哈希可能會存在安全漏洞所以同時使用sha256起到安全加固;至於hash256使用兩次sha256哈希算法的原因來源於sha1算法,由於一次sha1哈希存在被生日攻擊(birthday attack)的風險,所以當使用sha1運算時一種有效方式就是做兩次sha1哈希,sha256本身並不存在生日攻擊漏洞,但是防禦性的使用兩次sha256哈希借鑒於sha1.
默克樹
對於例如SPV這種本身只保存區塊頭信息而沒有交易信息的比特幣輕節點是如何驗證一筆交易存在於哪一個區塊的呢--默克樹。(圖片來自Mastering bitcoin)
默克樹的基本原理就是將葉子節點兩兩配對做哈希運算(如果葉子節點為奇數,那麽將最後一個葉子復制)生成父節點,不斷叠代這一過程最終生成唯一的根節點merkle root。如果要驗證一個葉子節點是否存在於默克樹中只需要傳入一個該節點到根節點路徑merkle path,而SPV比特幣節點只需保存root節點即可。例如,依上圖如果想驗證k交易是否在於該區塊,我們只需要傳遞路徑HL, HIJ, HMNOP和 HABCDEFGH
橢圓曲線函數EC(Elliptic Curve)
比特幣使用公鑰加密的方式保護個人隱私,並且選擇橢圓曲線函數來實現,為什麽選EC的綜合原因不太清楚,不過至少我覺得它足夠安全也足夠高效。在比特幣的實現上,EC在三個方面發揮作用,密鑰對生成,私鑰簽名和簽名驗證。
橢圓曲線的數學表達式為y2=x3+ax+b,橢圓曲線有兩個重要特性,1.任意一條非垂直的直線與曲線相交於兩點,那該直線必與曲線相交於第三點;2.任意一條非垂直的曲線的切線必與曲線相交於另一點。依據這兩個特性,令點Q與P為曲線上的點,得到如下定義,加操作:經過Q和P的直線與曲線相交於第三點R’,那麽Q+P=R,其中R為R’點對於x坐標軸的對稱點;同理當移動直線使得Q與P點不斷逼近並重合為一點D,那麽此時直線相切與曲線,根據特征2,與曲線交於一點R’,不難得出D+D=R,其中R為R’點對於x坐標軸的對稱點。乘操作:令Q=aP,假設a=3就有:
Q = 3P Q = P + 2P
這樣,乘操作被分解為兩個加操作,即交線加和切線加。橢圓曲線圖(圖片來自Mastering bitcoin):
現在來看看比特幣協議裏的橢圓曲線特征,比特幣使用的曲線版本中a=0,b=7,即y2=x3+7,同時為了保證函數取值是在一個有限的區間內,所以在實際的EC的應用中會對結果做模運算以期獲得只定範圍的結果,例如,令模數為7,8 mod 7 = 1,那麽x mod 7的取值限制在0到6。比特幣協議中的EC有如下的參數設定:
模數 p = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F 基點 G= 04 79BE667E F9DCBBAC 55A06295 CE870B07 029BFCDB 2DCE28D9 59F2815B 16F81798 483ADA77 26A3C465 5DA4FBFC 0E1108A8 FD17B448 A6855419 9C47D08F FB10D4B8 序數 n = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141
這些參數的設定經過大量的研究,復雜而精妙,這些都巨大的數值,使得逆向運算不可能,簡單的說p為曲線的取值區間,n為我們可以取得的最大私鑰值,即私鑰必須小於n值,依照上圖,計算公鑰的過程就是私鑰與G點的乘法運算。G點與n的取值依據是,當私鑰的取值無限接近於n時與橢圓曲線相交的直線的斜率無限接近於垂直。
該來看看橢圓曲線如何在比特幣三個應用方面發揮作用的了。
1.密鑰對生成
正如上面提到的,公鑰=私鑰xG,就是將G點累加私鑰值的次數,這裏有個小問題是,上面的曲線圖展示的是連續區間內的點分布情況,但當取模以後,我們需要用特定的公式達到目的,設Q和P為曲線上兩點,那麽兩點相加交於曲線上的R點為:
d = (Qy - Py) / (Qx - Px)
Rx = d2 - Px - Qx
Ry = d (Px - Rx) - Py
在兩點重合在一個點Q的切線情況下交點R的計算公式變為:
d = (3Qx2 + a) / 2Qy
Rx = d2 - 2Qx
Ry = d (Qx - Rx) - Qy
正如上文提到的曲線乘操作,乘法的過程就是把操作拆分成眾多的切線與交線加的操作,即分別使用以上兩個公式計算的過程。
2.使用私鑰為數據簽名
為了隱私保護,交易中使用私鑰簽名而非私鑰來驗證一筆unspent的歸屬,拋開比特幣的使用環境,那麽簽名操作就是使用私鑰dA加密一段數據z,具體如下:
(1).選擇一個數k範圍在大於0小於n(上文中的序數,即私鑰的上限)
(2).計算點p(x,y)=k*G
(3).計算r=x mod n ,如果r段是0,返回第一步重新選擇
(4).計算s=(z+r*dA)/k mod n,如果s段是0,返回第一步重新計算
(5).生成了數字簽名signature(r,s)
3.數字簽名驗證
比特幣交易的鏈式操作就是不斷的私鑰簽名和公鑰驗簽的過程,有了上面的signature,驗簽過程如下(令公鑰為dP):
(1).驗證r和s段都在0和n之前
(2).計算w=s-1 mod n
(3).計算u1=z*w mod n
(4).計算u2=r*w mod n
(5).計算p(x,y)=u1*G+u2*dP
(6).驗證r==x mod n,如果等式不成立,那麽驗證失敗
至於驗證過程為什麽有效,可以參考這裏,簡單來說就是將公鑰dP展開為dA*G,然後將u1和u2定義分別帶入求得。
比特幣背後的算法與數學