3

我有一个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)?;

4

2 回答 2

0

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

这是工作代码:

#![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, Clone, PartialEq, 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]);
    }
}

}

于 2021-07-19T23:04:14.343 回答
0

您应该能够通过<'a, 'b, E: Ext, S: state::BufIn> Environment<'a, 'b, E, S>实现的函数之一访问参数,Environment例如read, 。有关这方面的更多信息,请参阅Environment 结构read_asread_as_unbounded

您在此处引用的rand-extension示例也已更新,以演示在运行时将参数传递给链扩展。请参阅此处的示例。您应该能够遵循该示例并实施更改。

于 2021-11-24T13:52:25.450 回答