-
Notifications
You must be signed in to change notification settings - Fork 192
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
PRNG Library with Pyth Entropy Integration (#1733)
* Pyth Entropy PRNG library * Update to a contract to store internal state * Add seed setter function * Make functions internal * Add tests and address PR comments * Run end-of-file fixer
- Loading branch information
1 parent
5a2e4a8
commit e6ae23b
Showing
5 changed files
with
245 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
// SPDX-License-Identifier: Apache 2 | ||
pragma solidity ^0.8.0; | ||
|
||
import "@pythnetwork/entropy-sdk-solidity/PRNG.sol"; | ||
import "forge-std/Test.sol"; | ||
|
||
contract PRNGTestHelper is PRNG { | ||
constructor(bytes32 _seed) PRNG(_seed) {} | ||
|
||
function publicNextBytes32() public returns (bytes32) { | ||
return nextBytes32(); | ||
} | ||
|
||
function publicRandUint() public returns (uint256) { | ||
return randUint(); | ||
} | ||
|
||
function publicRandUint64() public returns (uint64) { | ||
return randUint64(); | ||
} | ||
|
||
function publicRandUintRange( | ||
uint256 min, | ||
uint256 max | ||
) public returns (uint256) { | ||
return randUintRange(min, max); | ||
} | ||
|
||
function publicRandomPermutation( | ||
uint256 length | ||
) public returns (uint256[] memory) { | ||
return randomPermutation(length); | ||
} | ||
} | ||
|
||
contract PRNGTest is Test { | ||
PRNGTestHelper prng; | ||
|
||
function setUp() public { | ||
prng = new PRNGTestHelper(keccak256(abi.encode("initial seed"))); | ||
} | ||
|
||
function testNextBytes32() public { | ||
bytes32 randomValue1 = prng.publicNextBytes32(); | ||
bytes32 randomValue2 = prng.publicNextBytes32(); | ||
|
||
assertNotEq( | ||
randomValue1, | ||
randomValue2, | ||
"Random values should not be equal" | ||
); | ||
} | ||
|
||
function testRandUint() public { | ||
uint256 randomValue1 = prng.publicRandUint(); | ||
uint256 randomValue2 = prng.publicRandUint(); | ||
|
||
assertNotEq( | ||
randomValue1, | ||
randomValue2, | ||
"Random values should not be equal" | ||
); | ||
} | ||
|
||
function testRandUint64() public { | ||
uint64 randomValue1 = prng.publicRandUint64(); | ||
uint64 randomValue2 = prng.publicRandUint64(); | ||
|
||
assertNotEq( | ||
randomValue1, | ||
randomValue2, | ||
"Random values should not be equal" | ||
); | ||
} | ||
|
||
function testRandUintRange() public { | ||
uint256 min = 10; | ||
uint256 max = 20; | ||
|
||
for (uint256 i = 0; i < 100; i++) { | ||
uint256 randomValue = prng.publicRandUintRange(min, max); | ||
assertGe( | ||
randomValue, | ||
min, | ||
"Random value should be greater than or equal to min" | ||
); | ||
assertLt(randomValue, max, "Random value should be less than max"); | ||
} | ||
} | ||
|
||
function testRandomPermutation() public { | ||
uint256 length = 5; | ||
uint256[] memory permutation = prng.publicRandomPermutation(length); | ||
|
||
assertEq(permutation.length, length, "Permutation length should match"); | ||
|
||
bool[] memory found = new bool[](length); | ||
for (uint256 i = 0; i < length; i++) { | ||
assertLt( | ||
permutation[i], | ||
length, | ||
"Permutation value should be within range" | ||
); | ||
found[permutation[i]] = true; | ||
} | ||
for (uint256 i = 0; i < length; i++) { | ||
assertTrue(found[i], "Permutation should contain all values"); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// SPDX-License-Identifier: Apache 2 | ||
pragma solidity ^0.8.0; | ||
|
||
/// @title PRNG Contract | ||
/// @notice A contract for pseudorandom number generation and related utility functions | ||
/// @dev This PRNG contract is designed to work with Pyth Entropy as the seed source. | ||
/// Pyth Entropy provides secure, rapid random number generation for blockchain applications, | ||
/// enabling responsive UX for NFT mints, games, and other use cases requiring randomness. | ||
contract PRNG { | ||
bytes32 private seed; | ||
uint256 private nonce; | ||
|
||
/// @notice Initialize the PRNG with a seed | ||
/// @param _seed The Pyth Entropy seed (bytes32) | ||
constructor(bytes32 _seed) { | ||
seed = _seed; | ||
nonce = 0; | ||
} | ||
|
||
/// @notice Set a new seed and reset the nonce | ||
/// @param _newSeed The new seed (bytes32) | ||
function setSeed(bytes32 _newSeed) internal { | ||
seed = _newSeed; | ||
nonce = 0; | ||
} | ||
|
||
/// @notice Generate the next random bytes32 value and update the state | ||
/// @return The next random bytes32 value | ||
function nextBytes32() internal returns (bytes32) { | ||
bytes32 result = keccak256(abi.encode(seed, nonce)); | ||
nonce++; | ||
return result; | ||
} | ||
|
||
/// @notice Generate a random uint256 value | ||
/// @return A random uint256 value | ||
function randUint() internal returns (uint256) { | ||
return uint256(nextBytes32()); | ||
} | ||
|
||
/// @notice Generate a random uint64 value | ||
/// @return A random uint64 value | ||
function randUint64() internal returns (uint64) { | ||
return uint64(uint256(nextBytes32())); | ||
} | ||
|
||
/// @notice Generate a random uint256 value within a specified range | ||
/// @param min The minimum value (inclusive) | ||
/// @param max The maximum value (exclusive) | ||
/// @return A random uint256 value between min and max | ||
/// @dev The result is uniformly distributed between min and max, with a slight bias toward lower numbers. | ||
/// @dev This bias is insignificant as long as (max - min) << MAX_UINT256. | ||
function randUintRange( | ||
uint256 min, | ||
uint256 max | ||
) internal returns (uint256) { | ||
require(max > min, "Max must be greater than min"); | ||
return (randUint() % (max - min)) + min; | ||
} | ||
|
||
/// @notice Generate a random permutation of a sequence | ||
/// @param length The length of the sequence to permute | ||
/// @return A randomly permuted array of uint256 values | ||
function randomPermutation( | ||
uint256 length | ||
) internal returns (uint256[] memory) { | ||
uint256[] memory permutation = new uint256[](length); | ||
for (uint256 i = 0; i < length; i++) { | ||
permutation[i] = i; | ||
} | ||
for (uint256 i = 0; i < length; i++) { | ||
uint256 j = i + (randUint() % (length - i)); | ||
(permutation[i], permutation[j]) = (permutation[j], permutation[i]); | ||
} | ||
return permutation; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
target_chains/ethereum/entropy_sdk/solidity/abis/PRNG.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[ | ||
{ | ||
"inputs": [ | ||
{ | ||
"internalType": "bytes32", | ||
"name": "_seed", | ||
"type": "bytes32" | ||
} | ||
], | ||
"stateMutability": "nonpayable", | ||
"type": "constructor" | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters