From 168abd8ac3086141cfdcd95edb257a153107bca8 Mon Sep 17 00:00:00 2001 From: romac Date: Wed, 29 Nov 2023 10:54:55 +0000 Subject: [PATCH] deploy: informalsystems/hermes@1c4b12f080ac832e16b1ce3e566ec0f09b8bfe46 --- src/ibc_relayer_cli/commands/evidence.rs.html | 414 ++++++++++++------ 1 file changed, 282 insertions(+), 132 deletions(-) diff --git a/src/ibc_relayer_cli/commands/evidence.rs.html b/src/ibc_relayer_cli/commands/evidence.rs.html index b7edf5731344..3bece29f7250 100644 --- a/src/ibc_relayer_cli/commands/evidence.rs.html +++ b/src/ibc_relayer_cli/commands/evidence.rs.html @@ -776,15 +776,90 @@ 776 777 778 +779 +780 +781 +782 +783 +784 +785 +786 +787 +788 +789 +790 +791 +792 +793 +794 +795 +796 +797 +798 +799 +800 +801 +802 +803 +804 +805 +806 +807 +808 +809 +810 +811 +812 +813 +814 +815 +816 +817 +818 +819 +820 +821 +822 +823 +824 +825 +826 +827 +828 +829 +830 +831 +832 +833 +834 +835 +836 +837 +838 +839 +840 +841 +842 +843 +844 +845 +846 +847 +848 +849 +850 +851 +852 +853
use std::collections::HashMap;
-use std::ops::Deref;
+use std::ops::{ControlFlow, Deref};
 use std::sync::Arc;
 use std::thread::sleep;
 use std::time::Duration;
 
 use abscissa_core::clap::Parser;
 use abscissa_core::{Command, Runnable};
-use ibc_relayer::config::ChainConfig;
+use ibc_relayer::config::{ChainConfig, Config};
 use tokio::runtime::Runtime as TokioRuntime;
 
 use tendermint::block::Height as TendermintHeight;
@@ -874,7 +949,13 @@
         );
 
         let chain = CosmosSdkChain::bootstrap(chain_config, rt.clone()).unwrap();
-        let res = monitor_misbehaviours(rt, chain, self.key_name.as_ref(), self.check_past_blocks);
+        let res = monitor_misbehaviours(
+            rt,
+            &config,
+            chain,
+            self.key_name.as_ref(),
+            self.check_past_blocks,
+        );
 
         match res {
             Ok(()) => Output::success(()).exit(),
@@ -885,6 +966,7 @@
 
 fn monitor_misbehaviours(
     rt: Arc<TokioRuntime>,
+    config: &Config,
     mut chain: CosmosSdkChain,
     key_name: Option<&String>,
     check_past_blocks: u64,
@@ -914,7 +996,7 @@
     while height >= target_height {
         debug!("checking for evidence at height {height}");
 
-        if let Err(e) = check_misbehaviour_at(rt.clone(), &chain, key_name, height) {
+        if let Err(e) = check_misbehaviour_at(rt.clone(), config, &chain, key_name, height) {
             warn!("error while checking for misbehaviour at height {height}: {e}");
         }
 
@@ -937,9 +1019,13 @@
                     if let IbcEvent::NewBlock(new_block) = &event_with_height.event {
                         info!("checking for evidence at height {}", new_block.height);
 
-                        if let Err(e) =
-                            check_misbehaviour_at(rt.clone(), &chain, key_name, new_block.height)
-                        {
+                        if let Err(e) = check_misbehaviour_at(
+                            rt.clone(),
+                            config,
+                            &chain,
+                            key_name,
+                            new_block.height,
+                        ) {
                             error!(
                                 "error while checking for misbehaviour at height {}: {e}",
                                 new_block.height
@@ -962,6 +1048,7 @@
 /// clients of the chain, freezing them.
 fn check_misbehaviour_at(
     rt: Arc<TokioRuntime>,
+    config: &Config,
     chain: &CosmosSdkChain,
     key_name: Option<&String>,
     height: Height,
@@ -976,13 +1063,13 @@
                 warn!("found duplicate vote evidence");
                 trace!("{dv:#?}");
 
-                handle_duplicate_vote(rt.clone(), chain, key_name, *dv)?;
+                handle_duplicate_vote(rt.clone(), config, chain, key_name, *dv)?;
             }
             tendermint::evidence::Evidence::LightClientAttack(lc) => {
                 warn!("found light client attack evidence");
                 trace!("{lc:#?}");
 
-                handle_light_client_attack(rt.clone(), chain, key_name, *lc)?;
+                handle_light_client_attack(rt.clone(), config, chain, key_name, *lc)?;
             }
         }
     }
@@ -990,114 +1077,167 @@
     Ok(())
 }
 
+fn spawn_runtime(
+    rt: Arc<TokioRuntime>,
+    config: &Config,
+    cache: &mut HashMap<ChainId, BaseChainHandle>,
+    chain_id: &ChainId,
+    key_name: Option<&String>,
+) -> eyre::Result<BaseChainHandle> {
+    if !cache.contains_key(chain_id) {
+        let chain_handle = spawn_chain_runtime_with_modified_config::<BaseChainHandle>(
+            config,
+            chain_id,
+            rt,
+            |chain_config| {
+                if let Some(key_name) = key_name {
+                    chain_config.set_key_name(key_name.to_string());
+                }
+            },
+        )?;
+
+        cache.insert(chain_id.clone(), chain_handle);
+    }
+
+    Ok(cache
+        .get(chain_id)
+        .expect("chain handle was either already there or we just inserted it")
+        .clone())
+}
+
 fn handle_duplicate_vote(
     rt: Arc<TokioRuntime>,
+    config: &Config,
     chain: &CosmosSdkChain,
     key_name: Option<&String>,
-    dv: DuplicateVoteEvidence,
+    evidence: DuplicateVoteEvidence,
 ) -> eyre::Result<()> {
-    use ibc_relayer::chain::requests::QueryConsensusStateHeightsRequest;
-
-    let config = app_config();
-
     // Fetch all the counterparty clients of this chain.
-    let counterparty_clients = fetch_all_counterparty_clients(chain)?;
+    let counterparty_clients = fetch_all_counterparty_clients(config, chain)?;
 
-    let mut chains = HashMap::new();
+    // Cache for the chain handles
+    let mut chains = HashMap::new();
 
     // For each counterparty client, build the double voting evidence and submit it to the chain,
     // freezing that client.
     for (counterparty_chain_id, counterparty_client_id) in counterparty_clients {
-        if !chains.contains_key(&counterparty_chain_id) {
-            let chain_handle = spawn_chain_runtime_with_modified_config::<BaseChainHandle>(
-                &config,
-                &counterparty_chain_id,
-                rt.clone(),
-                |chain_config| {
-                    if let Some(key_name) = key_name {
-                        chain_config.set_key_name(key_name.to_string());
-                    }
-                },
-            )?;
-
-            chains.insert(counterparty_chain_id.clone(), chain_handle);
+        let counterparty_chain_handle = match spawn_runtime(
+            rt.clone(),
+            config,
+            &mut chains,
+            &counterparty_chain_id,
+            key_name,
+        ) {
+            Ok(chain_handle) => chain_handle,
+            Err(e) => {
+                error!("failed to spawn runtime for chain `{counterparty_chain_id}`: {e}");
+                continue;
+            }
         };
 
-        let counterparty_chain_handle = chains.get(&counterparty_chain_id).unwrap();
+        let next = submit_duplicate_vote_evidence(
+            &rt,
+            chain,
+            &counterparty_chain_handle,
+            &counterparty_chain_id,
+            &counterparty_client_id,
+            &evidence,
+        );
 
-        let signer = counterparty_chain_handle.get_signer()?;
+        match next {
+            Ok(ControlFlow::Continue(())) => continue,
+            Ok(ControlFlow::Break(())) => break,
+            Err(e) => {
+                error!(
+                    "failed to report double voting evidence to chain `{counterparty_chain_id}`: {e}"
+                );
 
-        if !is_counterparty_provider(chain, counterparty_chain_handle, &counterparty_client_id) {
-            debug!("counterparty client `{counterparty_client_id}` on chain `{counterparty_chain_id}` is not a CCV client, skipping...");
-            continue;
+                continue;
+            }
         }
+    }
 
-        let infraction_height = dv.vote_a.height;
-
-        // Get the trusted height in the same way we do for client updates,
-        // ie. retrieve the consensus state at the highest height smaller than the infraction height.
-        //
-        // Note: The consensus state heights are sorted in increasing order.
-        let consensus_state_heights =
-            chain.query_consensus_state_heights(QueryConsensusStateHeightsRequest {
-                client_id: counterparty_client_id.clone(),
-                pagination: Some(PageRequest::all()),
-            })?;
-
-        // Retrieve the consensus state at the highest height smaller than the infraction height.
-        let consensus_state_height_before_infraction_height = consensus_state_heights
-            .into_iter()
-            .filter(|height| height.revision_height() < infraction_height.value())
-            .last();
-
-        let Some(trusted_height) = consensus_state_height_before_infraction_height else {
-            error!(
-                "cannot build infraction block header for client `{counterparty_client_id}` on chain `{counterparty_chain_id}`,\
-                reason: could not find consensus state at highest height smaller than infraction height {infraction_height}"
-            );
+    Ok(())
+}
 
-            continue;
-        };
+fn submit_duplicate_vote_evidence(
+    rt: &TokioRuntime,
+    chain: &CosmosSdkChain,
+    counterparty_chain_handle: &BaseChainHandle,
+    counterparty_chain_id: &ChainId,
+    counterparty_client_id: &ClientId,
+    evidence: &DuplicateVoteEvidence,
+) -> eyre::Result<ControlFlow<()>> {
+    use ibc_relayer::chain::requests::QueryConsensusStateHeightsRequest;
 
-        // Construct the light client block header for the consumer chain at the infraction height
-        let infraction_block_header =
-            fetch_infraction_block_header(&rt, chain, infraction_height, trusted_height)?;
+    let signer = counterparty_chain_handle.get_signer()?;
 
-        let submit_msg = MsgSubmitIcsConsumerDoubleVoting {
-            submitter: signer.clone(),
-            duplicate_vote_evidence: dv.clone(),
-            infraction_block_header,
-        }
-        .to_any();
+    if !is_counterparty_provider(chain, counterparty_chain_handle, counterparty_client_id) {
+        debug!("counterparty client `{counterparty_client_id}` on chain `{counterparty_chain_id}` is not a CCV client, skipping...");
+        return Ok(ControlFlow::Continue(()));
+    }
 
-        info!(
-            "submitting consumer double voting evidence to provider chain `{counterparty_chain_id}`"
+    let infraction_height = evidence.vote_a.height;
+
+    // Get the trusted height in the same way we do for client updates,
+    // ie. retrieve the consensus state at the highest height smaller than the infraction height.
+    //
+    // Note: The consensus state heights are sorted in increasing order.
+    let consensus_state_heights =
+        chain.query_consensus_state_heights(QueryConsensusStateHeightsRequest {
+            client_id: counterparty_client_id.clone(),
+            pagination: Some(PageRequest::all()),
+        })?;
+
+    // Retrieve the consensus state at the highest height smaller than the infraction height.
+    let consensus_state_height_before_infraction_height = consensus_state_heights
+        .into_iter()
+        .filter(|height| height.revision_height() < infraction_height.value())
+        .last();
+
+    let Some(trusted_height) = consensus_state_height_before_infraction_height else {
+        error!(
+            "cannot build infraction block header for client `{counterparty_client_id}` on chain `{counterparty_chain_id}`,\
+            reason: could not find consensus state at highest height smaller than infraction height {infraction_height}"
         );
 
-        let tracked_msgs = TrackedMsgs::new_static(vec![submit_msg], "double_voting_evidence");
-        let responses = counterparty_chain_handle.send_messages_and_wait_check_tx(tracked_msgs)?;
+        return Ok(ControlFlow::Continue(()));
+    };
 
-        for response in responses {
-            if response.code.is_ok() {
-                info!("successfully submitted double voting evidence to chain `{counterparty_chain_id}`, tx hash: {}", response.hash);
-            } else {
-                error!(
-                    "failed to submit double voting evidence to chain `{counterparty_chain_id}`: {response:?}"
-                );
-            }
-        }
+    // Construct the light client block header for the consumer chain at the infraction height
+    let infraction_block_header =
+        fetch_infraction_block_header(rt, chain, infraction_height, trusted_height)?;
 
-        // We have submitted the evidence to the provider, and because there can only be a single
-        // provider for a consumer chain, we can stop now. No need to check all the other
-        // counteparties.
-        break;
+    let submit_msg = MsgSubmitIcsConsumerDoubleVoting {
+        submitter: signer.clone(),
+        duplicate_vote_evidence: evidence.clone(),
+        infraction_block_header,
     }
+    .to_any();
 
-    Ok(())
+    info!("submitting consumer double voting evidence to provider chain `{counterparty_chain_id}`");
+
+    let tracked_msgs = TrackedMsgs::new_static(vec![submit_msg], "double_voting_evidence");
+    let responses = counterparty_chain_handle.send_messages_and_wait_check_tx(tracked_msgs)?;
+
+    for response in responses {
+        if response.code.is_ok() {
+            info!("successfully submitted double voting evidence to chain `{counterparty_chain_id}`, tx hash: {}", response.hash);
+        } else {
+            error!(
+                "failed to submit double voting evidence to chain `{counterparty_chain_id}`: {response:?}"
+            );
+        }
+    }
+
+    // We have submitted the evidence to the provider, and because there can only be a single
+    // provider for a consumer chain, we can stop now. No need to check all the other
+    // counteparties.
+    Ok(ControlFlow::Break(()))
 }
 
 fn fetch_infraction_block_header(
-    rt: &Arc<TokioRuntime>,
+    rt: &TokioRuntime,
     chain: &CosmosSdkChain,
     infraction_height: TendermintHeight,
     trusted_height: Height,
@@ -1134,62 +1274,55 @@
 
 fn handle_light_client_attack(
     rt: Arc<TokioRuntime>,
+    config: &Config,
     chain: &CosmosSdkChain,
     key_name: Option<&String>,
     evidence: LightClientAttackEvidence,
 ) -> eyre::Result<()> {
-    let config = app_config();
-
     // Build the two headers to submit as part of the `MsgSubmitMisbehaviour` message.
     let (header1, header2) = build_evidence_headers(rt.clone(), chain, evidence.clone())?;
 
     // Fetch all the counterparty clients of this chain.
-    let counterparty_clients = fetch_all_counterparty_clients(chain)?;
+    let counterparty_clients = fetch_all_counterparty_clients(config, chain)?;
 
-    let mut chains = HashMap::new();
+    // Cache for the chain handles
+    let mut chains = HashMap::new();
+
+    let chain_handle = spawn_runtime(rt.clone(), config, &mut chains, chain.id(), key_name)
+        .map_err(|e| {
+            eyre::eyre!(
+                "failed to spawn chain runtime for chain `{chain_id}`: {e}",
+                chain_id = chain.id()
+            )
+        })?;
 
     // For each counterparty client, build the misbehaviour evidence and submit it to the chain,
     // freezing that client.
     for (counterparty_chain_id, counterparty_client_id) in counterparty_clients {
+        let counterparty_chain_handle = match spawn_runtime(
+            rt.clone(),
+            config,
+            &mut chains,
+            &counterparty_chain_id,
+            key_name,
+        ) {
+            Ok(chain_handle) => chain_handle,
+            Err(e) => {
+                error!(
+                    "failed to spawn chain runtime for chain `{counterparty_chain_id}`: {e}",
+                    counterparty_chain_id = counterparty_chain_id
+                );
+
+                continue;
+            }
+        };
+
         let misbehaviour = TendermintMisbehaviour {
             client_id: counterparty_client_id.clone(),
             header1: header1.clone(),
             header2: header2.clone(),
         };
 
-        if !chains.contains_key(chain.id()) {
-            let chain_handle = spawn_chain_runtime_with_modified_config::<BaseChainHandle>(
-                &config,
-                chain.id(),
-                rt.clone(),
-                |chain_config| {
-                    if let Some(key_name) = key_name {
-                        chain_config.set_key_name(key_name.to_string());
-                    }
-                },
-            )?;
-
-            chains.insert(chain.id().clone(), chain_handle);
-        }
-
-        if !chains.contains_key(&counterparty_chain_id) {
-            let chain_handle = spawn_chain_runtime_with_modified_config::<BaseChainHandle>(
-                &config,
-                &counterparty_chain_id,
-                rt.clone(),
-                |chain_config| {
-                    if let Some(key_name) = key_name {
-                        chain_config.set_key_name(key_name.to_string());
-                    }
-                },
-            )?;
-
-            chains.insert(counterparty_chain_id.clone(), chain_handle);
-        };
-
-        let chain_handle = chains.get(chain.id()).unwrap();
-        let counterparty_chain_handle = chains.get(&counterparty_chain_id).unwrap();
-
         let counterparty_client = ForeignClient::restore(
             counterparty_client_id.clone(),
             counterparty_chain_handle.clone(),
@@ -1201,7 +1334,7 @@
             chain,
             counterparty_client,
             counterparty_client_id,
-            counterparty_chain_handle,
+            &counterparty_chain_handle,
             misbehaviour,
         );
 
@@ -1414,6 +1547,7 @@
 ///     2.2. From the client state, extract the chain id of the counterparty chain.
 /// 4. Return a list of all counterparty chains and counterparty clients.
 fn fetch_all_counterparty_clients(
+    config: &Config,
     chain: &CosmosSdkChain,
 ) -> eyre::Result<Vec<(ChainId, ClientId)>> {
     use ibc_relayer::chain::requests::{QueryClientStateRequest, QueryConnectionsRequest};
@@ -1427,12 +1561,18 @@
     let mut counterparty_clients = vec![];
 
     for connection in connections {
+        let client_id = connection.connection_end.client_id();
+        let counterparty_client_id = connection.connection_end.counterparty().client_id();
+
         debug!(
-            "fetching counterparty client state for connection `{}`",
+            "found connection `{}` with client `{client_id}` and counterparty client `{counterparty_client_id}`",
             connection.connection_id
         );
 
-        let client_id = connection.connection_end.client_id();
+        debug!(
+            "fetching client state for client `{client_id}` on connection `{}`",
+            connection.connection_id
+        );
 
         let client_state = chain.query_client_state(
             QueryClientStateRequest {
@@ -1445,9 +1585,7 @@
         let client_state = match client_state {
             Ok((client_state, _)) => client_state,
             Err(e) => {
-                error!(
-                    "failed to fetch client state for counterparty client `{client_id}`, skipping..."
-                );
+                error!("failed to fetch client state for client `{client_id}`, skipping...");
                 error!("reason: {e}");
 
                 continue;
@@ -1455,11 +1593,23 @@
         };
 
         let counterparty_chain_id = client_state.chain_id();
-        info!("found counterparty client with id `{client_id}` on counterparty chain `{counterparty_chain_id}`");
 
-        counterparty_clients.push((counterparty_chain_id, client_id.clone()));
+        if config.find_chain(&counterparty_chain_id).is_some() {
+            info!("found counterparty client `{counterparty_client_id}` which lives on counterparty chain `{counterparty_chain_id}`");
+
+            counterparty_clients.push((counterparty_chain_id, counterparty_client_id.clone()));
+        } else {
+            debug!(
+                "skipping counterparty client `{client_id}` on counterparty \
+                chain `{counterparty_chain_id}` which is not present in the config..."
+            );
+        }
     }
 
+    // Remove duplicates
+    counterparty_clients.sort();
+    counterparty_clients.dedup();
+
     Ok(counterparty_clients)
 }