[entropy] Add a default provider (#1150)
* making a mess here * add default provider * cleanup * test new abi * add uri to fortuna * pr comments
This commit is contained in:
parent
7641f7eacf
commit
a5646bf840
|
@ -1486,7 +1486,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fortuna"
|
||||
version = "1.1.0"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -55,6 +55,7 @@ pub async fn register_provider(opts: &RegisterProviderOptions) -> Result<()> {
|
|||
commitment,
|
||||
bincode::serialize(&commitment_metadata)?.into(),
|
||||
commitment_length,
|
||||
bincode::serialize(&opts.uri)?.into(),
|
||||
)
|
||||
.send()
|
||||
.await?
|
||||
|
|
|
@ -36,4 +36,10 @@ pub struct RegisterProviderOptions {
|
|||
#[arg(long = "pyth-contract-fee")]
|
||||
#[arg(default_value = "100")]
|
||||
pub fee: U256,
|
||||
|
||||
/// The URI where clients can retrieve random values from this provider,
|
||||
/// i.e., wherever fortuna for this provider will be hosted.
|
||||
#[arg(long = "uri")]
|
||||
#[arg(default_value = "")]
|
||||
pub uri: String,
|
||||
}
|
||||
|
|
|
@ -72,19 +72,12 @@ import "./EntropyState.sol";
|
|||
// protocol prior to the random number being revealed (i.e., prior to step (6) above). Integrators should ensure that
|
||||
// the user is always incentivized to reveal their random number, and that the protocol has an escape hatch for
|
||||
// cases where the user chooses not to reveal.
|
||||
//
|
||||
// TODOs:
|
||||
// - governance??
|
||||
// - correct method access modifiers (public vs external)
|
||||
// - gas optimizations
|
||||
// - 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 Entropy is IEntropy, EntropyState {
|
||||
// TODO: Use an upgradeable proxy
|
||||
constructor(uint pythFeeInWei) {
|
||||
constructor(uint pythFeeInWei, address defaultProvider) {
|
||||
_state.accruedPythFeesInWei = 0;
|
||||
_state.pythFeeInWei = pythFeeInWei;
|
||||
_state.defaultProvider = defaultProvider;
|
||||
}
|
||||
|
||||
// Register msg.sender as a randomness provider. The arguments are the provider's configuration parameters
|
||||
|
@ -96,7 +89,8 @@ contract Entropy is IEntropy, EntropyState {
|
|||
uint feeInWei,
|
||||
bytes32 commitment,
|
||||
bytes calldata commitmentMetadata,
|
||||
uint64 chainLength
|
||||
uint64 chainLength,
|
||||
bytes calldata uri
|
||||
) public override {
|
||||
if (chainLength == 0) revert EntropyErrors.AssertionFailure();
|
||||
|
||||
|
@ -117,6 +111,7 @@ contract Entropy is IEntropy, EntropyState {
|
|||
provider.currentCommitmentSequenceNumber = provider.sequenceNumber;
|
||||
provider.commitmentMetadata = commitmentMetadata;
|
||||
provider.endSequenceNumber = provider.sequenceNumber + chainLength;
|
||||
provider.uri = uri;
|
||||
|
||||
provider.sequenceNumber += 1;
|
||||
|
||||
|
@ -262,6 +257,15 @@ contract Entropy is IEntropy, EntropyState {
|
|||
info = _state.providers[provider];
|
||||
}
|
||||
|
||||
function getDefaultProvider()
|
||||
public
|
||||
view
|
||||
override
|
||||
returns (address provider)
|
||||
{
|
||||
provider = _state.defaultProvider;
|
||||
}
|
||||
|
||||
function getRequest(
|
||||
address provider,
|
||||
uint64 sequenceNumber
|
||||
|
|
|
@ -8,6 +8,7 @@ contract EntropyInternalStructs {
|
|||
struct State {
|
||||
uint pythFeeInWei;
|
||||
uint accruedPythFeesInWei;
|
||||
address defaultProvider;
|
||||
mapping(address => EntropyStructs.ProviderInfo) providers;
|
||||
mapping(bytes32 => EntropyStructs.Request) requests;
|
||||
}
|
||||
|
|
|
@ -19,10 +19,13 @@ contract EntropyTest is Test {
|
|||
bytes32[] provider1Proofs;
|
||||
uint provider1FeeInWei = 8;
|
||||
uint64 provider1ChainLength = 100;
|
||||
bytes provider1Uri = bytes("https://foo.com");
|
||||
bytes provider1CommitmentMetadata = hex"0100";
|
||||
|
||||
address public provider2 = address(2);
|
||||
bytes32[] provider2Proofs;
|
||||
uint provider2FeeInWei = 20;
|
||||
bytes provider2Uri = bytes("https://bar.com");
|
||||
|
||||
address public user1 = address(3);
|
||||
address public user2 = address(4);
|
||||
|
@ -32,7 +35,7 @@ contract EntropyTest is Test {
|
|||
bytes32 ALL_ZEROS = bytes32(uint256(0));
|
||||
|
||||
function setUp() public {
|
||||
random = new Entropy(pythFeeInWei);
|
||||
random = new Entropy(pythFeeInWei, provider1);
|
||||
|
||||
bytes32[] memory hashChain1 = generateHashChain(
|
||||
provider1,
|
||||
|
@ -44,14 +47,21 @@ contract EntropyTest is Test {
|
|||
random.register(
|
||||
provider1FeeInWei,
|
||||
provider1Proofs[0],
|
||||
hex"0100",
|
||||
provider1ChainLength
|
||||
provider1CommitmentMetadata,
|
||||
provider1ChainLength,
|
||||
provider1Uri
|
||||
);
|
||||
|
||||
bytes32[] memory hashChain2 = generateHashChain(provider2, 0, 100);
|
||||
provider2Proofs = hashChain2;
|
||||
vm.prank(provider2);
|
||||
random.register(provider2FeeInWei, provider2Proofs[0], hex"0200", 100);
|
||||
random.register(
|
||||
provider2FeeInWei,
|
||||
provider2Proofs[0],
|
||||
hex"0200",
|
||||
100,
|
||||
provider2Uri
|
||||
);
|
||||
}
|
||||
|
||||
function generateHashChain(
|
||||
|
@ -59,7 +69,9 @@ contract EntropyTest is Test {
|
|||
uint64 startSequenceNumber,
|
||||
uint64 size
|
||||
) public pure returns (bytes32[] memory hashChain) {
|
||||
bytes32 initialValue = keccak256(abi.encodePacked(startSequenceNumber));
|
||||
bytes32 initialValue = keccak256(
|
||||
abi.encodePacked(provider, startSequenceNumber)
|
||||
);
|
||||
hashChain = new bytes32[](size);
|
||||
for (uint64 i = 0; i < size; i++) {
|
||||
hashChain[size - (i + 1)] = initialValue;
|
||||
|
@ -212,6 +224,31 @@ contract EntropyTest is Test {
|
|||
);
|
||||
}
|
||||
|
||||
function testDefaultProvider() public {
|
||||
uint64 sequenceNumber = request(
|
||||
user2,
|
||||
random.getDefaultProvider(),
|
||||
42,
|
||||
false
|
||||
);
|
||||
assertEq(random.getRequest(provider1, sequenceNumber).blockNumber, 0);
|
||||
|
||||
assertRevealReverts(
|
||||
random.getDefaultProvider(),
|
||||
sequenceNumber,
|
||||
42,
|
||||
provider2Proofs[sequenceNumber]
|
||||
);
|
||||
|
||||
assertRevealSucceeds(
|
||||
random.getDefaultProvider(),
|
||||
sequenceNumber,
|
||||
42,
|
||||
provider1Proofs[sequenceNumber],
|
||||
ALL_ZEROS
|
||||
);
|
||||
}
|
||||
|
||||
function testNoSuchProvider() public {
|
||||
assertRequestReverts(10000000, unregisteredProvider, 42, false);
|
||||
}
|
||||
|
@ -318,7 +355,13 @@ contract EntropyTest is Test {
|
|||
10
|
||||
);
|
||||
vm.prank(provider1);
|
||||
random.register(provider1FeeInWei, newHashChain[0], hex"0100", 10);
|
||||
random.register(
|
||||
provider1FeeInWei,
|
||||
newHashChain[0],
|
||||
hex"0100",
|
||||
10,
|
||||
provider1Uri
|
||||
);
|
||||
assertInvariants();
|
||||
EntropyStructs.ProviderInfo memory info1 = random.getProviderInfo(
|
||||
provider1
|
||||
|
@ -387,7 +430,13 @@ contract EntropyTest is Test {
|
|||
|
||||
// Check that overflowing the fee arithmetic causes the transaction to revert.
|
||||
vm.prank(provider1);
|
||||
random.register(MAX_UINT256, provider1Proofs[0], hex"0100", 100);
|
||||
random.register(
|
||||
MAX_UINT256,
|
||||
provider1Proofs[0],
|
||||
hex"0100",
|
||||
100,
|
||||
provider1Uri
|
||||
);
|
||||
vm.expectRevert();
|
||||
random.getFee(provider1);
|
||||
}
|
||||
|
@ -437,7 +486,13 @@ contract EntropyTest is Test {
|
|||
|
||||
// Reregistering updates the required fees
|
||||
vm.prank(provider1);
|
||||
random.register(12345, provider1Proofs[0], hex"0100", 100);
|
||||
random.register(
|
||||
12345,
|
||||
provider1Proofs[0],
|
||||
hex"0100",
|
||||
100,
|
||||
provider1Uri
|
||||
);
|
||||
|
||||
assertRequestReverts(pythFeeInWei + 12345 - 1, provider1, 42, false);
|
||||
requestWithFee(user2, pythFeeInWei + 12345, provider1, 42, false);
|
||||
|
@ -466,4 +521,13 @@ contract EntropyTest is Test {
|
|||
vm.expectRevert();
|
||||
random.withdraw(providerOneBalance);
|
||||
}
|
||||
|
||||
function testGetProviderInfo() public {
|
||||
EntropyStructs.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.
|
||||
assertEq(providerInfo1.uri, provider1Uri);
|
||||
assertEq(providerInfo1.commitmentMetadata, provider1CommitmentMetadata);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,10 @@ contract EntropyStructs {
|
|||
// Metadata for the current commitment. Providers may optionally use this field to 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.
|
||||
|
|
|
@ -13,7 +13,8 @@ interface IEntropy is EntropyEvents {
|
|||
uint feeInWei,
|
||||
bytes32 commitment,
|
||||
bytes calldata commitmentMetadata,
|
||||
uint64 chainLength
|
||||
uint64 chainLength,
|
||||
bytes calldata uri
|
||||
) external;
|
||||
|
||||
// Withdraw a portion of the accumulated fees for the provider msg.sender.
|
||||
|
@ -55,6 +56,8 @@ interface IEntropy is EntropyEvents {
|
|||
address provider
|
||||
) external view returns (EntropyStructs.ProviderInfo memory info);
|
||||
|
||||
function getDefaultProvider() external view returns (address provider);
|
||||
|
||||
function getRequest(
|
||||
address provider,
|
||||
uint64 sequenceNumber
|
||||
|
|
Loading…
Reference in New Issue