Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add configuration to skip packet sequences when clearing #3862

Merged
merged 10 commits into from
Mar 6, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- Add a per-chain configuration `excluded_sequences` allowing
users to specify a list of packet sequences which will not be
cleared.
This configuration has no impact on standard packet relaying.
([\#3754](https://github.com/informalsystems/hermes/issues/3754))
12 changes: 12 additions & 0 deletions config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,18 @@ memo_prefix = ''
# This will override the global clear interval for this chain only, allowing different intervals for each chain.
# clear_interval = 50

# Specify packet sequences which should not be cleared, per channel.
#
# For each channel, specify a list of sequences which should not be cleared, eg.
#
# excluded_sequences = [
# ['channel-0', [1, 2, 3]],
# ['channel-1', [4, 5, 6]]
# ]
#
# Default: No filter
# excluded_sequences = []

[[chains]]
id = 'ibc-1'
rpc_addr = 'http://127.0.0.1:26557'
Expand Down
9 changes: 5 additions & 4 deletions crates/relayer-cli/src/chain_registry.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
//! Contains functions to generate a relayer config for a given chain

use std::collections::HashMap;
use std::fmt::Display;
use std::marker::Send;

use futures::future::join_all;
use http::Uri;
use ibc_relayer::config::dynamic_gas::DynamicGasPrice;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::fmt::Display;
use std::marker::Send;
use tokio::task::{JoinError, JoinHandle};
use tracing::{error, trace};

Expand Down Expand Up @@ -173,6 +173,7 @@ where
extension_options: Vec::new(),
compat_mode: None,
clear_interval: None,
excluded_sequences: BTreeMap::new(),
}))
}

Expand Down
58 changes: 53 additions & 5 deletions crates/relayer-cli/src/commands/clear.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use abscissa_core::config::Override;
use abscissa_core::{Command, FrameworkErrorKind, Runnable};

use ibc_relayer::chain::handle::{BaseChainHandle, ChainHandle};
use ibc_relayer::chain::requests::{IncludeProof, QueryChannelRequest, QueryHeight};
use ibc_relayer::config::Config;
use ibc_relayer::link::error::LinkError;
use ibc_relayer::link::{Link, LinkParameters};
Expand Down Expand Up @@ -146,27 +147,74 @@ impl Runnable for ClearPacketsCmd {
}
}

let mut ev_list = vec![];
let (channel, _) = match chains.src.query_channel(
QueryChannelRequest {
port_id: self.port_id.clone(),
channel_id: self.channel_id.clone(),
height: QueryHeight::Latest,
},
IncludeProof::No,
) {
Ok(channel) => channel,
Err(e) => Output::error(e).exit(),
};

let exclude_src_sequences = config
.find_chain(&chains.src.id())
.map(|chain_config| chain_config.excluded_sequences(&self.channel_id).to_vec())
.unwrap_or_default();

let exclude_dst_sequences =
if let Some(counterparty_channel_id) = channel.counterparty().channel_id() {
config
.find_chain(&chains.dst.id())
.map(|chain_config| {
chain_config
.excluded_sequences(counterparty_channel_id)
.to_vec()
})
.unwrap_or_default()
} else {
Vec::new()
};

// Construct links in both directions.
let opts = LinkParameters {
let fwd_opts = LinkParameters {
src_port_id: self.port_id.clone(),
src_channel_id: self.channel_id.clone(),
max_memo_size: config.mode.packets.ics20_max_memo_size,
max_receiver_size: config.mode.packets.ics20_max_receiver_size,
exclude_src_sequences,
};

let fwd_link = match Link::new_from_opts(chains.src.clone(), chains.dst, opts, false, false)
{
// Construct links in both directions.
let reverse_opts = LinkParameters {
src_port_id: self.port_id.clone(),
src_channel_id: self.channel_id.clone(),
max_memo_size: config.mode.packets.ics20_max_memo_size,
max_receiver_size: config.mode.packets.ics20_max_receiver_size,
exclude_src_sequences: exclude_dst_sequences,
};

let fwd_link = match Link::new_from_opts(
chains.src.clone(),
chains.dst.clone(),
fwd_opts,
false,
false,
) {
Ok(link) => link,
Err(e) => Output::error(e).exit(),
};

let rev_link = match fwd_link.reverse(false, false) {
let rev_link = match Link::new_from_opts(chains.dst, chains.src, reverse_opts, false, false)
{
Ok(link) => link,
Err(e) => Output::error(e).exit(),
};

let mut ev_list = vec![];

// Schedule RecvPacket messages for pending packets in both directions or,
// if packet sequences are provided, only on the specified chain.
// This may produce pending acks which will be processed in the next phase.
Expand Down
8 changes: 8 additions & 0 deletions crates/relayer-cli/src/commands/tx/packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,11 @@ impl Runnable for TxPacketRecvCmd {
src_channel_id: self.src_channel_id.clone(),
max_memo_size: config.mode.packets.ics20_max_memo_size,
max_receiver_size: config.mode.packets.ics20_max_receiver_size,

// Packets are only excluded when clearing
exclude_src_sequences: vec![],
};

let link = match Link::new_from_opts(chains.src, chains.dst, opts, false, false) {
Ok(link) => link,
Err(e) => Output::error(e).exit(),
Expand Down Expand Up @@ -185,7 +189,11 @@ impl Runnable for TxPacketAckCmd {
src_channel_id: self.src_channel_id.clone(),
max_memo_size: config.mode.packets.ics20_max_memo_size,
max_receiver_size: config.mode.packets.ics20_max_receiver_size,

// Packets are only excluded when clearing
exclude_src_sequences: vec![],
};

let link = match Link::new_from_opts(chains.src, chains.dst, opts, false, false) {
Ok(link) => link,
Err(e) => Output::error(e).exit(),
Expand Down
18 changes: 15 additions & 3 deletions crates/relayer/src/chain/cosmos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ use tendermint_rpc::endpoint::status;
use tendermint_rpc::{Client, HttpClient, Order};

use crate::account::Balance;
use crate::chain::client::ClientSettings;
use crate::chain::cosmos::batch::{
send_batched_messages_and_wait_check_tx, send_batched_messages_and_wait_commit,
sequential_send_batched_messages_and_wait_commit,
Expand Down Expand Up @@ -91,6 +92,7 @@ use crate::chain::handle::Subscription;
use crate::chain::requests::*;
use crate::chain::tracking::TrackedMsgs;
use crate::client_state::{AnyClientState, IdentifiedAnyClientState};
use crate::config::Error as ConfigError;
use crate::config::{parse_gas_prices, ChainConfig, GasPrice};
use crate::consensus_state::AnyConsensusState;
use crate::denom::DenomTrace;
Expand All @@ -102,10 +104,10 @@ use crate::light_client::tendermint::LightClient as TmLightClient;
use crate::light_client::{LightClient, Verified};
use crate::misbehaviour::MisbehaviourEvidence;
use crate::util::compat_mode::compat_mode_from_version;
use crate::util::pretty::PrettySlice;
use crate::util::pretty::{
PrettyIdentifiedChannel, PrettyIdentifiedClientState, PrettyIdentifiedConnection,
};
use crate::{chain::client::ClientSettings, config::Error as ConfigError};

use self::gas::dynamic_gas_price;
use self::types::app_state::GenesisAppState;
Expand Down Expand Up @@ -671,8 +673,7 @@ impl CosmosSdkChain {

let mut client = self
.block_on(ServiceClient::connect(grpc_addr.clone()))
.map_err(Error::grpc_transport)
.unwrap();
.map_err(Error::grpc_transport)?;

let request = tonic::Request::new(GetSyncingRequest {});

Expand Down Expand Up @@ -2430,6 +2431,17 @@ fn do_health_check(chain: &CosmosSdkChain) -> Result<(), Error> {
let grpc_address = chain.grpc_addr.to_string();
let rpc_address = chain.config.rpc_addr.to_string();

if !chain.config.excluded_sequences.is_empty() {
for (channel_id, seqs) in chain.config.excluded_sequences.iter() {
if !seqs.is_empty() {
warn!(
"chain '{chain_id}' will not clear packets on channel '{channel_id}' with sequences: {}. \
Ignore this warning if this configuration is correct.", PrettySlice(seqs)
);
}
}
}

chain.block_on(chain.rpc_client.health()).map_err(|e| {
Error::health_check_json_rpc(
chain_id.clone(),
Expand Down
6 changes: 5 additions & 1 deletion crates/relayer/src/chain/cosmos/config.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use core::time::Duration;
use std::collections::BTreeMap;
use std::path::PathBuf;

use byte_unit::Byte;
use ibc_relayer_types::core::ics04_channel::packet::Sequence;
use serde_derive::{Deserialize, Serialize};
use tendermint_rpc::Url;

use ibc_relayer_types::core::ics23_commitment::specs::ProofSpecs;
use ibc_relayer_types::core::ics24_host::identifier::ChainId;
use ibc_relayer_types::core::ics24_host::identifier::{ChainId, ChannelId};

use crate::chain::cosmos::config::error::Error as ConfigError;
use crate::config::compat_mode::CompatMode;
Expand Down Expand Up @@ -146,6 +148,8 @@ pub struct CosmosSdkConfig {
pub extension_options: Vec<ExtensionOption>,
pub compat_mode: Option<CompatMode>,
pub clear_interval: Option<u64>,
#[serde(default)]
pub excluded_sequences: BTreeMap<ChannelId, Vec<Sequence>>,
}

impl CosmosSdkConfig {
Expand Down
12 changes: 12 additions & 0 deletions crates/relayer/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ use core::cmp::Ordering;
use core::fmt::{Display, Error as FmtError, Formatter};
use core::str::FromStr;
use core::time::Duration;
use ibc_relayer_types::core::ics04_channel::packet::Sequence;
use std::borrow::Cow;
use std::{fs, fs::File, io::Write, ops::Range, path::Path};

use byte_unit::Byte;
Expand Down Expand Up @@ -703,6 +705,16 @@ impl ChainConfig {
Self::CosmosSdk(config) => config.query_packets_chunk_size = query_packets_chunk_size,
}
}

pub fn excluded_sequences(&self, channel_id: &ChannelId) -> Cow<'_, [Sequence]> {
match self {
Self::CosmosSdk(config) => config
.excluded_sequences
.get(channel_id)
.map(|seqs| Cow::Borrowed(seqs.as_slice()))
.unwrap_or_else(|| Cow::Owned(Vec::new())),
}
}
}

// /!\ Update me when adding a new chain type!
Expand Down
29 changes: 2 additions & 27 deletions crates/relayer/src/link.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use ibc_relayer_types::core::{
ics03_connection::connection::State as ConnectionState,
ics04_channel::channel::State as ChannelState,
ics04_channel::packet::Sequence,
ics24_host::identifier::{ChannelId, PortChannelId, PortId},
};
use tracing::info;
Expand Down Expand Up @@ -38,6 +39,7 @@ pub struct LinkParameters {
pub src_channel_id: ChannelId,
pub max_memo_size: Ics20FieldSizeLimit,
pub max_receiver_size: Ics20FieldSizeLimit,
pub exclude_src_sequences: Vec<Sequence>,
}

pub struct Link<ChainA: ChainHandle, ChainB: ChainHandle> {
Expand Down Expand Up @@ -177,31 +179,4 @@ impl<ChainA: ChainHandle, ChainB: ChainHandle> Link<ChainA, ChainB> {

Link::new(channel, with_tx_confirmation, opts)
}

/// Constructs a link around the channel that is reverse to the channel
/// in this link.
pub fn reverse(
&self,
with_tx_confirmation: bool,
auto_register_counterparty_payee: bool,
) -> Result<Link<ChainB, ChainA>, LinkError> {
let opts = LinkParameters {
src_port_id: self.a_to_b.dst_port_id().clone(),
src_channel_id: self.a_to_b.dst_channel_id().clone(),
max_memo_size: self.a_to_b.max_memo_size,
max_receiver_size: self.a_to_b.max_receiver_size,
};
let chain_b = self.a_to_b.dst_chain().clone();
let chain_a = self.a_to_b.src_chain().clone();

// Some of the checks and initializations may be redundant;
// going slowly, but reliably.
Link::new_from_opts(
chain_b,
chain_a,
opts,
with_tx_confirmation,
auto_register_counterparty_payee,
)
}
}
16 changes: 16 additions & 0 deletions crates/relayer/src/link/relay_path.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use alloc::collections::BTreeMap as HashMap;
use alloc::collections::VecDeque;
use ibc_relayer_types::core::ics04_channel::packet::Sequence;
use std::ops::Sub;
use std::time::{Duration, Instant};

Expand Down Expand Up @@ -115,6 +116,7 @@ pub struct RelayPath<ChainA: ChainHandle, ChainB: ChainHandle> {

pub max_memo_size: Ics20FieldSizeLimit,
pub max_receiver_size: Ics20FieldSizeLimit,
pub exclude_src_sequences: Vec<Sequence>,
}

impl<ChainA: ChainHandle, ChainB: ChainHandle> RelayPath<ChainA, ChainB> {
Expand Down Expand Up @@ -163,6 +165,8 @@ impl<ChainA: ChainHandle, ChainB: ChainHandle> RelayPath<ChainA, ChainB> {

max_memo_size: link_parameters.max_memo_size,
max_receiver_size: link_parameters.max_receiver_size,

exclude_src_sequences: link_parameters.exclude_src_sequences,
})
}

Expand Down Expand Up @@ -1155,6 +1159,12 @@ impl<ChainA: ChainHandle, ChainB: ChainHandle> RelayPath<ChainA, ChainB> {
return Ok(());
}

// Retain only sequences which should not be filtered out
let sequences: Vec<Sequence> = sequences
.into_iter()
.filter(|sequence| !self.exclude_src_sequences.contains(sequence))
.collect();

debug!(
dst_chain = %self.dst_chain().id(),
src_chain = %self.src_chain().id(),
Expand Down Expand Up @@ -1219,6 +1229,12 @@ impl<ChainA: ChainHandle, ChainB: ChainHandle> RelayPath<ChainA, ChainB> {
return Ok(());
}

// Retain only sequences which should not be filtered out
let sequences: Vec<Sequence> = sequences
.into_iter()
.filter(|sequence| !self.exclude_src_sequences.contains(sequence))
.collect();

debug!(
dst_chain = %self.dst_chain().id(),
src_chain = %self.src_chain().id(),
Expand Down
7 changes: 7 additions & 0 deletions crates/relayer/src/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ pub fn spawn_worker_tasks<ChainA: ChainHandle, ChainB: ChainHandle>(
(Some(cmd_tx), None)
}
Object::Packet(path) => {
let exclude_src_sequences = config
.find_chain(&chains.a.id())
.map(|chain_config| chain_config.excluded_sequences(&path.src_channel_id))
.unwrap_or_default()
.to_vec();

let packets_config = config.mode.packets;
let link_res = Link::new_from_opts(
chains.a.clone(),
Expand All @@ -119,6 +125,7 @@ pub fn spawn_worker_tasks<ChainA: ChainHandle, ChainB: ChainHandle>(
src_channel_id: path.src_channel_id.clone(),
max_memo_size: packets_config.ics20_max_memo_size,
max_receiver_size: packets_config.ics20_max_receiver_size,
exclude_src_sequences,
},
packets_config.tx_confirmation,
packets_config.auto_register_counterparty_payee,
Expand Down
Loading
Loading