[entropy] Change provider interface to simplify supporting many chains (#1149)
* grr * cleanup * fix semver
This commit is contained in:
parent
da72b6e250
commit
b7a089b51e
|
@ -1486,13 +1486,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fortuna"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
"axum-macros",
|
||||
"axum-test",
|
||||
"base64 0.21.4",
|
||||
"bincode",
|
||||
"byteorder",
|
||||
"clap",
|
||||
"ethabi",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "fortuna"
|
||||
version = "1.0.0"
|
||||
version = "2.0.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
@ -8,6 +8,7 @@ anyhow = "1.0.75"
|
|||
axum = { version = "0.6.20", features = ["json", "ws", "macros"] }
|
||||
axum-macros = { version = "0.3.8" }
|
||||
base64 = { version = "0.21.0" }
|
||||
bincode = "1.3.3"
|
||||
byteorder = "1.5.0"
|
||||
clap = { version = "4.4.6", features = ["derive", "cargo", "env"] }
|
||||
ethabi = "18.0.0"
|
||||
|
|
|
@ -1,45 +1,4 @@
|
|||
[
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "pythFeeInWei",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "constructor"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "AssertionFailure",
|
||||
"type": "error"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "IncorrectProviderRevelation",
|
||||
"type": "error"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "IncorrectUserRevelation",
|
||||
"type": "error"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "InsufficientFee",
|
||||
"type": "error"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "NoSuchProvider",
|
||||
"type": "error"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "OutOfRandomness",
|
||||
"type": "error"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
|
@ -66,9 +25,9 @@
|
|||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"internalType": "bytes",
|
||||
"name": "commitmentMetadata",
|
||||
"type": "bytes32"
|
||||
"type": "bytes"
|
||||
},
|
||||
{
|
||||
"internalType": "uint64",
|
||||
|
@ -92,7 +51,7 @@
|
|||
}
|
||||
],
|
||||
"indexed": false,
|
||||
"internalType": "struct PythRandomStructs.ProviderInfo",
|
||||
"internalType": "struct EntropyStructs.ProviderInfo",
|
||||
"name": "provider",
|
||||
"type": "tuple"
|
||||
}
|
||||
|
@ -130,11 +89,6 @@
|
|||
"name": "providerCommitmentSequenceNumber",
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "providerCommitmentMetadata",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "blockNumber",
|
||||
|
@ -142,7 +96,7 @@
|
|||
}
|
||||
],
|
||||
"indexed": false,
|
||||
"internalType": "struct PythRandomStructs.Request",
|
||||
"internalType": "struct EntropyStructs.Request",
|
||||
"name": "request",
|
||||
"type": "tuple"
|
||||
}
|
||||
|
@ -180,11 +134,6 @@
|
|||
"name": "providerCommitmentSequenceNumber",
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "providerCommitmentMetadata",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "blockNumber",
|
||||
|
@ -192,7 +141,7 @@
|
|||
}
|
||||
],
|
||||
"indexed": false,
|
||||
"internalType": "struct PythRandomStructs.Request",
|
||||
"internalType": "struct EntropyStructs.Request",
|
||||
"name": "request",
|
||||
"type": "tuple"
|
||||
},
|
||||
|
@ -337,9 +286,9 @@
|
|||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"internalType": "bytes",
|
||||
"name": "commitmentMetadata",
|
||||
"type": "bytes32"
|
||||
"type": "bytes"
|
||||
},
|
||||
{
|
||||
"internalType": "uint64",
|
||||
|
@ -362,7 +311,7 @@
|
|||
"type": "uint64"
|
||||
}
|
||||
],
|
||||
"internalType": "struct PythRandomStructs.ProviderInfo",
|
||||
"internalType": "struct EntropyStructs.ProviderInfo",
|
||||
"name": "info",
|
||||
"type": "tuple"
|
||||
}
|
||||
|
@ -412,18 +361,13 @@
|
|||
"name": "providerCommitmentSequenceNumber",
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "providerCommitmentMetadata",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "blockNumber",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"internalType": "struct PythRandomStructs.Request",
|
||||
"internalType": "struct EntropyStructs.Request",
|
||||
"name": "req",
|
||||
"type": "tuple"
|
||||
}
|
||||
|
@ -444,9 +388,9 @@
|
|||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"internalType": "bytes",
|
||||
"name": "commitmentMetadata",
|
||||
"type": "bytes32"
|
||||
"type": "bytes"
|
||||
},
|
||||
{
|
||||
"internalType": "uint64",
|
||||
|
|
|
@ -11,6 +11,12 @@ use {
|
|||
std::sync::Arc,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CommitmentMetadata {
|
||||
pub seed: [u8; 32],
|
||||
pub chain_length: u64,
|
||||
}
|
||||
|
||||
/// Register as a randomness provider. This method will generate and commit to a new random
|
||||
/// hash chain from the configured secret & a newly generated random value.
|
||||
pub async fn register_provider(opts: &RegisterProviderOptions) -> Result<()> {
|
||||
|
@ -25,21 +31,29 @@ pub async fn register_provider(opts: &RegisterProviderOptions) -> Result<()> {
|
|||
|
||||
// Create a new random hash chain.
|
||||
let random = rand::random::<[u8; 32]>();
|
||||
let mut chain = PebbleHashChain::from_config(&opts.randomness, &opts.chain_id, random)?;
|
||||
let commitment_length = opts.randomness.chain_length;
|
||||
let mut chain = PebbleHashChain::from_config(
|
||||
&opts.randomness.secret,
|
||||
&opts.chain_id,
|
||||
&random,
|
||||
commitment_length,
|
||||
)?;
|
||||
|
||||
// Arguments to the contract to register our new provider.
|
||||
let fee_in_wei = opts.fee;
|
||||
let commitment = chain.reveal()?;
|
||||
// Store the random seed in the metadata field so that we can regenerate the hash chain
|
||||
// at-will. (This is secure because you can't generate the chain unless you also have the secret)
|
||||
let commitment_metadata = random;
|
||||
let commitment_length = opts.randomness.chain_length;
|
||||
// Store the random seed and chain length in the metadata field so that we can regenerate the hash
|
||||
// chain at-will. (This is secure because you can't generate the chain unless you also have the secret)
|
||||
let commitment_metadata = CommitmentMetadata {
|
||||
seed: random,
|
||||
chain_length: commitment_length,
|
||||
};
|
||||
|
||||
if let Some(r) = contract
|
||||
.register(
|
||||
fee_in_wei,
|
||||
commitment,
|
||||
commitment_metadata,
|
||||
bincode::serialize(&commitment_metadata)?.into(),
|
||||
commitment_length,
|
||||
)
|
||||
.send()
|
||||
|
|
|
@ -2,6 +2,7 @@ use {
|
|||
crate::{
|
||||
api,
|
||||
chain::ethereum::PythContract,
|
||||
command::register_provider::CommitmentMetadata,
|
||||
config::{
|
||||
Config,
|
||||
RunOptions,
|
||||
|
@ -59,8 +60,15 @@ pub async fn run(opts: &RunOptions) -> Result<()> {
|
|||
// TODO: we may want to load the hash chain in a lazy/fault-tolerant way. If there are many blockchains,
|
||||
// then it's more likely that some RPC fails. We should tolerate these faults and generate the hash chain
|
||||
// later when a user request comes in for that chain.
|
||||
let random: [u8; 32] = provider_info.commitment_metadata;
|
||||
let hash_chain = PebbleHashChain::from_config(&opts.randomness, &chain_id, random)?;
|
||||
let metadata =
|
||||
bincode::deserialize::<CommitmentMetadata>(&provider_info.commitment_metadata)?;
|
||||
|
||||
let hash_chain = PebbleHashChain::from_config(
|
||||
&opts.randomness.secret,
|
||||
&chain_id,
|
||||
&metadata.seed,
|
||||
metadata.chain_length,
|
||||
)?;
|
||||
let chain_state = HashChainState {
|
||||
offsets: vec![provider_info
|
||||
.original_commitment_sequence_number
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
use {
|
||||
crate::{
|
||||
api::ChainId,
|
||||
config::RandomnessOptions,
|
||||
},
|
||||
crate::api::ChainId,
|
||||
anyhow::{
|
||||
ensure,
|
||||
Result,
|
||||
|
@ -36,17 +33,18 @@ impl PebbleHashChain {
|
|||
}
|
||||
|
||||
pub fn from_config(
|
||||
opts: &RandomnessOptions,
|
||||
secret: &str,
|
||||
chain_id: &ChainId,
|
||||
random: [u8; 32],
|
||||
random: &[u8; 32],
|
||||
chain_length: u64,
|
||||
) -> Result<Self> {
|
||||
let mut input: Vec<u8> = vec![];
|
||||
input.extend_from_slice(&hex::decode(opts.secret.clone())?);
|
||||
input.extend_from_slice(&hex::decode(secret)?);
|
||||
input.extend_from_slice(&chain_id.as_bytes());
|
||||
input.extend_from_slice(&random);
|
||||
input.extend_from_slice(random);
|
||||
|
||||
let secret: [u8; 32] = Keccak256::digest(input).into();
|
||||
Ok(Self::new(secret, opts.chain_length.try_into()?))
|
||||
Ok(Self::new(secret, chain_length.try_into()?))
|
||||
}
|
||||
|
||||
/// Reveal the next hash in the chain using the previous proof.
|
||||
|
|
|
@ -95,7 +95,7 @@ contract Entropy is IEntropy, EntropyState {
|
|||
function register(
|
||||
uint feeInWei,
|
||||
bytes32 commitment,
|
||||
bytes32 commitmentMetadata,
|
||||
bytes calldata commitmentMetadata,
|
||||
uint64 chainLength
|
||||
) public override {
|
||||
if (chainLength == 0) revert EntropyErrors.AssertionFailure();
|
||||
|
@ -186,7 +186,6 @@ contract Entropy is IEntropy, EntropyState {
|
|||
req.providerCommitment = providerInfo.currentCommitment;
|
||||
req.providerCommitmentSequenceNumber = providerInfo
|
||||
.currentCommitmentSequenceNumber;
|
||||
req.providerCommitmentMetadata = providerInfo.commitmentMetadata;
|
||||
|
||||
if (useBlockHash) {
|
||||
req.blockNumber = block.number;
|
||||
|
|
|
@ -44,19 +44,14 @@ contract EntropyTest is Test {
|
|||
random.register(
|
||||
provider1FeeInWei,
|
||||
provider1Proofs[0],
|
||||
bytes32(keccak256(abi.encodePacked(uint256(0x0100)))),
|
||||
hex"0100",
|
||||
provider1ChainLength
|
||||
);
|
||||
|
||||
bytes32[] memory hashChain2 = generateHashChain(provider2, 0, 100);
|
||||
provider2Proofs = hashChain2;
|
||||
vm.prank(provider2);
|
||||
random.register(
|
||||
provider2FeeInWei,
|
||||
provider2Proofs[0],
|
||||
bytes32(keccak256(abi.encodePacked(uint256(0x0200)))),
|
||||
100
|
||||
);
|
||||
random.register(provider2FeeInWei, provider2Proofs[0], hex"0200", 100);
|
||||
}
|
||||
|
||||
function generateHashChain(
|
||||
|
@ -323,12 +318,7 @@ contract EntropyTest is Test {
|
|||
10
|
||||
);
|
||||
vm.prank(provider1);
|
||||
random.register(
|
||||
provider1FeeInWei,
|
||||
newHashChain[0],
|
||||
bytes32(keccak256(abi.encodePacked(uint256(0x0100)))),
|
||||
10
|
||||
);
|
||||
random.register(provider1FeeInWei, newHashChain[0], hex"0100", 10);
|
||||
assertInvariants();
|
||||
EntropyStructs.ProviderInfo memory info1 = random.getProviderInfo(
|
||||
provider1
|
||||
|
@ -397,12 +387,7 @@ contract EntropyTest is Test {
|
|||
|
||||
// Check that overflowing the fee arithmetic causes the transaction to revert.
|
||||
vm.prank(provider1);
|
||||
random.register(
|
||||
MAX_UINT256,
|
||||
provider1Proofs[0],
|
||||
bytes32(keccak256(abi.encodePacked(uint256(0x0100)))),
|
||||
100
|
||||
);
|
||||
random.register(MAX_UINT256, provider1Proofs[0], hex"0100", 100);
|
||||
vm.expectRevert();
|
||||
random.getFee(provider1);
|
||||
}
|
||||
|
@ -452,12 +437,7 @@ contract EntropyTest is Test {
|
|||
|
||||
// Reregistering updates the required fees
|
||||
vm.prank(provider1);
|
||||
random.register(
|
||||
12345,
|
||||
provider1Proofs[0],
|
||||
bytes32(keccak256(abi.encodePacked(uint256(0x0100)))),
|
||||
100
|
||||
);
|
||||
random.register(12345, provider1Proofs[0], hex"0100", 100);
|
||||
|
||||
assertRequestReverts(pythFeeInWei + 12345 - 1, provider1, 42, false);
|
||||
requestWithFee(user2, pythFeeInWei + 12345, provider1, 42, false);
|
||||
|
|
|
@ -20,7 +20,7 @@ contract EntropyStructs {
|
|||
uint64 originalCommitmentSequenceNumber;
|
||||
// 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).
|
||||
bytes32 commitmentMetadata;
|
||||
bytes commitmentMetadata;
|
||||
// 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.
|
||||
|
@ -43,7 +43,6 @@ contract EntropyStructs {
|
|||
bytes32 userCommitment;
|
||||
bytes32 providerCommitment;
|
||||
uint64 providerCommitmentSequenceNumber;
|
||||
bytes32 providerCommitmentMetadata;
|
||||
// If nonzero, the randomness requester wants the blockhash of this block to be incorporated into the random number.
|
||||
uint256 blockNumber;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ interface IEntropy is EntropyEvents {
|
|||
function register(
|
||||
uint feeInWei,
|
||||
bytes32 commitment,
|
||||
bytes32 commitmentMetadata,
|
||||
bytes calldata commitmentMetadata,
|
||||
uint64 chainLength
|
||||
) external;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@pythnetwork/entropy-sdk-solidity",
|
||||
"version": "0.1.1",
|
||||
"version": "1.0.0",
|
||||
"description": "Generate secure random numbers with Pyth Entropy",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
Loading…
Reference in New Issue