Skip to content

Commit cf90bff

Browse files
author
Dev Kalra
authored
feat(fortuna): support multiple hashchains (#1509)
* 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
1 parent 37ee3b4 commit cf90bff

File tree

7 files changed

+114
-24
lines changed

7 files changed

+114
-24
lines changed

apps/fortuna/.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
/target
2-
config.yaml
2+
*config.yaml
33
*secret*
44
*private-key*

apps/fortuna/Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/fortuna/Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "fortuna"
3-
version = "4.0.1"
3+
version = "5.0.0"
44
edition = "2021"
55

66
[dependencies]
@@ -14,7 +14,7 @@ clap = { version = "4.4.6", features = ["derive", "cargo", "env"] }
1414
ethabi = "18.0.0"
1515
ethers = { version = "2.0.14", features = ["ws"] }
1616
futures = { version = "0.3.28" }
17-
hex = "0.4.3"
17+
hex = "0.4.3"
1818
prometheus-client = { version = "0.21.2" }
1919
pythnet-sdk = { path = "../../pythnet/pythnet_sdk", features = ["strum"] }
2020
rand = "0.8.5"
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
chains:
2+
lightlink-pegasus:
3+
commitments:
4+
# prettier-ignore
5+
- 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]
6+
chain_length: 10000
7+
original_commitment_sequence_number: 104

apps/fortuna/src/command/run.rs

+44-19
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ use {
88
chain::ethereum::PythContract,
99
command::register_provider::CommitmentMetadata,
1010
config::{
11+
Commitment,
1112
Config,
13+
ProviderConfig,
1214
RunOptions,
1315
},
1416
keeper,
@@ -27,7 +29,6 @@ use {
2729
collections::HashMap,
2830
net::SocketAddr,
2931
sync::Arc,
30-
vec,
3132
},
3233
tokio::{
3334
spawn,
@@ -121,38 +122,62 @@ pub async fn run_keeper(
121122

122123
pub async fn run(opts: &RunOptions) -> Result<()> {
123124
let config = Config::load(&opts.config.config)?;
125+
let provider_config = opts
126+
.provider_config
127+
.provider_config
128+
.as_ref()
129+
.map(|path| ProviderConfig::load(&path).expect("Failed to load provider config"));
124130
let private_key = opts.load_private_key()?;
125131
let secret = opts.randomness.load_secret()?;
126132
let (tx_exit, rx_exit) = watch::channel(false);
127133

128134
let mut chains: HashMap<ChainId, BlockchainState> = HashMap::new();
129135
for (chain_id, chain_config) in &config.chains {
130136
let contract = Arc::new(PythContract::from_config(&chain_config)?);
137+
let provider_chain_config = provider_config
138+
.as_ref()
139+
.and_then(|c| c.get_chain_config(chain_id));
140+
let mut provider_commitments = provider_chain_config
141+
.as_ref()
142+
.map(|c| c.get_sorted_commitments())
143+
.unwrap_or_else(|| Vec::new());
144+
println!("{} {:?}", chain_id, provider_commitments);
145+
131146
let provider_info = contract.get_provider_info(opts.provider).call().await?;
147+
let latest_metadata =
148+
bincode::deserialize::<CommitmentMetadata>(&provider_info.commitment_metadata)?;
149+
150+
provider_commitments.push(Commitment {
151+
seed: latest_metadata.seed,
152+
chain_length: latest_metadata.chain_length,
153+
original_commitment_sequence_number: provider_info.original_commitment_sequence_number,
154+
});
132155

133-
// Reconstruct the hash chain based on the metadata and check that it matches the on-chain commitment.
134-
// TODO: we should instantiate the state here with multiple hash chains.
135-
// This approach works fine as long as we haven't rotated the commitment (i.e., all user requests
136-
// are for the most recent chain).
137156
// TODO: we may want to load the hash chain in a lazy/fault-tolerant way. If there are many blockchains,
138157
// then it's more likely that some RPC fails. We should tolerate these faults and generate the hash chain
139158
// later when a user request comes in for that chain.
140-
let metadata =
141-
bincode::deserialize::<CommitmentMetadata>(&provider_info.commitment_metadata)?;
142159

143-
let hash_chain = PebbleHashChain::from_config(
144-
&secret,
145-
&chain_id,
146-
&opts.provider,
147-
&chain_config.contract_addr,
148-
&metadata.seed,
149-
metadata.chain_length,
150-
)?;
160+
let mut offsets = Vec::<usize>::new();
161+
let mut hash_chains = Vec::<PebbleHashChain>::new();
162+
163+
for commitment in &provider_commitments {
164+
let offset = commitment.original_commitment_sequence_number.try_into()?;
165+
offsets.push(offset);
166+
167+
let pebble_hash_chain = PebbleHashChain::from_config(
168+
&secret,
169+
&chain_id,
170+
&opts.provider,
171+
&chain_config.contract_addr,
172+
&commitment.seed,
173+
commitment.chain_length,
174+
)?;
175+
hash_chains.push(pebble_hash_chain);
176+
}
177+
151178
let chain_state = HashChainState {
152-
offsets: vec![provider_info
153-
.original_commitment_sequence_number
154-
.try_into()?],
155-
hash_chains: vec![hash_chain],
179+
offsets,
180+
hash_chains,
156181
};
157182

158183
if chain_state.reveal(provider_info.original_commitment_sequence_number)?

apps/fortuna/src/config.rs

+55-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ pub struct RandomnessOptions {
9797
/// The length of the hash chain to generate.
9898
#[arg(long = "chain-length")]
9999
#[arg(env = "FORTUNA_CHAIN_LENGTH")]
100-
#[arg(default_value = "10000")]
100+
#[arg(default_value = "100000")]
101101
pub chain_length: u64,
102102
}
103103

@@ -158,3 +158,57 @@ pub struct EthereumConfig {
158158
/// The gas limit to use for entropy callback transactions.
159159
pub gas_limit: U256,
160160
}
161+
162+
#[derive(Args, Clone, Debug)]
163+
#[command(next_help_heading = "Provider Config Options")]
164+
#[group(id = "ProviderConfig")]
165+
pub struct ProviderConfigOptions {
166+
#[arg(long = "provider-config")]
167+
#[arg(env = "FORTUNA_PROVIDER_CONFIG")]
168+
pub provider_config: Option<String>,
169+
}
170+
171+
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
172+
pub struct ProviderConfig {
173+
pub chains: HashMap<ChainId, ProviderChainConfig>,
174+
}
175+
176+
impl ProviderConfig {
177+
pub fn load(path: &str) -> Result<ProviderConfig> {
178+
// Open and read the YAML file
179+
let yaml_content = fs::read_to_string(path)?;
180+
let config: ProviderConfig = serde_yaml::from_str(&yaml_content)?;
181+
Ok(config)
182+
}
183+
184+
/// Get the provider chain config. The method returns an Option for ProviderChainConfig.
185+
/// We may not have past any commitments for a chain. For example, for a new chain
186+
pub fn get_chain_config(&self, chain_id: &ChainId) -> Option<ProviderChainConfig> {
187+
self.chains.get(chain_id).map(|x| x.clone())
188+
}
189+
}
190+
191+
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
192+
pub struct ProviderChainConfig {
193+
commitments: Vec<Commitment>,
194+
}
195+
196+
impl ProviderChainConfig {
197+
/// Returns a clone of the commitments in the sorted order.
198+
/// `HashChainState` requires offsets to be in order.
199+
pub fn get_sorted_commitments(&self) -> Vec<Commitment> {
200+
let mut sorted_commitments = self.commitments.clone();
201+
sorted_commitments.sort_by(|c1, c2| {
202+
c1.original_commitment_sequence_number
203+
.cmp(&c2.original_commitment_sequence_number)
204+
});
205+
sorted_commitments
206+
}
207+
}
208+
209+
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
210+
pub struct Commitment {
211+
pub seed: [u8; 32],
212+
pub chain_length: u64,
213+
pub original_commitment_sequence_number: u64,
214+
}

apps/fortuna/src/config/run.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use {
22
crate::config::{
33
ConfigOptions,
4+
ProviderConfigOptions,
45
RandomnessOptions,
56
},
67
anyhow::Result,
@@ -18,6 +19,9 @@ pub struct RunOptions {
1819
#[command(flatten)]
1920
pub config: ConfigOptions,
2021

22+
#[command(flatten)]
23+
pub provider_config: ProviderConfigOptions,
24+
2125
#[command(flatten)]
2226
pub randomness: RandomnessOptions,
2327

0 commit comments

Comments
 (0)