File size: 8,001 Bytes
bab1185
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
// 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;
    }
}