0.前言
本文为区块链的学习笔记,主要参考来源为北京大学肖臻老师《区块链技术与应用》公开课的比特币部分,链接https://www.bilibili.com/video/av37065233/?p=1这门公开课使我获益匪浅,非常感谢老师的讲授!
比特币(BitCoin)的概念最初由中本聪在2009年提出,包括根据中本聪的思路设计发布的开源软件以及建构其上的P2P网络。比特币是一种P2P(Point to Point)形式的数字货币,点对点的传输意味着一个去中心化的支付系统。
1.比特币的密码学原理
比特币是一种加密货币(crypto-currency),它依据特定密码学算法,通过大量的计算产生。比特币中使用了大量的密码学中的知识,例如hash函数,非对称加密体系,签名机制等。以下的文章中将简单介绍这些内容。
1.1 哈希函数(Cryptographic Hash Function)
1.1.1 性质
1.collision resistance:除非使用暴力破解(brute-force),找不到更好的方法制造一个碰撞,
因此这个性质可以应用于数字摘要。但是要说明一点,已知的哈希函数都不能通过数学方法证明其抗碰撞性。
2.hiding:已知x——>h(x),但是由h(x)不能求出x,除了用暴力算法求解x。
应用:与性质1结合,作为digital commitment or digital equivalent of a sealed envelope(将结果作为哈希公布,起到了信封的功能)
3.puzzle friendly(比特币中特有):比特币中挖矿阈值要求:h(block header)<=target,其中block header中的nonce是可变的,就是说,要想通过修改nonce求解出满足target的哈希值,除了一个一个的试nonce,找不到更好的办法来求解,这个性质可以作为挖矿的工作量证明(prove of work)。
1.1.2 SHA-256(Secure Hash Algorithm)
比特币中的哈希函数使用了SHA-256,输出结果为256bit,有关SHA-256算法的详细介绍参考以下链接https://blog.csdn.net/u011583927/article/details/80905740
1.2 签名(Signature)
1.应用了非对称加密算法(Asymmetric Encryption Algorithm),比特币用户只需要创建一对公钥(public key)私钥(private key)对,就可以加入比特币体系。公钥相当于银行账号,私钥相当于密码。
2.使用时用私钥签名,公钥验证签名的合法性。
3.需要一个好的随机源(a good source of randomness)产生公私钥对。
2.比特币的数据结构
2.1 哈希指针(Hash Pointer)
普通c指针指向的是一个地址,哈希指针除了指向一个地址外,还有一个哈希值。要说明的一点时:哈希指针用与无环的结构,环形会形成循环依赖。
2.2 区块链(Block Chain)
一个区块链的结构如下所示
它们之间有哈希指针连接,可以实现防篡改日志(tamper-evident log),只需要检测最近产生块(most recent block)的哈希指针,就可以检测出区块内容是否完整,是否被篡改。因此节点可以利用这个性质,只保存几个区块的哈希指针而不用保存所有的信息。
单个区块的结构如下:分为block header(包含区块的宏观信息)和block body(包含实际交易)两个部分
block header | block body |
---|---|
version | transaction list |
hash of previous block header | |
merkle root hash | |
target | |
nonce |
2.3 Merkle tree
2.3.1 Merkle tree简介
相比于数据结构中一般的树,Merkle tree使用了哈希指针,一个典型的Merkle tree如下
假如一笔交易发生变动,那么这笔交易的哈希指针会发生变化,该哈希指针的上层哈希指针也会变化,以此类推,从而导致根哈希发生变化,可以很方便的检测交易篡改。
在区块的结构中,block header中包含有Merkle tree的root hash。
2.3.2 Merkle tree作用
提供Merkle prove:轻节点用于确认一笔交易信息是否在区块中。
比特币系统中的节点分为全节点和轻节点,全节点包含了一个区块中交易的所有信息,轻节点只有block header。
如果要验证一个交易是否在区块中(如图黄色的transaction),可以通过Merkle prove的方法。由轻节点向全节点发出Merkle prove,全节点提供与这个交易相关的哈希指针(红色的),然后轻节点验证与transaction相关的哈希指针(绿色的),并且将两个指针拼接再次计算哈希,最终可以计算到root H(),轻节点将计算得出的哈希值与本地的哈希值作比较,相等则证明交易在区块中,不等则证明不在。
以上的证明过程是成员资格证明(prove of membership),这种证明过程的复杂度是对数级别的。若要进行非成员资格证明(prove of nonmembership),在Merkle tree未排序的情况下,是线性的;在排好序的Merkle tree中,复杂度是对数级别的。
3.比特币的共识协议
3.1 双花攻击(Double Spending Attack)
数字货币本质上是一串数字,假如仅仅依靠签名来确定数字货币,那么比特币持有者可以通过简单的复制,将这串数字复制多份,然后分别花费。这一节,只在一条链上的讨论这个攻击。
3.2 防范措施
首先举一个例子,交易如下:
1.用户A获得了铸币权(create coin)获得十个比特币这是铸币交易
2.A将十个比特币转给B五个,转给C五个
3.B将五个比特币转给C两个,给D三个
4.C将七个比特币给E七个
比特币的每笔交易都包含有输入和输出,输入是指比特币的来源,输出是指比特币转入的账户的公钥的哈希。当B想要将五个比特币给F的时候,回溯到B比特币的来源,会发现B的五个比特币是有交易记录的,已经消费了,因此这笔交易不能完成,成功阻止了double spending attack。
在交易中需要注意的问题:
1.A需要知道B的收款地址,收款地址由B给出。而B需要知道A的公钥以验证A的身份。
2.A的公钥由A给出,其他人不能伪造,因为每笔交易要说明币的来源。
3.交易不需要收款者在线,只是需要将这笔交易记录在区块中。
3.3 共识机制
3.3.1 分布式共识(Distributed Consensus)
比特币系统的各个用户维护一个共同的分布式哈希表(distributed hash table),在表中每一个键值对(key-value pair)都能找到对应。
下面介绍分布式系统中的几个结论
1.FLP impossibility result ,简单的说就是,在异步系统(asynchronous,网络传输时延无上限)中,即使只有一个系统是有问题的(存在faulty),那么这个系统也不会取得共识
2.CAP Theorem:CAP指的是分布式系统的三个性质,C——>consistency,A——>availability,P——>partition tolerance,在一个分布式系统中,如果满足任意两个性质,系统将不会满足第三个性质。
实际中的分布式系统达成共识是可能的,因为理论上不可能的结论是在一个特定的模型下实现的,而现实与理论上是有区别的,我们可以在现实中进行对应用场景进行改造,使系统达成分布式共识。
3.3.2 比特币中的共识协议(Consensus in BitCoin)
3.3.2.1 基于投票的共识协议
假设区块链系统中只有小部分节点是有恶意的,假如此时的投票权由联盟决定,这种方案是可行的。 存在的问题:女巫攻击(sybil attack),在比特币系统中,恶意节点产生大量的用户账号,当账号数量超过一半时,就可以操纵投票结果。
3.3.2.2 基于算力投票的共识协议
每个节点将合法交易在本地组装为一个区块,通过改变nonce的值,不断穷举符合条件的哈希值,然后放入Block Header,获得记账权,检查交易的合法性,最后组装成一个区块发布。
这个协议通过节点解puzzle的能力大小决定了投票的权重。
3.3.2.3 最长合法链
区块链中的链的扩展应该遵循最长合法链原则,即扩展当前位置的最长的那条链。假如两个区块同时发布,链的长度相同,则节点会接收先收到的区块,然后继续计算,直到最长合法链的再次出现,另一个区块会被放弃成为丢弃块(orphan block)
3.3.2.4 出块奖励
当节点通过不断尝试nonce计算出满足条件的hash值时,该节点获得记账权随后可以产生一定数量的新的比特币(也就是coinbase transaction),也就是出块奖励(block reward)。
出块奖励的计算
初始为50BTC,每隔210000个比特币减半。
3.3.2.5 交易费
获得记账权的节点,将一笔交易记录打包进入区块后,可以获得一笔交易费(transaction fee)。
4.比特币的实现
4.1 比特币的实例
比特币出现时,由于算力较低,如果要达到平均10分钟出一个块的要求,则要将target域变大,使puzzle难度低。矿工只需要穷举nonce就可以找到满足target的值。随着算力的增加,10分钟出一个块的要求不变,因此需要使puzzle难度变大,此时只靠改变nonce有可能找不到满足target域的解,因此需要通过改变同时Merkle Root(64bit)与nonce(32bit),形成二重循环来寻找解。具体的做法跟coinbase transaction域中的内容有关,先看下图
比特币的每笔交易包含着输入和输出,但是铸币交易(coinbase transaction)是产生比特币的源头,没有输入,而是获得记账权的节点向其中写入一段信息作为输入。矿工可以通过不断地修改coinbae transaction中的内容,从而改变这笔交易的hash,依次向上改变Merkle Root Hash。
4.2 挖矿的概率分析
每次挖矿相当于是伯努利实验(Bernoulli trial),当进行了很多的伯努利试验后,这些实验就构成了伯努利分布(Bernoulli process),具有无记忆性,就是前面的实验结果对后面的实验没有影响。
对于挖矿来说,伯努利分布可以由泊松分布(Poisson Process)来近似,出块时间可推导出符合指数分布(Exponential Distribution),指数分布也是具有无记忆性的,具体来说就是出块时间跟已经花费的计算时间没有关系,已经计算了十分钟没有出块,并不意味着在今后很短的一段时间内会出块。
假如,挖矿过程中的puzzle是有记忆的,这样对算力弱的矿工是不公平的。算力强的矿工可以计算出很多次,可以有更多的准备,相比于算力弱的矿工获得了与算力不成比例的优势。
4.3 比特币的数量
几何级数,每隔21万个区块,比特币减半,总量为
210000*50*(1+1/2+1/4+1/8+....)=21000000
4.4 分叉攻击(Forking Attack)
4.4.1 分叉攻击例一
如图所示,A转账给M,这笔交易写入区块后,A想回滚这笔交易,将比特币转给自己,并且重新打包了一个区块。假如没有后续的6个确认区块,那么这样是完全可行的。所以在交易过程中,为了避免这样的分叉攻击,可以等6个确认区块后再确认。
4.4.2 分叉攻击例二
4.4.2.1 selfish mining
如下图
A转账给M,黄色的区块为攻击者A事先计算好的,但是未发布的区块,等到M发现有6个确认区块后,完成交易,A立刻发布自己制造的区块,将比特币转给自己,使其成为最长合法链,然后按照这条链继续扩展,上面的链被丢弃,达到了目的。
这种攻击方式在现实中不可行,因为诚实的节点是大多数的,算力也被掌握在大多数节点手中,这种做法,在计算上不可行。
4.4.2.2 selfish mining的优势与风险
如下图
攻击者A由于算力足够大,在某个位置先于其他节点挖出了一个区块A1,但是它不发布这个区块,而是继续沿着A1计算A2,而此时其他节点正在计算区块其他1,这样做分散了算力,使A能够有更多的机会计算出A2。如果计算出A1后立即将A1发布,会使其他算力也向A1聚集,对A不利。
风险:A在计算A2时,区块其他1被计算出,由于A1未发布,其他链成为最长合法链,算力会聚集于这条链,更容易计算出其他2。归根到底,还是算力的竞争。
4.5 基于账本的交易模式(transaction-based ledger)
用户无法直接知道一个账号有多少比特币,只能通过一笔一笔的交易计算。比特币系统中的全节点维护了UTXO(Unspent Transaction Output)集合,也就是未消费的交易输出的集合,它的作用是检测double spending attack之类的攻击。
4.6 基于账户的模式(account-based ledger)
显式记录账户金额,方便查询
5.比特币网络的工作原理
5.1 比特币网络结构
结构 | 协议 |
---|---|
application layer | BitCoin Block Chian |
network layer | P2P Overlay Network |
比特币网络中的节点是完全对等的,不像传统的p2p中有master node
5.2 比特币网络中的节点
全节点 | 轻节点 |
---|---|
数量少 | 数量多 |
一直在线 | 需要的时候在线 |
在本地硬盘上维护完整的区块链信息 | 只保存区块块头 |
在内存中维护UTXO集合,可快速检验交易的正确性 | 只保存与自身相关的交易 |
监听比特币网络上的交易信息,验证每个交易的合法性 | 只验证与自身相关的交易的合法性 |
决定哪些交易会被打包到区块中 | |
监听其他矿工挖出的区块是否合法 | 无法验证区块是否合法 |
决定挖矿的方向 | 验证挖矿难度,检测最长链,而不是最长合法链 |
5.3 比特币网络设计原则
简单(simple),鲁棒(robust),而不是高效。
比特币网络中,交易消息的传播使用洪泛(flooding),牺牲了高效性,交易传播时不考虑下层的拓扑结构;区块的传播也使用了相同的方式,并且检测该区块是否处于最长合法链。
best effort:当一笔交易发布到比特币网络上时,不同节点收到的交易的顺序和能否收到交易时不确定的。
6.比特币的挖矿难度
6.1 挖矿难度(Difficulty)
挖矿难度的改变就是通过调整target值实现的,他们之间成反比。difficulty=difficulty_1_target/target.
调整挖矿难度是因为算力的增加,使区块的产生时间不是10分钟,而是小于10分钟。如果区块的产生时间过短,由于区块在比特币网络上传播有时延,各个节点不能统一信息,就会出现分叉,如下图。
此时恶意节点会通过集中算力,扩展自己的黑色链,发动51%攻击,此时的算力已经不需要51%了,因为其他的非恶意节点的算力被分散到了很多链中,当他们的算力没有恶意节点大时,由于恶意节点扩展了最长合法链,会使其他节点到最后也跟着恶意节点计算。
6.2 调整公式
规定,每隔2016个区块调整一次挖矿难度。也就是每隔:2016x10/(60x24)=14天,调整一次。
目标阈值调整公式
target=target*(actual time/expected time)=target*(time spent mining the last 2016 blocks/2 weeks)
挖矿难度调整公式
next_difficulty=previous_difficulty*(2 weeks/time to mine last 2016 blocks)
对这个公式的一些解释
1.实际产生2016个区块的时间大于预期时(14天),由公式得,阈值相比于之前会增大,挖矿难度降低
2.阈值变化有上下限,最高增大4倍,最低降为原来的1/4.
3.阈值调整是所有节点都应该进行的,假如有节点为降低挖矿难度增大阈值,降低挖矿难度,该节点计算出的hash是不被其他节点认可的。
7.挖矿
7.1 挖矿设备专业化
挖矿所用的设施变化:CPU——>GPU——>ASIC(Application Specific Integrated Circuit)
从通用计算到通用并行计算(深度学习再到挖矿专用
ASIC芯片专门设计用于某一种货币的挖矿,解决一个特定的mining puzzle,当新的ASIC芯片生产出之后,整个比特币市场的算力会有一个大幅度的提高,各个矿工会争相购买新的矿机进行挖矿以获取更高的利润。随后整体算力会增加,挖矿难度加大,促使下一代ASIC芯片出现。
可以通过Alternative puzzle实现ASIC resistance,使得CPU,GPU用户都可以参与,而不是使挖矿收益都被大厂的ASIC矿机垄断。
7.2 矿池出现
7.2.1 矿池(Mining Pool)
将矿工组织成矿池,用一个全节点驱动整个矿池运作。矿池一般结构如下图:
这种做法的优势在于
1.每个矿工不需要维护负责的全节点,只需进行哈希计算。
2.收入稳定,单个矿工的挖矿类似于买彩票,矿池挖矿有利于收益的稳定。
当前时间段矿池份额如下:
信息来源https://btc.com/stats/pool?pool_mode=day3
7.2.2 矿池的组织形式
集中式:所有的矿工经过pool manager的组织形成了一个类似公司的形式。
分布式:矿工来自世界各地,分别属于不同的组织或者无组织,仅仅参与挖矿。
7.2.3 矿池的收益分配
对于集中式,分配内部自行协商。
对于分布式,采取工作量证明,具体如下: 矿主要求矿工提交挖矿难度较小的一些区块,称为share也叫almost valid block。矿工将这些share提交给矿主作为工作量证明,矿主将这些share保存,等到某一时刻,有矿工提交了符合目标阈值的block,矿主获得比特币,再将比特币按各个矿工提交的share的数目进行分配。
安全性:矿工不可能偷到出块奖励。因为假如矿工将符合目标阈值的区块不交给矿主而是自己发布,矿工也拿不到记账权,因为矿工只负责计算哈希值,而块头信息中包含的是矿主的信息,即使该矿工发布了区块,最后获得收益的也是矿主。
7.2.4 矿池的安全性
矿池如果拥有51%的算力,可以发动51%攻击(51%不是绝对的,是一个概率值,每个矿池的算力也是动态变化的估计值)。
最常见的是分叉攻击,前面有介绍。
其次还有Boycott,如下图所示:
A想将自己的交易记录写入区块,但是此时某个矿池拥有51%的算力,它立即制造出两个合法的黑色区块,使自己的链成为最长合法链,使算力向这条链集中,包含A交易的区块变成了orphan block,等到A想将自己的交易写入区块时,矿池继续产生最长合法链,最终达到抵制A的目的。
8.比特币交易中使用的脚本形式
我们可以从图中看到一些有关交易的信息。下面是这笔交易的输入与输出脚本。
验证交易是否合法,需要将输入输出脚本分别验证,看脚本能否成功执行。如下图所示:
当B想要给C比特币时,需要将这笔交易的输入脚本执行,然后再将A——>B的输出脚本执行,最后根据结果判断是否是合法交易。
8.1 P2PK(Pay to Public Key)
input script:
PUSHDATA(Sig)
output script:
PUSHDATA(Pubkey) //收款人公钥
CHECKSIG
执行过程为先将签名压入栈,然后将公钥压入栈,随后公钥检验签名的正确性,栈变化如下图所示。
栈 | ——> | 栈 | ——> | 栈 |
---|---|---|---|---|
——> | pubkey | ——> | ||
sig | ——> | sig | ——> | true |
8.2 P2PKH(Pay to Public Key Hash)
input script:
PUSHDATA(Sig)
PUSHDATA(PubKey)
output script:
DUP
HASH160
PUSHDATA(PubKeyHash)
EQUALVERIFY
CHECKSIG
执行过程为签名入栈,公钥入栈。然后将入栈的公钥复制,并且计算哈希160值,放入栈中,然后将由收款方给出的PubKeyHash入栈,比较两个值是否相等,相等则返回true。
栈变化图如下:
栈 | ——> | 栈 | ——> | 栈 | ——> | 栈 | ——> | 栈 | ——> | 栈 |
---|---|---|---|---|---|---|---|---|---|---|
——> | ——> | ——> | pubkeyhash | ——> | ——> | |||||
——> | ——> | pubkey | ——> | pubkeyhash | ——> | ——> | ||||
——> | pubkey | ——> | pubkey | ——> | pubkey | ——> | pubkey | ——> | ||
sig | ——> | sig | ——> | sig | ——> | sig | ——> | sig | ——> | true |
这种方式最常见,如下图所示:
账户地址https://www.blockchain.com/zh/btc/address/1x6YnuBVeeE65dQRZztRWgUPwyBjHCA5g
8.3 P2SH(Pay to Script Hash)
input script:
…
PUSHDATA(Sig)
…
PUSHDATA(serialized redeemScript)
output script:
HASH160
PUSHDATA(redeemScriptHash)
EQUAL
P2SH在最初的协议设计时是不存在的,后来通过软分叉的方式加入。
在P2SH中,input script要给出一些签名(数目不定)及一段序列化的redeemScript
验证时分两步:第一步验证这段序列化的redeemScript是否与output script中的哈希值匹配?第二步反序列化并执行redeemScript ,配合前边的签名是否可以执行通过。
redeemScriptHash是赎回脚本,可以设计成多种形式,比如前面的P2PK或者P2PKH形式,以及多重签名形式。
用P2SH实现P2PK的实例为
redeemScript:
PUSHDATA(PubKey)
CHECKSIG
input script:
PUSHDATA(Sig)
PUSHDATA(serialized redeemScript)
output script:
HASH160
PUSHDATA(redeemScriptHash)
EQUAL
执行过程为,签名入栈,序列化的redeemScript入栈,然后计算哈希160,随后输出脚本中的redeemScriptHash入栈,比较两者是否相等,相等则同时出栈,然后执行赎回脚本,公钥入栈,检查是否与签名对应,若对应返回true。栈变化如下:
栈 | —> | 栈 | —> | 栈 | —> | 栈 | —> | 栈 | —> | 栈 | —> | 栈 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
—> | —> | —> | RSH | —> | —> | —> | ||||||
—> | serialRS | —> | RSH | —> | RSH | —> | —> | pubkey | —> | |||
sig | —> | sig | —> | sig | —> | sig | —> | sig | —> | sig | —> | true |
这种方式的应用,如下图所示
8.4 多重签名
多重签名是指多个用户对同一个文件进行签名和认证,增强了安全性,防止个人的破坏,同时增加了容错性。多重签名具体实现的输入输出脚本:
input script:
x //协议设计时的bug,需要添加一个没用的元素
PUSHDATA(Sig_1)
PUSHDATA(Sig_2)
...
PUSHDATA(Sig_M)
outputScript:
M //M个签名,满足数量后,可参与验证
PUSHDATA(pubkey_1)
PUSHDATA(pubkey_2)
...
PUSHDATA(pubkey_N)
N //N个公钥,事先存储
CHECKMULTISIG
一个实例脚本如下:
input script:
FALSE
PUSHDATA(Sig_1)
PUSHDATA(Sig_2)
outputScript:
2
PUSHDATA(pubkey_1)
PUSHDATA(pubkey_2)
PUSHDATA(pubkey_3)
3
CHECKMULTISIG
执行过程如下所示:首先输入脚本将FALSE入栈,然后两个签名入栈。输出脚本中参数2入栈,三个公钥入栈,用来检查签名的合法性,然后参数3入栈,随后检查多重签名,满足条件即可通过。
栈 | —> | 栈 | —> | 栈 | —> | 栈 | —> | 栈 | —> | 栈 | |
---|---|---|---|---|---|---|---|---|---|---|---|
—> | —> | —> | —> | 3 | —> | ||||||
—> | —> | —> | pubkey_3 | —> | pubkey_3 | —> | |||||
—> | —> | —> | pubkey_2 | —> | pubkey_2 | —> | |||||
—> | —> | —> | pubkey_1 | —> | pubkey_1 | —> | |||||
—> | —> | 2 | —> | 2 | —> | 2 | —> | ||||
—> | Sig_2 | —> | Sig_2 | —> | Sig_2 | —> | Sig_2 | —> | |||
—> | Sig_1 | —> | Sig_1 | —> | Sig_1 | —> | Sig_1 | —> | |||
FALSE | —> | FALSE | —> | FALSE | —> | FALSE | —> | FALSE | —> | TRUE |
这种多重签名的问题主要在于,将提供参数,提供给公钥的行为交与用户,不利于用户体验。因此产生了P2SH实现多重签名。
8.5 用P2SH实现多重签名
这种方式将复杂度从输出脚本转移到了输入脚本的redeemScript,将用户的工作降到最低。
RedeemScript:
M
PUSHDATA(pubkey_1)
PUSHDATA(pubkey_2)
...
PUSHDATA(pubkey_N)
N
CHECKMULTISIG
input script:
x
PUSHDATA(Sig_1)
PUSHDATA(Sig_2)
...
PUSHDATA(Sig_M)
PUSHDATA(Serialized RedeemScript)
output script:
HASH160
PUSHDATA(RedeemScriptHash)
EQUAL
一个实例脚本如下:
RedeemScript:
2
PUSHDATA(pubkey_1)
PUSHDATA(pubkey_2)
PUSHDATA(pubkey_3)
3
CHECKMULTISIG
input script:
FALSE
PUSHDATA(Sig_1)
PUSHDATA(Sig_2)
PUSHDATA(Serialized RedeemScript)
output script:
HASH160
PUSHDATA(RedeemScriptHash)
EQUAL
执行过程如下:输入脚本中的内容全部进栈,然后将Serialized RedeemScript计算hash160,将输出脚本中的RedeemScriptHash入栈,将两者作比较,若相等则执行RedeemScript,将RedeemScript的内容全部入栈,然后检查,满足条件则为true。
栈 | —> | 栈 | —> | 栈 | —> | 栈 | —> | 栈 | —> | 栈 | |
---|---|---|---|---|---|---|---|---|---|---|---|
—> | —> | —> | —> | 3 | —> | ||||||
—> | —> | —> | —> | pubkey_3 | —> | ||||||
—> | —> | —> | —> | pubkey_2 | —> | ||||||
—> | —> | RSH | —> | —> | pubkey_1 | —> | |||||
SeriRS | —> | RSH | —> | RSH | —> | —> | 2 | —> | |||
Sig_2 | —> | Sig_2 | —> | Sig_2 | —> | Sig_2 | —> | Sig_2 | —> | ||
Sig_1 | —> | Sig_1 | —> | Sig_1 | —> | Sig_1 | —> | Sig_1 | —> | ||
FALSE | —> | FALSE | —> | FALSE | —> | FALSE | —> | FALSE | —> | TRUE |
8.6 烧毁证明(Proof of Burn)
只存在于输出脚本,在这个脚本中无论return后为何值,都会返回false,包含了这样的output script的output被称为Provably Unspendable/Prunable Outputs。脚本格式为:
output script:
RETURN
…[zero or more ops or text]
这个脚本是证明销毁比特币的方法,应用场景主要有以下两种形式:
1.小币种要求销毁一定数量的比特币才可以得到该币种
2.往区块链中写入一些内容,可以把这些信息直接固定在区块链上,无法篡改。这个作用类似于每个区块中的coinbase域,Proof of Burn与之不同的一点在于任何节点,任何用户都可以使用。而往coinbase域写入信息是获得记账权的节点特有的属性。
如下图所示,Unable to decode output address - (Unspent) 部分就包含了Prunable Outputs,也就是该用户向区块链中写入了一些信息。
销毁比特币后,不用将其放到UTXO集合,节省了空间,对于矿工是很友好的。
9.区块链分叉
9.1 状态分叉(State Fork)
由于对区块链当前状态的判断不同产生的分叉,例如,同时产生两个合法区块,节点在先收到的区块后进行扩展,由于区块在网络上的传播有时延,各个节点可能接受两个中任何一个区块,造成state fork。state fork的一个实例就是分叉攻击。
9.2 协议分叉(Protocol Fork)
由于协议版本不同,或者具体实现的时候有差异产生的分叉成为protocol fork,protocol fork分为hard fork和soft fork。
9.2.1 硬分叉(Hard Fork)
硬分叉是对比特币协议进行扩展或者修改,增加一些新的特性,更新协议的节点与未更新协议的节点之间会产生不一致,未更新的节点拒绝验证已经更新的节点产生的块,造成硬分叉,这是一种长期的分叉。
其中的一个例子就是对区块大小限制的修改,假如对比特币协议进行修改,使区块大小限制由1M——>4M。如下图所示:
此时比特币系统中拥有绝大多数算力的用户更新了协议,而剩下的一部分用户未更新。会出现的问题:
1.新协议挖出的块,不被旧协议认可,因为其中包含的交易有他认为不合法的。
2.大部分算力集中于新协议,扩展速度快;而少部分算力由于不认可新协议,也在扩展自己的链,造成链的分叉,这是一种长期的分叉。
硬分叉会形成多种加密货币,分叉出的加密货币都是被认可的。
实际的例子:早期的ETH收到黑客攻击后,形成ETH和ETC,ETH是旧链。
硬分叉会带来的问题:账户金额同时存在于两条链,会存在重放攻击。如下图:
M是一个恶意用户,B收到A的一笔比特币,然后转在新的加密货币上给M,M把这笔交易在旧的加密货币上重放,结果M收到了两笔加密货币的交易。同样的B也可以进行类似的重放。为了防止这样的问题的出现,需要设置chain id,来确认是哪一条链的加密货币。
9.2.2 软分叉(Soft Fork)
软分叉是指区块链网络系统版本或者协议进行升级之后,旧的区块并不知道比特币网络以及升级了,并且继续接受由新节点创造的区块,新老节点还是在同一条链上工作。
举例说明就是对区块大小限制的修改,假如对比特币协议进行修改,使区块大小限制由1M——>0.5M。如下图所示:
由于大部分节点升级了协议,算力集中于新协议,能够很快的产生最长合法链,而少部分区块未升级协议,依旧按原来的方案挖矿,由于旧的协议接受新的区块,而新协议的链更容易形成最长合法链,旧协议区块会转向新协议。但是它后来挖出的区块,不被新协议接受,只是在做无用功,拿不到出块奖励。
实际例子:P2SH,软分叉加进去的
10.比特币的匿名性(anonymity)
匿名性通常与隐私保护联系。
10.1 匿名性遭到破坏
10.1.1 多个账户关联
在一笔交易中,如下图所示:
输入方的付款地址可能不止一个,这时就可以判断两个地址是关联的,有极大的可能性两个地址是同一个用户。除此之外,输出部分的地址也可能不止有收款方的地址,也可能存在有付款方的地址。
10.1.2 与现实世界联系
通过现实中的货币购买比特币,通过现实世界中的资金的转入链转出链,判断比特币与实体的关联情况。通过比特币购买商品,可以留下记录。
10.2 匿名性的增强方法
10.2.1 网络层匿名
网络层匿名研究已经较成熟,可以通过多路径路由的方式(应用实例:Tor),多跳转发,每个中间节点只知道自己的上一个节点,不知道源地址是什么。只要有一个节点按照这种方式转发,那么源地址就可以被隐藏。
10.2.2 应用层匿名
1.coin mixing的功能:部分服务商提供这样的服务,用户将自己的比特币放进去,换回来的是其他用户的比特币,金额相同但是地址变了。这样做的问题在于,服务商缺乏监管,容易造成风险。
2.比特币钱包软件:有些软件提供了类似于coin mixing的功能。
3.交易所交易:不同币种,不同用户的交易,可以间接提供coin mixing的功能。
10.3 零知识证明( Zero-knowledge Proofs)
零知识证明是指一方(证明者)向另一方(验证者)证明一个陈述是正确的,而无需透露除该陈述是正确的外的任何信息。加密数字货币与区块链为零知识证明的应用提供了新的方向。一个典型的零知识证明如下:
举例:A 要向 B 证明自己拥有某个房间的钥匙,假设该房间只能用钥匙打开锁,而其他任何方法都打不开。有两个方法:
1.A 把钥匙出示给 B,B 用这把钥匙打开该房间的锁,从而证明 A 拥有该房间的正确的钥匙。
2.B 确定该房间内有某一物体,A 用自己拥有的钥匙打开该房间的门,然后把物体拿出来出示给 B,从而证明自己确实拥有该房间的钥匙。 后面的方法 2 属于零知识证明。好处在于在整个证明的过程中,B 始终不能看到钥匙的样子,从而避免了钥匙的泄露。
Zcash是零知识证明在加密数字货币与区块链中的应用,官网链接https://z.cash/
Zcash 是分布式状态和个人用户所拥有的资产证明的结合体,其终极匿名特性也归功于零知识证明,通过名为 zk-SNARK https://z.cash/technology/zksnarks/ 的技术验证交易的真实性,利用一个公共区块链来展示交易,但会隐藏掉交易的金额,而查看密钥的所有者,即币的拥有者,可允许他人查看这个密钥相关联的信息。
10.4 同态隐藏(Homomorphic Hiding)
一个满足同态隐藏的函数有下面的几点属性:
1. For most x’s, given E(x) it is hard to find x.(单向性)
2. Different inputs lead to different outputs – so if x≠y,then E(x)≠E(y).(无碰撞,哈希函数是有碰撞的,找到碰撞在计算上不可行)
3. If someone knows E(x)and E(y),they can generate the HH of arithmetic expressions in x and y. For example, they can compute E(x+y) from E(x) and E(y).(同态运算)
同态运算举例如下:
Alice想要向Bob证明她知道一组数𝑥和𝑦使得𝑥 + 𝑦 = 7,同时不让Bob知道𝑥和𝑦的具体数值。 解决方法如下图
此外,在这个例子中,可以通过随机化的方法对x,y进行处理,使处理后的x’+y’=x+y提高安全性,防止Bob对x和y的穷举攻击。
10.5 盲签方法
前面提到了防范双花攻击的一种方法是对比特币进行编号,产生比特币与用户对应的键值对,防止攻击。而为了保证去中心化的这个要求,编号不能由中心化机构产生,应该由用户产生,如何验证用户的序号?采用盲签方法,具体实现如下
1.用户A提供SerialNum,银行在不知道SerialNum的情况下返回签名Token,减少A的存款
2.用户A把SerialNum和Token交给B完成交易
3.用户B拿SerialNum和Token给银行验证,银行验证通过,增加B的存款
4.银行无法把A和B联系起来
11.Q&A
11.1 转账交易收款方不在线
收款方不需要在线,比特币交易只需要将这笔交易写入区块,就可以证明收款方收到了转账
11.2 比特币账户可能是从未出现过的
这是可能的,因为创建一个账户只需要一个公钥和私钥,只要不发生交易,那么这个账户就有可能从来没有在这个网络上出现。
11.3 私钥丢失后
没有办法,成为死钱,无法找回。加密货币交易所存储私钥的安全性并不比个人存储高,因为缺乏监管,安全性不高。
11.4 私钥泄露后
尽快将比特币转到另一个自己的账号。
11.5 转账地址写错
没有办法补救,地址有很大可能性不存在。
11.6 矿工偷比特币
不可能存在,因为找到的hash值是由coinbase transaction中的节点的收款地址绑定的,如果恶意节点将收款地址修改,那么Merkel tree的根哈希值会变,从而导致计算出的哈希值变化。
11.7 交易费给哪个矿工
不需要提前指定交易费,因为如果矿工获得记账权,他会知道一笔交易的total input和total output,打包的时候,就会用total input-total output得到交易费。