温馨提示:这篇文章已超过846天没有更新,请注意相关的内容是否还可用!
在上期的技术视点中,我们简单介绍了如何在不依赖模板的情况下,完成一个简单的 Ontology Wasm 合约的开发,并介绍了 Ontology Wasm 工具库 API 文档的生成方式,方便开发者查询和调用已提供的功能。但我们发现在合约开发的过程中,通常需要进行以下操作:
- 解析调用合约的参数;
- 将自定义的结构体保存到链上;
- 从链上读取已存在的数据并解析成原始的数据类型;
- 跨合约调用的时候,传递目标合约需要的参数。

Encoder和Decoder接口
let mut sink = Sink::new(16); sink.write(1u128); sink.write(true); let addr = Address::repeat_byte(1); sink.write(addr); sink.write("test"); let vec_data = vec!["hello","world"]; sink.write(&vec_data); let tuple_data = (1u8, "hello"); sink.write(tuple_data); let mut source = Source::new(sink.bytes()); let res1:u128 = source.read().unwrap_or_default(); let res2:bool = source.read().unwrap_or_default(); let res3:Address = source.read().unwrap_or_default(); let res4:&str = source.read().unwrap_or_default(); let res5:Vec<&str> = source.read().unwrap_or_default(); let res6:(u8, &str) = source.read().unwrap_or_default(); assert_eq!(res1, 1u128); assert_eq!(res2, true); assert_eq!(res3, addr); assert_eq!(res4, "test"); assert_eq!(res5, vec_data); assert_eq!(res6, tuple_data);sink.write()方法传入的参数支持所有已经实现 Encoder 接口的数据类型,序列化的时候需要声明其数据类型,比如1u128等。source.read()方法可以读取所有已经实现 Decoder 接口的数据类型,反序列化的时候要指明其结果的数据类型,如下面的代码:
let res1:u8 = source.read().unwrap_or_default();在 res1后面要声明其数据类型是 u8。也可以写成:
let res1 = source.read_byte().unwrap_or_default();在读取的时候,已经使用了read_byte,所以不需要在 res1后面声明数据类型了。
合约在获得调用参数时通过runtime::input()方法获得,但是该方法仅能拿到bytearray格式的参数,需要反序列化成对应的参数,第一个参数是合约方法名,后面的是方法参数,示例如下:解析调用合约的参数
let input = runtime::input(); let mut source = Source::new(&input); let method_name: &str = source.read().unwrap(); let mut sink = Sink::new(); match method_name { "transfer" => { let (from, to, amount) = source.read().unwrap(); sink.write(ont::transfer(from, to, amount)); } _ => panic!("unsupported action!"), }Rust 支持类型推导,大部分情况下可以省略类型声明,比如在ont::transfer()方法中已经声明了 from、to 和 amount 的数据类型,所以在前面解析 from、to 和 amount 的时候没有声明数据类型。
如果参数是 Vec<&str>类型,可以按照如下的方式进行反序列化:
let param:Vec<&str> = source.read().unwrap();如果参数是 Vec<(&str,U128,Address)>类型,可以按照如下的方式进行反序列化:
let param:Vec<(&str,U128,Address)>= source.read().unwrap();
序列化自定义结构体
在合约开发中,我们经常需要对struct类型的数据进行序列化和反序列化,为了达到这个目的,我们仅需要在struct声明的地方加上#[derive(Encoder, Decoder)]即可,示例如下:
struct ReceiveRecord { account: Address, amount: u64, }在使用该功能的时候,要注意struct的每个字段必须都已经实现Encoder和Decoder接口,加上该注解后,就可以按照如下的方式序列化和反序列化了:#[derive(Encoder, Decoder)] struct EnvlopeStruct { token_addr: Address, total_amount: u64, total_package_count: u64, remain_amount: u64, remain_package_count: u64, records: Vec<ReceiveRecord>, }
let addr = Address::repeat_byte(1); let rr = ReceiveRecord{ account: addr, amount: 1u64, }; let es = EnvlopeStruct{ token_addr: addr, total_amount: 1u64, total_package_count: 1u64, remain_amount: 1u64, remain_package_count: 1u64, records: vec![rr], }; let mut sink = Sink::new(16); sink.write(&es);let mut source = Source::new(sink.bytes()); let es2:EnvlopeStruct = source.read().unwrap();
assert_eq!(&es.token_addr,&es2.token_addr); assert_eq!(&es.total_amount,&es2.total_amount); assert_eq!(&es.total_package_count,&es2.total_package_count); assert_eq!(&es.remain_amount,&es2.remain_amount); assert_eq!(&es.remain_package_count,&es2.remain_package_count);
从链上读取指定类型的数据
- fn put<K: AsRef<[u8]>, T: Encoder>(key: K, val: T) 根据 key 保存 T 类型的数据,要求 T 类型实现Encoder接口示例:
let es = EnvlopeStruct{ token_addr: addr, total_amount: 1u64, total_package_count: 1u64, remain_amount: 1u64, remain_package_count: 1u64, records: vec![rr], }; database::put("key", es);我们从database::put的源码可以看到,该方法在执行的时候,会先序列化 es 参数,然后将序列化结果保存到链上。
- fn get<K: AsRef<[u8]>, T>(key: K) -> Option<T> where for<'a> T: Decoder<'a> + 'static,根据 key 获得指定类型 T 的数据,其中,T 类型要求实现了 Decoder 接口。示例:
let res:EnvlopeStruct = database::get("key").unwrap();我们从database::get的源码可以看到,该方法在执行的时候,会先从链上拿到 bytearray 类型的数据,然后再反序列化得到 EnvlopeStruct 类型的数据。
跨合约参数传递
let (contract_address,method,a, b): (&Address,&str,u128, u128) = source.read().unwrap();
let mut sink = Sink::new(16); sink.write(method); sink.write(a); sink.write(b); let resv = runtime::call_contract(contract_address, sink.bytes()).expect("get no return");
结语
Wasm 技术具有十分庞大活跃的社区,且 Wasm 可以支持更加复杂的合约,并拥有丰富的第三库支持,生态十分完善,开发门槛比较低,在 Ontology 链上开发和部署 Wasm 合约对于想要入门的开发者来说十分友好。
来源:公众号:本体研究院