Blockchain 101

It is a tutorial provided by Prof. Shun-Wen Hsiao in National Chengchi University, Taiwan. The materials are adapted from the class slides, books, and documents from the network. Please contact hsiaom at nccu dot edu dot tw if any question. English version will be available later (hopefully).

The design of this tutorial is to understand the essentials of the blockchain and to avoid unrealistic high expectation to blockchain. You should build the capability of judging what problem can be solved by blockchain (or not) and make blockchain truly productive and to create innovative applications. I expect the reader has no experience of blockchain.

Outline

  1. What is Fintech (Financial Technology)?
    • FinTech definition
    • Why blockchain is an important technique of FinTech?
  2. How to prove and measure your 'sincerity' in the digitized world?
    • Hash function
    • Hashcash and the concept of Proof-of-Work
  3. Block and a valid block
    • What is Mining (mathematical lottery)? Why mining is a difficult problem?
    • What is the relationship between solving Sudoku and PoW?
  4. Blocks as a chain
    • Why blockchin is immutable? Chain Reaction
    • The very first block (Genesis Block)
  5. Distributed Blockchain
    • Which Miner has the right to append a new block on blockchain?
    • Fork problem and Confirmation
    • Can miner cheat?
    • What if we don't like PoW?
  6. Make blockchain as a ledger using Bitcoin as an example
    • Why blockchain can solve Double Spend problem of the digital currency?
    • Coinbase (i.e., mining reward)
  7. Bitcoin Transaction
    • Transaction (Tx), Unspent Output (UTXO) design
    • Blockchain never encrypts Tx, but it signs Tx by using Digital Signature: Pay-to-PubkeyHash (P2PKH)
  8. Bitcoin Header
    • Data Structure
    • Merkle Tree
  9. Bitcoin infrastructure
    • What a Wallet does for you?
    • Want to be a miner? Join a Mining Pool.
    • Different chains: Mainnet and Testnet.
    • Light weight client a.k.a Simplified Payment Verification (SPV) node
  10. Security Problems
    • 51% attack
    • Sybil attack
  11. Design
    • Simplified Payment Verification
    • Lightning Network

1. FinTech

  • In Wikipedia, the definition of FinTech is as follows.
    • "Financial technology is the new technology and innovation that aims to compete with traditional financial methods in the delivery of financial services. FinTech is a new industry that uses technology to improve activities in finance."
  • FinTech 是一個產業 (industry),而並非是一個特殊的技術或是商業模式。FinTech 的目標通常是有效率地 deliver 金融服務至客戶手上,但與傳統的金融服務不同,金融科技應該導入至少一個新的科技技術至金融服務上,或是提出一個創新的商業經營模式來提供金融服務。這些創新(無論是科技上或商業上)通常是傳統的金融機構尚未導入,或者是相關法規不允許金融機構導入的。數十年前「銀行網路連線系統」就是一個很好的早期 FinTech 案例,當時引入新的網路資料庫技術可以使得存款客戶不需要回到開戶本行就可以領款,使得金融服務更加便利。另外,以創新的「P2P 貸款」為例,傳統上貸款人需要跟金融機構貸款;但 P2P 貸款模式則是向不認識的群眾貸款,讓願意借款的群眾能賺取更高額的利息收入,但同時也分散借款的風險,而提供P2P 貸款服務的機構則是提供平台來搓合貸款人以及願意借款的群眾。
  • FinTech 不一定要採用新興的科技技術(例如我們將要討論的 blockchain),但起碼要提供一個創新的金融服務手段。FinTech 產業可以是提供新的應用或產品(例如:人工智慧推薦投資標的、電子貨幣)、更好的流程(例如:手機快速開戶、數位資產清算)、或是新的商業模式(例如:P2P 貸款)。
  • 以銀行服務 (payment) 來說,我們已經從傳統的現場紙幣交易,推進到網路銀行、塑膠貨幣、電子支付、第三方支付等,未來可能的下一步或許是發行電子貨幣、數位債券、ICO (Initial Coin Offering) 等。但由於金融服務受限於主管機關的規範,因此 FinTech 的發展通常不是受限於技術發展或是商業創新,而是法規監管的問題。
  • 數位貨幣 (digital currency) 是一個很常被討論的金融科技問題。數位貨幣是以數位的形式作為「交換的媒介」,提供即時交易、跨國界、交換擁有權等特性,與實體的貨幣有一定程度的差別。若數位貨幣可以成真,那麼應該可以引起一波 FinTech 的新應用風潮。但數位貨幣一直以來都飽受兩個技術問題,其一是防偽 (counterfeit)、另一個是雙花 (double spend),最終這兩個技術問題是由區塊鏈解決(稍後會陸續談到解決技術),但同樣的,數位貨幣能否實行一直以來是受限於各國法規問題,而非技術問題。

2. Hashcash and PoW

  • 在數位的世界裡面,我們很難衡量一些概念,例如「誠意」。因此我們把數位世界裡能夠使用到的「資源」視為一種手段拿來做討論,這些資源包含有:CPU 運算量、硬碟容量空間、網路頻寬等。基本上就是去 Amazon, Google, Microsoft 買虛擬機器時,你會考慮花費的成本以及所能獲取的資源。
  • 網際網路的使用基本上是免費的(除了「最後一哩」你要付錢給 ISP 廠商讓你家或手機連上網),例如寄一封 e-mail 到網際網路上是幾乎沒有任何成本的。這也就是為什麼垃圾電子郵件會這麼多的緣故,因為寄一封垃圾電子郵件和寄一萬封垃圾電子郵件,對濫發電子郵件的人來說幾乎沒有成本上的差異。但是在現實的世界裡面,濫發實體的廣告郵件是有郵費成本的,所以濫發者會慎選發放的對象。而站在(好的)寄件者這一方,我們通常會利用「郵資」的多寡來提醒收件者要注意這是一封重要的郵件,請多關注這封郵件,不要把它當廣告信。掛號、限時等機制就是如此來提醒收件者,寄件者已經付出了更多的成本(誠意?),請你務必要看這封信的內容。
  • 以上這樣的概念在數位世界裡也被實踐過:若是寄發電子郵件的人,能夠證明自己曾經花費過一定程度的資源成本在寄送這封電子郵件,那麼收件者應該要多注意這封郵件,而非當作是垃圾電子郵件。這個概念在 1997 年由 Adam Back 提出,稱之為 Hashcash,而這裡所謂的「資源」是指一定量的 CPU 的運算工作(工作量)。也就是說在寄出一封電子郵件時,寄件人必須提供一個數位的證明(證明寄件者已經花費了一定的 CPU 工作量作為成本)並夾帶在電子郵件中,收件者收到電子信件後可以簡單地覆核這個數位的證明(以實體信件來說,郵局會覆核郵資並蓋上郵戳,收件人也可看到該郵戳),並雙方同意這封信件具有一定的「誠意」。
  • 這樣具有數位證明工作量的系統稱為 Proof-of-Work (PoW) system,但要證明寄件者付出一定程度的 CPU 工作量是不容易的(而且收件者還要可以簡單的驗證),因此 Hashcash 引入雜湊函數 (hash function) 的技巧來解決這個 PoW 的問題。
  • Hash function 能夠將任何可以被數位化的資料(例如:數字、文字、聲音、影像)作為函式的輸入 (input),並且輸出一個一定範圍內的整數,稱為 hash 值。以數學的函式表示式來說 y = f(x) 中,x 就是任意輸入的數位化資料,f 是定義好的 hash function,y 就是輸出的 hash 值。許多數學家曾發明各種不同的 hash function,但這些雜湊函式都遵守一些基本的設計規範,例如 (1) 無法藉由 y 而反推導出 x,例如:y = f(x) = x mod 10 這個餘數函式 (mod) 無法藉由 y = 9 來反推 x 的值,因為有眾多的 x 們 9, 19 , 29, ... 符合這個函式 f 的定義。(2) 雜湊函式必須是 deterministic 的,也就是說給定雜湊函式的公式 f 以及 x 時,全世界任何人都可以獨立地計算出正確的 y 值,因此隨機亂數產生器 (random number generator) 是不可以用來設計 hash function。(3) 利用 x 來計算 y 時是沒有規律性的。以過去常見的 MD5 hash function 為例,以下五個範例可見即使 x 的差距是 1,但所對應的 y 值卻看似沒有規律性(y 值這裡是使用 16 進位表示,以下的 hash 值都是很大的整數)。(4) 若給定 y 值,幾乎不可能找到兩個不同的 x1 和 x2 ,使得他們具有相同的 y 值。比較簡易的方式去找 x1 與 x2 就是直接利用窮舉法,將所有可能的 x 值輸入到 f 中,看是否能碰巧找到相同的 y 值。但 x 值的數量是沒有上限的,你不知道什麼時候才能找到這樣的 x1 和 x2。
    • MD5(1) = C4CA4238A0B923820DCC509A6F75849B
    • MD5(2) = C81E728D9D4C2F636F067F89CC14862C
    • MD5(3) = ECCBC87E4B5CE2FE28308FD9F2A7BAF3
    • MD5(4) = A87FF679A2F3E71D9181A67B7542122C
    • MD5(5) = E4DA3B7FBBCE2345D7772B0674A318D5
  • 工作量證明系統是如何利用雜湊函式實現的呢?下圖是一個簡單的範例,從要寄出的郵件中選擇一些事先約定的欄位們(例如:寄件者與收件者的 email address )作為 x,並隨機選擇一個 counter c,將 x+c 輸入至雜湊函式,產出雜湊值 H。Hashcash 的雜湊函式是採用 SHA-1 的版本,其輸出的 hash 值會介於 0 到 2^160-1 之間(這是一個非常大的整數區間範圍)。Hashcash 有一個雙方事前同意的隱含規則,這規則要求其所輸出的 H 值必須小於 2^140,這封信件才符合規範。但由於郵件已經寫好了,所以 x 值是不能改變的;若先前隨機選取的 c 值無法讓產出的 H 值小於 2^140,那麼寄件人必須一再地更換 c 值,直到某個 c 值可以讓 SHA-1(x+c) = H < 2^140。由於 hash function 的特性,我們是無法用數學方法解出哪一個 c 值可以配合 x 來產出一個小於 2^140 的 H 值,所以最有效的方法就是一一窮舉所有的 c 值,然後試算其 H 是否小於 2^140。這樣一一窮舉的過程,必定會耗費寄件人一定程度的 CPU 運算量。
  • 寄件人若找到一個這樣的 c 值,則將該 c 值附在電子郵件中,一並寄送給收件人。收件人在收到 x 以及 c 的情況下,可以輕易的驗算 SHA-1(x+c) 是否小於 2^140。若是,則表示寄件人一定曾經花費某種程度的 CPU 資源來一一尋找適合的 c 值(但這裡不討論寄件人實際花費多少時間來尋找這個 c 值),所以收件人可以收下郵件;若否,則寄件人沒有通過運算量證明的機制,寄件人無法證明其工作量(也就是誠意或郵資),收件人可以視該郵件為垃圾郵件。有了這樣的 PoW 機制,寄送電子郵件再也不是近乎零成本了,每發出一封電子郵件就必須讓 CPU 運算一陣子,因此大量的發送垃圾電子郵件的情況可以降低。
  • 由於需要寄件與收件雙方共同採用此工作量證明的機制,因此在實務上 Hashcash 並沒有真正的被使用,但這樣的設計機制卻啟發了區塊鏈的技術。PoW 的機制還可以用來抵禦類 DoS (Denial of Service) 的攻擊,經過簡單的工作量證明,我們可以讓攻擊者不是肆無忌憚無成本的發送網路封包訊息或垃圾郵件,因此簡易的工作量證明過濾機制可以消弭部分的攻擊行為。基於這個原理,PoW 還可以應用在訂票系統上,熱門的票券常有黃牛大量訂購,訂票系統可以讓每一個使用者計算一個 PoW,並附在訂票流程當中,一般的使用者很可能不會在乎這一個額外的 CPU 工作量,但是對於黃牛來說,在要求多張票券的情況下會引發大量的 CPU 運算,如此可以遏止計算資源有限的黃牛。
  • 由於使用暴力法隨機尋找 c 值的時間是不可預估的,所以在多組人同時尋找 c 值的情況下,會產生一個無法事先預估的先後順序關係。而這個先後的順序關係將在區塊鏈中有著重要功用──確立共識(稍後會詳述)。

3. Block

  • 區塊鏈的基礎是區塊 (block),block 可以被視為是一個存放資料方法。最簡單的區塊結構有三個欄位:block number 欄位指名這是第幾號的區塊,data 欄位可以放置任意你想要放在區塊中的數位化資料,而 nonce 欄位則是一個整數。區塊鏈借鏡 Hashcash 的作法,一個合法 (valid) 的區塊被定義為:將欄位資料(下圖中黃色的部分)置入雜湊函式後,其輸出之 hash 值應小於一個 target 值。(這個雜湊函式以及 target 值都可以事先設定好,或臨時變動。)類同於 Hashcash,由於區塊編號是按順序編排的以及你想要存的資料內容是不可變動的(黃底綠紋),由於雜湊函式的輸出值是近乎無規則的,因此若要讓 hash 值能小於某 target,你只能用暴力法去試出哪一個 nonce 值可以讓區塊成為 valid block。
  • 在真實的區塊設計上,黃底綠紋的欄位還可以有很多,例如:時間戳記欄位、區塊資料大小、版本編號等資訊,這是由不同區塊鏈的設計者自行決定的。
  • 在比特幣區塊的設計上,target 值是由一個預先定義的數學公式所計算出來的,該數學公式會參考區塊中一個名為 Bits 的欄位,並可將 Bits 值轉換為 target 值。下圖例,雜湊值的輸出值 H 有一個固定範圍(圖例是 0 至 2^256-1),因此當 target 值越小,我們使用暴力法找到一個 nonce 值使區塊是合法的(H < Target),則越困難(花費的時間長);相反的,若 target 值越大,則找到一個 nonce 使得其 H 值恰好落在 0 至 target 值之間的機率就越大,暴力找到 nonce 就越快速。在比特幣的設計中,最大的 target 值(0xFFFF x 10^ 52)被定義為難度 (Difficulty) 1,難度是一個相比於最大 target 值的倍數;而現今 (2018.07) 的難度倍數值約莫是 5,363,678,461,481,也就是說目前的區塊 target 值為最大 target 值的 5.3 兆分之一。相較於 hash 的值域(0 到 2^256-1 ),目前的 target 是一個很小的數字,也因此暴力法找尋 nonce 值(比特幣的 nonce 值是一個介於 0 至 2^32-1 之間的整數)是一件很困難的事情,這個尋找 nonce 的過程我們稱之為挖礦 (mining)。挖礦基本上被視為是一種「數學樂透」(mathematical lottery),因為在 hash function 的特性下,我們完全無法預估下一個 nonce 值是否幸運地讓 block 是 valid 的(H < target) 。運氣好的礦工 (miner) 可以在第一次隨機選擇 nonce 時,就碰巧讓區塊是合法的;運氣不好的礦工要試完全部的 2^32 個 nonce 值,才在最後一個 nonce 值讓 block 合法。由於 data 這個欄位放置的是 miner 任意放置的數位資料,因此是否能迅速地找到搭配的 nonce 值完全關乎運氣,所以這樣帶有運氣成分的過程才被叫做挖礦。
  • 在區塊的設計上,target 值通常是可以動態調整的。這起因於隨著時間流逝,硬體計算速度(通常以每秒鐘能計算幾百萬次的 hash function 運算做評比,也就是 million hash per second, MH/S)增加,所以 target 值通常是隨時間逐漸下降的(也就是 difficulty 倍數會逐漸上升)。在比特幣的設計中,演算法會每 2016 個區塊會自動調整 target 值,使至於 nonce 值不會被太快找到。在區塊鏈的設定上,設計者可以自行評估全球的運算能力 MH/S 以及自行設定當前的 difficulty 值,來推導平均大約幾秒鐘可以找到 nonce 值,以比特幣的設計來說,設計者將這個數字設定在十分鐘。
  • 利用 PoW 來設計 valid block 有以下特性:(1) 可以經由調整 target 值,控制找到 nonce 的速度。(2) 給定 data 和 block number,暴力搜尋 nonce 值是困難的。(3) 若給定 data, block number, nonce,則驗證是否小於 target 是快速而容易的。以上三點特性跟數獨 (sudoku) 遊戲是類似的:(1) 數獨可以藉由調整棋盤的大小,來控制數獨遊戲的難度。(2) 給定數獨的提示數字,可直接暴力解空格的數字,但是需要花費一些時間。(3) 若給定提示數字與空格的答案,我們可以快速地驗證該答案是否符合數獨的規則(每行每列只出現每一個數字一次)。

4. Blockchain

  • 區塊鏈即是把數個區塊根據 block number 串聯成鏈。在實務的設計上會多一個 PreHash (previous hash) 欄位一並記錄上一個區塊的 hash 值,而因為第一個區塊沒有 previous block,通常我們會填 0 這個數字在這個欄位中。
  • 假設我們在一個已經有三個區塊的(極度簡化)區塊鏈上(見下圖),想要附加資料到第四個區塊上,步驟是:(1) 準備好一個空的區塊,並在 block number 欄位上填上 4。(2) 將區塊三的 hash 值 H3 填入區塊四的 PreHash 欄位。(3) 將自己要儲存的數位化資料(例如:任何文字、數字、聲音、圖片、影像、流水號)填入 data 欄位中。(4) 開始進行第四區塊的挖礦,暴力窮舉出找出一個 nonce 值,使得 block 4 的 H4 小於 target 值,使至於 block 4 為 valid。如此我們就完成了新增一個區塊資料到當前的區塊鏈上。
  • 區塊鏈上儲存的資料通常被稱為是不可竄改的 (immutable)。以區塊二號為例,若你修改了 data 欄位的內容,而根據 hash function 的特性,因為 input 不同了,所以其輸出新的 H2* 值必定(幾乎)不會等於原先的 H2 值。而此新的 H2* 值同時也(幾乎)不可能小於 target 值(因為 target 值通常真的很小),所以此時 block 2 在 data 欄位被改變的情況下會變成 invalid。而由於 block 3 中,有一個 PreHash 欄位要引用 H2(現在 block 2 被竄改後,會引用新的 H2* 值了),所以原先 H3 的值也變為新的 H3*,而同樣的 H3* 也(幾乎)不可能會小於 target,而使至於 block 3 會變成 invalid。這個 PreHash 欄位的設計,可以保證若區塊鏈上的資料有被竄改過,你一定可以藉由 block 是否 valid 來判別竄改是否發生,而這個 invalid 的狀況會連鎖反應從第一個被竄改的區塊蔓延至當前最新區塊。
  • 當然若你是這個區塊鏈上唯一的使用者 miner,你可以針對想要修改的 block 資料進行修改之後,重新 mine 所有 invalid 的區塊(也就是為每一個 invalid block 找尋新的 nonce 值,使其 H < target),並讓修改後的資料與新的 nonce 值更新於區塊鏈上,但是重 mining 是很耗費時間的,尤其是你想要修改的區塊離當前的區塊是很遠的時候。不過這個單人的使用 scenario 較不常見,因為通常區塊鏈是多人的使用環境。(下一章節會討論多人使用的環境。)
  • 通常編號第一號的區塊稱為創世區塊 (genesis block)。比特幣區塊鏈的第一個區塊在其某個資料區段中存放了當天泰唔士報的頭版標題與日期,因此我們之知道比特幣區塊鏈是2009年1月3日之後被創立的。當創世區塊被決定好之後,區塊鏈的參與者就可以接著下去製作自己的區塊,並接在其後。
  • 有一點很重要的是,區塊鏈的概念中並沒有限制資料欄位應該要存放什麼種類的資料,也沒有限定應該要有哪些欄位,這些設計都是由區塊鏈的設計者自行規劃的。舉例來說,可以將 IP address 與 Domain Name 的資訊放在 data 中,這樣這個區塊鏈就成為 DNS 查詢系統。或是將畢業學生資料放在 data 中,這樣這個區塊鏈就成為畢業證書查詢系統。因為區塊鏈可以自由的加入與退出而 data 區段原始的設計是沒有加密的,因此這樣的區塊鏈可以成為一個很好的資料庫系統,公開透明又可以抵抗竄改。
  • 區塊的生成是可以作弊的嗎?基本上因為 PreHash 這個欄位的設計,每一個新的 block 一定要包含上一個 block 的 hash 值,所以只能在上一個區塊產生之後才能開始窮舉 nonce 並計算該區塊的 hash 值,所以基本上你是不可能在目前 block number 是 3 的情況下,預先產生 block 5 的區塊,因為 H4 的值還未知道。

5. Distributed Blockchain

  • 可以假想分散在網路上的一群人參與區塊鏈的運作(彼此可以靠網際網路溝通),目前很巧合的每個人都在各自的硬碟上儲存了一份前三個 blocks 的內容,而且內容都完全一致。若其中的 A 想要在第四個區塊儲存自己的一張照片,而某 B 想要儲存自己寫的一首數位單曲,這兩人會各自準備好自己的區塊四內容,並且分頭開始進行 mining 的動作。若數學樂透很湊巧地讓 A 和 B 幾乎同時找到各自的 nonce 值來匹配 A 的數位影像和 B 的數位音樂,那麼在此時 A 與 B 會各自分別在通訊網路中向其他所有參與區塊鏈的人公布自己找到了 nonce 值,並同時把自己的第四個 valid 區塊一並給其他人。其餘的人在收到新的第四區塊時(無論是來自於 A 或是 B),會獨立地進行驗證,確認收到的區塊內容的 hash 值會小於 target,並且確認新的第四區塊的確是接在第三區塊後的(藉由 H3 值確認)。在此情況下,同時有兩個合法的第四區塊,我們稱這種情形為分岔 (fork)。
  • 分岔問題是需要被解決的,而目前所有的參與者會暫時接受有分岔的區塊鏈,但稍後會仍有其它人想要繼續儲存資料到區塊鏈上,此時新的區塊五會被接在分岔的分支之後。至於要接在 A 或是 B 的分支後面是由參與者自由選擇,而由於網際網路通訊通常有延遲,各參與者會在短時間內先後收到 A 與 B 各自公布的區塊四,而參與者通常是將區塊五接在先收到的那一個區塊四上。以下圖為例,假設 C 先收到 A 的區塊四(稍後才收到 B 的區塊四),那麼 C 產出的區塊五通常會接在 A 的區塊四後面;而若 D 先收到 B 的區塊四(稍後才收到 A 的區塊四),那 D 通常會將區塊五接在 B 後。若此時碰巧 C 和 D 又幾乎同時公布各自的區塊五,那麼區塊鏈分岔的情況仍會持續。由於所有的新區塊都會廣播至所有的參與者上,所以所有的參與者都知道有分岔的情況發生,但他們仍舊繼續各自努力地在自己支持的分支上產出新的區塊。分岔的情況會被解決在有某一分支上的區塊(例如 E 的區塊六)擁有比較長的鏈,而其它分支基於數學樂透的狀況,比較不幸運地未能較早(或幾乎同時)挖出同長度的區塊,而使至於該分支比較短。因此在 E 優先公布區塊六之後,B 與 D (和這個分支陣營的其他 miners) 會驗證 E 的區塊六無誤後,放棄 B-D 這一個分支的 valid blocks,轉而支持 A-C-E 這個較長的分支(主鏈)。而所有的 miners 會驗證並更新自己本機硬碟上所儲存的 blocks 資料,並又再次達到全員 synchronize 的狀態(所有人都有六個一致的 blocks)。
  • 因此 B 的數位音樂雖然曾經被放在 valid block 之中並上鏈,但由於有分岔的情況出現(並且輸了),該資料會被下鏈。被下鏈的資料通常會需要在主鏈上重新被 mine 過,因為此時 block number 和 PreHash 都不一樣了,即使 data 內容一樣,還是需要重新 mine 一個新的 nonce 值。數學的機率理論可以證明,若是使用比特幣區塊鏈,若你的區塊後接了六個以上的區塊(英文稱為 six confirmations),那麼被下鏈的機率是近乎於零。可以想像有一條(不死心的)短分支想要(在落後六個區塊的情況下)追上主鏈,那麼短鏈必須要在挖礦的過程中非常幸運,要比長分枝更快挖出區塊,才能慢慢追上長分支,但數學可以證明追上的機率是近乎零。
  • 各個 miners 必須要遵守遊戲規則(區塊鏈協定),才能使區塊鏈正常的運作下去。因此有人形容區塊鏈實際像是一個分散式的資料庫,最終所有人會達成一個共識 (consensus) 確認所有儲存在區塊鏈上的資料都是一致的 (identical)。這樣的共識是以分散式的形式被達到的,而非有一個中心管理者出來統籌資料的一致性或處理分岔問題,所有的 miners 只要持續接收被廣播的 blocks,並根據遊戲規則獨立進行驗證即可達到共識 。不過可以預先想像一個狀況,若有一個大戶或駭客可以掌握一半以上的 miners,並且故意毀壞區塊鏈協定,那麼這個區塊鏈基本上就失去了大眾的信任,不會有人想要參與這個區塊鏈了。但若只有少數人不遵守區塊鏈協定,那麼在所有資訊是廣播的情況下,我們可以忽略那些少數壞人的廣播訊息,在分散式的環境下,可以使用拜占庭演算法 (Byzantine Fault Tolerance, BFT) 來決定哪些訊息是多數且正確的訊息。
  • 在實務上,每一個 miner 只要把自己手上最新區塊的 hash 值拿出來廣播並比對,馬上就可以知道各自本機硬碟上整條區塊鏈資料是否是和其他人的區塊鏈是 identical 的。以上圖來說,miners 彼此交換 H6 的值就可以知道是否在資料的一致性上達到共識。若有 miner 不願意遵守遊戲規則,任意的產生或廣播不正確的區塊,其它的 miners 會直接 discard 這些 invalid 的區塊。
  • 共識來自於兩種規則:(1) 找到 nonce 的 miner 可以廣播自己的區塊,他擁有了寫資料上區塊鏈的權力。每一個收到該區塊的其它 miners 有能力獨立驗證該區塊是否 valid。(2) 若有分岔的情況,較長鏈的分支為主鏈,其餘分支的資料將下鏈。
  • 要注意共識的設計都是可以更改的,例如擁有將資料接上區塊鏈的權力,可以不一定是 PoW 機制。區塊鏈的設計者可以設計任何一種類似投票的機制來決定眾 miners 哪一個可以優先寫資料接上當前的區塊。驗證區塊是否 valid 也不一定要靠 nonce 與 target,數位簽章也可以確認區塊內容是正確的。

6. Distributed Ledger

  • 既然區塊鏈的 data 可以存放任何資料,那麼我們來試試存放帳務資料。一個最簡化的銀行帳務交易資料可能包含:交易流水號、付款帳號、收款帳號、金額。如果我們允許一個 block 裡面可以存多筆有順序的交易資料 (Transaction, Tx),它們在區塊鏈上可能看起像是下圖。試想若所有的 miners 都擁有一份這樣一致而完整的複本在自己的本機硬碟上,而 miners 肩負起記帳的工作,不斷的把新的交易資料寫上區塊鏈,那麼一個簡易的分散式帳本就完成了。這個帳本上登記的最終狀態紀錄(從創世區塊到目前最新的區塊)便是區塊鏈參與者共同認定的帳本,我們可以從這個有順序的紀錄去計算出每一個帳號目前有多少錢。
  • 要注意一點,區塊鏈到目前為止,只能處理資訊流,也就是 data 裡面都是數位化的資料。區塊鏈沒有辦法處理像是「鈔票」或是「物品」這樣具有實體的東西。因此我們這裡所說的記帳,只單純涉指帳的資料,並沒有考慮背後是否真的有金流的交換。但是(就是這個「但是」很重要),若我們承認帳上的數字其實就是對應一個新興的數位貨幣,所有人從創世區塊開始就沒有擁有任何這個新興數位貨幣的資產,而一步步的藉由交易轉換這個數位貨幣的擁有權。而加入這個區塊鏈的參與者都同意,帳上的數字的確蘊含某種價值,那麼這樣的數位貨幣也是有機會被實現的。目前比特幣就是以這樣的概念在運行。至於數位貨幣的發行,稍後會談到。
  • 但世界沒有這麼美好,雖然 data 可以存放任何資料,但上面這種登記帳務的設計方式有很多缺陷。例如:帳號的生成是否需要一個中心化的單位來支援開戶?若帳戶沒有足夠的金額,那麼交易應該不能被登記上鏈。miners 為何要免費幫大家記帳(要記住生成 block 是需要花費 CPU 工作量的成本)?若交易紀錄是造假的,miners 有辦法驗證嗎?交易的雙方和記帳的人可能都是網路上不認識的個體,我們要怎麼確認對方會按照交易的遊戲規則進行?(以往我可以相信銀行,但現在我可以相信 miners 嗎?)。而這一些的問題,目前是由比特幣的數位貨幣設計來解決,這個設計利用的一個叫做 unspent output (UTXO) 的想法(稍後會談到)。
  • 但先退一步來岔題:或許你會問若數位貨幣的設計是直接把紙鈔的流水號變成一個電磁紀錄(比方說一個檔案是一塊錢),存在擁有者的儲存裝置上,這樣不是簡單得多嗎?我若想要使用這一塊錢的數位貨幣,就把這個電磁紀錄檔案移轉到另外一個人的電子錢包上,這使用情境就跟使用實體貨幣一模一樣,只是實體的鈔票交換變成電磁紀錄的交換。這個設計會衍生兩個基本問題:防偽 (counterfeit) 與雙花 (double spend)。因為電磁紀錄是永遠可以被複製的,所以你難保這一個一塊錢的檔案會不會被有心人複製多份,並在交易的時候分送給不同的收款者,這個情境稱為 double spend。收款者因為沒有一個良好的驗證機制,所以他無法知曉這一塊錢是否在其他地方被用過。這個在實體紙鈔的世界裡不會發生,因為那一個流水號的紙鈔只有一份而已,要進行紙鈔的複製是很不容易的。實體紙鈔的防偽也很簡單,透過防偽機制(變色墨水、防偽條、浮水印)就可以辦到了,而且使用者可以輕易的自行驗證真偽。但是電磁紀錄就只是一堆零與一的組合,我們難以防範壞人自行產生假的一塊錢檔案,收到這偽造一塊錢電磁紀錄的人也無法簡單的確認這個電磁紀錄是否由權威單位(央行)發行。(不過如果你對非對稱式加解密有了解的話,這個電磁紀錄防偽的部分可以利用政府的私鑰加密來解決,不過這後頭需要龐大且中心化的 PKI 基礎設施來營運,暫且不考慮。)
  • 我們先來談談交易紀錄如何從交易雙方發送給 miners 以及被登記至區塊鏈的流程,再來討論交易紀錄的內容應該記載那些資料,以避免大家不誠實的記帳。以下是一個過度簡化的流程,詳細的資料結構和演算法我們後面再談。(1) 首先,交易雙方先互換帳號資料以及約定要交易的金額。在實務上,用手機的 APP 互傳訊息就可以辦到了。(2) 付款方準備一筆交易資料 Tx(你暫且認為是圖中 data 裡某一個 row 的資料就可以了)並把裡面的資料都填妥。(3) 付款方在 Tx 中加註一個付款方的數位簽章,這個簽章可以證明這個 Tx 的內容必定是真的 (genuine) 由付款方填寫,並且沒有竄改的可能。(4) 付款方把這個 Tx 經由網路廣播給收款方以及所有的 miners。(實際廣播的方式只有送給附近的,但附近的人會逐步再送給他們附近的人,一路下去直到所有人都收到這個 Tx)。(5) 收款方收到 Tx 後會看一下 Tx 的內容,確認收款人以及金額是正確的。若是錯誤的,這個損失是付款人承擔(就跟你去 ATM 打錯匯款帳號是類似的)。(6) Miners 收到這個 Tx 之後,會把 Tx 置入在一個 Tx pool 中。Tx pool 會放置所有尚未處理的 Tx 們,每一個 miner 都會自己 maintain 自己的 Tx pool。(7) 每一個 miner 會隨機地從自己的 Tx pool 挑選數個 Tx 出來(因為這世界無時無刻都有人在交易,所以 Tx pool 總是有尚未處理的 Tx),並且用這些被挑選出來的 Tx 們開始製作自己的 block。挑選與檢查 Tx 的機制後面會談。(8) 在眾多 miners 當中,總有一個幸運的 miner 最快找到適合的 nonce 值來搭配他選的 Tx 們,來產生一個 valid 的 block。這個幸運的 miner 會盡快廣播這個 block 給其他miners。(9) 其他 miners 可能正在努力的挖礦,但被這個新 block 的廣播打斷。其他 miners 會迅速的驗證這個 block 是否 valid,若是,則只好中斷並放棄先前自己正在猜 nonce 的 block。其他 miners 會承認這是新的 valid block,並接在當前的鏈上,並更新當前 block number 的值。這個 block 裡面內含的 Tx 們,則將會被一一的從 miners 各自的 Tx pool 中拿走。(10) 接下來,miners 會重複 6 到 9 的流程,不斷地接收新的 Tx,並且不斷地試圖產出新的 block。
  • 以上的流程有一些值得注意的地方。首先,全世界的 Tx 都會廣播給 miners,但由於網路延遲的關係,每一個 miner 手上維護的 Tx pool 不見得會是一致的(但你可以放心,被廣播出去的 Tx 最終一定會進到每一個 miners 的 Tx pool)。再者,要挑選使用哪些 Tx 組成一個 block 是 miner 的自由,所以儘管 block number 以及 preHash 都相同,但每一個 miner 的 data 可能都不同,所以 miners 要各自努力找自己的 nonce 值。第三,在每一個 block number 的競爭產出 valid block 這件事上,最終只會有一個贏家(即使有 fork 也是如此,最終 分支會被解決,只有長鏈上的 miner 才是贏家),因此其他剩下的 miners 的 CPU 運算量都被浪費了,而這樣的浪費是數學樂透的結果。
  • 一個額外的區塊欄位 Coinbase:miner 辛勤運算的獎勵(之一)與貨幣發行。事實上,miner 不會想要做白工,他需要有動機來幫所有的交易者正確無誤地記帳,因此在區塊鏈的設計上比較好的策略是:讓「miner 得到的獎勵」與「miner 正確無誤記帳」這兩件事是綑綁在一起的。亦即,如果你不按照規則產出 valid block,你就收不到你的 reward。獎勵有數種形式,我們這裡先談其中一個稱為 coinabse。miner 在組裝自己的 block 的時候,除了選擇 Tx 們之外,他會附加一筆自己創建的特殊 Tx,這個 Tx 沒有付款人,只有收款人(miner 會在收款人填上自己的帳號),這個 Tx 上的金額是區塊鏈協定決定的,必須按照一定的金額規則填寫。也就是說,若這一個 miner 很幸運的最快算到 nonce 值並廣播此 valid block 上鏈,那麼所有的區塊鏈參與者同意,有一筆無中生有的新貨幣被發行,並給予了這個 miner。這就是數位貨幣發行的一種方法,這樣的發行並非是有任何中心化的單位在主導的,而是參與者同意這套區塊鏈協定,並按照協定內容發明新的貨幣(其實就是一個數字被加到帳上)。
  • 有獎勵就會有作弊和監督的機制可以討論。我們先前說過,區塊的生成是數學的樂透,miner 根本就不知道自己是否夠幸運能夠生成下一個區塊並上鏈,所以你只能老實的挖礦。若你想要作弊(無論是生成 invalid 的 block 或是在 coinbase 上填寫比區塊鏈協定更高額的獎勵),其他所有的 miners 會直接把你的 block 給 discard 掉。在有利可圖的情況下,其他 miners 是不會放過做壞事的人,因為這會侵蝕到他們的獎勵。當然若你不同意這樣的獎勵分配,那最好的策略就是退出這個區塊鏈。當某個區塊鏈上的參與者越少,這個區塊鏈上的獎勵就越沒有價值。舉例來說,一個小村莊發行了自己的點數,點數是有價的,可以拿來換食物或服務。點數的取得是來自於在小村莊裡做公共服務,村長會訂定一套規則來產生點數發放給勞動的人。點數也可以私下與村民交換。若大多數的村民認可這個小村莊,認為村長有能力維持這個點數的「內在價值 (intrinsic value)」,而點數的流通與交換機制也是大夥兒同意的,那麼這個小村莊點數就可以較長遠的持續下去。但若村長濫發點數導致點數的購買力下降,或是村莊生產力少或點數流通困難,或是外部有一個更好用的城鎮點數,那麼想要參與這個點數機制的人就會少了(信任度下降)。因此,點數的發行人應該都會盡量的維繫住點數的生成與流通的透明,並且驅逐不良的點數使用,讓大眾有加入使用點數的動機。若一個點數沒有人願意使用,那麼它被認可價值是極低的。(請把這個段落的點數換成「法幣」,村長換成「央行」。)
  • 在數位貨幣裡面,沒有發行貨幣的這個角色,coinbase 是協定的內容,而大家願意認可與接受這樣的協定,所以就按照協定發行新的貨幣。你可以制定你自己的數位貨幣發行規則,只是你要考慮是否有參與者認可你的發行規則,願意加入你的區塊鏈。
  • 以比特幣來說,它就是一個自成體系的數位貨幣,你擁有多少比特幣就看帳上的結算數字就可以了。比特幣貨幣的發行在協定中有一個最高的發行上限約莫是 21 萬個。目前 (2018.07) 每一個新的區塊會加 12.5 個比特幣到那個幸運的 miner 的帳戶上。你可以跟擁有比特幣的人(無論他是從 coinbase 取得或是跟別人交易換來的)用真實的法幣去交換比特幣,這跟拿台幣換美金是類似的,網路上有合法的交易所 (Exchange) 提供這樣的服務,目前 (2018.07) 在交易所一個比特幣可以用約 6300 美金換得,交換後你可以在某個 block 上看到某 Tx 被轉入若干金額的比特幣到你指定的帳號上,這筆紀錄將由所有的 miners 共同維護,並永久的紀錄在區塊鏈上。
  • 挖礦是真的有利可圖的嗎?這關乎你平均一天要花多少電費(CPU 運算 hash 要吃電、冷卻要冷氣)、要購置哪些軟硬體設備才能幸運的取得 coinbase。在 miners 正常的競爭當中,挖礦其實是賺不到什麼錢的,你所取得的獎勵通常拿去付電費會幾乎是 even 的情況。你試想若你投入大量的硬體設備提高你的 MH/S,所以你可以有機會挖得比別人快,取得較高獎勵。但是投入的硬體設備會導致提升全球的 MH/S,我們之前提過,比特幣的協定中會儘量讓每一個區塊產出的時間控制在十分鐘左右,若全球的 MH/S 提升,則有對應的機制會調低 target 值,使得找 nonce 更加地不容易。在全球競爭的情況下,挖礦的獎勵應該會很接近 even。當有利潤時,投入挖礦的人會變多,又會導致利潤被稀釋(甚至不夠付電費),然後就會有 miners 退出;接著 miner 少了,利潤又高了,挖礦又容易了。這樣周而復始地下去,最終利潤會很接近打平電費。

7. Bitcoin Transaction

  • 這章要討論比特幣中 Tx 真實的資料樣貌,以及為何比特幣設計的電子簽章機制可以保證所有的參與者都會不想要作弊。如果你想要了解區塊鏈,而不是比特幣這個數位貨幣,那麼這章節可能不適合你閱讀,這章節帶有太多技術細節是只跟區塊資料若只放置交易資料有關,若你的區塊鏈不是要儲存交易資料,可以跳過這個章節。(或者你可以看到本章第二張圖之前的段落即可,但是你會少學很多東西就是。)
  • 比特幣官網的定義如下,每一個特性的說明如下。
    • "Bitcoin uses peer-to-peer technology to operate with no central authority or banks; managing transactions and the issuing of bitcoins is carried out collectively by the network. Bitcoin is open-source; its design is public, nobody owns or controls Bitcoin and everyone can take part."
      • Peer-to-peer 表示每一個加入的參與者彼此是對等的在網路上通訊,並沒有一個中心化的伺服器整合運作,但經由訊息廣播以及遵守比特幣的區塊鏈協定,參與者共同 (collectively) 經營整個比特幣區塊鏈。
      • No central authority 係指沒有中央的角色來做統整。而值得提到的一點是比特幣的協定也是逐漸有在修改的,會不定時的推出新版本的軟體或是演算法的設定,而這些些修改和開發並沒有限定特定人士參與,你也可以成為開發的一份子。
      • Transaction 是指 Bitcoin 區塊的 data 是專門放置 Tx 交易資料,這也成為他身為數位貨幣的最大特色。但請記住區塊鏈不等於數位貨幣,區塊鏈不等於比特幣
      • Issuing 是指在區塊中加入發行貨幣的概念,而不只是儲存交易資料,貨幣發行可以增加 miners 的 incentive。
      • Open-source 是指比特幣的軟體是開放原始碼,你可以在 GitHub 上找到最新的程式碼,內含所有的比特幣運作邏輯和協定,因此全世界的人都能細究運作的流程。
      • Pubic 與 everyone 是指比特幣的區塊鏈是任何一個人都可以參與的,你可自由地加入或退出,你可以成為 miner 或是單純的做為付款方或收款方,不會有任何的中央單位審核你是否可以加入或退出。這點與銀行相異。
  • 比特幣的技術是來自於 2008 年匿名(署名是 Satoshi Nakamoto,中本聰)發表的一篇論文,標題是「Bitcoin: A Peer-to-Peer Electronic Cash System 」,內容是闡述如何用 PoW 解決 double spend 的問題以及如何在眾多 miners 中達成共識選擇一個贏家的區塊上鏈。比特幣的區塊設計如前所述,但比特幣的 Tx 設計則大有蹊蹺。
  • 比特幣的一個交易外觀長得像是如下圖傳統的 double-entry bookkeeping ledger。一個 Tx 分兩側,左側為 input(s) 而右側為 output(s),分別對應帳簿的 debits 以及 credits。左側表示在這一次的交易中,有多少的金額會被拿來使用作為轉出;右側表示這些金額將會被分配轉出至不同的帳戶。在會計記帳的準則中,左右各自加總的數字應該要平衡,但比特幣的一個 Tx 總是不平衡。比特幣的交易紀錄方式並不是餘額制 (balance) 的,也就是說我們並不紀錄原先 A 有 50 元餘額,B 有 20 元餘額,在經過一次 10 元的交易後,A 剩下40 元餘額,而 B 增加至 30 元餘額。比特幣的交易紀錄比較像是支票交易,以下圖例來說,左側的三個 inputs 可以假想成 A 先前從別的地方取得了三張付給 A 的支票,上面載明了只有 A 本人可以使用,其上分別各有額度,其總額是 0.6 個比特幣 (BTC)。右側的 outputs 表示這 0.6 BTC 的金額將拆分為兩份,分別有各自的金額,每一份上面會標記這個 output 是要給哪一個帳號的,假設是 B 得到 output #0 而 C 得到 output #1。(是的,沒錯!一筆 Tx 可以付款給不只一個人。)我們可以發現左右的金額並不相等,多了 0.05 BTC。這多出來的金額會被視做為 transaction fee,金額不需要特別指明,這是 miner 的另外一個 reward。當此 Tx 被廣播至各 miners 並進入到 Tx pool 後,miner 在挑選 Tx 組裝成 block 時,miner 通常會依據這個餘留下的 Tx fee,來決定要不要挑選這個 Tx。若留下的 Tx fee 多,那麼你的這個 Tx 會有比較大的機會被納入 miner 的 block 中,進而上鏈。一旦 Tx 上鏈(而且不會因為 fork 而下鏈)之後,這筆 Tx 就算完成了。這個幸運的 miner 除了在這個新的 block 獲取 coinbase 之外,這些餘留下來的 Tx fee 也會進到這一個 miner 的口袋。(當然如果這個 miner 亂算 Tx 或是 coinbase,這個 block 廣播後是會被其他 miners 忽略的,這個 miner 也就收不到獎勵了。)
  • 每一個 output 上面會載明是要轉入至哪一個帳戶,並且金額是多少。這一個 Tx 整體的動作就類似畫掉 A 的三張支票(這三個 inputs 會被註記為使用過已花掉),並發出了新的支票給 B 和 C。一旦這個 Tx 紀錄上鏈後,B 和 C 稍後就能自行使用收到的新 output 在其他的 Tx 上。
  • 如下圖,你大概可以猜到,Bob 在 TxID 1234 所收到的 output,在稍後 Bob 自己的付款 TxID 5678 上,可以(概念上)直接被拿來當作 input 使用,所以這是一個類似支票的概念。這樣我們不需要維繫 Bob 的帳戶裡面到底有多少餘額,只要 Bob 能夠出示尚未使用過的 output 就可以進行交易了。下圖我們還可以看到一個特別的 output,也就是 Bob 在 Tx 5678 當中,又寫了一張 0.05 BTC 的支票給自己,這是一個找零 (change) 的概念,因為在比特幣的設計中,被標記成已經使用掉的 output (spent output) 會直接整個作廢,所以 Bob 若是只要付給 David 0.3 BTC 的時候,要記得幫自己找零。而尚未被使用的 output 被稱為 unspent transaction output,縮寫為 UTXO。
  • UTXO 的設計需要額外的幾個機制和使用條件:(1) output 指明要給 Bob 的時候(包含金額),別人不能竄改這個資訊。(2) Tx 1234 的訊息不可以加密,要大家都可以看到 Bob 和 0.4 這兩個值,這樣一來 Bob 也好驗證 output 真的是轉給他的。(3) Bob 在花費這個 output 的時候,要廣播送出 TxID 5678 的內容給 miners。Bob 必須證明是他本人要使用掉這個 output,而並非是一個隨意的人發出 Tx 要花掉 Bob 的 UTXO。Miner 需要有機制可以檢查送出 TxID 5678 的人真的是 Bob 本人。(4) 若 Bob 不在網路上,Bob 也同樣可以收錢。這類似在飛機上沒有網路也可以刷卡是一樣的概念,如此一來收款人不需要時時連線在網路上,只要付款人在網路上就可以了。
  • 基於以上的要求,input 和 output 裡面要記載的東西就要更複雜得多了,不能只是單純的帳號和金額。在付款給 Bob 的 output 上面,Alice 會留下一道鎖,這個鎖的運作方式有點像是贖回程序 (redeem process) 或是挑戰腳本 (challenge script)。這個鎖的製造不需要 Bob 的參與,Alice 可以單方面製作出來並留在 output 上,只要 Alice 有 Bob 的帳號資訊就可以製作了。然後 TxID 1234 會先上鏈,我們可以在此時認為 Alice 已經把錢給了 Bob,即使 Bob 根本沒有參與 TxID 1234 的製作。
  • Bob 在製作 TxID 5678 的時候,會提供一個回應腳本 (response script) 在 input #0 當中,這個 input #0 會指名要使用 TxID 1234 的 output #0 做為一對的 challenge-response pair。這個回應腳本裡面應該要包含一個鑰匙來對應 Alice 當初為 Bob 設下的鎖,若 Alice 當初設下的鎖,可以被 Bob 提供的鑰匙解開,那麼 Bob 的 TxID 5678 才會被 miner 處理,然後Bob 才可以使用這個 output 裡面的金額再發新的 output 給 David。(當然同理,Bob 這時候會對給 David 的 output 設下另一個鎖。)這組鎖和鑰匙實際上各是一小段的程式碼和資料的組合,所以我們才叫它們為 script。若 Bob 無法提供適當的解鎖腳本,即使 Bob 將此 Tx 廣播出去,這個 TxID 5678 也會直接被 miner 忽略,從 Tx pool 中被移除,因此也絕對不可能會上鏈。既然上不了鏈,David 就不認為 Bob 有給他錢。
  • 在討論腳本前,我們先說明 Alice 和 Bob 是如何產生帳號的。比特幣區塊鏈的使用者通常會先去下載一個名為錢包 (wallet) 的軟體,錢包軟體裡面有三項重要的功能:產生帳號與相對應的密碼學參數、追蹤紀錄帳號目前所有的 UTXO 以及收款紀錄 、利用 UTXO 產生 Tx 來付款。錢包首先會隨機產生一組對稱式加解密演算法的公鑰與私鑰,錢包會把私鑰保管好,絕對不能讓別人知道自己的私鑰。公鑰 (pubkey) 會經過特殊的 hash function,之後產出一個名為 pubkeyHash 的雜湊值。(要記住,pubkeyHash 是不能回推 pubkey 的!但有 pubkey 可以算出 pubkeyHash 。)pubkeyHash 會再經過運算產出一個錢包位址 (address),這個位址可以被視為是你的帳號。(注意,address 是可以以回推 pubkeyHash 的!)這個 address 可以公布在網路上或是直接傳給付款方,不需要保密。這個產生帳號流程完全由錢包負責,而且你可以在一個錢包中生成多個 addresses。(要記住公鑰不是帳號位址!)
  • 當 Bob 將自己的 address 交付給 Alice 之後,就沒有 Bob 的事情了。Alice 要獨自製作出 TxID 1234 的內容,我們先看 output 的部分,至於 input 的部分,我們稍後用 Bob 的 TxID 5678 來解釋。如下圖,單一個 output 的內容只有三項:nValue、scryptPubkeyLen、scriptPubkey。
    • nValue 表示要轉給 Bob 的金額,在比特幣裡面,實際上我們並不使用 BTC 當作單位,我們是使用 10^-8 BTC 做為單位,這個單位又叫做 satoshi,也就是比特幣論文上匿名使用的名字。因此 0.3 BTC 的轉帳,在 output 裡面 nValue 實際是標註 3000000。
    • scryptPubkeyLen 是一個整數,表示後方接續 scriptPubkey 這個腳本的長度。
    • scriptPubkey 是一個程式的腳本(比特幣所使用的腳本語言定義可上網參考),這個程式語言能做的事情很有限(術語是 Turing In-completeness),不似我們一般寫的 C、Java 或是 Python。不過比特幣已經給了幾套預先寫好的腳本(目前 default 的腳本叫做 Pay-to-PubkeyHash, P2PKH),我們只要套用資料上腳本就可以了。P2PKH 中的 scriptPubkey 腳本如下圖,共有五行(圖中是紅底線左到右),其中四個OP_ 開頭的是程式的 動作,第三個 <pubkeyHash> 是 Alice 要填入的資料,因為 Alice 有預先跟 Bob 要了 address,所以 Alice 可以回推 pubkeyHash 值,並填在這個欄位中。
  • 單一個 input 內容有五項:hash、n、scriptSigLen、scriptSig、nSequence。如下圖,我們使用 Bob 要送出的 TxID 5678 為例。
    • hash 涉指這一個 input 要花錢的時候,要使用當初 Bob 收到的哪一個 output。hash 指的就是 TxID 1234。
    • n 在此範例中就是 0,表示是 TxID 1234 的 output #0 是目前這一個 input 想要使用的 output。
    • scriptSigLen 同樣是一個整數,表示後方 scriptSig 腳本的長度。
    • scriptSig 是解鎖的腳本,要能證明 TxID 5678 是真的由 Bob 發出,這個腳本要能解鎖 TxID 1234 output #0 中,Alice 為 Bob 預先設下的鎖。P2PKH 中的 scriptSig 腳本只有兩個資料要填入,分別是 <signature> 以及 <pubkey>。後者就是 Bob 的公鑰,Bob 可以直接填入;前者是 Bob 的數位簽章,則複雜得多。
    • nSequence 原本有用途,但現在並不使用了。
  • 當 miner 收到 Bob 發出的 TxID 5678 的時候,會先去自己的硬碟上找到 TxID 1234 的 output #0 出來,並且把其中的 scriptPubkey 以及 TxID 5678 input 中的 scriptSig 都拿出來。這兩個腳本是要相互搭配執行的,就好似一個大的程式被拆分為前後兩塊,要拼起來才能一起執行,若拼起來執行後,若執行結果為「真」,那表示 Bob 的確可以花這筆 output,而花這筆 output 的人也真的被驗證過是 Bob。
  • 關於 input 和 output 真實的資料結構,可以參考由 Krzysztof Okupski 撰寫的 Bitcoin Developer Reference 的 3.2 章。

scriptSig & scriptPubkey

  • 每一個 miner 驗證 scriptPubkey 以及 scriptSig 的步驟如後圖所示,若在驗證的過程中,任何一個 miner 發現任何的異狀或是不符協定的情況,這個 Tx 5678 會被丟棄。所以這裡有一個提醒,若你是收款人的話,建議要等 Tx 已經上鏈之後,才交付商品或是服務。(而這點也是比特幣中非常令人詬病的一個點,但稍後都有各種新的設計來彌補這個缺陷。)我們會先把 scriptSig 和 scriptPubkey 如上圖並列放好,然後先處理 scriptSig(要注意 scriptSig 是在 Tx 5678 裡面用來解鎖的),miner 會先準備好 stack (先進後出)然後依順序處理 scriptSig 的內容。(1) 看到 scriptSig 的 <signature> 資料,先把他丟進 stack 的最底層。(2) 看到 scriptSig 的 <pubkey> 資料,把他丟進 stack 中,疊在 <signature> 上面。(3) 開始處理 TxID 1234 的 scriptPubkey,第一個遇到的是 OP_DUP,這個指定操作會把 stack 裡面,最上層的資料複製一次,並再加入 stack 當中。由於這個步驟要複製的資料來源 <pubkey> 是來自於 TxID 5678(藍色的),但是複製的指令操作 OP_DUP 是來自於 TxID 1234(綠色),所以我把這個複製產生的資料標示成雙色。(4) 再來處理 OP_HASH160,這個指令操作會將 stack 最上層的資料拿出,輸入到一個名為 HASH160 的 hash function 並將輸出的 hash 值放回 stack。所以剛剛雙色的 <pubkey> 會被拿走,放回去的是一個雙色的 <pubkeyH> hash 值。(5) 繼續處理 TxID 1234 scriptPubkey 的下一個值 <pubkeyHash>,放置到 stack 中。(6) 下一個指令是 OP_EQUALVERIFY,它會將 stack 中最上面兩個資料拿出來(不放回)做比對,若相等則繼續,若不相等,則解鎖失敗。(7) 最後一個指令為 OP_CHECHSIG,也是吃 stack 中最上面的兩個參數,進行一個 SIGHASH_ALL 的數學檢查,這個檢查我們稍後再談。同樣的,出來的檢查結果是正確的,stack 最後的結果就為真 (True);若否,則解鎖失敗。以上任何解鎖失敗的狀況,都會導致 miner 丟棄這一個 Tx,因為付款人 Bob 不能證明他能提供 response script (scriptSig) 來解鎖當初 Alice 為他設下的 challenge script (scriptPubkey)。
  • 我們來分析一下為什麼上面的程序可以保證是 Bob 本人發出 TxID 5678。有一件是要先說,由於 Alice 會先送出 TxID 1234 的(綠色)內容上鏈,所以綠色的資料在區塊鏈網路上都是公開的。在第 6 步中,Alice 在 TxID 1234 output #0 中設定的綠色 <pubkeyHash> 和來自 TxID 5678 由 Bob 提供的藍綠色 <pubkeyH> 將會互相比較。我們說過,當初 Alice 在製作 TxID 1234 的時候,只需要取得 Bob 的帳號位址 (address) 即可,不需要 Bob 參與。根據比特幣位址的設計,Bob 的 address 是可以根據公式回推 Bob 的 pubkeyHash。所以當初只要 Bob 給 Alice 的 address 是無誤的,Alice 就知道這個綠色的 pubkeyHash 欄位應該要填什麼值。而藍綠色的 pubkeyH 是由 Bob 提供的藍色 pubkey (利用綠色 OP_HASH160 的 hash function)計算出來的;我們前面有說過,hash function 是不可逆的,所以即使 Alice 已經在 TxID 1234 公布 pubkeyHash 的情況之下,全世界其他的人也很難反推 pubkey 的值;但此時 Bob 卻能在 TxID 5678 提供一個 pubkey 使此 pubkey 經過 OP_HASH160 後的 hash 值 pubkeyH 恰好等於 pubkeyHash 。所以我們有了初步了理由相信,Bob 是擁有屬於這個 address 正確 pubkey 的人。但是這樣還不夠,因為一旦 Bob 的 TxID 5678 上鏈之後,全世界的人都會知道 Bob 的 pubkey。所以步驟六只有在 Bob 第一次收付款會比較安全(這就是為什麼很多比特幣網站會希望你常常換 address 的原因),但是第二次之後,pubkeyHash 就充其量只能作為雙方確認 pubkey 是無誤的手段了。
  • 下一個重點是步驟七,既然已經確認 pubkey 的值是雙方都同意的之後,接下來是要確認 Tx 5678 是由 Bob 本人發出,並且裡面的內容(使用那些 output、轉發的金額、David 的帳號等)是無誤的了。我們知道 Bob 的錢包裡面儲存了一把(千萬不能給別人知道的)私鑰是與剛剛確認的 pubkey 是成對的,這把私鑰等一下要拿來製作藍色 scriptSig 裡面的 <signature>。首先,Bob 先把 TxID 5678 的內容全部備妥,但是將 input #0 裡面的 scriptSigLen 以及 scriptSig 留空。由於這個 scriptSig 前頭的 hash 和 n 表明了他是要使用 TxID 1234 的 output #0,所以這裡 miner 去找出這個 output #0 的 scriptPubkey。在對 scriptPubkey 做了一些運算後,直接把運算的結果填入 TxID 5678 input #0 的 scriptSig 和 scriptSigLen 中。然後 Bob 將這個搭載著 Alice challenge script 的 Tx 5678,再加上 Bob 的私鑰拿去做數位簽章 (digital signature),這個簽章就是 <signature>。所以接下來 Bob 會重新再備妥一次 TxID 5678 的內容,但是這一次 input #0 裡面的 scriptSig 以及 scriptSigLen 就不用留空了,而是在 scriptSig 填上剛剛產生的 <singature> 以及 Bob 的 <pubkey>;scriptSigLen 則填上 scriptSig 資料的長度。自此,Bob 終於可以把 TxID 5678 送出去給 miner 做驗證了。
  • 我們多談一點數位簽章的技術,前面所說的「搭載著 Alice challenge script 的 Tx 5678」這一份資料其實就是 Bob 想要保護的內容,裡面包含了各種 input 和 output 的資訊(甚至還有要給 David 的 output),而這些資料對 Bob 來說是不能被竄改的。在 Bob 同時又要證明自己的身分下,數位簽章是很有用的。數位簽章演算法的內涵有兩個(Wiki 上有一個漂亮的示意圖,自己連去看一下)。第一個是簽章:給定一份資料與一組公私鑰,可以利用私鑰針對這份資料的 hash 值加密產生一個數位簽章 (signature)。這個簽章會是獨一無二的,並且跟 Bob 的私鑰和文件有關連。第二個是簽章驗證:在給定一份附有數位簽章的資料與相對應的公鑰的情況下,驗證者要自行根據所收到的資料來計算資料的 hash 值,另外驗證者會利用公鑰來解密送來的數位簽章來取得當初未加密的 hash,若以上兩個 hash 值是相等的,那麼我們必定可以知道:(1) 公私鑰的確是一對的。而我們說過私鑰是絕對不可以給別人的,所以數位簽章若是正確的,我們必然知道這個附上數位簽章的人,必定擁有相對應的私鑰。(2) 資料的內容沒有竄改過。因為若是資料的內容竄改過,資料的 hash 一定和解密的 hash 不同。
  • 回到步驟七,在 Bob 的 pubkey 值被確定的情況下,若這個 <signature> 可以保證發出 TxID 5678 的人一定擁有相對應的私鑰(還附帶 TxID 5678 的內容不會被竄改),那麼我們可以肯定這個 TxID 5678 一定是 Bob 發出的沒問題。當 miner 從 Tx pool 中隨機抽出 TxID 5678 的時候,上面談到的所有檢查機制都會在 miner 的機器上檢查一次,所以這就是為什麼我們還要付 miner Tx fee 的理由吧。若是一切 Tx 無誤,再來就要看 miner 夠不夠幸運能把 Tx 們打包成 block 送上區塊鏈了。

8. Bitcoin Header and Payload

  • 這一章要談論真正 Bitcoin 的 block 放了什麼資料,如果你略過了上一章也沒有關係,這章可以看看一個真實可運作的 blockchain 到底至少需要哪一些欄位。以下是六個彼特幣的 header 欄位。在 Wiki 上有 Bitcoin 的 genesis block 的 header 內容,在 wiki 文章的最後一小段,可以參照著看。或者你可以上 blockchain.info 去看他的創世區塊資料,這個連結會以 json 的資料格式展示創世區塊。我們談完 header 之後會談 payload。
    • nVersion:4 bytes 長,此區塊比特幣使用的版本別。Genesis block 是 1,目前 (2018.07) 是 2。不是 2 的 block 會被丟棄,甚至不被廣播。
    • HashPrevBlock :32 bytes 長,是前一個 block 的 hash 值,也就是我們之前範例提到的 PreHash。雜湊要輸入的值就是前一個區塊的這六個 header 欄位,所使用的雜湊韓式是兩次的 SHA256。32 bytes 常意味著這裡使用的 hash function 對多只能輸出 0~2^256-1 這個範圍的值,如果不是像採用 SHA256 這樣的 hash function,這個欄位的長度就得改了。創世區塊的這個值被規定是 0,因為他沒有前一個 block,也自然沒有前一個 block 的 hash。
    • HashMerkleRoot:32 bytes 長,是 Bitcoin header 中,連結其 payload 的一個數值。我們稍後會談他是怎麼算出來的。
    • nTime:4 bytes 長,是這個區塊的時機戳記。俗稱的 Unix Time Stamp,從 1970 年 1 月 1 日 0 時 0 分 0 秒起至現在的總秒數。這個時間是 miner 給的,所以不一定很精確,另外這個時間不是挖到 nonce 的時間,是 miner 開始挖礦前就先給的時間。這是一個不精確又很粗(到秒)的時間戳記。
    • nBits:4 bytes 長,這個 nBits 值會被拿來計算 target。產出的 target 會是一個介於 0~2^256-1 的值。
    • nNonce:4 bytes 長,就是 miner 在運算的 nonce 值。
  • 以下是 python 的 code 範例,它使用 genesis block 的這六個資料算出 hash 值,你可以拿去跟網路上的值做比較。值得注意的是,在計算的過程中並不是使用十進位,而是使用十六進位,並且是採用 little endian 的表示法。舉例來說,genesis block 的 nonce 值為 2083236893,必須要轉成 16 進位 7c2bac1d,然後再轉成 little endian 的表示式 1dac2b7c 才能和其他五項資料一起過兩次的 sha256 函式,而最終要再轉回成 big endian 才是我們在區塊鏈網站上看到的區塊 hash 值。
  • 在區塊 header 的後面還記載了兩個 payload 資料,分別是 #vtx,也就是有多少 Tx 被包在這個區塊裡面。而其後就會接著這麼多個的 Tx。每一個 Tx 的資料可以參考 Wiki 上的 Tx 圖片。基本上有點複雜,但是每一個 Tx 應該包含了版本別、數個 inputs、數個 outputs、一個 nLockTime。每一個 input 和 output 的內部結構,前一章有解釋過了。我們要額外說明的是 nLockTime,它指明了這個 Tx 最早能上鏈的時間,這個時間可以用 Unix Time Stamp 或是區塊編號表示都可以,所以它限定了這個 Tx 要某時間之後才能被登記上鏈,有點像是支票有兌現時間限制。
  • 我們來談眾多個 Tx 被包含在一個區塊裡時,他們怎麼跟 HashMerkleRoot 扯上關係。由於每一個 block header 只有六個欄位,所以資料量並不會太大,但是 block payload 卻要大得多,它包含了每一個 Tx(當然也包含每一個 input 和 output),平均而言比特幣的一個 block 約莫是 1M byte 左右。有的時候,基於某些原因,我們不一定要完整地儲存整個 block,而只要存 block header 就好,所以在這裡有一個 HashMerkleRoot 用來表示相對應的 Tx 們。
  • 以下我們借用 wiki 的例子,這是一個樹的結構,稱為 Merkle Tree。這棵樹的四個 leaf nodes 分別表示有四筆資料要儲存,以比特幣來說就是四個 Tx。當然在真實的環境下,一個 block 可能擁有一千多個 Tx,所以這棵樹就會大得多,需要一千多個 leaf nodes。每一個 Tx 都會計算出一個 hash 值,之後便會以 binary tree 的形式,倆倆合併後再算一次 hash 值,直到 root 為止。這樣一棵樹上的資料就可以簡單的用 root 所算出的 hash 值代替了。這個值即叫做 HashMerkleRoot,它會成為 block header 的一部分。這樣的 hash 值表示了整棵樹的資料,只要有任何一個資料改變,這個 HashMerkleRoot 也會變動,所以我們可以拿這個 HashMerkleRoot 當作唯一辨識這棵樹上所有資料的一個方法。

9. Bitcoin Infrastructure

  • Bitcoin Wallet and Key Generation
  • Bitcoin Explorer
  • Bitcoin Developer APIs
  • Bitcoin Core
  • Mainent, Testnet and Regtest Mode
  • Bitcoin Improvement Proposals (BIP)
  • 比特幣交易所

10. Blockchain Security Problem

  • 51%
  • Sybil

11. New Design

  • Simplified Payment Verification
  • Lightning Network

To be continued...

Blockchain Insights

這一個章節才是我撰寫這整份文件的原因,希望你有看到最後。這個小章節提供一些技術節衍生出來的 insights,這個章節的內容會隨著前面的章節慢慢增加。主要是提供一些技術和管理上的建議和看法,這些建議和看法是來自於對於技術細節的了解,若是你不了解某些細節可能會不知道為什麼會有這些 insights。但是有一點很重要要先講,區塊鏈的技術時時在修改和進步,很多觀點會隨著技術細節的變革而有不一樣的解釋和看法,所以不要在討論的過程中堅持區塊鏈能做甚麼事或不能做甚麼事,或是認為某些商品只是假區塊鏈,很容易被打臉的。

  1. 區塊鏈的資料是不能修改的嗎?
    • Yes or no. 這關乎幾個問題的答案,例如區塊鏈上有多少參與者?若是區塊鏈只有一個 miner 或是少數的 miners,那麼已經上鏈的資料是可以透過妥協來修改的,只是會比較勞民傷財。在區塊鏈的歷史裡面,為了解決 The DAO 的攻擊事件,最後以太坊的參與者決定要修改區塊鏈上的資料。若你自己的公司內部有一個私有區塊鏈,若 miners 很少,公司是可以協調大家修改區塊鏈的內容。另外一個是時間問題:你上鏈的時候是不是有 fork?前面的章節提到你上鏈的時候,可能是在分支後面,而分支是有可能下鏈的,所以在這個短暫的時間點,你以為已經上鏈的資料其實有可能會下鏈,但是值得說明的是既然你曾經上過鏈,表示你的 Tx 是沒有問題的,所以這個 Tx 下鏈回到 Tx pool 之後,遲早還會再上鏈的。但若如果不考慮這些特殊的情況,區塊鏈的資料是不能修改的。
  2. 區塊鏈不就是一個比較炫的分散式資料庫,如果我沒有去中心化的要求,我用一般的 DB 就好啦?
    • Yes or no. 舉一個振災捐款的名單維護例子來說,我們可以把捐款清單當作 data 存在區塊鏈上,好處是人人看得到(有些網站可以看到區塊鏈上的資料,例如比特幣的所有資料可以在 https://www.blockchain.com/ 查閱),而且抗竄改。但是這件事情由一個普通的 Relational DB 再加上一個網頁介面也可以完成,而且建置成本(買個 Amazon 或 Google 的虛擬機器服務)可能更低、'效能更好。常常有人提到兩者其中的差別是「trust」,因為區塊鏈不是由某一個人控制的。我們以小農的生產履歷上區塊鏈這件事情來說,農藥的噴灑、栽種流程、農夫身份這些東西都可以上區塊鏈並公開的查詢。你可以想想這些 block 是不能修改的沒錯,但是資料來源是正確的嗎?是誰在負責把這些資料(如同 Tx)發送至區塊鏈?這過程不會有造假?這的問題就跟 MLB 的帽子上面都有雷射標籤一樣,你也難保標籤工廠不會流出多的標籤給仿冒品使用。反過來說,難道一般的 DB 會比較不公正嗎?也不見得。有很多法規和稽核的工具在幫我們管理以及確認資料庫運作無誤。我認為區塊鏈作為分散式資料庫(要注意區塊鏈還用作其他用途)是一種選擇,但你不能主打這個選擇是因為安全、不能竄改的緣故。去中心化或許是個選擇的原因,但是看一下這題的題目,這個答案跟題目不合。
  3. 我想開一家 FinTech 公司。
    • 先想想你要提供 financial service,然後想想你的 innovation 解決了什麼一般人去銀行或金融機構辦不到的事情。
  4. 台灣可以發行數位貨幣嗎?
    • 我記得有一個故事是小美(?)在家做了很好吃的蛋糕分送給家人朋友,然後公司的同事居然提出要花錢買。鄰居也來家門口訂蛋糕,漸漸越做越大,直到有一天國稅局來查稅。其實這一切都關乎規模的問題。舉一個很簡單的銀行清算問題,銀行間在彼此清算跨行轉帳的資料時,他們是真的拿鈔票出來交換嗎?或是買賣股票的時候,你有去集保所一張張算你的股票給買你的股票的人嗎?其實在現在數位化的時代,我們並沒有做這些事情,因為清算的錢或是買賣的股票其實都變成資料庫裡面的一筆數位的紀錄而已,而這筆紀錄記錄著你的擁有權(資料庫說你有股票幾張就是幾張,資料庫說你沒有就是沒有)。所以以數位化的 token 形式來代表資產來進行交易,早就不是新鮮事,只是在最終這個數字可以去銀行領實體的錢出來(你也可去集保所領股票當壁紙)。如果你錢不領出來,平常也都用信用卡,這個實體的鈔票其實對你沒有甚麼意義,我們其實也就都很相信帳上的數字了。
    • 如果我把這樣的紀錄資料從集中式的資料庫置換成區塊鏈,其實就紀錄的功能面上來說,是沒有差別的。上面這些清算、股票這些規模的例子,若公家單位有想要用區塊鏈實做,我相信有志之士會願意嘗試實做看看。但是當這個規模大到有一天政府說以後大家不要使用實體鈔票了,所有的新台幣就按照目前帳上的數字為準,實體鈔票請大家找個銀行存進去。我相信民眾會馬上暴動,並且把新台幣都換成美金(而且還不一定是換成美金現鈔,可能也是找個在瑞士的美金帳戶存著)。但是想一想這是不是一點都不理性呢?我很負責任的說,在未來的幾百年之內(也太遠?免得打臉),只會有數位貨幣而不會有實體紙鈔存在。這種不理性來自於變動得太快,所以我們還是從塑膠貨幣開始吧。
    • 台灣可以從發行小規模的 1:1 無條件兌換新台幣的數位貨幣開始(只要有人願意拿新台幣去換,那張新台幣就銷毀,並產生相對應額度的數位貨幣)。
  5. 以數位化的方式證明「誠意」(成本)可不可以不要用 PoW,這樣很燒電費?
    • 可以。你直接把匯錢的收據寄給對方就好了。(這個答案並不是開玩笑的,下面的答案才是開玩笑的。)
    • 突發奇想:可以成立一個「代算局」集中處理一些需要運算的科學問題,然後使用者可以下載問題來解,若解出可以得到一個數位證明。可以利用這個證明來表示你的誠意,起碼比算 hash 值有意義一些。(若是我們把運算力都拿去做比較有意義的事情,我們早就找到外星人了!要不天網的人工智慧早就 train 出來了!)

This page was last updated on 15 July 2018, at 22:45 (UTC+8).