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 call
function 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 request
address 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
)
internal
sufficientLINK(_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 nonce
assert(callbacks[requestId].callbackContract == address(0));
callbacks[requestId].callbackContract = _sender;
assert(_feePaid < 1e27); // Total LINK fits in uint96
callbacks[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 oracle
address 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);
}
}