Skip to content

Commit

Permalink
feat(fortuna): support multiple hashchains (#1509)
Browse files Browse the repository at this point in the history
* introduce provider config

* get provider chain config in order

* hash chain with multiple pebble chains

* update script to get metadata

* update version

* comments and move things around

* update comment

* minor fixes

* separate pr for this

* rename provider-config

* sample config

* auto sort commitments

* use seed and chain length

* refactor and simplify hashchain and offset vec

* better formatting

* make commitments private

* optional chain in provider-config

* set default value of chain length

* Version 5.0.0

* update comments

* version update

* optional provider config
  • Loading branch information
Dev Kalra authored Apr 26, 2024
1 parent 37ee3b4 commit cf90bff
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 24 deletions.
2 changes: 1 addition & 1 deletion apps/fortuna/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/target
config.yaml
*config.yaml
*secret*
*private-key*
2 changes: 1 addition & 1 deletion apps/fortuna/Cargo.lock

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

4 changes: 2 additions & 2 deletions apps/fortuna/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "fortuna"
version = "4.0.1"
version = "5.0.0"
edition = "2021"

[dependencies]
Expand All @@ -14,7 +14,7 @@ clap = { version = "4.4.6", features = ["derive", "cargo", "env"] }
ethabi = "18.0.0"
ethers = { version = "2.0.14", features = ["ws"] }
futures = { version = "0.3.28" }
hex = "0.4.3"
hex = "0.4.3"
prometheus-client = { version = "0.21.2" }
pythnet-sdk = { path = "../../pythnet/pythnet_sdk", features = ["strum"] }
rand = "0.8.5"
Expand Down
7 changes: 7 additions & 0 deletions apps/fortuna/provider-config.sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
chains:
lightlink-pegasus:
commitments:
# prettier-ignore
- seed: [219,125,217,197,234,88,208,120,21,181,172,143,239,102,41,233,167,212,237,106,37,255,184,165,238,121,230,155,116,158,173,48]
chain_length: 10000
original_commitment_sequence_number: 104
63 changes: 44 additions & 19 deletions apps/fortuna/src/command/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ use {
chain::ethereum::PythContract,
command::register_provider::CommitmentMetadata,
config::{
Commitment,
Config,
ProviderConfig,
RunOptions,
},
keeper,
Expand All @@ -27,7 +29,6 @@ use {
collections::HashMap,
net::SocketAddr,
sync::Arc,
vec,
},
tokio::{
spawn,
Expand Down Expand Up @@ -121,38 +122,62 @@ pub async fn run_keeper(

pub async fn run(opts: &RunOptions) -> Result<()> {
let config = Config::load(&opts.config.config)?;
let provider_config = opts
.provider_config
.provider_config
.as_ref()
.map(|path| ProviderConfig::load(&path).expect("Failed to load provider config"));
let private_key = opts.load_private_key()?;
let secret = opts.randomness.load_secret()?;
let (tx_exit, rx_exit) = watch::channel(false);

let mut chains: HashMap<ChainId, BlockchainState> = HashMap::new();
for (chain_id, chain_config) in &config.chains {
let contract = Arc::new(PythContract::from_config(&chain_config)?);
let provider_chain_config = provider_config
.as_ref()
.and_then(|c| c.get_chain_config(chain_id));
let mut provider_commitments = provider_chain_config
.as_ref()
.map(|c| c.get_sorted_commitments())
.unwrap_or_else(|| Vec::new());
println!("{} {:?}", chain_id, provider_commitments);

let provider_info = contract.get_provider_info(opts.provider).call().await?;
let latest_metadata =
bincode::deserialize::<CommitmentMetadata>(&provider_info.commitment_metadata)?;

provider_commitments.push(Commitment {
seed: latest_metadata.seed,
chain_length: latest_metadata.chain_length,
original_commitment_sequence_number: provider_info.original_commitment_sequence_number,
});

// Reconstruct the hash chain based on the metadata and check that it matches the on-chain commitment.
// TODO: we should instantiate the state here with multiple hash chains.
// This approach works fine as long as we haven't rotated the commitment (i.e., all user requests
// are for the most recent chain).
// 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 metadata =
bincode::deserialize::<CommitmentMetadata>(&provider_info.commitment_metadata)?;

let hash_chain = PebbleHashChain::from_config(
&secret,
&chain_id,
&opts.provider,
&chain_config.contract_addr,
&metadata.seed,
metadata.chain_length,
)?;
let mut offsets = Vec::<usize>::new();
let mut hash_chains = Vec::<PebbleHashChain>::new();

for commitment in &provider_commitments {
let offset = commitment.original_commitment_sequence_number.try_into()?;
offsets.push(offset);

let pebble_hash_chain = PebbleHashChain::from_config(
&secret,
&chain_id,
&opts.provider,
&chain_config.contract_addr,
&commitment.seed,
commitment.chain_length,
)?;
hash_chains.push(pebble_hash_chain);
}

let chain_state = HashChainState {
offsets: vec![provider_info
.original_commitment_sequence_number
.try_into()?],
hash_chains: vec![hash_chain],
offsets,
hash_chains,
};

if chain_state.reveal(provider_info.original_commitment_sequence_number)?
Expand Down
56 changes: 55 additions & 1 deletion apps/fortuna/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ pub struct RandomnessOptions {
/// The length of the hash chain to generate.
#[arg(long = "chain-length")]
#[arg(env = "FORTUNA_CHAIN_LENGTH")]
#[arg(default_value = "10000")]
#[arg(default_value = "100000")]
pub chain_length: u64,
}

Expand Down Expand Up @@ -158,3 +158,57 @@ pub struct EthereumConfig {
/// The gas limit to use for entropy callback transactions.
pub gas_limit: U256,
}

#[derive(Args, Clone, Debug)]
#[command(next_help_heading = "Provider Config Options")]
#[group(id = "ProviderConfig")]
pub struct ProviderConfigOptions {
#[arg(long = "provider-config")]
#[arg(env = "FORTUNA_PROVIDER_CONFIG")]
pub provider_config: Option<String>,
}

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct ProviderConfig {
pub chains: HashMap<ChainId, ProviderChainConfig>,
}

impl ProviderConfig {
pub fn load(path: &str) -> Result<ProviderConfig> {
// Open and read the YAML file
let yaml_content = fs::read_to_string(path)?;
let config: ProviderConfig = serde_yaml::from_str(&yaml_content)?;
Ok(config)
}

/// Get the provider chain config. The method returns an Option for ProviderChainConfig.
/// We may not have past any commitments for a chain. For example, for a new chain
pub fn get_chain_config(&self, chain_id: &ChainId) -> Option<ProviderChainConfig> {
self.chains.get(chain_id).map(|x| x.clone())
}
}

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct ProviderChainConfig {
commitments: Vec<Commitment>,
}

impl ProviderChainConfig {
/// Returns a clone of the commitments in the sorted order.
/// `HashChainState` requires offsets to be in order.
pub fn get_sorted_commitments(&self) -> Vec<Commitment> {
let mut sorted_commitments = self.commitments.clone();
sorted_commitments.sort_by(|c1, c2| {
c1.original_commitment_sequence_number
.cmp(&c2.original_commitment_sequence_number)
});
sorted_commitments
}
}

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct Commitment {
pub seed: [u8; 32],
pub chain_length: u64,
pub original_commitment_sequence_number: u64,
}
4 changes: 4 additions & 0 deletions apps/fortuna/src/config/run.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use {
crate::config::{
ConfigOptions,
ProviderConfigOptions,
RandomnessOptions,
},
anyhow::Result,
Expand All @@ -18,6 +19,9 @@ pub struct RunOptions {
#[command(flatten)]
pub config: ConfigOptions,

#[command(flatten)]
pub provider_config: ProviderConfigOptions,

#[command(flatten)]
pub randomness: RandomnessOptions,

Expand Down

0 comments on commit cf90bff

Please sign in to comment.