好书/好文 [学习笔记] 比特币的挖矿和共识

aaron67 · 2019年01月11日 · 296 次阅读
本帖已被设为精华帖!

https://aaron67.cc/2019/01/11/bitcoin-mining-consensus/

公式显示有点问题,可以去看原文


在介绍完比特币交易和区块的相关内容后,这篇文章记录网络节点是如何协同工作共同记账的

比特币的网络

互联网中最常见的网络模型是“客户端-服务器”结构,中心服务器提供特定服务,响应客户端请求

当你打开浏览器上网,打开Outlook收发邮件,都是在这样的模型下与中心服务器通信

一个更简单的例子是,当你使用手机时,需要电信运营商为你提供语音通信和数据流量服务

如果中心服务器掉线,你就无法使用

另一种常见的结构是对等网络P2P,Peer-to-Peer)

同一P2P网络里的每台设备都是彼此对等的,各个节点共同提供网络服务,不存在任何特殊节点

P2P网络没有中心服务器和中心化的服务,也没有层级结构,节点之间交互运作,协同处理,每个节点在对外提供服务的同时也使用网络中其他节点提供的服务

当你使用BT(BitTorrent)下载资源的时候,就是在P2P网络模型下与其他节点通信,你从别的节点下载数据,同时也给别的节点提供下载服务,你可以随时离开而不会对整个网络带来影响

比特币的网络是P2P结构,网络中的节点(运行了比特币软件的计算机)彼此对等,共同维护比特币的总账本

节点能互相“发现”彼此,并建立连接,同步数据,就像你用BT下载资源一样,你从别的节点下载数据,同步自己的本地状态,同时也在传播数据,给别的节点提供同步服务

Imgur

节点在收到新区块或新交易后,都会先验证数据的合法性,只有验证通过才会保存到本地,并继续传播数据给其他节点

如果一个节点保存了所有比特币区块的数据(比特币所有交易的帐本),我们称这是一个全节点(Full Node)

挖矿

网络不断产生新交易,需要不断创建新区块“整理”这些交易

既然比特币网络中的所有节点都是对等的,那要如何一起维护这个总账本呢?谁来记账(创建区块)?凭什么让这个节点来记账?

比特币网络使用工作量证明PoW,Proof-of-Work)的方式决定记账权

网络中的任何全节点,都可以试图创建区块,但区块只有在至少满足下列条件时,才是合法的,才会被其他节点认可和接受

  • 区块中包含的交易都是合法的
  • 区块哈希要小于等于一个目标值

要满足第一个条件很简单,节点只要将每笔交易都验证一遍,丢弃掉不合法的交易即可

但要满足第二个条件很难,先说说这个目标值

nBits

区块头中的nBits字段,标识了当前区块哈希要小于等于的目标值(target)

注意,区块头SHA256的结果有256位,而nBits4字节只有32位,用nBits计算目标值的规则如下,以区块277316为例

区块哈希为0x0000000000000001b6b9a13b095e96db41c4a928b97ef2d944a9b31b2cc7bdc4nBits0x1903a30c(十进制为419668748

目标值被存成系数/指数格式,高2位十六进制数是幂(exponent),接下来得6位是系数(coefficient),计算公式为

$$target = coefficient * 256^{exponent–3}$$

所以0x19是幂,0x03a30c是系数,这个区块的哈希要小于等于的目标值为

$$target = 238348 * 256^{22} = 22829202948393929850749706076701368331072452018388575715328$$

用十六进制表示,为0x0000000000000003a30c00000000000000000000000000000000000000000000

竞争记账权

让我们看看全节点是怎么试图创建新区块的,假设

  • 最新的比特币区块高度为100
  • 在高度100的区块构建好后,网络中又出现了1000笔新交易,这些交易被暂时搁在全节点的内存池中,等待被打包进区块(记入账本)

全节点开始试图构建一个新区块

  1. 计算出这1000笔交易的总手续费fee,计算出当前每个区块需要新发行的比特币数量new_coin
  2. 创建一笔Coinbase交易,输出到自己的地址,金额为new_coin + fee
  3. 用Merkle树结构归纳这1001笔交易,得到merkle_root
  4. 根据当前nBits的值,计算出区块哈希要满足的目标值target
  5. 构建一个区块头,共6个字段
字段
nVersion 确定值
hashPrevBlock 区块#100的哈希
hashMerkleRoot 计算出的merkle_root
nTime 当前Unix时间戳
nBits 确定值
nNonce 0
  1. 对步骤5构造的区块头,做两次SHA256运算,得到区块哈希hash

如果hash <= target则新区块创建成功

  • 节点保存这个新区块为#101,并向网络广播区块数据,然后试图构建区块#102
  • 其他节点收到这个新区块后,验证是合法的区块,立即放弃自己正在进行的工作(试图构建区块#101),保存收到的区块为#101并继续向网络广播,根据区块#101中已经包含的交易更新自己的内存池,同时基于区块#101开始试图构建区块#102

如果hash > target则新区块创建失败,调整区块头的内容重新计算,直到找到一个合法的区块哈希

  • nNonce字段值加1
  • 微调nTime
  • 调整区块包含的交易,改变merkle_root
  • 调整Coinbase数据,改变merkle_root(搜索关键字随机值升位方案或extra nonce solution了解更多)

节点在收到新区块后,可以直接对区块头做哈希运算,验证这个新区块是否符合要求,同时还会验证新区块中包含的所有交易,是否都是合法的交易

简单来说,对一个全节点有两种情况

  • 自己最快创建出新区块并通知全网,获得奖励(Coinbase和区块中所有交易的交易费)
  • 在找到一个合法的区块哈希前,收到了别人发过来的合法的新区块,任务失败,没有任何收益

创建区块可以获得奖励,不断驱使着节点之间互相竞争记账权

算力

计算区块哈希,会用到SHA256哈希函数,其计算结果被认为是一致的随机序列,也就是说SHA256的计算结果中的某一位的值,为二进制01的概率,是相同的

SHA256的计算结果是256位二进制,如果抛一枚硬币,结果是正面记为0,是反面记为1

你就可以把计算1区块哈希,想象成抛256硬币

目标值0x0000000000000003a30c00000000000000000000000000000000000000000000的前60位都是0,也就是说,如果要小于等于这个目标值,区块哈希至少前60位都是0

1次尝试需要抛256下硬币,为了让某次尝试前60下的结果都是正面,你平均需要尝试 $2^{60}$

如果一秒钟你能计算十亿($10^9$)次SHA256,排除运气因素,你需要大概1152921504秒才能计算出一个合法区块,约36.5年,而比特币网络中,平均每十分钟,就会有一个节点计算出新区块

我们把单位时间计算哈希的速度,称为算力

为了能在竞争中获胜,需要使用专门计算SHA256的定制设备(矿机)

在写这篇文章的时候(2019年1月11日)

最新的蚂蚁矿机S15,计算速度28T(每秒计算 $28 * 10^{12}$ 次哈希),功耗1600瓦,售价约10000

全网算力(所有节点的算力之和)为

比特币版本 全网算力
Bitcion(BTC) 42.19 Eh/s,每秒计算 $ 42.19 * 10^{18}$ 次哈希
Bitcoin Cash(BCH) 1.25 Eh/s
Bitcoin SV(BSV) 1.04 Eh/s

工作量证明

通过上面的描述,你会发现,只有不断投入新设备,让自己拥有更快的哈希计算速度,不断消耗能源(支付电费),才能在竞争中获胜,才能获得更多的收益,除此之外,别无他法

并且这样的竞争毫无门槛,规则公开,任何人都可以参与,只要你愿意付出成本

You pay, you play

为了让区块哈希满足要求,节点没有别的办法,只有不断尝试,失败后微调区块头数据重新计算,直到网络中的某个节点,最先构造出下一个合法区块

当你成功构建出合法区块,全网络的人都知道,这是你不断尝试和计算的结果

你无需提供任何其他证据来证明自己做了大量的计算投入了大量的成本,最快构建出下一个合法的区块,就是你工作量的最好证明

你也无需提供构建这个区块的详细过程,因为除了一次次尝试,没有其他方法

比特币通过区块发行,试图创建一个新区块就像开采矿产资源一样,需要投入大量的成本和努力,节点之间互相竞争记账权的过程被形象的称为“挖矿”,运行全节点提供算力挖矿的人,也被称为“矿工”

一个要注意的点是,比特币软件会自动控制和调整nBits的值,以保证平均10分钟的区块生产速度,调整规则和计算规则固定在程序中,所有的节点在同一时间都会计算出相同的目标值,如果你悄悄的改了nBits的值(让目标更容易达到)然后创建一个“合法”的区块,同样无法通过其他节点的验证

工作量证明的思想,在生活中处处可见

你如何证明自己已经掌握了必要的专业知识,能熟练操作各种网络设备,可以胜任网络工程师的职位?提供一张CCIE证书就好

常规分叉和共识

你注意到没有,只要计算出的区块哈希小于规定的目标值,这个新创建的区块就是合法的(不考虑其他限制)

如果目标值是100,只要区块哈希落在范围[0, 99],就是合法的,也就是说,下一个合法区块并不是唯一的

数据在网络中传输的同时,还会存在一定的延迟,如果不同的节点几乎同时发现了下一个合法区块,整个网络要如何应对?

  1. 目前,所有的节点状态一致,最新的区块都是星形

  1. 两个节点同时成功构建了下一个区块,并像网络中广播

  1. 随着数据的传播,区块链发生分叉,一部分节点会“跟随”白色三角形区块,在其上试图构建下一个区块,而另一部分节点会“跟随”桔色三角形区块

  1. 一段时间后,某个“跟随”白色三角形区块的节点,最先发现了下一个绿色菱形区块,向网络广播

  1. 所有节点都同步到最新的绿色菱形区块

区块链是连续的,你在计算下一个区块的时候,必须在区块头中记录当前区块的哈希

“跟随”了某条链,就是基于这条链接的最新区块,试图构建下一个区块延长这条链

此时,所有的节点都能看到两条链,并且都会根据规则,“跟随”工作量积累最多的链(一般是最长的那条链)

  • 所有的节点,都会“跟随”星形 > 白色三角形 > 绿色菱形这条链,并基于绿色菱形区块,计算下一个合法区块
  • 桔色三角形区块,被孤立成了孤块(Orphan),没有节点会继续延长它

节点会在短时间内消除分歧,重新达成共识

Imgur

交易确认和共识攻击

当全节点收到一笔新交易时,会先放到自己的内存池中(等待打包进区块),并继续向其他节点广播,此时,这笔交易是“未确认”的

“确认”的意思是,这笔交易已经被打包进区块,写入了比特币的全球总帐本

通过验证的交易会按照优先级顺序打包,假设当前的最新区块高度为100

区块#101中包含了这笔交易,此时,我们说这笔交易有了“一个确认”

随着区块链的不断延长,这笔交易上的确认数也随之增加,确认数越多,表示这笔交易被改写的可能性越低

因为区块是依次相连的,如果你想改写这笔交易(例如从区块中剔除,相当于你没有支付)

因为“最长链”共识的存在,你必须能计算的足够快,从区块#101开始,重新计算一条链超过当前工作量积累最多的那条链,才会让其他节点“跟随”,才能达到改写区块链目的

上面描述的场景被称为共识攻击,也常被称为“51%算力攻击”,如果攻击者拥有大量算力,这种尝试就有可能成功(算力超过全网51%时,攻击尝试几乎一定会成功)

值得注意的是,共识攻击并不能让攻击者直接从某个地址上偷取比特币或不签名就支付比特币,也不会影响加密算法的安全性,它只会影响区块链未来的共识(阻止特定地址的交易,拒绝服务攻击)或过去几个区块的共识(改写过去不久的区块,实现双重支付)

这也是为什么大额比特币转账,需要等待确认的原因

Alice去Bob的店里用比特币购买咖啡,产生了一笔当前未确认的交易

Bob愿意直接把咖啡给Alice而不等待交易确认,因为“小额转账遭遇双重支付”的风险和顾客购物的良好体验(Alice能立即拿到咖啡)比起来,显得微不足道

请注意,Bitcoin Cash(BCH)和Bitcoin SV(BSV)是小额支付零确认安全的(必须有一定的算力才可能双重支付成功)

Bitcoin(BTC)因为BIP-125引入了RBF(Replace by Fee)特性,已经不再适用于这个场景,如果Bob不等交易确认的话,Alice可以先拿走咖啡再通过RBF替换掉之前付给Bob的那笔未确认交易,实现双重支付(不需要任何算力)

Median Time-Past

比特币的挖矿节点彼此独立,不同的节点有着不同的时间精度

区块头中的时间戳是矿工决定的,考虑到网络传输延时,共识规则允许一定的误差,以解决分散节点之间的时间精度问题,但这可能会诱惑矿工说谎,通过让区块包含仍未释放时间锁定的交易,来赚取额外的交易费

BIP-113引入了过去中位时间(MTP,Median Time-Past)的概念,通过计算最后11个区块的时间戳的中位数,作为当前的共识时间,代码在这里

enum { nMedianTimeSpan = 11 };

int64_t GetMedianTimePast() const {
    int64_t pmedian[nMedianTimeSpan];
    int64_t *pbegin = &pmedian[nMedianTimeSpan];
    int64_t *pend = &pmedian[nMedianTimeSpan];

    const CBlockIndex *pindex = this;
    for (int i = 0; i < nMedianTimeSpan && pindex;
         i++, pindex = pindex->pprev) {
        *(--pbegin) = pindex->GetBlockTime();
    }

    std::sort(pbegin, pend);
    return pbegin[(pend - pbegin) / 2];
}

网络中所有与时间有关的计算都使用共识时间

比特币约10分钟产生一个新区块,所以共识时间会比墙上时间(现实世界的时间)慢约一小时

Fee Sniping

这是一种被称为费用狙击(Fee Sniping)的潜在攻击方式

矿工通过选择本应在未来区块中打包的那些交易费更高的交易,试图重写过去的区块,以实现收益的最大化

Fee-sniping is a theoretical attack scenario, where miners attempting to rewrite past blocks "snipe" higher-fee transactions from future blocks to maximize their profitability.

假设当前的区块高度是100

诚实的矿工会试图构建区块#101,延长区块链

某些矿工为了更高的收益,会(在收到区块#100后)重新计算区块#100,选择那些交易费更高(satoshi/KB)的交易打包,这些交易可以是区块#100中的交易,也可以是现在内存池中的交易

如果重新计算的区块#100在之后被网络接受(原来的区块#100成了孤块),则费用狙击成功,不过这并不容易实现(需要一定的算力和运气)

在所有比特币全部发行完前,因为Coinbase奖励的存在,这么干显得不那么有利可图,但这种潜在风险可能在未来出现

为了避免这种情况,可以在创建交易时,使用nLocktime时间锁,限制这笔交易只能在当前区块高度之后打包

  • 当前区块高度为H
  • 创建一笔交易
    • 设置nSequence的值为0xfffffffe,开启nLocktime时间锁
    • 设置nLocktime的值为H + 1

这样的时间锁对交易的打包时间没有任何影响

  • 正常情况下,这笔交易被打包,最快也要等到下一个区块
  • 如果发生费用狙击,重写当前块,这笔交易也不会被选中,因为交易上的nLocktime时间锁没有释放

总结

  • 比特币通过Coinbase交易发行,在所有比特币发行完之前,每个新区块都会包含一笔Coinbase交易
  • 就像采矿一样,我们把计算新区块以获得比特币奖励的过程,称为比特币“挖矿”
  • 比特币使用工作量证明,确定某个时刻的记账权
  • 为了能在竞争中获胜,获得收益,挖矿节点必须持续投入成本
  • 节点之间不需要信任保证,它们遵循同样的规则,有统一的行为,节点会验证收到的数据,符合规则就会认可和接受
  • 矿工会打包未确认的交易,延长区块链,维护比特币网络,成功构建新区块的矿工,可以获得Coinbase奖励和区块中所有交易的交易费
  • 算力是比特币网络安全的保证

因为共识的存在,有算力的全节点互相竞争的同时又一起协作,共同维护比特币的区块链,让网络健康持续的运行

参考

共收到 0 条回复
aaron67 将本帖设为了精华贴 01月11日 22:55
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册