#区块链 #p2p

无需 std smoldot

构建基于 Substrate 的区块链客户端的原始代码

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神奇豆

Download history 22798/week @ 2024-04-26 23005/week @ 2024-05-03 26729/week @ 2024-05-10 23952/week @ 2024-05-17 34464/week @ 2024-05-24 31033/week @ 2024-05-31 24737/week @ 2024-06-07 24909/week @ 2024-06-14 31587/week @ 2024-06-21 23754/week @ 2024-06-28 27050/week @ 2024-07-05 33517/week @ 2024-07-12 34939/week @ 2024-07-19 33147/week @ 2024-07-26 32606/week @ 2024-08-02 35636/week @ 2024-08-09

142,842 每月下载量
26 仓库(5 个直接使用)中使用

GPL-3.0-or-later…

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 算法。经运行时授权进行此操作的节点将在点对点网络中发出投票。当三分之二或更多授权节点为特定区块的最终化投票时,它实际上就变成了最终化。

收集完这些投票后,它们被收集在所谓的 证明 中。这个证明可以稍后被可能没有收到所有投票的节点请求,或者例如,如果它们处于离线状态。

请参阅 chainfinality 模块以获取更多信息。

用法

这个库故意不提供任何现成的区块链客户端。相反,它提供可以组合在一起创建客户端的工具。

最需要的组件是

  • 连接到点对点网络。请参阅 network 模块。
  • 用于存储区块的持久化存储。请参阅 database 模块。
  • 一个状态机,用于保存有关链状态的信息并验证从网络接收到的区块的真实性和/或正确性。请参阅 sync 模块。

可选

  • 创建新区块的能力。根据本文档的编写,这尚未实现。
  • 一个 JSON-RPC 客户端,以便在客户端上放置方便使用的 UI。请参阅 json_rpc 模块。
  • 待办事项:遥测

依赖项

~14–31MB
~535K SLoC