假设央行不用区块链发行数字货币,央行可以对每个货币用私钥签名,然后交易的时候,交易双方可以用公钥去验证这个签名的正确性,正确即是央行发行的货币,可以交易,否则就是假货币。
那么问题来了
货币的真伪性可以得到保证,但是这个数量你能控制吗?假如你现在有一个央行发行的货币,你虽然不能伪造但是可以复制,从而发生一钱多用的情况(花两次攻击,英文名:double spending attack)
改进一下
央行除了要对货币签名,还要给每个货币打上一个编号,然后维护一个数据库,这个编号的货币现在在谁手上。交易的时候,双方先用公钥验证真伪,然后准备收钱的一方访问一下央行数据库,这个货币现在是不是在你手上,在的话就可以继续交易,否则这个货币你是已经用过了,不能再用了。这样就解决了double spending attack问题。有问题吗?没有问题,只是这是中心化的方案,央行数据库压力很大。比特币系统解决的就是这种问题。
去中心化货币系统要解决的两个问题数字货币的发行,谁有权发?什么时候发?发多少?
怎么验证交易的有效性?怎么防止double spending attack问题?
比如说有一个用户,他获得了发行货币的权力,我们管他叫铸币权,他发行了10个比特币给了A,把这交易信息写入区块链中。然后A把比特币给了B和C,一人5个,这个交易需要A的签名,证明是A同意的,同时这个交易还要说明花掉的比特币是从哪来的,也就是下图的第二个交易要说明比特币从哪来的,这里是从铸币交易输出来的。比特币交易系统中都包含了输入和输出两部分,输入部分要说明币的来源,输出部分要给出收款人的公钥的hash,比如A给B转钱,要说明B的公钥的hash是什么。然后B再把钱转给C,2个和D,3个,同样要签名,以及说明币的来源。这时候C想转给E7个币,币的来源就有两个了。见下图
注意:上图有两种hash指针,一种就是前面一篇文章提到的连接区块的hash指针,把区块串起来构成一个链表,还有第二种指针,是为了说明币的来源。
为什么要说明币的来源?
A和B交易要哪些信息?
需要有A的签名和B的地址,比特币系统中收款方的地址是通过公钥推算出来的,比如B的地址是B的公钥取hash再进行一些转过生成的,这个地址相当于银行账号,A要给B转钱,需要知道B的银行账号。那么A怎么才能知道B的地址呢?比特币系统没有提供查询某个人的地址的方法,这得需要第三方的形式,比如说电商平台支持比特币支付,那么商家要把账户地址告诉电商平台,然后消费者就知道要给谁转账了。
另外需要知道A的地址,A要B的地址是为了知道给谁转账,B(其实是所有节点)要A的地址是为了①谁给B转的账②验证是否是A的签名(A的私钥签名,其他节点知道A的地址后就可以公钥验证)。
问题来了?怎么才能知道A的公钥呢?
A的公钥是交易自己给出的,交易有输入和输出,输入不仅要说明币的来源还要说明A的公钥。
那么问题又来了,交易自己决定输入公钥,那不是有冒名顶替的风险吗?
前面提到输入要给出币的来源和付款人的hash,而输出要给出收款人的公钥的hash,那么上面A到B的交易的币是哪里来的呢?是前面铸币交易来的,来的同时要带上付款人的公钥的hash也就是前面铸币交易的收款人的公钥的hash。也就是说第二个交易的输入的公钥要和第一个交易的输出的公钥要一致。
在比特币系统当中,验证过程是通过脚本实现的,每个交易的输入是一段脚本,包括公钥,也是在脚本指定的,每个输出也是一段脚本,验证是否合法,就是把当前输入的脚本和币的来源的那个交易的输出的脚本拼在一起,如果能正常执行,那么就是合法的。
注意:上图对区块进行了简化,每个区块只包含了一个交易,实际上,一个区块可以有很多交易,所有交易构成了一个merkle tree。每个区块分为块头和块身两部分。
块头包含这个区块宏观的一些信息,比如说用的是比特币版本的哪个协议(version),还有区块链当中指向前一个区块的hash指针(hash of previous block header),还有整个merkle tree 的根hash值(Merkle root hash),还有两个域和挖矿相关的,一个是挖矿的难度目标阈值(target),一个是随机数nonce
关于target
block header里面存的是关于目标阈值的编码(nBits)
关于hash指针
hash指针是只对block header做计算得出的,Merkle root hash 可以保证块身的交易列表是没有被修改的
关于节点和区块的不同:
节点:每一个挖矿的矿工客户端就是相当于比特币网络中的一个个节点。
区块:区块是挖矿节点挖到的某一段时间的交易打包的块,而这个块里的coinbase交易,就是比特币网络给矿工奖励的比特币,之后的网络上被广播的交易。
我们知道节点,分为全节点(full node)和轻节点(light node)。全节点保存区块链的所有信息,验证每一个交易,所以全节点也叫做fully validating node 。轻节点只保存block header 信息,一般来说轻节点不能独立验证交易的合法性,比如一个交易是不是double spending ,轻节点不知道,因为没有存以前的交易。系统中大部分节点是轻节点,全节点数目不多,前文所涉及的节点主要是全节点,因为轻节点没有参与区块链的构造和维护,只是利用了区块链的一些信息。每个节点可以发布交易,那么这个交易是广播给所有节点的,有些是合法的,有些是非法的,合法的会打包写进一个区块里。既然每个节点会对交易进行验证决定是否写入区块还是丢弃,那么就有可能不同节点记的账不一样。所以用分布式术语来说,账本的内容要取得分布式共识。
分布式共识(distributed consensus)
一个简单的例子就是分布式的hash表,比如系统中有很多台机器,共同维护一个全局的hash表,这里要取得的共识是hash表包含了哪些key-value对。分布式系统有许多不可能结论(impossibility result)。其中最著名是是FLP(三个分布式系统专家人名首字母),在一个异步(asynchronous)系统中,网络传输时延没有上限,即使只有一个成员是有问题的,也不可能取得共识。还有一个不可能结论是CAP(分布式系统的三性质:consistency,availability,partition tolerance),任何一个分布式系统,CAP三个性质只满足最多两个。分布式共识的一个著名协议是Paxos,这个协议能保证一致性。
比特币系统的共识协议要解决的问题是有些节点可能是恶意的,我们假设系统中大部分节点是好的。这种情况下的共识协议怎么设计?
既然大部分节点都是好的,那么就投票,比如说某一个节点提出一个候选区块,根据收到的交易信息,判断哪些交易是合法的,然后把这些交易按顺序打包到候选区块里。候选区块发布给所有节点,每个节点(其实是账户)收到区块后,检查一下,这里面的交易是不是合法的,如果都是合法的,投赞成票,否则投反对票。最后得票超过半数,候选区块写写入区块链中。
投票有哪些问题?
①、恶意节点不断提出有问题的区块,时间都浪费在了投票上面,区块链无法发展
②、没法强迫节点投票。如果都不投票,那么区块链就陷入了瘫痪。
③、效率上的问题。投票等多久决定于网络延迟
④、任何基于投票的方案都要先决定谁有投票权(membership),这是区块链最大的问题。如果区块链的成员有严格定义,比如说有一种区块链叫联盟链(hyperledger fabric),只有符合条件的大公司才能加入,这种方案就可行。但是比特币系统不是这样的。比特币系统中创建一个账户是很容易的,只需要在本地创建一个公私钥对,不需要任何人批准,别人甚至都不知道你产生了一个账户,只要在交易的时候别人才知道。如果比特币系统用的投票机制,那么不法分子可以整一台超级计算机,别的事都不干,只产生账户,账户数超过总数一半,就可以控制投票结果了,这种叫女巫攻击(sybil attack)
所以比特币系统不是根据账户数投票的,而是根据计算力投票的。每个节点都可以在本地组装出一个候选区块,把它认为是合法的交易放在这个区块里,然后尝试各种nonce值,即计算H(block header)<=target,nonce是个4 bytes的数。如果某个节点找到了nonce,就获得了记账权,就是往比特币去中心化的账本中写入下一个区块的权力,只有获得了记账权的节点才有权力发布下一个区块。其他节点收到区块后验证区块的合法性,比如block header里面的各个域是否正确,它里面的nBits域(时间上是target的一个编码),检查一下nBits设置的是不是符合比特币系统规定的难度要求;然后检查一下nonce是不是使得整个H(block header)小于等于target。然后验证一下block body里面的交易是不是合法的,第一要有合法签名,第二没有用过。
假设一个区块经过检查是合法的,那么有没有可能节点不接受它呢?
恶意节点不接受合法区块 假设一个获得记账权的节点发布一个合法的区块,如下图,插在了区块链的中间。这个区块是完全合法的,那么我们应不应该接受它?所以比特币系统要取得的共识到底是什么?前文提到的分布式hash表要取得的共识是hash表的内容,比特币系统中要取得的共识是去中心化账本里的内容。谁来决定写这个账本?取得记账权的节点写账本,所以比特币系统中的共识机制是根据算力(每秒钟能试多少个nonce,也成为hash rate)来投票的,hash rate越高,得到记账权和得到比特币的概率就越高。那么这种共识机制是如何避免女巫攻击的,因为不管创建多少账户,节点的hash rate 都不会改变,所以获得记账权和比特币的概率不会提高。