From 678277079298a9f1bad49842b00d6fa179a41815 Mon Sep 17 00:00:00 2001 From: Dev Kalra Date: Thu, 23 May 2024 12:58:46 +0530 Subject: [PATCH] feat(fortuna): set provider fee per chain (#1546) * set provider fee from provider config * optional committments * Refactor setup provider --------- Co-authored-by: Amin Moghaddam --- apps/fortuna/Cargo.lock | 2 +- apps/fortuna/Cargo.toml | 2 +- apps/fortuna/provider-config.sample.yaml | 1 + apps/fortuna/src/command/run.rs | 16 +- apps/fortuna/src/command/setup_provider.rs | 225 ++++++++++++--------- apps/fortuna/src/config.rs | 21 +- apps/fortuna/src/config/setup_provider.rs | 9 +- 7 files changed, 158 insertions(+), 118 deletions(-) diff --git a/apps/fortuna/Cargo.lock b/apps/fortuna/Cargo.lock index 73d5ac0b5..324b7ff96 100644 --- a/apps/fortuna/Cargo.lock +++ b/apps/fortuna/Cargo.lock @@ -1488,7 +1488,7 @@ dependencies = [ [[package]] name = "fortuna" -version = "5.3.3" +version = "5.4.0" dependencies = [ "anyhow", "axum", diff --git a/apps/fortuna/Cargo.toml b/apps/fortuna/Cargo.toml index 015b17afe..0d85b8e36 100644 --- a/apps/fortuna/Cargo.toml +++ b/apps/fortuna/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fortuna" -version = "5.3.3" +version = "5.4.0" edition = "2021" [dependencies] diff --git a/apps/fortuna/provider-config.sample.yaml b/apps/fortuna/provider-config.sample.yaml index d690801de..a7e6c79d2 100644 --- a/apps/fortuna/provider-config.sample.yaml +++ b/apps/fortuna/provider-config.sample.yaml @@ -5,3 +5,4 @@ chains: - 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 + fee: 1500000000000000 diff --git a/apps/fortuna/src/command/run.rs b/apps/fortuna/src/command/run.rs index 2f57228d8..5ce393678 100644 --- a/apps/fortuna/src/command/run.rs +++ b/apps/fortuna/src/command/run.rs @@ -149,25 +149,15 @@ 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 provider_config = ProviderConfig::load(&opts.provider_config.provider_config)?; let secret = opts.randomness.load_secret()?; let (tx_exit, rx_exit) = watch::channel(false); let mut chains: HashMap = 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()); - + let provider_chain_config = provider_config.get_chain_config(chain_id)?; + let mut provider_commitments = provider_chain_config.get_sorted_commitments(); let provider_info = contract.get_provider_info(opts.provider).call().await?; let latest_metadata = bincode::deserialize::(&provider_info.commitment_metadata) diff --git a/apps/fortuna/src/command/setup_provider.rs b/apps/fortuna/src/command/setup_provider.rs index 71d0a35f1..4d9afc11f 100644 --- a/apps/fortuna/src/command/setup_provider.rs +++ b/apps/fortuna/src/command/setup_provider.rs @@ -1,13 +1,21 @@ use { crate::{ - api::get_register_uri, - chain::ethereum::SignablePythContract, + api::{ + get_register_uri, + ChainId, + }, + chain::ethereum::{ + ProviderInfo, + SignablePythContract, + }, command::{ register_provider, register_provider::CommitmentMetadata, }, config::{ Config, + EthereumConfig, + ProviderConfig, RegisterProviderOptions, SetupProviderOptions, }, @@ -29,115 +37,150 @@ use { types::Bytes, }, std::sync::Arc, + tracing::Instrument, }; /// Setup provider for all the chains. +pub async fn setup_provider(opts: &SetupProviderOptions) -> Result<()> { + let config = Config::load(&opts.config.config)?; + for (chain_id, chain_config) in &config.chains { + setup_chain_provider(opts, chain_id, chain_config).await?; + } + Ok(()) +} + + +/// Setup provider for a single chain. /// 1. Register if there was no previous registration. /// 2. Re-register if there are no more random numbers to request on the contract. /// 3. Re-register if there is a mismatch in generated hash chain. /// 4. Update provider fee if there is a mismatch with the fee set on contract. /// 5. Update provider uri if there is a mismatch with the uri set on contract. -pub async fn setup_provider(opts: &SetupProviderOptions) -> Result<()> { - let config = Config::load(&opts.config.config)?; +#[tracing::instrument(name="setup_chain_provider", skip_all, fields(chain_id=chain_id))] +async fn setup_chain_provider( + opts: &SetupProviderOptions, + chain_id: &ChainId, + chain_config: &EthereumConfig, +) -> Result<()> { + tracing::info!("Setting up provider for chain: {0}", chain_id); + let provider_config = ProviderConfig::load(&opts.provider_config.provider_config)?; let private_key = opts.load_private_key()?; - let secret = opts.randomness.load_secret()?; let provider_address = private_key.clone().parse::()?.address(); + let provider_fee = provider_config.get_chain_config(chain_id)?.fee; + // Initialize a Provider to interface with the EVM contract. + let contract = Arc::new(SignablePythContract::from_config(&chain_config, &private_key).await?); - for (chain_id, chain_config) in &config.chains { - // Initialize a Provider to interface with the EVM contract. - let contract = - Arc::new(SignablePythContract::from_config(&chain_config, &private_key).await?); + tracing::info!("Fetching provider info"); + let provider_info = contract.get_provider_info(provider_address).call().await?; + tracing::info!("Provider info: {:?}", provider_info); - tracing::info!("{}: fetching provider info", chain_id); - let provider_info = contract.get_provider_info(provider_address).call().await?; - tracing::info!("{0}: provider info: {1:?}", chain_id, provider_info); + let mut register = false; - let mut register = false; + let uri = get_register_uri(&opts.base_uri, &chain_id)?; - let uri = get_register_uri(&opts.base_uri, &chain_id)?; - let uri_as_bytes: Bytes = AbiBytes::from(uri.as_str()).into(); + // This condition satisfies for both when there is no registration and when there are no + // more random numbers left to request + if provider_info.end_sequence_number <= provider_info.sequence_number { + tracing::info!( + "endSequenceNumber <= sequenceNumber. endSequenceNumber={}, sequenceNumber={}", + provider_info.end_sequence_number, + provider_info.sequence_number + ); + register = true; + } else { + let metadata = + bincode::deserialize::(&provider_info.commitment_metadata) + .map_err(|e| { + anyhow!( + "Chain: {} - Failed to deserialize commitment metadata: {}", + &chain_id, + e + ) + })?; - // This condition satisfies for both when there is no registration and when there are no - // more random numbers left to request - if provider_info.end_sequence_number <= provider_info.sequence_number { - tracing::info!( - "{0}: endSequenceNumber <= sequenceNumber. endSequenceNumber={1}, sequenceNumber={2}", - chain_id, - provider_info.end_sequence_number, - provider_info.sequence_number - ); - register = true; - } else { - let metadata = - bincode::deserialize::(&provider_info.commitment_metadata) - .map_err(|e| { - anyhow!( - "Chain: {} - Failed to deserialize commitment metadata: {}", - &chain_id, - e - ) - })?; - - let hash_chain = PebbleHashChain::from_config( - &secret, - &chain_id, - &provider_address, - &chain_config.contract_addr, - &metadata.seed, - opts.randomness.chain_length, - )?; - let chain_state = HashChainState { - offsets: vec![provider_info - .original_commitment_sequence_number - .try_into()?], - hash_chains: vec![hash_chain], - }; + let secret = opts.randomness.load_secret()?; + let hash_chain = PebbleHashChain::from_config( + &secret, + &chain_id, + &provider_address, + &chain_config.contract_addr, + &metadata.seed, + opts.randomness.chain_length, + )?; + let chain_state = HashChainState { + offsets: vec![provider_info + .original_commitment_sequence_number + .try_into()?], + hash_chains: vec![hash_chain], + }; - if chain_state.reveal(provider_info.original_commitment_sequence_number)? - != provider_info.original_commitment - { - tracing::info!( - "{}: the root of the generated hash chain does not match the commitment", - &chain_id - ); - register = true; - } + if chain_state.reveal(provider_info.original_commitment_sequence_number)? + != provider_info.original_commitment + { + tracing::info!("The root of the generated hash chain does not match the commitment",); + register = true; } + } + + if register { + tracing::info!("Registering"); + register_provider(&RegisterProviderOptions { + config: opts.config.clone(), + chain_id: chain_id.clone(), + private_key: private_key.clone(), + randomness: opts.randomness.clone(), + fee: provider_fee, + uri, + }) + .await + .map_err(|e| anyhow!("Chain: {} - Failed to register provider: {}", &chain_id, e))?; + tracing::info!("Registered"); + } else { + sync_fee(&contract, &provider_info, provider_fee) + .in_current_span() + .await?; + sync_uri(&contract, &provider_info, uri) + .in_current_span() + .await?; + } + Ok(()) +} - if register { - tracing::info!("{}: registering", &chain_id); - register_provider(&RegisterProviderOptions { - config: opts.config.clone(), - chain_id: chain_id.clone(), - private_key: private_key.clone(), - randomness: opts.randomness.clone(), - fee: opts.fee, - uri, - }) - .await - .map_err(|e| anyhow!("Chain: {} - Failed to register provider: {}", &chain_id, e))?; - tracing::info!("{}: registered", &chain_id); - } else { - if provider_info.fee_in_wei != opts.fee { - tracing::info!("{}: updating provider fee", chain_id); - if let Some(r) = contract.set_provider_fee(opts.fee).send().await?.await? { - tracing::info!("{0}: updated provider fee: {1:?}", chain_id, r); - } - } +async fn sync_uri( + contract: &Arc, + provider_info: &ProviderInfo, + uri: String, +) -> Result<()> { + let uri_as_bytes: Bytes = AbiBytes::from(uri.as_str()).into(); + if &provider_info.uri != &uri_as_bytes { + tracing::info!("Updating provider uri to {}", uri); + if let Some(receipt) = contract + .set_provider_uri(uri_as_bytes) + .send() + .await? + .await? + { + tracing::info!("Updated provider uri: {:?}", receipt); + } + } + Ok(()) +} - if &provider_info.uri != &uri_as_bytes { - tracing::info!("{}: updating provider uri", chain_id); - if let Some(receipt) = contract - .set_provider_uri(uri_as_bytes) - .send() - .await? - .log_msg("Pending transfer hash") - .await? - { - tracing::info!("{0}: updated provider uri: {1:?}", chain_id, receipt); - } - } +async fn sync_fee( + contract: &Arc, + provider_info: &ProviderInfo, + provider_fee: u128, +) -> Result<()> { + if provider_info.fee_in_wei != provider_fee { + tracing::info!("Updating provider fee {}", provider_fee); + if let Some(r) = contract + .set_provider_fee(provider_fee) + .send() + .await? + .await? + { + tracing::info!("Updated provider fee: {:?}", r); } } Ok(()) diff --git a/apps/fortuna/src/config.rs b/apps/fortuna/src/config.rs index a15b0eac5..872c8f7c3 100644 --- a/apps/fortuna/src/config.rs +++ b/apps/fortuna/src/config.rs @@ -167,7 +167,7 @@ pub struct EthereumConfig { pub struct ProviderConfigOptions { #[arg(long = "provider-config")] #[arg(env = "FORTUNA_PROVIDER_CONFIG")] - pub provider_config: Option, + pub provider_config: String, } #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] @@ -185,26 +185,33 @@ impl ProviderConfig { /// 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 { - self.chains.get(chain_id).map(|x| x.clone()) + pub fn get_chain_config(&self, chain_id: &ChainId) -> Result { + self.chains.get(chain_id).map(|x| x.clone()).ok_or( + anyhow!( + "Could not find chain id {} in provider configuration", + &chain_id + ) + .into(), + ) } } #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct ProviderChainConfig { - commitments: Vec, + commitments: Option>, + pub fee: u128, } 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 { - let mut sorted_commitments = self.commitments.clone(); - sorted_commitments.sort_by(|c1, c2| { + let mut commitments = self.commitments.clone().unwrap_or(Vec::new()); + commitments.sort_by(|c1, c2| { c1.original_commitment_sequence_number .cmp(&c2.original_commitment_sequence_number) }); - sorted_commitments + commitments } } diff --git a/apps/fortuna/src/config/setup_provider.rs b/apps/fortuna/src/config/setup_provider.rs index 1bee7fb9f..d1cb8dfe8 100644 --- a/apps/fortuna/src/config/setup_provider.rs +++ b/apps/fortuna/src/config/setup_provider.rs @@ -1,6 +1,7 @@ use { crate::config::{ ConfigOptions, + ProviderConfigOptions, RandomnessOptions, }, anyhow::Result, @@ -15,6 +16,9 @@ pub struct SetupProviderOptions { #[command(flatten)] pub config: ConfigOptions, + #[command(flatten)] + pub provider_config: ProviderConfigOptions, + /// Path to a file containing a 20-byte (40 char) hex encoded Ethereum private key. /// This key is required to submit transactions (such as registering with the contract). #[arg(long = "private-key")] @@ -24,11 +28,6 @@ pub struct SetupProviderOptions { #[command(flatten)] pub randomness: RandomnessOptions, - /// The fee to charge (in wei) for each requested random number - #[arg(long = "pyth-contract-fee")] - #[arg(default_value = "100")] - pub fee: u128, - /// The base URI for fortuna. /// e.g., https://fortuna-staging.dourolabs.app #[arg(long = "uri")]