4

In an ink! contract I need to use Self.env().block_number() to get the current block number to compare it with a block_number_guessed value that I provide when instantiating the contract and check that it is less than the current block number.

impl MyContract {
    #[ink(constructor)]
    pub fn new(
        block_number_guessed: BlockNumber,
    ) -> Self {
        let mut instance = Self::default();
        let block_number_current = Self::env().block_number();
        assert!(
            block_number_current > block_number_guessed,
            "block number when guessing occurred must be before the current block number"
        );

I have written a test but I don't know how to provide a fake value of Self.env().block_number() in the unit tests to pretend that the current block number is greater than the fake value of 100 that I have used for block_number_guessed so that the assertion in the implementation when the contract is instantiated is satisfied and the test passes.

#[cfg(test)]
mod tests {
    use super::*;

    use crate::my_contract::MyContract;

    /// We test a simple use case of our contract.
    #[ink::test]
    fn it_works() {
        let block_number_guessed = 100;
        let mut my_contract = MyContract::new(
            block_number_guessed.clone(),
        );
        ...
    }
}

I tried to use a custom environment CustomEnvironment and a custom chain extension CustomBlocknumberExtension like mentioned here as shown in the below code snippet, but I don't know how to write the implementation of fake_current_block_number, and I don't know if this is even a valid way to solve the issue.

#[cfg(test)]
mod tests {
    use super::*;

    use crate::my_contract::MyContract;

    use ink::env::{
        Environment,
        DefaultEnvironment,
    };

    #[ink::chain_extension]
    pub trait CustomBlocknumberExtension {
        type ErrorCode = CustomBlocknumberError;

        #[ink(extension = 0x1111)]
        fn fake_current_block_number() -> BlockNumber>;
    }    

    /// An environment using default ink environment types
    #[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 Timestamp = <ink::env::DefaultEnvironment as Environment>::Timestamp;
        type BlockNumber = <ink::env::DefaultEnvironment as Environment>::BlockNumber;

        type ChainExtension = crate::CustomBlocknumberExtension;
    }

    /// We test a simple use case of our contract.
    #[ink::test(env = crate::CustomEnvironment)]
    fn it_allows_use_of_stubbed_blocknumber() {
        assert_eq!(env().extension().fake_current_block_number(), 200);
        ...
    }
}

I looked through all the ink! examples repository, but I can't find any that stub the ink! environment in their tests.

How should we stub the ink! environment function Self.env().block_number() so we can provide a fake value for the current block number in our unit tests?

2

1 Answer 1

3

There is advance_block function in the ink::env::test and you can do something like this:

fn advance_n_blocks(n: u32) {
    for _ in 0..n {
        advance_block();
    }
}
fn advance_block() {
    ink::env::test::advance_block::<ink::env::DefaultEnvironment>();
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.