Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add example for mocking chain extensions in off-chain tests #882

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I need to mock chain extension inside every test function? Is there anyway to define outside and use that in all functions?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also define it outside, I just mocked it in here since it's only used in this one test.

/// 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]);
}
}
}