@@ -16,14 +16,16 @@ import "./EntropyStructConverter.sol";
16
16
// Entropy implements a secure 2-party random number generation procedure. The protocol
17
17
// is an extension of a simple commit/reveal protocol. The original version has the following steps:
18
18
//
19
- // 1. Two parties A and B each draw a random number x_{A,B}
20
- // 2. A and B then share h_{A,B} = hash(x_{A,B})
21
- // 3. A and B reveal x_{A,B}
22
- // 4. Both parties verify that hash(x_{A, B}) == h_{A,B}
23
- // 5. The random number r = hash(x_A, x_B)
19
+ // 1. Two parties A and B each randomly sample a contribution x_{A,B} to the random number
20
+ // 2. A commits to their number by sharing h_A = hash(x_A)
21
+ // 3. B reveals x_B
22
+ // 4. A reveals x_A
23
+ // 5. B verifies that hash(x_{A}) == h_A
24
+ // 6. The random number r = hash(x_A, x_B)
24
25
//
25
26
// This protocol has the property that the result is random as long as either A or B are honest.
26
- // Thus, neither party needs to trust the other -- as long as they are themselves honest, they can
27
+ // Honesty means that (1) they draw their value at random, and (2) for A, they keep x_A a secret until
28
+ // step 4. Thus, neither party needs to trust the other -- as long as they are themselves honest, they can
27
29
// ensure that the result r is random.
28
30
//
29
31
// Entropy implements a version of this protocol that is optimized for on-chain usage. The
@@ -37,26 +39,19 @@ import "./EntropyStructConverter.sol";
37
39
// verified against the previous one in the sequence by hashing it, i.e., hash(x_i) == x_{i - 1}
38
40
//
39
41
// Request: To produce a random number, the following steps occur.
40
- // 1. The user draws a random number x_U, and submits h_U = hash(x_U) to this contract
41
- // 2. The contract remembers h_U and assigns it an incrementing sequence number i, representing which
42
+ // 1. The user randomly samples their contribution x_U and submits it to the contract
43
+ // 2. The contract remembers x_U and assigns it an incrementing sequence number i, representing which
42
44
// of the provider's random numbers the user will receive.
43
- // 3. The user submits an off-chain request (e.g. via HTTP) to the provider to reveal the i'th random number.
44
- // 4. The provider checks the on-chain sequence number and ensures it is > i. If it is not, the provider
45
- // refuses to reveal the ith random number. The provider should wait for a sufficient number of block confirmations
46
- // to ensure that the request does not get re-orged out of the blockchain.
47
- // 5. The provider reveals x_i to the user.
48
- // 6. The user submits both the provider's revealed number x_i and their own x_U to the contract.
49
- // 7. The contract verifies hash(x_i) == x_{i-1} to prove that x_i is the i'th random number. The contract also checks that hash(x_U) == h_U.
45
+ // 3. The provider submits a transaction to the contract revealing their contribution x_i to the contract.
46
+ // 4. The contract verifies hash(x_i) == x_{i-1} to prove that x_i is the i'th random number.
50
47
// The contract stores x_i as the i'th random number to reuse for future verifications.
51
- // 8. If both of the above conditions are satisfied, the random number r = hash(x_i, x_U).
52
- // (Optional) as an added security mechanism, this step can further incorporate the blockhash of the block that the
53
- // request transaction landed in: r = hash(x_i, x_U, blockhash).
48
+ // 5. If the condition above is satisfied, the random number r = hash(x_i, x_U).
49
+ // 6. The contract submits a callback to the calling contract with the random number `r`.
54
50
//
55
51
// This protocol has the same security properties as the 2-party randomness protocol above: as long as either
56
- // the provider or user is honest, the number r is random. Honesty here means that the participant keeps their
57
- // random number x a secret until the revelation phase (step 5) of the protocol. Note that providers need to
58
- // be careful to ensure their off-chain service isn't compromised to reveal the random numbers -- if this occurs,
59
- // then users will be able to influence the random number r.
52
+ // the provider or user is honest, the number r is random. Note that this analysis assumes that
53
+ // providers cannot frontrun user transactions -- a dishonest provider who frontruns user transaction can
54
+ // manipulate the result.
60
55
//
61
56
// The Entropy implementation of the above protocol allows anyone to permissionlessly register to be a
62
57
// randomness provider. Users then choose which provider to request randomness from. Each provider can set
@@ -71,13 +66,6 @@ import "./EntropyStructConverter.sol";
71
66
// a compromised sequence. On rotation, any in-flight requests continue to use the pre-rotation commitment.
72
67
// Providers can use the sequence number of the request along with the event log of their registrations to determine
73
68
// which hash chain contains the requested random number.
74
- //
75
- // Warning to integrators:
76
- // An important caveat of this protocol is that the user can compute the random number r before
77
- // revealing their own number to the contract. This property means that the user can choose to halt the
78
- // protocol prior to the random number being revealed (i.e., prior to step (6) above). Integrators should ensure that
79
- // the user is always incentivized to reveal their random number, and that the protocol has an escape hatch for
80
- // cases where the user chooses not to reveal.
81
69
abstract contract Entropy is IEntropy, EntropyState {
82
70
using ExcessivelySafeCall for address;
83
71
@@ -357,24 +345,24 @@ abstract contract Entropy is IEntropy, EntropyState {
357
345
// Note that excess value is *not* refunded to the caller.
358
346
function requestWithCallback(
359
347
address provider,
360
- bytes32 userRandomNumber
348
+ bytes32 userContribution
361
349
) public payable override returns (uint64) {
362
350
return
363
351
requestV2(
364
352
provider,
365
- userRandomNumber ,
353
+ userContribution ,
366
354
0 // Passing 0 will assign the request the provider's default gas limit
367
355
);
368
356
}
369
357
370
358
function requestV2(
371
359
address provider,
372
- bytes32 userRandomNumber ,
360
+ bytes32 userContribution ,
373
361
uint32 gasLimit
374
362
) public payable override returns (uint64) {
375
363
EntropyStructsV2.Request storage req = requestHelper(
376
364
provider,
377
- constructUserCommitment(userRandomNumber ),
365
+ constructUserCommitment(userContribution ),
378
366
// If useBlockHash is set to true, it allows a scenario in which the provider and miner can collude.
379
367
// If we remove the blockHash from this, the provider would have no choice but to provide its committed
380
368
// random number. Hence, useBlockHash is set to false.
@@ -387,14 +375,14 @@ abstract contract Entropy is IEntropy, EntropyState {
387
375
provider,
388
376
req.requester,
389
377
req.sequenceNumber,
390
- userRandomNumber ,
378
+ userContribution ,
391
379
EntropyStructConverter.toV1Request(req)
392
380
);
393
381
emit EntropyEventsV2.Requested(
394
382
provider,
395
383
req.requester,
396
384
req.sequenceNumber,
397
- userRandomNumber ,
385
+ userContribution ,
398
386
uint32(req.gasLimit10k) * TEN_THOUSAND,
399
387
bytes("")
400
388
);
@@ -406,14 +394,14 @@ abstract contract Entropy is IEntropy, EntropyState {
406
394
// current commitment and returns the generated random number.
407
395
function revealHelper(
408
396
EntropyStructsV2.Request storage req,
409
- bytes32 userRevelation ,
410
- bytes32 providerRevelation
397
+ bytes32 userContribution ,
398
+ bytes32 providerContribution
411
399
) internal returns (bytes32 randomNumber, bytes32 blockHash) {
412
400
bytes32 providerCommitment = constructProviderCommitment(
413
401
req.numHashes,
414
- providerRevelation
402
+ providerContribution
415
403
);
416
- bytes32 userCommitment = constructUserCommitment(userRevelation );
404
+ bytes32 userCommitment = constructUserCommitment(userContribution );
417
405
if (
418
406
keccak256(bytes.concat(userCommitment, providerCommitment)) !=
419
407
req.commitment
@@ -436,8 +424,8 @@ abstract contract Entropy is IEntropy, EntropyState {
436
424
}
437
425
438
426
randomNumber = combineRandomValues(
439
- userRevelation ,
440
- providerRevelation ,
427
+ userContribution ,
428
+ providerContribution ,
441
429
blockHash
442
430
);
443
431
@@ -446,7 +434,7 @@ abstract contract Entropy is IEntropy, EntropyState {
446
434
];
447
435
if (providerInfo.currentCommitmentSequenceNumber < req.sequenceNumber) {
448
436
providerInfo.currentCommitmentSequenceNumber = req.sequenceNumber;
449
- providerInfo.currentCommitment = providerRevelation ;
437
+ providerInfo.currentCommitment = providerContribution ;
450
438
}
451
439
}
452
440
@@ -455,7 +443,7 @@ abstract contract Entropy is IEntropy, EntropyState {
455
443
function advanceProviderCommitment(
456
444
address provider,
457
445
uint64 advancedSequenceNumber,
458
- bytes32 providerRevelation
446
+ bytes32 providerContribution
459
447
) public override {
460
448
EntropyStructsV2.ProviderInfo storage providerInfo = _state.providers[
461
449
provider
@@ -473,14 +461,14 @@ abstract contract Entropy is IEntropy, EntropyState {
473
461
);
474
462
bytes32 providerCommitment = constructProviderCommitment(
475
463
numHashes,
476
- providerRevelation
464
+ providerContribution
477
465
);
478
466
479
467
if (providerCommitment != providerInfo.currentCommitment)
480
468
revert EntropyErrors.IncorrectRevelation();
481
469
482
470
providerInfo.currentCommitmentSequenceNumber = advancedSequenceNumber;
483
- providerInfo.currentCommitment = providerRevelation ;
471
+ providerInfo.currentCommitment = providerContribution ;
484
472
if (
485
473
providerInfo.currentCommitmentSequenceNumber >=
486
474
providerInfo.sequenceNumber
@@ -508,8 +496,8 @@ abstract contract Entropy is IEntropy, EntropyState {
508
496
function reveal(
509
497
address provider,
510
498
uint64 sequenceNumber,
511
- bytes32 userRevelation ,
512
- bytes32 providerRevelation
499
+ bytes32 userContribution ,
500
+ bytes32 providerContribution
513
501
) public override returns (bytes32 randomNumber) {
514
502
EntropyStructsV2.Request storage req = findActiveRequest(
515
503
provider,
@@ -528,13 +516,13 @@ abstract contract Entropy is IEntropy, EntropyState {
528
516
bytes32 blockHash;
529
517
(randomNumber, blockHash) = revealHelper(
530
518
req,
531
- userRevelation ,
532
- providerRevelation
519
+ userContribution ,
520
+ providerContribution
533
521
);
534
522
emit Revealed(
535
523
EntropyStructConverter.toV1Request(req),
536
- userRevelation ,
537
- providerRevelation ,
524
+ userContribution ,
525
+ providerContribution ,
538
526
blockHash,
539
527
randomNumber
540
528
);
@@ -554,8 +542,8 @@ abstract contract Entropy is IEntropy, EntropyState {
554
542
function revealWithCallback(
555
543
address provider,
556
544
uint64 sequenceNumber,
557
- bytes32 userRandomNumber ,
558
- bytes32 providerRevelation
545
+ bytes32 userContribution ,
546
+ bytes32 providerContribution
559
547
) public override {
560
548
EntropyStructsV2.Request storage req = findActiveRequest(
561
549
provider,
@@ -573,8 +561,8 @@ abstract contract Entropy is IEntropy, EntropyState {
573
561
bytes32 randomNumber;
574
562
(randomNumber, ) = revealHelper(
575
563
req,
576
- userRandomNumber ,
577
- providerRevelation
564
+ userContribution ,
565
+ providerContribution
578
566
);
579
567
580
568
// If the request has an explicit gas limit, then run the new callback failure state flow.
@@ -613,15 +601,17 @@ abstract contract Entropy is IEntropy, EntropyState {
613
601
if (success) {
614
602
emit RevealedWithCallback(
615
603
EntropyStructConverter.toV1Request(req),
616
- userRandomNumber ,
617
- providerRevelation ,
604
+ userContribution ,
605
+ providerContribution ,
618
606
randomNumber
619
607
);
620
608
emit EntropyEventsV2.Revealed(
621
609
provider,
622
610
req.requester,
623
611
req.sequenceNumber,
624
612
randomNumber,
613
+ userContribution,
614
+ providerContribution,
625
615
false,
626
616
ret,
627
617
SafeCast.toUint32(gasUsed),
@@ -642,8 +632,8 @@ abstract contract Entropy is IEntropy, EntropyState {
642
632
provider,
643
633
req.requester,
644
634
sequenceNumber,
645
- userRandomNumber ,
646
- providerRevelation ,
635
+ userContribution ,
636
+ providerContribution ,
647
637
randomNumber,
648
638
ret
649
639
);
@@ -652,6 +642,8 @@ abstract contract Entropy is IEntropy, EntropyState {
652
642
req.requester,
653
643
sequenceNumber,
654
644
randomNumber,
645
+ userContribution,
646
+ providerContribution,
655
647
true,
656
648
ret,
657
649
SafeCast.toUint32(gasUsed),
@@ -692,15 +684,17 @@ abstract contract Entropy is IEntropy, EntropyState {
692
684
693
685
emit RevealedWithCallback(
694
686
reqV1,
695
- userRandomNumber ,
696
- providerRevelation ,
687
+ userContribution ,
688
+ providerContribution ,
697
689
randomNumber
698
690
);
699
691
emit EntropyEventsV2.Revealed(
700
692
provider,
701
693
callAddress,
702
694
sequenceNumber,
703
695
randomNumber,
696
+ userContribution,
697
+ providerContribution,
704
698
false,
705
699
bytes(""),
706
700
gasUsed,
0 commit comments