Skip to content

Secure your RUFF integration

Fix the application’s choices before requesting randomness and apply each result once. A valid random number does not protect a game that allows bets to change or an unfavorable outcome to be discarded.

In the transaction that requests a result:

  1. Record the participants, stake or entry set, payout rules and any other inputs affecting the outcome.
  2. Freeze that state and associate the returned sequence number with that exact action.
  3. Keep the association independent of delivery order. Later requests may finish first.

A result may become known before its callback: the revealing service knows the lane secret, reveal transactions expose links, and later links expose earlier links. Pending state must not allow late entries, changed bets, participant substitution or a player’s choice to discard a losing result.

Use the consumer guide to authenticate the hub callback, validate the provider and reject unknown sequences. Store a completion flag separately from the value: zero is valid randomness.

Keep the callback small and make settlement idempotent. If the callback fails, recover the same stored hub result after checking the request’s consumer and fulfilled status. Do not request fresh randomness for the same action as a recovery mechanism.

A fixed context prevents a result from being interpreted under another round or another rules version. For example, a consumer can derive:

bytes32 gameRandom = keccak256(
abi.encode(
bytes32("MY_GAME_DRAW_V1"),
block.chainid,
address(this),
address(hub),
sequenceNumber,
roundContext,
ruffRandom
)
);

This is a derivation expression inside the consumer, not a complete integration. ruffRandom must come from the authenticated callback or checked recovery path. roundContext must identify the state frozen when requesting; it must not be supplied or edited at settlement. Use the same encoding and field types in any external verifier.

This hashing binds the output to an application context. Anyone who knows the RUFF result and the public context can still compute it. A round ID, wallet address or public contribution does not hide the outcome from someone who already knows those inputs.

For several values, fix their labels or counters and their meaning in advance. Never search through derived values and choose a favorable one. Fix range and payout mapping with the rules; exact uniform sampling over a non-power-of-two range requires an appropriate sampler rather than assuming % n is perfectly uniform.

Added input Purpose Boundary
Frozen round context or public userContribution Bind a result to the application’s chosen inputs. Adds no secret unknown to a party that can read those inputs and the RUFF result.
A previously committed secret held outside the RUFF host Can hide the final application outcome from a host attacker until that secret is disclosed. Requires a separate reveal protocol and protection against selective non-disclosure.
A result from an independent verifiable randomness source Can reduce dependence on one source’s secrecy. Adds another delivery dependency, proof check, cost and possible delay.

RUFF’s current userContribution is public when the request is accepted. Hashing a secret into that field supplies a public commitment; it does not make the secret itself part of the hub’s result or create an additional reveal phase.

An application-level extension can commit to a secret before requesting RUFF, include that commitment in the frozen round context, and reveal the secret only after the RUFF result is fixed. The consumer must verify the commitment and derive the final application result using that exact secret and the recorded RUFF result.

For this to hide the outcome from a compromised RUFF host, the secret must have sufficient entropy, stay outside that host’s control and remain undisclosed until the result is fixed. A secret generated on the same compromised server does not provide that separation.

The secret holder can still refuse to reveal after learning the outcome. Commitments prevent changing a secret; they do not force disclosure. Deadlines, any incentives and non-disclosure behavior require a separate design and review. Using zero when the secret is withheld gives that party a choice between outcomes.

Select the sources, their request or round identifiers, and the combination rule before any relevant result can be learned or selected. Validate both results and use the required inputs; do not let a caller choose one provider’s result, select a beacon round afterward, or retry until it likes the combination.

An additional source helps only to the extent that it is independent and remains unpredictable to the attacker when the other input is fixed. Hashing two inputs does not establish those properties. It also does not guarantee availability: someone may still stop a required input from being delivered.

These are optional designs for the consuming application. They are not additional entropy providers or reveal features built into RUFF, and require their own contracts, verification and failure policies.

Define failure handling before accepting funds

Section titled “Define failure handling before accepting funds”

Specify the settlement deadline and behavior for missing results, callbacks that fail, pauses and upgrades. Before treating a callback timeout as a missing result, check whether the hub already stores a fulfilled result and recover it where appropriate.

Settlement and any application refund path must be mutually exclusive on-chain. Test both transaction orders around the deadline. A refund can protect a player’s stake, but does not make selective cancellation statistically fair or return the hub’s request fee; RUFF does not automatically refund that fee in v1.

Avoid choosing a new salt, using the delivery timestamp or delivery block hash, or changing rules after seeing randomness. Those inputs can give a party control over the final outcome. A failing callback must not become an opportunity to obtain another draw.

Exercise out-of-order delivery, duplicate callbacks or recovery, unauthorized callbacks, unknown sequences, zero results, failed callbacks, and missing delivery. Verify that inputs cannot change while pending and that each action reaches at most one terminal state. For any additional entropy protocol, include refusal to reveal and loss of each source independently.

The reference ExampleConsumer demonstrates result storage and recovery. It holds no game stakes and does not implement these additional entropy protocols. Compare the RUFF guarantees with the guarantees your application needs.

Further reading: Chainlink’s integration security guidance covers request matching, fixed inputs, avoiding rerolls and callback handling. RUFF’s own cryptographic and delivery assumptions are specified in its trust model.