1.VRFConsumerBase
pragma solidity ^0.6.0;import "./vendor/SafeMathChainlink.sol";import "./interfaces/LinkTokenInterface.sol";import "./VRFRequestIDBase.sol";abstract contract VRFConsumerBase is VRFRequestIDBase {using SafeMathChainlink for uint256;/*** @notice fulfillRandomness handles the VRF response. Your contract must* @notice implement it. See "SECURITY CONSIDERATIONS" above for important* @notice principles to keep in mind when implementing your fulfillRandomness* @notice method.** @dev VRFConsumerBase expects its subcontracts to have a method with this* @dev signature, and will call it once it has verified the proof* @dev associated with the randomness. (It is triggered via a call to* @dev rawFulfillRandomness, below.)** @param requestId The Id initially returned by requestRandomness* @param randomness the VRF output*/function fulfillRandomness(bytes32 requestId, uint256 randomness)internal virtual;uint256 constant private USER_SEED_PLACEHOLDER = 0;function requestRandomness(bytes32 _keyHash, uint256 _fee)internal returns (bytes32 requestId){LINK.transferAndCall(vrfCoordinator, _fee, abi.encode(_keyHash, USER_SEED_PLACEHOLDER));// This is the seed passed to VRFCoordinator. The oracle will mix this with// the hash of the block containing this request to obtain the seed/input// which is finally passed to the VRF cryptographic machinery.uint256 vRFSeed = makeVRFInputSeed(_keyHash, USER_SEED_PLACEHOLDER, address(this), nonces[_keyHash]);// nonces[_keyHash] must stay in sync with// VRFCoordinator.nonces[_keyHash][this], which was incremented by the above// successful LINK.transferAndCall (in VRFCoordinator.randomnessRequest).// This provides protection against the user repeating their input seed,// which would result in a predictable/duplicate output, if multiple such// requests appeared in the same block.nonces[_keyHash] = nonces[_keyHash].add(1);return makeRequestId(_keyHash, vRFSeed);}LinkTokenInterface immutable internal LINK;address immutable private vrfCoordinator;// Nonces for each VRF key from which randomness has been requested.//// Must stay in sync with VRFCoordinator[_keyHash][this]mapping(bytes32 /* keyHash */ => uint256 /* nonce */) private nonces;/*** @param _vrfCoordinator address of VRFCoordinator contract* @param _link address of LINK token contract** @dev https://docs.chain.link/docs/link-token-contracts*/constructor(address _vrfCoordinator, address _link) public {vrfCoordinator = _vrfCoordinator;LINK = LinkTokenInterface(_link);}// rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF// proof. rawFulfillRandomness then calls fulfillRandomness, after validating// the origin of the callfunction rawFulfillRandomness(bytes32 requestId, uint256 randomness) external {require(msg.sender == vrfCoordinator, "Only VRFCoordinator can fulfill");fulfillRandomness(requestId, randomness);}}
1. requestRandomnessfunction requestRandomness
执行流程:
1).需要向vrfCoordinator 转账link ,同时向
2)生成传递给 VRCoordinator 的种子。oracle 会将其与包含此请求的块的哈希混合,以获得最终传递给 VRF 加密机制的种子/输入。
3) nonces[_keyHash] 必须与 VRCoordinator.nonces[_keyHash][this] 保持同步,它由上述成功的 LINK.transferAndCall(在 VRCoordinator.randomnessRequest 中)增加。 这提供了防止用户重复输入种子的保护,如果多个此类请求出现在同一块中,这将导致可预测/重复的输出。
function requestRandomness(bytes32 _keyHash, uint256 _fee)internal returns (bytes32 requestId){LINK.transferAndCall(vrfCoordinator, _fee, abi.encode(_keyHash, USER_SEED_PLACEHOLDER));// This is the seed passed to VRFCoordinator. The oracle will mix this with// the hash of the block containing this request to obtain the seed/input// which is finally passed to the VRF cryptographic machinery.uint256 vRFSeed = makeVRFInputSeed(_keyHash, USER_SEED_PLACEHOLDER, address(this), nonces[_keyHash]);//nonces[_keyHash] 必须与 VRCoordinator.nonces[_keyHash][this] 保持同步,它由上述成功的 LINK.transferAndCall(在 VRCoordinator.randomnessRequest 中)增加。// 这提供了防止用户重复输入种子的保护,如果多个此类请求出现在同一块中,这将导致可预测/重复的输出。nonces[_keyHash] = nonces[_keyHash].add(1);return makeRequestId(_keyHash, vRFSeed);}LinkTokenInterface immutable internal LINK;
2. VRFCoordinatoris
请求时触发的事件
event emit RandomnessRequest(_keyHash, preSeed, serviceAgreements[_keyHash].jobID, _sender, _feePaid, requestId);
将随机数返回时触发的事件
emit RandomnessRequestFulfilled(requestId, randomness);
contract VRFCoordinator is VRF, VRFRequestIDBase, Ownable {struct Callback { // Tracks an ongoing requestaddress callbackContract; // Requesting contract, which will receive response// Amount of LINK paid at request time. Total LINK = 1e9 * 1e18 < 2^96, so// this representation is adequate, and saves a word of storage when this// field follows the 160-bit callbackContract address.uint96 randomnessFee;// Commitment to seed passed to oracle by this contract, and the number of// the block in which the request appeared. This is the keccak256 of the// concatenation of those values. Storing this commitment saves a word of// storage.bytes32 seedAndBlockNum;}function randomnessRequest(bytes32 _keyHash,uint256 _consumerSeed,uint256 _feePaid,address _sender)internalsufficientLINK(_feePaid, _keyHash){uint256 nonce = nonces[_keyHash][_sender];uint256 preSeed = makeVRFInputSeed(_keyHash, _consumerSeed, _sender, nonce);bytes32 requestId = makeRequestId(_keyHash, preSeed);// Cryptographically guaranteed by preSeed including an increasing nonceassert(callbacks[requestId].callbackContract == address(0));callbacks[requestId].callbackContract = _sender;assert(_feePaid < 1e27); // Total LINK fits in uint96callbacks[requestId].randomnessFee = uint96(_feePaid);callbacks[requestId].seedAndBlockNum = keccak256(abi.encodePacked(preSeed, block.number));emit RandomnessRequest(_keyHash, preSeed, serviceAgreements[_keyHash].jobID,_sender, _feePaid, requestId);nonces[_keyHash][_sender] = nonces[_keyHash][_sender].add(1);}function fulfillRandomnessRequest(bytes memory _proof) public {(bytes32 currentKeyHash, Callback memory callback, bytes32 requestId,uint256 randomness) = getRandomnessFromProof(_proof);// Pay oracleaddress oadd = serviceAgreements[currentKeyHash].vRFOracle;withdrawableTokens[oadd] = withdrawableTokens[oadd].add(callback.randomnessFee);// Forget request. Must precede callback (prevents reentrancy)delete callbacks[requestId];callBackWithRandomness(requestId, randomness, callback.callbackContract);emit RandomnessRequestFulfilled(requestId, randomness);}}
