您如何将参数传递和处理到“ChainExtension”?

问题描述

我有一个调用扩展方法 ink!fetch_random() 合同。

// runtime/src/lib.rs
pub struct FetchRandomExtension;

impl ChainExtension<Runtime> for FetchRandomExtension {
    fn call<E: Ext>(
        func_id: u32,env: Environment<E,InitState>,) -> Result<RetVal,dispatchError>
    where
        <E::T as SysConfig>::AccountId:
            UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>,{
        match func_id {
            1101 => {
                let mut env = env.buf_in_buf_out();
                let random_seed = crate::RandomnessCollectiveFlip::random_seed().0;
                let random_slice = random_seed.encode();
                env.write(&random_slice,false,None).map_err(|_| {
                    dispatchError::Other("ChainExtension Failed to call random")
                })?;
            }

            _ => {
                return Err(dispatchError::Other("Unimplemented func_id"))
            }
        }
        Ok(RetVal::Converging(0))
    }

    fn enabled() -> bool {
        true
    }
}

// contract/lib.rs
let new_random = self.env().extension().fetch_random()?;

如何编写扩展处理程序以接收诸如 let new_random = self.env().extension().fetch_random(1,"hello",true)?; 之类的参数?

解决方法

您可以在 GitHub here 上找到完整的示例。

这是工作代码:

#![cfg_attr(not(feature = "std"),no_std)]

use ink_env::Environment;
use ink_lang as ink;

/// This is an example of how ink! contract should
/// call substrate runtime `RandomnessCollectiveFlip::random_seed`.

/// Define the operations to interact with the substrate runtime
#[ink::chain_extension]
pub trait FetchRandom {
    type ErrorCode = RandomReadErr;

    /// Note: this gives the operation a corresponding `func_id` (1101 in this 
 case),/// and the chain-side chain extension will get the `func_id` to do further operations.
#[ink(extension = 1101,returns_result = false)]
fn fetch_random() -> [u8; 32];
}

#[derive(Debug,Copy,Clone,PartialEq,Eq,scale::Encode,scale::Decode)]
#[cfg_attr(feature = "std",derive(scale_info::TypeInfo))]
pub enum RandomReadErr {
    FailGetRandomSource,}

impl ink_env::chain_extension::FromStatusCode for RandomReadErr {
    fn from_status_code(status_code: u32) -> Result<(),Self> {
        match status_code {
            0 => Ok(()),1 => Err(Self::FailGetRandomSource),_ => panic!("encountered unknown status code"),}
    }
 }

 #[derive(Debug,Eq)]
 #[cfg_attr(feature = "std",derive(scale_info::TypeInfo))]
 pub enum CustomEnvironment {}

 impl Environment for CustomEnvironment {
    const MAX_EVENT_TOPICS: usize =
        <ink_env::DefaultEnvironment as Environment>::MAX_EVENT_TOPICS;

    type AccountId = <ink_env::DefaultEnvironment as Environment>::AccountId;
    type Balance = <ink_env::DefaultEnvironment as Environment>::Balance;
    type Hash = <ink_env::DefaultEnvironment as Environment>::Hash;
    type BlockNumber = <ink_env::DefaultEnvironment as Environment>::BlockNumber;
    type Timestamp = <ink_env::DefaultEnvironment as Environment>::Timestamp;
    type RentFraction = <ink_env::DefaultEnvironment as Environment>::RentFraction;

    type ChainExtension = FetchRandom;
  }

#[ink::contract(env = crate::CustomEnvironment)]
mod rand_extension {
    use super::RandomReadErr;

    /// Defines the storage of your contract.
    /// Here we store the random seed fetched from the chain
    #[ink(storage)]
    pub struct RandExtension {
        /// Stores a single `bool` value on the storage.
        value: [u8; 32],}

    #[ink(event)]
    pub struct RandomUpdated {
        #[ink(topic)]
        new: [u8; 32],}

    impl RandExtension {
    /// Constructor that initializes the `bool` value to the given `init_value`.
    #[ink(constructor)]
    pub fn new(init_value: [u8; 32]) -> Self {
        Self { value: init_value }
    }

    /// Constructor that initializes the `bool` value to `false`.
    ///
    /// Constructors can delegate to other constructors.
    #[ink(constructor)]
    pub fn default() -> Self {
        Self::new(Default::default())
    }

    /// update the value from runtime random source
    #[ink(message)]
    pub fn update(&mut self) -> Result<(),RandomReadErr> {
        // Get the on-chain random seed
        let new_random = self.env().extension().fetch_random()?;
        self.value = new_random;
        // emit the RandomUpdated event when the random seed
        // is successfully fetched.
        self.env().emit_event(RandomUpdated { new: new_random });
        Ok(())
    }

    /// Simply returns the current value.
    #[ink(message)]
    pub fn get(&self) -> [u8; 32] {
        self.value
    }
}

/// Unit tests in Rust are normally defined within such a `#[cfg(test)]`
#[cfg(test)]
mod tests {
    /// Imports all the definitions from the outer scope so we can use them here.
    use super::*;
    use ink_lang as ink;

    /// We test if the default constructor does its job.
    #[ink::test]
    fn default_works() {
        let rand_extension = RandExtension::default();
        assert_eq!(rand_extension.get(),[0; 32]);
    }
}

}