☰
Substrate区块链框架实战:从架构到Pallet开发与无分叉升级
2026/9/28 22:15:38 网站建设 项目流程

讲个可能让不少人意外的事实:如果你在一个技术社区搜索框里敲下substrate这个单词,跳出来的结果大概率是三类东西——生物化学里酶催化的“底物”、半导体行业里的“衬底”,以及近几年区块链开发圈里几乎被它独占的“Substrate 区块链开发框架”。我这次要聊的,是最后一个。它的存在感已经强到,Polkadot 生态里绝大多数平行链,底层骨架都是它撑起来的。

Substrate 解决的事情,用一句话概括就是:让“从零造一条区块链”这件事,从几万行底层代码的手工苦力,变成“写业务逻辑、组装标准模块”的工程活。如果你是有 Rust 或 C++ 基础、想进入区块链底层开发的工程师,或者你在联盟链、应用链场景里需要一个能快速迭代、还能无分叉升级的链底座,这篇文章应该能帮你把 Substrate 的整张地图铺开。后面我会从架构讲起,手把手带你把模板链跑起来,再写一个真实的存证业务 Pallet,最后聊聊我实际开发半年里踩过的那些坑。

1. 起点:Substrate 到底解决了区块链开发的哪些痛点

1.1 从零写链,等于把整座冰山从海底捞起来

很多刚接触区块链开发的同学,对“写一条链”的复杂度是严重低估的。共识、网络、存储、账户、密码学、RPC,每一项都是硬骨头。就说共识层:节点之间怎么同步区块、怎么在作恶节点存在的环境下达成一致、怎么处理网络分区和重组,这套东西如果要自己从零实现,没有一年半载根本磨不出来。更危险的是,很多人误以为共识只需要“抄一个现成的”,结果抄过来的代码里藏着诡异的边界条件,主网上线之后某个极端时刻突然停止出块,这种事故在行业内不是没有先例。

网络层同样不省心。NAT 穿透、节点发现、消息广播、加密握手,每一层都有绕不开的工程细节。存储层更不用说,区块链的状态存储不能只“能存能读”,它要能向轻客户端证明某个状态确实存在,这就要求 Merkle 化、可验证、支持历史回溯。账户体系还要定义私钥签名、地址编码、交易序列号、多签逻辑。把这些全部手工拼起来,你会发现你做的根本不是你想要的业务,而是“软件基础设施的搬运工”。

1.2 Substrate 的答案:先造地基,再造房子

Substrate 这个词的英文含义本就是“底层基板”,Parity 团队把它定位成一套接近操作系统级别的区块链开发框架。它把上面说的那些通用组件全部内置实现了:网络层用 libp2p,共识层提供 Aura、BABE、GRANDPA 等可插拔方案,存储层内置基于 Merkle Patricia Trie 的状态数据库,账户系统、交易池、JSON-RPC 接口也都是现成的。

开发者真正需要写的,是 Runtime——也就是区块链的“状态转换函数”:输入一个区块,执行交易,输出一个新的状态。Substrate 还引入了一个非常关键的设计:把 Runtime 编译成 Wasm 字节码存储在链上。这意味着链上逻辑的升级,本质上就是替换一段 Wasm。我后面单独开一章讲这个机制,因为它是 Substrate 区别于绝大多数旧框架的灵魂所在。

我当时选择 Substrate,看中的就是这种“框架思维”。它不是在已经造好的链上给你开几个口子做定制,而是把整条链拆成标准件,让你在一开始就能决定共识用哪个、存储怎么组织、业务模块怎么挂。那种“操作系统 + 应用程序”的感觉,确实比传统的“改成品链”顺手太多。

1.3 横向对比:为什么不直接用 Cosmos SDK 或从零写

我经常被问到:既然都是模块化区块链框架,为什么不用 Cosmos SDK?这里我把几条主流路线放在一起对比一下,方便你结合自己的场景判断。

路线业务定制成本共识实现成本升级方式跨链生态学习曲线
从零开发最高极高硬分叉为主无极陡
Cosmos SDK中高中高(Tendermint 已内置)链上升级需社区协调Cosmos IBC中高
Substrate中(靠 Pallet 组合)低(共识已内置)无分叉升级(Wasm Runtime)Polkadot XCM偏高

Cosmos SDK 也是个好东西,它的 Tendermint 共识直接内置,业务逻辑用模块化方式组织,IBC 跨链协议也做得相当成熟。但 Substrate 在设计目标上更“激进”一点:它把升级这件事做成了链上原生能力,而不是靠社区协调表决硬分叉。这一点在迭代速度上天然有优势。

从零写链的路线,我只有在两种情况下会推荐:一是你纯粹为了学习底层原理,准备把自己按在地板上磨两年;二是你的需求特殊到任何框架都套不住,且团队有足够的人力和资金去养底层基础设施。否则,用 Substrate 这种框架“先跑起来”,永远是最划算的选择。

2. 架构拆解:Client 与 Runtime,一场刻意的分离

2.1 为什么把“网络共识”和“业务逻辑”分成两个世界

Substrate 节点程序从逻辑上被分成两部分:Client 和 Runtime。Client 就是那个“跑在物理机器上的程序”,负责网络连接、区块同步、共识参与、状态存储以及对外提供 RPC 接口;Runtime 则是链上业务逻辑的集合,包括所有交易处理、账户余额变化、业务状态更新。

这两者之间不是随意的分层,而是通过一组明确的接口通信。Client 并不知道 Runtime 具体实现了哪些业务,它只知道自己面对的是一段可以被调用的 Wasm 代码。这个设计的直接好处有两个。第一,共识和业务可以独立演进:出块机制出问题时,不需要动 Runtime;业务逻辑升级时,也不需要换共识。第二,不同语言的节点可以各自独立实现:只要它们都遵守同样的 Runtime 接口规范,就能参与同一条链。

你可以把 Client 类比成手机操作系统,Runtime 类比成手机上运行的应用。过去你想给应用加功能,得连操作系统一起重刷;Substrate 把“应用”单独拎出来了,底层系统保持不变,应用随时可以热更新。这个类比虽然不完美,但已经足够让新同学理解为何 Substrate 能在升级这件事上走出一条完全不同的路。

2.2 FRAME:Pallet 就是区块链世界的乐高

Runtime 不等于一个巨大的函数。Substrate 提供了一个组织 Runtime 代码的框架,叫 FRAME,全称是 Framework for Runtime Aggregation of Modular Entities。FRAME 把常用功能拆成了一个一个的 Pallet——你可以把 Pallet 理解成乐高积木里的一个标准颗粒。

官方提供了一堆开箱即用的 Pallet:balances管账户余额和转账,staking管质押和验证人选举,governance系列管提案和投票,sudo给单个账号超级权限(开发阶段常用,生产环境必须拆掉)。你自己写的业务逻辑,同样封装成一个 Pallet。最终在construct_runtime!宏里把这些 Pallet 像拼图一样组装起来,编译进同一个 Runtime。

需要提醒的是,这种组装是编译期完成的,不是运行时的动态插件系统。好处是类型安全、性能可控,不会有“动态加载模块”带来的运行时开销;代价是你每增加一个 Pallet、每改一段 Runtime 代码,都需要重新编译整个 Runtime 并走一次升级流程。这在早期迭代时会觉得“好烦”,用习惯之后反而觉得踏实——每次改动都是可审计、可回溯的。

2.3 一个区块的一生:Hooks 与生命周期回调

新接触 Substrate 的人,最容易忽略的是区块生命周期回调。一个区块从生产到被确认,Runtime 会在不同阶段调用各 Pallet 定义的回调函数,其中最核心的是on_initialize和on_finalize。

on_initialize在区块交易执行前按顺序执行,通常用来做每轮必须做的“家务活”,比如发放奖励、轮换验证人、更新链上参数。on_finalize在区块末尾执行,适合收尾类的操作,比如把本轮共识相关的结果记录到状态里。每个 Pallet 都可以声明自己在哪个阶段需要被调用,FRAME 会按照 Pallet 在construct_runtime!里声明的顺序依次执行。

这里有个新手高频翻车的点:把重逻辑或大量存储操作塞进on_initialize,导致单个区块的生产时间被拖长,节点开始积压区块,链的出块节奏被打乱。我自己的原则是:on_initialize里只做计算量小、存储操作少的轻量逻辑,凡是超过几十毫秒的批处理,要么拆到多个区块,要么放到 Offchain Worker 里异步处理。理解区块生命周期,是后续做链上性能调优的必修课,越早建立这个意识越好。

3. 实操:从环境准备到跑起第一条本地链

3.1 环境准备里最容易搞错的三个点

跑通 Substrate 模板链,最难的不是写代码,而是环境准备。如果你只是rustup update装个最新版就冲,大概率会在第一次编译时报一堆莫名其妙的错。这里说三个我反复踩过的点。

第一,Rust 工具链版本。早期 Substrate 对 nightly 版本强耦合,差一个日期都不行;后来虽然切到了 stable 也能编,但模板发行分支仍然可能指定专属工具链。正确的做法是:去模板根目录看有没有rust-toolchain.toml文件,有的话,rustup 会自动切换到对应版本,省去手动折腾。千万不要忽略这个文件。

第二,wasm 编译目标。Runtime 要编译成 Wasm,所以必须安装wasm32-unknown-unknown目标。命令是:

rustup target add wasm32-unknown-unknown

如果漏了这一步,编译时会在某个阶段报 “target may not be installed”,然后戛然而止。

第三,内存与并发度。Substrate 的首次编译是个内存大户,默认情况下 cargo 会开满 CPU 核并行编译,16 核机器吃满 30 多个 G 内存很常见。卡死、OOM 我都见过。稳妥的做法是在~/.cargo/config.toml里限制并行度,比如写成 4 到 8,牺牲一点速度保稳定。

3.2 用 Node Template 拉一条链出来

环境就绪后,从官方模板拉代码。我写这篇文章时的稳定分支是polkadot-v1.x.x系列,你按自己拿到的版本分支做即可:

git clone -b polkadot-v1.0.0 https://github.com/substrate-developer-hub/substrate-node-template cd substrate-node-template cargo build --release

首次编译时间在 30 到 60 分钟之间,视机器配置而定,这是正常的,别以为建错项目了。编译完成之后,启动开发链:

./target/release/node-template --dev --tmp

--dev表示以开发模式启动,会自动创建 Alice、Bob 等预置测试账户;--tmp表示所有链上数据只存在临时目录,退出即清理。这两个参数配合起来,最适合做实验,因为你不必担心把开发链弄得一团糟之后没法重置。

启动日志里出现 “Running JSON-RPC server” 之类的内容,就说明节点已经起来了。此时浏览器打开 polkadot.js apps ,切换到本地节点,连上默认的ws://localhost:9944。如果页面上能看到区块不断在出,恭喜你,第一条 Substrate 链已经跑起来了。

3.3 把 Polkadot.js 当作链上的调试器

很多人一上来就想自己写前端,我建议你先忍住。Polkadot.js Apps 是个非常强大的通用前端,在开发阶段完全可以当你的链上调试器用。

第一步先用它验证“链是真活着”。左侧导航里切到 Accounts 页签,正常情况下你能看到 Alice 和 Bob,余额显示为一个很大的数值。接着做一笔转账:选 Alice 发起交易,转给 Bob,填个数额,签名提交。几秒后,Bob 的余额变化了,就说明从“节点出块”到“交易进块”这条链路是通的。

第二步是查询状态。切到 Developer / Chain State 页签,选择system.account,输入一个账户地址,就能看到该账户的 nonce、余额、消费信息。这一步的意义在于理解 Substrate 的存储模型:所谓“链上状态”,本质上就是可以通过这些公共接口随时读取的一组键值数据。Polkadot.js 把这些都可视化了,你做 Pallet 验证时还会频繁回到这里,所以早点熟悉它的操作逻辑,后面能省很多时间。

4. 给链加业务:手写一个存证 Pallet 的完整过程

4.1 为什么拿存证练手最合适

跑通模板链之后,下一步就该让链上“长出你自己的业务”了。我推荐第一个练手项目做成存证(Proof of Existence):用户提交一段内容的哈希,链上记录“谁在什么区块高度提交了这个哈希”。它业务逻辑简单,但完整覆盖了 Pallet 开发的全部基本要素——存储定义、事件、错误处理、交易函数、权限校验。做过一遍,你就理解了 Pallet 的骨架长什么样,之后做更复杂业务只是往这个骨架上加肉。

我用这个练手项目带过不少人,速度快的四十分钟就能跑通。对第一次接触 FRAME 的开发者来说,这个反馈周期非常宝贵,能迅速建立“原来链上业务就是这么写”的信心。

4.2 建目录、写依赖、实现核心逻辑

在 node-template 根目录下创建pallets/poae/目录,里面放Cargo.toml和src/lib.rs。先看Cargo.toml:

[package] name = "pallet-poae" version = "0.1.0" edition = "2021" [dependencies] frame-support = { version = "4.0.0-dev", default-features = false } frame-system = { version = "4.0.0-dev", default-features = false } scale-info = { version = "2.11.0", default-features = false, features = ["derive"] } [features] default = ["std"] std = [ "frame-support/std", "frame-system/std", "scale-info/std", ]

版本号不用死记,直接参考模板里其他 Pallet 的依赖版本即可。接着写src/lib.rs,这是核心逻辑:

#![cfg_attr(not(feature = "std"), no_std)] pub use pallet::*; #[frame_support::pallet] pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; #[pallet::config] pub trait Config: frame_system::Config {} #[pallet::pallet] pub struct Pallet<T>(_); #[pallet::storage] pub type Proofs<T: Config> = StorageMap< _, Blake2_128Concat, T::Hash, (T::AccountId, T::BlockNumber), >; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event<T: Config> { ProofAdded(T::AccountId, T::Hash, T::BlockNumber), } #[pallet::error] pub enum Error<T> { ProofAlreadyExists, } #[pallet::call] impl<T: Config> Pallet<T> { #[pallet::weight(10_000)] pub fn create_proof( origin: OriginFor<T>, hash: T::Hash, ) -> DispatchResult { let who = ensure_signed(origin)?; ensure!( !Proofs::<T>::contains_key(&hash), Error::<T>::ProofAlreadyExists ); let block_number = frame_system::Pallet::<T>::block_number(); Proofs::<T>::insert(&hash, (who.clone(), block_number)); Self::deposit_event(Event::ProofAdded(who, hash, block_number)); Ok(()) } } }

代码不多,信息密度却不小。StorageMap定义了一个从哈希到“账户+区块高度”的映射;create_proof是链上交易入口,先检查提交人签名,再检查哈希是否已存在,最后写入存储并触发事件。这里的核心思想是:链上业务函数本质上都是在“改状态 + 记录事件”,而已提交的哈希要做幂等校验,否则同一条内容会被重复存证。

4.3 在 Runtime 里挂载并验证

Pallet 写完之后,要让 Runtime 认识它,需要改两处。第一处,在runtime/Cargo.toml的dependencies里加:

pallet-poae = { path = "../pallets/poae", default-features = false }

同时在[features]的std列表中追加:

"pallet-poae/std",

第二处,在runtime/src/lib.rs里加实现和注册:

impl pallet_poae::Config for Runtime { type RuntimeEvent = RuntimeEvent; }

然后在construct_runtime!里注册:

construct_runtime!( pub struct Runtime { System: frame_system, Balances: pallet_balances, Poae: pallet_poae, } );

注意不同模板分支的construct_runtime!写法略有差异,你以自己模板里的现有写法为准,别照抄结构。改完之后重新编译:

cargo build --release

重启开发链,打开 Polkadot.js,在 Extrinsics 页签里选poae.createProof,输入一个哈希值,比如0x12345678,以 Alice 身份提交。然后在 Chain State 里选poae.proofs,输入同样的哈希,如果能看到返回结果里的账户和区块高度,你的第一个业务 Pallet 就已经在链上真实运行了。

5. 无分叉升级:把 Runtime 当成可以热更新的系统

5.1 硬分叉的成本为什么高,而 Substrate 怎么绕开它

传统区块链做协议升级,最让人头疼的就是硬分叉。节点不升级软件、不认新规则,链就会从某个高度分裂成两条,社区分裂、资产重复、生态互相拉扯——这个过程成本极高,政治成本和技术成本都高。而 Substrate 的 Runtime 是 Wasm 形式存在链上的,这给了它一个别的框架羡慕不来的能力:无分叉升级。

简单来说,升级发生时,只需要通过链上交易把新的 Runtime Wasm 写入链上存储,所有节点同步到新块之后,会自动开始运行新逻辑。不需要停机,不需要换软件,节点也不需要逐个手动升级。手机 App 热更新你肯定用过——Substrate 的 Runtime 升级在原理上很像这个,只是“应用”变成了链上的状态转换逻辑。

5.2 从 Sudo 到 Governance:权限的交接

开发阶段你直接用sudoPallet 就可以发起无分叉升级,权限集中在一个账号手里,节奏最快。但主网想要持续的社区信任,就必须把升级权限从 sudo 过渡到治理模块,常见组合是collective(链上委员会)加democracy(公投)。

配置治理时不要照抄 Polkadot 主网参数,小规模应用链的社区体量完全撑不起那么长的周期。给你一份参考参数表:

参数含义应用链建议值大型主网参考值
LaunchPeriod提案进入公投前的公示期1 天28 天
VotingPeriod公投投票窗口1 天7 天
MinimumDeposit提交提案的最低押金1 个代币100 个代币
EnactmentPeriod通过后延迟生效的时间1 天8 天

核心思路是:让“升级前有讨论窗口、升级后有留出时间让工具跟进”的机制存在,但不至于因为周期太长而拖垮迭代。这是我调参时最深的体会——治理参数的本质是节奏控制,不是越高越好。

5.3 一次升级演练的完整步骤

无分叉升级的完整流程并不复杂,但你最好在测试网演练几遍。步骤大概是:

  1. 记录当前 Runtime 的版本号,通常在runtime/src/lib.rs里的spec_version字段,以及链上state.getRuntimeVersion的返回值。
  2. 修改 Runtime 代码,比如给存证 Pallet 加一个删除存证的新函数,确保持续编译通过。
  3. cargo build --release,在target/release/下找到wbuild/目录里的 Wasm 文件。
  4. 在 Polkadot.js 的 Extrinsics 页签,用 sudo 账号调用sudo.sudoUncheckedWeight,内部调用system.setCode,上传新的 Wasm。
  5. 提交后观察区块。通常升级瞬间节点会短暂停顿或延迟出块,之后恢复正常,新的业务函数就能用了。

我自己的习惯是,每次正式升级之前,先启动一个全新的--dev链跑一遍完整流程,确认 Wasm 文件没问题、链能正常出块,再上测试环境。这个习惯帮我在正式环境里少踩了很多坑,强烈建议你也养成。

6. 实测半年后,我总结的 Substrate 避坑清单

6.1 编译期的三个“老朋友”:内存、版本、缓存

Substrate 开发绕不开性能怪兽般的 Rust 编译器。我自己的经验是,除了前面提到的限制并行度和安装 wasm 目标,再补两个实用操作。

第一,锁死依赖版本。Cargo.lock一定要提交到代码仓库,否则团队同学的 CI 环境可能拉到完全不同的依赖版本,跑出一些无法复现的诡异问题。我遇到过一次,本地编译通过、CI 编译报错,最后发现就是依赖版本漂移导致的。

第二,用sccache缓存编译产物。Substrate 项目每次改动 Runtime 都要重新编译,而依赖链路的编译占了大头。配置好 sccache 之后,多次 build 的速度提升非常明显,尤其是反复改 Pallet 代码的场景。安装和配置过程不复杂,在项目根目录cargo build时会自动命中缓存,性价比极高。

6.2 上线后最容易踩到的 Runtime 冷坑

等到链上线、时间跑起来之后,很多问题才会暴露。我梳理三个高频点位。

第一类是存储结构变更导致的迁移问题。Runtime 升级时如果改了存储结构,但没写 Storage Migration,链上旧数据会以一种错误的方式被新逻辑读取,轻则存储混乱,重则把账算错。正确的做法是给 Runtime 增加一个实现OnRuntimeUpgrade的迁移模块,在升级时把旧数据转换为新结构。这事不难,但属于“忘了就炸”的典型。

第二类是事件声明不完整。Pallet 里定义了事件,但在construct_runtime!里没有正确地关联RuntimeEvent,会导致前端订阅不到任何事件,而交易本身却成功了。症状看起来像“函数没生效”,排查却要绕一大圈。所以每次要接前端时,先做一次事件验证再让前端介入,能省掉很多联调时间。

第三类是 Weight 设置太小。如果你给create_proof这类函数标注的 Weight 低于实际执行开销,节点在执行时会判定交易超重并回滚状态,表现依然是“函数没生效”。正确做法是用#[pallet::weight]留足余量,或者用基准测试工具生成准确的 Weight。别为了好看写一个不切实际的小值,那是在给自己埋雷。

6.3 业务设计里“看着没事其实埋雷”的做法

还有一种坑,不在编译期、不在运行时,而在业务设计层面。最常见的两个,我建议每个新手都提前知道。

第一,无上限地往Vec存储里追加数据。比如某个 Pallet 允许用户无限提交证据,每次都在同一个Vec后面 append,时间一长,这个存储键的值越来越大,每个区块的读取和验证成本都被拽着上涨,最终把链拖垮。解决思路是分页存储,或者设置总量上限,任何情况下都别让一个存储项“无限增长”。

第二,在on_initialize里做大计算。前面提过,on_initialize是在出块路径上的关键逻辑,它的执行时间是区块生产时间的一部分。如果你在这里做了重的排序、聚合或者大量存储遍历,出块时间会被明显拉长,节点之间的区块同步也会越来越不稳定。我见过一个项目因为这个原因,从 6 秒出块一路拖到 30 多秒,最后靠把逻辑挪到 Offchain Worker 才救回来。

总结成一句话:业务设计时要戴着一副“这段代码跑在每 6 秒只执行一次、所有节点都必须严格执行的关键路径上”的眼镜去看,很多坑就能提前避开。

7. 不只是单链:把 Substrate 链接入更大的生态

7.1 Cumulus 与平行链:从独立链到中继链

Substrate 单独用已经很强了,但它还有一个大招:把一条 Substrate 链变成 Polkadot 生态里的平行链。实现这一层的是 Cumulus,它相当于 Substrate 之上的一层扩展,给链引入了 Collator(收集人)节点的概念。Collator 负责收集并提交平行链区块,中继链上的验证人负责对平行链区块进行最终验证和确认。

接入平行链就要提到插槽拍卖。Polkadot 的平行链插槽是有限资源,项目方需要质押通过拍卖获取插槽使用权,租期固定,到期可以续租。对应用链团队来说,插槽机制意味着你需要提前规划好“为什么要连进中继链”,而不是纯追热点。技术关键词还有 ParaID、HRMP 通道、XCM 消息,这些都是做跨链交互时绕不开的概念。

7.2 共享安全性:一条链的成本账

作为独立链,你得自己维护或购买足够的验证人,才能保证共识安全,这个成本对小团队极不友好。而平行链走的是“共享安全性”路线:中继链的验证人集合为你的平行链出块兜底,你不需要自己养大规模验证人集群。相当于你租用了一层已经被验证过的安全保障,自己只需要维护少数 Collator。

当然,共享安全不是免单。插槽成本、Collator 运维成本、跨链消息费率,都是要考虑的开销。我的建议是:如果业务完全不需要跨链互操作,独立 Substrate 链完全够用;如果需要和 Polkadot 生态里的其他链互通,那平行链路线带来的确定性收益是值得的。这本质上是一笔工程账,而不是一笔信仰账。

7.3 想系统学 Substrate,路线图可以这样排

最后给想深入学习的同学一个我验证过的路线图。先把 node-template 跑熟,内容包括区块、账户、交易、Polkadot.js 的操作;然后开始写自己的 Pallet,同时补上 Pallet 测试的写法,比如用mock构造测试环境;下一步理解 Weight 和费用模型,把它跟区块性能联系起来;之后再研究 Cumulus 与 XCM,考虑是否要接入平行链;最后,如果你需要智能合约能力,再去研究 EVM Pallet 或 ink! 合约。

官方文档依然是信息密度最高的入口,模板仓库和 Polkadot.js 是日常最常用工具。很多人读不下去官方文档,觉得慢,但以我的体会来说,官方的概念拆解和代码示例是唯一不会过时的“源头活水”,社区博客更多是给你突破某个具体卡点的灵感。把这两者结合,你会少走很多弯路。

回顾这大半年,我踩过的最大一个弯,就是一开始试图把 staking、governance、智能合约全部塞进第一条链里,结果光是编译就劝退了自己两次。后来老老实实从空模板链起步,写了一个最简单的存证 Pallet,才真正理解了 Substrate 的布局逻辑。如果你也要上手这个框架,我的建议非常简单直接:别贪多,让一条空模板链先跑起来,再给链加一个小业务跑通,然后再谈升级、谈平行链。Substrate 的复杂度是客观存在的,但它的设计也在时刻提醒你一个道理——先把地基铺稳,再往上面造房子。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询