Random Number Generation Standard
Defines standard interfaces for secure random number generation on Lux Network
Abstract
This LP defines a standard interface for secure random number generation (RNG) on the Lux Network, supporting multiple randomness sources including Verifiable Random Functions (VRF), commit-reveal schemes, threshold signatures, and TEE-based generation. The standard ensures fair, unpredictable, and verifiable randomness for gaming, lotteries, NFT minting, and other applications requiring cryptographically secure randomness.
Motivation
Secure randomness is essential for:
- Gaming and Gambling: Fair dice rolls, card shuffles, and lottery draws
- NFT Generation: Random trait assignment and rare NFT distribution
- Protocol Mechanics: Random validator selection, shard assignment
- DeFi Applications: Randomized liquidation order, fair launches
- Security Operations: Nonce generation, key derivation
Specification
Core RNG Interface
interface IRandomNumberGenerator {
// Events
event RandomnessRequested(
uint256 indexed requestId,
address indexed requester,
uint256 numValues,
uint256 callbackGasLimit,
RandomnessType rngType
);
event RandomnessFulfilled(
uint256 indexed requestId,
uint256[] randomValues,
bytes proof
);
event ProviderRegistered(
address indexed provider,
RandomnessType rngType,
uint256 minBlockDelay,
uint256 fee
);
// Enums
enum RandomnessType {
VRF, // Verifiable Random Function
COMMIT_REVEAL, // Commit-reveal scheme
THRESHOLD_SIGNATURE, // Threshold BLS signatures
TEE_BASED, // TEE-generated randomness
BLOCK_HASH, // Block hash based (less secure)
HYBRID // Combination of methods
}
enum RequestStatus {
PENDING,
PROCESSING,
FULFILLED,
CANCELLED,
FAILED
}
// Structs
struct RandomnessRequest {
uint256 requestId;
address requester;
uint256 numValues;
uint256 callbackGasLimit;
address callbackContract;
bytes4 callbackFunction;
RandomnessType rngType;
uint256 seed;
uint256 blockNumber;
uint256 fee;
RequestStatus status;
}
struct RandomnessProvider {
address provider;
RandomnessType rngType;
uint256 minBlockDelay;
uint256 fee;
uint256 stake;
bool active;
bytes publicKey;
string endpoint;
}
// Request functions
function requestRandomValues(
uint256 numValues,
uint256 callbackGasLimit,
address callbackContract,
bytes4 callbackFunction,
RandomnessType rngType
) external payable returns (uint256 requestId);
function requestRandomValuesWithSeed(
uint256 numValues,
uint256 userProvidedSeed,
uint256 callbackGasLimit,
address callbackContract,
bytes4 callbackFunction,
RandomnessType rngType
) external payable returns (uint256 requestId);
// Fulfillment functions
function fulfillRandomness(
uint256 requestId,
uint256[] calldata randomValues,
bytes calldata proof
) external;
// Query functions
function getRequest(uint256 requestId) external view returns (RandomnessRequest memory);
function getRandomness(uint256 requestId) external view returns (uint256[] memory);
function isRequestPending(uint256 requestId) external view returns (bool);
function calculateRequestFee(uint256 numValues, RandomnessType rngType) external view returns (uint256);
// Provider management
function registerProvider(
RandomnessType rngType,
uint256 minBlockDelay,
uint256 fee,
bytes calldata publicKey,
string calldata endpoint
) external payable;
function updateProviderFee(uint256 newFee) external;
function withdrawProviderStake() external;
}
VRF Implementation
interface IVRFProvider {
struct VRFProof {
uint256[2] pk; // Public key
uint256[2] gamma; // VRF output point
uint256 c; // Challenge
uint256 s; // Response
uint256 seed; // Seed used
address uWitness; // Unique witness address
uint256[2] cGammaWitness; // c*gamma witness
uint256[2] sHashWitness; // s*hash witness
uint256 zInv; // Inverse of z
}
function requestVRFRandomness(
uint256 subId,
uint256 minimumRequestConfirmations,
uint256 callbackGasLimit,
uint256 numWords,
bytes32 keyHash
) external returns (uint256 requestId);
function fulfillVRFRequest(
uint256 requestId,
uint256[] memory randomWords,
VRFProof memory proof
) external;
function verifyVRFProof(
bytes32 keyHash,
uint256 seed,
VRFProof memory proof
) external view returns (bool valid, uint256 randomness);
// Subscription management
function createSubscription() external returns (uint256 subId);
function fundSubscription(uint256 subId) external payable;
function addConsumer(uint256 subId, address consumer) external;
function removeConsumer(uint256 subId, address consumer) external;
function getSubscription(uint256 subId) external view returns (
uint256 balance,
uint256 reqCount,
address owner,
address[] memory consumers
);
}
// Example VRF consumer
contract VRFConsumer {
IVRFProvider public vrfProvider;
uint256 public subscriptionId;
bytes32 public keyHash;
mapping(uint256 => bool) public requestIdToFulfilled;
mapping(uint256 => uint256[]) public requestIdToRandomWords;
function requestRandomness(uint256 numWords) external returns (uint256 requestId) {
requestId = vrfProvider.requestVRFRandomness(
subscriptionId,
3, // confirmations
200000, // callback gas limit
numWords,
keyHash
);
}
function fulfillRandomWords(
uint256 requestId,
uint256[] memory randomWords
) internal {
requestIdToFulfilled[requestId] = true;
requestIdToRandomWords[requestId] = randomWords;
// Use randomness
processRandomness(randomWords);
}
}
Commit-Reveal Scheme
interface ICommitRevealRNG {
struct Commitment {
bytes32 hash;
uint256 block;
bool revealed;
uint256 value;
}
struct RevealRound {
uint256 roundId;
uint256 commitDeadline;
uint256 revealDeadline;
mapping(address => Commitment) commitments;
address[] participants;
uint256 randomSeed;
bool finalized;
}
// Commit phase
function commitRandomValue(
uint256 roundId,
bytes32 commitment
) external;
// Reveal phase
function revealRandomValue(
uint256 roundId,
uint256 value,
uint256 nonce
) external;
// Finalization
function finalizeRound(
uint256 roundId
) external returns (uint256 randomness);
// Multi-party commit-reveal
function createMultiPartyRound(
address[] calldata participants,
uint256 commitDuration,
uint256 revealDuration
) external returns (uint256 roundId);
// Penalties for non-revelation
function slashNoReveal(
uint256 roundId,
address participant
) external;
}
// Example usage
contract CommitRevealLottery {
ICommitRevealRNG public rng;
uint256 public currentRound;
mapping(address => bytes32) public playerCommitments;
mapping(address => uint256) public playerNumbers;
function enterLottery(bytes32 commitment) external payable {
require(msg.value == 1 ether, "Invalid entry fee");
playerCommitments[msg.sender] = commitment;
rng.commitRandomValue(currentRound, commitment);
}
function revealNumber(uint256 number, uint256 nonce) external {
require(
keccak256(abi.encodePacked(number, nonce)) == playerCommitments[msg.sender],
"Invalid reveal"
);
playerNumbers[msg.sender] = number;
rng.revealRandomValue(currentRound, number, nonce);
}
}
Threshold Signature RNG
interface IThresholdRNG {
struct ThresholdGroup {
uint256 groupId;
address[] members;
uint256 threshold;
bytes publicKey;
mapping(address => bytes) publicKeyShares;
bool active;
}
struct SignatureShare {
address signer;
bytes share;
uint256 index;
}
// Group management
function createThresholdGroup(
address[] calldata members,
uint256 threshold,
bytes calldata publicKey,
bytes[] calldata publicKeyShares
) external returns (uint256 groupId);
// Distributed key generation
function initiateDKG(
address[] calldata participants,
uint256 threshold
) external returns (uint256 sessionId);
function submitDKGShare(
uint256 sessionId,
bytes calldata share,
bytes calldata proof
) external;
function finalizeDKG(
uint256 sessionId
) external returns (uint256 groupId);
// Random generation
function requestThresholdRandom(
uint256 groupId,
bytes32 message
) external returns (uint256 requestId);
function submitSignatureShare(
uint256 requestId,
bytes calldata share,
uint256 index
) external;
function combineShares(
uint256 requestId,
SignatureShare[] calldata shares
) external returns (uint256 randomness);
// Verification
function verifyShare(
uint256 groupId,
bytes calldata share,
uint256 index,
bytes32 message
) external view returns (bool);
}
TEE-Based RNG
interface ITEERandomness {
// TEE-based random generation
function requestTEERandomness(
bytes32 enclaveId,
uint256 numValues,
bytes calldata additionalEntropy
) external payable returns (uint256 requestId);
function getTEERandomness(
uint256 requestId
) external view returns (
uint256[] memory values,
bytes memory attestation
);
// Secure multi-party random generation in TEE
function initiateMPCRandom(
bytes32[] calldata enclaveIds,
uint256 numValues,
uint256 threshold
) external returns (uint256 sessionId);
function contributeMPCEntropy(
uint256 sessionId,
bytes calldata encryptedEntropy,
bytes calldata proof
) external;
function finalizeMPCRandom(
uint256 sessionId
) external returns (uint256[] memory randomValues);
}
Hybrid RNG System
contract HybridRNG is IRandomNumberGenerator {
IVRFProvider public vrfProvider;
ICommitRevealRNG public commitReveal;
IThresholdRNG public thresholdRNG;
ITEERandomness public teeRNG;
struct HybridRequest {
uint256[] vrfValues;
uint256[] commitRevealValues;
uint256[] thresholdValues;
uint256[] teeValues;
uint256 combinedSeed;
}
mapping(uint256 => HybridRequest) public hybridRequests;
function requestHybridRandomness(
uint256 numValues,
bool useVRF,
bool useCommitReveal,
bool useThreshold,
bool useTEE
) external payable returns (uint256 requestId) {
require(useVRF || useCommitReveal || useThreshold || useTEE, "No source selected");
requestId = uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender, numValues)));
if (useVRF) {
vrfProvider.requestVRFRandomness(subscriptionId, 3, 500000, numValues, keyHash);
}
if (useCommitReveal) {
// Initiate commit-reveal round
}
if (useThreshold) {
thresholdRNG.requestThresholdRandom(groupId, bytes32(requestId));
}
if (useTEE) {
teeRNG.requestTEERandomness(enclaveId, numValues, abi.encode(requestId));
}
emit RandomnessRequested(requestId, msg.sender, numValues, 0, RandomnessType.HYBRID);
}
function combineRandomness(uint256 requestId) external {
HybridRequest storage request = hybridRequests[requestId];
// Combine all sources using XOR and hashing
uint256 combined = uint256(keccak256(abi.encodePacked(
request.vrfValues,
request.commitRevealValues,
request.thresholdValues,
request.teeValues
)));
request.combinedSeed = combined;
// Generate final random values
uint256[] memory finalValues = new uint256[](numValues);
for (uint i = 0; i < numValues; i++) {
finalValues[i] = uint256(keccak256(abi.encodePacked(combined, i)));
}
emit RandomnessFulfilled(requestId, finalValues, "");
}
}
Applications
// Fair NFT launch with random allocation
contract FairNFTLaunch {
IRandomNumberGenerator public rng;
uint256 public constant MAX_SUPPLY = 10000;
uint256 public mintedCount;
mapping(address => uint256) public allocationRequests;
mapping(uint256 => address) public requestToMinter;
function requestMint(uint256 quantity) external payable {
require(mintedCount + quantity <= MAX_SUPPLY, "Exceeds supply");
require(msg.value >= rng.calculateRequestFee(quantity, RandomnessType.VRF), "Insufficient fee");
uint256 requestId = rng.requestRandomValues(
quantity,
500000,
address(this),
this.fulfillMint.selector,
RandomnessType.VRF
);
allocationRequests[msg.sender] = quantity;
requestToMinter[requestId] = msg.sender;
}
function fulfillMint(uint256 requestId, uint256[] memory randomValues) external {
require(msg.sender == address(rng), "Only RNG");
address minter = requestToMinter[requestId];
uint256 quantity = allocationRequests[minter];
for (uint i = 0; i < quantity; i++) {
uint256 tokenId = (randomValues[i] % (MAX_SUPPLY - mintedCount)) + mintedCount + 1;
_mint(minter, tokenId);
mintedCount++;
}
delete allocationRequests[minter];
delete requestToMinter[requestId];
}
}
// Decentralized lottery
contract DecentralizedLottery {
IRandomNumberGenerator public rng;
address[] public players;
uint256 public lotteryEndTime;
uint256 public randomnessRequestId;
function enterLottery() external payable {
require(msg.value == 0.1 ether, "Invalid entry");
require(block.timestamp < lotteryEndTime, "Lottery ended");
players.push(msg.sender);
}
function drawWinner() external {
require(block.timestamp >= lotteryEndTime, "Not ended");
require(randomnessRequestId == 0, "Already requested");
randomnessRequestId = rng.requestRandomValues{value: 0.01 ether}(
1,
500000,
address(this),
this.fulfillDraw.selector,
RandomnessType.HYBRID
);
}
function fulfillDraw(uint256 requestId, uint256[] memory randomValues) external {
require(msg.sender == address(rng), "Only RNG");
require(requestId == randomnessRequestId, "Invalid request");
uint256 winnerIndex = randomValues[0] % players.length;
address winner = players[winnerIndex];
(bool sent,) = winner.call{value: address(this).balance}("");
require(sent, "Failed to send prize");
emit WinnerSelected(winner, randomValues[0]);
}
}
Security Module
contract RNGSecurity {
// Bias detection
function detectBias(
uint256[] memory values,
uint256 expectedMean,
uint256 tolerance
) public pure returns (bool biased) {
uint256 sum = 0;
for (uint i = 0; i < values.length; i++) {
sum += values[i];
}
uint256 mean = sum / values.length;
return mean > expectedMean + tolerance || mean < expectedMean - tolerance;
}
// Entropy validation
function validateEntropy(
bytes memory data
) public pure returns (uint256 entropy) {
// Shannon entropy calculation
uint256[256] memory frequencies;
for (uint i = 0; i < data.length; i++) {
frequencies[uint8(data[i])]++;
}
for (uint i = 0; i < 256; i++) {
if (frequencies[i] > 0) {
// Simplified entropy calculation
entropy += frequencies[i] * (256 - frequencies[i]);
}
}
return entropy / data.length;
}
// Manipulation detection
function detectManipulation(
uint256 requestId,
uint256 blockNumber,
uint256 randomValue
) public view returns (bool suspicious) {
// Check for predictable patterns
if (randomValue == uint256(keccak256(abi.encodePacked(requestId, blockNumber)))) {
return true;
}
// Check for reused values
// Implementation depends on storage of historical values
return false;
}
}
Rationale
Design Decisions
- Multiple Sources: Support various RNG methods for different security/performance tradeoffs
- Callback Pattern: Asynchronous design for all RNG methods
- Proof Systems: Verifiable proofs for all generated randomness
- Subscription Model: Efficient payment handling for frequent users
- Hybrid Approach: Combine multiple sources for maximum security
Security Considerations
- Manipulation Resistance: Multiple sources prevent single point of failure
- Verifiability: All randomness comes with cryptographic proofs
- Unpredictability: Future values cannot be predicted
- Availability: System remains functional even if some providers fail
- Fair Ordering: Prevent front-running of randomness requests
Backwards Compatibility
This standard is designed to be compatible with:
- Chainlink VRF interface
- Existing commit-reveal implementations
- Standard callback patterns
Test Cases
VRF Test
function testVRFRandomness() public {
uint256 requestId = rng.requestRandomValues{value: 0.1 ether}(
5, // numValues
500000, // callback gas
address(this),
this.fulfillRandomness.selector,
RandomnessType.VRF
);
// Simulate VRF fulfillment
uint256[] memory randomValues = new uint256[](5);
for (uint i = 0; i < 5; i++) {
randomValues[i] = uint256(keccak256(abi.encodePacked(requestId, i)));
}
vm.prank(vrfProvider);
rng.fulfillRandomness(requestId, randomValues, generateVRFProof());
assertEq(rng.getRandomness(requestId), randomValues);
}
Commit-Reveal Test
function testCommitReveal() public {
uint256 roundId = commitReveal.createMultiPartyRound(
[alice, bob, charlie],
1 hours, // commit duration
1 hours // reveal duration
);
// Commit phase
uint256 aliceValue = 12345;
uint256 aliceNonce = 67890;
bytes32 aliceCommit = keccak256(abi.encodePacked(aliceValue, aliceNonce));
vm.prank(alice);
commitReveal.commitRandomValue(roundId, aliceCommit);
// Similar for bob and charlie...
// Advance time
vm.warp(block.timestamp + 1 hours);
// Reveal phase
vm.prank(alice);
commitReveal.revealRandomValue(roundId, aliceValue, aliceNonce);
// Finalize
uint256 finalRandom = commitReveal.finalizeRound(roundId);
assert(finalRandom != 0);
}
Implementation
Reference Implementation
Location: ~/work/lux/standard/src/rng/
Files:
RandomNumberGenerator.sol- Core RNG coordinatorIRandomNumberGenerator.sol- RNG interfaceVRFProvider.sol- VRF implementationCommitRevealRNG.sol- Commit-reveal schemeThresholdRNG.sol- Threshold signature RNGTEERandomness.sol- TEE-based RNGHybridRNG.sol- Hybrid RNG system
VRF Provider: ~/work/lux/standard/src/rng/vrf/
VRFCoordinator.sol- VRF coordinationVRFProver.sol- Proof generation/verificationVRFSubscription.sol- Subscription management
Deployment:
cd ~/work/lux/standard
forge build
# Deploy RNG to C-Chain
forge script script/DeployRNG.s.sol:DeployRNG \
--rpc-url https://api.avax.network/ext/bc/C/rpc \
--broadcast
Testing
Foundry Test Suite: test/rng/
cd ~/work/lux/standard
# Run all RNG tests
forge test --match-path test/rng/\* -vvv
# Run specific test
forge test --match RNGTest --match-contract -vvv
# Gas reports
forge test --match-path test/rng/\* --gas-report
# Coverage
forge coverage --match-path test/rng/\*
Test Cases (see /test/rng/RandomNumberGenerator.t.sol):
testVRFRandomness()- VRF verificationtestCommitReveal()- Multi-party commit-revealtestThresholdRNG()- Threshold signature randomnesstestTEERandomness()- TEE-based generationtestHybridRandomness()- Multi-source combinationtestSubscriptions()- VRF subscriptionstestBiasDetection()- Entropy validationtestNFTFairLaunch()- Fair allocation example
Gas Benchmarks (Apple M1 Max):
| Operation | Gas Cost | Time |
|---|---|---|
| requestRandomValues (VRF) | ~85,000 | ~2.1ms |
| fulfillRandomness | ~95,000 | ~2.4ms |
| commitRandomValue | ~45,000 | ~1.1ms |
| revealRandomValue | ~55,000 | ~1.4ms |
| finalizeRound | ~75,000 | ~1.9ms |
| createSubscription | ~65,000 | ~1.6ms |
Contract Verification
Etherscan/Sourcify:
forge verify-contract \
--chain-id 43114 \
--watch 0x<RNG_COORDINATOR_ADDRESS> \
src/rng/RandomNumberGenerator.sol:RandomNumberGenerator
Reference Implementation
Reference implementations available at:
Key features:
- Multiple RNG provider support
- Gas-efficient implementations
- Comprehensive test suite
- Integration examples
Security Considerations
Randomness Quality
- Ensure sufficient entropy sources
- Regular statistical testing
- Monitor for patterns or bias
Attack Vectors
- Block withholding attacks
- Collusion in multi-party schemes
- TEE compromise scenarios
- Front-running protection
Economic Security
- Adequate fees to prevent spam
- Slashing for misbehavior
- Insurance funds for critical applications
Copyright
Copyright and related rights waived via CC0.```