From 1e25f3fbff13f7af307e2535ff8d5303ccd743ac Mon Sep 17 00:00:00 2001 From: Amin Moghaddam Date: Thu, 23 May 2024 15:42:33 +0200 Subject: [PATCH] fix(fortuna): Eip1559 fixes (#1597) * fix(fortuna): Change order of middlewares to avoid calling gas oracle if legacy tx * Auto detect EIP 1559 * Fault tolerant start of api and keeper * Bump --- apps/fortuna/Cargo.lock | 2 +- apps/fortuna/Cargo.toml | 2 +- apps/fortuna/src/chain/ethereum.rs | 26 ++--- apps/fortuna/src/command/run.rs | 151 ++++++++++++++++------------- apps/fortuna/src/config.rs | 4 - 5 files changed, 103 insertions(+), 82 deletions(-) diff --git a/apps/fortuna/Cargo.lock b/apps/fortuna/Cargo.lock index 324b7ff967..e0e3095d80 100644 --- a/apps/fortuna/Cargo.lock +++ b/apps/fortuna/Cargo.lock @@ -1488,7 +1488,7 @@ dependencies = [ [[package]] name = "fortuna" -version = "5.4.0" +version = "5.4.1" dependencies = [ "anyhow", "axum", diff --git a/apps/fortuna/Cargo.toml b/apps/fortuna/Cargo.toml index 0d85b8e362..39bec43b83 100644 --- a/apps/fortuna/Cargo.toml +++ b/apps/fortuna/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fortuna" -version = "5.4.0" +version = "5.4.1" edition = "2021" [dependencies] diff --git a/apps/fortuna/src/chain/ethereum.rs b/apps/fortuna/src/chain/ethereum.rs index a7c030b008..eec7dda007 100644 --- a/apps/fortuna/src/chain/ethereum.rs +++ b/apps/fortuna/src/chain/ethereum.rs @@ -67,12 +67,12 @@ abigen!( ); pub type SignablePythContract = PythRandom< - GasOracleMiddleware< - TransformerMiddleware< + TransformerMiddleware< + GasOracleMiddleware< NonceManagerMiddleware, LocalWallet>>, - LegacyTxTransformer, + EthProviderOracle>, >, - EthProviderOracle>, + LegacyTxTransformer, >, >; pub type PythContract = PythRandom>; @@ -102,11 +102,15 @@ impl SignablePythContract { ) -> Result { let provider = Provider::::try_from(&chain_config.geth_rpc_addr)?; let chain_id = provider.get_chainid().await?; - + let eip1559_supported = provider + .get_block(ethers::prelude::BlockNumber::Latest) + .await? + .ok_or_else(|| anyhow!("Latest block not found"))? + .base_fee_per_gas + .is_some(); let gas_oracle = EthProviderOracle::new(provider.clone()); - let transformer = LegacyTxTransformer { - use_legacy_tx: chain_config.legacy_tx, + use_legacy_tx: !eip1559_supported, }; let wallet__ = private_key @@ -117,12 +121,12 @@ impl SignablePythContract { Ok(PythRandom::new( chain_config.contract_addr, - Arc::new(GasOracleMiddleware::new( - TransformerMiddleware::new( + Arc::new(TransformerMiddleware::new( + GasOracleMiddleware::new( NonceManagerMiddleware::new(SignerMiddleware::new(provider, wallet__), address), - transformer, + gas_oracle, ), - gas_oracle, + transformer, )), )) } diff --git a/apps/fortuna/src/command/run.rs b/apps/fortuna/src/command/run.rs index 5ce3936788..16619027e8 100644 --- a/apps/fortuna/src/command/run.rs +++ b/apps/fortuna/src/command/run.rs @@ -10,6 +10,7 @@ use { config::{ Commitment, Config, + EthereumConfig, ProviderConfig, RunOptions, }, @@ -149,77 +150,23 @@ pub async fn run_keeper( pub async fn run(opts: &RunOptions) -> Result<()> { let config = Config::load(&opts.config.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.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) - .map_err(|e| { - anyhow!( - "Chain: {} - Failed to deserialize commitment metadata: {}", - &chain_id, - e - ) - })?; - - provider_commitments.push(Commitment { - seed: latest_metadata.seed, - chain_length: latest_metadata.chain_length, - original_commitment_sequence_number: provider_info.original_commitment_sequence_number, - }); - - // 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 mut offsets = Vec::::new(); - let mut hash_chains = Vec::::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, - hash_chains, - }; - - if chain_state.reveal(provider_info.original_commitment_sequence_number)? - != provider_info.original_commitment - { - return Err(anyhow!("The root of the generated hash chain for chain id {} does not match the commitment. Are the secret and chain length configured correctly?", &chain_id).into()); - } else { - tracing::info!("Root of chain id {} matches commitment", &chain_id); + let state = setup_chain_state(&opts, &secret, chain_id, chain_config).await; + match state { + Ok(state) => { + chains.insert(chain_id.clone(), state); + } + Err(e) => { + tracing::error!("Failed to setup {} {}", chain_id, e); + } } - - let state = api::BlockchainState { - id: chain_id.clone(), - state: Arc::new(chain_state), - contract, - provider_address: opts.provider, - reveal_delay_blocks: chain_config.reveal_delay_blocks, - confirmed_block_status: chain_config.confirmed_block_status, - }; - - chains.insert(chain_id.clone(), state); + } + if chains.is_empty() { + return Err(anyhow!("No chains were successfully setup")); } @@ -254,6 +201,80 @@ pub async fn run(opts: &RunOptions) -> Result<()> { Ok(()) } +async fn setup_chain_state( + opts: &&RunOptions, + secret: &String, + chain_id: &ChainId, + chain_config: &EthereumConfig, +) -> Result { + let provider_config = ProviderConfig::load(&opts.provider_config.provider_config)?; + let contract = Arc::new(PythContract::from_config(&chain_config)?); + 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, + ) + .map_err(|e| { + anyhow!( + "Chain: {} - Failed to deserialize commitment metadata: {}", + &chain_id, + e + ) + })?; + + provider_commitments.push(Commitment { + seed: latest_metadata.seed, + chain_length: latest_metadata.chain_length, + original_commitment_sequence_number: provider_info.original_commitment_sequence_number, + }); + + // 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 mut offsets = Vec::::new(); + let mut hash_chains = Vec::::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, + hash_chains, + }; + + if chain_state.reveal(provider_info.original_commitment_sequence_number)? + != provider_info.original_commitment + { + return Err(anyhow!("The root of the generated hash chain for chain id {} does not match the commitment. Are the secret and chain length configured correctly?", &chain_id).into()); + } else { + tracing::info!("Root of chain id {} matches commitment", &chain_id); + } + + let state = BlockchainState { + id: chain_id.clone(), + state: Arc::new(chain_state), + contract, + provider_address: opts.provider, + reveal_delay_blocks: chain_config.reveal_delay_blocks, + confirmed_block_status: chain_config.confirmed_block_status, + }; + Ok(state) +} + #[derive(Clone, Debug, Hash, PartialEq, Eq, EncodeLabelSet)] pub struct ChainLabel { diff --git a/apps/fortuna/src/config.rs b/apps/fortuna/src/config.rs index 872c8f7c3b..5c8deff6af 100644 --- a/apps/fortuna/src/config.rs +++ b/apps/fortuna/src/config.rs @@ -148,10 +148,6 @@ pub struct EthereumConfig { /// its commitment. pub reveal_delay_blocks: BlockNumber, - /// Use the legacy transaction format (for networks without EIP 1559) - #[serde(default)] - pub legacy_tx: bool, - /// The BlockStatus of the block that is considered confirmed. /// For example, Finalized, Safe, Latest #[serde(default)]