Skip to content

Commit

Permalink
Merge branch 'master' into luca_joss/allow-icq-disabling
Browse files Browse the repository at this point in the history
  • Loading branch information
ljoss17 committed Jun 21, 2024
2 parents 824ae87 + b9b6663 commit 57e8065
Show file tree
Hide file tree
Showing 13 changed files with 291 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
- Improve the `excluded_sequences` configuration so that it now accepts
ranges of sequence values in addition to exact values.
Accepted format:
* Exact sequence, e.g. [1, 2, 3]
* "-" separator, e.g. ["1-3"]

These can be combined making the following configurations equivalent:
* `excluded_sequences = { 'channel-0' = [1, "3-5", 7, "9-12"] }`
* `excluded_sequences = { 'channel-0' = [1, 3, 4, 5, 7, 9, 10, 11, 12] }`

([\#4047](https://github.com/informalsystems/hermes/issues/4047))
12 changes: 6 additions & 6 deletions config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -443,15 +443,15 @@ memo_prefix = ''

# Specify packet sequences which should not be cleared, per channel.
#
# For each channel, specify a list of sequences which should not be cleared, eg.
# For each channel, specify a list of sequences which should not be cleared. Acceptable value
# include range of sequences with separator "-", eg.
#
# excluded_sequences = [
# ['channel-0', [1, 2, 3]],
# ['channel-1', [4, 5, 6]]
# ]
# [chains.excluded_sequences]
# channel-0 = [1, 2, "3-7", 9, 11],
# channel-1 = [4, 5, 6],
#
# Default: No filter
# excluded_sequences = []
# excluded_sequences = {}

# Enable or disable relaying of ICS31 Cross Chain Query packets.
# If this configuration is set to false, Hermes will skip ICS31
Expand Down
3 changes: 2 additions & 1 deletion crates/relayer-cli/src/chain_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use ibc_relayer::config::gas_multiplier::GasMultiplier;
use ibc_relayer::config::types::{MaxMsgNum, MaxTxSize, Memo, TrustThreshold};
use ibc_relayer::config::{default, AddressType, ChainConfig, EventSourceMode, GasPrice};
use ibc_relayer::keyring::Store;
use ibc_relayer::util::excluded_sequences::ExcludedSequences;

const MAX_HEALTHY_QUERY_RETRIES: u8 = 5;

Expand Down Expand Up @@ -167,7 +168,7 @@ where
extension_options: Vec::new(),
compat_mode: None,
clear_interval: None,
excluded_sequences: BTreeMap::new(),
excluded_sequences: ExcludedSequences::new(BTreeMap::new()),
allow_ccq: true,
}))
}
Expand Down
4 changes: 2 additions & 2 deletions crates/relayer/src/chain/cosmos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2567,8 +2567,8 @@ 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 !chain.config.excluded_sequences.map.is_empty() {
for (channel_id, seqs) in chain.config.excluded_sequences.map.iter() {
if !seqs.is_empty() {
warn!(
"chain '{chain_id}' will not clear packets on channel '{channel_id}' with sequences: {}. \
Expand Down
7 changes: 3 additions & 4 deletions crates/relayer/src/chain/cosmos/config.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
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, ChannelId};
use ibc_relayer_types::core::ics24_host::identifier::ChainId;

use crate::chain::cosmos::config::error::Error as ConfigError;
use crate::config::compat_mode::CompatMode;
Expand All @@ -20,6 +18,7 @@ use crate::config::{
};
use crate::config::{default, RefreshRate};
use crate::keyring::Store;
use crate::util::excluded_sequences::ExcludedSequences;

pub mod error;

Expand Down Expand Up @@ -149,7 +148,7 @@ pub struct CosmosSdkConfig {
pub compat_mode: Option<CompatMode>,
pub clear_interval: Option<u64>,
#[serde(default)]
pub excluded_sequences: BTreeMap<ChannelId, Vec<Sequence>>,
pub excluded_sequences: ExcludedSequences,

#[serde(default = "default::allow_ccq")]
pub allow_ccq: bool,
Expand Down
38 changes: 38 additions & 0 deletions crates/relayer/src/chain/cosmos/config/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use flex_error::define_error;
use flex_error::TraceError;

use ibc_relayer_types::core::ics02_client::trust_threshold::TrustThreshold;
use ibc_relayer_types::core::ics24_host::identifier::ChainId;

Expand Down Expand Up @@ -28,5 +30,41 @@ define_error! {
e.chain_id, e.gas_adjustment, e.gas_multiplier
)
},

ExpectedExcludedSequencesArray
|_| { "expected excluded_sequences to be an array of values" },

InvalidExcludedSequencesSeparator
{ separator: String }
|e| {
format!("excluded_sequences range `{}` is invalid, only '..', '..=' and '-' are valid separators", e.separator)
},

MissingStartExcludedSequence
{ entry: String }
|e| {
format!("missing the excluded sequence value before the separator in the entry `{}`", e.entry)
},

MissingEndExcludedSequence
{ entry: String }
|e| {
format!("missing the excluded sequence value after the separator in the entry `{}`", e.entry)
},

ParsingStartExcludedSequenceFailed
{ entry: String }
[ TraceError<std::num::ParseIntError> ]
|e| {
format!("Error parsing starting sequence as integer in entry `{}`", e.entry)
},


ParsingEndExcludedSequenceFailed
{ entry: String }
[ TraceError<std::num::ParseIntError> ]
|e| {
format!("Error parsing ending sequence as integer in entry `{}`", e.entry)
},
}
}
36 changes: 35 additions & 1 deletion crates/relayer/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,7 @@ impl ChainConfig {
match self {
Self::CosmosSdk(config) => config
.excluded_sequences
.map
.get(channel_id)
.map(|seqs| Cow::Borrowed(seqs.as_slice()))
.unwrap_or_else(|| Cow::Owned(Vec::new())),
Expand Down Expand Up @@ -844,7 +845,7 @@ impl From<CosmosConfigError> for Error {
mod tests {
use core::str::FromStr;

use super::{load, parse_gas_prices, store_writer};
use super::{load, parse_gas_prices, store_writer, ChainConfig};
use crate::config::GasPrice;
use test_log::test;

Expand Down Expand Up @@ -935,6 +936,39 @@ mod tests {
store_writer(&config, &mut buffer).unwrap();
}

#[test]
fn serialize_valid_excluded_sequences_config() {
let path = concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/config/fixtures/relayer_conf_example.toml"
);

let config = load(path).expect("could not parse config");

let excluded_sequences1 = match config.chains.first().unwrap() {
ChainConfig::CosmosSdk(chain_config) => chain_config.excluded_sequences.clone(),
};

let excluded_sequences2 = match config.chains.last().unwrap() {
ChainConfig::CosmosSdk(chain_config) => chain_config.excluded_sequences.clone(),
};

assert_eq!(excluded_sequences1, excluded_sequences2);

let mut buffer = Vec::new();
store_writer(&config, &mut buffer).unwrap();
}

#[test]
fn serialize_invalid_excluded_sequences_config() {
let path = concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/config/fixtures/relayer_conf_example_invalid_excluded_sequences.toml"
);

assert!(load(path).is_err());
}

#[test]
fn gas_price_from_str() {
let gp_original = GasPrice::new(10.0, "atom".to_owned());
Expand Down
1 change: 1 addition & 0 deletions crates/relayer/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod collate;
pub mod compat_mode;
pub mod debug_section;
pub mod diff;
pub mod excluded_sequences;
pub mod iter;
pub mod lock;
pub mod pretty;
Expand Down
113 changes: 113 additions & 0 deletions crates/relayer/src/util/excluded_sequences.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use serde::de::{Error, MapAccess, Visitor};
use serde::ser::SerializeMap;
use serde::Deserializer;
use serde::Serializer;
use serde_derive::Deserialize;
use serde_derive::Serialize;
use std::collections::BTreeMap;
use std::fmt;
use std::str::FromStr;

use ibc_relayer_types::core::ics04_channel::packet::Sequence;
use ibc_relayer_types::core::ics24_host::identifier::ChannelId;

use crate::chain::cosmos::config::error::Error as ConfigError;

#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct ExcludedSequences {
#[serde(
deserialize_with = "deserialize_excluded_sequences",
serialize_with = "serialize_excluded_sequences",
flatten
)]
pub map: BTreeMap<ChannelId, Vec<Sequence>>,
}

impl ExcludedSequences {
pub fn new(map: BTreeMap<ChannelId, Vec<Sequence>>) -> Self {
Self { map }
}
}

fn serialize_excluded_sequences<S>(
map: &BTreeMap<ChannelId, Vec<Sequence>>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_map(Some(map.len()))?;
for (k, v) in map {
seq.serialize_entry(k, v)?;
}
seq.end()
}

fn deserialize_excluded_sequences<'de, D>(
deserializer: D,
) -> Result<BTreeMap<ChannelId, Vec<Sequence>>, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_map(ExcludedSequencesVisitor)
}

struct ExcludedSequencesVisitor;

impl<'de> Visitor<'de> for ExcludedSequencesVisitor {
type Value = BTreeMap<ChannelId, Vec<Sequence>>;

fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("expected list of excluded sequences")
}

fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let mut map = BTreeMap::new();
while let Some((key, value)) = access.next_entry::<String, toml::Value>()? {
let channel_id = ChannelId::from_str(&key).map_err(|e| Error::custom(e.to_string()))?;
let sequences =
parse_sequence_range(&value).map_err(|e| Error::custom(e.to_string()))?;
map.insert(channel_id, sequences);
}
Ok(map)
}
}

fn parse_sequence_range(value: &toml::Value) -> Result<Vec<Sequence>, ConfigError> {
let mut res = Vec::new();
let sequences = value
.as_array()
.ok_or_else(ConfigError::expected_excluded_sequences_array)?;
for sequence in sequences.iter() {
if let Some(seq_str) = sequence.as_str() {
let (start, end) = get_start_and_end(seq_str)?;
for i in start..=end {
let seq = Sequence::from(i);
res.push(seq);
}
} else if let Some(seq) = sequence.as_integer() {
let seq = Sequence::from(seq as u64);
res.push(seq);
}
}
Ok(res)
}

fn get_start_and_end(value: &str) -> Result<(u64, u64), ConfigError> {
let split: Vec<&str> = value.split('-').collect();
let start: u64 = split
.first()
.ok_or_else(|| ConfigError::missing_start_excluded_sequence(value.to_string()))?
.parse()
.map_err(|e| ConfigError::parsing_start_excluded_sequence_failed(value.to_string(), e))?;
let end: u64 = split
.last()
.ok_or_else(|| ConfigError::missing_end_excluded_sequence(value.to_string()))?
.parse()
.map_err(|e| ConfigError::parsing_end_excluded_sequence_failed(value.to_string(), e))?;

Ok((start, end))
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ max_tx_size = 1048576
clock_drift = '5s'
trusting_period = '14days'
trust_threshold = { numerator = '1', denominator = '3' }
address_type = { derivation = 'cosmos' }

[chains.excluded_sequences]
channel-0 = [1, "3-5", 7, "9-12", 14, "17-19"]
channel-1 = ["3-6"]

[chains.packet_filter]
policy = 'allow'
Expand All @@ -62,4 +65,5 @@ gas_price = { price = 0.001, denom = 'stake' }
clock_drift = '5s'
trusting_period = '14days'
trust_threshold = { numerator = '1', denominator = '3' }
address_type = { derivation = 'ethermint', proto_type = { pk_type = '/injective.crypto.v1beta1.ethsecp256k1.PubKey' } }
address_type = { derivation = 'ethermint', proto_type = { pk_type = '/injective.crypto.v1beta1.ethsecp256k1.PubKey' } }
excluded_sequences = { 'channel-0' = [1, 3, 4, 5, 7, 9, 10, 11, 12, 14, 17, 18, 19], 'channel-1' = [3, 4, 5, 6] }
Loading

0 comments on commit 57e8065

Please sign in to comment.