区块链“分叉”的产生“ & ”BestChain选择准则

“分叉”现象的产生

非人为“分叉”的产生原因

所谓“挖矿”,是计算一个HASH值<=TargetHash的过程;

而符合计算规则的结果Hash有很多个,所以会出现同时好多个“矿工”同一时段(考虑到网络延迟)宣布“挖矿”成功;

所以,同一区块高度会出现多个区块的时候,由此就产生了区块链的Fork,也就是“分叉”现象。(插一句,这就是为什么中本聪要规定均值10分钟产生一个区块,为的就是降低碰撞的频次,减少fork次数);

人为“分叉”的产生原因

这种人为分叉,要从区块链的共识机制说起。

区块链是由点对点的去中心化节点共同维护的,区块链的有序运转靠的就是大家的共识;

即对于哪些交易是可以接受的、哪些节点具有记账权等关键事宜,有一套公认的标准。

这套标准是部署在区块链底层协议中自动化执行的。

当不同节点运行的底层标准(即客户端软件版本不一致)不一致的时候,人为分叉自然而然就产生了。

PS:为什么会出现不同的客户端软件版本不一致呢?主要原因有以下几种

  1. 利益原因:不同的矿工因为利益关系,矿工个体本身有选择性的接受或者不接受新的升级版本
    • 不接受新版本,挖到的新矿就在某个分支上。如果算力过硬,该分支有可能取代其他分支,作为主链。
  2. 时延原因:不可能所有的客户端在同一时刻同时升级,即使所有节点都同意升级并立刻升级,也会有时延
  3. 区块链网络节点可自由动态加入或者离线,离线的那部分节点会感知不到升级,等再次入局升级的时候,这中间会有一段时间。

“分叉”之后,最佳链(bestChain)的选择准则?

那么,要保证区块链的MainChain唯一性,当出现多处分叉的时候,该用什么准则去选择最佳链(select the bestChain as mainChain)呢?

  1. 如果不同分支的区块高度不同,那么选择最长区块高度的分支为MainChain(在POW共识机制下,最长工作量代表着最权威的说服力);
  2. 如果高度一致,则选择难度系数最大的分支为MainChain(在POW共识机制下,difficulty越大,则说明该区块被创造所需要的工作量越大,则权威说服力越大);
  3. 区块高度如果相同,并且难度系数也一样,那么选择接受时间最早的那个分支为MainChain;
  4. 若所有的评判系数均相同,则等待各自某分支的区块高度+1之后,重新重复上述1~3步骤选择出一个BestChain。

软分叉 VS 硬分叉

比特币网络正常运行出现的“分叉”,是非人为干扰的系统“自分叉”,系统的筛选逻辑可解决该种分叉;

所以,也会存在人为“分叉”,人为“分叉”分为:软分叉和硬分叉;

硬分叉指的是改变了比特币底层协议,使得之前被验证无效的区块变得有效,而为了保持兼容性,会强制要求所有节点都更新协议版本至最新。(一句话就是:凡是霸王手段耍流氓式的要求所有节点必须升级,否则运行机制不兼容影响新生成区块和交易验证的分叉都是硬分叉)

软分叉指的是虽然改变了比特币底层协议,但是只会使之前被验证有效的区块变的无效,而且即使节点不升级(虽然官方WIKI是这么描述的,但是是有具体的实施机制的,下文中会详细说明,暂不提),也不会影响新生成区块和交易被验证接受。软分叉是“向后兼容”的。

参考

通俗地讲,“挖矿”即生成新区块的本质是:做一系列的哈希运算,当运算得到的哈希值符合目标规则,即为挖矿成功(其实,就是寻找符合条件的Nonce参数的过程,下文会详细解释,此处记住这个概念即可)。

也就是比较俩数值的大小,target目标值(固定)与哈希计算值(矿工每次计算算出来的值),这两个值的计算公式是什么呢?

如下图,为区块头(header)结构,结合着图示,介绍下挖矿的原理。

这里直介绍两个关键信息:

1)难度系数:difficulty参数,顾名思义用来调节生成区块的难度的。该值决定了target的大小,

公式为:target=2**(256-Difficulty)

中本聪希望生成每个区块需要耗时10分钟,但是实际情况往往多变,生成区块(算出target)的时间有可能远小于十分钟(difficulty偏下)或者远大于十分钟(difficulty偏大);

那么在每2016【2016=14(day)24(hour)60(min)/10(min)】个区块生成完毕之后,系统要自动调节difficulty参数大小;

调整公式为:New Difficulty= Old Difficulty*(最新的2016区块实际耗时/ 20160 minutes).即:最新2016个区块花费时长与20160分钟(20160分钟是2016个区块期望的产出时间)比较所得。

2)随机数:Nonce参数,也可以理解为POW工作量证明的计数器;

该字段是最重要的一个字段,因为其实“挖矿”的过程就是寻找符合条件的Nonce值的过程;我们知道“挖矿”是做一些列的哈希计算的过程,该过程为:对区块头和nonce进行哈希运算【sha256(str(header)+str(nonce))】;如果本次所得的哈希值<=target,则“挖矿”成功;反之,Nonce+1,重复上述过程;

PS:

1)Nonce叫“随机数”的原因是因为原则上每次计算当前Nonce值可以随机产生。但是,实际情况下:什么方式最容易计算出目标值呢?穷举法;所以每次计算Nonce从0开始;计算Hash值是否<=targetHash; 如果本次所得的哈希值<=target,则“挖矿”成功;反之,Nonce+1,重复计算过程

2)为什么说“挖矿”这么难呢?(上文中说的target值符合特定的规则:十六进制的以连续0开头,且连续的0越多越能符合命中规则)

打个比方:当前target值为连续20个0开头的十六进制数字;

一个不透明的口袋里有俩形状一模一样的乒乓球,一个红色(1),一个蓝色(0);每次抓阄抓到蓝色则标记位0,红色标记位1,那么如果矿工想要“挖矿”成功,意味着:至少连续20次抓阄均抓到篮球。是不是瞬间觉得很难呢?

如果连续2016个区块的生成时间小于期望的20160分钟,系统会自动调节Difficulty系数,比如调节Difficulty系数之后,target变成了连续21个0开头的十六进制数字,此时,矿工想要挖矿成功,则需要连续21次抓到篮球,生成时间就会相对变长。是不是更难了呢?

如果连续2016个区块的生成时间大于期望的20160分钟,则调低难度系数。

参考

Java 8 引入了新的语言特性——默认方法(Default Methods)。

默认方法允许您添加新的功能到现有库的接口中,并能确保与采用旧版本接口编写的代码的二进制兼容性。

为什么要有默认方法

简单说,就是接口可以有实现方法,而且不需要实现类去实现其方法。只需在方法名前面加个default关键字即可。

为什么要有这个特性?首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,目前的java 8之前的集合框架没有foreach方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。

参考

https://my.oschina.net/benhaile/blog/176007

【比特币源码】交易 transaction

名词 术语

  • 交易: 简单地说,交易指把⽐特币从⼀个地址转到另⼀个地址。更准确地说,⼀笔“交易”指⼀个经过签名运算的,表达价值转移的
    数据结构。每⼀笔“交易”都经过⽐特币⽹络传输,由矿⼯节点收集并封包⾄区块中,永久保存在区块链某处。
  • 交易验证: 被验证成功的交易放入本地内存交易池中(Local Memory Tx Pool,交易池是存储在本地内存中,并不是存储在硬盘里,因此不同节点的两池内容可能有很大差别。原则是,要保证任何在本地内存交易池中的交易均是未确认的
  • 交易确认: 当⼀项交易被区块收录时,我们可以说它有⼀次确认。矿⼯们在此区块之后每再产⽣⼀个区块,此项交易的确认数就再加⼀。当确认数达到六及以上时,通常认为这笔交易⽐较安全并难以逆转。
    • 确认数+1什么意思?
  • 交易权重: 挨个打包进入到区块体中;优先处理权重最高的交易;偶尔会出现提示:”当前网路交易拥堵,建议提高交易费用”。正是因为按照优先级处理,所以在网络交易拥堵的时候,有可能造成低优先级的交易“永远”不会被打包。交易的权重大小取决于三个因素:1)交易创建时间越早;2)交易UTXO大小越大;3)交易费用越高,则权重越大。
  • ⼯作量证明: ⼯作量证明指通过有效计算得到的⼀⼩块数据。具体到⽐特币,矿⼯必须要在满⾜全⽹⽬标难度的情况下求解SHA256算法
  • 难度: 整个⽹络会通过调整“难度”这个变量来控制⽣成⼯作量证明所需要的计算⼒。难度怎样调整的,见。。
  • 难度⽬标: 使整个⽹络的计算⼒⼤致每10分钟产⽣⼀个区块所需要的难度数值即为难度⽬标。
  • 难度调整: 整个⽹络每产⽣2,106个区块后会根据之前2,106个区块的算⼒进⾏难度调整。

ss

⽐特币交易是⽐特币系统中最重要的部分。根据⽐特币系统的设计原理,系统中任何其他的部分都是为了确保⽐特币交易可
以被⽣成、能在⽐特币⽹络中得以传播和通过验证,并最终添加⼊全球⽐特币交易总账簿
(⽐特币区块链)。⽐特币交易的
本质是数据结构,这些数据结构中含有⽐特币交易参与者价值转移的相关信息。⽐特币区块链是全球复式记账总账簿,每个⽐特币交易都是在⽐特币区块链上的⼀个公开记录。

比特币“交易打包”底层原理

“挖矿”与“交易打包”

前文我们说到,所谓“挖矿”,就是生成一个最新“区块”的过程,“矿工”在该过程中,是为了获取比特币的奖励(经济驱动);

这部分奖励分为两部分:比特币网络系统的CoinBase奖励 和 所打包的所有交易的交易费(交易费的作用下文会介绍);

那么,交易打包的过程和底层原理是什么呢?下文我们将图文展示整个交易打包的细节;

“交易打包”过程?

每个比特币客户端节点自加入比特币网络开始,无时不刻不在做着创造新区块的操作;

而创造新区块的过程,即是打包每一笔“比特币交易”的过程;

下图是站在“比特币客户端节点”的角度,来描述一次区块生成过程中的交易打包过程。

源码

primitives/transaction.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/** The basic transaction that is broadcasted on the network and contained in
* blocks. A transaction can contain multiple inputs and outputs.
*/
class CTransaction
{
public:
// 以下变量采用常量,为了避免无意的改动
const std::vector<CTxIn> vin;
const std::vector<CTxOut> vout;
const int32_t nVersion;
const uint32_t nLockTime;
// ...
};

class COutPoint
{
public:
uint256 hash;
uint32_t n;
// ...
}


/** An input of a transaction. It contains the location of the previous
* transaction's output that it claims and a signature that matches the
* output's public key.
*/
class CTxIn
{
public:
COutPoint prevout;
CScript scriptSig;
uint32_t nSequence;
CScriptWitness scriptWitness; //! Only serialized through CTransaction

static const uint32_t SEQUENCE_FINAL = 0xffffffff;
static const uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG = (1 << 31);
static const uint32_t SEQUENCE_LOCKTIME_TYPE_FLAG = (1 << 22);
static const uint32_t SEQUENCE_LOCKTIME_MASK = 0x0000ffff;
static const int SEQUENCE_LOCKTIME_GRANULARITY = 9;
}

/** An output of a transaction. It contains the public key that the next input
* must be able to sign with to claim it.
*/
class CTxOut
{
public:
CAmount nValue;
CScript scriptPubKey
}

// 可变交易是干嘛用的?交易还能撤销?更改?回退?
struct CMutableTransaction
{
std::vector<CTxIn> vin;
std::vector<CTxOut> vout;
int32_t nVersion;
uint32_t nLockTime;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
┌─────────────────────────────────────────────┐
│Tx: 12af...e85d │
├─────────────────────┬───────────────────────┤
│TxIn │TxOut │
├─────────────┬───────┼──────┬────────────────┤
│prev hash │index │btc │pkScript │
├─────────────┼───────┼──────┼────────────────┤
│0000...0000 │ffff │12.5 │OP_DUP c58a... │
├─────────────┼───────┼──────┼────────────────┤
│2016...a3c5 │3 │0.15 │OP_DUP a1b2... │
├─────────────┼───────┼──────┼────────────────┤
│2015...b6d8 │1 │0.08 │OP_DUP c3d4... │
└─────────────┴───────┴──────┴────────────────┘

除了第一笔交易是矿工的挖矿所得外,每一笔交易都拥有一个或多个输入(TxIn),以及一个或多个输出(TxOut)。

第一笔矿工挖矿的收入交易通常被称为Coinbase,它没有输入,所以TxIn的Hash总是被标记为00000000...0000
其他的交易,任何一个TxIn都会唯一追溯到区块链上在本区块之前的某个交易Hash,以及索引。

通过交易Hash和索引(从0开始),即可唯一确定一个未花费的交易输出——UTXO(Unspent Transaction Output)。
这样,每一个Tx Input都和之前的某个Tx Output关联了起来。

交易的输出和输⼊

⽐特币交易的基本单位是未经使⽤的⼀个交易输出,简称UTX。UTXO是不能再分割、被所有者锁住或记录于区块链中的并
被整个⽹络识别成货币单位的⼀定量的⽐特币货币

扩展阅读

挖矿与共识

挖矿是增加⽐特币货币供应的⼀个过程。挖矿同时还保护着⽐特币系统的安全,防⽌欺诈交易,避免“双重⽀付”,“双重⽀
付”是指多次花费同⼀笔⽐特币。矿⼯们通过为⽐特币⽹络提供算⼒来换取获得⽐特币奖励的机会。

“挖矿”的原理,就是计算一个HASH值<=TargetHash的过程;

FAQ

比特币“挖矿”的平均时间为何规定成“10分钟”?

10 分钟是系统找到一个有效的交易链块所需要的平均时间(这个时间不是固定的);

基于运气这个时间实际上会稍长或稍短,比如突然间网络中,算力急速增加,那么生成新区块的时间就会变短;这个平均时长”力图逼近10分钟”,当然,这个出块速度是系统每生成2016个块之后自动调整的,通过调整难度系数difficulty(前序文章中有讲过),大致参考源代码:

1
2
3
static const int64 nTargetTimespan = 14 * 24 * 60 * 60;    //目标时间窗口长度:两周
static const int64 nTargetSpacing = 10 * 60;    // block频率,每10分钟一块
static const int64 nInterval = nTargetTimespan / nTargetSpacing;    // 每两周的产量2016,也是调节周期

10分钟—>1分钟会如何?

1)间隔太短,易导致较多孤块的出现,不安全,不利于交易确认,还浪费资源;

原因是:每一个“矿工”都需要时刻确认自己是否在主链上,那么就需要矿工之间时刻交流,交流受网速影响(在网络情况不良时,间隔时间越短,这个网络不良的影响对最终的结果影响越大),当有“矿工”生成新的区块,发现它们不在主链上,也就是孤块,只能丢弃,造成浪费。

2)如果出块间隔太短,侧面的也就说明,出块的难度太低;

当出块的频率变高的时候,块与块之间的碰撞频率也就变高,碰撞次数越高,主链被fork(分叉)的概率就越大,fork越多,链结构就会变成树结构。而且每当出现分叉的时候,系统需要花费性能去选出bestChain(bestChain被当做Main链,被各节点认可);

PS:fork即为分叉,分叉分为软分叉和硬分叉,关于分叉和bestChain的选择相关知识点会在本系列的其他文章中讲述。

参考

【比特币源码】block

背景 术语

  • 区块:
    ⼀个区块就是若⼲交易数据的集合,它会被标记上时间戳和之前⼀个区块的独特标记。区块头经过哈希运算后会⽣成⼀份⼯作量证明,从⽽验证区块中的交易。有效的区块经过全⽹络的共识后会被追加到主区块链中。
  • 区块链:
    区块链是⼀串通过验证的区块,当中的每⼀个区块都与上⼀个相连,⼀直连到创世区块。

区块头 - CBlockHeader

header只有80字节的数据,

block.h

1
2
3
4
5
6
7
8
9
class CBlockHeader
{
int32_t nVersion;
uint256 hashPrevBlock; // 上一个区块的Hash,区块哈希链就是比特币账本不可篡改的基础
uint256 hashMerkleRoot; // Merkle树根的Hash,根据本区块中的交易计算得来的
uint32_t nTime; // 该区块产生的近似时间 (精确到秒)
uint32_t nBits; // 该区块工作量证明算法的难度目标
uint32_t nNonce; // 用于验证 pow,当解决了pow时,广播该区块,并加入区块链
}

header不包括当前区块的Hash。当前区块的Hash,是GetHash这个函数算出来的,但算出来后在本地肯定有缓存供下个区块生成或者是到来较验用,下个区块如何包含进去的你就需要去了解下挖矿的过程了。

区块 - CBlock

block.h

区块由一个包含元数据的区块头和紧跟其后的构成区块主体的一长串交易组成

1
2
3
4
5
6
7
8
9
class CBlock : public CBlockHeader
{
public:
// network and disk
std::vector<CTransactionRef> vtx; // 交易,一个区块包含至少一笔交易。这些Transaction的Hash通过Merkle Tree计算出所有交易的Merkle Hash,并被包含至区块Hash中,从而实现交易的不可修改。

// memory only
mutable bool fChecked;
};

当前区块的hash值存在哪里

区块哈希值实际上并不包含在区块的数据结构里,不管是该区块在网络上传输时,抑或是它作为区块链的一部分被存储在某节点的永久性存储设备上时。相反,区块哈希值是当该区块从网络被接收时由每个节点计算出来的。区块的哈希值可能会作为区块元数据的一部分被存储在一个独立的数据库表中,以便于索引和更快地从磁盘检索区块。

尼玛,说的什么?看不懂

BlockLocator

1
2
3
4
5
struct CBlockLocator
{
std::vector<uint256> vHave;
...
}

疑问