1. 程式人生 > 實用技巧 >區塊鏈筆記-(肖臻)(二刷自用筆記)-----二

區塊鏈筆記-(肖臻)(二刷自用筆記)-----二

BTC實現

UTXO

比特幣系統的全節點要維護一個叫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)。也就是你把我的交易打包在區塊裡,我給你一些小費。交易費一般很小,也有一些簡單的交易沒有交易費。

圖解區塊,交易

在這裡插入圖片描述

一個區塊的例子
第一行表明:該區塊包含了686個交易
第二行:總輸出XXX個比特幣
第四行:總交易費(686個交易的交易費之和)
最下面一行:區塊獎勵(礦工挖礦的主要動力)
第五行:區塊的序號
第六行:區塊的時間戳

第九行:挖礦的難度(每隔2016個區塊要調整挖礦的難度,保持出塊時間在10分鐘左右)
倒數第二行:挖礦時嘗試的隨機數

右邊:第一行:該區塊塊頭的雜湊值
第二行:前一個區塊塊頭的雜湊值
(注意:計算雜湊值只算塊頭)
兩個雜湊值的共同點:前面都有一串0。是因為,設定的目標預值,表示成16進位制,就是前面一長串的0。所以凡是符合難度要求的區塊,塊頭的雜湊值算出來都是要有一長串的0。
第四行:merkle root 是該區塊中包含的那些交易構成的merkle tree的根雜湊值。
最後一行:是32位的無符號整數。nonce只有2的32次方個可能的取值。按照比特幣現在的挖礦情況來說,很可能把2的32次方個取值都驗了一遍也找不到合適的。那怎麼辦呢?block header 的資料結構裡還有哪些域是可以調整的呢?

在這裡插入圖片描述

第一行:比特幣協議的版本號(無法更改的)
第二行:前一個區塊的塊頭的雜湊值(無法更改)
第三行:merkle tree的根雜湊值(可以更改)
第四行:區塊產生的時間(可以調整)比特幣系統不要求特別精確的時間,可以在一定範圍內調整。
第五行:目標預值(編碼後的版本)(只能按協議中的要求定期調整)
第六行:隨機數

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

鑄幣交易沒有輸入,它有一個coinbase,可以寫入任何的內容。也可以把digital commitment裡的commit的雜湊值寫入裡面。也可以把第一節講到的預測股市的內容寫入裡面,coinbase的內容是沒有人會檢查的,甚至可以寫你的心情。

在這裡插入圖片描述

對應的是最後一個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。

在這裡插入圖片描述

該交易有兩個輸入和兩個輸出。
左上角:這裡的output其實是輸入,指的是之前交易的output。
右上角:這裡的output都是unspent,都沒有被花掉,會儲存在UTXO裡面。
右邊表格第一行:輸入的總金額。
依次往下:輸出總金額、兩者之間的差值。
兩表格下面:可以看出輸入和輸出都是用指令碼的形式來指定的。

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

挖礦

提到挖礦是一個無記憶性的過程Memoryless,符合Bernulli trail。

挖礦的工程如果不是Prograss free,那麼算力強的礦工會取得不成比例的優勢。

這塊的理解,以拋硬幣為例也是一個bernulli trail,memory less的過程,我想要一個正面的結果,和我前面幾次拋硬幣獲得反面的結果都沒有關係。在比特幣挖礦過程中,我計算一個nonce,再計算下一個nonce時是否能滿足條件都是不可知的。

這個Bernulli process過程,和窮舉金鑰攻擊的過程相識,但不一樣。窮舉金鑰,在金鑰空間中存在這樣的金鑰,計算一個結果就排除一個結果。但是挖礦過程,就好像拋一個2256個面的骰子,只有2target個面是符合條件的。

zero confirmation(交易才釋出,沒有放入區塊鏈)

電商執行一個全節點或委託一個全節點監聽區塊鏈上的交易,他收到轉賬交易之後要驗證該交易的合法性(有合法的簽名,以前沒有被花過),甚至不用等到該交易寫入區塊鏈裡。這種操作聽起來風險很大,交易剛釋出出去,都沒往區塊鏈裡寫呢。其實,零確認在實際當中,用的還是比較普遍的。為什麼呢?

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

假設某個有惡意的節點獲得記賬權,它還能做什麼壞事?能不能故意不把某些合法的交易寫入區塊鏈裡?即釋出的區塊故意不包含某些交易。這是可以的。

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

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

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

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

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

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

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

BTC網路

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

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

要加入P2P網路首先得知道至少有一個種子節點,然後你要跟種子節點聯絡,它會告訴你它所知道的網路中的其他節點,節點之間是通過TCP通訊的,

比如說A→B和A→C,這兩個如果同時廣播在網路上,那麼每個節點根據在網路中的位置的不同,收到兩個交易的先後順序不同。

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

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

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