Skip to content

Commit 97bbc8c

Browse files
authored
add authorization for reveal method (#1156)
1 parent 8202181 commit 97bbc8c

File tree

4 files changed

+92
-20
lines changed

4 files changed

+92
-20
lines changed

target_chains/ethereum/contracts/contracts/entropy/Entropy.sol

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ abstract contract Entropy is IEntropy, EntropyState {
206206
req.commitment = keccak256(
207207
bytes.concat(userCommitment, providerInfo.currentCommitment)
208208
);
209+
req.requester = msg.sender;
209210

210211
if (useBlockHash) {
211212
req.blockNumber = SafeCast.toUint96(block.number);
@@ -223,13 +224,15 @@ abstract contract Entropy is IEntropy, EntropyState {
223224
// Note that this function can only be called once per in-flight request. Calling this function deletes the stored
224225
// request information (so that the contract doesn't use a linear amount of storage in the number of requests).
225226
// If you need to use the returned random number more than once, you are responsible for storing it.
227+
//
228+
// This function must be called by the same `msg.sender` that originally requested the random number. This check
229+
// prevents denial-of-service attacks where another actor front-runs the requester's reveal transaction.
226230
function reveal(
227231
address provider,
228232
uint64 sequenceNumber,
229233
bytes32 userRandomness,
230234
bytes32 providerRevelation
231235
) public override returns (bytes32 randomNumber) {
232-
// TODO: this method may need to be authenticated to prevent griefing
233236
EntropyStructs.Request storage req = findRequest(
234237
provider,
235238
sequenceNumber
@@ -238,6 +241,8 @@ abstract contract Entropy is IEntropy, EntropyState {
238241
if (req.provider != provider || req.sequenceNumber != sequenceNumber)
239242
revert EntropyErrors.NoSuchRequest();
240243

244+
if (req.requester != msg.sender) revert EntropyErrors.Unauthorized();
245+
241246
bytes32 providerCommitment = constructProviderCommitment(
242247
req.numHashes,
243248
providerRevelation

target_chains/ethereum/contracts/forge-test/Entropy.t.sol

Lines changed: 83 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,13 @@ contract EntropyTest is Test, EntropyTestUtils {
9999
bool useBlockhash
100100
) public returns (uint64 sequenceNumber) {
101101
vm.deal(user, fee);
102-
vm.prank(user);
102+
vm.startPrank(user);
103103
sequenceNumber = random.request{value: fee}(
104104
provider,
105105
random.constructUserCommitment(bytes32(randomNumber)),
106106
useBlockhash
107107
);
108+
vm.stopPrank();
108109
}
109110

110111
function assertRequestReverts(
@@ -133,12 +134,14 @@ contract EntropyTest is Test, EntropyTestUtils {
133134
}
134135

135136
function assertRevealSucceeds(
137+
address user,
136138
address provider,
137139
uint64 sequenceNumber,
138140
uint userRandom,
139141
bytes32 providerRevelation,
140142
bytes32 hash
141143
) public {
144+
vm.prank(user);
142145
bytes32 randomNumber = random.reveal(
143146
provider,
144147
sequenceNumber,
@@ -156,18 +159,21 @@ contract EntropyTest is Test, EntropyTestUtils {
156159
}
157160

158161
function assertRevealReverts(
162+
address user,
159163
address provider,
160164
uint64 sequenceNumber,
161165
uint userRandom,
162166
bytes32 providerRevelation
163167
) public {
168+
vm.startPrank(user);
164169
vm.expectRevert();
165170
random.reveal(
166171
provider,
167172
sequenceNumber,
168173
bytes32(uint256(userRandom)),
169174
providerRevelation
170175
);
176+
vm.stopPrank();
171177
}
172178

173179
function assertInvariants() public {
@@ -203,6 +209,7 @@ contract EntropyTest is Test, EntropyTestUtils {
203209
assertEq(random.getRequest(provider1, sequenceNumber).blockNumber, 0);
204210

205211
assertRevealSucceeds(
212+
user2,
206213
provider1,
207214
sequenceNumber,
208215
42,
@@ -213,6 +220,7 @@ contract EntropyTest is Test, EntropyTestUtils {
213220
// You can only reveal the random number once. This isn't a feature of the contract per se, but it is
214221
// the expected behavior.
215222
assertRevealReverts(
223+
user2,
216224
provider1,
217225
sequenceNumber,
218226
42,
@@ -230,13 +238,15 @@ contract EntropyTest is Test, EntropyTestUtils {
230238
assertEq(random.getRequest(provider1, sequenceNumber).blockNumber, 0);
231239

232240
assertRevealReverts(
241+
user2,
233242
random.getDefaultProvider(),
234243
sequenceNumber,
235244
42,
236245
provider2Proofs[sequenceNumber]
237246
);
238247

239248
assertRevealSucceeds(
249+
user2,
240250
random.getDefaultProvider(),
241251
sequenceNumber,
242252
42,
@@ -249,13 +259,37 @@ contract EntropyTest is Test, EntropyTestUtils {
249259
assertRequestReverts(10000000, unregisteredProvider, 42, false);
250260
}
251261

262+
function testAuthorization() public {
263+
uint64 sequenceNumber = request(user2, provider1, 42, false);
264+
assertEq(random.getRequest(provider1, sequenceNumber).requester, user2);
265+
266+
// user1 not authorized, must be user2.
267+
assertRevealReverts(
268+
user1,
269+
provider1,
270+
sequenceNumber,
271+
42,
272+
provider1Proofs[sequenceNumber]
273+
);
274+
275+
assertRevealSucceeds(
276+
user2,
277+
provider1,
278+
sequenceNumber,
279+
42,
280+
provider1Proofs[sequenceNumber],
281+
ALL_ZEROS
282+
);
283+
}
284+
252285
function testAdversarialReveal() public {
253286
uint64 sequenceNumber = request(user2, provider1, 42, false);
254287

255288
// test revealing with the wrong hashes in the same chain
256289
for (uint256 i = 0; i < 10; i++) {
257290
if (i != sequenceNumber) {
258291
assertRevealReverts(
292+
user2,
259293
provider1,
260294
sequenceNumber,
261295
42,
@@ -267,6 +301,7 @@ contract EntropyTest is Test, EntropyTestUtils {
267301
// test revealing with the wrong user revealed value.
268302
for (uint256 i = 0; i < 42; i++) {
269303
assertRevealReverts(
304+
user2,
270305
provider1,
271306
sequenceNumber,
272307
i,
@@ -277,13 +312,14 @@ contract EntropyTest is Test, EntropyTestUtils {
277312
// test revealing sequence numbers that haven't been requested yet.
278313
for (uint64 i = sequenceNumber + 1; i < sequenceNumber + 3; i++) {
279314
assertRevealReverts(
315+
user2,
280316
provider1,
281317
i,
282318
42,
283319
provider1Proofs[sequenceNumber]
284320
);
285321

286-
assertRevealReverts(provider1, i, 42, provider1Proofs[i]);
322+
assertRevealReverts(user2, provider1, i, 42, provider1Proofs[i]);
287323
}
288324
}
289325

@@ -293,21 +329,56 @@ contract EntropyTest is Test, EntropyTestUtils {
293329
uint64 s3 = request(user1, provider1, 3, false);
294330
uint64 s4 = request(user1, provider1, 4, false);
295331

296-
assertRevealSucceeds(provider1, s3, 3, provider1Proofs[s3], ALL_ZEROS);
332+
assertRevealSucceeds(
333+
user1,
334+
provider1,
335+
s3,
336+
3,
337+
provider1Proofs[s3],
338+
ALL_ZEROS
339+
);
297340
assertInvariants();
298341

299342
uint64 s5 = request(user1, provider1, 5, false);
300343

301-
assertRevealSucceeds(provider1, s4, 4, provider1Proofs[s4], ALL_ZEROS);
344+
assertRevealSucceeds(
345+
user1,
346+
provider1,
347+
s4,
348+
4,
349+
provider1Proofs[s4],
350+
ALL_ZEROS
351+
);
302352
assertInvariants();
303353

304-
assertRevealSucceeds(provider1, s1, 1, provider1Proofs[s1], ALL_ZEROS);
354+
assertRevealSucceeds(
355+
user1,
356+
provider1,
357+
s1,
358+
1,
359+
provider1Proofs[s1],
360+
ALL_ZEROS
361+
);
305362
assertInvariants();
306363

307-
assertRevealSucceeds(provider1, s2, 2, provider1Proofs[s2], ALL_ZEROS);
364+
assertRevealSucceeds(
365+
user2,
366+
provider1,
367+
s2,
368+
2,
369+
provider1Proofs[s2],
370+
ALL_ZEROS
371+
);
308372
assertInvariants();
309373

310-
assertRevealSucceeds(provider1, s5, 5, provider1Proofs[s5], ALL_ZEROS);
374+
assertRevealSucceeds(
375+
user1,
376+
provider1,
377+
s5,
378+
5,
379+
provider1Proofs[s5],
380+
ALL_ZEROS
381+
);
311382
assertInvariants();
312383
}
313384

@@ -321,21 +392,13 @@ contract EntropyTest is Test, EntropyTestUtils {
321392
);
322393

323394
assertRevealSucceeds(
395+
user2,
324396
provider1,
325397
sequenceNumber,
326398
42,
327399
provider1Proofs[sequenceNumber],
328400
blockhash(1234)
329401
);
330-
331-
// You can only reveal the random number once. This isn't a feature of the contract per se, but it is
332-
// the expected behavior.
333-
assertRevealReverts(
334-
provider1,
335-
sequenceNumber,
336-
42,
337-
provider1Proofs[sequenceNumber]
338-
);
339402
}
340403

341404
function testProviderCommitmentRotation() public {
@@ -371,13 +434,15 @@ contract EntropyTest is Test, EntropyTestUtils {
371434
// Requests that were in-flight at the time of rotation use the commitment from the time of request
372435
for (uint256 i = 0; i < 10; i++) {
373436
assertRevealReverts(
437+
user2,
374438
provider1,
375439
sequenceNumber1,
376440
userRandom,
377441
newHashChain[i]
378442
);
379443
}
380444
assertRevealSucceeds(
445+
user2,
381446
provider1,
382447
sequenceNumber1,
383448
userRandom,
@@ -388,12 +453,14 @@ contract EntropyTest is Test, EntropyTestUtils {
388453

389454
// Requests after the rotation use the new commitment
390455
assertRevealReverts(
456+
user2,
391457
provider1,
392458
sequenceNumber3,
393459
userRandom,
394460
provider1Proofs[sequenceNumber3]
395461
);
396462
assertRevealSucceeds(
463+
user2,
397464
provider1,
398465
sequenceNumber3,
399466
userRandom,

target_chains/ethereum/entropy_sdk/solidity/EntropyErrors.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ library EntropyErrors {
2020
error IncorrectRevelation();
2121
// Governance message is invalid (e.g., deserialization error).
2222
error InvalidUpgradeMagic();
23-
// Unauthorized (e.g., invalid admin or owner authorisation).
23+
// The msg.sender is not allowed to invoke this call.
2424
error Unauthorized();
2525
}

target_chains/ethereum/entropy_sdk/solidity/EntropyStructs.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ contract EntropyStructs {
4949
// Note that we're using a uint96 such that we have an additional 20 bytes of storage afterward for an address.
5050
// Although block.number returns a uint256, 96 bits should be plenty to index all of the blocks ever generated.
5151
uint96 blockNumber;
52-
53-
// TODO: store the calling contract address here and authenticate the reveal method
52+
// The address that requested this random number.
53+
address requester;
5454
}
5555
}

0 commit comments

Comments
 (0)