17 次重大发布
0.18.0 | 2024 年 5 月 29 日 |
---|---|
0.17.0 | 2024 年 1 月 24 日 |
0.16.0 | 2024 年 1 月 3 日 |
0.15.0 | 2023 年 12 月 11 日 |
0.1.0 | 2021 年 3 月 5 日 |
#355 在 神奇豆
142,842 每月下载量
在 26 个 仓库(5 个直接使用)中使用
4MB
63K SLoC
Polkadot 和 Substrate 兼容链的客户端。
区块链概述
区块链本质上是一个分布式和去中心化的键值数据库。区块链的原则是使任何参与者都能够对数据库进行修改,并使所有参与者最终就数据库的当前状态达成一致。
在 Polkadot 和 Substrate 兼容链中,该数据库的状态被称为“存储”。存储可以看作是一个非常大的 HashMap
。
因此,区块链主要由以下三部分组成
- 区块链启动时存储的初始状态。
- 一系列块,每个块表示对存储执行的修改集合。
- 客户端之间的点对点网络,相互连接并交换信息,如新产生的块。
块层层叠加,形成一个修改存储的顺序列表,这些修改基于其初始状态。
块
块主要由以下三个属性组成
- 父块,通过其哈希值进行引用。
- 有序的 extrinsics 列表,表示对存储的状态更改。外联可以是 事务 或 内在。
- 一系列 摘要日志项,包括验证块真实性的必要信息,例如作者对块的加密签名。
为了使抽象更容易,还存在所谓的 创世块 或块编号 0。它没有任何父块、外联或摘要项。
从这三个块属性中,可以导出以下其他属性
- 块的 哈希。这是一个独特的 32 字节标识符,通过以特定的方式将块的所有信息一起散列而获得。
- 区块编号。它等于父区块编号加一,或者创世区块为0。
- 存储状态。它由父区块的存储状态组成,块的外部交易(extrinsics)在此基础上应用。创世区块的存储状态是初始状态。
注意:并非所有这些属性都存储在内存中,甚至不在磁盘上。例如,存储状态非常大,保留链上每个区块的完整存储版本是不现实的。
trie
trie 是一种数据结构,在区块链的工作方式中起着重要作用。
它由一个与键关联的节点树组成,其中一些节点包含一个值。每个节点都与一个称为 Merkle值 的哈希关联,该哈希包含节点子节点的Merkle值。树根节点的Merkle值被称为“Merkle trie根”或“trie根”。
有关更多详细信息,请参阅trie
模块。
区块头部
在实践中,当一个区块需要存储或在机器之间传输时,它被分为两部分:一个 头部 和一个 主体。区块的主体只是其外部交易列表。
注意:区块的主体和区块的外部交易列表是同一件事,这两个名称可以互换使用。
区块头部包含以下内容
- 父区块的哈希值。
- 区块编号。
- 状态trie根,它由该区块存储的所有键和值的trie根组成。
- 外部交易trie根,它由包含区块外部交易的trie的Merkle根组成。
- 摘要日志项列表。
区块的哈希是其头部 blake2
哈希。
有关更多信息,请参阅header
模块。
运行时
从客户端的角度来看,每个区块的存储状态大多是不透明的。然而,存在一些硬编码的键,其中最重要的是 [0x3a, 0x63, 0x6f, 0x64, 0x65]
(这是字符串 :code
的ASCII编码)。与该键关联的值必须始终是一个称为 运行时 的WebAssembly 二进制代码。
这个WebAssembly二进制代码必须遵循一定的ABI,并负责以下操作
- 验证区块是否被其作者正确创建。
- 提供由区块的头部和主体执行的存储修改列表。
- 生成新创建的区块的头部。
- 验证从第三方接收的交易,以便将来可能将其包含在区块中。
- 提供创建交易所需的工具。
换句话说,运行时负责链运行背后的所有实际 逻辑。例如,在执行一个账户到另一个账户的代币转账时,是运行时的WebAssembly代码验证余额是否正确并更新它们。
由于外置操作会改变存储状态,因此外置操作可能会修改这个WebAssembly二进制代码。这被称为 运行时升级。换句话说,任何区块都可能修改链的逻辑。
有关更多信息,请参阅 executor
模块。
分叉和最终化
虽然区块是层层叠建的,但它们不一定形成一个单一链条。多个不同的区块可能有相同的父区块,从而形成多个链条。这被称为 分叉。
当多个链条在网络中飞行时,每个节点会选择一条他们认为“最好”的链条。然而,由于延迟、断开连接或类似原因,节点可能不会立即意识到存在一个比当前最好的链条更好的链条。稍后,当这个节点了解到这个更好的链条时,它将需要进行 重组(简称 重组),其中原本属于最佳链条的区块不再存在。
为了避免重组带来的麻烦,Substrate/Polkadot 提供了 最终化 的概念。一旦一个区块被最终化,就保证它始终是最佳链条的一部分。由此扩展,最终化区块的父区块也总是被最终化。一个链条的创世区块定义为总是被最终化的。
因此,一个区块链条由两部分组成:一个 已最终化 的部分(通常是最长的部分),和一个非最终化部分。虽然已最终化部分是单个线性区块列表,但非最终化部分由一个 有向根树 组成。
为了最终化一个区块,Substrate/Polkadot 节点使用 GrandPa 算法。经运行时授权进行此操作的节点将在点对点网络中发出投票。当三分之二或更多授权节点为特定区块的最终化投票时,它实际上就变成了最终化。
收集完这些投票后,它们被收集在所谓的 证明 中。这个证明可以稍后被可能没有收到所有投票的节点请求,或者例如,如果它们处于离线状态。
请参阅 chain
和 finality
模块以获取更多信息。
用法
这个库故意不提供任何现成的区块链客户端。相反,它提供可以组合在一起创建客户端的工具。
最需要的组件是
- 连接到点对点网络。请参阅
network
模块。 - 用于存储区块的持久化存储。请参阅
database
模块。 - 一个状态机,用于保存有关链状态的信息并验证从网络接收到的区块的真实性和/或正确性。请参阅
sync
模块。
可选
- 创建新区块的能力。根据本文档的编写,这尚未实现。
- 一个 JSON-RPC 客户端,以便在客户端上放置方便使用的 UI。请参阅
json_rpc
模块。 - 待办事项:遥测
依赖项
~14–31MB
~535K SLoC