Verify Proof

To verify the proof on chain you'll need to integrate the verifyProof function which has been separated into a second part, recoverSigner for legibility.

function _verifyProof(
    bytes32 userProvidedSeed,
    uint256 requestId,
    bytes32 randomNumber,
    bytes memory proof
) internal view returns (bool) {
    // Reconstruct the message that was signed to generate the proof
    bytes32 message = keccak256(abi.encodePacked(userProvidedSeed, requestId));

    // Hash the message according to the Ethereum signed message format
    bytes32 ethSignedMessageHash = keccak256(
        abi.encodePacked("\x19Ethereum Signed Message:\n32", message)
    );

    // Recover the signer address from the proof (signature)
    address recoveredSigner = recoverSigner(ethSignedMessageHash, proof);

    // Retrieve the VRF public key from the VRFConsumer contract
    address vrfPublicKey = vrfConsumer.getVRFPublicKey();

    // Verify that the recovered signer matches the VRF public key
    if (recoveredSigner != vrfPublicKey) {
        return false;
    }

    // Verify that the provided random number matches the hash of the signature
    return randomNumber == keccak256(proof);
}

function recoverSigner(
    bytes32 ethSignedMessageHash,
    bytes memory signature
) internal pure returns (address) {
    require(signature.length == 65, "Invalid signature length");
    bytes32 r;
    bytes32 s;
    uint8 v;
    assembly {
        // Extract the r, s, and v components from the signature
        r := mload(add(signature, 32))
        s := mload(add(signature, 64))
        v := byte(0, mload(add(signature, 96)))
    }
    // Recover and return the signer address
    return ecrecover(ethSignedMessageHash, v, r, s);
}

How The Proof Validation Works

Proof validation involves a few key steps:

  1. Recreating the Original Message:

    • The message that was signed to generate the proof is recreated. This message consists of the seed and request ID, which are unique to each randomness request.

  2. Recovering the Signer Address:

    • Using the proof (which is the cryptographic signature), the contract can recover the address of the signer. This is done using the ecrecover function, a standard Ethereum operation that retrieves the signer address from a message and its corresponding signature.

  3. Verifying the Signer:

    • The recovered address is then compared to the VRF public key. If they match, the proof is valid, meaning the random number was indeed generated by the VRF private key.

All these functions work together in the next complete example contract to provide you fully verified randomness on chain.

Last updated