# Using Existing Sygma MPC relayers as a VRF Oracle Sygma (our cross-chain product) has a committee of relayers who run software which watches some origin chain and, upon observing certain events, collectively signs a transaction using threshold ECDSA via MPC to be submitted to the destination. In theory the same committee and same procedure could be used to implement a Verifiable Random Function (VRF). The committee observes a chain, sees an event, collectively computes the VRF output via MPC then submits it back to the same chain. **The aim of this project is to:** 1. Understand ChainSafe VRF-Lootbox contract dependency on Chainlink VRF contracts. 2. Provide a detailed overview of Chainlink's VRF implementation. 3. Identify the minimal changes required for [sygma relayers](https://github.com/sygmaprotocol/sygma-relayer) such that the relayers can also perform the VRF role. - See if it is possible to be compatible with existing [Chainlink VRF verifier contract](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/vrf/VRF.sol) 4. Design a specification of the work required to make the changes to Sygma-relayer and an upgrade path for existing relayers to add the new role 5. Future work considerations for MPC based relayer VRF upgrades. ### Proposal Introduction Chainlink's VRF oracles are selected by the consuming contract upon [direct funding](https://docs.chain.link/vrf/v2/direct-funding) or [subscription](https://docs.chain.link/vrf/v2/subscription/supported-networks/#overview) through thier public key hash depending on the blockchain and gas limit needs of the consuming contract. This method relies on trusting a single node through its reputation in Chainlink's oracle network. Although this method is centralized and not ideal, we also have not found MPC adaptations that are vetted or part of any standards in literature so far. We suggest that the minimal work approach for Sygma to implement a one-server-to-one-VRF-request service similar to Chainlink, with the addition of an aggregator node for preventing front running attacks. The rest of the proposal goes as follows: In section one we provide details on how ChainSafe's lootbox contract requests verifiable randomness from Chainlink. Next in Section two we describe how Chainlink's VRF implementation works focusing on the aspects that matter for Sygma. Afterwards we propose a minimal changes implementation path for relayer based VRF service in Section three. A specification of work requried for the minimal change path is provided in Section four. Finally, in Section five we conclude with future work paths for MPC style implementations of VRF using the Sygma relayers, among other considerations. ## 1. How does ChainSafe VRF-Lootbox contract request randomness from Chainlink VRF First we generically guide through understanding the Chainlink contract interactions that facilitate the VRF process. Afterwards, we demonstrate how ChainSafe's VRF-Lootbox contract engages Chainlink's contracts. ### 1.1 Chainlink VRF Contract Overview The process of making a VRF request from Chainlink involves several smart contracts, each with its own role in ensuring the secure, decentralized generation of random numbers. The key contracts involved are [VRFCoordinatorV2](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/vrf/VRFCoordinatorV2.sol), [VRFV2WrapperConsumerBase](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/vrf/VRFV2WrapperConsumerBase.sol), and [VRF](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/vrf/VRF.sol) (as part of the cryptographic operations). #### **1.1.1 VRFCoordinatorV2 Contract:** The `VRFCoordinatorV2` contract acts as the central coordinator for VRF requests and responses. Users interact with this contract to request random numbers, and it coordinates with the Chainlink network to generate and return these numbers securely. Here is how the process works in the context of the `VRFCoordinatorV2` contract: 1. **Requesting Randomness:** A user or smart contract that needs a random number calls the `requestRandomWords()` function in the `VRFCoordinatorV2`. This function takes parameters like the key hash (identifying the specific Chainlink VRF key), the subscription ID, the number of confirmations, the callback gas limit, and the number of random words required. ```solidity function requestRandomWords( bytes32 keyHash, uint64 subId, uint16 requestConfirmations, uint32 callbackGasLimit, uint32 numWords ) external returns (uint256 requestId) { // ... (uint256 requestId, uint256 preSeed) = _computeRequestId(keyHash, msg.sender, subId, nonce); // ... } ``` The seed, or `preseed` is computed along with the request ID through calling `_computeRequestId` from within `requestRandomWords`. ```solidity function _computeRequestId( bytes32 keyHash, address sender, uint64 subId, uint64 nonce ) private pure returns (uint256, uint256) { uint256 preSeed = uint256(keccak256(abi.encode(keyHash, sender, subId, nonce))); return (uint256(keccak256(abi.encode(keyHash, preSeed))), preSeed); } ``` 2. **Fulfilling Requests:** The Chainlink nodes monitor these requests and generate random numbers using the VRF process. Once the process is complete, a corresponding `fulfillRandomWords()` function is called by the Chainlink node to return the randomness back to the user's contract. ```solidity function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) external { // Function implementation... } ``` #### **1.1.2 VRFV2WrapperConsumerBase Contract:** This contract is a base for user contracts that want to interact with the Chainlink VRF without managing subscriptions directly. Instead of directly interacting with the `VRFCoordinatorV2`, consumer contracts can extend `VRFV2WrapperConsumerBase` to request randomness in a more simplified manner. The consumer would implement the `fulfillRandomWords()` function to handle the randomness once it's returned: ```solidity function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override { // Handle the incoming random words... } ``` In the context of making a VRF request, the consumer would call `requestRandomness()`, which is a method defined in `VRFV2WrapperConsumerBase` that simplifies the interaction with the VRF Coordinator: ```solidity function requestRandomness( uint32 callbackGasLimit, uint16 requestConfirmations, uint32 numWords ) internal returns (uint256 requestId) { // Request implementation... } ``` #### **1.1.3 VRF Contract:** This contract contains the cryptographic functions necessary for the VRF process. It's not directly called by users but is utilized by the Chainlink VRF infrastructure to ensure the randomness provided is verifiable and secure. The critical function in this context is `_randomValueFromVRFProof()`, which verifies the VRF proof and returns the random value if the proof is valid: ```solidity function _randomValueFromVRFProof(Proof memory proof, uint256 seed) internal view returns (uint256 output) { // Verification and randomness extraction... } ``` The `VRF` contract ensures the integrity and verifiability of the randomness generation process, working behind the scenes as part of the overall Chainlink VRF system. #### **1.1.4 Summary** To make a VRF request from Chainlink, a consumer contract extends `VRFV2WrapperConsumerBase`, calls `requestRandomness()` with the desired parameters, and implements `fulfillRandomWords()` to handle the returned random numbers. The `VRFCoordinatorV2` coordinates these requests and responses, while the cryptographic guarantees are provided by the `VRF` contract. ### 1.2 ChainSafe VRF-Lootbox engagement of Chainlink VRF Next we describe how the ChainSafe VRF-Lootbox contract [Lootbox.sol](https://github.com/ChainSafe/vrf-lootbox-contracts/blob/main/contracts/Lootbox.sol) requests randomness from Chainlink's VRF contracts. We break down the interaction between the key contracts in making a VRF request as follows: #### **1.2.1 Contract inheritance and initialization** The ChainSafe `Lootbox` contract inherits `VRFV2WrapperConsumerBase` contract from Chainlink providing two functions, `requestRandomness` and `fulfillRandomWords`, for requesting and fulfilling randomness from Chainlink VRF. ```solidity contract Lootbox is VRFV2WrapperConsumerBase, ERC721Holder, ERC1155Holder, ERC1155Base, Multicall { //rest of initialization } ``` #### **1.2.2 Requesting randomness** The randomness from Chainlink is requested in the Lootbox contract through the following mechanism: 1. **_requestRandomness Function**: This internal Lootbox contract function is where the randomness is requested from Chainlink's VRF. It's invoked within the `_requestOpen` function, which is used when a user wants to open a lootbox. ```solidity function _requestRandomness(uint32 _gas) internal returns (uint256 requestId) { return requestRandomness( _gas, REQUEST_CONFIRMATIONS, NUMWORDS ); } ``` The `_requestRandomness` function is a wrapper around the `requestRandomness` function, inherited from the `VRFV2WrapperConsumerBase`. It takes the following parameters: gas limit for the callback (`_gas`), the number of confirmations (`REQUEST_CONFIRMATIONS`), and the number of random words required (`NUMWORDS`). 2. **Open Functionality**: The actual request for randomness is initiated when a user tries to open a lootbox, which is handled in the `open` function: ```solidity function open(uint32 _gas, uint[] calldata _lootIds, uint[] calldata _lootAmounts) external notEmergency() payable { // ... code ... uint unitsToGet = _requestOpen(opener, _gas, _lootIds, _lootAmounts); // ... code ... } ``` Inside `_requestOpen`, after validating the user's request and burning the required tokens, `_requestRandomness` is called to initiate the random number generation process: ```solidity uint256 requestId = _requestRandomness(_gas); ``` `requestId` is then used to track the random number request and match it with the fulfillment callback described below. #### **1.2.3 Fulfilling randomness request** Once the randomness request is processed by Chainlink VRF, the Chainlink node calls back the `fulfillRandomWords` function with the requested random numbers: ```solidity function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override { try this._allocateRewards{gas: gasleft() - 20000}(requestId, randomWords[0]) { emit OpenRequestFulfilled(requestId, randomWords[0]); } ``` The randomness provided by Chainlink as `randomWords[0]` along with the matching request ID are emited as an event to be utilized by the ChainSafe `Lootbox` contract. ## 2. Chainlink VRF details related to Sygma implementation As described above, the Chainlink VRF implementation consists of several on-chain contracts that implements verification of VRFs paired with off-chain computations via their decentralized oracle network (DON). The Chainlink DON is a collection of oracles (nodes) which run Chainlink core software and participate in the network. A summary of how requests are processed goes as follows: - When a contract sends a request for randomness, a hash of the public key related to the VRF protocol of the node being requested is used as input, calling on the node to perform the incoming request. - The proof component of the VRF and accompanying randomness is computed off-chain. - On-chain verification is invoked by Chainlinks coordinator contract. - ChainSafe's VRF-Lootbox contract overloaded callback function `fulfillRandomWords` is then called on-chain after verification is accepted for use. Next we describe Chainlink's ECVRF implementation in detail. ### 2.1 Chainlink ECVRF implementation Chainlink VRF utilizes a Goldberg’s VRF [standard](https://www.ietf.org/archive/id/draft-irtf-cfrg-vrf-10.html#name-elliptic-curve-vrf-ecvrf) ([security proofs](https://eprint.iacr.org/2017/099.pdf)) to generate randomness in a way that is unpredictable to anyone without the secret key. The process involves the following steps, and is based on Chainlink's technical blog [description](https://blog.chain.link/chainlink-vrf-on-chain-verifiable-randomness/#how_chainlink_vrf_works): #### **Setup Phase (Performed by Oracle):** 1. [**Oracle Key Generation**:](https://github.com/smartcontractkit/chainlink/blob/develop/core/services/keystore/keys/vrfkey/private_key.go) - The oracle generates a VRF secret key (scalar `sk`) by securely choosing a number from a uniform distribution within the range of `{0, …, #secp256k1-1}`. - A corresponding public key `pk` is computed from this secret key. This public key `pk` will be used to identify the oracle. - The key pair is only computed once and the public key is required for all future verifications. 2. [**Oracle Registration**:](https://github.com/smartcontractkit/chainlink/blob/e74aeab286f642bdc5b168d8e6f716d92bfcc8ea/contracts/src/v0.8/vrf/VRFCoordinatorV2.sol#L169) - The oracle registers its VRF public key `pk` on the blockchain through the `VRFCoordinatorV2` contract. This registration process also includes specifying the Chainlink job-ID which the oracle will respond to. #### **Request Phase (Initiated by Consumer Contract):** 3. [**Randomness Request**:](https://github.com/smartcontractkit/chainlink/blob/e74aeab286f642bdc5b168d8e6f716d92bfcc8ea/contracts/src/v0.8/vrf/VRFCoordinatorV2.sol#L349) - A consumer contract that requires randomness calls the `requestRandomWords` function from the VRFCoordinatorV2 contract ( or through consumer base contract if using subscription), specifying parameters including the `keyHash` which corresponds to the oracle's public key `pk` that is being requested and an request input seed. 4. [**Broadcast and Oracle Notification**:](https://github.com/smartcontractkit/chainlink/blob/e74aeab286f642bdc5b168d8e6f716d92bfcc8ea/contracts/src/v0.8/vrf/VRFCoordinatorV2.sol#L395) - Older Chainlink blog says this request is broadcasted as an Ethereum log. In newer version V2, looks like there is an event emit instead. The designated oracle detects this emit based on the matching `keyHash` and starts the process to generate a VRF output. #### **Generation Phase (Performed by Oracle):** 5. [**Hashing Input to Curve**:](https://github.com/smartcontractkit/chainlink/blob/93762ccbd868b9e227abf3220afb9ad22ba41b92/core/services/keystore/keys/vrfkey/crypto.go#L144) - The oracle obtains a cryptographically secure random sample from secp256k1, using block data and the oracle’s public key `pk` as inputs. It does this by recursively hashing the inputs using keccak256 until the output is less than the order of secp256k1’s base field. - The hashed value is mapped as the x coordinate of some point `(x,y)` on secp256k1 where the value of `y` computed by `y^2=x^3+7` in the base field. - `(x,y)` is then the hash of the input to the curve. 6. [**VRF Output Generation**:](https://github.com/smartcontractkit/chainlink/blob/93762ccbd868b9e227abf3220afb9ad22ba41b92/core/services/keystore/keys/vrfkey/key_v2.go#L72) - The oracle computes `Ɣ = (x,y) * sk`, where `(x,y)` is the point on the curve obtained in the previous step. The VRF output is the keccak256 hash of `Ɣ` as a uint256. 7. [**Proof Generation**:](https://github.com/smartcontractkit/chainlink/blob/93762ccbd868b9e227abf3220afb9ad22ba41b92/core/services/keystore/keys/vrfkey/key_v2.go#L72) - The oracle generates a proof (similar to a Schnorr signature) that `Ɣ` is the same multiple of `(x,y)` as the oracle’s public key `pk` is of the secp256k1 generator `g`. - This involves securely sampling a nonce `n` in range of `{0, …, #secp256k1-1}`, computing points `u = n * g` and `v = n * (x,y)`, and taking the ethereum address of `u`. - Then computes `c` by hashing together `(x,y)`, the oracles VRF public key, `Ɣ`, the address of `u`, and `v`, and takes the remainder of that hash modulo `#secp256k1`. - Finally, `s = (n - c * sk) mod #secp256k1` is computed. - The proof is then `(pk,Ɣ,c,s,seed)`, where seed is the input seed provided to the oracle with the request. #### **Verification and Fulfillment Phase (Performed on-chain):** 8. **Proof Submission**: - The oracle submits the VRF output along with its proof back to the VRFCoordinatorV2 contract. 9. [**On-Chain Proof Verification**:](https://github.com/smartcontractkit/chainlink/blob/93762ccbd868b9e227abf3220afb9ad22ba41b92/contracts/src/v0.8/vrf/VRFCoordinatorV2.sol#L492) - The `VRFCoordinatorV2` contract calls `_randomValueFromVRFProof` function in `VRF.sol` contract that calls `_verifyVRFProof` function to verify, by: - Checking the validity of `pk` and `Ɣ`. - Verifying that the address of the point `c * pk + s * g` matches the address of `u`. - Finally, computing the “hash to curve” `h` of `(x,y)` from the public key and the request seed, and checks that the hash of `h`, `pk`, `Ɣ`, the address of `u`, and `c * Ɣ + s * (x,y)` matches `c`. 10. **Randomness Delivery**: - If the proof is verified successfully, the `VRFCoordinatorV2` contract triggers the callback function `fulfillRandomWords` with the validated random number as input for the requesting consumer contract to use. ### 2.2 Chainlink verify contract Chainlink's [verify contract](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/vrf/VRF.sol) implements the general structure and steps of the ECVRF_verify function from the [specification](https://www.ietf.org/archive/id/draft-irtf-cfrg-vrf-10.html#name-elliptic-curve-vrf-ecvrf), but with adaptations specific to the Ethereum platform and the secp256k1 curve. The correspondence between the specification and Chainlinks implementation is described below: 1. **Decoding the Proof (`ECVRF_decode_proof`)**: - The spec's `ECVRF_decode_proof` is not explicitly implemented as a single function in the contract. However, the components of the VRF proof (Gamma, c, s) seem to be expected as inputs to the function `_verifyVRFProof`, effectively assuming that the decoding step has already been performed outside the contract or in the preparation of the proof data structure. The contract operates directly with these components rather than a single `pi_string`. 2. **Hash to Curve (`ECVRF_hash_to_curve`)**: - The contract function `_hashToCurve` performs this step, taking the public key `Y` (referred to as `pk` in the code) and `alpha_string` (the seed in the code) to hash the input onto the curve, generating point H. 3. **Calculating U and V**: - In the specification, U and V are intermediate variables used in the verification process. The contract does not explicitly calculate these as separate steps. Instead, it computes related verifications through `_verifyLinearCombinationWithGenerator` and `_linearCombination`, which perform operations involving s, B (implicitly the generator point G in Ethereum), Y, H, and Gamma. The combination of these functions indirectly handles the roles of U and V by working directly with the elliptic curve operations and leveraging Ethereum's built-in functionalities. 4. **Hashing Points (`ECVRF_hash_points`)**: - The contract's equivalent is `_scalarFromCurvePoints`, which takes points H, Gamma, along with the verification elements (akin to U and V in the spec) and computes c', corresponding to the hash of these values following the VRF protocol. 5. **Comparison of c and c'**: - This step is directly reflected in the contract within `_verifyVRFProof` where `c` from the proof and `derivedC` (which represents c' in the specification) are compared. If they match, the proof is considered valid. 6. **Output**: - If the proof is valid, the contract function `_randomValueFromVRFProof` computes `beta_string` as the VRF hash output from Gamma using the keccak256 hash function, which is the Ethereum equivalent of producing the VRF output. If the verification fails, the contract reverts, which is equivalent to outputting "INVALID" in the spec. While Chainlink's verify contract aligns with the essential steps of the spec ECVRF_verify function, it integrates Ethereum-specific optimizations, such as using keccak256 for hashing and leveraging the EVM's built-in elliptic curve arithmetic. The differences in structure between the spec and the solidity implementation reflect adaptations for efficiency, gas optimization, and the constraints of the Ethereum platform. #### 2.2.1 Verify Contract Efficiency Details Chainlink's implementation deviates from the draft spec for efficiency in the following ways: 1. Keccak is used instead of the [recommended](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.5) SHA256 hash. Keccak costs much less gas on the EVM, and provides similar security. 2. Secp256k1 curve is used instead of the P-256 or ED25519 curves [recommended](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.5). For curve-point multiplication, it's much cheaper to abuse ECRECOVER. 3. `_hashToCurve` recursively hashes until it finds a curve x-ordinate. On the EVM, this is slightly more efficient than the recommendation in [step 5](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.1.1), to concatenate with a nonce then hash, and rehash with the nonce updated until a valid x-ordinate is found. 4. `_hashToCurve` does not include a cipher version string or the byte 0x1 in the hash message, as recommended in step 5.B of the above draft standard. They are unnecessary here because no variation in the cipher suite is allowed. 5. Similarly, the hash input in `_scalarFromCurvePoints` does not include a commitment to the cipher suite, either, which differs from [step 2](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.4.3). Also, the hash input is the concatenation of the uncompressed points, not the compressed points as recommended in step 3. 6. In the calculation of the challenge value "c", the "u" value (i.e. the value computed by the prover as the nonce times the secp256k1 generator point, (see [steps 5 and 7](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vrf-05#section-5.3)) is replaced by its ethereum address, i.e. the lower 160 bits of the keccak hash of the original u. This is because they only verify the calculation of u up to its address, by abusing ECRECOVER. ## 3. Minimal Changes to Sygma Proposal Chainlink's VRF oracles are selected by the consuming contract upon [direct funding](https://docs.chain.link/vrf/v2/direct-funding) or [subscription](https://docs.chain.link/vrf/v2/subscription/supported-networks/#overview) through thier public key hash depending on the blockchain and gas limit needs of the consuming contract. This method relies on trusting a single node through its reputation in Chainlink's oracle network. Although this method is centralized and not ideal, we also have not found MPC adaptations that are vetted or part of any standards in literature so far. We suggest that the minimal work approach for Sygma to implement a one-server-to-one-VRF-request service similar to Chainlink, with the addition of an aggregator node for preventing front running attacks. We propsoe to implement ECVRF similar to Chainlinks implementation, but instead of having the public key of a relayer as part of the input in the request, have a single node out of the relayers be the “aggregator” and let that aggregator choose at random which relayer will fulfill each request or batch of requests. The main points to consider are: 1. Smart contracts only communicate with the aggregator, and the aggregator does not have access to the private key of any of the VRF nodes in the network, preventing front runner attacks. 2. Over time, the randomness should be equally distributed with no participating relayers favored over others. 3. Chainlink's verify contract could be reused, with the addition of a Sygma coordinator contract for coordinating conusmer requests and callbacks with the aggregator. 4. Chainlink's core software implmentations are in GO, vetted, and open source. This is a great help towards implementing offline logic. Although some extra logic for implementing the aggregator would be required. ### 3.1 Compatibility with Chainlink VRF verifier contract. Chainlink's server side implementation of key generation, hashing to the curve, point multiplication, and proof generation are implemented in go. A sygma relayer implementation could re-implement the same functions and utility to take advantage of reusing Chainlinks verify solidity contract [VRF.sol](https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/vrf/VRF.sol). The issue is that Chainlink relies heavily on a VRFCoordinator contract to corrdinate between the oracles, consumer contracts and verification contract. Some sort of Sygma coordinator contract will need to be implemented. ## 4. Specification of Work: ECVRF Implementation ### Project Overview The objective is to implement ECVRF based on Goldberg’s VRF standard using the Sygma Relayers. This implementation will be used to generate and verify randomness within smart contracts for ChainSafe, and possibly for public use in the future. ### Components The ECVRF system consists of several components divided into on-chain and off-chain functionalities. #### On-Chain Components 1. **Coordinator Contract**: - Manages aggregator relayer communication and VRF requests. - Coordinates with Chainlink's verficy contract for on-chain VRF proof verification. - Handles the callback function to deliver verified randomness. 2. **Verify Contract**: - Contains the logic for VRF proof verification (_verifyVRFProof function). - Reuse Chainlink's VRF.sol contract 3. **OPTIONAL: Consumer Base Contract** (probably only needed if VRF is provided as a public service): - Facilitates consumer contracts to request randomness by interacting with the Coordinator contract. ### Off-Chain Components 1. **Chainlink Oracle Nodes**: - Run Chainlink core software and participate in decentralized oracle network (DON). - Detect randomness requests and generate VRF outputs and proofs. ### Steps #### Step 1: Node(s) Setup (Relayer Offchain) - Aggregator node to establish contact with coordinator contract - Aggregator to accept registration of VRF relayer public keys - Aggregator logic for managing random VRF relayer nodes to do the work - VRF relayer key generation (secret key `sk` and public key `pk`). #### Step 2: Proof/Randomness Generation (Relayer Off-Chain) - Cryptographicaly secure random sample from secp256k1, using block data from request and the relayers public key `pk` as inputs. - Mechanism for VRF output generation using the oracle’s secret key `sk`. - Logic for proof generation that ensures integrity and authenticity of the VRF output. #### Step 3: Requests (Consumer Contract) - Develop coordinator contract functionalities to facilitate requesting randomness, and accepting the results from aggregator. - Seed and request ID facilitation. #### Step 4: Verification and Fulfillment (On-Chain) - Implement proof submission method for realyers to submit VRF output and proof to aggregator, and then aggregator submissions methods to coordinator contract. - Code the on-chain VRF proof verification process in coordinator contract. - Develop the callback function that consumer contracts can inherit to receive and utilize verified randomness. ### Deliverables 1. **Smart Contracts**: - coordinator, consumerBase (if needed) contracts with full VRF functionality. 2. **Relayer Node Scripts**: - Off-chain scripts for aggregator management, VRF key generation, output and proof generation. 3. **Integration Guide**: - Documentation for integrating the ECVRF system into consumer contracts (if publicly available). 4. **Testing Framework**: - Comprehensive test cases for all components (unit, integration, and simulation tests). 5. **Deployment Scripts**: - Scripts for deploying contracts to Ethereum testnets and mainnet. ### Security and Testing - Perform extensive security audits on new smart contracts and off-chain components. - Conduct testnet trials to ensure system reliability and security before mainnet deployment. ### Maintenance and Support - Set up monitoring and alerting for VRF request handling and oracle performance. - Provide ongoing support and updates to the ECVRF implementation. ## 5. Future Work and Considerations ### 5.1 MPC Upgrades for Relayers We have found two main paths for MPC style relayer aggregation of VRF randomness and proofs that could be researched and implemented in the future. 1. Implement a BLS style distributed VRF similar to [GLOW](https://github.com/fetchai/research-dvrf/blob/master/dvrfs-report.pdf) (and [SUPRA](https://supra.com/docs/SupraOracles-VRF-Service-Whitepaper.pdf)). There exists a [C++](https://github.com/fetchai/research-dvrf) proof of concept implementation, and SUPRA's implementation is not currently open source. Ethereum op codes should have BLS efficiencies at some point soon, (2024 maybe). This path would require a lot engineering, and audits to go from research paper to vetted implementation. - Pro: Trustless, non interactive, has seen some momentum for usage/standardization - Con: Relayers do not have BLS MPC currently, no public implementations in action that I can find 2. Implement a secp256k1 style distributed VRF, by understanding how Shamir secret sharing works in other implementations, and applying it to ECVRF standard protocol. Much of the code outside the secret sharing in key generation, and the aggregation at the end already exists and is vetted. Would require engineering, and audits as well. - Pro: Trustless, non interactive, Relayers already use similar cryptographic primitives - Con: We would be making our own protocol exposing our selves ### 5.2 Other Considerations Here we mentioned some improvements, alternatives and other future work. #### 5.2.1 Request Batching - [It is possible to implement batch verification VRF requests.](https://eprint.iacr.org/2022/1045.pdf) ECVRF constructions are essentially [Fiat-Shamir transformed Σ-protocol](https://www.win.tue.nl/~berry/papers/crypto94.pdf) and possibly suitable for batch verification. Bernstein et al. [exploited](https://eprint.iacr.org/2011/368.pdf) this technique with the state-of-the-art algorithms in multi-scalar multiplication, achieving a factor-two improvement in signature verification using batches of 64 signatures. - Chainlink's VRF implementation includes a [batch coordinating contract](https://github.com/smartcontractkit/chainlink/blob/93762ccbd868b9e227abf3220afb9ad22ba41b92/contracts/src/v0.8/vrf/BatchVRFCoordinatorV2.sol) to be used instead of the regular coordinating contract to process large batches of requests. #### 5.2.2 Output Private VRF There are some limitations for VRF's related to the output of a VRF becomeing public as soon as it is posted to a smart contract. This public nature of VRF outputs introduces two main problems: 1. **Latency and Synchronization Issues**: Requesters cannot pre-request randomness to have it ready at the exact moment it is needed, which results in significant latency. Applications must wait for the VRF request to complete before proceeding, leading to delays that must be carefully managed to prevent exploitation and ensure fairness. 2. **Inability to Re-use Outputs**: Since the VRF output is public, it cannot be securely reused for future purposes. This lack of reusability forces requesters to initiate and pay for new VRF requests each time new randomness is required, incurring additional costs in terms of both gas and service fees. The authors at [SUPRA Oracle](https://supra.com/docs/SupraOracles-VRF-Service-Whitepaper.pdf) suggest using a Private VRF (PVRF) that they had developed for their distributed VRF solution using BLS curves. I think this is a VERY interesting idea and we should probably look into it in the future! #### 5.2.3 DRAND If using the Sygma relayers for VRF service is not viable, [Drand](https://drand.love/) may be a possible alternative to Chainlink. It is a distributed randomness beacon daemon written in Golang. Servers running drand can be linked with each other to produce collective, publicly verifiable, unbiased, unpredictable random values at fixed intervals using bilinear pairings and threshold cryptography.