主页 > 下载安卓版imtoken > 第 7 章区块链
第 7 章区块链
7.1 简介
区块链是一种数据结构,由包含交易信息的块从后到前顺序链接。它可以存储为平面文件(包含没有相对关系的记录的文件),也可以存储在简单的数据库中。比特币核心客户端使用谷歌的 LevelDB 数据库来存储区块链元数据。块在这个链中从后到前顺序链接,每个块都指向前一个块。区块链通常被视为一个垂直堆栈,第一个块是堆栈底部的第一个块,每个后续块都放在其他块的顶部。可视化按顺序堆叠块的概念后,我们可以使用以下术语:“高度”表示块与第一个块之间的距离;和 "top" 或 "top" 表示最新添加的块。
通过对每个块头执行 SHA256 加密哈希来生成哈希值。通过这个哈希值,可以识别出区块链中对应的区块。同时,每个块都可以通过其块头的“父块哈希”字段来引用前一个块(父块)。也就是说,每个区块头都包含其父区块哈希。这个将每个区块链接到其各自父区块的哈希序列创建了一条链,该链一直追溯到第一个区块(创世区块)。
虽然每个区块只有一个父区块,但也可以暂时拥有多个子区块。每个子块与其父块具有相同的块,并且在“父块哈希”字段中具有相同的(父块)哈希。一个区块中出现多个子区块称为“区块链分叉”。区块链分叉是一种临时状态,仅当不同矿工几乎同时发现多个不同区块时才会出现(参见“8.10.1 区块链分叉”)。最终,只有一个子区块会成为区块链的一部分,解决“区块链分叉”问题。虽然一个区块可能有多个子区块,但每个区块只有一个父区块,这是因为一个区块只有一个“父区块哈希”字段可以指向其唯一的父区块。
由于区块头包含“父区块哈希值”字段,当前区块的哈希值也受该字段影响。如果父块的身份发生变化,子块的身份也会发生变化。当父块发生任何变化时,父块的哈希值也会发生变化。父区块哈希值的变化会迫使子区块的“父区块哈希”字段发生变化,进而导致子区块的哈希值发生变化。子块哈希值的变化将迫使孙块的“父块哈希”字段发生变化,进而改变孙块的哈希值,以此类推。一旦一个区块存在许多代,这种瀑布效应将确保该区块不会被更改,除非该区块的所有后续区块都被强制重新计算。正是因为这样的重新计算需要大量的计算,长区块链的存在可以使区块链的历史不可变,这也是比特币安全性的一个关键特征。
您可以将区块链视为地质构造中的地质层或冰川核心样本。表层可能随季节变化,甚至在沉积之前就被风吹走。但是你走得越深,地质层就越稳定。向下走几百英尺,您会看到已经保存了数百万年但仍保持其历史状态的岩层。在区块链中,最近的区块可能会因为区块链的分叉导致重新计算而被修改。最新的六个区块就像几英寸深的表土。但是,除了这六个区块之外,区块在区块链中的深度越深,被更改的可能性就越小。在 100 个区块之后,区块链足够稳定,可以支付 Coinbase 交易(包含新开采的比特币的交易)。几千块(一个月)之后的区块链将成为永远不会改变的确定性历史。
7.2块结构
区块是一种容器数据结构,用于聚合包含在公共分类账(区块链)中的交易信息。它由一个包含元数据的块头和组成块体的一长串交易组成。区块头为 80 字节,而平均交易至少为 250 字节,平均区块包含至少 500 笔交易。因此,包含所有交易的完整块比块头大 1000 倍。表 7-1 描述了一个块结构。
表 7-1 块结构
尺寸字段说明
4 个字节
块大小
此字段后的块大小(以字节为单位)
80 字节
区块头
组成区块头的几个字段
1-9(可变整数)
交易计数器
交易数量
变量
交易
p>
区块中记录的交易信息
7.3 个区块头
区块头由三组区块元数据组成。第一个是引用父块哈希的一组数据,这组元数据用于将这个块连接到区块链中的前一个块。第二组元数据,难度、时间戳和随机数,与挖矿竞争有关,详见第 8 章。第三组元数据是 merkle 根(一种用于有效汇总块中所有交易的数据结构)。表7-2描述了块头的数据结构。
表 7-2 块头结构
尺寸字段说明
4 个字节
版本
版本号,用于跟踪软件/协议更新
32 字节
父区块哈希
在区块链Hash值中引用父块的hash
32 字节
默克尔根
本区块交易的默克尔根哈希值
4 字部分
时间戳
生成区块的大致时间(Unix 时间戳精确到秒)
4 个字节
难度目标
这个区块的工作量证明算法的难度目标
4 个字节
随机数
用于工作量证明算法的计数器
挖矿过程中会用到Nonce、难度目标和时间戳,更多细节将在第8章讨论。
7.4 区块标识符:区块头哈希和区块高度
Block Master Identifier 是它的加密散列,一个 SHA256 算法对块头进行第二次散列计算得到的数字指纹。生成的 32 字节哈希称为区块哈希,但更准确的名称是:区块头哈希,因为只有区块头用于计算。例如:000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f 是第一个比特币区块的区块哈希。区块哈希值可以唯一地一、标识一个区块,任何节点都可以通过简单地对区块头进行哈希运算来独立获得区块哈希值。
请注意,区块哈希实际上并不包含在区块的数据结构中,无论是通过网络传输区块还是作为区块链的一部分存储在节点上的持久存储上。相反,当从网络接收到块时,每个节点都会计算块哈希。块哈希可以作为块元数据的一部分存储在单独的数据库表中,以便于索引和更快地从磁盘检索块。
识别区块的第二种方法是通过它在区块链中的位置,即“区块高度”。第一个区块高度为 0,与之前的哈希值 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f 引用的区块相同。因此,可以通过两种方式识别块:块哈希或块高度。随后存储在第一个块之上的每个块在区块链中比前一个块“高”一个位置,就像一个接一个地堆叠在其他盒子顶部的盒子一样。 2014 年 1 月 1 日的区块高度约为 278,000,这意味着在 2009 年 1 月创建的第一个区块之上已经堆叠了 278,000 个区块。
与区块哈希不同,区块高度不是唯一标识符。虽然单个块将始终具有明确的固定块高度,但反之则不成立,并且块高度并不总是标识单个块。两个或多个区块可能具有相同的区块高度并在区块链中竞争相同的位置。在“8.10.1 区块链分叉”一节中详细讨论了这种情况。块高度也不是块数据结构的一部分,它不存储在块中。当一个节点从比特币网络接收到一个区块时,它会动态识别区块在区块链中的位置(区块高度)。块高度也可以作为元数据存储在索引数据库表中,以便快速检索。
一个区块的区块哈希总是唯一标识一个特定的区块。块也始终具有特定的块高度。但是,特定的块高度并不总是唯一标识特定块。相反,两个或多个区块可能会在区块链中竞争一席之地。
7.5 个创世块
区块链中的第一个区块创建于 2009 年,称为创世区块。它是区块链中所有区块的共同祖先,这意味着如果你从任何一个区块往回走,你最终会到达创世区块。
由于创世块被编程到比特币客户端软件中,每个节点都以包含至少一个区块的区块链开始,这确保了创世块不会被阻塞。改变。每个节点都“知道”创世块的哈希值、结构、创建时间以及其中的交易。因此,每个节点都将这个区块视为区块链的第一个区块,从而构建了安全可信区块链的根。
在chainparams.cpp 中,您可以看到创世块被编程到比特币核心客户端中。
创世块的哈希为:
0000000000
19d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
您可以在任何区块浏览网站值上搜索此区块哈希,例如blockchain.info,您会找到描述此区块内容的页面,其中包含包含此哈希值的链接:
000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
从命令行核心客户端使用比特币:
$ bitcoind getblock 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
{
"hash":"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
"confirmations":308321,
"size":285,
"height":0,
"version":1,
"merkleroot":"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b",
"tx":["4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"],
"time":1231006505,
"nonce":2083236893,
"bits":"1d00ffff",
"difficulty":1.00000000,
"nextblockhash":"00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048"
}
创世区块包含一条隐藏消息。其 Coinbase 交易的条目包含“2009 年 1 月 3 日泰晤士报总理濒临对银行进行第二次救助”这句话。这句话是当天的泰晤士报头版头条,引用这句话既是对区块生成时间的参考,也可以算是半开玩笑的提醒区块链的重要性。一个独立的货币体系,同时告诉人们随着比特币的发展,将会发生一场前所未有的全球货币革命。该消息由比特币的创造者 Satoshi Nakamoto 嵌入到创世区块中。
7.6 块连接
比特币的全节点拥有来自创世块的区块链的本地副本。随着新区块的产生,区块链的本地副本不断更新以扩展链。当一个节点从网络接收到传入的块时,它会验证这些块,然后链接到现有的区块链。为了建立连接,节点将检查传入的块头并查找该块的“父块哈希”。
假设,例如,一个节点在其区块链的本地副本中有 277,314 个区块。节点知道最后一个块是第277314块,这个块的块头哈希是:00000000000000027e7ba6fe7bad39faf3b5a83daed765f05f7d1b71a1632249。
然后比特币节点从网络接收一个新的区块,描述如下:
{
"size":43560,
"version":2,
"previousblockhash":"00000000000000027e7ba6fe7bad39faf3b5a83daed765f05f7d1b71a1632249",
"merkleroot":"5e049f4030e0ab2debb92378f53c0a6e09548aea083f3ab25e1d94ea1155e29d",
"time":1388185038,
"difficulty":1180923195.25802612,
"nonce":4215469401,
"tx":["257e7497fb8bc68421eb2c7b699dbab234831600e7352f0d9e6522c7cf3f6c77",
#[...many more transactions omitted...]
"05cfd38f6ae6aa83674cc99e4d75a1458c165b7ab84725eda41d018a09176634"
]
}
对于这个新的区块,节点会在“父区块哈希值”字段中找到包含它的父区块的哈希值。这是节点已知的哈希值,也就是区块277314的哈希值。所以这个区块是链中最后一个区块的子区块,所以现有的区块链可以进行扩展。节点将新区块添加到链的末端,使链增长到 277,315 的新高度。图 7-1 显示了由“父区块哈希”字段连接的三个区块链。
图 7-1 块通过引用父块的块头的哈希值连接成链
7.7 默克尔树
区块链中的每个区块都包含该区块中生成的所有交易,并由默克尔树表示。
Merkle 树是哈希二叉树,是一种用于快速泛化和验证大规模数据完整性的数据结构。此二叉树包含加密哈希值。术语“树”在计算中经常用于描述具有分支的数据结构,但树通常是上下颠倒的,“根”在图的顶部,“叶子”在底部,正如您将请参阅相应示例的后续章节。
在比特币网络中,默克尔树用于汇总一个区块中的所有交易,生成整个交易集的数字指纹,并提供一种方法来验证某个交易是否存在于一个区块中。有效的方式。生成一棵完整的 Merkle 树需要对哈希节点对进行递归哈希,并将新生成的哈希节点插入到 Merkle 树中,直到只剩下一个哈希节点交易区块哈希值如何生成,即 Merkle 树的根。 SHA256算法在比特币的Merkle树中使用了两次,所以它的密码哈希算法也被称为双SHA256。
当N个数据元素被加密并插入Merkle树时,最多可以通过计算2*log2(N)次来检查树中是否有任何数据元素,这使得数据结构非常高效。
Merkle 树是自下而上构建的。在下面的示例中,我们从组成 Merkle 树的叶子的四个事务 A、B、C 和 D 开始,如图 7-2 所示。开始时,所有交易还没有存储到 Merkle 树中,而是先对数据进行哈希处理,然后将哈希值存储到对应的叶子节点中。这些叶子节点分别是HA、HB、HC和HD:
H~A~ = SHA256(SHA256(交易A))
通过将相邻叶子节点的hash值拼接起来,再进行hash,这对叶子节点再汇总为父节点。例如,要创建父节点 HAB,子节点 A 和子节点 B 的两个 32 字节哈希将连接成一个 64 字节字符串。然后对字符串进行两次散列,生成父节点的散列:
H~AB~=SHA256(SHA256(H~A~ + H~B~))
继续类似的操作,直到只剩下顶部节点交易区块哈希值如何生成,即 Merkle 根。生成的 32 字节哈希值存储在区块头中,并汇总了四个交易的所有数据。
图 7-2 Merkle 树中的节点计数
因为 Merkle 树是二叉树,它需要偶数个叶子节点。如果只需要汇总奇数笔交易,则复制最后一笔交易,形成偶数个叶子节点。这种叶节点数为偶数的树也称为平衡树。如图7-3所示,节点C被复制。
图7-3 复制一个数据节点,使整棵树中的数据节点个数为偶数
它由四个交易组成。构建 Merkle 树的相同方法适用于从任意数量的交易构建 Merkle 树。在比特币中,在一个区块中有成百上千的交易是很常见的,所有这些交易都以相同的方式汇总,产生仅 32 字节的数据作为 Merkle 根。在图 7-4 中,您将看到由 16 个事务组成的树。需要注意的是,虽然图的根看起来比所有叶子节点都大,但它们实际上都是相同大小的 32 字节。无论区块中只有一笔交易还是有 100,000 笔交易,Merkle 根总是将所有交易汇总为 32 个字节。
图 7-4 包含许多数据元素的 Merkle 树
为了证明区块中存在特定区块的交易,节点只需要计算log2(N)个32字节的哈希值就可以形成从特定交易到区块链的认证路径或Merkle路径树的根。随着交易数量的急剧增加,这个计算量变得非常重要,因为以 2 为底的交易数量的对数相对于交易数量的增加增长得慢得多。这允许比特币节点有效地生成 10 或 12 个散列(320-384 字节)的路径,以证明一个大字节大小的块中数千个交易之一的有效性。存在。
在图 7-5 中,节点可以通过生成只有四个 32 字节哈希(总共 128 字节)K 的 Merkle 路径来证明区块中交易的存在。这条路径有 4 个哈希(在图 7-5) HL、HIJ、HMNOP 和 HABCDEFGH。这四个哈希值生成的认证路径,然后通过计算另外四对哈希值HKL、HIJKL、HIJKLMNOP和Merkle树根(图中虚线标注),任意节点都可以证明HK(图中由标记为绿色)包含在 Merkle 根中。
图 7-5 用于证明树包含数据元素的 Merkle 路径
例7-1中的代码借用了libbitcoin库中的一些帮助程序来演示从叶子节点哈希到根节点创建整个Merkle树的过程。
示例 7-1 构建 Merkle 树
#include
bc::hash_digest create_merkle(bc::hash_digest_list& merkle)
{
// Stop if hash list is empty.
if (merkle.empty())
return bc::null_hash;
else if (merkle.size() == 1)
return merkle[0];
// While there is more than 1 hash in the list, keep looping…
while (merkle.size() > 1)
{
// If number of hashes is odd, duplicate last hash in the list.
if (merkle.size() % 2 != 0)
merkle.push_back(merkle.back());
// List size is now even.
assert(merkle.size() % 2 == 0);
// New hash list.
bc::hash_digest_list new_merkle;
// Loop through hashes 2 at a time.
for (auto it = merkle.begin(); it != merkle.end(); it += 2)
{
// Join both current hashes together (concatenate).
bc::data_chunk concat_data(bc::hash_size * 2);
auto concat = bc::make_serializer(concat_data.begin());
concat.write_hash(*it);
concat.write_hash(*(it + 1));
assert(concat.iterator() == concat_data.end());
// Hash both of the hashes.
bc::hash_digest new_root = bc::bitcoin_hash(concat_data);
// Add this to the new list.
new_merkle.push_back(new_root);
}
// This is the new list.
merkle = new_merkle;
// DEBUG output -------------------------------------
std::cout << "Current merkle hash list:" << std::endl;
for (const auto& hash: merkle)
std::cout << " " << bc::encode_hex(hash) << std::endl;
std::cout << std::endl;
// --------------------------------------------------
}
// Finally we end up with a single item.
return merkle[0];
}
int main()
{
// Replace these hashes with ones from a block to reproduce the same merkle root.
bc::hash_digest_list tx_hashes{
{
bc::decode_hash("0000000000000000000000000000000000000000000000000000000000000000"),
bc::decode_hash("0000000000000000000000000000000000000000000000000000000000000011"),
bc::decode_hash("0000000000000000000000000000000000000000000000000000000000000022"),
}
};
const bc::hash_digest merkle_root = create_merkle(tx_hashes);
std::cout << "Result: " << bc::encode_hex(merkle_root) << std::endl;
return 0;
}
示例 7-2 显示了上述代码的编译运行结果。
示例 7-2 编译运行默克尔树代码
$ # Compile the merkle.cpp code
$ g++ -o merkle merkle.cpp $(pkg-config --cflags --libs libbitcoin)
$ # Run the merkle executable
$ ./merkle
Current merkle hash list:
32650049a0418e4380db0af81788635d8b65424d397170b8499cdc28c4d27006
30861db96905c8dc8b99398ca1cd5bd5b84ac3264a4e1b3e65afa1bcee7540c4
Current merkle hash list:
d47780c084bad3830bcdaf6eace035e4c6cbf646d103795d22104fb105014ba3
Result: d47780c084bad3830bcdaf6eace035e4c6cbf646d103795d22104fb105014ba3
Merkle 树的效率随着交易规模的增加而增加非常明显。表 7-3 显示了需要转换为 Merkle 路径以证明区块中存在交易的数据量。
表 7-3 默克尔树效率
事务块数近似大小路径大小(哈希数)路径大小(字节)
16 笔交易
4KB
4 个哈希
128 字节
512 笔交易
128KB
9 个哈希
288 字节
2048 笔交易
512KB
11 个哈希
352 字节
65,535 次交易
16MB
16 个哈希
512 字节
p>
根据表格,当区块大小从 16 个交易 (4KB) 急剧增加到 65,535 个交易 (16MB) 时,证明交易存在的 Merkle 路径的长度增长极其缓慢,仅从 128 个字节增加到 65,535 个交易(16MB)。 512 字节。使用 Merkle 树,节点可以只下载块头(80 字节/块),然后通过从全节点回溯一条小的 Merkle 路径来验证交易的存在,而无需存储或传输大块中的大部分内容区块链,这些内容可能有几千兆字节的大小。这种不需要维护完整区块链的节点,也称为简单支付验证(SPV)节点,不需要下载整个区块并经过Merkle路径来验证交易的存在。
7.8 个 Merkle 树和简单支付验证 (SPV)
Merkle 树被 SPV 节点广泛使用。 SPV 节点不保存所有交易也不下载整个区块,只下载区块头。他们使用认证路径或 Merkle 路径来验证区块中是否存在交易,而无需下载区块中的所有交易。
例如,如果一个 SPV 节点想知道其钱包中某个比特币地址的即将付款,该节点将在节点之间的通信链路上建立一个布隆过滤器,限制它只接受目标比特币。地址交易。当节点检测到交易与布隆过滤器匹配时,它将将该块作为 Merkleblock 消息发送。 Merkleblock 消息包含块头和将目标交易连接到 Merkle 根的 Merkle 路径。 SPV 节点可以通过该路径找到与交易相关的区块,然后在对应的区块中验证交易是否存在。 SPV 节点还使用区块头将区块与区块链中的区域区块相关联。交易与区块、区块与区块链这两个关联,证明了交易存在于区块链上。简而言之,SPV 节点接收到的关于区块头和 Merkle 路径的数据不到 1KB,这比一个完整的区块(目前大约 1MB)少了一千多倍。
原始发布日期:2018 年 1 月 23 日