三、用拋硬幣的方式來決定記賬人
共識演算法需要適應如下要求:
1、所有記賬人都是公平的,而且這種公平建立在他們具有不同的體質上。
2、競爭成果很容易驗證,每個記賬人看到成果的時候,不需要和其他記賬人溝通,就能自己驗證這個成果。
3、這個競爭成果有一定的難度,不能太容易達成。否則,同一個時間有太多記賬人得到成果,無法定義誰是合法的記錄人。
所以,大家決定,採取拋硬幣的方式,來決定誰是記賬人。誰能夠最快連續丟擲十個硬幣是正面朝上的,將這個圖拍攝下來,就能證明自己是這一次賬單的記錄人。(在這裡,我們假設這個城市的硬幣是有魔法的,它們只能透過拋起隨機的呈現正面和反面,不能擺拍。也就是說,照片是真實可信的,只要能拍下十個硬幣正面朝上的照片,就能證明這個人真的丟擲了這個結果)
於是,每當一批賬單出現時,所有在場的記賬人就分頭自己拋硬幣。直到其中一個人丟擲了十個硬幣正面朝上的結果,展示出這個照片。其他記賬人一看,就停止他們的碰運氣,開始抄錄這個人這一次的賬單。
但是時間一長,有人就發現問題了:如果記賬人事先把硬幣的結果拋好,拍攝下很多張十個硬幣正面朝上的照片。當有賬單產生的時候再展示出來,那他豈不是每次都能獲得記賬權嗎?
四、進化一下拋硬幣的方法
於是,拋硬幣的結果,就要修改一下了。不再是丟擲十個硬幣正面朝上,而必須和實際的賬單內容記錄下來。例如,以下這條賬單:
李四支付張三50塊錢理髮錢
假設李四的身份證號是808,張三的身份證號是707,那麼用數字來表示這個賬單就可以寫成:
808-707-50 (我們約定第一個出現的數字是支付者的身份證號,第二個數字是接收者的身份證號,第三個數字是金額)
轉化成數字就是:808707000050(最後六位是金額,前面留零位)
轉化成二進位制就是:1011110001001010101100011000011011110010
1代表正面,0代表反面。
於是,如果一個記賬人需要記錄“李四支付張三50塊錢”的話。
他需要把硬幣拋成:正反正正正正反反反正反反正反正反正反正正反反反正正反反反反正正反正正正正反反正反
然後將結果拍成照片,公開展示。其他記賬人看到“李四支付張三50塊錢”這個賬單,他們也很容易可以得出這個結果必須是:正反正正正正反反反正反反正反正反正反正正反反反正正反反反反正正反正正正正反反正反。然後他們驗證這個記賬人的照片,如果照片結果和賬單吻合,他們才抄錄這個記賬人的這一次賬單,到他們的賬本里面。
如果這一次需要記錄三條賬單,或者更多怎麼辦呢?例如這次要記錄的賬單是:
李四支付張三50塊錢理髮錢
張三支付王五30塊錢肉錢
王五支付李四20塊錢打車錢
將這些賬單分別轉發為數字就是:
808707000050
707606000030
606808000020
再轉二進位制:
1011110001001010101100011000011011110010
1010010011000000100110101011000110011110
1000110101001000100100110100011000010100
最後再將三個二進位制數字相加,得出結果:
11110111001010011110111110111111010100100
這一次,記賬人必須把硬幣丟擲“正正正正反正正正反反正反正反反正正正正反正正正正正反正正正正正正反正反正反反正反反”的結果,才能獲得這三條賬單的記錄權。
由於最終結果和三條賬單都有關係,所以記賬人如果修改其中一條賬單的任何細節,都會引起最後拋硬幣的結果不同。
然後,我們來看一下這一次記錄的賬單,和拋硬幣的結果:
李四支付張三50塊錢理髮錢
張三支付王五30塊錢肉錢
王五支付李四20塊錢打車錢
證明:11110111001010011110111110111111010100100
恭喜你,你已經獲得一個區塊了。接下來,我們嘗試把很多個區塊連線起來,成為區塊鏈。
五、將拋硬幣的動作連貫起來
按照上述方法,我們記錄下來的賬本大概是這樣子的(請注意每一次的拋硬幣結果都和賬單內容繫結在一起):
1、11110111001010011110111110111111010100100
李四支付張三50塊錢
張三支付王五30塊錢
王五支付李四20塊錢
2、11111011110111011111001101011000111100100
錢七支付趙六80塊錢
候八支付王五100塊錢
馬九支付朱一210塊錢
3、101011100011111100001110011101101100100100
關二支付張三80塊錢
劉十六支付王五100塊錢
曹一百支付關二210塊錢
……
我們發現,雖然每一次記賬,都有照片證明,但他們是互相獨立的。假如有人要修改第一條賬單,將其改成“李四支付張三50000塊錢”的話,他只需要重新丟擲符合這個賬單的結果就可以了。
1、11110100001010011110111110001111010100100
李四支付張三50000塊錢
張三支付王五30塊錢
王五支付李四20塊錢
這時候,這個人將這個區塊放到他的賬本第一條,這和後面兩個區塊是不矛盾的,於是張三的餘額就會大幅度增加,造假的成本太低啦!
我們來改進一下拋硬幣的方式,我們規定,下一次拋硬幣的時候,必須先重複上一次拋硬幣結果的後十位結果才行。於是,賬本就變成這樣:
1、11110111001010011110111110111111010100100
李四支付張三50塊錢
張三支付王五30塊錢
王五支付李四20塊錢
2、1010100100-11111011110111011111001101011000111100100
錢七支付趙六80塊錢
候八支付王五100塊錢
馬九支付朱一210塊錢
3、0111100100-101011100011111100001110011101101100100100
關二支付張三80塊錢
劉十六支付王五100塊錢
曹一百支付關二210塊錢
於是,所有區塊的拋硬幣結果,都關聯起來了,如果一個人要修改第一個區塊,他就必須把後面所有區塊的硬幣都重新拋一次。這個成本可就太高了。區塊鏈為什麼安全不可篡改,道理就在這裡。一個人要造假,最多就是在最新的區塊造假,幾乎不可能修改以前的區塊。
六、比特幣是怎麼玩的
如果讀懂了上面的內容,那麼就知道比特幣其實就是這麼玩的:
2008年或2009年的某一天,中本聰在自己的賬本里寫下了這麼一個賬單:
1、11110111001010011110111110111111010100100
中本聰獲得一個比特幣
這就是比特幣的創世區塊。
然後,中本聰將這個區塊釋出上網,告訴其他所有人。你們來記錄下一筆賬單吧,誰能證明自己丟擲了符合結果的硬幣,誰就能獲得50個比特幣。
於是,有些人覺得好玩的,就加入了這個拋硬幣遊戲,第二個區塊大概是這樣子的:
2、1010100100-101011100011111100001110011101101100100100
李四獲得50個比特幣獎勵
然後,越來越的人參與到這個遊戲中來,拋硬幣的人越來越多,所以難度也就持續上升了。如果說,中本聰一開始只需要拋500次硬幣就能獲得記賬權,現在可能要拋50000億次硬幣,才能獲得。
所以,為什麼比特幣的挖礦如此耗資源,因為挖礦本身就是在拋硬幣,這個動作和記賬本身毫無關係,只是為了獲得記賬權。而且,假設世界上有1000萬人在拋硬幣爭奪,只有一個人能贏得這一次的比賽,其他輸的人都白拋了,他們消耗的能量全部都浪費了。
拋硬幣這種共識演算法,非常的浪費系統能量,但作為完全基於不可信任的去中心化區塊鏈,這也是無奈之舉。如果能建築在一定程度的信任基礎上,可以有其他的共識演算法,有機會我們再講。