相信你們都看過上面的這張圖,這張圖來自中本聰的比特幣白皮書,用來介紹比特幣的交易。在這張圖的上面,中本聰寫下了這樣幾句話:We define an electronic coin as a chain of digital signatures. Each owner transfers the coin to the next by digitally signing a hash of the previous transaction and the public key of the next owner and adding these to the end of the coin. A payee can verify the signatures to verify the chain of ownership. 爲了保持原汁原味,我就不翻譯了,相信你們這點閱讀理解的功力還保留得住。git
但問題是,他說的每一句話我都看懂了,可我仍是看不明白這張圖到底在說什麼。也許比特幣的交易在中本聰的腦海裏就是這個樣子,因此我一直懷疑他是否是潛伏在地球的外星人,腦回路跟地球人不同,包括那個UTXO的概念,實在有悖於人類正常的思惟。因此,這篇文章的任務就是儘可能將這張圖轉換成地球人容易理解的形式。算法
如上圖所示,這是一個比特幣交易的簡化結構(忽略了一些參數,簡化了id)。安全
在介紹交易結構以前,先簡單說一下比特幣的UTXO概念,方便零基礎的讀者閱讀。在比特幣系統中,沒有餘額的概念,只有UTXO(Unspent Transaction Output),即未花費的交易輸出。翻譯成大白話就是我花的錢都是別人給個人錢,並且花的時候必須花完。好比我有一個2 BTC(比特幣) 的UTXO,我想給你1 BTC,我就必須把這個 2BTC的UTXO 花掉,而後生成兩個UTXO,一個 1BTC 的UTXO給你,一個UTXO(小於1 BTC,差值爲交易費)給我本身。想看餘額怎麼辦,就查一下這個地址裏有多少UTXO,把裏面的BTC加起來就是餘額。數據結構
好了,如今咱們來看交易的結構。比特幣的交易主要由兩部分構成:輸入(input)和輸出(output)。electron
Input是要說明我打算花的這UTXO是從哪兒來的。學習
具體的參數包括:spa
txid : 引用的UTXO所在的那筆交易ID.net
vout : 引用的UTXO所在交易的輸出中的序號(從0開始)翻譯
scriptSig : 解鎖腳本,包含付款人對本次交易的簽名(<sig>)和付款人公鑰(<PubK(A)>)。blog
Output是要說明我打算生成幾個UTXO,分別給誰,每一個UTXO裏面有多少BTC。
具體的參數包括:
value : 比特幣數量
n : UTXO序號(從0開始)
scriptPubkey : 鎖定腳本,包含命令(OP_DUP等)和收款人的公鑰哈希(<PubKHash(B)>)。
瞭解了交易的結構以後,如今咱們經過一個交易的示例,來看看簽名和驗證是如何進行的。
咱們來看上面這張圖,A經過交易001轉給B 1BTC,B經過交易002轉給C 1BTC,簡化起見,忽略交易費的問題。
咱們來重點分析交易002。
B 打算轉給C 1BTC,他先找到A轉給他的那個UTXO,即交易001的out中n=0的那個UTXO,把相關參數寫入交易002中的in。而後在out中輸入比特幣數量,UTXO序號,鎖定腳本。鎖定腳本中的命令都是固定的,C的公鑰哈希(<PubKHash(C)>)可經過C的錢包地址解碼得到。
這樣,交易002相關的數據都已經準備好了,就差最後的簽名了。這個簽名就相似於開支票時的簽名,證實我贊成把這筆錢給你。可是具體的實現要比籤個字複雜不少,緣由就在於互聯網中一切都是能夠複製的,如何證實你擁有這筆錢,如何證實這個交易是你建立的且沒有被修改過,這背後都有嚴密的數學理論和算法來保證。
咱們先來看下簽名的過程:
簽名的輸入:
1. 待簽名的交易數據(輸入和輸出),即<tx002>。
2. 引用的UTXO相關信息(交易ID、序號、鎖定腳本)
3. B的私鑰,即<PriK(B)>。
4. 簽名類型
簽名的輸出:
1. scriptSig ,即解鎖腳本,包含簽名(<sig>)和 B的公鑰(<PubK(B)>)。
至此,一個完整的交易即建立成功,能夠發送給其它節點驗證了。
這裏多說一句,細心的讀者可能會發現,輸入2的信息其實輸入1已經包含了,或者能夠根據輸入1查的到,爲何還要單獨列出呢。目前我也沒有找到明確的可信服的解釋,不知道是否還有其它深意。期待大神們的指教。
交易發送至其它節點後,其它節點會對其進行驗證,只有驗證經過的交易纔會被繼續傳播。交易驗證的項目不少,這裏只講關於簽名的驗證。
簽名驗證的目的有兩個:
1. 證實交易所引用的UTXO的確屬於付款人。
具體到本次交易,就是證實交易001的序號爲0的UTXO的確是發給B的。
2. 證實交易的全部數據的確是付款人提供的,且未被修改過。
具體到本次交易,就是證實B的確建立了交易002,且交易內的數據未被修改過。
下面咱們來看驗證是如何進行的,其實很簡單,就是用解鎖腳本解鎖對應UTXO的鎖定腳本,對應上圖就是橙色線所鏈接的兩個腳本:
<sig><PubK(B)> OP_DUP OP_HASH160 <PubKHash(B)> OP_EQUALVERIFY OP_CHECKSIG
比特幣腳本的執行基於堆棧模型,遵循從左到右,後入先出的原則。關於堆棧的介紹,文末的參考文章中有比較清晰的圖示,不清楚的讀者能夠參考。本文爲方便闡釋各步驟的意義,採用文字方式描述。各步操做以下:
1. <sig> <sig> 入棧
2. <PubK(B)> <PubK(B)>入棧
3. OP_DUP 複製位於棧頂的<PubK(B)> ,將副本置於棧頂。
4. OP_HASH160 對位於棧頂的<PubK(B)>副本進行HASH160,<PubK(B)>轉變爲<PubKHash(B)>。
5. <PubKHash(B)> <PubKHash(B)>入棧
6. OP_EQUALVERIFY 比較位於棧頂的兩個元素是否相同,若相同則移除這兩個元素,繼續執行。若不一樣,則中斷執行,返回失敗。
7. OP_CHECKSIG 檢查簽名(注意棧內現有的元素爲<sig><PubK(B)>),根據結果返回成功或失敗。
下面咱們來分析下每一步的意義,步驟1~6的意義其實很明顯,用B提供的公鑰(<PubK(B)>)進行雙哈希(HASH160),而後與鎖定腳本中的公鑰哈希(<PubKHash(B)>)做對比,相同則返回成功。咱們知道公鑰哈希(<PubKHash(B)>)就是A在建立交易時根據B的地址生成的,它就是B的公鑰通過雙哈希運算得來的,因此這一步只要提供了B的公鑰,驗證確定是成功的。因此,步驟1~6 就至關於A把1 BTC發給了B的郵箱,B拿把鑰匙打開了郵箱,證實了B確實擁有這1 BTC。也就是證實了上文中提到的驗證目的1:證實交易001的序號爲0的UTXO的確是發給B的。
比較麻煩的是第7步,不少文章說到這裏都只是泛泛而談,或是一筆帶過,我學習到這裏的時候真是如墮霧中,四顧茫然啊。如今回過頭再去看一些文章中的表述是很是不許確的。這一步簡單的CHECKSIG操做,實際上蘊含了複雜的密碼學和數學原理,證實的其實不是全部權的問題,而是證實了B的確建立了交易002,且交易內的數據未被修改過,也就是上文中提到的驗證目的2。
那麼,CHECKSIG的驗證是如何實現的呢?這裏運用了橢圓曲線數字簽名算法(ECDSA:The Elliptic Curve Digital Signature Algorithm ),一種利用橢圓曲線進行數字簽名和驗證的算法。下面將簡單介紹這種算法是如何用來進行比特幣交易的簽名和驗證的。涉及到的數學知識不做深刻介紹,感興趣的讀者可參照文末的文章連接深刻了解。
首先,咱們看一下橢圓曲線的形狀,如上圖紅線。咱們能夠把這個曲線上的點定義一種加法:鏈接兩點的直線與橢圓曲線的交點相對於X軸的對稱點,即爲兩點之和。如圖中的 A+B=C。
A+A時咱們取A點的切線與曲線交點相對X軸的對稱點。有了A+A,咱們就能很方便的定義出乘法。有了乘法,咱們選擇一個基點G,可以很方便地計算出 K=kG,然而,給定K和G,卻很難計算k(至今沒有有效的算法),這就是橢圓曲線離散對數問題。而橢圓曲線密碼學(ECC Elliptic Curves Cryptography)的安全性正是創建於橢圓曲線離散對數問題的困難性之上。基於此,在ECC中咱們定義k爲私鑰,K爲公鑰。
而後,咱們再來看一下基於有限域Fp的橢圓曲線域E(Fp):
y^2 ≡ x^3 + ax + b (mod p)
當:a, b ∈ Fp 且知足 4a^3+27b^2 ≠ 0 (mod p). , x, y ∈ Fp時,這條曲線上的點的集合P=(x,y)就構成了一個基於有限域Fp的橢圓曲線域E(Fp)。
完整描述一個橢圓曲線域實際須要6個參數:
p:限定有限域邊界的質數
a,b:橢圓曲線的參數
G:基點
n:G的階,nG=O∞
h:餘因數,控制點的密度。
能夠將橢圓曲線域簡單理解爲只取橢圓曲線上的那些整數點,可是因爲多了一步模運算,所以展現出的形狀與以前的平滑曲線是有差異的(以下圖),可是以前定義的加法乘法規則是不變的。
好了,有了這些概念,咱們如今來看一下簽名和驗證的過程:
簽名者的密鑰對:(d, Q);(d爲私鑰,Q爲公鑰)
待簽名的信息:M;
簽名:Signature(M) = ( r, s)
簽名過程:
一、根據ECC算法隨機生成一個密鑰對(k, R), R=(xR, yR)
二、令 r = xR mod n,若是r = 0,則返回步驟1
三、計算 H = Hash(M)
四、按照數據類型轉換規則,將H轉化爲一個big endian的整數e
五、s = k^-1 (e + rd) mod n,若s = 0, 則返回步驟1
六、輸出的S =(r,s)即爲簽名。
驗證過程:
一、 計算 H = Hash(M)
二、按照數據類型轉換規則,將H轉化爲一個big endian的整數e
三、計算 u1 = es^-1 mod n, u2 = rs^-1 mod n
四、計算 R = (xR, yR) = u1G + u2Q, 若是R = 零點,則驗證該簽名無效
五、令 v = xR mod n
六、若 v == r,則簽名有效,若 v ≠ r, 則簽名無效。
從數學上能夠證實,若 v == r,便可證實信息M的確爲持有密鑰對(d, Q)的簽名者所簽署,且未被修改過。
上述過程當中的Q、S、R均爲橢圓曲線域中的點。
咱們把上述示例中的輸入參數與比特幣交易002中的參數作個對比:
如上圖所示,能夠看出這兩個過程當中的參數是一一對應的,咱們把交易002中相關的參數按照示例作相應的操做,就不難理解簽名和驗證的具體過程了。因爲數學原理上的保證,若簽名驗證成功,便可證實B的確建立了交易002,且交易內的數據未被修改過,也就是上文中提到的驗證目的2。
好了,以上就是筆者目前關於比特幣交易中籤名和驗證的所有理解和思考,水平有限,難免存在謬誤和紕漏,歡迎各位大神批評指正!
ECDSA 部分的兩張動圖引自參考文章,對原做者Nick Sullivan表示感謝,同時對全部參考文章的做者表示感謝!
[1] 深刻比特幣原理(四)——鎖定腳本(locking script)與解鎖腳本(unlocking script)
https://bbs.huaweicloud.com/blogs/d4c97558190611e89fc57ca23e93a89f
[2] 比特幣交易的數據結構與簽名類型
https://blog.csdn.net/awewong/article/details/78310017
[3] 橢圓曲線密碼學的簡單介紹
https://zhuanlan.zhihu.com/p/26029199
[4] 比特幣系統採用的公鑰密碼學方案和ECDSA簽名算法介紹——第一部分:原理
http://www.8btc.com/btc_ecc_dsa_a