1

I know that the "how to generate random number" in solidity is a very common question. However, after reading the great majority of answers I did not find one to fit my case.

A short description of what I want to do is: I have a list of objects that each have a unique id, a number. I need to produce a list that contains 25% of those objects, randomly selected each time the function is called. The person calling the function cannot be depended on to provide input that will somehow influence predictably the resulting list.

The only answer I found that gives a secure random number was Here. However, it depends on input coming from the participants and it is meant to address a gambling scenario. I cannot use it in my implementation.

All other cases mention that the number generated is going to be predictable, and even some of those depend on a singular input to produce a single random number. Once again, does not help me.

Summarising, I need a function that will give me multiple, non-predictable, random numbers.

Thanks for any help.

riverwastaken
  • 219
  • 4
  • 10

3 Answers3

1

Do you need these non-predictable numbers committed to the blockchain (e.g. because they determine a payment transaction), or synchronized between multiple users?

If no, then the answer given above will work. Randomness is generated from contract state using view function, which means you may as well off-load it to Javascript code calling on the client's computer, and then use Javascript's random number generator.

If you do need on-chain randomness and can wait until Ethereum 2.0 next year, there will supposedly be a non-predictable randomness function available through VDF (verifiable delay functions) or VRF (verifiable random functions), which would also be used in selecting the next validator for each block. https://our.status.im/two-point-oh-randomness/

Paul Pham
  • 638
  • 3
  • 8
0

You can use this pattern:

pragma solidity 0.4.26;

contract Test {
    uint256[] public globalArray = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];

    function getPartialArray() public view returns (uint256[] memory) {
        uint256[] memory localArray = clone(globalArray, globalArray.length);
        for (uint256 i = 0; i < localArray.length; i++) {
            uint256 n = i + uint256(keccak256(abi.encodePacked(msg.sender, now))) % (localArray.length - i);
            uint256 temp = localArray[n];
            localArray[n] = localArray[i];
            localArray[i] = temp;
        }
        return clone(localArray, localArray.length / 4);
    }

    function clone(uint256[] memory _array, uint256 _length) private pure returns (uint256[] memory) {
        uint256[] memory array = new uint256[](_length);
        for (uint256 i = 0; i < _length; i++) {
            array[i] = _array[i];
        }
        return array;
    }
}

Here is a short Truffle test to print some output (though it doesn't actually verify anything):

contract("test", accounts => {
    it("test", async () => {
        let test = await artifacts.require("Test").new();
        for (let i = 0; i < 100; i++) {
            const array = await test.getPartialArray({from: accounts[i % accounts.length]});
            console.log(array.join(", "));
        }
    });
});
goodvibration
  • 26,003
  • 5
  • 46
  • 86
  • This doesn't satisfy the non-predictable criterion set in the question. The random-generation is entirely deterministic, as now and msg.sender would both be available to any malicious contract intending to abuse the contract above. – AnAllergyToAnalogy Oct 03 '19 at 06:40
  • @AnAllergyToAnalogy: That's not possible by the definition of the blockchain (at least this blockchain), since everything is publicly viewable. – goodvibration Oct 03 '19 at 06:45
  • Exactly, so the answer to the question is that it's not doable in a single transaction. – AnAllergyToAnalogy Oct 03 '19 at 06:46
  • @AnAllergyToAnalogy: However, I believe that it actually does satisfy the specific use-case that this dude is trying to handle - some random sender who will call this function at some random time. Of course, one can generate the same numbers using that sender's address and the time at which he/she called the function. However, by the time this "malicious" user regenerates the same numbers, the original sender had already completed their activity (gambling or whatever). Of course, it's hard to tell without knowing the exact use-case at hand, but I've got a feeling that this is the intention. – goodvibration Oct 03 '19 at 06:48
  • It was specified in the question that the function be non-predictable. I assumed the person calling the function was the potential attacker. I would just create a contract that has a duplicate function, looks at the result, and then if it's favourable, executes the function on your contract, if not, it reverts. – AnAllergyToAnalogy Oct 03 '19 at 06:53
  • @AnAllergyToAnalogy: True, but let's wait to see what this dude has to say about this, given that he's got both answers at hand. – goodvibration Oct 03 '19 at 06:54
  • My only concern is that given he's a beginner (based on his score) that he might not appreciate the risks in your answer, since he specified in bold that it not be predictable. But yes, let's let the market decide. – AnAllergyToAnalogy Oct 03 '19 at 06:56
  • @AnAllergyToAnalogy: BTW, I think you're wrong about msg.sender would both be available to any malicious contract (from your first comment). Not in the actual statement, but in the implications of that statement. It is true that any malicious contract would know about it, but in order to call that function and get it to generate the exact same numbers, that contract would need the private key of that msg.sender... (continue) – goodvibration Oct 03 '19 at 07:05
  • Now, in my coding example, I wrote a view function. But imagine that in the actual system, we would take these numbers and register them on this msg.sender (i.e., save them in a mapping in the contract's storage). Then, at a given time (say, after many users have made their gamble), the owner of the system executes the gambling-draw and grants a prize to the winner. An attack of the type that you've described is not feasible in this case. – goodvibration Oct 03 '19 at 07:05
  • I assumed the function was called by any user, and the result specific to them. The questioner implied that their problem was not gambling related, but following what you've described, you still have the problem of a deterministic draw, the possibility of cheating is just shifted to the drawer. They could have played their own numbers, and then only execute the function if it benefits them. Although I think at this point, especially with multiple txes required, we are far from what the user was asking about. – AnAllergyToAnalogy Oct 03 '19 at 07:35
  • To clarify, a simple way to cheat as the drawer would be if the drawing function takes X gas to do. Then submit 2 txes simultaneously, the first (with all accoutns remaining eth) to a malicious contract which evaluates the result and if it's an advantageous result, returns the eth to msg sender. The second to the actual drawing function. If its not advantageous, the account will have no eth for gas and the tx will fail. So a dishonest drawer can't lose. – AnAllergyToAnalogy Oct 03 '19 at 07:39
0

It isn't possible to generate non-predictable numbers in a single transaction in Solidity. Any data that would be used to generate the random number would be available to an attacker immediately before the transaction, and therefore the outcome could be known before the attacker chooses to act.

AnAllergyToAnalogy
  • 3,553
  • 11
  • 23