1. 程式人生 > >比特幣原理整理(一)

比特幣原理整理(一)

        前幾天研究了一下比特幣,對這幾天的研究結果做一個總結,裡面有些內容是我自己的一些理解,如果有錯誤望指正,謝謝。

        現在比特幣這個詞有多種含義,可能指代的是一種數字貨幣,也可能指代的是其底層的儲存方式-區塊鏈。本文中也不做區分,根據上下文文意,比特幣可能表示數字貨幣,也可能表示區塊鏈。

        比特幣是由中本聰發明的,中本聰這個名字可能是一個化名,其發表了一篇關於比特幣的論文:《⽐特幣:⼀個點對點的電⼦現⾦系統》,從此比特幣正式進入人們的視野。2009年,開發人員將比特幣進行了實現。

        比特幣使用分散式賬簿記錄網路中出現的每一筆交易。既然是分散式的賬簿,也就意味著這個賬簿會在網路中的很多機器上儲存,網路中的機器使用P2P協議進行通訊,將各自儲存的賬簿進行統一。網路中發生的交易也是通過P2P協議傳送到網路中的其他機器上。比特幣網路中沒有所謂的中央管理機構,各個機器都是自主驗證交易合法性,並將合法交易記錄到分散式賬簿中。

        比特幣中還涉及到公鑰、私鑰和地址的概念。每一個比特幣交易都必須含有比特幣持有人的私鑰數字簽名,否則比特幣網路是不會接收該交易的。公鑰在比特幣網路中是公開的,用於對交易的合法性進行驗證。地址用於表示收款人的地址。

        上面這些是一些基礎內容,我覺得有了上面這些基礎知識就足夠了。

        我們作為一個普通使用者使用比特幣時,首先需要申請一個比特幣的私鑰。私鑰是其實就是一個隨機數,它是位於1--1.158*10^77-1之間的一個數,1.158*10^77略小於2^256(為什麼選取小於2^256的一個數字作為上限,沒有從網上找到答案)。所以私鑰佔256bit的空間。私鑰是由比特幣的客戶端完成的,不要我們去幹預。下面是私鑰的一個例子:1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD,這個私鑰按16進位制顯示。

        通過私鑰使用橢圓曲線演算法可以得到公鑰。橢圓曲線演算法是在一個曲線上找到一個點,這個點的座標就是公鑰(本人數學功底so so,想知道這個演算法的可以網上搜)。依據上面的私鑰可以得到公鑰:

       x =F028892BAD7ED57D2FB57BF33081D5CFCF6F9ED3D3D7F159C2E2FFF579DC341A

       y =07CF33DA18BD734C600B96A72BBC4749D5141C90EC8AC328AE52DDFE2E505BDB

    但是這個演算法有一個很奇妙的地方就是通過公鑰無法得出私鑰。我覺的這就是數學很奇妙的地方,由A可以得到B,但是B無法得出A。當得到了公鑰之後,下面就要生成位元

幣的地址了。比特幣的地址是由數字和字母組成的,但是金鑰完全是由數字(上面有字母是因為使用16進位制編碼的原因)組成的,記住這點不同。比特幣地址的例子如下:

1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy

    比特幣地址有一個特點:地址都是“1”開頭的。這可能和下面的雜湊計算有關。下面這個公式中我們以公鑰作為輸入,輸出結果就是比特幣地址:

    A =RIPEMD160(SHA256(K))

    上面這個公式涉及到兩個不同的雜湊演算法:SHA256和RIPEMD160,英文全稱是Secure Hash Algorithm (SHA)和the RACEIntegrity Primitives Evaluation Message Digest(RIPEMD)。

    但是A並不是我們最後見到的比特幣地址,下面還需要使用Base58Check對A進行編碼,編碼之後和上面的地址一樣了。Base58Check是從Base58變化而來的,只不過在Base58的基礎上新增一個校驗碼。要生成最後的比特幣地址,還需要兩樣東西:版本字首,校驗碼。在比特幣中,有下列資料使用Base58Check編碼前需要新增版本字首:


       上圖中的第一行就是比特幣地址,版本字首為0x00。校驗碼是根據下面的式子得到的:

checksum= SHA256(SHA256(0x00+A))

       上述公式中兩次使用SHA256進行雜湊。然後區checksum的前四個位元組作為校驗碼,新增到A的後面。然後在使用Base58編碼,編碼後的結果就是比特幣地址。

 

       還有一點需要注意:通過比特幣地址是無法推算出公鑰的。

       下面來看一下由私鑰生成比特幣地址的流程:

 

上述流程反過來不成立,也就是後者無法計算得出前者。

名稱

長度

私鑰

256bit

公鑰

非壓縮:520bit(字首佔8位元,兩個座標各256bit)

壓縮:264bit(字首佔8位元,x座標佔256bit)

地址

Base58Check編碼前,A長度是160bit,編碼後長度不固定

       上表中提到公鑰的字首佔8個位元組。公鑰有三個字首,分別是0x02、0x03、0x04,其中0x04是一般公鑰,也就是非壓縮公鑰新增的字首,其他兩個字首下面會介紹。

公鑰佔據的空間特別大,所以需要對公鑰進行壓縮。壓縮前的公鑰新增字首後表示為0x04xy,其中x、y代表了公鑰的兩個座標。因為公鑰其實是某一個橢圓曲線上的一個點,當知道其中一個座標時,完全可以計算得出另一個座標。所以我們可以對公鑰進行壓縮,在公鑰中只包含一個座標即可。由橢圓的特性,我們可以知道通過x可以計算得到一正一負兩個y,所以我們就需要新增一個標誌用於區分y值的正負。上面提到的字首0x02、0x03就是用於區分正負的。正負是我自己的理解,演算法中並不是這樣描述的,演算法中說的是用0x02、0x03區分y的奇偶性。但是意思是一個意思。壓縮之後的公鑰就變成了:

0x02x或者0x03x

除了公鑰壓縮之外,還有私鑰壓縮這種說法。注意私鑰壓縮只是一種誤傳,其實只是私鑰的一種表示方式。私鑰被比特幣客戶端匯出時,只能以WIF格式或者WIF壓縮格式匯出。我理解的匯出其實就是我們可以直接在客戶端看到的格式或者匯出到一個檔案中。上面提到了兩種不同的公鑰表示方法:壓縮公鑰和非壓縮公鑰。對於這兩種公鑰,我們都可以生成對應的比特幣地址,但是兩個地址是不一樣的。對於一些比較新的客戶端使用的是壓縮公鑰,對於一些舊的客戶端使用的是非壓縮公鑰。這就帶來了一個問題:如果一個客戶端使用的是非壓縮公鑰,從該客戶端將私鑰匯出在匯入到一個新的使用壓縮公鑰的客戶端中時,該新的客戶端從私鑰計算出公鑰然後再計算出地址就與舊客戶端上的地址不一致了,這就會導致使用者資金損失。為了解決這個問題,我們在私鑰上加了字尾0x01。當客戶端看到字尾為0x01的私鑰時,就知道這個需要使用壓縮公鑰匯出地址。但是使用0x01又會出現另一個問題:如果當前私鑰中正好有0x01,但是這個0x01就是私鑰中本來就有的,需要使用非壓縮公鑰匯出地址。對於這個問題我想的解決辦法是:通過判斷私鑰的長度即可解決。回到本段開始說的問題,人們誤傳的壓縮私鑰指的就是加了字尾0x01的私鑰。

下面介紹一下匯出WIF格式私鑰的流程(其實就是Base58Check編碼過程):

 

       WIF壓縮格式的私鑰是在私鑰PK上加上字尾0x01然後重複上述流程。因為有固定的字首和字尾,所以WIF格式的私鑰和WIF壓縮格式的私鑰都有相同的開頭:

種類

開頭內容

WIF

5

WIF壓縮

K或者L

       下一篇介紹交易的編碼方式。