Skip to content

Commit

Permalink
Add example for mocking chain extensions in off-chain tests (#882)
Browse files Browse the repository at this point in the history
* Improve language

* Add test with mocked chain extension

* Export off-chain trait `ChainExtension`

* Add missing space

* Remove unequal assert
  • Loading branch information
Michael Müller authored Jul 30, 2021
1 parent 27b8d77 commit eb44ace
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 16 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ See [here](https://paritytech.github.io/ink/ink_lang/attr.contract.html) for a m

### Trait Definitions

Use`#[ink::trait_definition]` to define your very own trait definitions that are then implementable by ink! smart contracts.
Use `#[ink::trait_definition]` to define your very own trait definitions that are then implementable by ink! smart contracts.
See e.g. the [`examples/trait-erc20`](https://github.com/paritytech/ink/blob/master/examples/trait-erc20/lib.rs#L49-L51) contract on how to utilize it or [the documentation](https://paritytech.github.io/ink/ink_lang/attr.trait_definition.html) for details.

### Off-chain Testing
Expand Down
12 changes: 6 additions & 6 deletions crates/env/src/engine/off_chain/test_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@

//! Operations on the off-chain testing environment.

use super::{
pub use super::{
chain_extension::ChainExtension,
db::ChainSpec,
CallData,
EmittedEvent,
};
use super::{
db::ExecContext,
AccountError,
EnvInstance,
OnInstance,
};
pub use super::{
db::ChainSpec,
CallData,
EmittedEvent,
};
use crate::{
Environment,
Result,
Expand Down
53 changes: 44 additions & 9 deletions examples/rand-extension/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@
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
/// This is an example of how an ink! contract may call the Substrate
/// runtime function `RandomnessCollectiveFlip::random_seed`. See the
/// file `runtime/chain-extension-example.rs` for that implementation.
///
/// Here we define the operations to interact with the Substrate runtime.
#[ink::chain_extension]
pub trait FetchRandom {
type ErrorCode = RandomReadErr;
Expand Down Expand Up @@ -69,8 +70,9 @@ impl Environment for CustomEnvironment {
mod rand_extension {
use super::RandomReadErr;

/// Defines the storage of your contract.
/// Here we store the random seed fetched from the chain
/// Defines the storage of our contract.
///
/// Here we store the random seed fetched from the chain.
#[ink(storage)]
pub struct RandExtension {
/// Stores a single `bool` value on the storage.
Expand All @@ -92,19 +94,19 @@ mod rand_extension {

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

/// update the value from runtime random source
/// Update the value from the runtimes 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
// Emit the `RandomUpdated` event when the random seed
// is successfully fetched.
self.env().emit_event(RandomUpdated { new: new_random });
Ok(())
Expand All @@ -130,5 +132,38 @@ mod rand_extension {
let rand_extension = RandExtension::default();
assert_eq!(rand_extension.get(), [0; 32]);
}

#[ink::test]
fn chain_extension_works() {
// given
struct MockedExtension;
impl ink_env::test::ChainExtension for MockedExtension {
/// The static function id of the chain extension.
fn func_id(&self) -> u32 {
1101
}

/// The chain extension is called with the given input.
///
/// Returns an error code and may fill the `output` buffer with a
/// SCALE encoded result. The error code is taken from the
/// `ink_env::chain_extension::FromStatusCode` implementation for
/// `RandomReadErr`.
fn call(&mut self, _input: &[u8], output: &mut Vec<u8>) -> u32 {
let ret: [u8; 32] = [1; 32];
scale::Encode::encode_to(&ret, output);
0
}
}
ink_env::test::register_chain_extension(MockedExtension);
let mut rand_extension = RandExtension::default();
assert_eq!(rand_extension.get(), [0; 32]);

// when
rand_extension.update().expect("update must work");

// then
assert_eq!(rand_extension.get(), [1; 32]);
}
}
}

0 comments on commit eb44ace

Please sign in to comment.