diff --git a/.changelog/unreleased/features/ibc-relayer/3811-relayer-memo-overwrite.md b/.changelog/unreleased/features/ibc-relayer/3811-relayer-memo-overwrite.md new file mode 100644 index 0000000000..fcd1a840d3 --- /dev/null +++ b/.changelog/unreleased/features/ibc-relayer/3811-relayer-memo-overwrite.md @@ -0,0 +1,3 @@ +- Add a per-chain configuration `memo_overwrite` allowing users + to overwite the relayer memo used for each transaction + ([\#3811](https://github.com/informalsystems/hermes/issues/3811)) \ No newline at end of file diff --git a/config.toml b/config.toml index f45e2289cf..63c5881060 100644 --- a/config.toml +++ b/config.toml @@ -369,6 +369,13 @@ trust_threshold = '2/3' # operational debugging information, e.g., relayer build version. memo_prefix = '' +# If this is set to a string, it will overwrite the memo used by Hermes for each transaction +# it submits to this chain. +# Default: not set. +# This is used for chains which have a very small character limit for the memo, +# and the additional information appended by Hermes would overflow that limit. +# memo_overwrite = '' + # This section specifies the filters for policy based relaying. # # Default: no policy / filters, allow all packets on all channels. diff --git a/crates/relayer-cli/src/chain_registry.rs b/crates/relayer-cli/src/chain_registry.rs index 04b28f21c6..93f68821b1 100644 --- a/crates/relayer-cli/src/chain_registry.rs +++ b/crates/relayer-cli/src/chain_registry.rs @@ -160,6 +160,7 @@ where client_refresh_rate: default::client_refresh_rate(), ccv_consumer_chain: false, memo_prefix: Memo::default(), + memo_overwrite: None, proof_specs: Default::default(), trust_threshold: TrustThreshold::default(), gas_price: GasPrice { diff --git a/crates/relayer-cli/src/commands.rs b/crates/relayer-cli/src/commands.rs index 60879018f5..e922061029 100644 --- a/crates/relayer-cli/src/commands.rs +++ b/crates/relayer-cli/src/commands.rs @@ -151,7 +151,11 @@ impl Configurable for CliCmd { for ccfg in config.chains.iter_mut() { #[allow(irrefutable_let_patterns)] if let ChainConfig::CosmosSdk(ref mut cosmos_ccfg) = ccfg { - cosmos_ccfg.memo_prefix.apply_suffix(&suffix); + if let Some(memo) = &cosmos_ccfg.memo_overwrite { + cosmos_ccfg.memo_prefix = memo.clone(); + } else { + cosmos_ccfg.memo_prefix.apply_suffix(&suffix); + } } } diff --git a/crates/relayer/src/chain/cosmos/config.rs b/crates/relayer/src/chain/cosmos/config.rs index 57314bccf5..a81ede704a 100644 --- a/crates/relayer/src/chain/cosmos/config.rs +++ b/crates/relayer/src/chain/cosmos/config.rs @@ -106,6 +106,9 @@ pub struct CosmosSdkConfig { #[serde(default)] pub memo_prefix: Memo, + #[serde(default)] + pub memo_overwrite: Option, + // This is an undocumented and hidden config to make the relayer wait for // DeliverTX before sending the next transaction when sending messages in // multiple batches. We will instruct relayer operators to turn this on diff --git a/tools/integration-test/src/tests/memo.rs b/tools/integration-test/src/tests/memo.rs index 2b44b90d48..52de0c5602 100644 --- a/tools/integration-test/src/tests/memo.rs +++ b/tools/integration-test/src/tests/memo.rs @@ -12,6 +12,8 @@ use ibc_test_framework::ibc::denom::derive_ibc_denom; use ibc_test_framework::prelude::*; use ibc_test_framework::util::random::{random_string, random_u128_range}; +const OVERWRITE_MEMO: &str = "Overwritten memo"; + #[test] fn test_memo() -> Result<(), Error> { let memo = Memo::new(random_string()).unwrap(); @@ -19,6 +21,13 @@ fn test_memo() -> Result<(), Error> { run_binary_channel_test(&test) } +#[test] +fn test_memo_overwrite() -> Result<(), Error> { + let memo = Memo::new(random_string()).unwrap(); + let test = MemoTest { memo }; + run_binary_channel_test(&test) +} + pub struct MemoTest { memo: Memo, } @@ -82,6 +91,70 @@ impl BinaryChannelTest for MemoTest { } } +pub struct MemoOverwriteTest { + memo: Memo, +} + +impl TestOverrides for MemoOverwriteTest { + fn modify_relayer_config(&self, config: &mut Config) { + for chain in config.chains.iter_mut() { + match chain { + ChainConfig::CosmosSdk(chain_config) => { + chain_config.memo_prefix = self.memo.clone(); + chain_config.memo_overwrite = Some(Memo::new(OVERWRITE_MEMO).unwrap()) + } + } + } + } +} + +impl BinaryChannelTest for MemoOverwriteTest { + fn run( + &self, + _config: &TestConfig, + _relayer: RelayerDriver, + chains: ConnectedChains, + channel: ConnectedChannel, + ) -> Result<(), Error> { + info!( + "testing IBC transfer with memo configured: \"{}\"", + self.memo + ); + + let denom_a = chains.node_a.denom(); + + let a_to_b_amount = random_u128_range(1000, 5000); + + chains.node_a.chain_driver().ibc_transfer_token( + &channel.port_a.as_ref(), + &channel.channel_id_a.as_ref(), + &chains.node_a.wallets().user1(), + &chains.node_b.wallets().user1().address(), + &denom_a.with_amount(a_to_b_amount).as_ref(), + )?; + + let denom_b = derive_ibc_denom( + &channel.port_b.as_ref(), + &channel.channel_id_b.as_ref(), + &denom_a, + )?; + + chains.node_b.chain_driver().assert_eventual_wallet_amount( + &chains.node_b.wallets().user1().address(), + &denom_b.with_amount(a_to_b_amount).as_ref(), + )?; + + let tx_info = chains + .node_b + .chain_driver() + .query_recipient_transactions(&chains.node_b.wallets().user1().address())?; + + assert_tx_memo_equals(&tx_info, OVERWRITE_MEMO)?; + + Ok(()) + } +} + fn assert_tx_memo_equals(tx_info: &json::Value, expected_memo: &str) -> Result<(), Error> { debug!("comparing memo field from json value {}", tx_info); diff --git a/tools/test-framework/src/types/single/node.rs b/tools/test-framework/src/types/single/node.rs index 9d4e7ea280..c898322da0 100644 --- a/tools/test-framework/src/types/single/node.rs +++ b/tools/test-framework/src/types/single/node.rs @@ -191,6 +191,7 @@ impl FullNode { packet_filter: Default::default(), address_type: chain_type.address_type(), memo_prefix: Default::default(), + memo_overwrite: None, proof_specs: Default::default(), extension_options: Default::default(), sequential_batch_tx: false,