3

We were using self.env.random() in our contract to generate a random number. The calling user's AccountId was used to give additional entropy to the random generation.

fn get_random_number(&self, min: u64, max: u64, user_account: AccountId) -> u64 {
    let random_seed = self.env().random(user_account.as_ref());
    let mut seed_converted: [u8; 32] = Default::default();
    seed_converted.copy_from_slice(random_seed.0.as_ref());
    let mut rng = ChaChaRng::from_seed(seed_converted);
    ((rng.next_u64() / u64::MAX) * (max - min) + min) as u64
}

The random number was used to select an index in a Vec<AccountId> when the caller performed a read operation on the contract. This function allowed us to create basic load balancer functionality, in which the AccountIds in the vector are selected at random. Subsequent operations involving the selected AccountId are performed off-chain. There is no transaction involved.

Now self.env.random() has been removed from ink, how should we generate a source of randomness in the contract? The block hash would probably provide a random enough source but it is not available. block_timestamp is too predictable.

2 Answers 2

5

Unfortunately there is no way to generate randomness using ink!
The only options I see:

  • Creating an VRF oracle contract that will generate randomness(DIA is working on it for Astar)
  • On runtime level add pallet RandomnessCollectiveFlip chain-extension to be accessible in ink! contracts
  • Last one and best one IMO: add a call in ink_env to get current block hash and previous block hash and create randomness from it. I don't see security issue with ink! exposing those env values.
4
  • how exactly can we get current block hash with call in ink_env? I have found block_number() but not function to return block hash.
    – Ganesh11
    Jan 17, 2023 at 14:07
  • 2
    By "add" I mean open a PR in ink to add this feature OR open an issue in ink! repo to ask ink dev to add it
    – P.Ossun
    Jan 17, 2023 at 14:11
  • 1
    I understand why you might use previous block hash. How can the current block hash be known during the contract operation? I thought the hash was finalised after completion of all operations.
    – forgetso
    Jan 19, 2023 at 9:24
  • you are right @forgetso you can only get the previous block hash
    – P.Ossun
    Jan 23, 2023 at 9:46
2

You write that you need the random numbers for load balancing. This suggests that unpredictability is not a requirement to those numbers. You merely want a random distribution. On the other hand you state that you do not want to use block_timestamp because it is predictable. This is contradictory.

If you don't need unpredictability then you should just use a non-crypto RNG that yields equally distributed numbers. This will be much faster than using a cryptographically secure RNG as you do in your code. You can use a hardcoded number as seed if you don't care that all contracts using this code use the same but equally distributed stream of numbers.

If you need unpredictability (or low influence) then using ink!'s random functionality is unsafe (always has been). The collective coin-flip can be manipulated by collators which are not subject to Polkadot's economic security. You need to find an oracle or deploy on a chain which offers this functionality via chain extension.

Some comments on the code you posted: You use a cryptographically secure RNG to derive a random number from a public seed. You don't gain anything from doing so. Its purpose is to to prevent an observer from learning about a secret seed from its output. Using a non-cryptographic RNG would be sufficient.

Additionally, you only ever use the first number coming out of the RNG per initialisation. Why do you use an RNG in the first place then? Just use the seed directly.

1
  • We need the chosen AccountId to be reasonably hard to predict as we are not running the associated nodes. It's load balancing in the sense that the cost of servicing the network is being farmed out to a network of independent nodes. We cannot use an oracle as we don't want an additional transaction simply to select a node. Thanks for the comments on the implementation. It's definitely going to be overhauled!
    – forgetso
    Jan 21, 2023 at 10:25

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.