VictorLink Random Number Service

Introduction

VRF (Verifiable Random Function), i.e., verifiable random function, which can generate safe and secure random numbers. The random number is determined by the user's seed, nonce (private state of the VRFCoordinator contract), the request block hash and the private key of the random number generation node, and the random number node cannot cheat. The random number is verified before it is returned to the user's dApp, thus ensuring the security of the random number.

The random number generation process is as follows.

  1. A request to generate a random number is sent by the user contract on the chain.
  2. The node listens to the request and generates the random number and the proof under the chain, and then responds in the on-chain contract.
  3. The generated random number is verified and passed by the on-chain contract and then fed back to the user Dapp with a callback function.
    It can be used in any application that requires reliable random numbers for.
  4. Blockchain games and NFTs
  5. Random assignment of duties and resources (e.g. random assignment of judges to hear cases)
  6. Selecting representative samples for consensus mechanisms
    This document describes how to deploy and use VRF contracts.

Preparation

VictorLink maintainers need to have some knowledge of VISION and be familiar with the smart contract deployment and invocation process.

VRFCoordinator Contract

The VRFCoordinator contract is a prophecy machine contract deployed on the VISION public chain. The main functions are as follows.

  1. Receiving data requests from Consumer Contract and trigger Event Log
    Send data requests with a WIN transfer as a usage fee
  2. Accepting random numbers and proofs submitted by WINkLink nodes
    VRFCoordinator receives the contract and verifies the random numbers
  3. Settling the WIN token fee for the data request and withdraw the proceeds
    The contract code is located in VRFCoordinator.sol (https://github.com/vision-consensus/victorlink/blob/master/vvm-contracts/v1.0/VRF/VRFCoordinator.sol).
    When deploying the VRFCoordinator contract, you need to provide the relevant parameters in the constructor.
constructor(address _vct, address _victorMid, address _blockHashStore)

_blockHashStore is the address of the BlockHashStore contract, _vct is the address of the VCT token, and _victorMid is the address of the VictorMid contract.
For the convenience of developers, the Vpioneer test network has deployed the VictorMid contract, which encapsulates the Vpioneer test network VCT tokens. Developers can use this contract address directly without additional deployment. Vpioneer Test Network also provides a tap address to receive test VS and VCT tokens.

Vision Master Network VictorLink VRF Information

Node Deployment

The node deployment section can be referred to VictorLink, this section only lists the differences in VRF node deployment.
Once the VRFCoordinator contract is deployed, the VictorLink node deployment can be started.
The VictorLink node code is located at: https://github.com/vision-consensus/victorlink/tree/master/node and the compiled node-v1.0.jar is located in node/build/libs/ in the project source code directory.

Node configuration

fter confirming the node configuration file, you need to create a vrfKeyStore.yml file to write the private key used to generate the VRF (multiple VRF private keys can be added):

privateKeys:
  - *****(32 bytes hex encoded private key)

Support dynamic update of vrfKeyStore without restarting the node server, as follows: First add a new VRF private key to the vrfKeyStore.yml file and then execute the following command.

curl --location --request GET 'http://localhost:8081/vrf/updateVRFKey/vrfKeyStore.yml'

Tip

In the production environment, you need to set the private file vrfKeyStore.yml with permission 600, i.e. only the owner can read and write.

Start The Node

All configuration files need to be copied to the current runtime directory of the node application, i.e. cp node/src/main/resource/*.yml . /The visionApiKey section of the application-dev file needs to be populated with apikey.
Start the VictorLink node program with the following command.

java -jar node/build/libs/node-v1.0.jar -k key.store -vrfK vrfKeyStore.yml

Specific configuration items can also be specified via the command line, e.g.
Mainnet:

java -jar node/build/libs/node-v1.0.jar --server.port=8081 --spring.profiles.active=dev --key key.store  --vrfKey vrfKeyStore.yml

Vpioneer test network.

java -jar node/build/libs/node-v1.0.jar --env dev --server.port=8081 --spring.profiles.active=dev --key key.store  --vrfKey vrfKeyStore.yml

Use the following command to determine if the VictorLink node is functioning properly.

tail -f logs/vision.log

The node account must have enough VS tokens for contract calls. This can be requested through the test network tap address.

Add a Job to a Node

The job of a node represents the data service supported by the node, and the job ID is uniquely identified by a 32-byte number.
Once the VictorLink node is operational, the job can be added to the node via the HTTP API:
Example: (modify the address parameter in the following code to be the address of the VRFCoordinator contract deployed in the above step; the publicKey parameter is the compressed value of the node's public key, which can be obtained by viewing the terminal display after the node is running, and the corresponding item is ecKey compressed)

curl --location --request POST 'http://localhost:8081/job/specs' \
  --header 'Content-Type: application/json' \
    --data-raw '{
    "initiators": [
        {
        "type": "randomnesslog",
        "params": {
            "address": "Vxxxxxxxxxx"
        }
        }
    ],
    "tasks": [
        {
        "type": "random",
        "params": {
        "publicKey":"0x024e6bda4373bea59ec613b8721bcbb56222ab2ec10b18ba24ae369b7b74ab1452"
        }
        },
        {
        "type": "visiontx",
        "params": {
            "type": "VisionVRF"
        }
    }
    ]
    }'

Query Node Job

curl --location --request GET 'http://localhost:8081/job/specs'

Authorization for Node Accounts

Node accounts need to be authorized to submit data to the VRFCoordinator contract, otherwise an error will be reported.
The owner of the VRFCoordinator contract needs to make the following contract call to add the node account to the whitelist

function registerProvingKey(uint256 _fee, address _oracle, bytes calldata _publicProvingKey, bytes32 _jobID)

Where _fee is the minimum VCT token fee for the registered node to generate a random number, _oracle is the address of the registered node to receive the VCT tokens paid by the Dapp application to it, _publicProvingKey is the public key used by the registered node to generate a random number, i.e. x||y, _jobID is the JobID of the node's VRF service.
Example calls such as registerProvingKey(10,Vxxxxxxxx, 0x4e6bda4373bea59ec613b8721bcbb56222ab2ec10b18ba24ae369b7b74ab145224d509bc2778e6d1c8a093522ba7f9b6669a9aef57d2231f856e4b594ad5f4ac , 04d773890bc347f88544dc85bea24985).

Dapp Contracts

Example Dapp contract: VRFDemo.sol(https://github.com/vision-consensus/victorlink/blob/master/vvm-contracts/v1.0/VRF/VRFDemo.sol)
The example is a game contract, VictorLink VRF requests random numbers, converts the random values to 1~20, each number represents a room, if the converted number is 1, it is assigned to Targaryan room, 2 corresponds to Lannister room, and so on.
When writing a new Dapp contract, the following rules need to be followed.
a) Introduce VRFConsumerBase:

pragma solidity ^0.6.0;

  import "./VRFConsumerBase.sol";
  
  contract VRFDemo is VRFConsumerBase {
  
  }

b) Set s_keyHash to be the VRF key used to generate random numbers; s_fee to be the fee paid for a single random number request.

bytes32 private s_keyHash;
  uint256 private s_fee;

c) Dapp contract initialization.

constructor(address vrfCoordinator, address vct, address victorMid, bytes32 keyHash, uint256 fee)
    public
    VRFConsumerBase(vrfCoordinator, vct, victorMid)
  {
    s_keyHash = keyHash;
    s_fee = fee;   
  }

d) Call requestRandomness to initiate a random number request and record the corresponding requestId:

function rollDice(uint256 userProvidedSeed, address roller)
  {
    require(winkMid.balanceOf(address(this)) >= s_fee, "Not enough VCT to pay fee");
    requestId = requestRandomness(s_keyHash, s_fee, userProvidedSeed);
    emit DiceRolled(requestId, roller);
  }

e) Implement fulfillRandomness to receive the verified random numbers requestId and randomness from the VRFCoordinator contract callback.

function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
    uint256 d20Value = randomness.mod(20).add(1);
    s_results[s_rollers[requestId]] = d20Value; 
    emit DiceLanded(requestId, d20Value);
  }

Deploy Dapp Contracts

To deploy the VRFDemo contract, you need to fill the constructor with parameters

constructor(address vrfCoordinator, address win, address victorMid, bytes32 keyHash, uint256 fee)

Where vrfCoordinator is the VRFCoordinator contract address, vct is the VCT token contract address, victorMid is the VictorMid contract address, and keyHash is the hash value of the registered node's public key, which can be obtained by calling the VRFCoordinator contract's hashOfKeyBytes function of the VRFCoordinator contract (input is x||y). fee pays the cost of the WIN token generated by the random number, which can be modified, and its value should be greater than the fee required for the registration of the random number node.
For example constructor(Vxxxxxxx,Vyyyyyy,Vzzzzzzzzz,0xe4f280f6d621db4bccd8568197e3c84e3f402c963264369a098bb2f0922cb125,12).

Transfer VCT Tokens for The Contract

The VRFDemo contract needs to call the VRFCoordinator contract, so the contract account needs to have enough VCT tokens. You can transfer a number of VCT tokens to the contract by transferring or testing the web faucet.

Calling The Dapp Contract

function rollDice(uint256 userProvidedSeed, address roller)

Where userProvidedSeed is the seed provided by the user and roller is the address of the random number used

Call Example rollDice(0x852f725894485e4979af5ea47ddd90cc68ea1ac0f4b99e52e9b91fa35a7204e2, Vxxxxxxxxx).