智能合约语言(eDSL)—— 实现合约语言中调用链上数据

        写合约的时候,我们需要获取链上的数据,但是在你编译合约的时候,合约没有这个能力,所以,我们把它做成一个接口,等到真正在链上执行的时候,wasm就可以帮你把它链接到对应的数据上,wasm的这部分,我们下次介绍wasm虚拟机部分的时候,在介绍。今天就说一下,如何实现合约语言中,这部分的实现。

          GitHub - XuHugo/xwasm

        我们以获取区块高度,来详细介绍一些流程。

#[call(contract = "xq", function = "abc")]

fn rcv<C: Context + Copy>(ctx: C) -> CResult<RetValue> {

    let a: Param = ctx.paramteter();

    let ret = RetValue {

        name: a.name,

        age: a.age,

        sex: a.sex,

    };

    let num = ctx.block_number();

    //let a:Param = ctx.paramteter();

    Ok(ret)

}

        ctx.block_number()就是获取当前区块高度的函数。但是这个ctx是个泛型,他是一个context trait;这个context是怎么来的,可以看看你上一节,如何生成init函数中,介绍的;

他的定义如下,

pub trait Context{

    fn owner(&self) -> Address;

    fn invoker(&self) -> Address;

    fn self_address(&self) -> Address;

    fn self_balance(&self) -> u64;

   

    fn paramteter<T:>(&self)-> T where T: serde::de::DeserializeOwned;

    fn store_get<T>(&self, key: String)->T where T: serde::de::DeserializeOwned;

    fn store_set(&self, key: String, value:String)->bool;

    fn error(&self, err:String);

    fn event(&self, event:String);

    fn return_data(&self, data:String);

    fn transfer(&self, addr:Address, amount:u64)->bool;

    fn call(&self, addr:Address, amount:u64, func:String, rg:String)->bool;

    fn block_time(&self)->u64;

    fn block_number(&self)->u64;

    fn tx_hash(&self)->String;

}

        我们可以看到它里边的函数都是一些,和链相关的,例如获取地址,余额,高度时间等;我们写合约的时候,就是通过它,实现和链交互的,他是内置在合约语言里的;

        实际上他是由contractcontex实现的,它的定义是空,因为我们需要的数据,都在链上,所以这里定义一个空的;

#[derive(Clone, Copy)]

pub struct ContractContext;

        下面我们来看看,他是怎么实现Context trait的;

impl Context for ContractContext {

    fn owner(&self) -> Address {

        let mut bytes: MaybeUninit<[u8; ADDRESS_NUM_LEN]> = MaybeUninit::uninit();

        let ptr = bytes.as_mut_ptr();

        let address = unsafe {

            get_owner(ptr as *mut u8);

            bytes.assume_init()

        };

        Address(address)

    }

    fn invoker(&self) -> Address {

        let mut bytes: MaybeUninit<[u8; ADDRESS_NUM_LEN]> = MaybeUninit::uninit();

        let ptr = bytes.as_mut_ptr();

        let address = unsafe {

            get_invoker(ptr as *mut u8);

            bytes.assume_init()

        };

        Address(address)

    }



… …

}

        我们看owner函数,他是获取当前合约拥有者的地址,可以看到,它先分配了一个地址长度的数组MaybeUninit<[u8; ADDRESS_NUM_LEN]>,准备用来放地址;然后是一个unsafe的,在里边调用了一个get_owner()函数,返回address;组装Address返回给用户;至于其中的MaybeUninit,是为了让变量bytes,在get_owner获取到address之后才进行初始化;

        我们再来看一下get_owenr的定义,如下

#[cfg_attr(target_arch = "wasm32", link(wasm_import_module = "xq"))]

extern "C" {

    pub(crate) fn get_invoker(value: *mut u8);

    pub(crate) fn get_owner(value: *mut u8);

    pub(crate) fn get_contract_address(value: *mut u8);

    pub(crate) fn get_contract_balance() -> u64;

    pub(crate) fn get_parameter(value: *mut u8) -> u32;

… …

    pub(crate) fn get_block_time() -> u64;

    pub(crate) fn get_block_hash(value: *mut u8) -> u64;

    pub(crate) fn get_block_height() -> u64;

    pub(crate) fn set_event(start: *const u8, length: u32) -> i32;

    pub(crate) fn set_return_data(start: *const u8, length: u32) -> u32;

}

        Target_arch = "32"告诉编译器,编译的成wasm32平台的格式,wasm_import_module则是定义wasm的input模块的名字,后边有机会,可以用rust实现版wasm,详细说一下,wasm的构造;其实当前项目中,原计划是自己写一个简单的,但是写了一半,没写完,所以后边直接用wasmtime了。

        get_owenr是FFI,通过extern “C”定义了一组外部接口,其实是和context trait对应的;这意味着,wasm会提供这样一组实现相应功能的接口,这样合约里调用链上的数据的整个链条就完成一半了,另外一半,在wasm虚拟机中实现。这些函数都是使用基础类型,指针,u32之类的,这是因为FFI要兼容C的ABI。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

0xweb3q

有钱的捧个钱场,没钱的捧个人场

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值