6

I am trying to generate random number using the tutorial:

#[pallet::config]
pub trait Config: frame_system::Config {        
    type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
    type RandomnessSource: Randomness<Self::Hash, Self::BlockNumber>;
}

fn get_and_increment_nonce() -> Vec<u8> {
        let nonce = <Nonce<T>>::get();
        <Nonce<T>>::put(nonce.wrapping_add(1));
        nonce.encode()
    }

 
for _ in draws_in_round..end_index {
    let nonce = Self::get_and_increment_nonce();
    let random_seed = T::RandomnessSource::random(&nonce).encode();
    let random_number = u64::decode(&mut random_seed.as_ref())
                .expect("secure hashes should always be bigger than u64; qed");
     println!("random_number, {:?}", random_number);
 }

But I don't get random number when I run the tests. I get
random_number, 0
random_number, 1
random_number, 2
random_number, 3
random_number, 4

How to generate u64 random numbers?.

5
  • What did you set as RandomnessSource type in the runtime?
    – André
    Feb 27, 2022 at 18:18
  • 1
    Do you have Randomness = TestRandomness or Randomness = RandomnessCollectiveFlip in your mock.rs? The TestRandomness is not doing much, annoyed me in the past as well. Feb 27, 2022 at 20:22
  • I am using TestRandomness (use frame_support_test::TestRandomness;) github.com/paritytech/substrate/blob/master/frame/lottery/src/… How can I use RandomnessCollectiveFlip in tests? I am using it in runtime. Feb 28, 2022 at 9:25
  • Why do you need real randomness in tests? Normally you want to have always the same "random" values in tests to ensure everyone runs the same test.
    – bkchr
    Feb 28, 2022 at 13:28
  • Ok I am trying to use same "random" values in tests @bkchr Feb 28, 2022 at 14:06

2 Answers 2

3

TestRandomness is a very simple wrapper which can allow you to easily write tests pretending to be a randomness generator, but with full control over the values, since writing tests with real randomness can be very difficult or even impossible.

/// Provides an implementation of [`frame_support::traits::Randomness`] that should only be used in
/// tests!
pub struct TestRandomness<T>(sp_std::marker::PhantomData<T>);

impl<Output: codec::Decode + Default, T> frame_support::traits::Randomness<Output, T::BlockNumber>
    for TestRandomness<T>
where
    T: frame_system::Config,
{
    fn random(subject: &[u8]) -> (Output, T::BlockNumber) {
        use sp_runtime::traits::TrailingZeroInput;

        (
            Output::decode(&mut TrailingZeroInput::new(subject)).unwrap_or_default(),
            frame_system::Pallet::<T>::block_number(),
        )
    }
}

For example, if you have a test that requires a coinflip return "heads", using real randomness means your test will fail half of the time.

As you can see in the code above, the randomness is exactly taken from the subject, with no actual randomness happening.

If you want to introduce real randomness, you are allowed to use std for tests, so you can simply introduce the rand crate into your tests and hook it up that way.

5

The entropy in your randomness seems to be taking in both block number and nonce. If you want to capture this behaviour in your tests, you should be manually incrementing set_block_number() and set_extrinsic(): your mock runtime doesn't do this for you and here, get_and_increment_nonce() isn't doing anything at all.

Using these, your loop would look like:

for _ in draws_in_round..end_index {

    // get the nonce, starting at 0
    let nonce = PalletName::get_and_increment_nonce();
        
    // pass in the nonce
    let random_seed = <Test as Config>::RandomnessSource::random(&nonce).encode();
        
    // pass in random_seed
    let random_number = u64::decode(&mut random_seed.as_ref())
        .expect("secure hashes should always be bigger than u64; qed");
    println!("random_number, {:?}", random_number);
        
    // increment the extrinsic index 
    frame_system::Pallet::<Test>::set_extrinsic_index(1);
        
    // increment the block number
    System::set_block_number(System::block_number() + 1);
}

Note that it's not clear what you're actually testing here and that in an actual test you'd need to be asserting some condition, e.g. "two values aren't equal" or maybe you want to make a vector of values and check that they have a minimum number of different values?

In addition, you'd have this set up in an actual tests.rs file with its mock runtime. Refer to this guide on how to properly set your test up.

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.