区块链系列(九)之挖矿与共识

1. 简介

挖矿的过程是产生比特币的过程,即发行比特币的过程(类似中央银行的货币发行)。挖矿同时还保护着比特币系统的安全,防止欺诈交易,避免“双重支付”,双重支付是指多次花费同一笔比特币。

挖矿的过程:

  • 每十分钟产生一个新区块。
  • 矿工们争相完成一道基于加密哈希算法的数学难题[工作量证明]。
  • 算出的矿工有权在区块链上记录交易(确认交易)[去中心化结算]。
  • 交易经确认后,新的拥有者才能在之后花费这些比特币。

挖矿的激励机制:

  • 创建新区块的新币奖励
  • 区块链中所含的交易费

挖矿机制特点:

  • 发行速度为指数递减模式:新区块得到的比特币数量每210,000个块(大约四年)减少一半。
  • 起始时间为2009年1月每个区块奖励50个比特币。
  • 2016年每个新区块奖励12.5个比特币。
  • 2140年所有比特币(20,999,999.98)全部发行完毕,不会再有新的比特币产生,矿工收益则主要为交易费。

挖矿的目的:

挖矿是一种去中心化结算(形成共识)的过程,每个结算所对处理的交易进行验证和结算。挖矿保护了比特币系统的安全,实现了在没有中心机构的情况下,也能使整个比特币网络达成共识。

而挖矿产生的比特币作为激励方式是手段,并不是目的,即挖矿不是为了产生比特币而是为了达成共识,形成去中心化结算

1.1. 比特币经济学和货币创造

通过创造出新区块,比特币以一个确定的但不断减慢的速率被发行出来。大约每十分钟产生一个新区块,每一个新区块都伴随着一定数量从无到有的全新比特币。总量有限并且发行速度递减创造了一种抗通胀的货币供应模式。法币可被中央银行无限制地印刷出来,而比特币永远不会因超额印发而出现通胀。

比特币发行总量的计算脚本[max_money.py]

# 初始的块奖励为50BTC
start_block_reward = 50
# 以10分钟为一个区块的间隔,210000个块共约4年时间
reward_interval = 210000

def max_money():
    # 50 BTC = 50 0000 0000 Satoshis
    current_reward = 50 * 10**8
    total = 0
    while current_reward > 0:
        total += reward_interval * current_reward
        current_reward /= 2
    return total

print "Total BTC to ever be created:", max_money(), "Satoshis"

运行 max_money.py 脚本

$ python max_money.py
Total BTC to ever be created: 2099999997690000 Satoshis

比特币货币供应量曲线图

bitcoin-supply
图片 - bitcoin-supply

通货紧缩货币

通缩是一种由于货币的供应和需求不匹配导致的货币增值的现象。它与通胀相反,价格通缩意味着货币随着时间有越来越强的购买力。

比特币最重要并且最有争议的一个结论是一种事先确定的发行速率递减的货币发行模式会导致货币通货紧缩(简称通缩)。

2. 去中心化共识

比特币没有中心化的机构,几乎所有节点都有一份公共总账的副本,由所有节点按照某种规则共同维护该账簿。

比特币的主要特点就是去中心化的自发共识机制,自发即没有明确的选举和固定的达成共识的时间,由所有节点按照某种规则异步交互形成。

去中心化共识主要由4种独立过程相互作用产生:

  • 独立验证:每个全节点依据综合标准对每个交易独立验证
  • 独立打包:通过完成工作量证明算法的验算,挖矿节点将交易记录独立打包进新区块
  • 独立装块:每个节点独立对新区块进行校验并组装进区块链
  • 独立选链:每个节点对区块链进行独立选择,在工作量证明机制下选择累计工作量最大的区块链(最长链原则)

3. 交易的独立校验

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

在交易传递到临近的节点前,每一个收到交易的比特币节点将会首先验证该交易,只有有效的交易才会在网络中传播,而无效的交易将会在第一个节点处被废弃,并以接收时的相应顺序,为有效的新交易建立一个池(交易池)。

校验标准:

  • 交易的语法和数据结构必须正确。
  • 输入与输出列表都不能为空。
  • 交易的字节大小是小于MAX_BLOCK_SIZE的。
  • 每一个输出值,以及总量,必须在规定值的范围内 (小于2,100万个币,大于0)。
  • 没有哈希等于0,N等于-1的输入(coinbase交易不应当被中继)。
  • nLockTime是小于或等于INT_MAX的。
  • 交易的字节大小是大于或等于100的。
  • 交易中的签名数量应小于签名操作数量上限。
  • 解锁脚本(scriptSig)只能够将数字压入栈中,并且锁定脚本(scriptPubkey)必须要符合isStandard的格式 (该格式将会拒绝非标准交易)。
  • 池中或位于主分支区块中的一个匹配交易必须是存在的。
  • 对于每一个输入,如果引用的输出存在于池中任何的交易,该交易将被拒绝。
  • 对于每一个输入,在主分支和交易池中寻找引用的输出交易。如果输出交易缺少任何一个输入,该交易将成为一个孤立的交易。如果与其匹配的交易还没有出现在池中,那么将被加入到孤立交易池中。
  • 对于每一个输入,如果引用的输出交易是一个coinbase输出,该输入必须至少获得COINBASE_MATURITY (100)个确认。
  • 对于每一个输入,引用的输出是必须存在的,并且没有被花费。
  • 使用引用的输出交易获得输入值,并检查每一个输入值和总值是否在规定值的范围内 (小于2100万个币,大于0)。
  • 如果输入值的总和小于输出值的总和,交易将被中止。
  • 如果交易费用太低以至于无法进入一个空的区块,交易将被拒绝。
  • 每一个输入的解锁脚本必须依据相应输出的锁定脚本来验证。

4. 挖矿节点

在比特币网络中,一些节点被称为专业节点矿工。对于新区块,挖矿节点竞争挖矿,如果竞争获胜则立即参与下一个新区块的挖矿中,以此循环。

5. 整合交易至区块

验证交易后,比特币节点会将这些交易添加到自己的内存池中。内存池也称作交易池,用来暂存尚未被加入到区块的交易记录。之后会将交易整合到一个候选区块中。

5.1. 交易块龄、矿工费和优先级

比特币节点需要为内存池中的每笔交易分配一个优先级,并选择较高优先级的交易记录来构建候选区块。交易的优先级是由交易输入所花费的UTXO的块龄决定,交易输入值高、“块龄”大的交易比那些新的、输入值小的交易拥有更高的优先级。如果区块中有足够的空间,高优先级的交易行为将不需要矿工费。

交易的优先级是通过输入值和输入的“块龄”乘积之和除以交易的总长度得到的:

Priority = Sum (Value of input * Input Age) / Transaction Size

其中:

  • 交易输入的值是由比特币单位“聪”(1亿分之1个比特币)来表示的。
  • UTXO的块龄是自该UTXO被记录到区块链为止所经历过的区块数,即这个UTXO在区块链中的深度。
  • 交易记录的大小由字节来表示。

区块中存储交易的规则:

  • 区块中用来存储交易的前50K字节是保留给较高优先级交易的。
  • 然后优先选择矿工费高的交易来填充剩下的区块,区块大小上限为MAX_BLOCK_SIZE
  • 如区块中仍有剩余空间,挖矿节点可以选择那些不含矿工费的交易,也可能忽略这些交易。
  • 在区块被填满后,内存池中的剩余交易会成为下一个区块的候选交易,这些交易由于块龄增大,下次打块的优先级也会增大。

比特币交易中没有过期、超时的概念,一笔交易现在有效,那么它就永远有效。

5.2. 创币交易

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

例如:

$ bitcoin-cli getrawtransaction
d5ada064c6417ca25c4308bd158c34b77e1c0eca2a73cda16c737e7424afba2f 1

创币交易

{
    "hex" :
"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0f03443b0403858402062f503253482fffffffff0110c08d9500000000232102aa970c592640d19de03ff6f329d6fd2eecb023263b9ba5d1b81c29b523da8b21ac00000000",
    "txid" : "d5ada064c6417ca25c4308bd158c34b77e1c0eca2a73cda16c737e7424afba2f", 
    "version" : 1,
    "locktime" : 0,
    "vin" : [
        {
            "coinbase" : "03443b0403858402062f503253482f",                  "sequence" : 4294967295
        } 
    ],
    "vout" : [ 
        {
            "value" : 25.09094928, 
            "n":0, "
            scriptPubKey" : {
                "asm" : "02aa970c592640d19de03ff6f329d6fd2eecb023263b9ba5d1b81c29b523da8b21OP_CHECKSIG",
                "hex" : "2102aa970c592640d19de03ff6f329d6fd2eecb023263b9ba5d1b81c29b523da8b21ac",
                "reqSigs" : 1, 
                "type" : "pubkey", 
                "addresses" : [
                    "1MxTkeEP2PmHSMze5tUZ1hAV3YTKu2Gh1N"
                ]
            }
        } 
    ],
    "blockhash" : 
"0000000000000001b6b9a13b095e96db41c4a928b97ef2d944a9b31b2cc7bdc4",
    "confirmations" : 35566, 
    "time" : 1388185914, 
    "blocktime" : 1388185914
}

5.3. Coinbase奖励与矿工费

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

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

5.4. 创币交易的结构

创币交易的结构比较特殊,与一般交易输入需要指定一个先前的UTXO不同,它包含一个“coinbase“输入。在创币交易中,“交易哈希”字段32个字节全部填充0,“交易输出索引”字段全部填充0xFF(十进制的255),这两个字段的值表示不引用UTXO。“解锁脚本”由coinbase数据代替,数据可以由矿工自定义。

“普通交易“输入的结构

长度 字段 描述
32 字节 交易哈希 指向包含有将要被花费UTXO的交易
4 字节 交易输出索引 UTXO在交易中的索引,0 从0开始计数
1-9 字节 解锁脚本长度 解锁脚本的长度
(VarInt) 可变长度 Unlocking-Script 一段脚本,用来解锁UTXO锁定脚本中的条件
4 bytes 顺序号 当前未启用的TX替换功能,设置为0xFFFFFFFF

“创世交易”输入的结构

长度 字段 描述
32 字节 交易哈希 不引用任何一个交易,值全部为0
4 字节 交易输出索引 值全部为1
1-9 字节 Coinbase数据长度 coinbase数据长度
(VarInt) 可变长度 Coinbase数据 在v2版本的区块中,除了需要以区块高度开始外,其他数据可以任意填写,用于extra nonce和挖矿标签
4 bytes 顺序号 值全部为1,0xFFFFFFFF

5.5. Coinbase数据

创币交易不包含“解锁脚本“(又称作 scriptSig)字段,这个字段被coinbase数据替代,长度最小2字节,最大100字节。除了开始的几个字节外,矿工可以任意使用coinbase的其他部分,随意填充任何数据。

6. 构造区块头

为了构造区块头,挖矿节点需要填充六个字段

区块头的结构

长度 字段 描述
4 字节 版本 版本号,用来跟踪软件或协议的升级
32 字节 前区块哈希 链中前一个区块(父区块)的哈希值
32 字节 Merkle根 一个哈希值,表示这个区块中全部交易构成的merkle树的根
4 字节 时间戳 以Unix纪元开始到当下秒数记录的区块生成的时刻
4 bytes 难度目标 该区块的工作量证明算法难度目标
4 bytes Nonce 一个用于工作量证明算法的计数器

区块头中的merkle根字段,需要将全部的交易组成一个merkle树,创币交易作为区块的首个交易,其他交易在其后,如果是奇数个交易则复制最后一个交易组成偶数个交易,最后生成merkle根的值。

挖矿的目标是找到一个使区块头哈希值小于难度目标的nonce。挖矿节点通常需要尝试数十亿甚至数万亿个不同的nonce取值,直到找到一个满足条件的nonce值。

7. 构建区块

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

7.1. 工作量证明算法

哈希函数的特点:

  • 输入的数据可以是任意长度
  • 输出的数据为固定长度
  • 不同输入得到不同输出,相同输入得到相同输出
  • 几乎不可能通过输出来反推出输入(不同哈希函数安全性可能不同)

比特币一般采用的哈希函数为SHA256函数(输出总是256位的长度),工作量证明过程可简化为如下数学问题:

找出一个 $x$ 使得满足 $f(x,x_0) < y_0$,其中 $x $ 为Nonce值,$x_0$ 为其他已知的常量值,$y_0$为目标值(target值)。

由于$f(x,x_0)$ 是一个单向哈希函数,即无法通过 $y_0$ 反向求出 $x$,所以需要不断地尝试 $x$ 从而得出 $f(x,x_0)$与目标值 $y_0$ 比较来判断该 $x_0$ 是否为所要找的Nonce值。

例如:

假设$x_0$为I am Satoshi Nakamoto,$x$为某个数字(即Nonce值),通过不断更改$x$ 的值,将$x$添加到$x_0$末尾并计算其哈希值,直到找到一个$x$使得得出的哈希值的十六进制表示以0开头(即target值)。

$ python hash_example.py

I am Satoshi Nakamoto0 => a80a81401765c8eddee25df36728d732...
I am Satoshi Nakamoto1 => f7bc9a6304a4647bb41241a677b5345f...
I am Satoshi Nakamoto2 => ea758a8134b115298a1583ffb80ae629...
I am Satoshi Nakamoto3 => bfa9779618ff072c903d773de30c99bd...
I am Satoshi Nakamoto4 => bce8564de9a83c18c31944a66bde992f...
I am Satoshi Nakamoto5 => eb362c3cf3479be0a97a20163589038e...
I am Satoshi Nakamoto6 => 4a2fd48e3be420d0d28e202360cfbaba...
I am Satoshi Nakamoto7 => 790b5a1349a5f2b909bf74d0d166b17a...
I am Satoshi Nakamoto8 => 702c45e5b15aa54b625d68dd947f1597...
I am Satoshi Nakamoto9 => 7007cf7dd40f5e933cd89fff5b791ff0...
I am Satoshi Nakamoto10 => c2f38c81992f4614206a21537bd634a...
I am Satoshi Nakamoto11 => 7045da6ed8a914690f087690e1e8d66...
I am Satoshi Nakamoto12 => 60f01db30c1a0d4cbce2b4b22e88b9b...
I am Satoshi Nakamoto13 => 0ebc56d59a34f5082aaef3d66b37a66...   # 得出的哈希值以0开头
I am Satoshi Nakamoto14 => 27ead1ca85da66981fd9da01a8c6816...

通过以上例子可以看出,其中$x$为13得出的哈希值为0ebc56d59a34f5082aaef3d66b37a66...是满足要求的,即13就是一个满足条件的Nonce值。

同理当target值不断减小,即以0开头的位数增加,则找到该Nonce值的难度也随之增大。

7.2. 难度表示

难度值查询网址:https://bitcoinwisdom.com/bitcoin/difficulty

7.3. 难度目标与难度调整

比特币的区块平均每10分钟生成一个。是货币发行速率和交易达成速度的基础。

计算机性能会不断增长,即挖矿能力会不断提升,难度的调整可以确保新区块产生速度始终稳定在10分钟1个。难度值的调整是自动发生的,依据以下公式。

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

难度的调整公式是由最新2,016个区块的花费时长与20,160分钟(两周,即这些区块以10分钟一个速率所期望花费的时长)比较得出的。如果网络发现区块产生速率比10分钟要快时会增加难度。如果发现比10分钟慢时则降低难度。目标难度与交易的数量和金额无关。

8. 成功构建区块

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

9. 校验新区块

每个节点独立校验每个新区块。当新区块在网络中传播时,每一个节点在将它转发到其节点之前,会进行一系列的测试去验证它。这确保了只有有效的区块会在网络中传播。独立校验还确保了诚实的矿工生成的区块可以被纳入到区块链中,从而获得奖励。行为不诚实的矿工所产生的区块将被拒绝,这不但使他们失去了奖励,而且也浪费了本来可以去寻找工作量证明解的机会,因而导致其电费亏损。

验证区块的内容:

  • 区块的数据结构语法上有效
  • 区块头的哈希值小于目标难度(确认包含足够的工作量证明)
  • 区块时间戳早于验证时刻未来两个小时(允许时间错误)
  • 区块大小在长度限制之内
  • 第一个交易(且只有第一个)是coinbase交易
  • 使用检查清单验证区块内的交易并确保它们的有效性
  • “交易的独立校验”一节已经讨论过这个清单。

10. 区块链的组装与选择

比特币去中心化共识的最后一步是将区块合到最长链中(即有最大工作量证明的链),如果某个节点验证了新的区块,它将尝试将新的区块连接到现有的区块链中,如果验证过程失败,则该区块会被节点拒绝,不会加入链中。

节点维护三种区块:

  • 连接到主链的
  • 从主链上产生分叉的备用链
  • 在已知链中未找到已知父区块的

10.1. 区块链分叉

分叉的发生:

分叉发生在两名矿工在较短的时间内,各自都算得了工作量证明解的时候。两个矿工在各自的候选区块一发现解,便立即传播自己的“获胜”区块到网络中,先是传播给邻近的节点而后传播到整个网络。每个收到有效区块的节点都会将其并入并延长区块链。如果该节点在随后又收到了另一个候选区块,而这个区块又拥有同样父区块,那么节点会将这个区块连接到候选链上。其结果是,一些节点收到了一个候选区块,而另一些节点收到了另一个候选区块,这时两个不同版本的区块链就出现了。

分叉的解决:

解决的办法是,每一个节点总是选择并尝试延长代表累计了最大工作量证明的区块链,也就是最长的或最大累计难度的链。节点通过将记录在每个区块中的难度加总起来,得到建立这个链所要付出的工作量证明的总量。只要所有的节点选择最长累计难度的区块链,整个比特币网络最终会收敛到一致的状态。

11. 共识攻击

待完成

文章参考:

Copyright © www.huweihuang.com 2017-2018 all right reserved,powered by GitbookUpdated at 2022-05-16 12:51:52

results matching ""

    No results matching ""