查看原文
其他

Subdev 讨论 | Substrate 不同环境的写法和代码编译

一块链习 一块Plus社区 2020-11-11

Parity 科技公司研发的 Substrate 框架,让我们拥有了可以几乎不受限制的快速的开发出一个完整、高性能、安全的区块链项目。
 
今年6月份,一块链习与 Polkadot 社区大使陈锡亮老师倾力打造了《Substrate快速入门与实战开发》课程。


目前第三期课程已经进行到第四周,同学们平常会在班级群将学习中遇到的难题或不解向老师、助教提问、讨论。

另外,每周六晚 8 点,都会进行《Substrate 快速入门与开发实战》开发课的内容知识拓展——作业点评会,助教们会从解题思路、以及本组学员的作业问题分析每一课的知识点。

现在将第三周班级群日常优质的讨论内容和黎倚杭助教对第 1、2、3 课的作业点评分享给大家。


Q&A

   具体内容如下:



 01 


Q:红领巾@工程师:我有一个问题,每一课示例的代码都是怎么开始跑的呢?

是全部重新git clone下来,重新跑过还是丢进第一课的subs的文件夹再跑呢?


我新建了猫的项目 ,还是拉了一大片东西,而且还报错。

 

A:陈锡亮@讲师:可以直接丢进第一课的文件夹里跑。

 

Q:红领巾@工程师:丢进bin里新建一个kitty,比如这样么?

 

A:陈锡亮@讲师:我指的是建立的项目里面跑,不是Substrate里面。也可以直接clone下来进去跑。

 

Q:红领巾@工程师:我clone下来,又down了很多其他东西,然后编译又报错。

              
似乎 kitty胡乱操作一通后,原来前面课的Substrate文件夹里也不能直接跑了。
 
A:陈锡亮@讲师:先make build-wasm。
 
A:红领巾@工程师:只有make build命令,
就是build: 
 SKIP_WASM_BUILD= cargo build 
 
A:黄志光@助教:
WASM_BUILD_TYPE=release cargo build
第一次编译线运行一下这个,编译一下wasm。
              
make build-wasm
应该也是有的。
 

 02 

Q:何怡@电商:调用crate的方法,应该直接写成:
System::Module::block_number();
为什么这里前前还要加上尖括号呢?
             
还有个问题:像这个KittiesCount是一个u32类型的变量,在自定义Module的宏模块,我们用了它的put方法,我猜想是这种u32类型自带的方法。我这里能否用下面的方法来替代它呢?
Self::KittiesCount += 1
               
A:黎倚杭@助教:看经过这个定义,Self::KittiesCount也是调用了get方法,无法赋值通过Self::KittiesCount当左值使用。
              
Q:何怡@电商:所以这个是个私有变量,不能直接加对吧,只能通过pub的方法来操作。
polkadot.js.org这个网站提供的这些功能模块,应该怎么理解它的作用?能否帮忙看下我的理解是不是正确的?
 
A:黎倚杭@助教:嗯嗯,只能通过put操作。
 
Q:何怡@电商KittiesCount本身就是一个变量吧,而且声明是pub的,应该是可以直接修改值?
            
A:黎倚杭@助教:是变量,试试看。
 
 
 03 

Q:何怡@电商:polkadot.js.org这个网站提供的这些功能模块,应该怎么理解它的作用?能否帮忙看下我的理解是不是正确的?
                
A:黎倚杭@助教:javascript是调用Substrate的node js api。chain state是查看存储状态的。exrinsics是执行函数的。
 
Q:何怡@电商:chain state查看存储状态的意思是,只在定义在自定义存储里边的pub变量才可以看得到,其它地方的函数变量都是看不到的,是吧?
 
A:黎倚杭@助教:目前看应该是定义在decl_storage里的pub变量。
 
Q:何怡@电商:“exrinsics是执行函数的”的意思是,可以在extrisics里,手动调用任何定义在decl_module里边的dispatchable 函数,是吗?
 
A:黎倚杭@助教:runtime的decl_module里面的,还有系统模块的一些函数。
 
Q:何怡@电商:嗯嗯,就是这里展示一些我们可以调用的函数,我们可以通过这个可视化页面调用它,并在这里看到调用后的结果。这个模块是干这个用的,是吗?
 
A:黎倚杭@助教:是的。
 
Q:何怡@电商:「Li Smith:javascript是调用Substrate的node js api」
- - - - - - - - - - - - - - -
我们好像没有下载Node.js在我们的环境里。

这个Node.js是干什么用的呢?我们用的不是Rust编的程吗?怎么又涉及到javascript了呢?
 
A:黎倚杭@助教:javascript在https://polkadot.js.org中,通过Substrate node sdk访问Substrate网络用的。
 
 
 04 

Q:何怡@电商:作业点评的时候,你有提到说是如果代码里边用了std标准库,wasm就不能编译出来吗?这个能解释一下不?
 
 A:黎倚杭@助教:wasm环境不支持std标准库,所以runtime 用到的所有库都必须支持没有std的编译环境。来源是第二期锡亮老师说的。
 
A:陈威@区块链从业者:Substrate的功能都分std和nostd, 还有 test,  不同的环境写法统一, 但编译出来的代码不一样。要打印账户, 不同的环境最终执行的代码不一样。
                
std的环境能打印, nostd 环境什么都不做。

还有这个, 只有单元测试的时候才会编译。不是单元测试则编译出来的二进制里面没有这些逻辑。
                
std组合test的情况才有。std组合非test则没有。nostd显然更没有了。
runtime 里面加了 #![cfg_attr(not(feature = "std"), no_std)] 
 
后面想使用rust里面的result, 
nostd:  pub use core::result;
std   :   pub use std::result;
 
所以为了统一就搞了个项目primitives/std
最后rumtime中最终写法是    use sp-std::result;


Q:陈威@区块链从业者:但是为什么,  runtime 运行的是native的时候, 能使用std的功能,  运行的是wasm的时候不能?不是整个runtime都是nostd的吗?
 
在native的 runtime运行的时候是能看到打印的, 就是执行的下面代码:
 #[cfg(feature = "std")]
fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
   let s = self.to_ss58check();
   write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&self.0), &s[0..8])
}



A:陈锡亮@讲师:Substrate 有两个executor ,wasm 和 native。wasm是no std,native是std。

wasm executor 使用链上的wasm runtime,可以无缝升级,native executor升级需要升级节点。

native executor 和wasm runtime都一个版本,版本兼容的时候可以用native,不然只能用wasm。
 
Q:陈威@区块链从业者:整个lib.rs 虽然写的是#![cfg_attr(not(feature = "std"), no_std)]
 
但是下面有改为std的地方.
// Make the WASM binary available.
#[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
 
也就是runtime确实不是一律nostd。


 05 

Q:何怡@电商:老师的意思是,这里的default  feature是配置给native用的,平常runtime就是把这个default禁掉,对吗?
                
A:陈锡亮@讲师:对,native也是测试时候要用到的。
 
Q:何怡@电商:release的时候用的native,也不支持std是吗?
 
A:陈锡亮@讲师:native runtime和wasm runtime执行的结果必须一致才能达成共识,所以native runtime不能用std中会影响结果的功能,比如随机数。
 
A:陈威@区块链从业者:release和debug是另外的维度的东西。跟 std nostd 正交。
 
Q:何怡@电商:那就是测试的时候支持std是为了方便测试人员使用。把std写成default,就是为了用的时候,不用一个个再编上去,加个 default=true的参数就收放自如了。
 


 06 

Q:何怡@电商:
                             
老师,在这个 Kitty的struct结构里,什么内容都没有实现,只传入了一个DNA数组参数。

那么,在下面的unwrap().0,获取的是传入参数的值?unwrap截回来是some的类型,unwrap之后是Kitty类型,Kitty.0是它的传入参数值?
 
A:黎倚杭@助教:Kitty是元组,Kitty.0访问元组第0个元素。
 
Q:何怡@电商:我听老师的视频里,说是[u8;16]是一个定义数组的格式。但第0个值也不对啊,上下文看起来是整一条DNA的信息都有,而不是DNA的第一个字节。这个地方有点不明白。
 
A:陈威@区块链从业者:Kitty.0[0]才是第一个字节。
 
Q:何怡@电商:嗯嗯,那unwrap拿回来的是个啥?
 
A:黎倚杭@助教:([u8;16])
 
Q:何怡@电商:只拿回来一个传入参数,是吗?
 
A:黎倚杭@助教:传入的是[u8;16]数组,返回的是([u8;16])元组格式,多了一层封装。
 
Q:何怡@电商:嗯嗯,所以最后面只有0位元素,后面都是没有的,因为只有一个传入参数。如果有两种传入参数,比如
 Kitty( a: pub[u8;16], b: u32)
 
那么,我使用  Kitty.unwrap().0 =a
Kitty.unwrap().1 = b
这样理解对吗?
 
A:黎倚杭@助教:元组不能命名成员的吧,Kitty( pub[u8;16], u32)   那么,使用下面语句是对了
Kitty.unwrap().0 =a
Kitty.unwrap().1 = b
 
A:陈威@区块链从业者:struct  Kitty{a: pub[u8;16], b: u32}   这样可以。
 
 
 07 

Q:何怡@电商:为什么这个Kitty的struct后面,跟了一个小括号?这是个什么语法?
               
难道就是助教说的,Kitty结构体内,只有一个没有名字的元组成员。实际上这个结构体应该写成:
struct Kitty{
 
   pub ([u8;16]);//定义了一个无名元组成员?
}
 
只是因为只有一行代码,所以连花括号也省去了?


A:黎倚杭@助教:花括号声明结构体,圆括号声明元组。不是省略的问题。
 
Q:何怡@电商:所以是这个Kitty结构体内是定义了一个元组成员,是吗?
 
A:黎倚杭@助教:是定义了Kitty的结构就是元组,有一个数组成员。
 
Q:何怡@电商:这个助教有语法资料推荐吗?我看到的struct都是复合类型,在花括号里写成员,这种没有见过。刚搜索了一下,没有找到资料。
 
A:黎倚杭@助教:
https://doc.rust-lang.org/stable/rust-by-example/primitives/tuples.html
 
Q:何怡@电商:那如果我定义 Kitty是一种 map或者string,我可以这样写吗?
struct Kitty map();
struct Kitty String;
 
A:黎倚杭@助教:不可以,struct只支持声明结构体和元组结构,下面文档只有这两种用法  https://doc.rust-lang.org/book/ch05-01-defining-structs.html?highlight=struct#defining-and-instantiating-structs
 
 
 08 

Q:陈威@区块链从业者:
fn transfer(
   source: &AccountId,
   dest: &AccountId,
   value: Self::Balance,
   existence_requirement: ExistenceRequirement,
) -> DispatchResult;
 
这个新的定义, 让我想到一个问题, 要是买了猫, 然后销户了怎么办? 
 
往账户里再打钱, 创建出原来的账户就ok了吧。
 
A:陈锡亮@讲师:对。
 
Q:陈威@区块链从业者:请问个问题,我看有些pallet都由decl_storage!生成了一个DefaultInstance和16个Instance。对于pallet-balances来说, DefaultInstance是否就是链上的主币.。
 
             
 
A : 陈锡亮@讲师:一般是的,但你真想拿Instance7当主币也没人能拦着你。
 
 
 09 

Q:李示佳@程序员:在Cargo.toml里增加rand依赖,编译不过。
[dependencies]
futures = '0.3.1'
log = '0.4.8'
parking_lot = '0.9.0'
tokio = '0.1.22'
trie-root = '0.15.2'
rand = "0.7.2"
 
知道了,要clean后重新build。
 
runtime里面无法用std的?
 
A:陈锡亮@讲师:嗯wasm runtime 不能用std。
 
  
 10 

Q:于超@自由职业:问个字符串和u8的转换问题。我百度的,下面的代码应该可以执行啊,不明白哪里错了?
 let test:u8= "11100100".to_string().parse::<u8>().unwrap();
 println!("test is ={}", test);
 
A:周洋@助教:什么错误?
 
Q:于超@自由职业: let test_32:u32= "11100100".to_string().parse::<u32>().unwrap();
 println!("test u32 is ={}", test_32);
 
 let test_8:u8= "11100100".to_string().parse::<u8>().unwrap();
 println!("test is ={}", test_8);
 
Finished dev [unoptimized + debuginfo] target(s) in 0.54s
     Running `target/debug/playground`
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ParseIntError { kind: Overflow }', src/libcore/result.rs:1165:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
Standard Outputtest u32 is =11100100
 
A:周洋@助教:"11100100" 按照10进制 parse 的,u8 会越界。


 11 

Q:何怡@电商:
              
请问为什么这个是有2个冒号呢?这是个什么用法?这个T不是代表泛型吗?为什么可以直接当一个crate来使用?
 
A:陈锡亮@讲师:T是一个泛型参数,别的地方有限制T: Trait,所以可以使用Trait里面定制的类型。Trait: system::Trait所以可以用里面定义的AccountId。
 
Q:何怡@电商
              
Trait里边只定义了KittyIndex一个类型,没有看见AccountId呢
 
A:陈威@区块链从业者:trait聚合了system trait的accountid。
 
Q:何怡@电商:
              
是这里的原因吗?
 
A:陈锡亮@讲师:差不多吧,你要看Substrate frame system 里面的代码。
 
Q: 何怡@电商:Substrate frame system的代码在哪里看?
 
A: 陈锡亮@讲师:
https://github.com/paritytech/substrate
 
 
 12 

Q: 李示佳@程序员:请教一个问题,加入我构建的公链,如何通过Defi,让普通用户用法币或ETH获取公链上的稳定币?或者,将链上的稳定币变现为法币或ETH?公链,是否需要在Defi那里有抵押金,Staking?
 
假如,类似forkPolkadot,也就有了稳定币,但这个稳定币在其它Defi链上是无法评估价值的,需要两者之间有公认的锚定加密货币(BTC/ETH/USDT )吗?
 
A: 陈锡亮@讲师:看具体需求,最简单的就是一个DEX,用户可以用其他货币购买。稳定币的话就要看稳定的机制了,比如MakerDAO的模式,就需要抵押,Acala就是做这个的。
 
Q: 李示佳@程序员:这个抵押其实就是现实中的银行储备金,对吧?
 
A: 陈锡亮@讲师:稳定币一般都是锚定某个法币,所以价值还是比较好评估的,最多因为脱锚的风险打个折扣,或者需求太高有溢价。

作业

&
点评   具体内容如下:

第一课的点评


 作业描述:
1. 从新项目启动单节点测试网络
2. 使用 https://polkadot.js.org/apps/ 连接本地测试网络并成功发起一笔转账交易

 作业要求:
项目推送到GitHub
发送交易,交易成功,包含交易的区块详细资料,分别截图

 点评 
第一课作业同学们的完成度都比较高,因作业比较简单,不做一一点评了。比较优秀的作业,有程华峥,林少婷,陈威,宣祎华.何怡与吴绪曼也很努力哦。
 

第二课的点评


 一.设计加密猫模块 
   1. 数据结构
   2. 存储定义
   3. 可调用函数
   4. 算法伪代码

 二. 需求 
   1. 链上存储加密猫数据
   2. 遍历所有加密猫
   3. 每只猫都有自己的dna,为128bit的数据2
   4. 设计如何生成dna (伪代码算法)
   5. 每个用户可以拥有零到多只猫
   6. 每只猫只有一个主人
   7. 遍历用户拥有的所有猫
 
第二课作业,二组的完成度比较高,这课的重点在于数据结构的设计。比较优秀的同学,何怡,陈威,宣祎华
 
@林少婷
1. 实现了完整的需求。
2. 比较用心学习了workshop的kitty,工程中用到了workshop的学习经验,很赞。
3. 使用了线上随机数生成。
               
@宣祎华
1. 实现了完整的需求。
2. 在满足需求外新增dna合成方法的设计,很赞。
              
@吴绪曼
1. 实现了完整的需求。
2. 使用了线上随机数生成的设计。
3. 查找某个用户的猫时,算法时间复杂度为N,有优化空间。
              
 @何怡
1. 实现了完整的需求。
2. 有使用链表的方式,实现用户猫的遍历,这个很赞。不过实施的时候查找某个用户的猫时,是使用遍历算法时间复杂度为N^2,有优化空间。
3. 使用线上时间戳作为md5随机数产生。

struct cat{ //猫的数据结构
cat_id://猫的ID 
cat_owner_id://猫的所有者ID 
cat_dna://猫的DNA 
cat_birth_timestamp://猫的生成时间 
owner_prev_cat_id://同一个所有者上一只猫的id 
owner_next_cat_id://同一个所有者下一只猫的id
cat_instance_address://猫的实例地址
}
               
 @程华峥
1. 实现了完整的需求,并编码为工程,很赞。
2. 使用线上Hash作为dna的随机数。
            
@陈威
1. 实现了完整的需求,并编码为工程,很优秀。
2. 使用链表存储数据,并做了优化,可按照range offsize获取特定范围的猫,很赞。
                                   


第三课的点评


 作业需求 
1. 设计加密猫模块V2
   需求:
   1. 繁殖小猫
   2. 选择两只现有的猫作为父母
   3. 小猫必须继承父母的基因
   4. 同样的父母生出来的小猫不能相同
2. create 这里面的kitties_count有溢出的可能性,修复这个问题

 额外作业 
1. 解释如何在链上实现(伪)随机数?
2. 对比不同方案的优缺点。
 
第三课作业同学们都完成啦。这课的重点在于还有随机数的生成,如何混合两个dna序列。比较优秀的同学有程华峥, 林少婷同学。
 
 随机数的生成有两个方案 
1. 链上的作为seed生成随机数(如区块hash,时间戳)。   
2. 使用链下的预言机来生成随机数 (不过存在信任问题) 。

 基因混合方案 
selector = get_random_128bits()
for i in 0 .. 128
output[i] = selector[i] == 0 ? parent1[i] : parent2[i]

 防止溢出的方案 
(1)checked_add
let new_all_kitties_count = kitties_count.checked_add(1).ok_or("Overflow adding a new kitty to total supply")?;
(2) u32::max_value
if count == u32::max_value() {
            return Err("Kitties count overflow");
        }
 
@林少婷
1. 合并dna的方法实现的很好。
2. 使用checked_add检查溢出比较专业。
3. 使用了前置检测,体现了代码健壮性。
               
@宣祎华
1. 使用了随机数标志位数组来遍历赋值父方or母方的dna,这个方案挺不错的。
2. 使用checked_add防止溢出,这个方案很优秀。
3. 对随机数的生成有独特见解。
   
@程华峥
1. 使用了随机数标志位数组合成父方or母方的dna,这个方案挺不错的。
2. create漏了做溢出检查。
               
@何怡
1. 功能完整了,同时也有比较大的优化空间。
2. 使用了std::io::rand产生随机数,不过std应该在runtime用不了。
3. 使用了count>=2^32-1方式检测溢出,这个方法挺好的。
              
@陈威
1. 使用u32::max_value()检查溢出这个方法很棒。
2.不小心漏了父母dna混合生成新的dna的功能。
3. 使用了单元测试,对代码质量有追求。
               
以上便是第一次的作业点评内容,在这里已经全部分享给你。想成为他们中的一员吗?更多内容,请关注一块+(微信公众号ID: yikuailianxi),一所开发者的在线学习与技术实战社区。这里有成体系的线上课程,有挑战的线下实战活动,以及有深度的区块链技术观察+评论。



更多阅读:
▎Web3.0 :加密协议对传统平台构成降维打击
▎Subdev 分享 | Substrate Runtime 中的堆排序
▎Subdev 讨论 ▏探究语言规则背后的含义


扫码关注公众号,回复“1”加入开发者社群


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存