看完這篇區塊鏈的文章,就有東西和別人扯皮了,而且扯的還很好

網上有很多關於區塊鏈的文章,要麼過於深奧,對於初學者很難弄懂,要麼過於淺顯,也很難想通。這篇文章從底層講起,由淺入深,從密碼學算法講起,不斷深入,算是一遍比較完備的區塊鏈——比特幣的文章。文章以比特幣爲例,來講解區塊鏈。因爲區塊鏈最初就是因爲比特幣被人們熟知的。堅持把這篇文章看完,比特幣的知識就基本瞭解了。在輔以區塊鏈的一些其他應用,就會真正在區塊鏈這個方面上一個臺階。

1. 比特幣中的密碼學知識

 1.1 哈希函數

  又稱散列函數,散列算法。基本原理就是把任意長度的輸入,通過Hash算法變成固定長度的輸出這個映射的規則就是對應的Hash算法,而原始數據映射後的二進制串就是哈希值。最常使用的MD5和SHA都是歷史悠久的Hash算法。(文末有sha256算法代碼)
  簡單來說,什麼是哈希算法呢?例如比特幣中用到的哈希算法SHA256。對於任意長度的消息,執行SHA256算法都會產生一個256bit長的哈希值,稱作消息摘要。例如:SHA256(“任何東西”)=01011101010…,一共256個0和1。比如說一張圖片,又或者是一段視頻,只要執行SHA256算法,就會輸出一個256bit的字符串,這個字符串就叫做摘要

下面是比特幣中哈希函數的重要特性

  • 【1】collision reistance

  碰撞避免(就是說一般不可能發生碰撞)
  哈希碰撞指的是:x\neqy,H(x)=H(y), H指的是哈希函數,H(X)指的是將X進行一次哈希運算。理論上哈希碰撞是不可避免的但是,一般來說,對於好的哈希算法,人爲製造哈希碰撞的可能性幾乎爲零!這被稱爲:collision reistance。
  哈希碰撞不可避免證明:例如,SHA256,能將任意的輸入轉換爲256bit的串。輸入空間有無窮大,輸出卻只有2256。根據鴿籠原理,一定會有兩個不同的輸入,對應相同的輸出。
  collision reistance有什麼用呢?它比較重要的一個應用是,驗證一個message是否被篡改。例如:如果對message進行哈希運算,即H(m)。一旦message被篡改,那麼可以說他的哈希值一定與之前的哈希值不同。

  • 【2】hiding

  哈希運算單向,不可逆
  正向運算很簡單,也很快,但是逆向運算幾乎不可能。也就是說,給你一個值,要你算它的哈希值,這個很簡單。但是,給你一個哈希值,你不可能知道它的輸入。(輸入空間足夠大,分佈足夠均勻)。

  • 【3】puzzle friendly

  哈希值的運算,事先是不可預測的
  也就是說,如果要求你的哈希運算結果是a,對於想知道輸入是什麼,沒有什麼好的辦法,只能一個一個去試,不可能猜到應該輸入什麼,也沒有什麼好的方法去解開輸入,只能暴力破解(如果輸入空間很大,那麼就稱不可能破解)。

 1.2 數字簽名

  這裏就要說一下,比特幣系統中的賬戶管理。如何開一個比特幣賬戶呢?開戶過程很簡單,就是創立一個公鑰、私鑰對。公鑰就好像你的銀行賬戶,只要別人知道你的公鑰,就可以轉錢給你。私鑰就好像你的賬戶密碼。
  公鑰、私鑰又是什麼呢?這就要提起非對稱加密(RSA)——在計算機網絡中很常見的一種加密算法。
  公鑰和私鑰唯一對應,用某個公鑰簽名過得內容只能用對應的私鑰才能驗證簽名;同樣用某個私鑰加密的內容只能用對應的公鑰才能解密。公鑰私鑰,常涉及兩種情況:

情景假設:A有一個屬於自己公鑰、私鑰對。B有一個屬於自己的公鑰、私鑰對。當A想給B發一條消息時(公鑰是公開的,私鑰只有自己才能知道):

  • 簽名:
    (1)A將要發送信息做一次哈hash,得到一個hash值,再將hash值用A的私鑰加密作爲簽名,後綴在信息的末尾。
    (2)B接到傳輸的資料後,使用A的公鑰解密這段加密過的hash,得到hash值,然後對信息原文進行hash,對比兩次hash是否一致(驗籤)。

  • 信息加密:A用B的公鑰對信息內容和簽名進行加密,這樣,只有擁有B私鑰的人(也就是B)才能將信息和簽名解密。解密後,B再用A的公鑰對簽名進行驗籤

在使用RSA進行通訊的時候,一般是兩者結合,即:簽名->加密->解密->驗籤


2. 區塊鏈的數據結構

 2.1 區塊

  區塊鏈由一個個區塊(block)組成。區塊很像數據庫的記錄,每次寫入數據,就是創建一個區塊。每個區塊包含兩個部分:

  • 區塊頭:記錄當前區塊的特徵值

在這裏插入圖片描述

  • 區塊體記錄的就是交易的詳細信息
    在這裏插入圖片描述
      區塊鏈中的一個個區塊是怎樣鏈接起來的呢?區塊鏈中用哈希指針代替了普通的指針。也就是,區塊頭中存了前一個區塊的哈希值(父區塊的哈希值),通過這樣將區塊鏈接在了一起。(哈希指針並不是真的存在,只是這麼個意思)

  區塊的哈希值是通過區塊頭中的信息進行計算的,對區塊頭進行二次哈希計算得到的數字被稱爲區塊的哈希值。這個哈希值是區塊的標識符,可以通過這個哈希值找到對應的區塊(當然也可以通過區塊高度來查詢,但區塊分叉時,區塊高度有可能會對應多個區塊)。
  我們知道當前區塊中包含來前一個區塊的哈希值,但自己本區塊的哈希值是不存儲當前區塊結構中的。而是當該區塊被接收時由每個節點計算出來。然後存儲在一個獨立的索引數據庫中的,這樣可以方便檢索。

 2.2 Merkle 樹 (區塊中交易信息的組織形式)

  區塊鏈中的節點分爲兩類,一類是全節點,一類是輕節點(SPV)。全節點是保存區塊的整個內容。輕節點是保存區塊頭(不包括具體交易信息),像手機上的區塊鏈錢包通常就是輕節點。

  由於在一個區塊裏面包含很多交易信息(以哈希值來表示),這些交易信息就是通過Merkle樹進行表示的。那麼要怎麼得到這顆樹的呢?
  Merkle樹是自底向上構建的。假設區塊中有A、B、C、D 4個交易信息,那麼將這個4個交易信息分別哈希之後,構成Merkle樹的葉子節點。

  • A節點的哈希與B結點的哈希又組成了它們父節點的哈希值H(AB)
  • C節點的哈希與D結點的哈希又組成了它們父節點的哈希值H(CD)
  • 最後HAB節點與HCD節點又組成了根結點的哈希H(ABCD)
  • 而H(ABCD)就是Merkle根,它歸納總結了所有的交易信息。

在這裏插入圖片描述
  有人可能要問了,這是二叉樹,如果區塊中的交易個數爲奇數,那麼如何計算呢?那就將最後一個交易複製一份然後就可以組成滿二叉樹了。

  那麼,這個Merkle樹到底有什麼作用呢?
例如下圖
  如果輕節點想驗證某個交易(下圖中的tx)是否在某個區塊中,那麼,它要向全節點求助。但是,全節點不會把區塊的所有信息發送給輕節點來驗證,這樣速度會大打折扣。

  • 如果全節點發現,輕節點問的交易信息tx確實在區塊中。那麼,全節點就會把下圖的紅色節點的值發給輕節點,
  • 綠色節點的值,輕節點自己就能計算。
  • 輕節點根據這些哈希值,就能算出根節點的哈希值。再和自己存儲的區塊頭中的Merkle根哈希值對比,如果一樣,就說明信息tx確實在區塊中。

PS: 這裏要聲明一下,有很多人會很疑惑,當全節點知道交易信息tx在區塊中,直接告訴輕節點結果不就行了?爲什麼還要把一條通往根節點的路徑發給輕節點?
  這是因爲,區塊鏈中會有惡意節點。如果全節點告訴你的消息是假的怎麼辦?這時候hash就派上用場了。因爲輕節點已經保存了一個正確的Merkle樹根哈希值,這樣,任何一個全節點,是無法創造出一條錯誤路徑,最後算出的Merkle樹根哈希值與輕節點的值等同(除非這條路徑是確實存在的),這是由hash性質決定的。
在這裏插入圖片描述
  Merkle樹的另一個重要作用是,只要記住Merkle根,就能檢測出對樹中任何一個部分的修改。也就是說,Merkle樹中任何一個交易信息被篡改,那麼最後算出的Merkle樹根的哈希值一定與之前的不同。

3. BTC協議

 3.1 交易

  每個交易都由一個輸入、輸出組成。當前這個交易的輸入腳本和提供幣來源的那個交易的輸出腳本拼在一起,如果能執行,就是那這個交易是合法的。關於輸入、輸出腳本,後面會具體解釋。

舉個例子:一筆交易,需要的信息(輸入、輸出):

  • 輸入:A的公鑰(簽名)、A的幣的來源(A的幣是來源於哪個交易) 、轉賬的數量等
  • 輸出:收款人的地址、收款人的公鑰(或者公鑰的哈希等)

鑄幣交易:所謂的鑄幣交易,就是挖礦所得的獎勵。例如,A挖礦成功,獲得比特幣獎勵,這個交易就叫做鑄幣交易。這個交易只有輸出,沒有輸入。

  • 每個區塊都有一個鑄幣交易。
  • 鑄幣的這次交易,有一個域,叫coinbase域,這個域裏面可以存儲任何東西而不影響其他因素,存儲權在挖礦成功(也就是獲得打包權力)的人手裏。

 3.2 雙重支付問題

  數字貨幣最常見的一個問題就是雙重支付問題。
  比如說A有10個BTC,他卻同時發了兩條消息,一條消息是給B10個,另一條消息給了C10個,這該如何鑑別(紙質貨幣就不會出現這樣的問題)?
  這裏要首先說一下餘額檢查。區塊鏈是把好多交易信息一個塊一個塊進行打包,然後鏈接起來的。每一個礦工(參加記賬的人),都會把把從創始區塊開始的所有區塊下載下來(主鏈)。這樣,他就可以知道別人的餘額。例如:A廣播了一條消息,說A給了B10個BTC,那麼收到消息的人,就會在區塊鏈上查詢、計算,看看A的比特幣最初是從哪來(挖礦獲得、或者是別人給他)、A自己用了多少、還剩多少。若A剩餘BTC大於等於10個,則這個消息被網絡認可,可以作爲一條記錄被打包放到區塊鏈上,否則網絡不接受這個消息。這一過程稱之爲追溯。
  這裏要重申一點,很重要!只有交易記錄被打包放到區塊鏈上,才叫真正的交易成功,否則,交易失敗
  利用餘額檢查,就可以解決雙重支付的問題。接着上邊所說的問題,若D先收到消息1(D查詢知道,此時A餘額已經爲0),那麼他就會拒絕接收消息2。與此同時,若E先收到消息2(E查詢知道,此時A餘額已經爲0),那麼他就會拒絕接收消息1。至於說,D和E誰接受到的消息是對的,就看D和E誰先算出那個數學題,誰先把自己的消息記錄打包到主鏈上。
在這裏插入圖片描述
  所以說,當我們要接收別人的付款時或者付款給別人時,我們不能就認爲錢已經到賬了,我們要等着,只有那條付款記錄被打包放到主鏈上才叫真正到賬成功。一般來說,經過6個確認之後(也就是6個區塊之後),就能保證交易成功。

 3.2 分佈式共識(記賬)

  區塊鏈系統中要記賬,但是記賬以誰爲準?(你銀行賬戶裏有多少錢,不是你說多少就是多少,而是中央銀行裏記錄你有多少錢)。但是區塊鏈是去中心化的,不存在一箇中央機制去記賬。區塊鏈中的賬本是分佈保存在所有的區塊鏈節點中的,也就是說區塊鏈系統中每個節點都維護一個相同的賬本。

  簡單的來說,區塊鏈有一條主鏈,這條主鏈是公開的,是大家都承認的,這個鏈就是由一個一個區塊組成,每個區塊都包含交易信息。每個人都有自己的個人區塊和主鏈,個人區塊裏面記錄他剛剛接收到的交易信息。主鏈就相當於一個賬本。那麼,誰能往賬本上添加信息呢(誰能將一段時間內的交易信息打包成區塊,然後接到主鏈上)?

  每個人通過工作量證明機制(POW)來爭奪記賬權,他們將會計算一個很複雜的數學題,第一個計算出正確結果的人獲得打包的權力。這個數學題很難很難,難到沒有任何一個人能直接用腦袋算出來。必須一個一個去嘗試,類似於暴力破解(就是用計算機去暴力搜索),破解的過程就叫挖礦。一旦這個數學題被成功破解,那麼破解的人會獲得系統給它的獎勵,這就叫挖礦成功。這個獎勵是比特幣的真正來源所在。

比特幣系統中,發行比特幣的唯一方式就是挖礦所得的比特幣(被稱爲鑄幣交易——coinbase transaction)。
  中本聰設計的獎勵方案是,每十分鐘生成一個區塊,每打包一個區塊會獎勵一定數量的比特幣。最開始每打包一個區塊是50個BTC,過4年會獎勵25個BTC,再過4年再減少一半,以此類推。這樣比特幣的產生會越來越少。越來越趨近於一個最值,計算公式:50×6×24×365×4×(1+1/2+1/4+1/8+…)≈2100萬,其中最初獎勵50個比特幣,每小時有6個區塊,每天24小時,每年365天,前四年是如此,之後每四年減半。也就是說,流通的比特幣最多隻有2100萬個,通過打包獎勵將其擴散出去,比特幣不是無限的。

  • 每個人都會有屬於自己的一個字符串,包括:父區塊的哈希值、自己收到記錄(也就是賬單)、時間戳、隨機數nonce、和難度係數n等。(除了交易信息,其他的都在區塊頭中)
  • 每個人都將自己的這個字符串作兩次哈希運算,得到自己哈希值。Hash=SHA256(SHA256(字符串))= 一個256bit的字符串
  • 系統要求,Hash值的前n位爲0的人(這就是前面說到的數學題),即爲獲勝者,將獲得打包的權力,將自己的Hash值放到區塊頭部裏面,再鏈接到主鏈上,然後拿到獎勵,挖礦成功!
  • 第一個成功計算出數學題的人,將獲得打包權力。再將區塊廣播給所有人,別的節點在檢查完區塊內信息的有效性之後,就會把這個區塊加到自己本地保存的主鏈上。
  • 那如何才能使前n位爲0呢?通過不斷的改變隨機數nonce的值,來一個一個嘗試,直到找到符合要求的隨機數。誰先找到,誰就挖礦成功。這裏要注意一下,每個人的計算難度是不同的,因爲每個人所記錄的信息、時間戳等都不同,所以每個人的正確答案也不一樣。有的人可能運氣好,算一次,就可以找到正確答案,有的人算幾萬次都算不出來,這也是有可能的。但平均來看,誰的CPU在單位時間內計算的次數越多,誰就能更快的找到答案。這也是大家爲什麼拼命的去買高性能礦機的原因。
  • 上邊的這個n,是如何確定的呢?很顯然可以看出,n越大,題目就越難。平均每十分鐘產生一個區塊,總體上來看,挖礦成功的概率爲1/2n。現假設世界上有1W臺礦機,每臺礦機的算力是14T次/s = 1.4×1013 次/s,即一秒可以計算這麼多次。10分鐘是600s,所以10分鐘可以做1.4×1013× 600×104=8×1019次哈希運算,從概率角度看,想要挖礦成功需要做2n次運算,列出等式2n = 8×10^19,可以解出n約爲66。這時,n就被設置成66,第一個算出前66位爲0的人將獲得打包的權力。n的值是動態的,隨着礦機的增加、計算能力的增加,難度也會隨之增加,保持出塊時間穩定。所以,挖礦沒有捷徑,只能買更多、更高性能的礦機來增加單位時間內計算的次數。
  • 比特幣系統中規定,每2016個區塊(14天)改變一次難度係數。公式是:target=target*(actual time/expected time)。actual time是過去兩個星期產生2016個區塊的實際時間。expected time是預期產生2016個區塊的時間(也就是兩個星期)。這裏有一個規定,爲了防止意外情況,target不能超過4倍(不管是增加還是減少)。

  PS:這裏多說一句,比特幣系統中實際上是沒有這個n值的。n只是通俗來講,準確的說,數學題應該是:H(H(block header))<=target,target纔是挖礦的難度係數。target越大,挖礦難度越小。這個數學題通俗來說就是前n位爲0

 3.3 最長合法鏈(防篡改)

  考慮一種情況,因爲網絡中傳輸延遲的存在,有可能存在兩個人同時破解了這個數學題(雖然這種可能性很小,但是還是存在的)。那麼,主鏈就會出現分岔
  解決這個問題的方法就是最長合法鏈。比特幣裏面的最長鏈原則,假設某一個區塊後面有兩個礦工同時挖到了礦,或者由於網絡延遲等原因產生了分歧,這時,各個節點先根據自己認爲對的區塊鏈分支,接着挖礦直到下一個區塊產生,這時會有兩條鏈,但是有一條是長的,比特幣規定,以最長的鏈爲準。如果某個節點仍然的固執的以較短的鏈爲準,他就是在和大多數算力作對,這樣做的結果是,他挖的塊不被大家認可,會浪費時間和算力。

還有一種情況,有惡意節點想篡改賬本(就是想篡改主鏈上的信息):
  假設A轉給B10個比特幣,但是他想把這個信息從區塊鏈上刪除,這樣大家就都不知道這個事情存在,就可以賴賬。
在這裏插入圖片描述
  如果有一個人想要篡改信息(假設他想篡改的信息在區塊三),那麼他就必須這樣做,如下,他要在原有的區塊鏈上,計算數學題,重新打包一個不含A給B10個BTC這個消息的包,鏈接到區塊二上,形成B鏈。但是B鏈太短,別人不承認怎麼辦?他必須以一己之力,對抗全世界所有礦機,他要把所有數學題都最先算出來,他纔有一直打包權力,直到他把B鏈長度變得比A鏈的長,那麼他就篡改成功了。
  但是,理論上雖然可以,但現實操作卻幾乎不可能,全世界的礦機都在鏈接A鏈,而你卻要鏈接B鏈,每十分鐘才能出來一個區塊,而且必須你一直都能是第一個算出數學題的人,這可能麼?基本沒可能。區塊鏈的防篡改就是這麼來的。在這裏插入圖片描述

4. 比特幣的實現(Transaction-交易)

  因爲比特幣是去中心化的,所以系統中並沒有哪個地方能顯式的知道A這個賬戶上有多少錢。這個需要通過交易記錄來推算,系統中有哪些交易是給A轉錢,哪些是從A中花錢…
  比特幣系統中的全節點,要維護一個叫UTXO的數據結構。

 4.1 UTXO(Unspent Transaction Out)

  比特幣中沒有餘額的概念。只有一個被全節點維護的UTXO數據結構,這裏面記錄了比特幣系統中所有的“未花費的交易輸出”。

  比特幣系統中的交易,分爲輸入和輸出。例如一筆交易n:A->B轉了一筆錢,

  • 輸入就是: B的地址,轉賬的錢數,A的簽名等…
  • 輸出就是:B獲得了A給他的錢。若以後B想要花掉這筆錢,那麼這筆錢的來源就是這次交易n(Transaction)。
    而,UTXO記錄的就是,那些沒有被花費掉的輸出。

  Transaction之間的網狀關係:一切交易可追溯
  PS:比特幣中規定一次交易必須把賬戶的所有錢一次性花完,除了需要支付的比特幣,剩下的作爲找零,正常的錢包軟件會幫你生成一個找零地址(爲了保護隱私),你的剩餘錢就在那個找零地址裏面了。

考慮如下場景:用戶A和用戶B之間發生了一個交易T3,A向B轉100元。

  • 那這100元,哪來的呢? 來自交易T1:C向A轉的80元 +
    交易T2:D向A轉的30元(共110元,但A只轉了100元給B,10元找零返回給A的賬號)。
  • 同理,C向A轉的這80元,來自用戶E、F的某次交易。
  • D向A轉的這30元,來自用戶E的某次交易。

舉這個例子,是想說明一個問題
  交易與交易之間組成了網狀關係,1個交易的輸出,成爲了下1個交易的輸入;下1個交易的輸出,又成了下下1個交易的輸入。所有的錢,在這個網絡中流動,每1筆錢的去向、來源,都是可追溯的,而這也是區塊鏈網絡的一個重要特點。

  一個交易,可以有多個輸入和多個輸出。

 4.2 錢包

深刻理解了UTXO的概念,錢包就很容易理解了:

某個人的錢包的餘額 = 屬於他的UTXO的總和

在這裏,你會發現一個不同於現實世界的“銀行”裏的一個概念:

在銀行裏,會存儲每個賬號剩餘多少錢。但這裏,我們存儲的並不是每個賬號的餘額,而存的是1筆筆的交易,也就是1筆筆的UTXO,每個賬戶的餘額是通過UTXO計算出來的,而不是直接存儲餘額!!

5. BTC網絡

  區塊鏈是去中心化的網絡,利用P2P技術 (peer-to-peer),所有節點具有平等地位,沒有所謂的中心節點。在這個P2P網絡上的所有節點都直接或者間接地聯通起來,某一個節點上發出的信息,最終可以擴散到全球所有節點。任何節點自由的加入和退出。P2P架構,健壯性強,部分節點遭到破壞對整個系統影響很小。P2P網絡中的聯通不成問題,但是會存在信任問題,這條消息可能歷經了多個節點,消息是否被篡改?消息是否真實(確實是發送方發送的)?這些都需要密碼學的知識來解決這些問題。

區塊鏈節點之間的通信類型主要分爲2種:

① 爲了維持節點與區塊鏈網絡之間的連接而 進行的通信,通常包括索取其他節點的地址信息和 廣播自己的地址信息(地址信息是指 TCP/IP中的 IP地址和端口號).節點新加入區塊鏈網絡時,首先 讀取硬編碼在客戶端程序中的種子地址並向這些種 子節點索取其鄰居節點地址,然後通過這些地址繼 續搜索更多的地址信息並建立連接,直到節點的鄰 居節點的數量達到穩定值.此後,節點會定期通過
ping等方式驗證鄰居節點的可達性,並使用新的節 點替代不可達節點.此外,爲了保證新節點的信息被更多節點接收,節點將定期向自己的鄰居節點廣播 自己的地址信息。

② 爲了完成上層業務而進行的通信,通常包括 轉發交易信息和同步區塊信息(交易和區塊是區塊 鏈中的數據結構,將在交易層介紹).節點轉發交易 信息時採用中繼轉發的模式.始發節點首先將交易 轉發給鄰居節點,鄰居節點收到交易後再轉發給自 己的鄰居節點,以此類推,逐漸傳遍整個網絡.同步 區塊信息採用請求響應的模式.節點首先向鄰居節 點發送自己的區塊高度(類似於ID),如果小於鄰居 節點的高度則索取自己欠缺的區塊,如果大於鄰居 節點的高度則鄰居節點將反向索取區塊信息.所有 節點都不斷地和鄰居節點交換區塊信息,從而保證 整個網絡中所有節點的區塊信息保持同步。

6. 比特幣腳本

  比特幣腳本是一種基於棧的腳本語言,也被稱爲堆棧語言。不是圖靈完備的,在比特幣沒有賬戶的概念,誰擁有這筆交易的輸出誰就可以花費這筆交易中的比特幣,爲了證明擁有這筆交易的輸出就需要提供密鑰接受驗證,驗證通過就可以花費這筆交易的輸出。

一個交易實例如下:
在這裏插入圖片描述

  • Input Scripts——輸入腳本,分別對應上邊的最左邊兩個
  • Output Scripts——輸出腳本,分別對應上邊的最右邊兩個輸出

交易結構:
在這裏插入圖片描述

  • txid:交易的id號
  • hash:交易的哈希值
  • version:使用的比特幣協議版本號
  • size:這個交易的大小
  • locktime:用來設定交易的生效時間,一般爲0
  • vin:輸入腳本(後面會詳細講)
  • vout:輸出腳本
  • blockhash:所在區塊的哈希值,可以看到是以一長串的0開頭,這就與前面提到的挖礦的難度要求相對應。
  • confirmations:這個交易已經有多少個確認。一般6個確認後,交易就可以說是不可改變的了。
  • time:交易產生的時間(表示從很早的一個時間點到現在過了多久)
  • blocktime:區塊產生的時間

交易結構是一個數組:每個交易的輸入,都要說明輸入花的這個幣的來源,是來自之前的哪個交易的輸出。

在這裏插入圖片描述

  • txid:輸入要花的這個幣的,是來自於id=txid的那個交易的輸出
  • vout:表示是那個交易的第幾個輸出
  • scriptsig:輸入腳本

如果有多個輸入的話,就要說明每個輸入的來源,並且要簽名。也就是說,比特幣中的一個交易可能要多個簽名。

在這裏插入圖片描述

  • value:轉賬的金額,單位是比特幣(BTC)
  • n:表示序號,是這個交易的第幾個輸出
  • scriptpubkey:輸出腳本

如下圖
  前面一個區塊中,交易是:A把比特幣轉給了B。後面的那個交易指:B把比特幣有轉給了C。B->C比特幣這個交易中,幣的來源是前面A->B這個交易。要驗證這個交易的合法性,就要B->C這個交易的輸入腳本,和A->B這個交易的輸出腳本,拼接在一起執行。早期是拼接在一起執行,後來爲了安全起見,就先運行B->C這個交易的輸入腳本,運行成功後,再運行A->B這個交易的輸出腳本。兩個腳本都運行成功(棧頂的結果爲非零值),表示這個交易是合法的。
  如果有多個輸入的話,每個輸入腳本都要和前一個交易的輸出腳本相運行,來檢查這個交易是否合法。全都驗證通過,這個交易纔是合法的。
在這裏插入圖片描述
輸入/輸出腳本的幾種形式:

 6.1 P2PK(Pay to Public Key)

——這種形勢是最簡單的

input script:
    OP_PUSHDATA(Sig)
output script:
    OP_PUSHDATA(PubKey)
    OP_CHECKSIG

輸出腳本中直接給出收款人的公鑰。輸入腳本中直接給出簽名就行。CHECKSIG是檢查簽名的操作。

腳本的執行:
PUSHDATA(Sig)——把輸入腳本的簽名壓入棧
PUSHDATA(PubKey)——把輸出提供的公鑰壓入棧
CHECKSIG——講棧內兩個元素彈出,用公鑰檢驗簽名是否正確。若正確返回True

  6.2 P2PKH(Pay to Public Key Hash)

——這種形式是最常見的

input script:
    OP_PUSHDATA(Sig)  //壓入簽名
    OP_PUSHDATA(PublicKey)  //壓入公鑰
output script:
    OP_DUP  //複製棧頂元素,再壓入棧
    OP_HASH160  //彈出棧頂元素,取哈希在壓入棧
    OP_PUSHDATA(PubKeyHash)  //壓入輸出腳本提供的公鑰哈希
    OP_EQUALVERIFY   //彈出棧頂元素,比較是否相等
    OP_CHECKSIG   //公鑰檢查簽名是否正確

輸出腳本里沒有直接給出收款人的公鑰,而是給出了收款人公鑰的哈希值,公鑰是在輸入腳本里面給出的。

腳本的執行:
PUSHDATA(Sig)——將簽名壓入棧
PUSHDATA(PubKey)——將收款人公鑰壓入棧
DUP——把棧頂元素複製一遍
HASH160——將棧頂元素彈出,再將彈出的元素取哈希,再把得到的哈希值再壓入棧。所以棧頂變成了公鑰的哈希值。
PUSHDATA(PubKeyHash)——把輸出腳本里提供的公鑰的哈希值壓入棧
EQUALVERIFY——若棧頂兩個公鑰哈希值相同,則彈出
CHECKSIG——用公鑰檢驗簽名,若正確返回True

 6.3 P2SH(Pay to Script Hash)

——這種是最複雜的一種

  這種形式的輸出腳本是收款人提供腳本(redeemScript)的哈希,到時候收款人要花費這筆交易的時候需要輸入腳本的內容和簽名,驗證的時候分兩步;

  • 驗證輸入腳本的哈希是否與輸出腳本中的哈希值匹配
  • 反序列化並執行redeemScript,驗證input script給出的簽名是否正確
input script:
    ...
    OP_PUSHDATA(Sig)          
    ...
    OP_PUSHDATA(serialized redeemScript)  
output script:
    OP_HASH160                   
    OP_PUSHDATA(redeemScriptHash)  
    OP_EQUAL

Pay to Script Hash在比特幣最初版本是沒有的,後期軟分叉加入,最重要的一點是對多重簽名的支持。

多重簽名中,只要提超過供指定數量即可,容忍了一定程度的私鑰丟失。
原來的多重簽名需要外部用戶提供一一對應的公鑰,一共有多少公鑰,幾個公鑰通過驗證就可以完成交易,對用戶比較繁瑣,現在使用Pay to Script Hash將整個過程打包到腳本中,對外部用戶來說降低了多重簽名的複雜性,將複雜性轉移到了系統中。

 6.4 Proof of Burn

output script
    RETURN
    .
    .
    .
    .[zero or more ops or text]

這種形式的output被稱爲Provably Unspendable/Prunable Outputs

腳本說明:加入有一個交易的input指向這個output,不論input裏的input script如何設計,執行到RETURN命令之後都會直接返回false,不會執行RETURN後面的其他指令。所以這個output永遠無法被花出去。其對應的UTXO也就可以被剪枝了,無需保存。

  這種類型的腳本,他的輸出腳本中第一個就是RETURN——無條件返回錯誤。也就是說,包含這個操作的腳本永遠不可能被驗證通過,RETURN後面跟的內容根本沒法執行。這樣的輸出,意思就是這個交易的錢永遠花不出去。
設計這個腳本的目的:

  • 比特幣的銷燬(帶有一定目的的)
  • 往區塊鏈裏面寫入一些內容,區塊鏈是一個不可被篡改的賬本,有人利用這個特性,在區塊鏈中保存一個需要永久保存的內容。就是把你要保存的內容放在RETURN後面。有的交易只有少量輸入,輸出比特幣是0。說明輸入的比特幣都用來給礦工獎勵費了。

PS:前面講到的,鑄幣交易裏面會有一個coinbase域,coinbase域裏面也可存儲任何東西,而不會對系統造成任何影響。但是隻有獲得打包權力的礦工有權利來使用相應區塊裏面的coinbase域。而Proof of Burn是所有節點都有權。

7. 比特幣的分叉(fork)

  • state fork,forking attck(分叉攻擊),人爲造成的,deliberate fork
  • protocol fork,比特幣協議發生了改變,需要對軟件升級,在一個去中心化的環境中,沒有辦法保證所有的節點都能升級軟件。根據修改協議內容的不同,又可以分爲硬分叉、軟分叉。

 7.1 硬分叉(hard fork)

  如果對比特幣協議增加一些新特性,例如對區塊大小限制(block size limit)進行修改,之前規定block size limit是1MB,大概每個區塊有4000個交易左右,平均十分鐘出一個區塊,這樣算下來,每秒鐘大概7比交易,延遲非常高。像我們平常使用的信用卡,每秒鐘處理的交易,和比特幣每秒處理的交易,完全不在一個數量級上。所以,有些人覺得,區塊太小,限制了比特幣的交易,並且增加了交易延遲。假設發佈軟件更新,將1MB更新爲4MB。假設大多數節點已經更新,只有少部分沒更新。就會發生如下的情況:
在這裏插入圖片描述
上邊的鏈,是已經更新過協議的新節點挖出的區塊,區塊大小限制≤4MB。下邊是還未更新的舊節點挖出的區塊,區塊大小限制≤1MB。
  舊節點認上邊的鏈是非法鏈,因爲超過了block size限制,所以舊節點只挖下面的那條鏈。
  因爲大部份節點都已更新,新節點挖出的區塊一定比就舊節點挖出的多。所以上邊的鏈比下邊的鏈要長,新節點根據最長鏈原則,只在上邊那條鏈上挖區塊。
  所以最後的結果就是,只要舊節點不更新協議,這個分叉就會一直存在,是永久性的。這就是所謂的硬分叉(hard fork)。

  比特幣社區中有人比較保守,對於有些軟件更新,不支持。並且block size limit也不是越大越好,比特幣採用的P2P網絡的廣播,區塊越大,對網絡帶寬消耗是非常大的,帶寬是瓶頸。

 7.2 軟分叉(soft fork)

  對比特幣協議加一些限制,原來一些合法的交易,在新區塊中可能變的不合法了。
  假設發佈軟件更新,將1MB更新爲0.5MB。假設大多數節點已經更新,只有少部分沒更新。就會發生如下的情況:
在這裏插入圖片描述
上邊的鏈,是已經更新過協議的新節點挖出的區塊,區塊大小限制≤0.5MB。下邊是還未更新的舊節點挖出的區塊,區塊大小限制≤1MB。
  舊節點認上邊的鏈是合法鏈,因爲≤1MB。舊節點認爲下面的鏈也是合法鏈,因爲≤1MB。但根據最長鏈原則,舊節點認爲上邊的鏈式合法鏈,下面的是非法鏈。會轉而去挖上邊的鏈。
  新節點認爲下面的鏈是非法鏈,因爲存在>=0.5MB的區塊。所以會挖上邊的鏈。
  所以最後的結果就是,新舊節點都認爲上邊的鏈是合法鏈,但是,當舊節點將1MB的區塊鏈接到上邊的鏈上後,新節點又不認同,又會出現分叉…
  這個分叉是臨時性的(這也是爲什麼被稱爲軟分叉的原因)。舊節點如果不更新協議,就會發生挖礦失效的情況,就是說,舊節點辛苦挖到的比特幣,不被認同,並且自己也不認同自己挖到的礦(比較委屈)。

  實際當中,可能出現軟分叉的情況:給某些協議中沒有規定的域,增加一些新的含,賦予她們一些新的規則。例如:鑄幣交易中的coinbase域,挖礦的時候,需要修改block header中的nonce來找到合適的結果,但是nonce只有四個字節,很有可能出現所有情況都試完還沒有發現正確結果。這時候就要調整coinbase域,例如將coinbase域中前8個字節也用來作爲nonce。有人提出將coinbase後面的部分存儲UTXO的根哈希值。這樣,新節點發布的區塊,舊節點是認的(因爲coinbase域裏面本來是可以存儲任何東西)。舊節點發布的區塊,新節點有可能不認同(coinbase域裏面放的不是一個根哈希值)——這就是軟分叉。比特幣中比較著名的軟分叉是:P2SH:Pay to Script Hash。


8.區塊鏈總結

  關於區塊鏈——比特幣的知識就說到這。下面我會就區塊鏈的特性,以及其他的一些應用來擴展講解區塊鏈。區塊鏈近幾年很火,但是還是要有一顆清醒的心,要真正理解一項技術,再去談是否要把它作爲方向,不能隨波逐流。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章