randomness Archives - Justin Silver https://www.justinsilver.com/tag/randomness/ Technology, Travel, and Pictures Tue, 29 Mar 2022 17:14:44 +0000 en-US hourly 1 https://wordpress.org/?v=6.0.1 https://www.justinsilver.com/wp-content/uploads/2013/06/cropped-apple-touch-icon-160x160.png randomness Archives - Justin Silver https://www.justinsilver.com/tag/randomness/ 32 32 NFT – Mint Random Token ID https://www.justinsilver.com/technology/cryptocurrency/nft-mint-random-token-id/?utm_source=rss&utm_medium=rss&utm_campaign=nft-mint-random-token-id https://www.justinsilver.com/technology/cryptocurrency/nft-mint-random-token-id/#comments Thu, 17 Feb 2022 08:36:28 +0000 https://www.justinsilver.com/?p=5055 The perceived value of many NFTs is based on that item’s rarity making it ideal to mint them fairly. Rarity snipers and bad actors on a team can scoop up rare items from a...

The post NFT – Mint Random Token ID appeared first on Justin Silver.

]]>
AmpedSense.OptimizeAdSpot('AP'); AmpedSense.OptimizeAdSpot('IL'); AmpedSense.OptimizeAdSpot('IR');

The perceived value of many NFTs is based on that item’s rarity making it ideal to mint them fairly. Rarity snipers and bad actors on a team can scoop up rare items from a collection in an attempt to further profit on the secondary market. How can you both fairly distribute the tokens – both to the community and the project team?

One solution is to hide the metadata until after reveal and mint the token IDs out of order – either using a provable random number, a pseudo random number, as pseudo random number seeded with a provable random beacon, or other options depend on your security needs. For provable random numbers check out Provable Randomness with VDF.

How It Works

uint16[100] public ids;
uint16 private index;

function _pickRandomUniqueId(uint256 random) private returns (uint256 id) {
    uint256 len = ids.length - index++;
    require(len > 0, 'no ids left');
    uint256 randomIndex = random % len;
    id = ids[randomIndex] != 0 ? ids[randomIndex] : randomIndex;
    ids[randomIndex] = uint16(ids[len - 1] == 0 ? len - 1 : ids[len - 1]);
    ids[len - 1] = 0;
}

We can efficiently track which IDs have – and have not – been minted by starting with an empty and cheap to create array of empty values with a size that matches your total supply. The array size could suffice in lieu of tracking the index, but this is more gas efficient than pop()ing the array. For each round it will select a random index, bounded by the remaining supply – we will call this a “roll” as in “roll of the dice” except we will reduce the number of sides by one for each round.

Round 0

When we start the Data array will match the supply we want to create (five) and be empty (all zeroes), as well our Results, which are just empty (this represents the token ids that would be minted).

Index:    0  1  2  3  4  5
--------------------------
Data:    [0, 0, 0, 0, 0, 0] 
Results: []
Round 1: 3

For the first round, let’s say it’s a 3. We look at the third index, check to see if it is 0, and if is we return the index – this will make more sense in a moment. Next we look at the last position in the array given our remaining supply and if it is a 0 we move that index to the 3 position we rolled.

Index:    0  1  2 *3* 4  5
---------------------------
Data:    [0, 0, 0, 0, 0, 0] 
Results: []

<< before
after >>

Data:    [0, 0, 0, 5, 0] 
Results: [3]
Round 2: 3

In the previous step when we check an index for a value, if a value was set a that index we would use it rather than the index. To demonstrate this, let’s assume we rolled a 3 again. This time we look at this third position and it contains a 5, so we return that instead of a three. This is great, because we already selected a 3 and we want these to be unique. Again we look at the last position, and since it is not set we set the index 4 as the value of index 3.

Index:    0  1  2 *3* 4  5
---------------------------
Data:    [0, 0, 0, 5, 0] 
Results: [3]

<< before
after >>

Data:    [0, 0, 0, 4] 
Results: [3, 5]
Round 3: 2

Next, we roll a 2 again. We look at position 2, it’s not set, so we return a 2, again a number we haven’t selected previously. Next we check the last position which now as a 4 set, so it is moved into index 2 as we have yet to select it.

Index:    0  1 *2* 3  4  5
---------------------------
Data:    [0, 0, 0, 4] 
Results: [3, 5]

<< before
after >>

Data:    [0, 0, 4] 
Results: [3, 5, 2]
Round 4: 1

We roll a 1, and since the first index contains a 4 we move that to our results.

Index:    0 *1* 2  3  4  5
---------------------------
Data:    [0, 0, 4] 
Results: [3, 5, 2]

<< before
after >>

Data:    [0, 4] 
Results: [3, 5, 2, 1]
Round 5: 1

We roll a 1 again. This time we return the 4, but since there is nothing to move into its place, we move on.

Index:    0 *1* 2  3  4  5
---------------------------
Data:    [0, 4] 
Results: [3, 5, 2, 1]

<< before
after >>

Data:    [0] 
Results: [3, 5, 2, 1, 4]
Round 6: 0

Lastly, we get a 0, since that’s all that remains. It both contains a 0 and is in that position so we select a 0.

Index:   *0* 1  2  3  4  5
---------------------------
Data:    [0] 
Results: [3, 5, 2, 1, 4]

<< before
after >>

Data:    [] 
Results: [3, 5, 2, 1, 4, 0]

TLDR;

Each index of the array tracks an unminted ID. If the position isn’t set, that ID hasn’t been minted. If it is set, it’s because the last position was moved to it when the available indexes shrank and the last index wasn’t the one selected so we want to preserve it. If you want to start minting at 1, add 1.

Pseudo Random

Uses a pseudo random number to select from a unique set of token IDs.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

import '@openzeppelin/contracts/token/ERC721/ERC721.sol';

contract RandomTokenIdv1 is ERC721  {

    uint16[100] public ids;
    uint16 private index;

    constructor() ERC721('RandomIdv1', 'RNDMv1') {}

    function mint(address[] calldata _to) external {
        for (uint256 i = 0; i < _to.length; i++) {
            uint256 _random = uint256(keccak256(abi.encodePacked(index++, msg.sender, block.timestamp, blockhash(block.number-1))));
            _safeMint(_to[i], _pickRandomUniqueId(random));
        }
    }

    function _pickRandomUniqueId(uint256 random) private returns (uint256 id) {
        uint256 len = ids.length - index++;
        require(len > 0, 'no ids left');
        uint256 randomIndex = random % len;
        id = ids[randomIndex] != 0 ? ids[randomIndex] : randomIndex;
        ids[randomIndex] = uint16(ids[len - 1] == 0 ? len - 1 : ids[len - 1]);
        ids[len - 1] = 0;
    }

}

Provable Random

Uses a provable random number and derivatives to select from a unique set of token IDs.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

import '@openzeppelin/contracts/token/ERC721/ERC721.sol';
import './libraries/Randomness.sol';
import './libraries/SlothVDF.sol';

contract RandomTokenIdv2 is ERC721  {

    using Randomness for Randomness.RNG;
    
    Randomness.RNG private _rng;

    mapping(address => uint256) public seeds;
    uint256 public prime = 432211379112113246928842014508850435796007;
    uint256 public iterations = 1000;

    uint16[100] public ids;
    uint16 private index;

    constructor() ERC721('RandomIdv2', 'RNDMv2') {}

    function createSeed() external payable {
        seeds[msg.sender] = _rng.getRandom();
    }

    function mint(address[] calldata _to, uint256 proof) external {
        require(SlothVDF.verify(proof, seeds[msg.sender], prime, iterations), 'Invalid proof');

        uint256 _randomness = proof;
        uint256 _random;
        for (uint256 i = 0; i < _to.length; i++) {
            (_randomness, _random) = _rng.getRandom(_randomness);
            _safeMint(_to[i], _pickRandomUniqueId(_random));
        }
    }

    function _pickRandomUniqueId(uint256 random) private returns (uint256 id) {
        uint256 len = ids.length - index++;
        require(len > 0, 'no ids left');
        uint256 randomIndex = random % len;
        id = ids[randomIndex] != 0 ? ids[randomIndex] : randomIndex;
        ids[randomIndex] = uint16(ids[len - 1] == 0 ? len - 1 : ids[len - 1]);
        ids[len - 1] = 0;
    }

}

Random Beacon

Uses a provable random number as a beacon which is used as the seed for a pseudo random number and derivatives to select from a unique set of token IDs.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;
 
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/token/ERC721/ERC721.sol';
import './libraries/Randomness.sol';
import './libraries/SlothVDF.sol';
 
contract RandomTokenIdv3 is ERC721, Ownable  {

    using Randomness for Randomness.RNG;
     
    Randomness.RNG private _rng;

    uint16[100] public ids;
    uint16 private index;
 
    uint256 public prime = 432211379112113246928842014508850435796007;
    uint256 public iterations = 1000;
    uint256 public seed;
    uint256 public beacon;
 
    constructor() ERC721('RandomTokenIdv3', 'RNDMv3') {}
 
    // create a set - use something interesting for the input.
    function createSeed() external onlyOwner {
        (uint256, uint256 _random) = _rng.getRandom();
        seed = _random;
    }
 
    // once calclated set the beacon
    function setBeacon(uint256 proof) external {
        require(SlothVDF.verify(proof, seeds[msg.sender], prime, iterations), 'Invalid proof');
        beacon = proof;
    }

    function mint(address[] calldata _to) external {
        require(beacon > 0, 'Beacon not set');
        uint256 _randomness = beacon;
        uint256 _random;
        for (uint256 i = 0; i < _to.length; i++) {
            (_randomness, _random) = _rng.getRandom(_randomness);
            _safeMint(_to[i], _pickRandomUniqueId(_random));
        }
    }
 
    function _pickRandomUniqueId(uint256 random) private returns (uint256 id) {
        uint256 len = ids.length - index++;
        require(len > 0, 'no ids left');
        uint256 randomIndex = random % len;
        id = ids[randomIndex] != 0 ? ids[randomIndex] : randomIndex;
        ids[randomIndex] = uint16(ids[len - 1] == 0 ? len - 1 : ids[len - 1]);
        ids[len - 1] = 0;
    }
 
}

The post NFT – Mint Random Token ID appeared first on Justin Silver.

]]>
https://www.justinsilver.com/technology/cryptocurrency/nft-mint-random-token-id/feed/ 1
Provable Randomness with VDF https://www.justinsilver.com/technology/cryptocurrency/provable-randomness-with-vdf/?utm_source=rss&utm_medium=rss&utm_campaign=provable-randomness-with-vdf https://www.justinsilver.com/technology/cryptocurrency/provable-randomness-with-vdf/#respond Mon, 07 Feb 2022 05:31:01 +0000 https://www.justinsilver.com/?p=5019 A Verifiable Delay Function (VDF) is a linearly computed function that takes a relatively long time to calculate, however the resulting proof can be verified to be the result of this computation in a...

The post Provable Randomness with VDF appeared first on Justin Silver.

]]>
AmpedSense.OptimizeAdSpot('AP'); AmpedSense.OptimizeAdSpot('IL'); AmpedSense.OptimizeAdSpot('IR');

A Verifiable Delay Function (VDF) is a linearly computed function that takes a relatively long time to calculate, however the resulting proof can be verified to be the result of this computation in a much shorter period of time. Since the computation can’t be sped up through parallelization or other tricks we can be sure that for a given seed the resulting value can’t be known ahead of time – thus making it a provable random number.

We can apply this to a blockchain to achieve provable randomness without an oracle by having the client compute the VDF. This process takes two transactions – the first to commit to the process and generate a seed for the VDF input, and the second to submit the calculated proof. If the length of time to calculate the VDF proof exceeds the block finality for the chain you are using, then the result of the second transaction can’t be known when the seed is generated and can thus be used as a provable random number. For more secure applications you can use multiple threads to calculate multiple VDF proofs concurrently, or for less strict requirements you can bitshift the value to get “new” random numbers.

Provable Random Numbers

The good stuff first – provable random numbers without an oracle. The user first makes a request to createSeed() typically with a commitment such as payment. This seed value along with the large prime and number of iterations are used to calculate the VDF proof – the larger the prime and the higher the iterations, the longer the proof takes to calculate and can be adjusted as needed. As long as the number of iterations takes longer to compute than the block finality we know it’s random since it’s not possible to know the result before it’s too late to change it. A blockchain like Fantom is ideal for this application with block times of ~1 second and finality after one block – validators cannot reorder blocks once the are minted.

This proof is then passed in to the prove() function. It uses the previously created seed – which now can’t be changed – and other inputs to verify the proof. If it passes, the value can be used as a random number, or can be passed into another function (as below) to create multiple random numbers by shifting the bits on each request for a random(ish) number.

Smart Contract

You can find large primes for your needs using https://bigprimes.org/, potentially even rotating them. Note that the code below is an example and should not be used directly without modifying for your needs.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

import './libraries/SlothVDF.sol';

contract RandomVDFv1  {
    
    // large prime
    uint256 public prime = 432211379112113246928842014508850435796007;
    // adjust for block finality
    uint256 public iterations = 1000;
    // increment nonce to increase entropy
    uint256 private nonce;
    // address -> vdf seed
    mapping(address => uint256) public seeds;

    function createSeed() external payable {
        // commit funds/tokens/etc here
        // create a pseudo random seed as the input
        seeds[msg.sender] = uint256(keccak256(abi.encodePacked(msg.sender, nonce++, block.timestamp, blockhash(block.number - 1))));
    }

    function prove(uint256 proof) external {
        // see if the proof is valid for the seed associated with the address
        require(SlothVDF.verify(proof, seeds[msg.sender], prime, iterations), 'Invalid proof');

        // use the proof as a provable random number
        uint256 _random = proof;
    }
}

Hardhat Example

This code is an example Hardhat script for calling the RandomVDFv1 contract. It shows the delay to calculate a proof and attempts to submit it. In a real implementation this could be an NFT mint, etc.

import { ethers, deployments } from 'hardhat';
import { RandomVDFv1 } from '../sdk/types';
import sloth from './slothVDF';

async function main() {
  // We get the signer
  const [signer] = await ethers.getSigners();

  // get the contracts
  const deploy = await deployments.get('RandomVDFv1');
  const token = (await ethers.getContractAt('RandomVDFv1', deploy.address, signer)) as RandomVDFv1;

  // the prime and iterations from the contract
  const prime = BigInt((await token.prime()).toString());
  const iterations = BigInt((await token.iterations()).toNumber());
  console.log('prime', prime.toString());
  console.log('iterations', iterations.toString());

  // create a new seed
  const tx = await token.createSeed();
  await tx.wait();

  // get the seed
  const seed = BigInt((await token.seeds(signer.address)).toString());
  console.log('seed', seed.toString());

  // compute the proof
  const start = Date.now();
  const proof = sloth.computeBeacon(seed, prime, iterations);
  console.log('compute time', Date.now() - start, 'ms', 'vdf proof', proof);

  // this could be a mint function, etc
  const proofTx = await token.prove(proof);
  await proofTx.wait();
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});

Sloth Verifiable Delay

This off-chain implementation of Sloth VDF in Typescript will let us compute the proof on the client.

const bexmod = (base: bigint, exponent: bigint, modulus: bigint) => {
  let result = 1n;
  for (; exponent > 0n; exponent >>= 1n) {
    if (exponent & 1n) {
      result = (result * base) % modulus;
    }
    base = (base * base) % modulus;
  }
  return result;
};

const sloth = {
  compute(seed: bigint, prime: bigint, iterations: bigint) {
    const exponent = (prime + 1n) >> 2n;
    let beacon = seed % prime;
    for (let i = 0n; i < iterations; ++i) {
      beacon = bexmod(beacon, exponent, prime);
    }
    return beacon;
  },
  verify(beacon: bigint, seed: bigint, prime: bigint, iterations: bigint) {
    for (let i = 0n; i < iterations; ++i) {
      beacon = (beacon * beacon) % prime;
    }
    seed %= prime;
    if (seed == beacon) return true;
    if (prime - seed === beacon) return true;
    return false;
  },
};

export default sloth;

Next we need to be able to verify the Sloth VDF proof on chain which is easy using the following library.

// SPDX-License-Identifier: MIT
// https://eprint.iacr.org/2015/366.pdf

pragma solidity ^0.8.11;

library SlothVDF {

    /// @dev pow(base, exponent, modulus)
    /// @param base base
    /// @param exponent exponent
    /// @param modulus modulus
    function bexmod(
        uint256 base,
        uint256 exponent,
        uint256 modulus
    ) internal pure returns (uint256) {
        uint256 _result = 1;
        uint256 _base = base;
        for (; exponent > 0; exponent >>= 1) {
            if (exponent & 1 == 1) {
                _result = mulmod(_result, _base, modulus);
            }

            _base = mulmod(_base, _base, modulus);
        }
        return _result;
    }

    /// @dev compute sloth starting from seed, over prime, for iterations
    /// @param _seed seed
    /// @param _prime prime
    /// @param _iterations number of iterations
    /// @return sloth result
    function compute(
        uint256 _seed,
        uint256 _prime,
        uint256 _iterations
    ) internal pure returns (uint256) {
        uint256 _exponent = (_prime + 1) >> 2;
        _seed %= _prime;
        for (uint256 i; i < _iterations; ++i) {
            _seed = bexmod(_seed, _exponent, _prime);
        }
        return _seed;
    }
    
    /// @dev verify sloth result proof, starting from seed, over prime, for iterations
    /// @param _proof result
    /// @param _seed seed
    /// @param _prime prime
    /// @param _iterations number of iterations
    /// @return true if y is a quadratic residue modulo p
    function verify(
        uint256 _proof,
        uint256 _seed,
        uint256 _prime,
        uint256 _iterations
    ) internal pure returns (bool) {
        for (uint256 i; i < _iterations; ++i) {
            _proof = mulmod(_proof, _proof, _prime);
        }
        _seed %= _prime;
        if (_seed == _proof) return true;
        if (_prime - _seed == _proof) return true;
        return false;
    }
}

Randomness Library

Instead of using the proof directly as a single random number we can use it as the input to a random number generator for multiple provable random numbers. If we want to save a bit more gas instead of calling for a new number every time we can just shift the bits of the random number to the right and refill it when it empties. This pattern is more efficient if implemented directly your contract, but this library can be reused if you can support the relaxed security.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

library Randomness {

    // memory struct for rand
    struct RNG {
        uint256 seed;
        uint256 nonce;
    }

    /// @dev get a random number
    function getRandom(RNG storage _rng) external returns (uint256 randomness, uint256 random) {
        return _getRandom(_rng, 0, 2**256 - 1, _rng.seed);
    }

    /// @dev get a random number
    function getRandom(RNG storage _rng, uint256 _randomness) external returns (uint256 randomness, uint256 random) {
        return _getRandom(_rng, _randomness, 2**256 - 1, _rng.seed);
    }

    /// @dev get a random number passing in a custom seed
    function getRandom(
        RNG storage _rng,
        uint256 _randomness,
        uint256 _seed
    ) external returns (uint256 randomness, uint256 random) {
        return _getRandom(_rng, _randomness, 2**256 - 1, _seed);
    }

    /// @dev get a random number in range (0, _max)
    function getRandomRange(
        RNG storage _rng,
        uint256 _max
    ) external returns (uint256 randomness, uint256 random) {
        return _getRandom(_rng, 0, _max, _rng.seed);
    }

    /// @dev get a random number in range (0, _max)
    function getRandomRange(
        RNG storage _rng,
        uint256 _randomness,
        uint256 _max
    ) external returns (uint256 randomness, uint256 random) {
        return _getRandom(_rng, _randomness, _max, _rng.seed);
    }

    /// @dev get a random number in range (0, _max) passing in a custom seed
    function getRandomRange(
        RNG storage _rng,
        uint256 _randomness,
        uint256 _max,
        uint256 _seed
    ) external returns (uint256 randomness, uint256 random) {
        return _getRandom(_rng, _randomness, _max, _seed);
    }

    /// @dev fullfill a random number request for the given inputs, incrementing the nonce, and returning the random number
    function _getRandom(
        RNG storage _rng,
        uint256 _randomness,
        uint256 _max,
        uint256 _seed
    ) internal returns (uint256 randomness, uint256 random) {
        // if the randomness is zero, we need to fill it
        if (_randomness <= 0) {
            // increment the nonce in case we roll over
            _randomness = uint256(
                keccak256(
                    abi.encodePacked(_seed, _rng.nonce++, block.timestamp, msg.sender, blockhash(block.number - 1))
                )
            );
        }
        // mod to the requested range
        random = _randomness % _max;
        // shift bits to the right to get a new random number
        randomness = _randomness >>= 4;
    }
}

This example uses the Randomness library to generate multiple random numbers from a single proof in an efficient way. Note that this is a less secure application, though still valid for many use cases.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

import './libraries/Randomness.sol';
import './libraries/SlothVDF.sol';

contract RandomVDFv2  {

    using Randomness for Randomness.RNG;

    Randomness.RNG private _rng;
    
    // large prime
    uint256 public prime = 432211379112113246928842014508850435796007;
    // adjust for block finality
    uint256 public iterations = 1000;
    // increment nonce to increase entropy
    uint256 private nonce;
    // address -> vdf seed
    mapping(address => uint256) public seeds;

    // commit funds/tokens/etc here
    function createSeed() external payable {
        // create a pseudo random seed as the input
        seeds[msg.sender] = Randomness.RNG(0, nonce++).getRandom();
    }

    function prove(uint256 proof) external {
        // see if the proof is valid for the seed associated with the address
        require(SlothVDF.verify(proof, seeds[msg.sender], prime, iterations), 'Invalid proof');
        
        uint256 _randomness;
        uint256 _random;
        
        (_randomness, _random) = _rng.getRandom(_randomness, proof);
        (_randomness, _random) = _rng.getRandom(_randomness, proof);
        (_randomness, _random) = _rng.getRandom(_randomness, proof);
    }
}

The post Provable Randomness with VDF appeared first on Justin Silver.

]]>
https://www.justinsilver.com/technology/cryptocurrency/provable-randomness-with-vdf/feed/ 0