比特幣、以太坊的一些問題介紹(二)
這篇接著介紹比特幣的Transaction Malleability和Segregated Witness
Transaction Malleability
這個漏洞來自於比特幣的簽章機制:
解鎖script(比特幣的script介紹可看這篇)因為包含簽名本身,所以不會成為被簽名的一部分(否則就會產生無窮迴圈:簽名包含script,script本身又包含簽名….)。也因為script和簽名是分開的,才會有transaction malleability的問題。
首先介紹雜湊值與ID
交易的雜湊值可以視為該交易的ID(區塊裡Merkle tree的各個leaf node的值即是交易的雜湊值),由該筆交易資料序列化後丟進雜湊函數後產生。如果交易資料不一樣,所得到的雜湊值就會不一樣。
怎麼發生的?
以下是一筆正常交易中的解鎖script: 0x48 <signature> 0x41 <public key> (0x48及0x41指令是將後面的48 bytes、41 bytes的值推進stack裡,也就是分別把signature和public key推進去),當你把這筆簽名廣播出去,有心人士收到之後,去將解鎖script修改成 0x4D4800 <signature> 0x4D4100 <public key> (0x4D指令將長度為後面兩個byte構成的數字的bytes推進stack裡,也就是把長度共0048的bytes推進去,opcode詳細可看wiki),這和原本的script做的事情是一樣的,但修改過後的交易丟進雜湊函數後產生的ID會完全不一樣。
怎麼利用這個漏洞?
因此當你還在用你手中的ID檢查是否被收入區塊裡、交易是否完成的時候,交易可能以另外一個ID被收入了(聽起來好像沒差,畢竟你的交易最後還是完成了)。
2014年的MtGox,有心人士先向MtGox提回比特幣,同時攔截、修改並發出。如果原本的交易先被收入區塊鏈裡,那就正常的拿回原本應該要拿的錢;如果修改過的交易先被收入,則他除了拿到比特幣之外,等到MtGox系統發現交易沒有完成並補償或重發時,有心人士就額外多獲得了一筆錢。
還有很多方法可以修改script,利用malleability的漏洞(例如在script開頭推入無用資料、在script裡拼接public key等等),預防機制就是不要使用交易ID當作確認交易是否完成的條件。
Segregated Witness
Segregated Witness(以下簡稱SegWit)最早由Pieter Wuille在2015年提出,在16年加進BIP裡,被視為目前解決比特幣交易量問題的最佳解決辦法。比特幣的區塊大小限制在比特幣社群裡引起廣泛的討論有很長一段時間了,許多人覺得將區塊大小提升兩倍並不能真正解決問題,也有許多人支持用硬分叉方式擴大區塊大小先應急(但擴大到多大也沒有共識),一直沒有一個多數支持的解法。
推行方式:
SegWit不透過硬分叉的方式擴大區塊大小,而是透過改變礦工認證交易的方法來解決問題。雖然不需要用代價很高的硬分叉的方式,但仍然需要絕大多數的礦工同意(需要礦工都接受SegWit驗證交易的方式,否則會有衝突,即一邊的礦工覺得這筆交易合法,另一邊覺得不合法),因此目前正在進行投票中。
所以Segregated Witness在做什麼事?
這裡witness指的是交易的簽名(signature),而顧名思義,Segregated Witness做的就是分離簽名,把交易的簽名從script中抽出去。但簽名抽掉之後其他新的節點加入時要怎麼驗證這些交易?你可以想像成將這些簽名做成另一個Merkle Tree。

原本一個區塊的Header裡有包含一個Merkle Root,這是由這個區塊包含的所有交易的ID組成的Merkle Tree的root。
先介紹一下目前的txid: 是由[nVersion][txins][txouts][nLockTime]經過雜湊而得;
而SegWit的IDwtxid則是[nVersion][marker][flag][txins][txouts][witness][nLockTime]經過雜湊而得。witness 是該筆交易input(txins)相對應的簽名資料,而txid的簽名是包含在txins裡面。每個交易都會有 txid 和 wtxid,如果是一筆普通交易,那它的txid會和wtxid長得一樣。
現在SegWit將 txid相對應的wtxid組合成另一個相對應的Merkle Tree,但如果要把這新的root塞進Header裡,表示要改變Header結構,就必須要硬分叉。
所以SegWit將這個root存在區塊的coinbase transaction裡,coinbase transaction是礦工領獎賞的交易,且可以讓該礦工在這個交易的script填入任意資料,例如中本聰在比特幣創始區塊裡填入的:
“The Times 03/Jan/2009 Chancellor on brink of second bailout for banks”
。
而SegWit的script因為抽走了簽名,所以也做了修改。上鎖的script變成一串長度為數十bytes的資料(在沒有採用SegWit的節點眼中,看到的是一串無意義的資料),分成兩部分:1 byte長的版本號和2至40bytes長的witness program。
目前版本號是零(0x00),而在這個版本之下有兩種witness program:
1. 長度為20 bytes的pay-to-witness-public-key-hash,可以視為SegWit版的P2PKH。如果要花這個UTXO,你必須提供witness,witness 包含signature和public key,而這個public key經過雜湊(HASH160)後要等於那個20 bytes長的witness program。
2. 長度為32 bytes的pay-to-witness-script-hash,可以視為SegWit版的P2SH。如果要花這個UTXO,你必須提供witness,witness 包含一個redeem script,而script經過雜湊(SHA256)後要等於那個32bytes長的witness program(如同P2SH)。
因為witness是額外紀錄,所以一個SegWit交易的解鎖script有可能會是空的(可以玩玩這個比特幣script的模擬器測試看看),SegWit節點從其他方式收到witness再用來解鎖。
新設計帶來的好處
版本號提升了系統的彈性,未來若推出新的script規則及驗證方式,只需利用版本號來判別即可,不但能新舊兼容,還不需要用硬分叉的方式來施行。
此外,因為SegWit的P2WSH的redeem script是包含在witness裡,表示redeem script的大小不受限制,可以設計更為複雜的redeem script。
那非SegWit節點呢?
如同前面所述,沒有採用SegWit的節點看SegWit交易的script看到的會是一串沒有意義的東西,也就表示解鎖script不要求任何簽名,這在他們眼中都是”Anyone-can-spend”的交易(這一篇有介紹一些這類型的交易),所以非SegWit節點可以直接把SegWit交易的錢提走(解鎖script是空的即可),這也是為什麼SegWit投票需要絕大多數的礦工支持。
基本上非SegWit節點不會受到影響,還是可以製作和驗證非SegWit交易,但如果要提走SegWit交易的錢,最後會發現沒人承認而白做工。
SegWit將會對比特幣的交易效率產生很大影響,包含以下方面:
1.影響區塊容量:
將簽名從交易裡抽走之後減少了交易資料的大小。如果一個區塊裡都是SegWit交易的話,一般情況約是2MB大小,最差情況可塞到4MB。裡頭包含一般交易資料和winess,而且是在一般交易資料沒有超過1MB容量限制的前提下。
2.影響交易費用:
因為比特幣的交易費用是看交易資料的大小而決定,所以SegWit將signature從交易資料中拿掉,減少了交易資料的大小,直接的降低了交易費用。
3.影響礦工成本:
1. 礦工的成本之一是他必須將所有UTXO(Unspent Transaction Output)記下來存在記憶體中,如果比特幣網路UTXO越多礦工成本就愈高,而且使用者有動機來產生越多的UTXO。試想如果你有三筆UTXO分別是0.5、0.5及0.9 BTC,你要支付一筆0.75的費用,考量交易費用後你會選擇花一筆0.9 BTC的UTXO而不是0.5 BTC + 0.5 BTC共兩筆UTXO,因為input(即UTXO)越多表示交易資料越大,交易費用就越高。但這會產生更多的UTXO(一筆0.9變成0.75 + 0.15兩筆),造成礦工成本上升,所以使用者的利益和礦工的利益是相衝突的。
在SegWit計算交易費用的方式裡,假設交易A的input數量大於output數量(3 inputs, 2 outputs),交易B的input數量小於output數量(2 inputs, 3 outputs,表示交易A減少UTXO數量而交易B則產生更多的UTXO),則交易A的交易費用會小於交易B。
但在沒有SegWit的情況下,交易A資料大小會大於交易B(因為一個input的大小大於一個output的大小),交易A的交易費用就會比較高,所以在SegWit的機制下,減少UTXO對使用者和礦工都是有利的。
4.解決Transaction Malleability
因為SegWit將簽名從script中抽離,而上鎖script(witness program)和解鎖script(witness)都固定了格式,因此即便有心人士攔截了交易資料,他也無從竄改起。改了witness program任何一個位元,都會導致witness經過雜湊後得到的雜湊值和witness program不一樣。
而解決了Transaction Malleability也間接解決了閃電交易網路(Lighting Network)的一大問題,SegWit不僅加速了線上交易,也同時解決了線下交易的問題。
越來越多錢包、礦場和應用完成對接SegWit的升級,詳情可見這個列表。投票狀況可到這裡觀看。SegWit投票從2016年11月中開始持續一年,必須超過95%的礦工投票支持才會通過,也就是2016個區塊裡要有1916個區塊標記贊成。
Reference:
[1] http://www.tik.ee.ethz.ch/file/7e4a7f3f2991784786037285f4876f5c/malleability.pdf
[2] https://en.bitcoin.it/wiki/Script
[3] https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki
[4] https://bitcoincore.org/en/segwit_adoption/
[5] http://segwit.co
[7] http://www.livebitcoinnews.com/coincheck-acknowledges-support-segregated-witness/
