'Is Runtime::generate_uuid() safe in Scrypto?
I want to make a game in a Scrypto blueprint where users can play with their Gumball NFTs.
My blueprint has a pub fn attack(&self, my_gumball: Proof, other_gumball_key: NonFungibleId)
method that attacks another NFT by assigning it a random damage between 1 and 10. Should I use Runtime::generate_uuid()
for this?
Solution 1:[1]
Great question! I'll give you a little example here from Radix as the generation of random numbers is an issue that all blockchains/DLTs/public networks struggle with and it's a problem that is genuinely hard to solve.
First of all, I'm assuming that you're using UUID
s as the random number for your dApp, so this entire reply is based on that. Underneath the hood, when you call the Uuid::generate
function, at the end of a long chain of calls that take place, the following function is the function that handles the generation of the UUID
: https://github.com/radixdlt/radixdlt-scrypto/blob/24168ae772215af5169549a7a2cc1adeb666baa6/radix-engine/src/engine/id_allocator.rs#L78
If you look through this function you will see that the this function uses the transaction hash + the next available id to generate the UUID
for you. The next ID available is nothing special, it's simply a counter that is incremented each time we need to generate a new ID. All that this method does is that it hashes the tx_hash + next_id
twice before loading it into a u128
and that is pretty much how the UUID
is generated. This means that the UUID is a pseudorandom number and that if somebody has knowledge over what the transaction hash is, then they WILL be able to determine the "random" number that you will be using.
Let's go away from all of the theory for a second and let's try a few things in code. Here is some simple Scrypto code to show you how not-random the UUID really is:
use radix_engine::engine::{IdAllocator, IdSpace};
use scrypto::prelude::*;
#[test]
fn test_randomness() {
// Creating the ID allocator
let mut id_allocator: IdAllocator = IdAllocator::new(IdSpace::Application);
// A fictional transaction hash
let tx_hash: H256 = H256::from_str("0e4c5812f00b3c821335c54b3bbc835a157df1149480f6469a4dc6b51489e989").unwrap();
// Generating four UUIDs
println!("Generated UUID: {:?}", id_allocator.new_uuid(tx_hash).unwrap());
println!("Generated UUID: {:?}", id_allocator.new_uuid(tx_hash).unwrap());
println!("Generated UUID: {:?}", id_allocator.new_uuid(tx_hash).unwrap());
println!("Generated UUID: {:?}", id_allocator.new_uuid(tx_hash).unwrap());
}
As we have said, the IdAllocator.new_uuid
method requires a transaction hash to run, so I have provided it with a sample transaction hash. If we run this code and look at the output, both you and I would have the following in our command line terminals:
Generated UUID: 333873524275763974188597434119212610710
Generated UUID: 315396769568132504258157739854036837613
Generated UUID: 31497316649309892037047888539219683042
Generated UUID: 300332381675622117598720587595812830316
You might ask, Well why are we both getting the same output, isn't this random?
The output that both of us would get would be exactly the same as the randomness of the UUID
relies on the changing of the transaction hash and the next_id
. So it's easy to tell that this is not a random function but a pseudorandom function.
So to answer your question:
Someone would be able to guess the number 5-10 before the scrypto code can even generate it?
Yes! Transactions in the mempool have their hashes visible so there is the hash part. Plus somebody with knowledge of your blueprint and with the number of IDs allocated during the transaction will 100% of the time be able to guess the random number before the scrypto code does, the code above is an example of how that can be done.
Conclusion: The Uuid::generate
generates UUIDs pretty well but does not generate random numbers well because it's not meant to be a true random number function.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | 0xOmarA |