基于交易的账本模式(transaction-based ledger)
区块链是去中心化的账本,比特币采用的是基于交易的账本模式(transaction-based ledger),只记录了转账交易和铸币交易,并没有直接记录每个账户上有多少钱。如果想知道某个比特币账户上有多少钱,要通过交易记录来推算。
(1)UTXO(Unspent Transaction Output)
比特币中的全结点要维护一个叫UTXO的数据结构,即还没有被花出去的交易的输出。一个交易可能有多个输出,被花掉的就不在UTXO里了。
如下图中A转账给B和C的交易,B的已经花出去了,也就不在UTXO里了,而C的还没花出去,就在UTXO里
UTXO集合中的每个元素要给出产生这个输出的交易的哈希值,以及它在这个交易中是第几个输出。用这两个信息就可以定位到一个确定的交易中确定的输出。
使用UTXO可以用来快速检测双花攻击,想知道新发布的交易是不是合法的,要查一下全结点存在内存中的UTXO。要花掉的币只有在这个UTXO这个集合里才是合法的,否则要么是不存在的,要么是已经花过了的。
随着交易的发布,每个交易会消耗掉一些输出,同时也会产生一些新的输出。例如上图例子中B将5个BTC给D,这消耗掉了前面A把5个BTC给B的输出,但产生了新的B把5个BTC给D的输出,这个输出会保存在UTXO里。
(2)交易中:total inputs=total outputs
每个交易可以有若干输入和若干输出,所有输入的金额之和要等于所有输出的金额之和。
(3)第二个激励机制:交易费(transaction fee)
有些交易的总输入可能略微大于总输出,如可能总输入是1个BTC,总输出是0.99个BTC,这之中的差额就作为记账费给了获得记账权的那个结点。
目前来讲矿工挖矿,主要的目的还是为了第一个激励机制——得到出块奖励。因为出块奖励是要逐渐减少的,每隔21万个区块就要减半,比特币系统的平均出块时间是10分钟,大约每隔4年出块奖励就会减半。到很多年之后,出块奖励变得很小,这时候交易费就成为主要动机了。
transaction-based ledger
除了比特币系统这样基于交易的账本模式(transaction-based ledger),还有一些系统是基于账户的模式(account-based ledger),比如后面要学的以太坊。在这种模式中系统要显式的记录每个账户中有多少个币。
比特币系统的这种模式,隐私保护性比较好,但会带来一些代价,如转账交易要说明币的来源(币是从之前的哪个交易的哪个输出中来的)以防止双花攻击。
一些区块的例子
区块中的nonce是4字节即32位整数,也就只232
种取值。因为比特币近些年太火爆了,挖矿的人数很多,所以挖矿的难度被调整的很高,单纯靠调整nonce是很可能得不到符合难度要求的解的(搜索空间不够大)。所以区块的块头中哪些域可以改?
- 版本号(4):不能改
- 前一个区块块头哈希值(32):不能改
- Merkle Tree的根哈希值(32):通过修改Merkle Tree中铸币交易的CoinBase域来调整其根哈希值
- 区块产生的时间(4):有一定的调整余地,比特币系统并不要求非常精确的时间,这个域可以在一定范围内调整
- 挖矿目标阈值编码后的版本(4):只能按照协议中的要求定期进行调整,不能随便改
- nonce(4):可以改
可以看到,因为铸币交易是没有交易来源的,所以可以在其CoinBase域
里随便写入内容,铸币交易的变化会使该交易的哈希发生变化,变化沿着Merkle Tree一路向上传递,最终使整棵Merkle Tree的根哈希值发生变化,间接地调整块头的哈希值。
所以可以把这个字段当做一个extra nonce
,块头的nonce字段不够用,就再拿着这个域的一部分字节一起调整,就增大了搜索空间。
实际挖矿时,一般也是为此设计了两层循环,外层循环调整铸币交易的CoinBase
域的extra nonce
,算出Merkle Tree的根哈希值;内层循环调整块头的nonce,计算整个块头的哈希值。
在这个转账交易中,左边是交易的两个输入(虽然旁边写的是Output,这是表示它们花掉的是之前哪个交易的Output);右边是交易的两个输出(从绿色Unspent字样可以看到还没有花出去,所以会保存在UTXO里)
可以看到比特币系统中交易的输入和输出都是用脚本来指定的,验证交易输入输出的过程就是把输入脚本和输出脚本配对执行(不是把同一个交易的输入输出脚本配对执行,而是把这个交易的输入脚本和提供币的来源的那个交易的输出脚本配对执行)。只要配对后都能成功执行,交易验证就是通过的。
挖矿过程的概率分析
挖矿的过程就是不断尝试nonce去求解puzzle,每次尝试可以看做一个伯努利试验(Bernoulli trial:a random experiment with binary outcome)
当进行了大量的伯努利试验,这些伯努利试验就构成了伯努利过程(Bernoulli process)。伯努利过程的一个性质是无记忆性(memoryless),即做大量的试验,前面的试验结果对后面没有影响
当伯努利分布(也即二项分布)的n很大而p很小时(试验次数很多,每次试验成功概率很小),可以近似为泊松分布。这里挖矿就是一个n很大p很小的伯努利过程,所以可以近似为泊松过程(Poisson process)
progress free与算力优势——挖矿公平性的保证:
出块时间是服从指数分布(exponential distribution) 的,整个系统的出块时间按照比特币协议被调整在10分钟左右,而具体到某个矿工的出块时间,取决于其算力占整个系统中矿工的算力的百分比
比特币安全性的分析
- 转走别人的BTC
假设一个有恶意的结点M获得了记账权,它想把结点A的钱转走,但因为没法伪造A的签名(没有A的私钥),写个任何不正确的签名上去,都会导致诚实的结点不会接受这个候选区块,而是继续沿着上一个区块扩展。因为这个区块是不合法的,所以多长都不是最长合法链,这样的攻击是无效的。
- 分叉攻击
M把BTC转给A,然后就紧接着挖矿挖到了一个区块,在这里填写了M把BTC转给自己的交易,以希望沿着这个区块成为最长合法链,这样就能将转给A的挤掉,从而将花出去的BTC回滚。这也是双花攻击的一种
如果M->A这个交易所在区块后面又跟上一些区块,这个攻击的难度就会大大增加。因为它最好的方式仍然是在M->A的前一个区块位置插入,但是想让它成为最长合法链却非常难,因为它已经不是最长合法链,诚实的结点只会去扩展最长合法链。
如果大部分结点掌握在诚实结点手里,这样攻击的难度非常大,有恶意的结点要连续获得好多次记账权才可能改变最长合法链。所以一种最简单的防范方法就是多等几个区块,也叫多等几个确认(confirmation)
比特币协议中缺省的是要等6个confirmation(大约一小时),才认为one confirmation
区块中的交易是不可篡改的。从前面的分析可以看出,比特币交易在区块链账本上的不可篡改性(irrevocable) 只是一种概率上的保证。
- zero confirmation
zero confirmation是指交易刚发布出去,还没有写入区块链中的时候,就认为交易已经不可篡改了。
- 故意不发布某些合法交易
比特币协议没有规定获得记账权的结点必须发布哪些交易,而且没有写进这个区块也可以写进下一个区块,总有诚实的结点愿意发布这些交易。而且比特币系统在正常工作时候也会出现某些交易被滞后发布的情况,可能就是一段时间内的交易太多了,毕竟一个区块不能超过1M。
- selfish mining
正常情况下结点挖到一个区块就立即发布,这是为了得到出块奖励和收取交易费。selfish mining就是挖到的区块都留着,这样的动机是,比如在前面的分叉攻击中,一直等到6个confirmation过了,再一口气把算好的很长的分叉发布出去,替换掉最长合法链。
实际上这样做的难度还是很大,因为这个恶意结点的算力要超过那些诚实的算力才可能在一定时间后比它长。