Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 55 additions & 61 deletions target_chains/ethereum/contracts/contracts/entropy/Entropy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ import "./EntropyStructConverter.sol";
// Entropy implements a secure 2-party random number generation procedure. The protocol
// is an extension of a simple commit/reveal protocol. The original version has the following steps:
//
// 1. Two parties A and B each draw a random number x_{A,B}
// 2. A and B then share h_{A,B} = hash(x_{A,B})
// 3. A and B reveal x_{A,B}
// 4. Both parties verify that hash(x_{A, B}) == h_{A,B}
// 5. The random number r = hash(x_A, x_B)
// 1. Two parties A and B each randomly sample a contribution x_{A,B} to the random number
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you update the docs as well to make this consistent?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah will do

// 2. A commits to their number by sharing h_A = hash(x_A)
// 3. B reveals x_B
// 4. A reveals x_A
// 5. B verifies that hash(x_{A}) == h_A
// 6. The random number r = hash(x_A, x_B)
//
// This protocol has the property that the result is random as long as either A or B are honest.
// Thus, neither party needs to trust the other -- as long as they are themselves honest, they can
// Honesty means that (1) they draw their value at random, and (2) for A, they keep x_A a secret until
// step 4. Thus, neither party needs to trust the other -- as long as they are themselves honest, they can
// ensure that the result r is random.
//
// Entropy implements a version of this protocol that is optimized for on-chain usage. The
Expand All @@ -37,26 +39,19 @@ import "./EntropyStructConverter.sol";
// verified against the previous one in the sequence by hashing it, i.e., hash(x_i) == x_{i - 1}
//
// Request: To produce a random number, the following steps occur.
// 1. The user draws a random number x_U, and submits h_U = hash(x_U) to this contract
// 2. The contract remembers h_U and assigns it an incrementing sequence number i, representing which
// 1. The user randomly samples their contribution x_U and submits it to the contract
// 2. The contract remembers x_U and assigns it an incrementing sequence number i, representing which
// of the provider's random numbers the user will receive.
// 3. The user submits an off-chain request (e.g. via HTTP) to the provider to reveal the i'th random number.
// 4. The provider checks the on-chain sequence number and ensures it is > i. If it is not, the provider
// refuses to reveal the ith random number. The provider should wait for a sufficient number of block confirmations
// to ensure that the request does not get re-orged out of the blockchain.
// 5. The provider reveals x_i to the user.
// 6. The user submits both the provider's revealed number x_i and their own x_U to the contract.
// 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.
// 3. The provider submits a transaction to the contract revealing their contribution x_i to the contract.
// 4. The contract verifies hash(x_i) == x_{i-1} to prove that x_i is the i'th random number.
// The contract stores x_i as the i'th random number to reuse for future verifications.
// 8. If both of the above conditions are satisfied, the random number r = hash(x_i, x_U).
// (Optional) as an added security mechanism, this step can further incorporate the blockhash of the block that the
// request transaction landed in: r = hash(x_i, x_U, blockhash).
// 5. If the condition above is satisfied, the random number r = hash(x_i, x_U).
// 6. The contract submits a callback to the calling contract with the random number `r`.
//
// This protocol has the same security properties as the 2-party randomness protocol above: as long as either
// the provider or user is honest, the number r is random. Honesty here means that the participant keeps their
// random number x a secret until the revelation phase (step 5) of the protocol. Note that providers need to
// be careful to ensure their off-chain service isn't compromised to reveal the random numbers -- if this occurs,
// then users will be able to influence the random number r.
// the provider or user is honest, the number r is random. Note that this analysis assumes that
// providers cannot frontrun user transactions -- a dishonest provider who frontruns user transaction can
// manipulate the result.
//
// The Entropy implementation of the above protocol allows anyone to permissionlessly register to be a
// randomness provider. Users then choose which provider to request randomness from. Each provider can set
Expand All @@ -71,13 +66,6 @@ import "./EntropyStructConverter.sol";
// a compromised sequence. On rotation, any in-flight requests continue to use the pre-rotation commitment.
// Providers can use the sequence number of the request along with the event log of their registrations to determine
// which hash chain contains the requested random number.
//
// Warning to integrators:
// An important caveat of this protocol is that the user can compute the random number r before
// revealing their own number to the contract. This property means that the user can choose to halt the
// protocol prior to the random number being revealed (i.e., prior to step (6) above). Integrators should ensure that
// the user is always incentivized to reveal their random number, and that the protocol has an escape hatch for
// cases where the user chooses not to reveal.
abstract contract Entropy is IEntropy, EntropyState {
using ExcessivelySafeCall for address;

Expand Down Expand Up @@ -357,24 +345,24 @@ abstract contract Entropy is IEntropy, EntropyState {
// Note that excess value is *not* refunded to the caller.
function requestWithCallback(
address provider,
bytes32 userRandomNumber
bytes32 userContribution
) public payable override returns (uint64) {
return
requestV2(
provider,
userRandomNumber,
userContribution,
0 // Passing 0 will assign the request the provider's default gas limit
);
}

function requestV2(
address provider,
bytes32 userRandomNumber,
bytes32 userContribution,
uint32 gasLimit
) public payable override returns (uint64) {
EntropyStructsV2.Request storage req = requestHelper(
provider,
constructUserCommitment(userRandomNumber),
constructUserCommitment(userContribution),
// If useBlockHash is set to true, it allows a scenario in which the provider and miner can collude.
// If we remove the blockHash from this, the provider would have no choice but to provide its committed
// random number. Hence, useBlockHash is set to false.
Expand All @@ -387,14 +375,14 @@ abstract contract Entropy is IEntropy, EntropyState {
provider,
req.requester,
req.sequenceNumber,
userRandomNumber,
userContribution,
EntropyStructConverter.toV1Request(req)
);
emit EntropyEventsV2.Requested(
provider,
req.requester,
req.sequenceNumber,
userRandomNumber,
userContribution,
uint32(req.gasLimit10k) * TEN_THOUSAND,
bytes("")
);
Expand All @@ -406,14 +394,14 @@ abstract contract Entropy is IEntropy, EntropyState {
// current commitment and returns the generated random number.
function revealHelper(
EntropyStructsV2.Request storage req,
bytes32 userRevelation,
bytes32 providerRevelation
bytes32 userContribution,
bytes32 providerContribution
) internal returns (bytes32 randomNumber, bytes32 blockHash) {
bytes32 providerCommitment = constructProviderCommitment(
req.numHashes,
providerRevelation
providerContribution
);
bytes32 userCommitment = constructUserCommitment(userRevelation);
bytes32 userCommitment = constructUserCommitment(userContribution);
if (
keccak256(bytes.concat(userCommitment, providerCommitment)) !=
req.commitment
Expand All @@ -436,8 +424,8 @@ abstract contract Entropy is IEntropy, EntropyState {
}

randomNumber = combineRandomValues(
userRevelation,
providerRevelation,
userContribution,
providerContribution,
blockHash
);

Expand All @@ -446,7 +434,7 @@ abstract contract Entropy is IEntropy, EntropyState {
];
if (providerInfo.currentCommitmentSequenceNumber < req.sequenceNumber) {
providerInfo.currentCommitmentSequenceNumber = req.sequenceNumber;
providerInfo.currentCommitment = providerRevelation;
providerInfo.currentCommitment = providerContribution;
}
}

Expand All @@ -455,7 +443,7 @@ abstract contract Entropy is IEntropy, EntropyState {
function advanceProviderCommitment(
address provider,
uint64 advancedSequenceNumber,
bytes32 providerRevelation
bytes32 providerContribution
) public override {
EntropyStructsV2.ProviderInfo storage providerInfo = _state.providers[
provider
Expand All @@ -473,14 +461,14 @@ abstract contract Entropy is IEntropy, EntropyState {
);
bytes32 providerCommitment = constructProviderCommitment(
numHashes,
providerRevelation
providerContribution
);

if (providerCommitment != providerInfo.currentCommitment)
revert EntropyErrors.IncorrectRevelation();

providerInfo.currentCommitmentSequenceNumber = advancedSequenceNumber;
providerInfo.currentCommitment = providerRevelation;
providerInfo.currentCommitment = providerContribution;
if (
providerInfo.currentCommitmentSequenceNumber >=
providerInfo.sequenceNumber
Expand Down Expand Up @@ -508,8 +496,8 @@ abstract contract Entropy is IEntropy, EntropyState {
function reveal(
address provider,
uint64 sequenceNumber,
bytes32 userRevelation,
bytes32 providerRevelation
bytes32 userContribution,
bytes32 providerContribution
) public override returns (bytes32 randomNumber) {
EntropyStructsV2.Request storage req = findActiveRequest(
provider,
Expand All @@ -528,13 +516,13 @@ abstract contract Entropy is IEntropy, EntropyState {
bytes32 blockHash;
(randomNumber, blockHash) = revealHelper(
req,
userRevelation,
providerRevelation
userContribution,
providerContribution
);
emit Revealed(
EntropyStructConverter.toV1Request(req),
userRevelation,
providerRevelation,
userContribution,
providerContribution,
blockHash,
randomNumber
);
Expand All @@ -554,8 +542,8 @@ abstract contract Entropy is IEntropy, EntropyState {
function revealWithCallback(
address provider,
uint64 sequenceNumber,
bytes32 userRandomNumber,
bytes32 providerRevelation
bytes32 userContribution,
bytes32 providerContribution
) public override {
EntropyStructsV2.Request storage req = findActiveRequest(
provider,
Expand All @@ -573,8 +561,8 @@ abstract contract Entropy is IEntropy, EntropyState {
bytes32 randomNumber;
(randomNumber, ) = revealHelper(
req,
userRandomNumber,
providerRevelation
userContribution,
providerContribution
);

// If the request has an explicit gas limit, then run the new callback failure state flow.
Expand Down Expand Up @@ -613,15 +601,17 @@ abstract contract Entropy is IEntropy, EntropyState {
if (success) {
emit RevealedWithCallback(
EntropyStructConverter.toV1Request(req),
userRandomNumber,
providerRevelation,
userContribution,
providerContribution,
randomNumber
);
emit EntropyEventsV2.Revealed(
provider,
req.requester,
req.sequenceNumber,
randomNumber,
userContribution,
providerContribution,
false,
ret,
SafeCast.toUint32(gasUsed),
Expand All @@ -642,8 +632,8 @@ abstract contract Entropy is IEntropy, EntropyState {
provider,
req.requester,
sequenceNumber,
userRandomNumber,
providerRevelation,
userContribution,
providerContribution,
randomNumber,
ret
);
Expand All @@ -652,6 +642,8 @@ abstract contract Entropy is IEntropy, EntropyState {
req.requester,
sequenceNumber,
randomNumber,
userContribution,
providerContribution,
true,
ret,
SafeCast.toUint32(gasUsed),
Expand Down Expand Up @@ -692,15 +684,17 @@ abstract contract Entropy is IEntropy, EntropyState {

emit RevealedWithCallback(
reqV1,
userRandomNumber,
providerRevelation,
userContribution,
providerContribution,
randomNumber
);
emit EntropyEventsV2.Revealed(
provider,
callAddress,
sequenceNumber,
randomNumber,
userContribution,
providerContribution,
false,
bytes(""),
gasUsed,
Expand Down
Loading
Loading