Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[entropy] Change provider interface to simplify supporting many chains #1149

Merged
merged 4 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion fortuna/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion fortuna/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
[package]
name = "fortuna"
version = "1.0.0"
version = "1.1.0"
edition = "2021"

[dependencies]
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"
Expand Down
78 changes: 11 additions & 67 deletions fortuna/src/abi.json
Original file line number Diff line number Diff line change
@@ -1,45 +1,4 @@
[
{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why these got deleted? :?

"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": [
Expand All @@ -66,9 +25,9 @@
"type": "uint64"
},
{
"internalType": "bytes32",
"internalType": "bytes",
"name": "commitmentMetadata",
"type": "bytes32"
"type": "bytes"
},
{
"internalType": "uint64",
Expand All @@ -92,7 +51,7 @@
}
],
"indexed": false,
"internalType": "struct PythRandomStructs.ProviderInfo",
"internalType": "struct EntropyStructs.ProviderInfo",
"name": "provider",
"type": "tuple"
}
Expand Down Expand Up @@ -130,19 +89,14 @@
"name": "providerCommitmentSequenceNumber",
"type": "uint64"
},
{
"internalType": "bytes32",
"name": "providerCommitmentMetadata",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "blockNumber",
"type": "uint256"
}
],
"indexed": false,
"internalType": "struct PythRandomStructs.Request",
"internalType": "struct EntropyStructs.Request",
"name": "request",
"type": "tuple"
}
Expand Down Expand Up @@ -180,19 +134,14 @@
"name": "providerCommitmentSequenceNumber",
"type": "uint64"
},
{
"internalType": "bytes32",
"name": "providerCommitmentMetadata",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "blockNumber",
"type": "uint256"
}
],
"indexed": false,
"internalType": "struct PythRandomStructs.Request",
"internalType": "struct EntropyStructs.Request",
"name": "request",
"type": "tuple"
},
Expand Down Expand Up @@ -337,9 +286,9 @@
"type": "uint64"
},
{
"internalType": "bytes32",
"internalType": "bytes",
"name": "commitmentMetadata",
"type": "bytes32"
"type": "bytes"
},
{
"internalType": "uint64",
Expand All @@ -362,7 +311,7 @@
"type": "uint64"
}
],
"internalType": "struct PythRandomStructs.ProviderInfo",
"internalType": "struct EntropyStructs.ProviderInfo",
"name": "info",
"type": "tuple"
}
Expand Down Expand Up @@ -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"
}
Expand All @@ -444,9 +388,9 @@
"type": "bytes32"
},
{
"internalType": "bytes32",
"internalType": "bytes",
"name": "commitmentMetadata",
"type": "bytes32"
"type": "bytes"
},
{
"internalType": "uint64",
Expand Down
26 changes: 20 additions & 6 deletions fortuna/src/command/register_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<()> {
Expand All @@ -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()
Expand Down
12 changes: 10 additions & 2 deletions fortuna/src/command/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use {
crate::{
api,
chain::ethereum::PythContract,
command::register_provider::CommitmentMetadata,
config::{
Config,
RunOptions,
Expand Down Expand Up @@ -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
Expand Down
16 changes: 7 additions & 9 deletions fortuna/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
use {
crate::{
api::ChainId,
config::RandomnessOptions,
},
crate::api::ChainId,
anyhow::{
ensure,
Result,
Expand Down Expand Up @@ -36,17 +33,18 @@ impl PebbleHashChain {
}

pub fn from_config(
opts: &RandomnessOptions,
secret: &str,
chain_id: &ChainId,
random: [u8; 32],
random: &[u8; 32],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think seed is a better name here.

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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -186,7 +186,6 @@ contract Entropy is IEntropy, EntropyState {
req.providerCommitment = providerInfo.currentCommitment;
req.providerCommitmentSequenceNumber = providerInfo
.currentCommitmentSequenceNumber;
req.providerCommitmentMetadata = providerInfo.commitmentMetadata;
Copy link
Contributor Author

@jayantk jayantk Nov 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drive by: I deleted this field from req (it's not needed)


if (useBlockHash) {
req.blockNumber = block.number;
Expand Down
30 changes: 5 additions & 25 deletions target_chains/ethereum/contracts/forge-test/Entropy.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,14 @@ contract EntropyTest is Test {
random.register(
provider1FeeInWei,
provider1Proofs[0],
bytes32(keccak256(abi.encodePacked(uint256(0x0100)))),
hex"0100",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drive-by: shorter way to write this

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(
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down
Loading
Loading