substrate基础(3)-基础概念和原理

涉及到substrate的概念和基础原理

1. 概述

  1. substrate主要分为两层结构:
    1. 外部节点(outer node):用于处理网络活动,例如对等发现、管理事务请求、与对等方达成共识以及响应 RPC 调用。主要由以下部分内容组成:
      1. 存储:键值存储层来持久化状态演变
      2. p2p网络:使用了rust实现的libp2p network stack
      3. 共识:用于达成一致
      4. 远程过程调用RPC API:交互
      5. 监测:方便运维检测节点指标,比如普罗米修斯系统
      6. 执行环境:选择WebAssemblyRust环境,然后将任务派发给运行时(runtime),比如执行合约。
    2. 运行时(Runtime):包含用于执行区块链的状态转换函数的所有业务逻辑,

2. 运行时(Runtime)开发

  1. 运行时包含用于执行事务、保存状态转换以及与外部节点交互的所有业务逻辑。
  2. Substrate的核心主要就是根据自己的业务开发运行时
  3. Runtime通过Api与外部节点进行通信
  4. 可以灵活自定义接口sp_api impl_runtime_apis 每个自定义的Runtime都必须实现CoreMetadata接口,当然,大多数的Runtime都实现了如下几个接口,可以作为参考:
    1. BlockBuilder 以获取构建块所需的功能。
    2. TaggedTransactionQueue 用于验证交易。
    3. OffchainWorkerApi 用于启用链下操作。
    4. AuraApi 用于使用共识的轮循机制方法进行块创作和验证。
    5. SessionKeys 用于生成和解码会话密钥。
    6. GrandpaApi 用于将块最终确定为运行时。
    7. AccountNonceApi 用于查询交易索引。
    8. TransactionPaymentApi 用于查询有关事务的信息。
    9. Benchmark 用于估计和测量完成事务所需的执行时间。
  5. 必须实现的核心基础(数据类型等):
    1. Hash:对某些数据的加密摘要进行编码的类型。通常只有 256 位数量。
    2. DigestItem:一种类型,必须能够编码与共识和更改跟踪相关的许多“硬连线”替代方案之一,以及与运行时内的特定模块相关的任意数量的“软编码”变体。
    3. Digest:一系列文摘项目。这将对与轻量级客户端相关的所有信息进行编码,以便在块中拥有这些信息。
    4. Extrinsic:一种类型,表示区块链外部由区块链识别的单个数据。这通常涉及一个或多个签名,以及某种编码指令(例如,用于转移资金所有权或调用智能合约)。
    5. Header:一种代表(加密或其他方式)与块相关的所有信息的类型。它包括父哈希,存储根和外在三根,摘要和块号。
    6. Block:本质上只是一系列s的组合,以及要使用的哈希算法的规范。HeaderExtrinsic
    7. BlockNumber:一种类型,用于对任何有效块具有的祖先总数进行编码。通常为 32 位数量。
  6. Frame(Framework for Runtime Aggregation of Modularized Entities的缩写),各种不同的库,方便定制化开发
    1. 官方在frame中预定义了大量的pallet,你也可以自定义
    2. 大多数pallet由以下部分组成:
      1. 导入和依赖项
      2. pallet类型声明
      3. 运行时配置特征
      4. 运行时存储
      5. 运行时事件
      6. 应在特定上下文中执行的逻辑的挂钩
      7. 可用于执行事务的函数调用

3. 共识

  1. 默认提供了一些常见的共识机制
  2. Substrate的共识分为两个独立阶段:
    1. Block authoring:生成一个新的块
    2. Block finalization:用于控制分叉和选择规范链的过程
  3. Substrate默认提供Aura来进行Block authoring(块创建)GRANDPA来进行finalization(块确认)

3.1 Block authoring

目前Substrate提供了三种用于块创建的共识,具体这三种的工作原理这里就不解释了,需要自行去查找资料:

  1. Authority-based round-robin scheduling (Aura)
  2. Blind assignment of blockchain extension (BABE) slot-based scheduling.
  3. Proof of work computation-based scheduling.

3.2 Finalization and forks

主要是针对分叉的处理,Substrate提供了机制来处理。比如:

  1. GRANDPA协议,最长链规则来确定哪条是合法的链
  2. GHOST协议,构建的块最多的分支来解决的

3.3 deterministic finality

就是一笔交易何时给出结果。这个需要结合共识来考虑

3.4 默认共识

Substrate默认提供Aura来进行Block authoring(块创建)GRANDPA来进行finalization(块确认)

3.4.1 Aura

提供的基于插槽的块生成机制,由权威机构轮流生成块

3.4.2 BABE

BABE使用一组已知的验证器提供基于插槽的块创作,并且通常用于权益证明区块链。与Aura不同,插槽分配基于可验证随机函数(VRF)的评估。为每个验证器分配一个epoch的权重。该epoch被分解为多个槽,验证器在每个槽评估其VRF。对于验证器的VRF输出低于其权重的每个插槽,允许编写一个块。
因为多个验证器可能能够在同一个插槽中生成一个块,所以即使在良好的网络条件下,分叉在BABE中也比在Aura中更常见
Subscriber的BABE实现还有一个回退机制,用于在给定的插槽中没有选择权限。这些辅助插槽分配允许BABE实现恒定的块时间。
可前往此处进一步了解BABE算法

3.4.3 POW

烂大街了,这个就不讲了

3.4.4 GRANDPA(爷爷)

GRANDPA不会创建块,只是根据创建块的信息进行投票,三分之二投票后,最终决定哪个块合适。最长链规则来决定最终合法的链。
可前往此处进一步了解GRANDPA(爷爷)算法

4. 交易

分为3类:
已签名的交易、未签名事务、固有事务

4.1 已签名交易

很常见的,比如转账,转账前总得有自己的密钥签名才能执行。执行期间,通常也需要手续费

4.2 未签名交易

顾名思义不用签名的交易操作。只能由验证器节点来调用

4.3 固有事务

只能由创建块的节点将信息插入到块中,不会对外广播。比如某节点将时间戳插入块中,不管准确不准确,反正插入了。该块能不能被别的节点接受,这就是验证器的事了。

4.4 交易生命周期

  1. 汇总生命周期:
    1. 本地节点侦听网络上的事务。
    2. 每笔交易都经过验证。
    3. 有效的事务将放在事务池中。
    4. 事务池对相应事务队列中的有效事务进行排序,执行模块调用运行时以开始下一个区块。
    5. 执行事务并将状态更改存储在本地内存中。
    6. 构造的块将发布到网络。
  2. 验证和排队交易
    1. 验证事务池中的事务 (交易池仅处理交易的有效性以及交易队列中放置的有效交易的排序。)
      1. 事务索引(也称为事务随机数)是否正确?
      2. 用于签署交易的账户是否有足够的资金来支付相关费用?
      3. 用于签署交易的签名是否有效?
    2. 将有效事务添加到事务队列
      1. 就绪队列 挂起
      2. 未来队列 等待
    3. 无效的事务处理
      1. 事务已包含在块中,因此将从验证队列中删除。
      2. 交易的签名无效,因此会立即被拒绝。
      3. 交易太大而无法放入当前区块,因此将其放回队列中以进行新的验证轮次。
  3. 按优先级排序的交易
    1. 如果存在一个未签名的事务和另一个已签名的事务,则将未签名的事务放在队列中的第一个。
    2. 如果有两笔来自不同发送方的交易,则确定哪笔交易更重要,应首先包含在区块中。
    3. 如果有两笔来自同一发送方的交易具有相同的:区块中只能包含一笔交易,因此队列中仅包含费用较高的交易
  4. 执行事务和生成区块
    1. 初始化块
    2. 执行事务
      1. 在区块初始化后,每个有效的交易都按交易优先级的顺序执行。请务必记住,在执行之前不会缓存状态。相反,状态更改在执行期间直接写入存储。如果事务在执行过程中失败,则在失败之前发生的任何状态更改都不会恢复,从而使块处于不可恢复状态。在将任何状态更改提交到存储之前,运行时逻辑应执行所有必要的检查,以确保外部操作成功。
      2. 请注意,事件也写入存储。因此,运行时逻辑在执行补充操作之前不应发出事件。如果事务在发出事件后失败,则不会还原该事件。
    3. 完成块

5. 状态转换和存储

  1. Substrate使用RocksDB实现其存储。这是一种用于快速存储环境的持久键值存储。它还支持实验奇偶校验数据库.
  2. Trie abstraction
    1. 使用了改良后的默克尔树,直接访问trie成本很高,一般都是会进行键值缓存
    2. State trie(状态树)
      1. 树根的hash值放在每个块头部,可以方便验证链状态,并且很容易应用于轻量级客户端
      2. 该状态树值存放规范的内容,不存储分叉数据,单独的一个状态数据库来维护它。
    3. Child trie(子树)
      1. 状态树的节点?
      2. 这个没看明白,以后看懂了再说
      3. 主要是方便开发人员自己定义trie?
  3. 存储查询
    1. 使用TwoX 128 hash来存储数据
    2. 查询的多级key,是合并各级的key的hash来查询的,如下:
1
2
3
4
5
6
7
twox_128("Balances")                                             = "0xc2261276cc9d1f8598ea4b6a74b15c2f"
twox_128("FreeBalance") = "0x6482b9ade7bc6657aaca787ba1add3b4"
scale_encode("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY") = "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"

blake2_128_concat("0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d") = "0xde1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"

state_getStorage("0xc2261276cc9d1f8598ea4b6a74b15c2f6482b9ade7bc6657aaca787ba1add3b4de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d") = "0x0000a0dec5adc9353600000000000000"

6. 帐户、地址和密钥

  1. 公钥和私钥
    没啥好说的。其中公钥可以根据不同的平行链,生成对应格式的地址。也就是说,一个公钥可以派生出不同的地址,方便跟不同的链交互
  2. 地址编码和链特定地址
    1. 默认substrate使用SS58 address format格式,基于 base-58 encoding编码
    2. base-58编码优点:
      1. 编码的地址由 58 个字母数字字符组成。
      2. 字母数字字符串省略了在字符串中难以区分的字符
      3. 网络信息(例如,特定于网络的前缀)可以在地址中进行编码
      4. 可以使用校验和检测输入错误,以确保正确输入地址。
      5. 可以注册一个特定的前缀
  3. frame中的帐户信息
    1. 从概念上讲,帐户表示具有公钥/私钥对和一个或多个公共地址的标识。但是,在使用 FRAME 构建的运行时中,帐户被定义为具有 32 字节地址标识符和相应帐户信息(如帐户已进行的事务数、依赖于帐户的模块数以及帐户余额)的存储映射。
    2. 帐户属性(如 )可以在模块中一般定义。然后,在运行时实现中将泛型类型解析为特定类型,并最终分配特定值。
  4. 专业账户
    1. 虽然大多数账户用于表示控制资金或执行交易的公钥/私钥对,但Substrate支持一些专用账户来控制如何使用特定的密钥对。例如,你可能拥有需要自定义加密方案、只能用于执行特定功能或只能访问特定plate的帐户。
    2. 质押账户和密钥
      1. 在大多数情况下,专用帐户是在特定 FRAME plate的上下文中实现的。例如,提名权益证明(NPoS)可能要求节点验证者和提名者持有大量代币。为确保这些账户中的余额安全,质押托盘提供了一些账户抽象,用于分离执行特定操作所需的密钥对。
    3. 账户区分:
      1. 存储帐户
        1. 可以理解为用于质押的账户
      2. 控制器帐户
        1. 理解为控制质押池的账户
      3. 会话帐户
        1. 管理池子的gas消耗等行为的账户
    4. 无密钥代理账户
      1. 没理解,我认为是一个替身账户

7. Rust for Substrate

  1. 对于编写外部节点(客户端)的开发人员,技术要求复杂:需要掌握异步框架Asynchronous Rust
  2. 对于编写运行时的开发人员,专注业务,技术要求普通:Rust idioms working with no_std以及
  3. 需要编译为:Wasm。也就是说,某些典型的标准库不能用,需要用专门定制的库来编译,如:asm32-unknown-unknown no_std
    1. substrate使用了大量的宏,因此开发人员一定要了解宏的使用
    2. substrate中的宏涉及到两类:派生属性自定义属性
  4. 泛型和配置特性

8. 链下操作

  1. Substrate提供了链下操作的机制,调用相应的独立模块,如查询或者进一步处理链上数据:
    1. 链下工作线程(OCW)
    2. 链外存储
    3. 链外索引
  2. 链下worker,需要实施相应验证机制来约束
    1. 能够提交交易(有符号或无符号)到链以发布计算结果。
    2. 一个功能齐全的 HTTP 客户端,允许工作线程访问和从外部服务获取数据。
    3. 访问本地密钥库以对语句或事务进行签名和验证。
    4. 另外一个本地键值数据库在所有链下工作者之间共享。
    5. 用于随机数生成的安全局部熵源。
    6. 访问节点的精确度当地时间.
    7. 睡眠和恢复工作的能力。
  3. 链下存储
  4. 链下索引

总结

本文编辑完毕

参考

[1] Substrate官方文档

Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2017-2023 Jason
  • Visitors: | Views:

谢谢打赏~

微信