1

My current situation, I'm creating 10 random models that store two digit numbers inside the struct and store that struct inside an array of said struct.

struct Model {
        uint a;
        uint b;
        uint c;
        uint d;
    }

Model[] private _models;

function _create(uint dna) private returns(uint) {
    uint a = uint(keccak256(dna, "a")) % 10 ** 2;
    uint b =  uint(keccak256(dna, "b")) % 10 ** 2;
    uint c = uint(keccak256(dna, "c")) % 10 ** 2;
    uint d = 1;
    return _models.push(Model(a,b,c,d)) - 1;
}

function _generate() private {
    for(uint x=0; x<10; x++){
        uint dna = uint(keccak256(now, msg.sender, x)) % 10 ** 2;
        uint id = _create(dna);
        Owners[id] = msg.sender;
        decks[msg.sender].push(cardId);
    }
}

The issue I'm having is this code is horribly expensive to execute.

  • I don't think I need the loop inside _generate since I already have dna variable there with random numbers. I'm thinking I can do something like this

        function _generate() private {
            //for(uint x=0; x< 10; x++){  //remove loop
    
                uint dna = uint(keccak256(now, msg.sender, x)); 
    
                // % 10 ** 2; //remove mod division or change it to the correct length of 10 two digit numbers so maybe 20
    
                //Help: reading chunks of two digit data from dna. 
                //I'm not sure how to split uint data types into 
                //subsets of data.
                //sudo code of taking subdata of dna and assigning it 
                //to the variables a,b,c
    
                for(x=0;x<dnaChunks.length;x++){
    
                //loop through sets of 6 digit numbers and break them into 2 digits and call _create
                   uint a = dna[0,1];
                   uint b = dna[2,3];
                   uint c = dna[4,5];
                   _create(a,b,c);
                }
                //uint id = _create(dna);
                Owners[id] = msg.sender;
                decks[msg.sender].push(cardId);
            //}
        }
    
        function _create(uint _a, uint _b, uint _c) private returns(uint) {
            uint a = _a;
            uint b = _b;
            uint c = _c
            uint d = 1;
            return _models.push(Model(a,b,c,d)) - 1;
        }
    

3 Answers 3

0

If you want to use something "random", try using the hash of the previous block (see https://www.reddit.com/r/ethereum/comments/442z66/how_to_generate_a_number_between_110_in_solidity/)

I think creating 4 random 2-digit numbers in solidity would then come to something like this:

uint randomNumber = uint(block.blockhash(block.number-1));
uint a = randomNumber % 100;
uint b = (randomNumber >> 1) % 100;
uint c = (randomNumber >> 2) % 100;
uint d = (randomNumber >> 4) % 100;

Edit: you should however be aware that this number may seem randomish to you as the developer of the contract, but it could be prone to manipulation from miners. Read the article suggested in the comments on this answer about possible ways miners can interfere with your contract: https://blog.keep.network/miners-arent-your-friends-cde9b6e0e9ac

I've been looking for ways to solve this problem as well and someone suggested other sources of randomness for Solidity contracts, like:

These things were suggested on a reddit topic on lotteries.

7
  • This solution may allow miners to manipulate the randomness to their own benefit. If the random number has "value" of any kind, using a block hash isn't recommended. Commented Jan 17, 2018 at 11:46
  • Can you explain a bit how miners could manipulate this randomness? The hash is what is calculated based on the contents of the block and the proof-of-work right?
    – jhkuperus
    Commented Jan 17, 2018 at 12:58
  • 4
    Consider a situation where there's a random selection of a "winner" of Ether, say, where a miner's address is one of ten contestants. This random number code is run by the contract owner, and is used to select the winner. When the owner submits the chooseWinner transaction, the miner can calculate the block, determine they did not win, and then mine the block without that transaction. And then try again on the next block. Eventually, the miner can force the "random" number to declare themselves the winner, at which point the transaction is included in their mined block. Commented Jan 17, 2018 at 14:08
  • See more here: blog.keep.network/miners-arent-your-friends-cde9b6e0e9ac Commented Jan 17, 2018 at 14:10
  • I need to look more into shifting because that seems to be a simple solution to creating the 2 digit chunks for a larger number
    – blackops
    Commented Jan 17, 2018 at 20:28
0

You can multiply an divide to extract digits from an uint

function splitAtTwoDigits(uint num) public pure returns (uint[4] res) {
    res[0] = (b % 100);
    res[1] = ((b / 100) % 100);
    res[2] = ((b / 10000) % 100);
    res[3] = ((b / 1000000) % 100);
}
0

The "usual" way of doing that (cutting a big number into smaller ones) is by using bit-shifting. You could do :

// Our big, 32 digits hex number
uint256 dna;
// We can create (up to) 16 2-digits hex numbers from a 32 digits one. 
for(uint i; i < 16; i++) {
  // This effectively takes the lower 8 bits ( = the 1st 2 hex digits) of the number 
  // For example, if dna is  0x0000[...]01234, shortDna will be 0x34.
  uint8 shortDna = uint8(dna);
  // Shifts the number 8 bits to the right, effectively making the 2 last hex digits become the first 2.
  // For example, if your number was 0x0000[...]01234, it's now 0x3400[...]0012;
  dna>>8;
  // Do something with shortDna, i guess. and then start again
}
1
  • Also, as it's been already mentionned, using blockhash as a randomness source is a bad idea. If this random number holds any kind of value, use an off chain source for your randomness.
    – Foxxxey
    Commented Jul 22, 2022 at 21:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.