依序將介紹時間扭曲攻擊(time warp attack)、以太坊的uncle挖礦、比特幣的交易可鍛性(malleability problem)及投票中的隔離見證(segregated witness)。
時間扭曲攻擊(time warp attack)
這個攻擊的起因來自于
1.區塊的timestamp是由礦工填入,雖然timestamp的值要遵守一些限制(大小要超過過去十一個區塊按照timestamp大小排列的中間值、小于其他連線中節點的當地時間的平均值加兩小時等等的規則),但還是是一個礦工可以操縱的變數。
2. 鏈的難度在調整時有一個空檔,例如:[blk 0, blk 1, … , blk 2015] [blk 2016, blk 2017, … , blk 4031] …
所以礦工可以調整blk 2015的timestamp,例如多加兩個小時,這樣大家在調整難度時會發現blk2015減去blk 0的時間變多了,所以調低難度。
但要發動這個攻擊前提需要攻擊者的算力要夠(因為要能掌握特定區塊,控制timestamp),這在如今相當成熟的比特幣非常難實行(這也算是51%算力攻擊的一種) ,但有一些altcoin都曾經被成功攻擊過(Myriad、Geist Geld等)。
Uncle Mining
這個問題來自于以太坊挖礦的機制—? GHOST。
因為以太坊縮短了區塊產生的時間,導致會有更多被挖到的區塊因為網路延遲或是運氣問題而被丟棄(稱為stale block)。所以GHOST的設計讓這些stale block也能被采納,獲得(比較少的)獎賞。這也是為什么這些區塊會被稱為uncle block的原因,見下圖:
藍色的2060 uncle block被2061收入
Uncle Mining顧名思義,是專門挖uncle block來獲利的一種旁門走道。
礦工把挖到的區塊(假設應該要是第2001個區塊)藏起來,等到主鏈的第2001個區塊被挖出才公布出去,成為uncle block。
收入uncle block的區塊也會獲得Reward,且uncle block越快被收入雙方獲得的Reward越多。
假設某礦工(或礦池)的算力為25%,正常情況他的獲利是Reward*0.25:
正常情況,每四個區塊有一個是他挖出的。
現在他不挖主鏈(表示挖主鏈的算力只剩75%),改挖uncle block(假設uncle block馬上被下一個高度的區塊且獲利為Reward*7/8):
因為挖主鏈算力剩75%,所以變成每三個區塊一個循環。
如此他的獲利變成Reward*(7/8)*1/3 = Reward*0.29。
假設礦工的算力是X%,正常情況獲利是Reward*X;采用uncle mining獲利是Reward*7/8*X/(1-X),X只要大于12.5獲利即大于正常情況(但7/8是理想情況,如果uncle block越慢被收入獲利就會越少,門檻也就越高;再加上以太坊限制每個區塊最多只可收入兩個uncle block)。
因為以太坊的挖礦機制不是贏者全拿的零和游戲(如比特幣),所以如果有礦工采用uncle mining,全體礦工的獲利其實是會提升的(從Reward*1/4變成Reward* 1/3,但也會造成總體發行的以太幣數量增加,間接導致價格下跌)。
但如果有其他礦工也采用uncle mining,則會出現競爭情況讓彼此獲利下降。
交易可鍛性(Transaction Malleability)
這個漏洞來自于比特幣的簽名機制:
解鎖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.影響礦工成本:
礦工的成本之一是他必須將所有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個區塊標記贊成。
評論
查看更多