@@ -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