去中心化共识和挖矿

去中心化共识

比特币网络中的所有参与者如何达成对任意一个所有权的共识呢?所有的传统支付系 统都依赖于一个中心认证机构,依靠中心机构提供的结算服务来验证并处理所有的交易。比特币没有中心机构,几乎所 有的完整节点都有一份公共总帐的备份,这份总帐可以被视为权威记录。区块链并不是由一个中心机构创造的,它是由比特币网络中的所有节点各自独立竞争完成的。换句话说比特币网络中的所有节点,依靠着节点间的不稳定的网络连接所传输的信息,最终得出同样的结果并维护了同一个公共总帐。

​中本聪的主要发明就是这种去中心化的自发共识(emergent consensus)机制。这种自发,是指共识没有明确的完成点,因为共识达成时,没有明确的选举和固定时刻。换句话说,共识是数以千计的独立节点遵守了简单的规则通过异步交互自发形成的产物。所有的比特币属性,包括货币、交易、支付以及不依靠中心机构和信任的安全模型等都依赖于这个发明。

比特币的去中心化共识由所有网络节点的4种独立过程相互作用而产生:

  • 每个全节点依据综合标准对每个交易进行独立验证

  • 通过完成工作量证明算法的验算,挖矿节点将交易记录独立打包进新区块

  • 每个节点独立的对新区块进行校验并组装进区块链

  • 每个节点对区块链进行独立选择,在工作量证明机制下选择累计工作量最大的区块链。

交易的独立校验

钱包软件通过收集UTXO、提供正确的解锁脚本、构造一个新的支出(支付)给接收者这一系列的方式来创建交易。产生的交易随后将被发送到比特币网络临近的节点,从而使得该交易能够在整个比特币网络中传播。

然而,在交易传递到临近的节点前,每一个收到交易的比特币节点将会首先验证该交易,这将确保只有有效的交易才会 在网络中传播,而无效的交易将会在第一个节点处被废弃。

每一个节点在校验每一笔交易时,都需要对照一个长长的标准列表:

  • 交易的语法和数据结构必须正确。

  • 输入与输出列表都不能为空。

  • 交易的字节大小是小于 MAX_BLOCK_SIZE 的。

  • 每一个输出值,以及总量,必须在规定值的范围内 (小于2,100万个币,大于0)。

  • 没有哈希等于0,N等于-1的输入(coinbase交易不应当被传递)。

  • nLockTime是小于或等于 INT_MAX 的。或者nLocktime and nSequence的值满足MedianTimePast(MedianTime是这个块的前面11个块按照block time排序后的中间时间)

  • 交易的字节大小是大于或等于100的。

  • 交易中的签名数量(SIGOPS)应小于签名操作数量上限。

  • 解锁脚本( scriptSig )只能够将数字压入栈中,并且锁定脚本( scriptPubkey )必须要符合isStandard的格式 (该格式将会拒绝非标准交易)。

  • 池中或位于主分支区块中的一个匹配交易必须是存在的。

  • 对于每一个输入,引用的输出是必须存在的,并且没有被花费。

  • 对于每一个输入,如果引用的输出存在于池中任何别的交易中(这笔输入引用的输出有人家自己的输入,不是你),该交易将被拒绝。

  • 对于每一个输入,在主分支和交易池中寻找引用的输出交易。如果输出交易缺少任何一个输入,该交易将成为一个孤 立的交易。如果与其匹配的交易还没有出现在池中,那么将被加入到孤立交易池中。

  • 对于每一个输入,如果引用的输出交易是一个coinbase输出,该输入必须至少获得 COINBASE_MATURITY(100)个确认。

  • 使用引用的输出交易获得输入值,并检查每一个输入值和总值是否在规定值的范围内 (小于2100万个币,大于0)。

  • 如果输入值的总和小于输出值的总和,交易将被中止。

  • 如果交易费用太低以至于无法进入一个空的区块,交易将被拒绝。

  • 每一个输入的解锁脚本必须依据相应输出的锁定脚本来验证。

这些条件能够在比特币标准客户端下的 AcceptToMemoryPool 、 CheckTransaction 和 CheckInputs 函数中获得更详细的阐述。 请注意,这些条件会随着时间发生变化,为了处理新型拒绝服务攻击,有时候也为交易类型多样化而放宽规则。

​在收到交易后,每一个节点都会在全网广播前对这些交易进行独立校验,并以接收时的相应顺序,为有效的新交易建立一个验证池(还未确认),这个池可以叫做交易池,或者memory pool或者mempool。

挖矿节点

在比特币网络中,一些节点被称为专业节点“矿工”。Jing,他就是一位矿工。​同其他节点一样,Jing的节点时刻监听着传播到比特币网络的新区块。而这些新加入的区块对挖矿节点有着特殊的意 义。矿工间的竞争以新区块的传播而结束,如同宣布谁是最后的赢家。对于矿工们来说,收到一个新区块进行验证意味着别人已经赢了,而自己则输了这场竞争。然而,一轮竞争的结束也代表着下一轮竞争的开始。

打包交易到区块

验证交易后,比特币节点会将这些交易添加到自己的内存池中。内存池也称作交易池,用来暂存尚未被加入到区块的交 易记录。与其他节点一样,Jing的节点会收集、验证并传递新的交易。而与其他节点不同的是,Jing的节点会把这些交 易整合到一个候选区块中。

创币交易

区块中的第一笔交易是笔特殊交易,称为创币交易或者coinbase交易。这个交易是由Jing的节点构造并用来奖励矿工们所做的贡献的。

与常规交易不同,创币交易没有输入,不消耗UTXO。它只包含一个被称作coinbase的输入,仅仅用来创建新的比特 币。创币交易有一个输出,支付到这个矿工的比特币地址。

Coinbase奖励与矿工费

为了构造创币交易,Jing的节点需要计算矿工费的总额,将这418个已添加到区块交易的输入和输出分别进行求和,然 后用输入总额减去输出总额得到矿工费总额,公式如下:

Total Fees = Sum(Inputs) - Sum(Outputs)

在区块277,316中,矿工费的总额是0.09094925个比特币。

构建区块头

区块头结构:

image

​在区块277,316被挖出的时候,区块结构中用来表示版本号的字段值为2,长度为4字节,以小端格式编码值为 0x20000000。

​接着,挖矿节点需要填充“前区块哈希”。

​为了向区块头填充merkle根字段,要将全部的交易组成一个merkle树。创币交易作为区块中的首个交易,后将余下的 418笔交易添至其后,这样区块中的交易一共有419笔。在之前,我们已经见到过“Merkle树”,树中必须有偶数个叶子 节点,所以需要复制最后一个交易作为第420个叶子节点,每个叶子节点是对应交易的哈希值。这些交易的哈希值逐层地、成对地组合,直到最终组合并成一个根节点。merkle数的根节点将全部交易数据摘要为一个32字节长度的值。

​挖矿节点会继续添加一个4字节的时间戳,以Unix纪元时间编码,即自1970年1月1日0点到当下总共流逝的秒数。本例 中的1388185914对应的时间是2013年12月27日,星期五,UTC/GMT。

接下来,Jing的节点需要填充Target字段(难度目标值),为了使得该区块有效,这个字段定义了所需满足的工作量证明的难度。难度在区块中以“尾数-指数”的格式,编码并存储,这种格式称作target bits(难度位)。这种编码的首字节表示指数,后面的3字节表示尾数(系 数)。以区块277316为例,难度位的值为0x1903a30c,0x19是指数的十六进制格式,后半部0x03a30c是系数。这部分的概念在后面的“难度目标与难度调整”和“难度表示”有详细的解释。

​最后一个字段是nonce,初始值为0。

​区块头完成全部的字段填充后,挖矿就可以开始进行了。挖矿的目标是找到一个使区块头哈希值小于难度目标的 nonce。挖矿节点通常需要尝试数十亿甚至数万亿个不同的nonce取值,直到找到一个满足条件的nonce值。

构建区块

既然Jing的节点已经构建了一个候选区块,那么就轮到Jing的矿机对这个新区块进行“挖掘”,求解工作量证明算法以使这个区块有效。从本书中我们已经学习了比特币系统中不同地方用到的哈希加密函数。比特币挖矿过程使用的是 SHA256哈希函数。

​用最简单的术语来说,挖矿就是重复计算区块头的哈希值,不断修改该参数,直到与哈希值匹配的一个过程。哈希函数 的结果无法提前得知,也没有能得到一个特定哈希值的模式。哈希函数的这个特性意味着:得到哈希值的唯一方法是不断的尝试,每次随机修改输入,直到出现适当的哈希值。

工作量证明算法(PoW)

简短来说明:比如对语句“I am Satoshi Nakumoto”后面不断累加数字,然后经过SHA256哈希运算,然后将哈希值的十六进制表示以0开头 。
这里刚好是累加到13就找到哈希值了。用概率的角度 来看,如果哈希函数的输出是平均分布的,我们可以期望每16次得到一个以0开头的哈希值(十六进制个位数字为0到 F)。
从数字的角度来看,我们要找的是小于 0x1000000000000000000000000000000000000000000000000000000000000000 的哈希值。

我们称这个为Target目标阈值,我们的目的是找到一个小于这个目标的哈希值。如果我们减小这个目标值,那找到一个小于它的哈希值会越来越难。

​比特币的工作量证明和上例中的挑战非常类似。矿工用一些交易构建一个候选区块。接下来,这个矿工计算这个区块头信息的哈希值,看其是否小于当前目标值。如果这个哈希值不小于目标值,矿工就会修改这个nonce(通常将之加 1)然后再试一次。按当前比特币系统的难度,矿工得试10^15次(10的15次方)才能找到一个合适的nonce使区块头信息哈希值足够小。

难度表示

在区块中看到难度目标,其被标为"难度位"或简称"bits"。在区块277,316中,它的值为 0x1903a30c。 这个标记的值被存为系数/指数格式,前两位十六进制数字为幂(exponent),接下来得六位为系数(coefficient)。在这个区块里,0x19为幂,而 0x03a30c为系数。

计算难度目标的公式为:

target = coefficient \* 2^\(8 \* \(exponent – 3\)\)

这个难度目标值转成16进制后:

0x0000000000000003A30C00000000000000000000000000000000000000000000

高度为277,316的有效区块的头信息哈希值是小于这个目标值的。这个数字的二进制表示中前60位都是0。

难度目标和难度调整

目标决定了难度,进而影响求解工作量证明算法所需要的时间。那么问题来了:为什么这个难度值是可调整 的?由谁来调整?如何调整?

​比特币的区块平均每10分钟生成一个。这就是比特币的心跳,是货币发行速率和交易达成速度的基础。不仅是在短期内,而是在几十年内它都必须要保持恒定。在此期间,计算机性能将飞速提升。此外,参与挖矿的人和计算机也会不断 变化。为了能让新区块的保持10分钟一个的产生速率,挖矿的难度必须根据这些变化进行调整。事实上,难度是一个动 态的参数,会定期调整以达到每10分钟一个新区块的目标。简单地说,难度被设定在,无论挖矿能力如何,新区块产生 速率都保持在10分钟一个。

​那么,在一个完全去中心化的网络中,这样的调整是如何做到的呢?难度的调整是在每个完整节点中独立自动发生的。 每2,016个区块中的所有节点都会调整难度。难度的调整公式是由最新2,016个区块的花费时长与20,160分钟(即 这些区块以10分钟一个速率所期望花费的时长)比较得出的。难度是根据实际时长与期望时长的比值进行相应调整的 (或变难或变易)。简单来说,如果网络发现区块产生速率比10分钟要快时会增加难度。如果发现比10分钟慢时则降低难度。

​这个公式可以总结为如下形式:

New Difficulty = Old Difficulty \* \(Actual Time of Last 2016 Blocks / 20160 minutes\)

注意:虽然目标校准每2,016个块发生,但是由于Bitcoin Core客户端的一个错误,它是基于之前的2,015个块的总时间(不应该是2,016个),导致重定向偏差向较高难度提高0.05%。

​为了防止难度的变化过快,每个周期的调整幅度必须小于一个因子(值为4)。如果要调整的幅度大于4倍,则按4倍调 整。由于在下一个2,016区块的周期不平衡的情况会继续存在,所以进一步的难度调整会在下一周期进行。因此平衡哈 希计算能力和难度的巨大差异有可能需要花费几个2,016区块周期才会完成。

​提示:寻找一个比特币区块需要整个网络花费10分钟来处理,每发现2,016个区块时会根据前2,016个区块完成的时间对难度进行调整。

成功构建区块

,Jing的节点创建了一个候选区块,准备拿它来挖矿。Jing有几个安装了ASIC(专用集成电路)的矿机, 上面有成千上万个集成电路可以超高速地并行运行SHA256算法。这些定制的硬件通过USB连接到他的挖矿节点上。接 下来,运行在Jing的桌面电脑上的挖矿节点将区块头信息传送给这些硬件,让它们以每秒亿万次的速度进行nonce测 试。

​在对区块277,316的挖矿工作开始大概11分钟后,这些硬件里的其中一个求得了解并发回挖矿节点。当把这个结果放进 区块头时,nonce 4,215,469,401 就会产生一个区块哈希值,而这个值小于难度目标值。

Jing的挖矿节点立刻将这个区块发给它的所有相邻节点。这些节点在接收并验证这个新区块后,也会继续传播此区块。当这个新区块在网络中扩散时,每个节点都会将它作为区块277,316加到自身节点的区块链副本中。当挖矿节点收到并验证了这个新区块后,它们会放弃之前对构建这个相 同高度区块的计算,并立即开始计算区块链中下一个区块的工作。

你可能感兴趣的:(去中心化共识和挖矿)