Skip to content

Instantly share code, notes, and snippets.

@HarryR
Last active January 27, 2026 16:10
Show Gist options
  • Select an option

  • Save HarryR/65e2f330e0fca7e36cf674770a265ca2 to your computer and use it in GitHub Desktop.

Select an option

Save HarryR/65e2f330e0fca7e36cf674770a265ca2 to your computer and use it in GitHub Desktop.

On the Verifiability of Supra's dVRF

An analysis of Supra's "decentralized VRF" implementation. The conclusion: under the actual trust model, it fails to provide not just the "d" (decentralized), but also the "V" (verifiable) and "R" (random) properties that define a VRF.


Supra's Claims

From Supra's website:

Supra's decentralized Verifiable Random Function (dVRF) provides a secure, tamper-proof solution for generating random numbers in Web3 applications. Unlike centralized VRF systems that create single points of failure, dVRF distributes trust across multiple nodes while maintaining cryptographic verifiability.

Centralized VRF computation introduces its own problem by relying on a single node to store the private key. This creates a single point of failure and is inherently incompatible with decentralization principles. The centralized approach requires complete trust in one entity, which contradicts the fundamental values of blockchain technology.

Supra's approach uses the GLOW construction developed by Galindo et al., which builds heavily on BLS threshold signatures. BLS threshold signatures enable signature generation by any T+1 nodes, naturally ensuring the consistency property required for dVRF.

The application receives both the VRF output and the BLS signature as proof. Verification involves checking that the proof is a valid BLS signature and that the VRF output equals the hash of that signature. This verification process is identical to centralized VRF verification, enabling seamless integration.

The VRF Service Whitepaper is available at: https://supra.com/documents/VRF-Service-Whitepaper.pdf


The Problem

The "Verifiable" in VRF refers to verifying that an output was correctly computed from a given input and public key. It does not verify that the key was generated in a distributed manner, or that a threshold committee exists and is operating as claimed.

From an external observer's perspective, Supra's dVRF is indistinguishable from a centralized VRF.

What IS Verifiable

  • The BLS signature is valid against the published public key
  • The VRF output is the hash of that signature

What is NOT Verifiable

  • That the public key was generated via a proper Distributed Key Generation (DKG) ceremony
  • The threshold parameter t (how many nodes must collude to predict outputs)
  • The total number of nodes n in the committee
  • The identities or independence of committee members
  • That the aggregator isn't colluding or operating as a single signer
  • That key rotations follow any distributed process

It's Not Even a VRF

The problems go deeper than unverifiable decentralization. Under the actual trust model, the core VRF properties themselves—Verifiable and Random—collapse entirely.

"V" is Meaningless When Keys Can Rotate

VRF verification proves that an output was correctly computed for a given public key. But when the admin can rotate the public key at will via updatePublicKey, verification becomes tautological.

Attack scenario:

  1. Admin observes a VRF request (the input is public)
  2. Admin wants output to fall in a specific range (e.g., to rig a lottery)
  3. Admin generates keypairs until finding one where H(BLS.sign(sk, input)) produces desired output
  4. Admin calls updatePublicKey(pk) with the new key
  5. Admin submits the signature
  6. Verification passes—the signature is valid for the current public key

The user is verifying against a target the adversary controls. This is like a casino "proving" dice rolls are fair by showing the dice match the outcome they announced—after they rolled.

For verification to be meaningful, the public key must be committed before the input is known. The contract includes a block hash in the input:

// Input includes: _clientSeed, block.chainid, _requestBlockNumber

But the admin sees the finalized block (including its hash) before they sign and submit. There's no commit-reveal scheme that binds the key to a point before the input is determined.

"R" is Deterministic From the Admin's Perspective

BLS signatures are deterministic functions of the secret key and message:

$$\sigma = sk \cdot H(m)$$

There is no randomness in the signing process. The "random" output is:

$$output = H(\sigma) = H(sk \cdot H(m))$$

This is a deterministic function. From the admin's perspective (knowing sk), every output is fully predictable before any request is made.

The "randomness" in a VRF comes from the unpredictability of the output to parties who don't know sk. This requires:

  1. Threshold assumption: No single party knows sk—only shares $sk_i$ exist
  2. This assumption is unverifiable in Supra's implementation

If the admin controls the full secret key (which is indistinguishable from threshold operation on-chain), they can compute:

for all possible future inputs:
    output[input] = H(BLS.sign(sk, input))

There is no randomness. It's a lookup table the admin has already computed.

What You Actually Have

Stripping away the terminology, Supra's system is:

Property VRF Requirement Supra Reality
Random Output unpredictable to all parties Fully predictable to admin
Verifiable Anyone can check correctness Checking against admin-controlled key
Function Deterministic for given key Yes (this part works)

It's a Deterministic Function with an admin-controlled key—a DF, not a VRF. The "V" and "R" require trust assumptions that aren't enforced anywhere on-chain.

A more honest description would be: "Supra's Administered Deterministic Function (ADF) provides outputs that users can verify were signed by Supra's current key."


Evidence from the Whitepaper

The whitepaper itself reveals the trust gap:

1. DKG is Explicitly a Black Box

From Section 4 (page 8):

"We do not provide specific details of the distributed key-generation (DKG) procedure here, instead we just mention the input and output."

The DKG produces a public key $pk = {g_2}^s$ and individual shares $sk_i$, but only $pk$ is ever visible on-chain.

2. Security Model Assumes What It Should Prove

From Section 3 (page 5):

"We assume that at most f ≤ t parties can be maliciously corrupt."

And:

"We always assume f = t < ⌈n/2⌉ – this restriction is required for the liveness/availability."

These parameters (n, t, f) are never published or committed to on-chain. External observers have no way to verify these assumptions hold.

3. Individual Verification Keys Are Not Exposed

The GLOW construction produces individual verification keys $vk_i = {G_1}^{s_i}$ for each committee member. These would allow verification of partial signatures. However, these are never published—only the aggregated public key pk reaches the chain.

4. The Relay/Aggregator is Another Trust Point

From Section 6 (page 14):

"The relay nodes fetch the input, verify legitimacy of INP (for example, by verifying the signature provided by the contract), whether it was previously used, and if all checks are satisfactory, forwards INP to all nodes within the VRF clan."

The aggregator collects partial evaluations and produces the final signature. This is entirely off-chain and unauditable.

5. Ironic Criticism of Chainlink

From Section 3 (page 4):

"However, from their description [Cha] it seems that their VRF secret-key is not decentralized (in other words, they do not use a DVRF), and therefore, their scheme is susceptible to a single point of failure. This means that if a specific node is compromised, the entire secret-key is known to the attacker."

Yet Supra's deployment is indistinguishable from this to external observers. The cryptographic machinery for threshold signatures exists, but there's no way to verify it's actually being used.


On-Chain Analysis

Arbitrum Contracts

Note: The Router contract doesn't expose the Generator contract's address (_supraGeneratorContract is internal). This address was identified by monitoring admin contract calls.

Verified Flow (from source)

1. Client requests randomness via Router

// SupraRouterContract.sol:324
function generateRequest(string memory _functionSig, uint8 _rngCount, ...) {
    // Forwards to generator contract
    _supraGeneratorContract.call(abi.encodeWithSignature(
        'rngRequest(uint256,string,uint8,address,uint256,uint256,address)', ...
    ));
}

2. Generator emits event, off-chain "free nodes" observe it

// SupraGeneratorContract.sol:482
emit RequestGenerated(_nonce, instanceId, _callerContract, ...);

3. A whitelisted "free node" submits the callback with a BLS signature

// SupraGeneratorContract.sol:199
function generateRngCallback(
    uint256 _nonce,
    uint256[2] calldata _signature,  // BLS signature
    ...
) public nonReentrant {
    if(!isFreeNodeWhitelisted(msg.sender)) {
        revert FreeNodeNotWhitelisted();
    }
    // ...
}

4. The signature is verified against a stored public key

// SupraGeneratorContract.sol:600
function verify(bytes32 _message, uint256[2] calldata _signature) internal view {
    (checkSuccess, callSuccess) = BLS.verifySingle(
        _signature,
        publicKey,  // uint256[4] - a single BLS G2 point
        BLS.hashToPoint(domain, abi.encode(_message)),
        blsPreCompileGasCost
    );
}

5. Random numbers are derived from the signature and sent to Router

// SupraGeneratorContract.sol:417-419
for (uint256 loop = 0; loop < _rngCount; ++loop) {
    rngList[loop] = uint256(keccak256(abi.encodePacked(_signature, loop + 1)));
}
// Then calls supraRouterContract.rngCallback(...)

6. Router invokes the client's callback

// SupraRouterContract.sol:356
function rngCallback(uint256 nonce, uint256[] memory rngList, address _clientContractAddress, string memory _functionSig) {
    if (msg.sender != _supraGeneratorContract) revert OwnerOnly();
    _clientContractAddress.call(abi.encodeWithSignature(_functionSig, nonce, rngList));
}

Critical Observations from Code

The public key is a single point, not a set of verification keys:

// SupraGeneratorContract.sol:29
uint256[4] public publicKey;  // Single BLS12-381 G2 point

There is no storage for:

  • Individual verification keys $vk_i = {G_1}^{s_i}$ for committee members
  • Threshold parameter t
  • Number of nodes n
  • Committee member identities

The updatePublicKey function has no DKG verification:

// SupraGeneratorContract.sol:575-583
function updatePublicKey(uint256[4] memory _publicKey)
    external
    onlyOwner
    returns (bool)
{
    publicKey = _publicKey;
    emit PublicKeyUpdated(block.timestamp);
    return true;
}

This is a simple assignment guarded only by onlyOwner. There is:

  • No proof that the new key came from a DKG ceremony
  • No commitment to committee membership
  • No multi-sig requirement
  • No timelock or governance process

"Free nodes" are not the threshold signers:

The whitelistedFreeNodes (line 56) are addresses permitted to submit callbacks—they are the relay/aggregator layer, not the VRF committee itself. The actual signing happens entirely off-chain.

Attack Scenario

A single entity with access to the owner key could:

  1. Generate a standard (non-threshold) BLS keypair: sk, pk = BLS.keygen()
  2. Call updatePublicKey(pk)
  3. Sign all VRF requests themselves: sig = BLS.sign(sk, message)
  4. Submit via any whitelisted free node

This would be completely undetectable to users of the service. The on-chain verification would pass, and there's no way to distinguish this from legitimate threshold operation.


What Would Make This Verifiable

For the "d" in dVRF to be meaningful, Supra would need to provide:

  1. On-chain DKG commitments: Publish all individual verification keys $vk_i$ during key generation
  2. Published threshold parameters: Commit to n (total nodes) and t (threshold) on-chain
  3. Committee transparency: Publish the identities or at minimum pseudonymous identifiers of committee members
  4. Verifiable key rotation: Key updates should require on-chain proof of a new DKG ceremony, not a single updatePublicKey call
  5. Partial signature transparency: Optionally publish individual partial signatures $(y_i, π_i)$ before aggregation, allowing verification that multiple parties participated

Without these, the security guarantee reduces to: "Trust Supra's operational claims."


Conclusion

The GLOW construction is cryptographically sound and was published at IEEE EuroS&P 2021. The mathematics of threshold BLS signatures genuinely provide the security properties Supra claims—if implemented correctly with independent parties and proper transparency.

However, Supra's deployment architecture doesn't just make decentralization unverifiable—it collapses the VRF properties entirely:

Property Status Reason
Decentralized Unverifiable No on-chain DKG proof, committee info, or threshold parameters
Verifiable Meaningless Verification is against an admin-controlled key that can rotate
Random Nonexistent Outputs are deterministic and fully predictable to the key holder

From the admin's perspective, the system is equivalent to:

def supra_vrf(input):
    return sha256(bls_sign(ADMIN_SECRET_KEY, input))

A deterministic function they fully control, wrapped in threshold cryptography terminology.

This directly contradicts Supra's own criticism of centralized VRF systems:

"The centralized approach requires complete trust in one entity, which contradicts the fundamental values of blockchain technology."

Supra's implementation requires exactly this: complete trust that they're operating a threshold committee, not predicting outputs, and not rotating keys adversarially. None of these can be verified on-chain.

What users are paying for: a deterministic function controlled by Supra, with a smart contract that checks Supra's signatures are valid against Supra's current public key.

What users think they're getting: unpredictable, unbiasable randomness secured by distributed cryptography.


References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment