1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//! ## Memo Test
//!
//! This walkthrough illustrates an integration test that utilizes a non-empty
//! struct as test input. This test asserts that the process of transferring
//! IBC messages preserves the `memo` field. For the purposes of this explanation,
//! the `memo` field is nothing more than a `String` field for carrying along
//! some arbitrary metadata as part of the transaction.
//!
//! The test in most of its entirety (some parts omitted for brevity) looks like this:
//!
//! ```no_run
//! # use serde_json as json;
//! # use ibc_relayer::config::{types::Memo, Config, ChainConfig};
//! # 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};
//!
//!
//! #[test]
//! fn test_memo() -> Result<(), Error> {
//! let memo = Memo::new(random_string()).unwrap();
//! let test = MemoTest { memo };
//! run_binary_channel_test(&test)
//! }
//!
//! pub struct MemoTest {
//! memo: Memo,
//! }
//!
//! impl TestOverrides for MemoTest {
//! fn modify_relayer_config(&self, config: &mut Config) {
//! for mut chain in config.chains.iter_mut() {
//! match chain {
//! ChainConfig::CosmosSdk(chain_config) | ChainConfig::Namada(chain_config) => {
//! chain_config.memo_prefix = self.memo.clone();
//! }
//! }
//! }
//! }
//! }
//!
//! impl BinaryChannelTest for MemoTest {
//! fn run<ChainA: ChainHandle, ChainB: ChainHandle>(
//! &self,
//! _config: &TestConfig,
//! _relayer: RelayerDriver,
//! chains: ConnectedChains<ChainA, ChainB>,
//! channel: ConnectedChannel<ChainA, ChainB>,
//! ) -> Result<(), Error> {
//! 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(
//! &chains.node_b.chain_driver().value().chain_type,
//! &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, self.memo.as_str())?;
//!
//! Ok(())
//! }
//! }
//!
//! # fn assert_tx_memo_equals(tx_info: &json::Value, expected_memo: &str) -> Result<(), Error> {
//! # debug!("comparing memo field from json value {}", tx_info);
//! #
//! # let memo_field = &tx_info["txs"][0]["tx"]["body"]["memo"];
//! #
//! # info!("memo field value: {}", memo_field);
//! #
//! # let memo_str = memo_field
//! # .as_str()
//! # .ok_or_else(|| eyre!("expect memo string field to be present in JSON"))?;
//! #
//! # assert_eq!(memo_str, expected_memo);
//! #
//! # Ok(())
//! # }
//! ```
//!
//! This test runs initializes a `MemoTest` struct with a random string
//! in the `memo` field, then calls the `run_binary_channel_test` function
//! with it. The `TestOverrides` trait is implemented in order to set the
//! `memo_prefix` configuration value on the chains that are initialized
//! over the course of the test.
//!
//! At a high level, this test performs an IBC token transfer operation
//! from chain A to chain B. Once chain B has received the transaction
//! that chain A initialized, the test asserts that the value of the
//! memo string is indeed what we expected.
//!
//! The first two lines of the `run` function perform some necessary
//! setup for performing an IBC token transfer, namely fetching the
//! coin denomination of chain A as well as generating a random amount
//! of that denomination that will be sent to chain B. It then calls
//! the `ibc_token_transfer` function to generate a transaction with
//! this information, including the memo string that was generated
//! earlier, and sends it to chain B.
//!
//! Next, the `derive_ibc_denom` function is called in order to
//! calculate the appropriate amount of chain B's coin denomination
//! based on chain A's denomination and how much of that denomination
//! was sent over the transaction so that chain B can represent the
//! transferred value.
//!
//! The `assert_eventual_wallet_amount` function is then called on
//! chain B in order to confirm that the transaction was indeed
//! received by checking that chain B's wallet amount reflects the
//! expected updated value. The `query_recipient_transactions`
//! method is then called to fetch the memo value from the transaction
//! so that we can confirm that its value is indeed what we expect.
//!
//! You can find the file containing this test at `tools/integration-test/src/tests/memo.rs`.