diff --git a/target_chains/ethereum/contracts/contracts/random/PythRandom.sol b/target_chains/ethereum/contracts/contracts/entropy/Entropy.sol similarity index 88% rename from target_chains/ethereum/contracts/contracts/random/PythRandom.sol rename to target_chains/ethereum/contracts/contracts/entropy/Entropy.sol index 16d0b401f..0da17f84a 100644 --- a/target_chains/ethereum/contracts/contracts/random/PythRandom.sol +++ b/target_chains/ethereum/contracts/contracts/entropy/Entropy.sol @@ -2,12 +2,13 @@ pragma solidity ^0.8.0; -import "@pythnetwork/entropy-sdk-solidity/PythRandomState.sol"; -import "@pythnetwork/entropy-sdk-solidity/PythRandomErrors.sol"; -import "@pythnetwork/entropy-sdk-solidity/PythRandomEvents.sol"; +import "@pythnetwork/entropy-sdk-solidity/EntropyStructs.sol"; +import "@pythnetwork/entropy-sdk-solidity/EntropyErrors.sol"; +import "@pythnetwork/entropy-sdk-solidity/EntropyEvents.sol"; import "@pythnetwork/entropy-sdk-solidity/IEntropy.sol"; +import "./EntropyState.sol"; -// PythRandom implements a secure 2-party random number generation procedure. The protocol +// 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} @@ -20,7 +21,7 @@ import "@pythnetwork/entropy-sdk-solidity/IEntropy.sol"; // Thus, neither party needs to trust the other -- as long as they are themselves honest, they can // ensure that the result r is random. // -// PythRandom implements a version of this protocol that is optimized for on-chain usage. The +// 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. // @@ -51,9 +52,9 @@ import "@pythnetwork/entropy-sdk-solidity/IEntropy.sol"; // 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 PythRandom implementation of the above protocol allows anyone to permissionlessly register to be a +// 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 -// their own fee for the service. In addition, the PythRandom contract charges a flat fee that goes to the +// their own fee for the service. In addition, the Entropy contract charges a flat fee that goes to the // Pyth protocol for each requested random number. Fees are paid in the native token of the network. // // This implementation has two intricacies that merit further explanation. First, the implementation supports @@ -79,7 +80,7 @@ import "@pythnetwork/entropy-sdk-solidity/IEntropy.sol"; // - function to check invariants?? // - need to increment pyth fees if someone transfers funds to the contract via another method // - off-chain data ERC support? -contract PythRandom is IEntropy, PythRandomState { +contract Entropy is IEntropy, EntropyState { // TODO: Use an upgradeable proxy constructor(uint pythFeeInWei) { _state.accruedPythFeesInWei = 0; @@ -97,9 +98,9 @@ contract PythRandom is IEntropy, PythRandomState { bytes32 commitmentMetadata, uint64 chainLength ) public override { - if (chainLength == 0) revert PythRandomErrors.AssertionFailure(); + if (chainLength == 0) revert EntropyErrors.AssertionFailure(); - PythRandomStructs.ProviderInfo storage provider = _state.providers[ + EntropyStructs.ProviderInfo storage provider = _state.providers[ msg.sender ]; @@ -126,7 +127,7 @@ contract PythRandom is IEntropy, PythRandomState { // Calling this function will transfer `amount` wei to the caller (provided that they have accrued a sufficient // balance of fees in the contract). function withdraw(uint256 amount) public override { - PythRandomStructs.ProviderInfo storage providerInfo = _state.providers[ + EntropyStructs.ProviderInfo storage providerInfo = _state.providers[ msg.sender ]; @@ -157,26 +158,26 @@ contract PythRandom is IEntropy, PythRandomState { bytes32 userCommitment, bool useBlockHash ) public payable override returns (uint64 assignedSequenceNumber) { - PythRandomStructs.ProviderInfo storage providerInfo = _state.providers[ + EntropyStructs.ProviderInfo storage providerInfo = _state.providers[ provider ]; if (_state.providers[provider].sequenceNumber == 0) - revert PythRandomErrors.NoSuchProvider(); + revert EntropyErrors.NoSuchProvider(); // Assign a sequence number to the request assignedSequenceNumber = providerInfo.sequenceNumber; if (assignedSequenceNumber >= providerInfo.endSequenceNumber) - revert PythRandomErrors.OutOfRandomness(); + revert EntropyErrors.OutOfRandomness(); providerInfo.sequenceNumber += 1; // Check that fees were paid and increment the pyth / provider balances. uint requiredFee = getFee(provider); - if (msg.value < requiredFee) revert PythRandomErrors.InsufficientFee(); + if (msg.value < requiredFee) revert EntropyErrors.InsufficientFee(); providerInfo.accruedFeesInWei += providerInfo.feeInWei; _state.accruedPythFeesInWei += (msg.value - providerInfo.feeInWei); // Store the user's commitment so that we can fulfill the request later. - PythRandomStructs.Request storage req = _state.requests[ + EntropyStructs.Request storage req = _state.requests[ requestKey(provider, assignedSequenceNumber) ]; req.provider = provider; @@ -210,11 +211,11 @@ contract PythRandom is IEntropy, PythRandomState { // TODO: do we need to check that this request exists? // TODO: this method may need to be authenticated to prevent griefing bytes32 key = requestKey(provider, sequenceNumber); - PythRandomStructs.Request storage req = _state.requests[key]; + EntropyStructs.Request storage req = _state.requests[key]; // This invariant should be guaranteed to hold by the key construction procedure above, but check it // explicitly to be extra cautious. if (req.sequenceNumber != sequenceNumber) - revert PythRandomErrors.AssertionFailure(); + revert EntropyErrors.AssertionFailure(); bool valid = isProofValid( req.providerCommitmentSequenceNumber, @@ -222,9 +223,9 @@ contract PythRandom is IEntropy, PythRandomState { sequenceNumber, providerRevelation ); - if (!valid) revert PythRandomErrors.IncorrectProviderRevelation(); + if (!valid) revert EntropyErrors.IncorrectProviderRevelation(); if (constructUserCommitment(userRandomness) != req.userCommitment) - revert PythRandomErrors.IncorrectUserRevelation(); + revert EntropyErrors.IncorrectUserRevelation(); bytes32 blockHash = bytes32(uint256(0)); if (req.blockNumber != 0) { @@ -247,7 +248,7 @@ contract PythRandom is IEntropy, PythRandomState { delete _state.requests[key]; - PythRandomStructs.ProviderInfo storage providerInfo = _state.providers[ + EntropyStructs.ProviderInfo storage providerInfo = _state.providers[ provider ]; if (providerInfo.currentCommitmentSequenceNumber < sequenceNumber) { @@ -258,19 +259,14 @@ contract PythRandom is IEntropy, PythRandomState { function getProviderInfo( address provider - ) - public - view - override - returns (PythRandomStructs.ProviderInfo memory info) - { + ) public view override returns (EntropyStructs.ProviderInfo memory info) { info = _state.providers[provider]; } function getRequest( address provider, uint64 sequenceNumber - ) public view override returns (PythRandomStructs.Request memory req) { + ) public view override returns (EntropyStructs.Request memory req) { bytes32 key = requestKey(provider, sequenceNumber); req = _state.requests[key]; } @@ -323,7 +319,7 @@ contract PythRandom is IEntropy, PythRandomState { bytes32 revelation ) internal pure returns (bool valid) { if (sequenceNumber <= lastSequenceNumber) - revert PythRandomErrors.AssertionFailure(); + revert EntropyErrors.AssertionFailure(); bytes32 currentHash = revelation; while (sequenceNumber > lastSequenceNumber) { diff --git a/target_chains/ethereum/contracts/contracts/entropy/EntropyState.sol b/target_chains/ethereum/contracts/contracts/entropy/EntropyState.sol new file mode 100644 index 000000000..1044c9726 --- /dev/null +++ b/target_chains/ethereum/contracts/contracts/entropy/EntropyState.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: Apache 2 + +pragma solidity ^0.8.0; + +import "@pythnetwork/entropy-sdk-solidity/EntropyStructs.sol"; + +contract EntropyInternalStructs { + struct State { + uint pythFeeInWei; + uint accruedPythFeesInWei; + mapping(address => EntropyStructs.ProviderInfo) providers; + mapping(bytes32 => EntropyStructs.Request) requests; + } +} + +contract EntropyState { + EntropyInternalStructs.State _state; +} diff --git a/target_chains/ethereum/contracts/forge-test/PythRandom.t.sol b/target_chains/ethereum/contracts/forge-test/Entropy.t.sol similarity index 95% rename from target_chains/ethereum/contracts/forge-test/PythRandom.t.sol rename to target_chains/ethereum/contracts/forge-test/Entropy.t.sol index c8e7c513c..e59c901cf 100644 --- a/target_chains/ethereum/contracts/forge-test/PythRandom.t.sol +++ b/target_chains/ethereum/contracts/forge-test/Entropy.t.sol @@ -2,23 +2,16 @@ pragma solidity ^0.8.0; -import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import "forge-std/Test.sol"; - -import "@pythnetwork/pyth-sdk-solidity/IPyth.sol"; -import "@pythnetwork/pyth-sdk-solidity/PythErrors.sol"; -import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol"; -import "./utils/WormholeTestUtils.t.sol"; -import "./utils/PythTestUtils.t.sol"; -import "./utils/RandTestUtils.t.sol"; -import "../contracts/random/PythRandom.sol"; +import "@pythnetwork/entropy-sdk-solidity/EntropyStructs.sol"; +import "../contracts/entropy/Entropy.sol"; // TODO // - what's the impact of # of in-flight requests on gas usage? More requests => more hashes to // verify the provider's value. // - fuzz test? -contract PythRandomTest is Test, RandTestUtils { - PythRandom public random; +contract EntropyTest is Test { + Entropy public random; uint pythFeeInWei = 7; @@ -39,7 +32,7 @@ contract PythRandomTest is Test, RandTestUtils { bytes32 ALL_ZEROS = bytes32(uint256(0)); function setUp() public { - random = new PythRandom(pythFeeInWei); + random = new Entropy(pythFeeInWei); bytes32[] memory hashChain1 = generateHashChain( provider1, @@ -182,7 +175,7 @@ contract PythRandomTest is Test, RandTestUtils { random.getAccruedPythFees(); assertEq(address(random).balance, expectedBalance); - PythRandomStructs.ProviderInfo memory info1 = random.getProviderInfo( + EntropyStructs.ProviderInfo memory info1 = random.getProviderInfo( provider1 ); assert( @@ -191,7 +184,7 @@ contract PythRandomTest is Test, RandTestUtils { ); assert(info1.currentCommitmentSequenceNumber < info1.sequenceNumber); assert(info1.sequenceNumber <= info1.endSequenceNumber); - PythRandomStructs.ProviderInfo memory info2 = random.getProviderInfo( + EntropyStructs.ProviderInfo memory info2 = random.getProviderInfo( provider2 ); assert( @@ -337,7 +330,7 @@ contract PythRandomTest is Test, RandTestUtils { 10 ); assertInvariants(); - PythRandomStructs.ProviderInfo memory info1 = random.getProviderInfo( + EntropyStructs.ProviderInfo memory info1 = random.getProviderInfo( provider1 ); assertEq(info1.endSequenceNumber, newHashChainOffset + 10); diff --git a/target_chains/ethereum/entropy_sdk/solidity/PythRandomErrors.sol b/target_chains/ethereum/entropy_sdk/solidity/EntropyErrors.sol similarity index 96% rename from target_chains/ethereum/entropy_sdk/solidity/PythRandomErrors.sol rename to target_chains/ethereum/entropy_sdk/solidity/EntropyErrors.sol index f1f8dc213..9fea3c683 100644 --- a/target_chains/ethereum/entropy_sdk/solidity/PythRandomErrors.sol +++ b/target_chains/ethereum/entropy_sdk/solidity/EntropyErrors.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; -library PythRandomErrors { +library EntropyErrors { // An invariant of the contract failed to hold. This error indicates a software logic bug. error AssertionFailure(); // The provider being registered has already registered diff --git a/target_chains/ethereum/entropy_sdk/solidity/EntropyEvents.sol b/target_chains/ethereum/entropy_sdk/solidity/EntropyEvents.sol new file mode 100644 index 000000000..39e6afb52 --- /dev/null +++ b/target_chains/ethereum/entropy_sdk/solidity/EntropyEvents.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +import "./EntropyStructs.sol"; + +interface EntropyEvents { + event Registered(EntropyStructs.ProviderInfo provider); + + event Requested(EntropyStructs.Request request); + + event Revealed( + EntropyStructs.Request request, + bytes32 userRevelation, + bytes32 providerRevelation, + bytes32 blockHash, + bytes32 randomNumber + ); +} diff --git a/target_chains/ethereum/entropy_sdk/solidity/PythRandomState.sol b/target_chains/ethereum/entropy_sdk/solidity/EntropyStructs.sol similarity index 95% rename from target_chains/ethereum/entropy_sdk/solidity/PythRandomState.sol rename to target_chains/ethereum/entropy_sdk/solidity/EntropyStructs.sol index 0ff407b4c..8e4980bb8 100644 --- a/target_chains/ethereum/entropy_sdk/solidity/PythRandomState.sol +++ b/target_chains/ethereum/entropy_sdk/solidity/EntropyStructs.sol @@ -1,9 +1,8 @@ -// contracts/State.sol // SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.0; -contract PythRandomStructs { +contract EntropyStructs { struct State { uint pythFeeInWei; uint accruedPythFeesInWei; @@ -49,7 +48,3 @@ contract PythRandomStructs { uint256 blockNumber; } } - -contract PythRandomState { - PythRandomStructs.State _state; -} diff --git a/target_chains/ethereum/entropy_sdk/solidity/IEntropy.sol b/target_chains/ethereum/entropy_sdk/solidity/IEntropy.sol index e121de3f8..d1088006b 100644 --- a/target_chains/ethereum/entropy_sdk/solidity/IEntropy.sol +++ b/target_chains/ethereum/entropy_sdk/solidity/IEntropy.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.0; -import "./PythRandomEvents.sol"; +import "./EntropyEvents.sol"; -interface IEntropy is PythRandomEvents { +interface IEntropy is EntropyEvents { // Register msg.sender as a randomness provider. The arguments are the provider's configuration parameters // and initial commitment. Re-registering the same provider rotates the provider's commitment (and updates // the feeInWei). @@ -53,12 +53,12 @@ interface IEntropy is PythRandomEvents { function getProviderInfo( address provider - ) external view returns (PythRandomStructs.ProviderInfo memory info); + ) external view returns (EntropyStructs.ProviderInfo memory info); function getRequest( address provider, uint64 sequenceNumber - ) external view returns (PythRandomStructs.Request memory req); + ) external view returns (EntropyStructs.Request memory req); function getFee(address provider) external view returns (uint feeAmount); diff --git a/target_chains/ethereum/entropy_sdk/solidity/PythRandomEvents.sol b/target_chains/ethereum/entropy_sdk/solidity/PythRandomEvents.sol deleted file mode 100644 index fe2bb4c6a..000000000 --- a/target_chains/ethereum/entropy_sdk/solidity/PythRandomEvents.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "./PythRandomState.sol"; - -interface PythRandomEvents { - event Registered(PythRandomStructs.ProviderInfo provider); - - event Requested(PythRandomStructs.Request request); - - event Revealed( - PythRandomStructs.Request request, - bytes32 userRevelation, - bytes32 providerRevelation, - bytes32 blockHash, - bytes32 randomNumber - ); -}