区块链
依序将介绍时间扭曲攻击(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个区块标记赞成。
全部0条评论
快来发表一下你的评论吧 !