區塊鏈與加密貨幣簡介
作者作為對區塊鏈和加密貨幣的零基礎小白, 嘗試透過本文來整理區塊鏈和加密貨幣的初步知識和理解。 有謬誤之處還請指正。 在此也推薦一個全面介紹比特幣的部落格連結(英文):
https://
github。com/bitcoinbook/
bitcoinbook
一、區塊鏈
1.1 區塊鏈
區塊本身是記錄任何資訊的載體。 則區塊鏈的意義在於, 透過一串有序區塊記錄的資訊歷史。 為了保證歷史資訊的記錄“不被篡改”, 需要使修改歷史的成本變得極高而不可行。
以比特幣所依賴的區塊鏈為例, 設每個區塊均有一個編號
, 也稱為區塊高度(Height)。 第
塊記錄的資訊為
,
考慮一個雜湊函式
, 首先該函式輸出固定長度的結果, 且哪怕輸入相似資料, 輸出也大為不同。 則每一個區塊放入這個雜湊函式後得到的結果
都是唯一確定的。
為了將區塊連成鏈, 要求
中包含兩部分資訊,一部分稱為
; 餘下一部分為
即前一個區塊列舉問題的雜湊值。 一旦修改前面任何區塊的資訊, 或者調換區塊的位置都會使得
發生變化, 所以保證了歷史資訊的一致性, 但此時的區塊鏈的一致性仍然依賴區塊鏈維護機構的信用, 中心化的區塊鏈並沒有突破傳統的共識機制。
1.2 分散式區塊鏈與共識機制
The essence of blockchain: “consensus system” involving multiple parties and a “strong security mechanism under an open architecture” (Cai 2018)。
為了進一步消除對區塊鏈維護機構的依賴, 需要所有參與節點的
去中心化(decentralization)
。 以比特幣區塊鏈網路為例, 其建立在一個
非同步p2p網路之上(Asynchronous Peer-to-Peer Network)
, 要求每一個參與節點均保有相同的區塊鏈, 即
分散式區塊鏈(Distributed Blockchain)
。
由於各節點可能存在廣播失效或惡意廣播的行為, 如何對當前區塊鏈記錄達成共識便成為了一個挑戰, 該問題便是廣為所知的
拜占庭將軍問題
。 該類問題的解決方法稱為Byzantine Fault Tolerance System(BFT)。 比特幣的BFT設計如下:
考慮在區塊鏈的
中再加入一個特殊變數
。 則考察如下運算: 礦工節點透過列舉變數
, 使得
, 其中difficulty為事先決定的常數。 只有成功解算的區塊才可以被礦工節點廣播並新增到區塊鏈上。 同時要求所有節點(交易節點、礦工節點)均執行如下協議: 當出現新加入的區塊不一致時, 總是以更長的鏈為準; 如果長度一致(極小機率), 則保留分叉以至出現更長的鏈。
顯然列舉
需要花費一定算力(稱為
雜湊率, hash rate
)。 任何修改
的行為都會導致計算出的
不再為列舉問題的解, 如果修改更往期的區塊, 會使得被修改的塊及其之後的塊的所有
值都不再可用, 除非重新計算所有失效的
。
因此, 如果一個礦工節點在惡意修改區塊鏈記錄的基礎上解算新塊並廣播, 則所有人會收到兩個版本的新塊。 只要該節點不掌握51%以上的算力, 那麼這個惡意更改最終會成為
孤塊(orphaned blocks)
。 而且被惡意修改的塊越靠前, 惡意節點所需修改的後續
更多, 成本更高。 所以在進行比特幣交易時, 一般需要在交易寫入區塊鏈後多等幾個塊再確認交易。
上述機制保證了一旦區塊被新增到區塊鏈, 則鏈上資訊極難更改。比特幣正是透過利用
的計算所消耗的算力來保證共識達成。 因此
便是每個區塊所凝結的“勞動”, 又稱為
proof-of-work
。
1.3 比特幣的節點、客戶端
以比特幣為例, 交易節點可透過
Bitcoin Core
(社群官方) 等軟體參與比特幣網路。 有兩種交易節點(與礦工節點不同), 一種稱為全節點(full node) 另一種為輕量客戶端(light client)。 全節點將儲存全網所有歷史交易資料的節點(只儲存最新版本,保留過往所有版本的為更臃腫的archive node), 因此全節點可完整執行區塊鏈中所有規定的驗證計算, 具有可自我驗證性。 但由於全節點資料過大(目前上百個G),所以一般交易所等機構會更多采用全節點(Bitcoin core就是全節點)。 而對於普通交易者,可選擇輕量客戶端(比如Electrum), 但輕量客戶端必須透過連線到有全節點的伺服器才能正常執行交易操作。 (詳見
https://
bisontrails。co/blockcha
in-client-types/
)。
一般交易客戶端都可以生成一個個錢包(wallet)來儲存一些
公鑰私鑰對(public-private key pairs)
, 以提交交易所需的資訊。 此外, 客戶端還會計算出持有比特幣的餘額(後面會看到比特幣區塊鏈本身不記錄餘額)。
交易節點透過交易客戶端釋出比特幣的交易資訊、收聽礦工節點廣播的區塊; 而礦工節點將收聽到的資訊打包成區塊並解算相應
, 然後將解算好的區塊廣播並新增到之前收聽的區塊鏈上。
二、加密貨幣
由於分散式區塊鏈網路具有可信的資訊記錄, 因此可以在區塊鏈中記錄各種交易對應的等價交換價值; 而這種信用需要礦工節點的算力以維持, 必然要在新增新塊的同時給予成功解算出新塊的礦工以一定激勵價值。 正是這兩點原因共同催生了分散式區塊鏈網路的內生的貨幣。 比特幣就是最好的例子。
由於算力隨著礦工節點數目的增加(減少)而變強(變弱), 為了維持穩定的解出速度/出塊速度, 需要相應提高(降低) difficulty。 在比特幣的例子中, difficulty一般是一個以多個0開頭固定長度的16進位制數。 difficulty越高, 開頭的0數目越多。 調整週期為兩個星期。(這種設計也存在問題, 擁有大量算力的礦池可以突然減少算力使得難度調低, 在接下來的2個星期低難度的環境下再突然恢復高算力以獲取更大相對優勢)。
三、資訊傳遞的匿名性與零知識證明
在具體進入比特幣區塊鏈的交易方式之前, 本節先討論如何在隱匿身份情況下驗證身份。
考慮參與人
向
傳送資訊內容
, 則除
之外的人如何證明的確由
發出資訊, 且資訊內容是
呢? 設所有參與人
都有公鑰
和私鑰
。 所有人都知道所有人的公鑰, 但所有人的私鑰均對外人保密。
則透過數字簽名演算法
,
透過計算
便得到
對資訊內容
的唯一簽名, 即任何對
的修改均會得到不一樣的簽名, 不同發出人的簽名也不同。
因此
在向
傳送資訊
的同時還需傳送相應簽名
。 而透過驗證函式
取值是否為真, 便可知道是否是
發出的資訊,且資訊內容為
。
這種在不知道
時證明真實性的方法稱為
零知識證明(zero-knowledge proof)
。
四、交易記錄方式
比特幣的賬簿系統並非記錄所有人的餘額(Balance), 否則賬簿仍然會記錄每個獨立賬戶的所有交易。 相反,一筆比特幣交易資料本質上是一個invoice賬單。 在付款人(sender)支付時, 透過回溯付款人之前交易中收到的invoice來確認當前這筆交易的資金來源。 這種回溯性也保證了至少在邏輯上每筆交易不會被
雙花(Double Spending)
, 但現實中由於區塊鏈的特點仍有可能出現雙花。
據此, 每一筆比特幣的
交易(Transaction, TX)
一般包含如下內容:
4.1 交易(TX)所包含的內容
TX Output
:
包括: 本次交易所需付給各個收款人的
金額(value)和包含賬單地址
(
Invoice Address
, 本質是hashed receivers‘ public keys)的指令碼。
收款人在後續交易使用這筆invoice之前, 這筆TX Output都被標記為一個
Unspent Transaction Output(UTXO)
。 每筆TX被新增到區塊鏈後所有全節點均會標記產生的的UTXO, 進而生成一個 UTXO set, 記錄相應的金額與賬單地址指令碼。
但由於無法透過賬單地址反推收款人的公鑰, 因此僅有收款人的儲存了相應公鑰的錢包客戶端在收聽到區塊鏈中的這個UTXO後才會知道這筆UTXO是支付給自己的, 進而將這個UTXO加入餘額。
可以將UTXO比喻為一個匿名的漂流瓶, 僅有收款人能識別並證明所有權。 因此賬單地址指令碼又稱redeem condition/locking script。
TX Input
:
包括: 本次交易所需引用(refer to)之前sender作為收款人的交易中, sender擁有使用權的UTXO。 然後需要sender以指令碼形式1提供相應公鑰、簽名以證明兩點: 1。 sender確實擁有UTXO的所有權;2。 sender的確為本次input發起人。
可以將這兩點驗證比喻為解鎖匿名漂流瓶, 因此也稱這個指令碼為unlocking script。
在交易確認後, 這筆output會被記為spent。 則一旦TX input這兩步驗證成功且在區塊鏈上成功確認, 則被redeemed的UTXO變為spent transaction output(STXO), 便從所有全節點的UTXO set中除名, 則sender無法再發出同樣的交易資訊, 也就保證了UTXO的一次性使用。 而且一旦UTXO變為STXO, 也就暴露了所有者(也即sender)的公鑰。
按照這個思路, 下面簡述比特幣的兩種具體交易指令碼型別:
4.1 P2PKH(Pay-to-Public-Key-Hash)舉例
以如下交易例: 記第1號 為
, 其中Bob向Clement支付50 Satoshi。
付款人(Bob)需要解鎖自己的UTXO: Bob透過引用之前自己為收款人的UTXO以作為
的資金來源 (refer to some UTXO of
where Bob is receiver as the input ingredient for
)。 假設在之前存在一筆交易
, 其中index為0的output中, Alice曾向Bob支付過60 Satoshi, 且該Output未被花費(unspent), 則
的 output #0 可被引用。 這個資訊包含如下要素:
。
注意, 真實的
還需包含相應的指令碼命令, 這個
就是
的locking script/redeem condition;
下面說明Bob在發起支付時提交的 #FormatImgID_56# 所需包含的要素
:
第一, Bob 需要在交易
中引用
中index為0的UTXO:
。 其中,
: transaction identifier。
因此所有全節點客戶端可以透過交易的雜湊值回溯到
中, 並找到金額與locking condition:
。
第二, 為了驗證 Bob 有權使用該筆output (
, #0), 需要提供 Bob 自己的public key:
因此各個客戶端只需計算
是否等於
中
便可驗證 Bob 是否有使用權。
第三, 為了驗證
確實由 Bob 本人發起, 需要查驗Bob在input中提交的signature:
, 其中
: Elliptic Curve Digital Signature Algorithm。
則透過驗證函式
即可驗證發起人是否為 Bob。
記
。 注意, 真實的
還需包含相應的指令碼命令。 綜上, Bob 透過提交如下
便可發起支付:
然後說明Bob在發起支付時提交的 #FormatImgID_76# 所需包含的要素
:
與
中 Alice向Bob支付的60 Satoshi 時相同, 此時Bob向Clement支付50 Satoshi
中一般還包括找零:
由於被引用UTXO可能出現大於支付金額的情況, 所有剩餘均會支付給礦工作為
交易費(Transaction Fee)
。 因此一般output中還需包含以sender自身為收款人的找零
找零(Change)
, 本例中10 Satoshi 將分為找零與交易費。
可以發現, 一筆UTXO就是一個鎖定的待使用的比特幣, 所有UTXO就是比特幣的存量, 比特幣交易的本質上就是這種right to unlock/redeem的流轉.
4.2 P2SH(Pay-to-Script-Hash) 原理
根據 Gavin Andresen 的說法, “(P2SH) Is to move the responsibility for supplying the conditions to redeem a transaction from the sender of the funds to the redeemer”。 在P2PKH下, 一筆交易的output中需要sender提供包含redeemer/receiver invoice address的locking script。 但當redeemer對接收地址的安全性有特殊需求時, 會將redeem condition/locking script設計得比較複雜。此時sender無需對這種複雜的安全設計負責,而應當交由redeemer自己來負責處理。sender僅需知曉這種複雜設計透過加密後產生的P2SH address即可。 具體實現如下:
設redeemer所設計的
Full Redeem Script
為
。 一般情況下,
即包括了一組公鑰。 因此sender以P2SH方式付款給redeemer時僅需提供包含
的
指令碼作為redeem condition。
則redeemer使用這筆UTXO時, 需要提供
如下: 首先, 需要提供完整的
。 透過計算
的
函式值, 與被引用交易的
中P2SH invoice address進行比對。 而在原來的P2PKH中, 需要提交redeemer單個public key生成的invoice address進行比對, 這個區別正是P2
PK
H與P2
S
H名字中的差別。
之後與P2PKH一樣, 需要提交簽名, 簽名透過redeemer private keys 與 redeemed output information加工而成。 此時允許多個簽名
。 則此時仍記
。
這種 redeem condition 稱為
-of-
multi-signature transaction。
除了上述兩種指令碼型別之外還有一種bc1, 這裡不再詳述。
4.3 具體交易操作
可透過具有Hierarchical deterministic wallet(HD wallet) 功能的的客戶端, 透過12個英文單詞作為seed生成一個包含一定數目的(一般為100)公鑰私鑰對的錢包。 透過一個公鑰, 可以確定地生成一個
收款地址
(
invoice address
, P2PKH以1開頭, P2SH以3開頭, bech32以bc1開頭)。
對於每一筆收款應當使用唯一的invoice(也意味著為一的公鑰私鑰對以備未來redeem這筆收入)。 考慮一個批發商作為收款方的情況, 如果Alice已經付款後, Bob拿著同樣的已經公開的invoice來謊稱這筆付款是Bob而非Alice支付的, 由於比特幣網路的匿名性, 無法實名找到付款人, 因此會引起糾紛。 同樣, 在進行支付時需要要求收款人提供唯一的invoice。
除此之外, 在收付款時必須確保收款人保有這筆invoice對應的私鑰。 例如當用一個新生成的地址收款時, 如果不慎丟失了對應的公鑰(比如錢包回滾到之前未生成新地址時的鑰匙備份), 則一旦有付款匯入這個地址, 任何人將永遠無法再解鎖這筆UTXO, 因此使用HD wallet可透過儲存seed來生成所需鑰匙對來避免這個問題。