qcrypt-rng / quantum-oracle /contracts /src /QuantumRandomnessOracle.sol
rocRevyAreGoals15's picture
Add quantum dashboard, VRF, PQC, data protection, and HF Spaces deployment
bab1185
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/**
* @title QuantumRandomnessOracle
* @dev A decentralized oracle that provides verifiable quantum randomness
* to blockchain applications using a two-phase commit-reveal scheme.
*
* Flow:
* 1. User calls requestRandomness() and pays a fee.
* 2. Oracle node generates quantum randomness off-chain, computes
* commitment = keccak256(abi.encodePacked(randomness)), and calls
* submitCommitment(requestId, commitment).
* 3. After COMMIT_REVEAL_DELAY blocks the oracle calls
* fulfillRandomness(requestId, randomness) which verifies the
* preimage, stores the result, and invokes the requester callback.
*/
contract QuantumRandomnessOracle {
struct Request {
address requester;
uint256 fee;
bytes32 commitment; // keccak256(abi.encodePacked(randomness))
uint256 blockNumber;
uint256 commitBlock; // block at which commitment was submitted
bool fulfilled;
uint256 randomness;
uint256 timestamp;
}
// State variables
mapping(uint256 => Request) public requests;
uint256 public requestCounter;
address public owner;
address public oracleNode;
uint256 public fee;
uint256 public constant FULFILLMENT_GAS_LIMIT = 200000;
uint256 public constant COMMIT_REVEAL_DELAY = 2; // blocks between commit and reveal
// Events
event RandomnessRequested(
uint256 indexed requestId,
address indexed requester,
uint256 feePaid
);
event CommitmentSubmitted(
uint256 indexed requestId,
bytes32 commitment
);
event RandomnessFulfilled(
uint256 indexed requestId,
uint256 randomness,
uint256 entropyBits
);
event OracleNodeUpdated(address indexed oldNode, address indexed newNode);
event FeeUpdated(uint256 oldFee, uint256 newFee);
// Modifiers
modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call this function");
_;
}
modifier onlyOracleNode() {
require(msg.sender == oracleNode, "Only oracle node can fulfill requests");
_;
}
/**
* @dev Constructor to initialize the contract
* @param _fee The fee required to request randomness
* @param _oracleNode The address of the oracle node
*/
constructor(uint256 _fee, address _oracleNode) {
owner = msg.sender;
fee = _fee;
oracleNode = _oracleNode;
requestCounter = 0;
}
/**
* @dev Request quantum randomness from the oracle
* @return requestId The unique identifier for this request
*/
function requestRandomness() external payable returns (uint256 requestId) {
require(msg.value >= fee, "Insufficient fee paid");
requestId = requestCounter++;
requests[requestId] = Request({
requester: msg.sender,
fee: msg.value,
commitment: bytes32(0),
blockNumber: block.number,
commitBlock: 0,
fulfilled: false,
randomness: 0,
timestamp: block.timestamp
});
emit RandomnessRequested(requestId, msg.sender, msg.value);
return requestId;
}
/**
* @dev Phase 1 -- Submit a commitment (hash of the quantum randomness).
* Must be called before fulfillRandomness.
* @param requestId The request ID to commit for
* @param commitment keccak256(abi.encodePacked(randomness))
*/
function submitCommitment(
uint256 requestId,
bytes32 commitment
) external onlyOracleNode {
Request storage request = requests[requestId];
require(request.requester != address(0), "Invalid request ID");
require(!request.fulfilled, "Request already fulfilled");
require(request.commitment == bytes32(0), "Commitment already submitted");
request.commitment = commitment;
request.commitBlock = block.number;
emit CommitmentSubmitted(requestId, commitment);
}
/**
* @dev Phase 2 -- Reveal the randomness and fulfill the request.
* The commitment must have been submitted at least COMMIT_REVEAL_DELAY
* blocks ago.
* @param requestId The request ID to fulfill
* @param randomness The quantum-generated randomness value
*/
function fulfillRandomness(
uint256 requestId,
uint256 randomness
) external onlyOracleNode {
Request storage request = requests[requestId];
require(!request.fulfilled, "Request already fulfilled");
require(request.requester != address(0), "Invalid request ID");
require(request.commitment != bytes32(0), "Commitment not yet submitted");
require(
block.number >= request.commitBlock + COMMIT_REVEAL_DELAY,
"Commit-reveal delay not elapsed"
);
// Verify the preimage matches the stored commitment
require(
keccak256(abi.encodePacked(randomness)) == request.commitment,
"Commitment mismatch"
);
// Update request state
request.randomness = randomness;
request.fulfilled = true;
// Deliver randomness to requester via callback
(bool success, ) = request.requester.call{gas: FULFILLMENT_GAS_LIMIT}(
abi.encodeWithSelector(this.receiveRandomness.selector, requestId, randomness)
);
// If the callback fails, refund the requester
if (!success) {
uint256 amountToTransfer = request.fee;
(bool refundSuccess, ) = request.requester.call{value: amountToTransfer}("");
require(refundSuccess, "Refund failed");
}
emit RandomnessFulfilled(requestId, randomness, 256);
}
/**
* @dev Callback function to receive randomness
* @param requestId The request ID
* @param randomness The randomness value
*/
function receiveRandomness(uint256 requestId, uint256 randomness) external {
require(requests[requestId].requester == msg.sender, "Only requester can receive randomness");
}
/**
* @dev Withdraw accumulated fees (owner only)
*/
function withdrawFees() external onlyOwner {
uint256 balance = address(this).balance;
require(balance > 0, "No balance to withdraw");
(bool success, ) = owner.call{value: balance}("");
require(success, "Withdrawal failed");
}
/**
* @dev Update the oracle node address (owner only)
* @param newOracleNode The new oracle node address
*/
function updateOracleNode(address newOracleNode) external onlyOwner {
require(newOracleNode != address(0), "Invalid oracle node address");
address oldOracleNode = oracleNode;
oracleNode = newOracleNode;
emit OracleNodeUpdated(oldOracleNode, newOracleNode);
}
/**
* @dev Update the request fee (owner only)
* @param newFee The new fee amount
*/
function updateFee(uint256 newFee) external onlyOwner {
uint256 oldFee = fee;
fee = newFee;
emit FeeUpdated(oldFee, newFee);
}
/**
* @dev Get request details
* @param requestId The request ID
* @return Request struct containing all request details
*/
function getRequest(uint256 requestId) external view returns (Request memory) {
return requests[requestId];
}
/**
* @dev Check if a request is fulfilled
* @param requestId The request ID
* @return true if fulfilled, false otherwise
*/
function isRequestFulfilled(uint256 requestId) external view returns (bool) {
return requests[requestId].fulfilled;
}
/**
* @dev Get the current contract balance
* @return The contract's ETH balance
*/
function getBalance() external view returns (uint256) {
return address(this).balance;
}
}