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
//! ## 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};
//! # 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() {
//!             chain.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(
//!             &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`.