本文为 PolkaWorld 特约技术专栏「Substrate 的 RPC 客户端」,作者为波卡技术大使 John。
距离 上一篇入门篇 已经过去一段时间了,Substrate 和 Subxt 也有了很多的进展。上一次,我们用 Subxt 成功给 Substrate 提交了一个交易。但是大家知道,基于 Substrate 开发的项目很多情况下是从 node-template 中创建的,而这次我们就给未经修改的 node-template 节点尝试提交一个交易。我们使用的所有代码都基于 alpha3 版本(substrate,node-template 和 subxt)
首先,结合目前 Subxt 给出的例子,我们来复习一下最新的能给 Substrate 提交交易的代码,注意 tokio 已经被 async_std 取代了。
use sp_keyring::AccountKeyring; use substrate_subxt::{ balances, system::System, DefaultNodeRuntime as Runtime, ExtrinsicSuccess, }; type AccountId = as System>::AccountId; type Balance = as balances::Balances>::Balance; fn main() { let result: Result, Box<dyn std::error::Error + 'static>> = async_std::task::block_on(async move { env_logger::init(); let signer = AccountKeyring::Alice.pair(); let dest = AccountKeyring::Bob.to_account_id(); let cli = substrate_subxt::ClientBuilder::::new() .build() .await?; let xt = cli.xt(signer, None).await?; let xt_result = xt .watch() .submit(balances::transfer::(dest.clone().into(), 10_000)) .await?; Ok(xt_result) }); match result { Ok(extrinsic_success) => { match extrinsic_success .find_event::<(AccountId, AccountId, Balance)>( "Balances", "Transfer", ) { Some(Ok((_from,_to, value))) => { println!("Balance transfer success: value: {:?}", value) } Some(Err(err)) => println!("Failed to decode code hash: {}", err), None => println!("Failed to find Balances::Transfer Event"), } } Err(err) => println!("Error: {:?}", err), } }
前半部分我们看到新出现了一个 watch 函数,它可以监视并返回提交交易的上链后的结果 ExtrinsicSuccess。match result
之后的后半部分则是在 ExtrinsicSuccess 中寻找 Transfer 这一 Event,并将转账金额打出。
注意,使用的 Subxt (https://github.com/paritytech/substrate-subxt/blob/bee03e25df78bdb6d4a572e33846c4458bc529e4) 支持的是 alpha3 版的 Substrate
那么用这段程序向 node-template 节点发送转账会发生什么呢?我们会得到这样一个错误:
Err(Rpc(Request(Error { code: MethodError(1002), message: "Verification Error: Execution(ApiError(\"Could not convert parameter `tx` between node and runtime: No such variant in enum MultiSignature\"))", data: Some(String("RuntimeApi(\"Execution(ApiError(\\\"Could not convert parameter `tx` between node and runtime: No such variant in enum MultiSignature\\\"))\")")) })))
实际上,由于 Substrate 的可配置性,我们也需要在 Subxt 中配置正确的 Runtime。比如说这里我们只需要转账的情况,那么需要为这个 Runtime 实现 system::System 和 balances::Balances。对于未经修改的 node-template,我们可以观察它的 lib.rs,然后如下实现一个定制的 Runtime: MyNodeRuntime
use substrate_subxt::{ balances::{ AccountData, Balances, }, system::System, }; use sp_runtime::{ generic::Header, traits::{ BlakeTwo256, IdentifyAccount, Verify, }, MultiSignature, OpaqueExtrinsic, }; #[derive(Debug, Clone, Eq, PartialEq)] pub struct MyNodeRuntime; impl System for MyNodeRuntime { type Index = u32; type BlockNumber = u32; type Hash = sp_core::H256; type Hashing = BlakeTwo256; type AccountId = <as Verify>::Signer as IdentifyAccount>::AccountId; type Address = Self::AccountId; type Header = Header; type Extrinsic = OpaqueExtrinsic; type AccountData = AccountData<<Self as Balances>::Balance>; } impl Balances for MyNodeRuntime { type Balance = u128; }
通过比较可以得知,原来 Substrate 和 node-template 的默认 Address 类型并不相同。这样只要把之前代码中的 DefaultNodeRuntime 换成 MyNodeRuntime,程序就可以成功给 node-template 使用了。
但是这样做有一个很大的劣势,就是每次 node-template 做了修改,这里也需要做相应的修改,非常麻烦。从工程开发的角度来说,如果依赖能够自动帮我们搞定这些类型,那无论对于开发还是维护都是一个好消息。一个直观的想法就是直接把 node_template_runtime::Runtime 中的类型照搬过来。
use subxt::{ balances::Balances, system::System, ExtrinsicSuccess, }; use node_template_runtime::Runtime; use sp_runtime::OpaqueExtrinsic; #[derive(Debug, Clone, Eq, PartialEq)] pub struct MyNodeRuntime; impl System for MyNodeRuntime { type Index = as frame_system::Trait>::Index; type BlockNumber = as frame_system::Trait>::BlockNumber; type Hash = as frame_system::Trait>::Hash; type Hashing = as frame_system::Trait>::Hashing; type AccountId = as frame_system::Trait>::AccountId; type Address = Self::AccountId; type Header = as frame_system::Trait>::Header; type Extrinsic = OpaqueExtrinsic; type AccountData = as frame_system::Trait>::AccountData; } impl Balances for MyNodeRuntime { type Balance = as pallet_balances::Trait>::Balance; }
这样一来,我们就可以再也不用每次 node-template 升级的时候都要一堆复制粘贴啦。
这一讲带大家使用 Subxt 给 node-template 节点实现了一个可以发起转账的 RPC 客户端,也针对开发和运维的问题提出了一个可能的优化方案,希望大家自己试试。再次提醒大家要注意版本问题,关于依赖,这里面还是有些坑的,祝大家蹚坑愉快~
https://github.com/paritytech/substrate
https://github.com/paritytech/polkadot
https://bootcamp.web3.foundation/
相关内容:
Substrate 的 RPC 客户端之 Subxt 入门篇
开发了两年 Dapp 二层网络后,我转投了 Substrate 阵营
扫码关注公众号,回复 “1” 加入波卡群
关注 PolkaWorld
发现 Web 3.0 时代新机遇
点个 “在看” 再走吧!
声明:本内容为作者独立观点,不代表 CoinVoice 立场,且不构成投资建议,请谨慎对待,如需报道或加入交流群,请联系微信:VOICE-V。
简介:波卡(Polkadot)第一中文社区,带你寻找 Web 3.0 时代新机遇!
评论0条