It's based on a hash function I came up with inspired by BitLattice. It's a hash function, similar to the pseudo-randomness used in dApps so far. I call it "maze hash function". It uses the state itself as an almost infinitely long source of random numbers. (read more)
I understand the reasoning that randomness cannot be generated on-state, and that you need an oracle, but so far, dApps have experimented with hashing things to get randomness. Maybe, with enough complexity, it begins to approximate randomness, similar to how an oracle gets randomness from the "external world" which is based on complexity as well.
So even if a miner can hack some of the input to the RNG, they will have to predict which input generates what hash in the maze hash function (mhf). That is hard even with a keccak256 hash, and with a hash function of much higher cryptographic hardness, the mhf, it is much harder. Current ideas for deterministic randomness in dApps already use the state to get entropy, as an example, uint randomNumber = uint(sha3(block.blockhash(block.number-1), now, entropy)) % maxNumber;
, the maze hash function just expands on that.
So it's a bit like, instead of getting a block.hash for randomness, you get transactions hashes, but only bits and pieces of them, and you use those bits and pieces to get the next transaction hash, and continue through the state like a labyrinth, until you reach the end of the map the input, and the recursive mutations of the input, directed you to. There you find your random number.
A maze hash function with blockhashes on Ethereum
The parts of the state that can be accessed from a smart contract is limited in Ethereum. What is available is that you can get the blockhash from any block within 256 blocks of the most recent one. That is a lot of random numbers there already, which can be recombined based on an input as a map.
With a map, lets call it uint treasureMap
, generated first from block variables and entropy from the previous random number, the 256 blocks that can be accessed can be traversed as a labyrinth. The output of the maze hash function (mhf) will be a uint256
.
uint treasureMap;
function mazeHashFunction() {
}
function getNthByte(uint _nth) returns (bytes1) {
bytes memory ret = toBytes(treasureMap);
return ret[_nth];
}
function toBytes(uint256 x) internal returns (bytes b) {
b = new bytes(32);
assembly { mstore(add(b, 32), x) }
}
The first byte in treasureMap
specifies a block, block.blockhash(block.number - ret[31])
. The first byte in that blockhash, is xored with the first byte of the treasureMap, generating the mutated first byte for the random number, as well as the pointer that specifies the next block in the labyrinth. In the next block, the second byte of the blockhash is xored with the second byte of the treasureMap
, generating the second byte in the random number as well as the pointer to the next block in the labyrinth. This continues once for each byte in the treasureMap
, 32 times for a uint256
.