Randomization in Contracts
By using openssl
, sha256
and checksum256
, a value can be produced that is deterministic and effectively random. This can be achieved by hashing (sha256) n
number of hashes and their corresponding secrets and then selecting n
number of values from the result. The determinism and randomness comes from having an outside source supply a hash and its corresponding secret.
In the example of two users wanting to play a game with 50/50 odds, each player must first submit a hash of their secret. By submitting their hash, they then become eligible to play the game. Once both players have submitted the hash of their secret, they are effectively engaged in the game. It's only when both players reveal their secrets that both of their submitted hashes and recently submitted secrets are hashed. From the result of this hash, two numbers are selected to determine a winner and a loser.
Step 1: Generate Secrets
openssl rand -hex 32
28349b1d4bcdc9905e4ef9719019e55743c84efa0c5e9a0b077f0b54fcd84905
openssl rand -hex 32
15fe76d25e124b08feb835f12e00a879bd15666a33786e64b655891fba7d6c12
Step 2: Generate sha256 hashes
echo -n '28349b1d4bcdc9905e4ef9719019e55743c84efa0c5e9a0b077f0b54fcd84905' | xxd -r -p | sha256sum -b | awk '{print $1}'
d533f24d6f28ddcef3f066474f7b8355383e485681ba8e793e037f5cf36e4883
echo -n '15fe76d25e124b08feb835f12e00a879bd15666a33786e64b655891fba7d6c12' | xxd -r -p | sha256sum -b | awk '{print $1}'
50ed53fcdaf27f88d51ea4e835b1055efe779bb87e6cfdff47d28c88ffb27129
Step 3: Submit Hash of Secret
Alice and Bob each submit their hash of their secret.
cleos push action chance submithash '[ "alice", "d533f24d6f28ddcef3f066474f7b8355383e485681ba8e793e037f5cf36e4883" ]' -p alice@active
cleos push action chance submithash '[ "bob", "50ed53fcdaf27f88d51ea4e835b1055efe779bb87e6cfdff47d28c88ffb27129" ]' -p bob@active
Step 4: Submit Secret
Alice and Bob each submit the hash of their secret and the secret itself.
cleos push action chance submitboth '[ "d533f24d6f28ddcef3f066474f7b8355383e485681ba8e793e037f5cf36e4883", "28349b1d4bcdc9905e4ef9719019e55743c84efa0c5e9a0b077f0b54fcd84905" ]' -p alice@active
cleos push action chance submitboth '[ "50ed53fcdaf27f88d51ea4e835b1055efe779bb87e6cfdff47d28c88ffb27129", "15fe76d25e124b08feb835f12e00a879bd15666a33786e64b655891fba7d6c12" ]' -p bob@active
Step 5: Conclusion
The simplest data type that can be used to store the secret and the hash of the secret is a struct.
struct player {
checksum256 hash;
checksum256 secret;
};
When the players of the game are being instantiated, it is important to do so in succession. This is done so that the secrets and hashes for all the players are contiguously stored in memory.
struct game {
uint64_t id;
player player1;
player player2;
}
Once the secret and the hash of the secret are received for all the players, the hashing of it all can be done.
// variable to get result from hashing all players hashes and secrets
checksum256 result;
// hash the contents in memory, starting at game.player1 and spanning for
// sizeof(player)*2 bytes
sha256( (char *)&game.player1, sizeof(player)*2, &result);
// compares first and second 4 byte chunks in result to determine a winner
int winner = result.hash[1] < result.hash[0] ? 0 : 1;
// report appropriate winner
if( winner ) {
report_winner(game.player1);
} else {
report_winner(game.player2);
}
It's important to note that sha256 is 32 bytes and that there are 8 chunks of uint32_t in it. In the above example, the first two are considered, but it could have been any of them.
Updated about 6 years ago