Entropy
Protocol Design

Protocol Design

The Entropy protocol is an extension of a classical commit/reveal protocol. The original version has the following steps:

  1. Two parties A and B each draw secret random numbers, xAx_A and xBx_B.
  2. A and B hash their random numbers and share the hashes, hA=hash(xA)h_A = \mathrm{hash}(x_A) and hB=hash(xB)h_B = \mathrm{hash}(x_B)
  3. A and B reveal xAx_A and xBx_B
  4. Both parties verify that hash(xA)=hA\mathrm{hash}(x_A) = h_A and hash(xB)=hB\mathrm{hash}(x_B) = h_B
  5. The random number r=hash(xA,xB)r = \mathrm{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 ensure that the result rr is random.

Entropy implements a version of this protocol that is optimized for on-chain usage. The key difference is that one of the participants (the provider) commits to a sequence of random numbers up-front using a hash chain. Users of the protocol then simply grab the next random number in the sequence.

Setup: The provider P computes a sequence of NN random numbers, xix_i for 0iN10 \leq i \leq N-1:

  • xN1=random()x_{N-1} = \mathrm{random}()
  • xi=hash(xi+1)x_i = \mathrm{hash}(x_{i + 1})

The provider commits to x0x_0 by posting it to the Entropy contract. Each random number in the sequence can then be verified against the previous one in the sequence by hashing it, i.e., hash(xi)=xi1\mathrm{hash}(x_i) = x_{i - 1}

Pyth Entropy uses automatic callbacks to simplify the flow:

  • Request: To produce a random number, the following steps occur.
  1. The user U draws a random number xUx_U, and submits it to the contract. The contract generates the hash hU=hash(xU)h_U = \mathrm{hash}(x_U) and records both xUx_U and hUh_U. The contract uses constructUserCommitment (opens in a new tab) to generate the user's commitment.
  2. The contract remembers hUh_U and assigns it an incrementing sequence number ii (opens in a new tab), representing which of the provider's random numbers the user will receive. xUx_U is recorded in the event logs (opens in a new tab).
  3. After sufficient block confirmations, the provider submits a reveal transaction with xix_i and xUx_U to the contract.
  4. The contract verifies hash(xU)=hU\mathrm{hash}(x_U) = h_U and hash(xi)=xi1\mathrm{hash}(x_i) = x_{i-1} to prove that xix_i is the ii'th random number.
  5. If both of the above conditions are satisfied, the random number r=hash(xi,xU)r = \mathrm{hash}(x_i, x_U) is generated and a callback is made to the requesting contract.

In this flow, providers can refuse revealing xix_i if the final random number rr is not in their favor, or they may be able to access xUx_U before on-chain submission (e.g. via mempool) and rotate their commitments to influence the random number rr. Of course, both of these behaviors are detectable and protocols can blacklist providers that exhibit them.

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 rr is random.

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 rr.

The code of default deployed provider can be found here (opens in a new tab).

Last updated on