比特币系统的全节点要维护一个叫UTXO(unspent transaction output)(还没有被花出去的交易的输出)的数据结构。区块链上有很多交易,有些交易的输出可能已经被花掉,有些还没有被花掉。所有没有被花掉的输出的集合就叫做UTXO。
一个交易可能有多个输出。假如A给B5个比特币,B花掉了。A也给了C3个比特币,C没有花掉。这时5个比特币就不算UTXO,而3个比特币算。UTXO集合当中的每个元素要给出产生输出的交易的哈希值,以及它在这个交易里是第几个输出。这两个信息就可以定位到UTXO中的输出。
要UTXO集合有什么作用? 为了检测double spending。即检测新发布的交易是否合法。因此全节点要在内存中维护UTXO这样一个数据结构,以便快速检测double spending。
每个交易要消耗掉一部分输出,也会产生新的输出。还看上面的例子,B花掉的5个比特币虽然不在UTXO里面,但如果他转账给D,而D没有花掉,那么这5个比特币又要保存在UTXO里面。如果D始终不花,那么这个信息要永久保存在UTXO里面。有可能是不想花,也有可能是把密钥丢了。
每个交易可以有多个输入,也可以有多个输出,所有输入金额之和要等于输出金额之和。即total inputs=total outputs。因此一个交易可能来自多个地址,可能有多个签名。
有些交易total inputs略微大于total outputs。 假如输入1比特币,输出0.99比特币,另外0.01比特币作为交易费给获得记账权发布区块的节点。
区块奖励也不能完全作为挖矿的奖励,发布区块的节点为什么一定要把你的交易打包在区块呢?他们还要验证你的交易的合法性,如果交易较多占用的带宽会比较大,网络传播速度也会更慢。所以只有区块奖励是不够的。
因此比特币系统设计了第二个激励机制:交易费(transaction fee)。也就是你把我的交易打包在区块里,我给你一些小费。交易费一般很小,也有一些简单的交易没有交易费。
一个区块的例子 第一行表明:该区块包含了686个交易 第二行:总输出XXX个比特币 第四行:总交易费(686个交易的交易费之和) 最下面一行:区块奖励(矿工挖矿的主要动力) 第五行:区块的序号 第六行:区块的时间戳 第九行:挖矿的难度(每隔2016个区块要调整挖矿的难度,保持出块时间在10分钟左右) 倒数第二行:挖矿时尝试的随机数
右边:第一行:该区块块头的哈希值 第二行:前一个区块块头的哈希值 (注意:计算哈希值只算块头) 两个哈希值的共同点:前面都有一串0。是因为,设置的目标预值,表示成16进制,就是前面一长串的0。所以凡是符合难度要求的区块,块头的哈希值算出来都是要有一长串的0。 第四行:merkle root 是该区块中包含的那些交易构成的merkle tree的根哈希值。 最后一行:是32位的无符号整数。nonce只有2的32次方个可能的取值。按照比特币现在的挖矿情况来说,很可能把2的32次方个取值都验了一遍也找不到合适的。那怎么办呢?block header 的数据结构里还有哪些域是可以调整的呢?
第一行:比特币协议的版本号(无法更改的) 第二行:前一个区块的块头的哈希值(无法更改) 第三行:merkle tree的根哈希值(可以更改) 第四行:区块产生的时间(可以调整)比特币系统不要求特别精确的时间,可以在一定范围内调整。 第五行:目标预值(编码后的版本)(只能按协议中的要求定期调整) 第六行:随机数
挖矿时只改随机数不够,还可以更改根哈希值。
铸币交易没有输入,它有一个coinbase,可以写入任何的内容。也可以把digital commitment里的commit的哈希值写入里面。也可以把第一节讲到的预测股市的内容写入里面,coinbase的内容是没有人会检查的,甚至可以写你的心情。
对应的是最后一个block header里的根哈希值对应的merkle tree,左下角的交易是coinbase,把它的域改了之后,其上的哈希值就发生了变化,然后沿着merkle tree的结构往上传递。最后导致block header里的根哈希值发生变化(merkle root是block header的一部分)。块头里4个字节的nonce不够用,还有其他字节可以用,比如coinbase域的前八个字节当做extra nonce来用,这样子搜索空间就增大到了2的96次方。
所以真正挖矿的时候只有两层循环,外层循环调整coinbase域的extra nonce。算出block header里的根哈希值之后,内层循环再调整header里的nonce。
该交易有两个输入和两个输出。 左上角:这里的output其实是输入,指的是之前交易的output。 右上角:这里的output都是unspent,都没有被花掉,会保存在UTXO里面。 右边表格第一行:输入的总金额。 依次往下:输出总金额、两者之间的差值。 两表格下面:可以看出输入和输出都是用脚本的形式来指定的。
比特币系统中验证交易的合法性,就是把input scripts和output script配对后执行来完成的。注意:不是把图中的input scripts 和output scripts配对,因为这两个脚本是一个交易中的脚本。不是把同一个交易里的输入脚本和输出脚本配对,而是把这里的输入脚本和前面提供币来源的交易的输出脚本配对。如果输入输出脚本拼接在一起,能顺利执行不出现错误,那么该交易就是合法的。
提到挖矿是一个无记忆性的过程Memoryless,符合Bernulli trail。
挖矿的工程如果不是Prograss free,那么算力强的矿工会取得不成比例的优势。
这块的理解,以抛硬币为例也是一个bernulli trail,memory less的过程,我想要一个正面的结果,和我前面几次抛硬币获得反面的结果都没有关系。在比特币挖矿过程中,我计算一个nonce,再计算下一个nonce时是否能满足条件都是不可知的。
这个Bernulli process过程,和穷举密钥攻击的过程相识,但不一样。穷举密钥,在密钥空间中存在这样的密钥,计算一个结果就排除一个结果。但是挖矿过程,就好像抛一个2256个面的骰子,只有2target个面是符合条件的。
zero confirmation(交易才发布,没有放入区块链)
电商运行一个全节点或委托一个全节点监听区块链上的交易,他收到转账交易之后要验证该交易的合法性(有合法的签名,以前没有被花过),甚至不用等到该交易写入区块链里。这种操作听起来风险很大,交易刚发布出去,都没往区块链里写呢。其实,零确认在实际当中,用的还是比较普遍的。为什么呢?
这其中有两个原因:①比特币协议缺省的设置是节点接收最先听到的那个交易。所以在零确认的位置,M→A的节点收到后,再发M→M’的交易,有比较大的概率诚实的节点是不会接受的。 ②很多购物网站,从支付成功,到发货,是有一定的时间间隔的,即有一定的处理时间。
假设某个有恶意的节点获得记账权,它还能做什么坏事?能不能故意不把某些合法的交易写入区块链里?即发布的区块故意不包含某些交易。这是可以的。
比特币协议并没有规定获得记账权的节点一定要把那些交易发布到区块里。但出现这种情况问题也不大,因为这些合法的交易一定会被写入下一个区块里,总有诚实的节点愿意发布这些交易。
其实,区块链在正常工作下,也会出现合法的交易没有被包含进去的情况,可能就是这段时间交易的数目太多了。比特币协议中规定,每个区块的大小是有限制的,最多不能超过一兆字节。所以如果交易的数目太多了,那么有些交易可能就只能等到下一个区块再发布。
会不会出现这种情况?M→M’的交易所在的区块所在的链条虽然短,但是先偷偷的生成比上面更多的区块,然后等上面的链条公布后再公布,就能够胜过上面的几个区块了?这种方法叫作selfish mining。
正常情况下挖到一个区块马上就发布,原因是你不发布别人可能就发布了,那样就拿不到区块奖励了。而selfish mining是先藏着不急着发布,这是分岔工具的一种手段。
但这样成功的概率并不大,因为有恶意的节点本来算力占比就不高,还要生成更多的区块,就非常困难。
以上是selfish mining的其中一个目的,它还有另一个目的。假如A挖了两个区块都没有发布,而在B挖到一个区块公布后立马公布,这样B挖的区块就作废了。这样的好处就是减少竞争,因为A在挖第二个区块时,别人还在挖第一个区块(前提是A算力足够强)。
但这样也有不好的地方,假如A挖出一个区块,A以为他能赶在别人面前再挖一个区块,结果这时有人挖出了第一个区块,那这样的话A就要在别人发布之后立马发布,去争取区块奖励。
比特币工作在应用层(application layer:Bitcoin block chain),它的底层是一个网络层(network layer:P2P overlay network)。
比特币的P2P网络是非常简单的,所有节点都是对等的。不像有的P2P网络有所谓的超级节点、纸节点。
要加入P2P网络首先得知道至少有一个种子节点,然后你要跟种子节点联系,它会告诉你它所知道的网络中的其他节点,节点之间是通过TCP通信的,
比如说A→B和A→C,这两个如果同时广播在网络上,那么每个节点根据在网络中的位置的不同,收到两个交易的先后顺序不同。
比如一个人先收到第一个交易,就写入到集合里,再收到第二个交易的时候就不会写入集合,因为跟上一个交易有冲突,就认定是非法的。假设这两个交易花的是同一个币,那么写入集合的交易就会被删掉。
还需要注意的一点:我们讲的比特币网络的传播属于best effort 。一个交易发布到比特币网络上,不一定所以的节点都能收到,而且不同的节点收到这个交易的顺序也不一定是一样的。网络传播存在延迟,而且这个延迟有的时候可能会很长,有的节点也不一定按照比特币协议的要求进行转发。
可能有的该转发的不转发,导致某些合法的交易收不到,也有的节点可能转发一些不该转发发的消息,比如说有些不合法的交易也被转发了。这就是我们面临的一个实际问题。