From 580376c14a9cd7af4b9a8d2e3ae142f40c59ae54 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Thu, 24 Apr 2025 11:11:06 -0700 Subject: [PATCH 01/12] duplicating structs and old events --- .../contracts/contracts/entropy/Entropy.sol | 56 +++++++-------- .../contracts/entropy/EntropyState.sol | 8 +-- .../contracts/forge-test/Entropy.t.sol | 56 ++++++++------- .../entropy_sdk/solidity/EntropyEvents.sol | 2 + .../entropy_sdk/solidity/EntropyEventsV2.sol | 72 +++++++++++++++++++ .../entropy_sdk/solidity/EntropyStructs.sol | 6 -- .../entropy_sdk/solidity/EntropyStructsV2.sol | 71 ++++++++++++++++++ .../entropy_sdk/solidity/IEntropy.sol | 5 +- 8 files changed, 209 insertions(+), 67 deletions(-) create mode 100644 target_chains/ethereum/entropy_sdk/solidity/EntropyEventsV2.sol create mode 100644 target_chains/ethereum/entropy_sdk/solidity/EntropyStructsV2.sol diff --git a/target_chains/ethereum/contracts/contracts/entropy/Entropy.sol b/target_chains/ethereum/contracts/contracts/entropy/Entropy.sol index 8e9986715d..82993cef68 100644 --- a/target_chains/ethereum/contracts/contracts/entropy/Entropy.sol +++ b/target_chains/ethereum/contracts/contracts/entropy/Entropy.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; -import "@pythnetwork/entropy-sdk-solidity/EntropyStructs.sol"; +import "@pythnetwork/entropy-sdk-solidity/EntropyStructsV2.sol"; import "@pythnetwork/entropy-sdk-solidity/EntropyErrors.sol"; import "@pythnetwork/entropy-sdk-solidity/EntropyEvents.sol"; import "@pythnetwork/entropy-sdk-solidity/IEntropy.sol"; @@ -106,7 +106,7 @@ abstract contract Entropy is IEntropy, EntropyState { // use a more consistent amount of gas. // Note that these requests are not live because their sequenceNumber is 0. for (uint8 i = 0; i < NUM_REQUESTS; i++) { - EntropyStructs.Request storage req = _state.requests[i]; + EntropyStructsV2.Request storage req = _state.requests[i]; req.provider = address(1); req.blockNumber = 1234; req.commitment = hex"0123"; @@ -128,7 +128,7 @@ abstract contract Entropy is IEntropy, EntropyState { ) public override { if (chainLength == 0) revert EntropyErrors.AssertionFailure(); - EntropyStructs.ProviderInfo storage provider = _state.providers[ + EntropyStructsV2.ProviderInfo storage provider = _state.providers[ msg.sender ]; @@ -156,7 +156,7 @@ abstract contract Entropy is IEntropy, EntropyState { // 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(uint128 amount) public override { - EntropyStructs.ProviderInfo storage providerInfo = _state.providers[ + EntropyStructsV2.ProviderInfo storage providerInfo = _state.providers[ msg.sender ]; @@ -178,7 +178,7 @@ abstract contract Entropy is IEntropy, EntropyState { address provider, uint128 amount ) external override { - EntropyStructs.ProviderInfo storage providerInfo = _state.providers[ + EntropyStructsV2.ProviderInfo storage providerInfo = _state.providers[ provider ]; @@ -213,8 +213,8 @@ abstract contract Entropy is IEntropy, EntropyState { bool useBlockhash, bool isRequestWithCallback, uint32 callbackGasLimit - ) internal returns (EntropyStructs.Request storage req) { - EntropyStructs.ProviderInfo storage providerInfo = _state.providers[ + ) internal returns (EntropyStructsV2.Request storage req) { + EntropyStructsV2.ProviderInfo storage providerInfo = _state.providers[ provider ]; if (_state.providers[provider].sequenceNumber == 0) @@ -294,7 +294,7 @@ abstract contract Entropy is IEntropy, EntropyState { bytes32 userCommitment, bool useBlockHash ) public payable override returns (uint64 assignedSequenceNumber) { - EntropyStructs.Request storage req = requestHelper( + EntropyStructsV2.Request storage req = requestHelper( provider, userCommitment, useBlockHash, @@ -330,7 +330,7 @@ abstract contract Entropy is IEntropy, EntropyState { bytes32 userRandomNumber, uint32 gasLimit ) public payable override returns (uint64) { - EntropyStructs.Request storage req = requestHelper( + EntropyStructsV2.Request storage req = requestHelper( provider, constructUserCommitment(userRandomNumber), // If useBlockHash is set to true, it allows a scenario in which the provider and miner can collude. @@ -355,7 +355,7 @@ abstract contract Entropy is IEntropy, EntropyState { // commitment in the in-flight request. If both values are validated, this method will update the provider // current commitment and returns the generated random number. function revealHelper( - EntropyStructs.Request storage req, + EntropyStructsV2.Request storage req, bytes32 userRevelation, bytes32 providerRevelation ) internal returns (bytes32 randomNumber, bytes32 blockHash) { @@ -391,7 +391,7 @@ abstract contract Entropy is IEntropy, EntropyState { blockHash ); - EntropyStructs.ProviderInfo storage providerInfo = _state.providers[ + EntropyStructsV2.ProviderInfo storage providerInfo = _state.providers[ req.provider ]; if (providerInfo.currentCommitmentSequenceNumber < req.sequenceNumber) { @@ -407,7 +407,7 @@ abstract contract Entropy is IEntropy, EntropyState { uint64 advancedSequenceNumber, bytes32 providerRevelation ) public override { - EntropyStructs.ProviderInfo storage providerInfo = _state.providers[ + EntropyStructsV2.ProviderInfo storage providerInfo = _state.providers[ provider ]; if ( @@ -461,7 +461,7 @@ abstract contract Entropy is IEntropy, EntropyState { bytes32 userRevelation, bytes32 providerRevelation ) public override returns (bytes32 randomNumber) { - EntropyStructs.Request storage req = findActiveRequest( + EntropyStructsV2.Request storage req = findActiveRequest( provider, sequenceNumber ); @@ -507,7 +507,7 @@ abstract contract Entropy is IEntropy, EntropyState { bytes32 userRandomNumber, bytes32 providerRevelation ) public override { - EntropyStructs.Request storage req = findActiveRequest( + EntropyStructsV2.Request storage req = findActiveRequest( provider, sequenceNumber ); @@ -627,7 +627,7 @@ abstract contract Entropy is IEntropy, EntropyState { function getProviderInfo( address provider - ) public view override returns (EntropyStructs.ProviderInfo memory info) { + ) public view override returns (EntropyStructsV2.ProviderInfo memory info) { info = _state.providers[provider]; } @@ -643,7 +643,7 @@ abstract contract Entropy is IEntropy, EntropyState { function getRequest( address provider, uint64 sequenceNumber - ) public view override returns (EntropyStructs.Request memory req) { + ) public view override returns (EntropyStructsV2.Request memory req) { req = findRequest(provider, sequenceNumber); } @@ -664,7 +664,7 @@ abstract contract Entropy is IEntropy, EntropyState { address providerAddr, uint32 gasLimit ) internal view returns (uint128 feeAmount) { - EntropyStructs.ProviderInfo memory provider = _state.providers[ + EntropyStructsV2.ProviderInfo memory provider = _state.providers[ providerAddr ]; @@ -704,7 +704,7 @@ abstract contract Entropy is IEntropy, EntropyState { // Set provider fee. It will revert if provider is not registered. function setProviderFee(uint128 newFeeInWei) external override { - EntropyStructs.ProviderInfo storage provider = _state.providers[ + EntropyStructsV2.ProviderInfo storage provider = _state.providers[ msg.sender ]; @@ -720,7 +720,7 @@ abstract contract Entropy is IEntropy, EntropyState { address provider, uint128 newFeeInWei ) external override { - EntropyStructs.ProviderInfo storage providerInfo = _state.providers[ + EntropyStructsV2.ProviderInfo storage providerInfo = _state.providers[ provider ]; @@ -740,7 +740,7 @@ abstract contract Entropy is IEntropy, EntropyState { // Set provider uri. It will revert if provider is not registered. function setProviderUri(bytes calldata newUri) external override { - EntropyStructs.ProviderInfo storage provider = _state.providers[ + EntropyStructsV2.ProviderInfo storage provider = _state.providers[ msg.sender ]; if (provider.sequenceNumber == 0) { @@ -752,7 +752,7 @@ abstract contract Entropy is IEntropy, EntropyState { } function setFeeManager(address manager) external override { - EntropyStructs.ProviderInfo storage provider = _state.providers[ + EntropyStructsV2.ProviderInfo storage provider = _state.providers[ msg.sender ]; if (provider.sequenceNumber == 0) { @@ -767,7 +767,7 @@ abstract contract Entropy is IEntropy, EntropyState { // Set the maximum number of hashes to record in a request. This should be set according to the maximum gas limit // the provider supports for callbacks. function setMaxNumHashes(uint32 maxNumHashes) external override { - EntropyStructs.ProviderInfo storage provider = _state.providers[ + EntropyStructsV2.ProviderInfo storage provider = _state.providers[ msg.sender ]; if (provider.sequenceNumber == 0) { @@ -785,7 +785,7 @@ abstract contract Entropy is IEntropy, EntropyState { // Set the default gas limit for a request. function setDefaultGasLimit(uint32 gasLimit) external override { - EntropyStructs.ProviderInfo storage provider = _state.providers[ + EntropyStructsV2.ProviderInfo storage provider = _state.providers[ msg.sender ]; if (provider.sequenceNumber == 0) { @@ -861,7 +861,7 @@ abstract contract Entropy is IEntropy, EntropyState { function findActiveRequest( address provider, uint64 sequenceNumber - ) internal view returns (EntropyStructs.Request storage req) { + ) internal view returns (EntropyStructsV2.Request storage req) { req = findRequest(provider, sequenceNumber); // Check there is an active request for the given provider and sequence number. @@ -878,7 +878,7 @@ abstract contract Entropy is IEntropy, EntropyState { function findRequest( address provider, uint64 sequenceNumber - ) internal view returns (EntropyStructs.Request storage req) { + ) internal view returns (EntropyStructsV2.Request storage req) { (bytes32 key, uint8 shortKey) = requestKey(provider, sequenceNumber); req = _state.requests[shortKey]; @@ -893,7 +893,7 @@ abstract contract Entropy is IEntropy, EntropyState { function clearRequest(address provider, uint64 sequenceNumber) internal { (bytes32 key, uint8 shortKey) = requestKey(provider, sequenceNumber); - EntropyStructs.Request storage req = _state.requests[shortKey]; + EntropyStructsV2.Request storage req = _state.requests[shortKey]; if (req.provider == provider && req.sequenceNumber == sequenceNumber) { req.sequenceNumber = 0; } else { @@ -908,7 +908,7 @@ abstract contract Entropy is IEntropy, EntropyState { function allocRequest( address provider, uint64 sequenceNumber - ) internal returns (EntropyStructs.Request storage req) { + ) internal returns (EntropyStructsV2.Request storage req) { (, uint8 shortKey) = requestKey(provider, sequenceNumber); req = _state.requests[shortKey]; @@ -929,7 +929,7 @@ abstract contract Entropy is IEntropy, EntropyState { // Returns true if a request is active, i.e., its corresponding random value has not yet been revealed. function isActive( - EntropyStructs.Request storage req + EntropyStructsV2.Request storage req ) internal view returns (bool) { // Note that a provider's initial registration occupies sequence number 0, so there is no way to construct // a randomness request with sequence number 0. diff --git a/target_chains/ethereum/contracts/contracts/entropy/EntropyState.sol b/target_chains/ethereum/contracts/contracts/entropy/EntropyState.sol index 94b6e0ec59..a2a777f8c5 100644 --- a/target_chains/ethereum/contracts/contracts/entropy/EntropyState.sol +++ b/target_chains/ethereum/contracts/contracts/entropy/EntropyState.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; -import "@pythnetwork/entropy-sdk-solidity/EntropyStructs.sol"; +import "@pythnetwork/entropy-sdk-solidity/EntropyStructsV2.sol"; contract EntropyInternalStructs { struct State { @@ -30,10 +30,10 @@ contract EntropyInternalStructs { // considered active if their sequenceNumber is > 0. // // WARNING: the number of requests must be kept in sync with the constants below - EntropyStructs.Request[32] requests; - mapping(bytes32 => EntropyStructs.Request) requestsOverflow; + EntropyStructsV2.Request[32] requests; + mapping(bytes32 => EntropyStructsV2.Request) requestsOverflow; // Mapping from randomness providers to information about each them. - mapping(address => EntropyStructs.ProviderInfo) providers; + mapping(address => EntropyStructsV2.ProviderInfo) providers; // proposedAdmin is the new admin's account address proposed by either the owner or the current admin. // If there is no pending transfer request, this value will hold `address(0)`. address proposedAdmin; diff --git a/target_chains/ethereum/contracts/forge-test/Entropy.t.sol b/target_chains/ethereum/contracts/forge-test/Entropy.t.sol index a2638e6de1..34beee7edd 100644 --- a/target_chains/ethereum/contracts/forge-test/Entropy.t.sol +++ b/target_chains/ethereum/contracts/forge-test/Entropy.t.sol @@ -4,7 +4,9 @@ pragma solidity ^0.8.0; import "forge-std/Test.sol"; import "@pythnetwork/entropy-sdk-solidity/EntropyStructs.sol"; +import "@pythnetwork/entropy-sdk-solidity/EntropyStructsV2.sol"; import "@pythnetwork/entropy-sdk-solidity/EntropyEvents.sol"; +import "@pythnetwork/entropy-sdk-solidity/EntropyEventsV2.sol"; import "@pythnetwork/entropy-sdk-solidity/IEntropyConsumer.sol"; import "@pythnetwork/entropy-sdk-solidity/IEntropy.sol"; import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; @@ -181,7 +183,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { random.getAccruedPythFees(); assertEq(address(random).balance, expectedBalance); - EntropyStructs.ProviderInfo memory info1 = random.getProviderInfo( + EntropyStructsV2.ProviderInfo memory info1 = random.getProviderInfo( provider1 ); assert( @@ -190,7 +192,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { ); assert(info1.currentCommitmentSequenceNumber < info1.sequenceNumber); assert(info1.sequenceNumber <= info1.endSequenceNumber); - EntropyStructs.ProviderInfo memory info2 = random.getProviderInfo( + EntropyStructsV2.ProviderInfo memory info2 = random.getProviderInfo( provider2 ); assert( @@ -219,7 +221,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { ALL_ZEROS ); - EntropyStructs.Request memory reqAfterReveal = random.getRequest( + EntropyStructsV2.Request memory reqAfterReveal = random.getRequest( provider1, sequenceNumber ); @@ -520,7 +522,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { provider1Uri ); assertInvariants(); - EntropyStructs.ProviderInfo memory info1 = random.getProviderInfo( + EntropyStructsV2.ProviderInfo memory info1 = random.getProviderInfo( provider1 ); assertEq(info1.endSequenceNumber, newHashChainOffset + 10); @@ -733,7 +735,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { } function testGetProviderInfo() public { - EntropyStructs.ProviderInfo memory providerInfo1 = random + EntropyStructsV2.ProviderInfo memory providerInfo1 = random .getProviderInfo(provider1); // These two fields aren't used by the Entropy contract itself -- they're just convenient info to store // on-chain -- so they aren't tested in the other tests. @@ -777,7 +779,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { function testRequestWithCallbackAndReveal() public { bytes32 userRandomNumber = bytes32(uint(42)); uint fee = random.getFee(provider1); - EntropyStructs.ProviderInfo memory providerInfo = random + EntropyStructsV2.ProviderInfo memory providerInfo = random .getProviderInfo(provider1); vm.roll(1234); @@ -789,7 +791,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { user1, providerInfo.sequenceNumber, userRandomNumber, - EntropyStructs.Request({ + EntropyStructsV2.Request({ provider: provider1, sequenceNumber: providerInfo.sequenceNumber, numHashes: SafeCast.toUint32( @@ -843,7 +845,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { uint64 assignedSequenceNumber = consumer.requestEntropy{value: fee}( userRandomNumber ); - EntropyStructs.Request memory req = random.getRequest( + EntropyStructsV2.Request memory req = random.getRequest( provider1, assignedSequenceNumber ); @@ -880,7 +882,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { ) ); - EntropyStructs.Request memory reqAfterReveal = random.getRequest( + EntropyStructsV2.Request memory reqAfterReveal = random.getRequest( provider1, assignedSequenceNumber ); @@ -896,7 +898,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { provider1, userRandomNumber ); - EntropyStructs.Request memory req = random.getRequest( + EntropyStructsV2.Request memory req = random.getRequest( provider1, assignedSequenceNumber ); @@ -919,7 +921,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { provider1Proofs[assignedSequenceNumber] ); - EntropyStructs.Request memory reqAfterReveal = random.getRequest( + EntropyStructsV2.Request memory reqAfterReveal = random.getRequest( provider1, assignedSequenceNumber ); @@ -973,7 +975,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { uint64 assignedSequenceNumber = consumer.requestEntropy{value: fee}( userRandomNumber ); - EntropyStructs.Request memory req = random.getRequest( + EntropyStructsV2.Request memory req = random.getRequest( provider1, assignedSequenceNumber ); @@ -1010,7 +1012,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { ) ); - EntropyStructs.Request memory reqAfterReveal = random.getRequest( + EntropyStructsV2.Request memory reqAfterReveal = random.getRequest( provider1, assignedSequenceNumber ); @@ -1058,7 +1060,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { ); // Verify request is still active after failure - EntropyStructs.Request memory reqAfterFailure = random.getRequest( + EntropyStructsV2.Request memory reqAfterFailure = random.getRequest( provider1, assignedSequenceNumber ); @@ -1106,7 +1108,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { ); // Verify request is cleared after successful reveal - EntropyStructs.Request memory reqAfterReveal = random.getRequest( + EntropyStructsV2.Request memory reqAfterReveal = random.getRequest( provider1, assignedSequenceNumber ); @@ -1129,7 +1131,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { uint64 assignedSequenceNumber = consumer.requestEntropy{value: fee}( userRandomNumber ); - EntropyStructs.Request memory req = random.getRequest( + EntropyStructsV2.Request memory req = random.getRequest( provider1, assignedSequenceNumber ); @@ -1172,7 +1174,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { ); // Verify request is still active after failure - EntropyStructs.Request memory reqAfterFailure = random.getRequest( + EntropyStructsV2.Request memory reqAfterFailure = random.getRequest( provider1, assignedSequenceNumber ); @@ -1219,7 +1221,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { ); // Verify request is cleared after successful reveal - EntropyStructs.Request memory reqAfterReveal = random.getRequest( + EntropyStructsV2.Request memory reqAfterReveal = random.getRequest( provider1, assignedSequenceNumber ); @@ -1283,7 +1285,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { request(user1, provider1, 42, false); } assertInvariants(); - EntropyStructs.ProviderInfo memory info1 = random.getProviderInfo( + EntropyStructsV2.ProviderInfo memory info1 = random.getProviderInfo( provider1 ); assertEq(info1.currentCommitmentSequenceNumber, 0); @@ -1353,7 +1355,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { seqNumber, provider1Proofs[seqNumber] ); - EntropyStructs.ProviderInfo memory info1 = random.getProviderInfo( + EntropyStructsV2.ProviderInfo memory info1 = random.getProviderInfo( provider1 ); assertEq(info1.sequenceNumber, seqNumber + 1); @@ -1374,7 +1376,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { function testSetMaxNumHashes(uint32 maxNumHashes) public { vm.prank(provider1); random.setMaxNumHashes(maxNumHashes); - EntropyStructs.ProviderInfo memory info1 = random.getProviderInfo( + EntropyStructsV2.ProviderInfo memory info1 = random.getProviderInfo( provider1 ); assertEq(info1.maxNumHashes, maxNumHashes); @@ -1455,7 +1457,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { emit ProviderDefaultGasLimitUpdated(provider1, 0, newGasLimit); random.setDefaultGasLimit(newGasLimit); - EntropyStructs.ProviderInfo memory info = random.getProviderInfo( + EntropyStructsV2.ProviderInfo memory info = random.getProviderInfo( provider1 ); assertEq(info.defaultGasLimit, newGasLimit); @@ -1500,7 +1502,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { userRandomNumber ); - EntropyStructs.Request memory req = random.getRequest( + EntropyStructsV2.Request memory req = random.getRequest( provider1, sequenceNumber ); @@ -1601,7 +1603,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { ); // Check the gasLimit10k field in the request - EntropyStructs.Request memory req = random.getRequest( + EntropyStructsV2.Request memory req = random.getRequest( provider1, sequenceNumber ); @@ -1649,7 +1651,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { consumer.setTargetGasUsage(callbackGasUsage); - EntropyStructs.Request memory req = random.getRequest( + EntropyStructsV2.Request memory req = random.getRequest( provider1, sequenceNumber ); @@ -1678,7 +1680,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { ); // Verify request is still active after failure - EntropyStructs.Request memory reqAfterFailure = random.getRequest( + EntropyStructsV2.Request memory reqAfterFailure = random.getRequest( provider1, sequenceNumber ); @@ -1707,7 +1709,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { ); // Verify request is cleared after successful callback - EntropyStructs.Request memory reqAfterSuccess = random.getRequest( + EntropyStructsV2.Request memory reqAfterSuccess = random.getRequest( provider1, sequenceNumber ); diff --git a/target_chains/ethereum/entropy_sdk/solidity/EntropyEvents.sol b/target_chains/ethereum/entropy_sdk/solidity/EntropyEvents.sol index 120cb0c858..d62c3d9295 100644 --- a/target_chains/ethereum/entropy_sdk/solidity/EntropyEvents.sol +++ b/target_chains/ethereum/entropy_sdk/solidity/EntropyEvents.sol @@ -3,6 +3,8 @@ pragma solidity ^0.8.0; import "./EntropyStructs.sol"; +// Deprecated -- these events are still emitted, but the lack of indexing +// makes them hard to use. interface EntropyEvents { event Registered(EntropyStructs.ProviderInfo provider); diff --git a/target_chains/ethereum/entropy_sdk/solidity/EntropyEventsV2.sol b/target_chains/ethereum/entropy_sdk/solidity/EntropyEventsV2.sol new file mode 100644 index 0000000000..f37792c047 --- /dev/null +++ b/target_chains/ethereum/entropy_sdk/solidity/EntropyEventsV2.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + + +interface EntropyEventsV2 { + + /* + event Registered(address indexed provider, EntropyStructs.ProviderInfo info); + + // TODO + event Requested(EntropyStructs.Request request); + + event RequestedWithCallback( + address indexed provider, + address indexed requestor, + uint64 indexed sequenceNumber, + bytes32 userRandomNumber, + EntropyStructs.Request request + ); + + event Revealed( + EntropyStructs.Request request, + bytes32 userRevelation, + bytes32 providerRevelation, + bytes32 blockHash, + bytes32 randomNumber + ); + event RevealedWithCallback( + EntropyStructs.Request request, + bytes32 userRandomNumber, + bytes32 providerRevelation, + bytes32 randomNumber + ); + + event CallbackFailed( + address indexed provider, + address indexed requestor, + uint64 indexed sequenceNumber, + bytes32 userRandomNumber, + bytes32 providerRevelation, + bytes32 randomNumber, + bytes errorCode + ); + + event ProviderFeeUpdated(address indexed provider, uint128 oldFee, uint128 newFee); + + event ProviderDefaultGasLimitUpdated( + address indexed provider, + uint32 oldDefaultGasLimit, + uint32 newDefaultGasLimit + ); + + event ProviderUriUpdated(address indexed provider, bytes oldUri, bytes newUri); + + event ProviderFeeManagerUpdated( + address indexed provider, + address oldFeeManager, + address newFeeManager + ); + event ProviderMaxNumHashesAdvanced( + address indexed provider, + uint32 oldMaxNumHashes, + uint32 newMaxNumHashes + ); + + event Withdrawal( + address indexed provider, + address recipient, + uint128 withdrawnAmount + ); + */ +} diff --git a/target_chains/ethereum/entropy_sdk/solidity/EntropyStructs.sol b/target_chains/ethereum/entropy_sdk/solidity/EntropyStructs.sol index 002ab7be20..16ec10baf9 100644 --- a/target_chains/ethereum/entropy_sdk/solidity/EntropyStructs.sol +++ b/target_chains/ethereum/entropy_sdk/solidity/EntropyStructs.sol @@ -37,8 +37,6 @@ contract EntropyStructs { // Maximum number of hashes to record in a request. This should be set according to the maximum gas limit // the provider supports for callbacks. uint32 maxNumHashes; - // Default gas limit to use for callbacks. - uint32 defaultGasLimit; } struct Request { @@ -63,9 +61,5 @@ contract EntropyStructs { bool useBlockhash; // Status flag for requests with callbacks. See EntropyConstants for the possible values of this flag. uint8 callbackStatus; - // The gasLimit in units of 10k gas. (i.e., 2 = 20k gas). We're using units of 10k in order to fit this - // field into the remaining 2 bytes of this storage slot. The dynamic range here is 10k - 655M, which should - // cover all real-world use cases. - uint16 gasLimit10k; } } diff --git a/target_chains/ethereum/entropy_sdk/solidity/EntropyStructsV2.sol b/target_chains/ethereum/entropy_sdk/solidity/EntropyStructsV2.sol new file mode 100644 index 0000000000..9997a3cb23 --- /dev/null +++ b/target_chains/ethereum/entropy_sdk/solidity/EntropyStructsV2.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: Apache 2 + +pragma solidity ^0.8.0; + +contract EntropyStructsV2 { + struct ProviderInfo { + uint128 feeInWei; + uint128 accruedFeesInWei; + // The commitment that the provider posted to the blockchain, and the sequence number + // where they committed to this. This value is not advanced after the provider commits, + // and instead is stored to help providers track where they are in the hash chain. + bytes32 originalCommitment; + uint64 originalCommitmentSequenceNumber; + // Metadata for the current commitment. Providers may optionally use this field to help + // manage rotations (i.e., to pick the sequence number from the correct hash chain). + bytes commitmentMetadata; + // Optional URI where clients can retrieve revelations for the provider. + // Client SDKs can use this field to automatically determine how to retrieve random values for each provider. + // TODO: specify the API that must be implemented at this URI + bytes uri; + // The first sequence number that is *not* included in the current commitment (i.e., an exclusive end index). + // The contract maintains the invariant that sequenceNumber <= endSequenceNumber. + // If sequenceNumber == endSequenceNumber, the provider must rotate their commitment to add additional random values. + uint64 endSequenceNumber; + // The sequence number that will be assigned to the next inbound user request. + uint64 sequenceNumber; + // The current commitment represents an index/value in the provider's hash chain. + // These values are used to verify requests for future sequence numbers. Note that + // currentCommitmentSequenceNumber < sequenceNumber. + // + // The currentCommitment advances forward through the provider's hash chain as values + // are revealed on-chain. + bytes32 currentCommitment; + uint64 currentCommitmentSequenceNumber; + // An address that is authorized to set / withdraw fees on behalf of this provider. + address feeManager; + // Maximum number of hashes to record in a request. This should be set according to the maximum gas limit + // the provider supports for callbacks. + uint32 maxNumHashes; + // Default gas limit to use for callbacks. + uint32 defaultGasLimit; + } + + struct Request { + // Storage slot 1 // + address provider; + uint64 sequenceNumber; + // The number of hashes required to verify the provider revelation. + uint32 numHashes; + // Storage slot 2 // + // The commitment is keccak256(userCommitment, providerCommitment). Storing the hash instead of both saves 20k gas by + // eliminating 1 store. + bytes32 commitment; + // Storage slot 3 // + // The number of the block where this request was created. + // Note that we're using a uint64 such that we have an additional space for an address and other fields in + // this storage slot. Although block.number returns a uint256, 64 bits should be plenty to index all of the + // blocks ever generated. + uint64 blockNumber; + // The address that requested this random number. + address requester; + // If true, incorporate the blockhash of blockNumber into the generated random value. + bool useBlockhash; + // Status flag for requests with callbacks. See EntropyConstants for the possible values of this flag. + uint8 callbackStatus; + // The gasLimit in units of 10k gas. (i.e., 2 = 20k gas). We're using units of 10k in order to fit this + // field into the remaining 2 bytes of this storage slot. The dynamic range here is 10k - 655M, which should + // cover all real-world use cases. + uint16 gasLimit10k; + } +} diff --git a/target_chains/ethereum/entropy_sdk/solidity/IEntropy.sol b/target_chains/ethereum/entropy_sdk/solidity/IEntropy.sol index 9d1f13a8c6..55690b2038 100644 --- a/target_chains/ethereum/entropy_sdk/solidity/IEntropy.sol +++ b/target_chains/ethereum/entropy_sdk/solidity/IEntropy.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.0; import "./EntropyEvents.sol"; +import "./EntropyStructsV2.sol"; interface IEntropy is EntropyEvents { // Register msg.sender as a randomness provider. The arguments are the provider's configuration parameters @@ -111,14 +112,14 @@ interface IEntropy is EntropyEvents { function getProviderInfo( address provider - ) external view returns (EntropyStructs.ProviderInfo memory info); + ) external view returns (EntropyStructsV2.ProviderInfo memory info); function getDefaultProvider() external view returns (address provider); function getRequest( address provider, uint64 sequenceNumber - ) external view returns (EntropyStructs.Request memory req); + ) external view returns (EntropyStructsV2.Request memory req); // Get the fee charged by provider for a request with the default gasLimit (`request` or `requestWithCallback`). // If you are calling `requestWithCallbackAndGasLimit`, please use `getFeeForGas`. From 79e9aa9cd17bbebe3fec795170bbd90e988a2d02 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Thu, 24 Apr 2025 11:35:46 -0700 Subject: [PATCH 02/12] fix signatures? --- .../contracts/contracts/entropy/Entropy.sol | 13 +++++++------ .../ethereum/contracts/forge-test/Entropy.t.sol | 17 ++++++++--------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/target_chains/ethereum/contracts/contracts/entropy/Entropy.sol b/target_chains/ethereum/contracts/contracts/entropy/Entropy.sol index 82993cef68..ec4e7b2e54 100644 --- a/target_chains/ethereum/contracts/contracts/entropy/Entropy.sol +++ b/target_chains/ethereum/contracts/contracts/entropy/Entropy.sol @@ -11,6 +11,7 @@ import "@openzeppelin/contracts/utils/math/SafeCast.sol"; import "@nomad-xyz/excessively-safe-call/src/ExcessivelySafeCall.sol"; import "./EntropyState.sol"; import "@pythnetwork/entropy-sdk-solidity/EntropyStatusConstants.sol"; +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: @@ -149,7 +150,7 @@ abstract contract Entropy is IEntropy, EntropyState { provider.sequenceNumber += 1; - emit Registered(provider); + emit Registered(EntropyStructConverter.toV1ProviderInfo(provider)); } // Withdraw a portion of the accumulated fees for the provider msg.sender. @@ -302,7 +303,7 @@ abstract contract Entropy is IEntropy, EntropyState { 0 ); assignedSequenceNumber = req.sequenceNumber; - emit Requested(req); + emit Requested(EntropyStructConverter.toV1Request(req)); } // Request a random number. The method expects the provider address and a secret random number @@ -346,7 +347,7 @@ abstract contract Entropy is IEntropy, EntropyState { req.requester, req.sequenceNumber, userRandomNumber, - req + EntropyStructConverter.toV1Request(req) ); return req.sequenceNumber; } @@ -482,7 +483,7 @@ abstract contract Entropy is IEntropy, EntropyState { providerRevelation ); emit Revealed( - req, + EntropyStructConverter.toV1Request(req), userRevelation, providerRevelation, blockHash, @@ -564,7 +565,7 @@ abstract contract Entropy is IEntropy, EntropyState { if (success) { emit RevealedWithCallback( - req, + EntropyStructConverter.toV1Request(req), userRandomNumber, providerRevelation, randomNumber @@ -602,7 +603,7 @@ abstract contract Entropy is IEntropy, EntropyState { } else { // This case uses the checks-effects-interactions pattern to avoid reentry attacks emit RevealedWithCallback( - req, + EntropyStructConverter.toV1Request(req), userRandomNumber, providerRevelation, randomNumber diff --git a/target_chains/ethereum/contracts/forge-test/Entropy.t.sol b/target_chains/ethereum/contracts/forge-test/Entropy.t.sol index 34beee7edd..820fefeab2 100644 --- a/target_chains/ethereum/contracts/forge-test/Entropy.t.sol +++ b/target_chains/ethereum/contracts/forge-test/Entropy.t.sol @@ -791,7 +791,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { user1, providerInfo.sequenceNumber, userRandomNumber, - EntropyStructsV2.Request({ + EntropyStructs.Request({ provider: provider1, sequenceNumber: providerInfo.sequenceNumber, numHashes: SafeCast.toUint32( @@ -807,8 +807,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { blockNumber: 1234, requester: user1, useBlockhash: false, - callbackStatus: EntropyStatusConstants.CALLBACK_NOT_STARTED, - gasLimit10k: 0 + callbackStatus: EntropyStatusConstants.CALLBACK_NOT_STARTED }) ); vm.roll(1234); @@ -852,7 +851,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { vm.expectEmit(false, false, false, true, address(random)); emit RevealedWithCallback( - req, + EntropyStructConverter.toV1Request(req), userRandomNumber, provider1Proofs[assignedSequenceNumber], random.combineRandomValues( @@ -905,7 +904,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { vm.expectEmit(false, false, false, true, address(random)); emit RevealedWithCallback( - req, + EntropyStructConverter.toV1Request(req), userRandomNumber, provider1Proofs[assignedSequenceNumber], random.combineRandomValues( @@ -985,7 +984,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { vm.expectEmit(false, false, false, true, address(random)); emit RevealedWithCallback( - req, + EntropyStructConverter.toV1Request(req), userRandomNumber, provider1Proofs[assignedSequenceNumber], random.combineRandomValues( @@ -1091,7 +1090,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { consumer.setReverts(false); vm.expectEmit(false, false, false, true, address(random)); emit RevealedWithCallback( - reqAfterFailure, + EntropyStructConverter.toV1Request(reqAfterFailure), userRandomNumber, provider1Proofs[assignedSequenceNumber], random.combineRandomValues( @@ -1204,7 +1203,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { // Calling without a gas limit should succeed vm.expectEmit(false, false, false, true, address(random)); emit RevealedWithCallback( - reqAfterFailure, + EntropyStructConverter.toV1Request(reqAfterFailure), userRandomNumber, provider1Proofs[assignedSequenceNumber], random.combineRandomValues( @@ -1692,7 +1691,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { } else { vm.expectEmit(false, false, false, true, address(random)); emit RevealedWithCallback( - req, + EntropyStructConverter.toV1Request(req), userRandomNumber, provider1Proofs[sequenceNumber], random.combineRandomValues( From adfcedf9c18a793591ac1ed8ac4ffe9e9133b63e Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Thu, 24 Apr 2025 11:44:09 -0700 Subject: [PATCH 03/12] fix event signatures --- target_chains/ethereum/contracts/forge-test/Entropy.t.sol | 2 +- .../ethereum/entropy_sdk/solidity/EntropyStructs.sol | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/target_chains/ethereum/contracts/forge-test/Entropy.t.sol b/target_chains/ethereum/contracts/forge-test/Entropy.t.sol index 820fefeab2..fbb8e9f24a 100644 --- a/target_chains/ethereum/contracts/forge-test/Entropy.t.sol +++ b/target_chains/ethereum/contracts/forge-test/Entropy.t.sol @@ -807,7 +807,7 @@ contract EntropyTest is Test, EntropyTestUtils, EntropyEvents { blockNumber: 1234, requester: user1, useBlockhash: false, - callbackStatus: EntropyStatusConstants.CALLBACK_NOT_STARTED + isRequestWithCallback: true }) ); vm.roll(1234); diff --git a/target_chains/ethereum/entropy_sdk/solidity/EntropyStructs.sol b/target_chains/ethereum/entropy_sdk/solidity/EntropyStructs.sol index 16ec10baf9..0b7a796bd6 100644 --- a/target_chains/ethereum/entropy_sdk/solidity/EntropyStructs.sol +++ b/target_chains/ethereum/entropy_sdk/solidity/EntropyStructs.sol @@ -59,7 +59,7 @@ contract EntropyStructs { address requester; // If true, incorporate the blockhash of blockNumber into the generated random value. bool useBlockhash; - // Status flag for requests with callbacks. See EntropyConstants for the possible values of this flag. - uint8 callbackStatus; + // True if this is a request that expects a callback. + bool isRequestWithCallback; } } From bd70bd22c264c64c2403cdc4ea4b2b043acee87d Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Thu, 24 Apr 2025 11:49:44 -0700 Subject: [PATCH 04/12] cleanup --- .../contracts/forge-test/Entropy.t.sol | 1 - .../entropy_sdk/solidity/EntropyEventsV2.sol | 72 ------------------- .../entropy_sdk/solidity/EntropyStructs.sol | 5 ++ .../solidity/abis/EntropyEvents.json | 49 ++++--------- .../entropy_sdk/solidity/abis/IEntropy.json | 53 ++++---------- 5 files changed, 31 insertions(+), 149 deletions(-) delete mode 100644 target_chains/ethereum/entropy_sdk/solidity/EntropyEventsV2.sol diff --git a/target_chains/ethereum/contracts/forge-test/Entropy.t.sol b/target_chains/ethereum/contracts/forge-test/Entropy.t.sol index fbb8e9f24a..3a6cd0b15f 100644 --- a/target_chains/ethereum/contracts/forge-test/Entropy.t.sol +++ b/target_chains/ethereum/contracts/forge-test/Entropy.t.sol @@ -6,7 +6,6 @@ import "forge-std/Test.sol"; import "@pythnetwork/entropy-sdk-solidity/EntropyStructs.sol"; import "@pythnetwork/entropy-sdk-solidity/EntropyStructsV2.sol"; import "@pythnetwork/entropy-sdk-solidity/EntropyEvents.sol"; -import "@pythnetwork/entropy-sdk-solidity/EntropyEventsV2.sol"; import "@pythnetwork/entropy-sdk-solidity/IEntropyConsumer.sol"; import "@pythnetwork/entropy-sdk-solidity/IEntropy.sol"; import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; diff --git a/target_chains/ethereum/entropy_sdk/solidity/EntropyEventsV2.sol b/target_chains/ethereum/entropy_sdk/solidity/EntropyEventsV2.sol deleted file mode 100644 index f37792c047..0000000000 --- a/target_chains/ethereum/entropy_sdk/solidity/EntropyEventsV2.sol +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - - -interface EntropyEventsV2 { - - /* - event Registered(address indexed provider, EntropyStructs.ProviderInfo info); - - // TODO - event Requested(EntropyStructs.Request request); - - event RequestedWithCallback( - address indexed provider, - address indexed requestor, - uint64 indexed sequenceNumber, - bytes32 userRandomNumber, - EntropyStructs.Request request - ); - - event Revealed( - EntropyStructs.Request request, - bytes32 userRevelation, - bytes32 providerRevelation, - bytes32 blockHash, - bytes32 randomNumber - ); - event RevealedWithCallback( - EntropyStructs.Request request, - bytes32 userRandomNumber, - bytes32 providerRevelation, - bytes32 randomNumber - ); - - event CallbackFailed( - address indexed provider, - address indexed requestor, - uint64 indexed sequenceNumber, - bytes32 userRandomNumber, - bytes32 providerRevelation, - bytes32 randomNumber, - bytes errorCode - ); - - event ProviderFeeUpdated(address indexed provider, uint128 oldFee, uint128 newFee); - - event ProviderDefaultGasLimitUpdated( - address indexed provider, - uint32 oldDefaultGasLimit, - uint32 newDefaultGasLimit - ); - - event ProviderUriUpdated(address indexed provider, bytes oldUri, bytes newUri); - - event ProviderFeeManagerUpdated( - address indexed provider, - address oldFeeManager, - address newFeeManager - ); - event ProviderMaxNumHashesAdvanced( - address indexed provider, - uint32 oldMaxNumHashes, - uint32 newMaxNumHashes - ); - - event Withdrawal( - address indexed provider, - address recipient, - uint128 withdrawnAmount - ); - */ -} diff --git a/target_chains/ethereum/entropy_sdk/solidity/EntropyStructs.sol b/target_chains/ethereum/entropy_sdk/solidity/EntropyStructs.sol index 0b7a796bd6..58f2aaafa6 100644 --- a/target_chains/ethereum/entropy_sdk/solidity/EntropyStructs.sol +++ b/target_chains/ethereum/entropy_sdk/solidity/EntropyStructs.sol @@ -2,6 +2,11 @@ pragma solidity ^0.8.0; +// This contract holds old versions of the Entropy structs that are no longer used for contract storage. +// However, they are still used in EntropyEvents to maintain the public interface of prior versions of +// the Entropy contract. +// +// See EntropyStructsV2 for the struct definitions currently in use. contract EntropyStructs { struct ProviderInfo { uint128 feeInWei; diff --git a/target_chains/ethereum/entropy_sdk/solidity/abis/EntropyEvents.json b/target_chains/ethereum/entropy_sdk/solidity/abis/EntropyEvents.json index f257fccf35..7d39346333 100644 --- a/target_chains/ethereum/entropy_sdk/solidity/abis/EntropyEvents.json +++ b/target_chains/ethereum/entropy_sdk/solidity/abis/EntropyEvents.json @@ -237,11 +237,6 @@ "internalType": "uint32", "name": "maxNumHashes", "type": "uint32" - }, - { - "internalType": "uint32", - "name": "defaultGasLimit", - "type": "uint32" } ], "indexed": false, @@ -294,14 +289,9 @@ "type": "bool" }, { - "internalType": "uint8", - "name": "callbackStatus", - "type": "uint8" - }, - { - "internalType": "uint16", - "name": "gasLimit10k", - "type": "uint16" + "internalType": "bool", + "name": "isRequestWithCallback", + "type": "bool" } ], "indexed": false, @@ -378,14 +368,9 @@ "type": "bool" }, { - "internalType": "uint8", - "name": "callbackStatus", - "type": "uint8" - }, - { - "internalType": "uint16", - "name": "gasLimit10k", - "type": "uint16" + "internalType": "bool", + "name": "isRequestWithCallback", + "type": "bool" } ], "indexed": false, @@ -438,14 +423,9 @@ "type": "bool" }, { - "internalType": "uint8", - "name": "callbackStatus", - "type": "uint8" - }, - { - "internalType": "uint16", - "name": "gasLimit10k", - "type": "uint16" + "internalType": "bool", + "name": "isRequestWithCallback", + "type": "bool" } ], "indexed": false, @@ -522,14 +502,9 @@ "type": "bool" }, { - "internalType": "uint8", - "name": "callbackStatus", - "type": "uint8" - }, - { - "internalType": "uint16", - "name": "gasLimit10k", - "type": "uint16" + "internalType": "bool", + "name": "isRequestWithCallback", + "type": "bool" } ], "indexed": false, diff --git a/target_chains/ethereum/entropy_sdk/solidity/abis/IEntropy.json b/target_chains/ethereum/entropy_sdk/solidity/abis/IEntropy.json index 3012caf605..96084f204d 100644 --- a/target_chains/ethereum/entropy_sdk/solidity/abis/IEntropy.json +++ b/target_chains/ethereum/entropy_sdk/solidity/abis/IEntropy.json @@ -237,11 +237,6 @@ "internalType": "uint32", "name": "maxNumHashes", "type": "uint32" - }, - { - "internalType": "uint32", - "name": "defaultGasLimit", - "type": "uint32" } ], "indexed": false, @@ -294,14 +289,9 @@ "type": "bool" }, { - "internalType": "uint8", - "name": "callbackStatus", - "type": "uint8" - }, - { - "internalType": "uint16", - "name": "gasLimit10k", - "type": "uint16" + "internalType": "bool", + "name": "isRequestWithCallback", + "type": "bool" } ], "indexed": false, @@ -378,14 +368,9 @@ "type": "bool" }, { - "internalType": "uint8", - "name": "callbackStatus", - "type": "uint8" - }, - { - "internalType": "uint16", - "name": "gasLimit10k", - "type": "uint16" + "internalType": "bool", + "name": "isRequestWithCallback", + "type": "bool" } ], "indexed": false, @@ -438,14 +423,9 @@ "type": "bool" }, { - "internalType": "uint8", - "name": "callbackStatus", - "type": "uint8" - }, - { - "internalType": "uint16", - "name": "gasLimit10k", - "type": "uint16" + "internalType": "bool", + "name": "isRequestWithCallback", + "type": "bool" } ], "indexed": false, @@ -522,14 +502,9 @@ "type": "bool" }, { - "internalType": "uint8", - "name": "callbackStatus", - "type": "uint8" - }, - { - "internalType": "uint16", - "name": "gasLimit10k", - "type": "uint16" + "internalType": "bool", + "name": "isRequestWithCallback", + "type": "bool" } ], "indexed": false, @@ -802,7 +777,7 @@ "type": "uint32" } ], - "internalType": "struct EntropyStructs.ProviderInfo", + "internalType": "struct EntropyStructsV2.ProviderInfo", "name": "info", "type": "tuple" } @@ -873,7 +848,7 @@ "type": "uint16" } ], - "internalType": "struct EntropyStructs.Request", + "internalType": "struct EntropyStructsV2.Request", "name": "req", "type": "tuple" } From 871331affb0a88264f4a4dffeec009bca92ca308 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Thu, 24 Apr 2025 11:54:08 -0700 Subject: [PATCH 05/12] forgot a file --- .../entropy/EntropyStructConverter.sol | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 target_chains/ethereum/contracts/contracts/entropy/EntropyStructConverter.sol diff --git a/target_chains/ethereum/contracts/contracts/entropy/EntropyStructConverter.sol b/target_chains/ethereum/contracts/contracts/entropy/EntropyStructConverter.sol new file mode 100644 index 0000000000..fe8cf7e237 --- /dev/null +++ b/target_chains/ethereum/contracts/contracts/entropy/EntropyStructConverter.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: Apache 2 +pragma solidity ^0.8.0; + +import "@pythnetwork/entropy-sdk-solidity/EntropyStructs.sol"; +import "@pythnetwork/entropy-sdk-solidity/EntropyStructsV2.sol"; + +// Conversions from V2 structs to their V1 equivalents +library EntropyStructConverter { + function toV1Request( + EntropyStructsV2.Request memory v2Request + ) internal pure returns (EntropyStructs.Request memory v1Request) { + v1Request = EntropyStructs.Request({ + provider: v2Request.provider, + sequenceNumber: v2Request.sequenceNumber, + numHashes: v2Request.numHashes, + commitment: v2Request.commitment, + blockNumber: v2Request.blockNumber, + requester: v2Request.requester, + useBlockhash: v2Request.useBlockhash, + isRequestWithCallback: v2Request.callbackStatus > 0 + }); + } + + function toV1ProviderInfo( + EntropyStructsV2.ProviderInfo memory v2ProviderInfo + ) + internal + pure + returns (EntropyStructs.ProviderInfo memory v1ProviderInfo) + { + v1ProviderInfo = EntropyStructs.ProviderInfo({ + feeInWei: v2ProviderInfo.feeInWei, + accruedFeesInWei: v2ProviderInfo.accruedFeesInWei, + originalCommitment: v2ProviderInfo.originalCommitment, + originalCommitmentSequenceNumber: v2ProviderInfo + .originalCommitmentSequenceNumber, + commitmentMetadata: v2ProviderInfo.commitmentMetadata, + uri: v2ProviderInfo.uri, + endSequenceNumber: v2ProviderInfo.endSequenceNumber, + sequenceNumber: v2ProviderInfo.sequenceNumber, + currentCommitment: v2ProviderInfo.currentCommitment, + currentCommitmentSequenceNumber: v2ProviderInfo + .currentCommitmentSequenceNumber, + feeManager: v2ProviderInfo.feeManager, + maxNumHashes: v2ProviderInfo.maxNumHashes + }); + } +} From 435af2287b96f785bfa3a3daabf0cb253fc13fd3 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Thu, 24 Apr 2025 12:32:45 -0700 Subject: [PATCH 06/12] generate ABI --- target_chains/ethereum/entropy_sdk/solidity/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target_chains/ethereum/entropy_sdk/solidity/package.json b/target_chains/ethereum/entropy_sdk/solidity/package.json index c8fb9e3e84..e0ef46a626 100644 --- a/target_chains/ethereum/entropy_sdk/solidity/package.json +++ b/target_chains/ethereum/entropy_sdk/solidity/package.json @@ -14,7 +14,7 @@ "scripts": { "test:format": "prettier --check .", "fix:format": "prettier --write .", - "build": "generate-abis IEntropy IEntropyConsumer EntropyErrors EntropyEvents EntropyStructs EntropyStatusConstants PRNG", + "build": "generate-abis IEntropy IEntropyConsumer EntropyErrors EntropyEvents EntropyStructs EntropyStructsV2 EntropyStatusConstants PRNG", "test": "git diff --exit-code abis" }, "keywords": [ From 0f0a14cbb5ecb0952b32420cdca4b1805ec60006 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Thu, 24 Apr 2025 12:35:49 -0700 Subject: [PATCH 07/12] add missing abi --- .../ethereum/entropy_sdk/solidity/abis/EntropyStructsV2.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 target_chains/ethereum/entropy_sdk/solidity/abis/EntropyStructsV2.json diff --git a/target_chains/ethereum/entropy_sdk/solidity/abis/EntropyStructsV2.json b/target_chains/ethereum/entropy_sdk/solidity/abis/EntropyStructsV2.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/target_chains/ethereum/entropy_sdk/solidity/abis/EntropyStructsV2.json @@ -0,0 +1 @@ +[] From 55843c16ec6784e1475f1ae5f40a1308afbc1717 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Thu, 24 Apr 2025 12:48:03 -0700 Subject: [PATCH 08/12] fix ABI stuff in fortuna --- apps/fortuna/src/command/inspect.rs | 6 +++--- apps/fortuna/src/command/setup_provider.rs | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/fortuna/src/command/inspect.rs b/apps/fortuna/src/command/inspect.rs index de88c7738d..a593111468 100644 --- a/apps/fortuna/src/command/inspect.rs +++ b/apps/fortuna/src/command/inspect.rs @@ -1,6 +1,6 @@ use { crate::{ - chain::ethereum::{PythContract, Request}, + chain::ethereum::{PythContract, EntropyStructsV2Request}, config::{Config, EthereumConfig, InspectOptions}, }, anyhow::Result, @@ -66,7 +66,7 @@ async fn inspect_chain( ); current_request_number -= 1; } - let return_data: Vec = multicall.call_array().await?; + let return_data: Vec = multicall.call_array().await?; for request in return_data { process_request(rpc_provider.clone(), request).await?; } @@ -89,7 +89,7 @@ async fn inspect_chain( Ok(()) } -async fn process_request(rpc_provider: Provider, request: Request) -> Result<()> { +async fn process_request(rpc_provider: Provider, request: EntropyStructsV2Request) -> Result<()> { if request.sequence_number != 0 && request.callback_status != 0 { let block = rpc_provider .get_block(request.block_number) diff --git a/apps/fortuna/src/command/setup_provider.rs b/apps/fortuna/src/command/setup_provider.rs index 3c587b0d14..8d56b15b60 100644 --- a/apps/fortuna/src/command/setup_provider.rs +++ b/apps/fortuna/src/command/setup_provider.rs @@ -1,7 +1,7 @@ use { crate::{ api::{get_register_uri, ChainId}, - chain::ethereum::{ProviderInfo, SignablePythContract}, + chain::ethereum::{EntropyStructsV2ProviderInfo, SignablePythContract}, command::register_provider::{register_provider_from_config, CommitmentMetadata}, config::{Config, EthereumConfig, SetupProviderOptions}, state::{HashChainState, PebbleHashChain}, @@ -178,7 +178,7 @@ async fn setup_chain_provider( async fn sync_uri( contract: &Arc, - provider_info: &ProviderInfo, + provider_info: &EntropyStructsV2ProviderInfo, uri: String, ) -> Result<()> { let uri_as_bytes: Bytes = AbiBytes::from(uri.as_str()).into(); @@ -198,7 +198,7 @@ async fn sync_uri( async fn sync_fee( contract: &Arc, - provider_info: &ProviderInfo, + provider_info: &EntropyStructsV2ProviderInfo, provider_fee: u128, ) -> Result<()> { if provider_info.fee_in_wei != provider_fee { @@ -217,7 +217,7 @@ async fn sync_fee( async fn sync_fee_manager( contract: &Arc, - provider_info: &ProviderInfo, + provider_info: &EntropyStructsV2ProviderInfo, fee_manager: Address, ) -> Result<()> { if provider_info.fee_manager != fee_manager { @@ -231,7 +231,7 @@ async fn sync_fee_manager( async fn sync_max_num_hashes( contract: &Arc, - provider_info: &ProviderInfo, + provider_info: &EntropyStructsV2ProviderInfo, max_num_hashes: u32, ) -> Result<()> { if provider_info.max_num_hashes != max_num_hashes { From 771f8153cfbdd112ff352a052f8ee3ab76764d69 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Thu, 24 Apr 2025 20:48:49 -0700 Subject: [PATCH 09/12] CLI for testing --- apps/fortuna/src/chain/ethereum.rs | 32 +++++++++++++ apps/fortuna/src/command/generate.rs | 68 +++++++++++++--------------- apps/fortuna/src/config.rs | 1 - apps/fortuna/src/config/generate.rs | 8 ---- 4 files changed, 64 insertions(+), 45 deletions(-) diff --git a/apps/fortuna/src/chain/ethereum.rs b/apps/fortuna/src/chain/ethereum.rs index 939f8f7cc8..ffae061289 100644 --- a/apps/fortuna/src/chain/ethereum.rs +++ b/apps/fortuna/src/chain/ethereum.rs @@ -93,6 +93,38 @@ impl SignablePythContractInner { } } + /// Submit a request for a random number to the contract. + /// + /// This method is a version of the autogenned `request` method that parses the emitted logs + /// to return the sequence number of the created Request. + pub async fn request_with_callback_wrapper( + &self, + provider: &Address, + user_randomness: &[u8; 32], + ) -> Result { + let fee = self.get_fee(*provider).call().await?; + + let hashed_randomness: [u8; 32] = Keccak256::digest(user_randomness).into(); + + if let Some(r) = self + .request_with_callback(*provider, hashed_randomness) + .value(fee) + .send() + .await? + .await? + { + // Extract Log from TransactionReceipt. + let l: RawLog = r.logs[0].clone().into(); + if let PythRandomEvents::RequestedFilter(r) = PythRandomEvents::decode_log(&l)? { + Ok(r.request.sequence_number) + } else { + Err(anyhow!("No log with sequence number")) + } + } else { + Err(anyhow!("Request failed")) + } + } + /// Reveal the generated random number to the contract. /// /// This method is a version of the autogenned `reveal` method that parses the emitted logs diff --git a/apps/fortuna/src/command/generate.rs b/apps/fortuna/src/command/generate.rs index a1216d0b14..163d5738e5 100644 --- a/apps/fortuna/src/command/generate.rs +++ b/apps/fortuna/src/command/generate.rs @@ -1,12 +1,9 @@ use { crate::{ - api::GetRandomValueResponse, - chain::ethereum::SignablePythContract, + chain::ethereum::{SignablePythContract, RevealedWithCallbackFilter}, config::{Config, GenerateOptions}, - }, - anyhow::Result, - base64::{engine::general_purpose::STANDARD as base64_standard_engine, Engine as _}, - std::sync::Arc, + }, anyhow::Result, base64::{engine::general_purpose::STANDARD as base64_standard_engine, Engine as _}, ethers::providers::Middleware, std::sync::Arc, + tokio::time::{self, Duration}, }; /// Run the entire random number generation protocol to produce a random number. @@ -22,42 +19,41 @@ pub async fn generate(opts: &GenerateOptions) -> Result<()> { let user_randomness = rand::random::<[u8; 32]>(); let provider = opts.provider; + let mut last_block_number = contract.provider().get_block_number().await?; + // Request a random number on the contract let sequence_number = contract - .request_wrapper(&provider, &user_randomness, opts.blockhash) + .request_with_callback_wrapper(&provider, &user_randomness) .await?; tracing::info!(sequence_number = sequence_number, "random number requested",); - // Get the committed value from the provider - let resp = reqwest::get(opts.url.join(&format!( - "/v1/chains/{}/revelations/{}", - opts.chain_id, sequence_number - ))?) - .await? - .json::() - .await?; - - tracing::info!( - response = base64_standard_engine.encode(resp.value.data()), - "Retrieved the provider's random value.", - ); - let provider_randomness = resp.value.data(); - - // Submit the provider's and our values to the contract to reveal the random number. - let random_value = contract - .reveal_wrapper( - &provider, - sequence_number, - &user_randomness, - provider_randomness, - ) - .await?; - - tracing::info!( - number = base64_standard_engine.encode(random_value), - "Random number generated." - ); + for _i in [0..10] { + let current_block_number = contract.provider().get_block_number().await?; + tracing::info!( + start_block = last_block_number.as_u64(), + end_block = current_block_number.as_u64(), + "Checking events between blocks." + ); + + let mut event = contract.revealed_with_callback_filter(); + event.filter = event.filter.from_block(last_block_number).to_block(current_block_number); + + let res: Vec = event.query().await?; + + for r in res.iter() { + if r.request.sequence_number == sequence_number && r.request.provider == provider { + tracing::info!( + number = base64_standard_engine.encode(r.random_number), + "Random number generated." + ); + break; + } + } + + last_block_number = current_block_number; + time::sleep(Duration::from_secs(1)).await; + } Ok(()) } diff --git a/apps/fortuna/src/config.rs b/apps/fortuna/src/config.rs index 70014ddcb7..5bf3582992 100644 --- a/apps/fortuna/src/config.rs +++ b/apps/fortuna/src/config.rs @@ -25,7 +25,6 @@ mod setup_provider; mod withdraw_fees; const DEFAULT_RPC_ADDR: &str = "127.0.0.1:34000"; -const DEFAULT_HTTP_ADDR: &str = "http://127.0.0.1:34000"; #[derive(Parser, Debug)] #[command(name = crate_name!())] diff --git a/apps/fortuna/src/config/generate.rs b/apps/fortuna/src/config/generate.rs index 198b15eee4..71198f9eea 100644 --- a/apps/fortuna/src/config/generate.rs +++ b/apps/fortuna/src/config/generate.rs @@ -2,7 +2,6 @@ use { crate::{api::ChainId, config::ConfigOptions}, clap::Args, ethers::types::Address, - reqwest::Url, }; #[derive(Args, Clone, Debug)] @@ -27,11 +26,4 @@ pub struct GenerateOptions { /// Submit a randomness request to this provider #[arg(long = "provider")] pub provider: Address, - - #[arg(long = "url")] - #[arg(default_value = super::DEFAULT_HTTP_ADDR)] - pub url: Url, - - #[arg(short = 'b')] - pub blockhash: bool, } From f87293587ae0ce26fc3d153baf6c67a7f30e311b Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Fri, 25 Apr 2025 06:52:21 -0700 Subject: [PATCH 10/12] hmm --- apps/fortuna/src/chain/ethereum.rs | 5 +++++ apps/fortuna/src/command/generate.rs | 3 +++ 2 files changed, 8 insertions(+) diff --git a/apps/fortuna/src/chain/ethereum.rs b/apps/fortuna/src/chain/ethereum.rs index ffae061289..82fbbbf5ca 100644 --- a/apps/fortuna/src/chain/ethereum.rs +++ b/apps/fortuna/src/chain/ethereum.rs @@ -102,8 +102,12 @@ impl SignablePythContractInner { provider: &Address, user_randomness: &[u8; 32], ) -> Result { + println!("1"); + let fee = self.get_fee(*provider).call().await?; + println!("2"); + let hashed_randomness: [u8; 32] = Keccak256::digest(user_randomness).into(); if let Some(r) = self @@ -113,6 +117,7 @@ impl SignablePythContractInner { .await? .await? { + println!("3"); // Extract Log from TransactionReceipt. let l: RawLog = r.logs[0].clone().into(); if let PythRandomEvents::RequestedFilter(r) = PythRandomEvents::decode_log(&l)? { diff --git a/apps/fortuna/src/command/generate.rs b/apps/fortuna/src/command/generate.rs index 163d5738e5..8e0d6bd169 100644 --- a/apps/fortuna/src/command/generate.rs +++ b/apps/fortuna/src/command/generate.rs @@ -19,7 +19,10 @@ pub async fn generate(opts: &GenerateOptions) -> Result<()> { let user_randomness = rand::random::<[u8; 32]>(); let provider = opts.provider; + tracing::info!("starting"); + let mut last_block_number = contract.provider().get_block_number().await?; + tracing::info!(block_number = last_block_number.as_u64(), "block number"); // Request a random number on the contract let sequence_number = contract From 6a3060de1fd2a4b881507551752737a145735d18 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Fri, 25 Apr 2025 07:43:48 -0700 Subject: [PATCH 11/12] cleanup --- apps/fortuna/src/chain/ethereum.rs | 9 +++------ apps/fortuna/src/command/generate.rs | 23 ++++++++++++++++------- apps/fortuna/src/command/inspect.rs | 7 +++++-- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/apps/fortuna/src/chain/ethereum.rs b/apps/fortuna/src/chain/ethereum.rs index 82fbbbf5ca..89c9775a9b 100644 --- a/apps/fortuna/src/chain/ethereum.rs +++ b/apps/fortuna/src/chain/ethereum.rs @@ -102,12 +102,8 @@ impl SignablePythContractInner { provider: &Address, user_randomness: &[u8; 32], ) -> Result { - println!("1"); - let fee = self.get_fee(*provider).call().await?; - println!("2"); - let hashed_randomness: [u8; 32] = Keccak256::digest(user_randomness).into(); if let Some(r) = self @@ -117,10 +113,11 @@ impl SignablePythContractInner { .await? .await? { - println!("3"); // Extract Log from TransactionReceipt. let l: RawLog = r.logs[0].clone().into(); - if let PythRandomEvents::RequestedFilter(r) = PythRandomEvents::decode_log(&l)? { + if let PythRandomEvents::RequestedWithCallbackFilter(r) = + PythRandomEvents::decode_log(&l)? + { Ok(r.request.sequence_number) } else { Err(anyhow!("No log with sequence number")) diff --git a/apps/fortuna/src/command/generate.rs b/apps/fortuna/src/command/generate.rs index 8e0d6bd169..08ffc93e4a 100644 --- a/apps/fortuna/src/command/generate.rs +++ b/apps/fortuna/src/command/generate.rs @@ -1,8 +1,12 @@ use { crate::{ - chain::ethereum::{SignablePythContract, RevealedWithCallbackFilter}, + chain::ethereum::{RevealedWithCallbackFilter, SignablePythContract}, config::{Config, GenerateOptions}, - }, anyhow::Result, base64::{engine::general_purpose::STANDARD as base64_standard_engine, Engine as _}, ethers::providers::Middleware, std::sync::Arc, + }, + anyhow::Result, + base64::{engine::general_purpose::STANDARD as base64_standard_engine, Engine as _}, + ethers::providers::Middleware, + std::sync::Arc, tokio::time::{self, Duration}, }; @@ -19,19 +23,19 @@ pub async fn generate(opts: &GenerateOptions) -> Result<()> { let user_randomness = rand::random::<[u8; 32]>(); let provider = opts.provider; - tracing::info!("starting"); - let mut last_block_number = contract.provider().get_block_number().await?; tracing::info!(block_number = last_block_number.as_u64(), "block number"); + tracing::info!("Requesting random number..."); + // Request a random number on the contract let sequence_number = contract .request_with_callback_wrapper(&provider, &user_randomness) .await?; - tracing::info!(sequence_number = sequence_number, "random number requested",); + tracing::info!(sequence_number = sequence_number, "Random number requested",); - for _i in [0..10] { + for _i in 0..10 { let current_block_number = contract.provider().get_block_number().await?; tracing::info!( start_block = last_block_number.as_u64(), @@ -40,7 +44,10 @@ pub async fn generate(opts: &GenerateOptions) -> Result<()> { ); let mut event = contract.revealed_with_callback_filter(); - event.filter = event.filter.from_block(last_block_number).to_block(current_block_number); + event.filter = event + .filter + .from_block(last_block_number) + .to_block(current_block_number); let res: Vec = event.query().await?; @@ -58,5 +65,7 @@ pub async fn generate(opts: &GenerateOptions) -> Result<()> { time::sleep(Duration::from_secs(1)).await; } + tracing::info!("Failed to receive a callback with the random number."); + Ok(()) } diff --git a/apps/fortuna/src/command/inspect.rs b/apps/fortuna/src/command/inspect.rs index a593111468..4ab1541fe9 100644 --- a/apps/fortuna/src/command/inspect.rs +++ b/apps/fortuna/src/command/inspect.rs @@ -1,6 +1,6 @@ use { crate::{ - chain::ethereum::{PythContract, EntropyStructsV2Request}, + chain::ethereum::{EntropyStructsV2Request, PythContract}, config::{Config, EthereumConfig, InspectOptions}, }, anyhow::Result, @@ -89,7 +89,10 @@ async fn inspect_chain( Ok(()) } -async fn process_request(rpc_provider: Provider, request: EntropyStructsV2Request) -> Result<()> { +async fn process_request( + rpc_provider: Provider, + request: EntropyStructsV2Request, +) -> Result<()> { if request.sequence_number != 0 && request.callback_status != 0 { let block = rpc_provider .get_block(request.block_number) From 6cd50321f5a968943ddb69e4c3e5d90ea9f180f1 Mon Sep 17 00:00:00 2001 From: Jayant Krishnamurthy Date: Fri, 25 Apr 2025 08:23:31 -0700 Subject: [PATCH 12/12] minor fixes --- apps/fortuna/src/chain/ethereum.rs | 4 +--- apps/fortuna/src/command/generate.rs | 11 ++++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/fortuna/src/chain/ethereum.rs b/apps/fortuna/src/chain/ethereum.rs index 89c9775a9b..daae50a4ee 100644 --- a/apps/fortuna/src/chain/ethereum.rs +++ b/apps/fortuna/src/chain/ethereum.rs @@ -104,10 +104,8 @@ impl SignablePythContractInner { ) -> Result { let fee = self.get_fee(*provider).call().await?; - let hashed_randomness: [u8; 32] = Keccak256::digest(user_randomness).into(); - if let Some(r) = self - .request_with_callback(*provider, hashed_randomness) + .request_with_callback(*provider, *user_randomness) .value(fee) .send() .await? diff --git a/apps/fortuna/src/command/generate.rs b/apps/fortuna/src/command/generate.rs index 08ffc93e4a..825c1f1386 100644 --- a/apps/fortuna/src/command/generate.rs +++ b/apps/fortuna/src/command/generate.rs @@ -35,7 +35,9 @@ pub async fn generate(opts: &GenerateOptions) -> Result<()> { tracing::info!(sequence_number = sequence_number, "Random number requested",); - for _i in 0..10 { + let mut num_retries = 0; + let mut found_request = false; + while !found_request && num_retries < 10 { let current_block_number = contract.provider().get_block_number().await?; tracing::info!( start_block = last_block_number.as_u64(), @@ -57,15 +59,18 @@ pub async fn generate(opts: &GenerateOptions) -> Result<()> { number = base64_standard_engine.encode(r.random_number), "Random number generated." ); - break; + found_request = true; } } last_block_number = current_block_number; + num_retries += 1; time::sleep(Duration::from_secs(1)).await; } - tracing::info!("Failed to receive a callback with the random number."); + if !found_request { + tracing::info!("Failed to receive a callback with the random number."); + } Ok(()) }