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?
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
UUIDs as the random number for your dApp, so this entire reply is based on that. Underneath the hood, when you call theUuid::generatefunction, at the end of a long chain of calls that take place, the following function is the function that handles the generation of theUUID: https://github.com/radixdlt/radixdlt-scrypto/blob/24168ae772215af5169549a7a2cc1adeb666baa6/radix-engine/src/engine/id_allocator.rs#L78If you look through this function you will see that the this function uses the transaction hash + the next available id to generate the
UUIDfor 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 thetx_hash + next_idtwice before loading it into au128and that is pretty much how theUUIDis 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:
As we have said, the
IdAllocator.new_uuidmethod 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: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
UUIDrelies on the changing of the transaction hash and thenext_id. So it's easy to tell that this is not a random function but a pseudorandom function.So to answer your question:
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::generategenerates UUIDs pretty well but does not generate random numbers well because it's not meant to be a true random number function.