Address
304 North Cardinal St.
Dorchester Center, MA 02124
Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM
注意 – 这是我发布的更广泛的微课程的一部分。但是,为了这个特殊的 Hackernoon 版本,我已经尽可能地浓缩了它。我删掉了一些细节——如果你想阅读它,请务必关注我的 微信公众号(BlockChain888) 并 dm 我!
今天,我将向你展示如何基于 Substrate 创建一个“About Me”区块链模块。它将允许任何用户在链上分配关于他们自己的信息。
没错,我们不再交易金钱——而是以完全不同的方式创造价值。
从本质上讲,您将建立和开发您自己的区块链——下面提供的所有工具和说明均由您自己提供。
事不宜迟,让我们开始吧!
首先——请记住,您应该有一些开发和/或软件开发经验。您无需成为专家,但在开始之前您应该具备以下条件:
要引用Substrate 文档,
区块链是一个去中心化的分类账,它在一系列区块中记录信息。块中包含的信息是一组有序的指令,可能会导致状态发生变化。
换句话说,区块链创建了一个链接的记录集合。通常,我们习惯于看到这是为了跟踪货币交易而实施的。
但是,情况已不再如此。有趣的是,就像我们可以验证货币数据/余额一样,我们可以将这些相同的概念应用于我们想要的几乎任何类型的数据。
区块链本质上是状态管理机器。换句话说,他们跟踪谁更改了链的状态、时间以及它是否有效。当有人希望更改状态时,他们会提交一个请求,通常称为交易,该请求会更改链的状态——只有在其获得批准的情况下!
在区块链上开发是困难的。区块链开发要么需要您花时间学习要构建的特定协议,要么从头开始构建全新的链。
Substrate 通过提供一个框架来缓解这种情况,使从头开始构建自己的区块链变得更加容易。
他们负责处理困难的部分,因此您可以专注于有趣的部分——应用程序。
从架构的角度来看,Substrate 链也是可互操作的。可以很容易地执行跨链操作(请参阅Polkadot 如何工作以了解更多信息)。
它的构建方式还使其能够适应未来——在这个不断发展的行业中,这非常重要。
例如,如果您需要与以太坊通信,但还需要利用您自己的自定义链上功能,您可以在 Substrate 上构建它。
由于我们将使用 Rust 来开发我们的自定义托盘,因此请确保您已安装 Rust 工具链。如果您需要安装 Rust 的指南,请访问此处。
前端模板是一个宝贵的资源,它可以让我们以最小的干扰快速验证和测试我们的 Substrate 链。
# Clone the repository
git clone https://github.com/substrate-developer-hub/substrate-front-end-template.git
cd substrate-front-end-template
yarn install
# To start it
yarn start
更多信息可以在repo 链接中找到。
资源管理器也是深入了解节点操作的好方法。去:
在那里,您可以单击左上角,并确保您选择了本地节点。
您应该已经从上一步安装了 Rust 工具集。如果没有,请继续关注那些然后回到这里。请记住,这可能需要一些时间来构建和运行,所以请在等待时随意喝杯咖啡或阅读一篇很棒的 Medium 文章。
请记住,这个 repo 是原始基板节点模板的克隆,但进行了一些调整,以便在我们开始托盘开发时更容易使用。
git clone https://github.com/CrackTheCode016/substrate-node-template-course.git
cd substrate-node-template-course/
# this will build and launch the node
# if you wish to just build it, then run cargo build --release
cargo run --release -- --dev
一旦构建完成,我们就有多种方式来交互和运行我们的节点。
要立即获得确保我们的链启动的结果,请继续访问 Polkadot.js 资源管理器:
此链接将自动连接到您的localhost
节点,您可以在其中查看各种链统计信息。例如,喜欢帐户及其余额!您也可以在链上查看区块和事件,稍后会派上用场。好好看看周围吧!
我们可以做的另一件很酷的事情是通过检查节点下的存储实例来主动观察状态变化Developer > Chain State
。在这里,您可以获取先前在运行时中由托盘定义的各种存储映射或值的状态。这些称为状态查询。
例如,您可以选择时间戳状态查询并单击最右侧的加号按钮以获取节点的时间:
您也可以通过原始十六进制键简单地搜索存储,但是大多数时候通过相应的托盘执行状态查询更容易。
substrate-frontend-template
虽然资源管理器是执行一般功能的好地方,但让我们看看我们可以用substrate-node-template
我们之前安装的那个做些什么。
导航到您安装它的位置,然后运行yarn start
. 一旦它启动,我应该会看到如下内容:
瞧!您现在拥有大部分功能并通过 GUI 访问您的链。您可以使用transfer
托盘在账户之间转移货币,通过无叉升级升级您的运行时,并与托盘交互以直接修改状态。
随意在这里玩,并尽可能多地尝试这个界面。只要这样做,你就会学到很多东西。如果您在下拉列表中注意到 – 其中一个托盘被称为templateModule
.
在下一节中,我们将修改并检查这个托盘,使其成为我们自己的。
如果您认为我们在最后几节中掌握了技术知识,那么这就是真正要失败的地方。
这个托盘将非常简单。它将允许任何用户在区块链上公开分配关于他们自己或“关于我”的信息,类似于 Discord 之类的东西。
事不宜迟,让我们快速创建有史以来最好的区块链模块。
substrate-node-template
对我们来说幸运的是,它substrate-node-template
为我们提供了一个模板托盘,可以直接使用。
如果您还没有,请确保按照我们在以下部分中讨论的方式克隆和构建节点模板:
导航到您的节点包含的托盘模板,然后在您喜欢的代码编辑器中打开目录:
# Open this in your coding editor.
cd substrate-node-template/pallets/template
进入后,您会发现它看起来就像您的标准 Rust Cargo 板条箱——因为它是。Pallets 就像 Rust crates 一样运行,并且可以这样共享和分发。
进入后,您应该会看到如下结构:
.
├── Cargo.toml
├── README.md
└── src
├── benchmarking.rs
├── lib.rs
├── mock.rs
└── tests.rs
1 directory, 6 files
默认情况下,您会注意到我们已经有示例代码和测试。 benchmarking.rs
处理在链上运行的上下文中测量函数的性能。这是为了帮助确保您不会使用使用过多资源的函数来停止链。
对于我们的托盘,我们基本上会将一些用户信息与特定用户相关联。这里的用户通常被定义为AccountId
,并且是加密地址。我们的大脑很难记住 64 个字符的字符串,所以让我们通过给它一个人类可读的结构来解决这个问题!
转到lib.rs
,在那里您会看到托盘的核心。这是我们要放置业务逻辑、错误和事件的地方。你会注意到这个Config
特性——这就是我们稍后在运行时中实现托盘的方式。
/// Configure the pallet by specifying the parameters and types on which it depends.
#[pallet::config]
pub trait Config: frame_system::Config {
/// Because this pallet emits events, it depends on the runtime's definition of an event.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
}
在这个特征下,让我们定义一个用户应该看起来如何的结构(UserInfo
):
#[derive(Encode, Decode, Clone, PartialEq)]
pub struct UserInfo {
/// Username, stored as an array of bytes.
pub username: Vec<u8>,
/// Number id of the user.
pub id: i64,
/// The "About Me" section of the user.
pub about_me: Vec<u8>,
}
需要注意的重要一点是使用Vec<u8>
– 这本质上是我们告诉 Substrate 运行时它是我们期望的字符串的方式。通常,我们会确保这个有界有一个限制,我们稍后会这样做。
我们还用几个 trait 宏 ( Encode, Decode, Clone, PartialEq, Default, TypeInfo
) 实现了这个结构,以便稍后在我们的调度函数中处理它。
StorageMap
定义了自定义数据结构后,我们现在可以开始添加存储实现,以告诉 Substrate 存储与用户关联的自定义结构。
Substrate 有一些方法可以做到这一点。但是,对于本课程,我们将使用StorageMap
. 它非常适合我们的用例,并且易于使用。
在我们的结构下,继续定义我们的StorageMap
:
/// Mapping of account ids to UserInfo.
#[pallet::storage]
#[pallet::getter(fn info)]
pub type AccountIdToUserInfo<T: Config> =
StorageMap<_, Blake2_128Concat, T::AccountId, UserInfo, ValueQuery>;
就这样,我们为我们的用户定义了一个存储映射。当新用户注册他们的信息时,他们将通过此映射直接添加到区块链的存储中。
该[pallet::getter(fn info)]
宏确保了一个 getter 方法,这意味着我们可以简单地通过知道用户的地址 ( AccountId
) 来获取存储的后续信息。
错误是不言自明的——当出现灾难性错误时,我们可以返回一个信息性错误,让开发人员知道他们在使用我们的托盘时是如何搞砸的。
Substrate 中的事件与 Solidity 中的事件非常相似。
它们本质上是指示特定操作何时完成的通知。这对于前端应用非常有用——例如,一旦交易被确认,您就可以显示应用内通知。
让我们继续在包含enum Event
的用户注册时添加一个事件:
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// Indicates a user has been registered.
UserCreated { user: T::AccountId },
}
我们将在下一节之后重新讨论错误,因为我们将直接修改我们的托盘配置。去
提前并将其添加到您的enum Error
:
// Errors inform users that something went wrong.
#[pallet::error]
pub enum Error<T> {
AboutMeTooLong,
}
最后,最后一步。现在我们已经初始化了所有的数据/存储结构、事件和错误,我们可以将它们组合成一个大而快乐的调度函数!
作为原始substrate-node-template
状态,可调度函数是:
可调度函数允许用户与托盘交互并调用状态更改。这些功能具体化为“外在”,通常与交易进行比较。Dispatchable 函数必须使用权重进行注释,并且必须返回 DispatchResult。
需要注意的重要一点是我们如何使用一个新词——外在词。还记得我们说过区块链是一个状态管理机器,我们发送交易来改变状态吗?
外在因素就是——改变区块链的状态。它们与交易的概念相同,但我们可以自定义它们以包含我们喜欢的任何内容。
对于我们的用例,我们希望更新链的状态以更改特定用户信息的状态。
#[pallet::call]
impl<T: Config> Pallet<T> {
// Dispatchable calls go here!
// Register a new user and change the state of the chain.
#[pallet::weight(10_000 + T::DbWeight::get().writes(1).ref_time())]
pub fn register_user(
origin: OriginFor<T>,
username: Vec<u8>,
id: i64,
about_me: Vec<u8>,
) -> DispatchResult {
// Gets the caller or signer of the function.
let sender = ensure_signed(origin)?;
// Define a new user in accordance to UserInfo.
let new_user = UserInfo { username, id, about_me };
// Change the state of our storage mapping by adding user info to our sender AccountId.
<AccountIdToUserInfo<T>>::insert(&sender, new_user);
// Emit an event indicating the user is now created and registered.
Self::deposit_event(Event::<T>::UserCreated { user: sender });
Ok(())
}
}
就这样,我们完成了使托盘正常运行所需的大部分工作。接下来,让我们快速浏览一下运行时,看看我们的托盘是如何配置并实际添加 到区块链实例中的。
我们几乎已经完成了定制托盘所需的一切。但是,重要的是要了解它是如何在运行时使用的。
继续并导航到您的runtime/src/lib.rs
内部节点模板并在您的代码编辑器中打开它。如果你滚动,你会看到它作为一个普通的 Rust crate 导入,和其他任何一样:
/// Import the template pallet.
pub use pallet_template;
如果您还记得托盘中的 Config 特征 – 这就是您实现它的地方。任何自定义变量或配置选项都在运行时初始化:
/// Configure the pallet-template in pallets/template.
impl pallet_template::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
}
最后,如果您查看construct_runtime!
宏,您会看到许多托盘,其中包括我们的自定义托盘:
// Create the runtime by composing the FRAME pallets that were previously configured.
construct_runtime!(
pub struct Runtime
where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
...
// Include the custom logic from the pallet-template in the runtime.
TemplateModule: pallet_template,
}
);
aaaanddd 这是包装!现在,让我们构建和测试这个东西!
现在,终于到了构建和运行配置了自定义模块的 Substrate 节点的时候了。
导航到substrate-node-template-course
并运行以下命令来构建和运行节点:
cd substrate-node-template-course/
cargo run --release -- --dev
这可能需要一段时间,所以请坐下来,放松一下,看着您的节点自行构建。
构建并运行节点后,您应该会看到类似于以下内容的输出:
如果你看到了,恭喜,你有自己的自定义区块链在工作!
接下来,让我们从之前启动substrate-front-end
模板:
cd substrate-frontend-template/
yarn start
在您的网络浏览器中启动它,然后向下滚动到Pallet Interactor。 单击下拉菜单,您应该会看到带有我们register_user
之前定义的可调度函数的 templateModule。
Bytes
本质上只是一个字符串,所以我们可以相应地填写它。请记住以下三个选项:
我们可以进行三种不同类型的交易或外部交易。未签名不需要签名,因此对网络来说更危险。但是,我们的可调度文件中的那个已签名。
填写它,点击签名,您应该会看到以下内容。请注意右侧的事件,UserCreated
,以及我们刚刚为其分配身份的用户的地址!
复制那个地址——因为现在我们要查询它以确保它通过了。单击交互类型中的查询,然后templateModule
再次选择,然后accountIdToUserInfo
。将地址粘贴到字段中,然后点击查询:
虽然值是十六进制的,但我们可以清楚地看到我们确实有一个用户配置文件在链上!
恭喜!您刚刚完全使用 web3 技术创建了一个非常酷、超级简单的用户配置文件系统,从而创造了历史。