iota_cluster_test/test_case/
coin_merge_split_test.rs1use async_trait::async_trait;
6use iota_json_rpc_types::{IotaTransactionBlockEffectsAPI, IotaTransactionBlockResponse};
7use iota_types::{
8 base_types::{IotaAddress, ObjectID},
9 iota_serde::BigInt,
10 object::Owner,
11};
12use jsonrpsee::rpc_params;
13use tracing::{debug, info};
14
15use crate::{TestCaseImpl, TestContext, helper::ObjectChecker};
16
17pub struct CoinMergeSplitTest;
18
19#[async_trait]
20impl TestCaseImpl for CoinMergeSplitTest {
21 fn name(&self) -> &'static str {
22 "CoinMergeSplit"
23 }
24
25 fn description(&self) -> &'static str {
26 "Test merge and split IOTA coins"
27 }
28
29 async fn run(&self, ctx: &mut TestContext) -> Result<(), anyhow::Error> {
30 let mut iota_objs = ctx.get_iota_from_faucet(Some(1)).await;
31 let gas_obj = iota_objs.swap_remove(0);
32
33 let signer = ctx.get_wallet_address();
34 let mut iota_objs_2 = ctx.get_iota_from_faucet(Some(1)).await;
35
36 let primary_coin = iota_objs_2.swap_remove(0);
37 let primary_coin_id = *primary_coin.id();
38 let original_value = primary_coin.value();
39
40 info!("Testing coin split.");
42 let amounts = vec![1.into(), ((original_value - 2) / 2).into()];
43
44 let response =
45 Self::split_coin(ctx, signer, *primary_coin.id(), amounts, *gas_obj.id()).await;
46 let tx_digest = response.digest;
47 let new_coins = response.effects.as_ref().unwrap().created();
48
49 ctx.let_fullnode_sync(vec![tx_digest], 5).await;
51
52 let _ = futures::future::join_all(
53 new_coins
54 .iter()
55 .map(|coin_ref| {
56 ObjectChecker::new(coin_ref.reference.object_id)
57 .owner(Owner::AddressOwner(signer))
58 .check_into_gas_coin(ctx.get_fullnode_client())
59 })
60 .collect::<Vec<_>>(),
61 )
62 .await;
63
64 info!("Testing coin merge.");
66 let mut coins_merged = Vec::new();
67 let mut txes = Vec::new();
68 for new_coin in new_coins {
71 let coin_to_merge = new_coin.reference.object_id;
72 debug!(
73 "Merging coin {} back to {}.",
74 coin_to_merge, primary_coin_id
75 );
76 let response =
77 Self::merge_coin(ctx, signer, primary_coin_id, coin_to_merge, *gas_obj.id()).await;
78 debug!("Verifying the merged coin {} is deleted.", coin_to_merge);
79 coins_merged.push(coin_to_merge);
80 txes.push(response.digest);
81 }
82
83 ctx.let_fullnode_sync(txes, 5).await;
85
86 let _ = futures::future::join_all(
87 coins_merged
88 .iter()
89 .map(|obj_id| {
90 ObjectChecker::new(*obj_id)
91 .owner(Owner::AddressOwner(signer))
92 .deleted()
93 .check(ctx.get_fullnode_client())
94 })
95 .collect::<Vec<_>>(),
96 )
97 .await
98 .into_iter()
99 .collect::<Vec<_>>();
100
101 debug!(
103 "Verifying owner still owns the primary coin {}",
104 *primary_coin.id()
105 );
106 let primary_after_merge = ObjectChecker::new(primary_coin_id)
107 .owner(Owner::AddressOwner(ctx.get_wallet_address()))
108 .check_into_gas_coin(ctx.get_fullnode_client())
109 .await;
110 assert_eq!(
111 primary_after_merge.value(),
112 original_value,
113 "Split-then-merge yields unexpected coin value, expect {}, got {}",
114 original_value,
115 primary_after_merge.value(),
116 );
117 Ok(())
118 }
119}
120
121impl CoinMergeSplitTest {
122 async fn merge_coin(
123 ctx: &TestContext,
124 signer: IotaAddress,
125 primary_coin: ObjectID,
126 coin_to_merge: ObjectID,
127 gas_obj_id: ObjectID,
128 ) -> IotaTransactionBlockResponse {
129 let params = rpc_params![
130 signer,
131 primary_coin,
132 coin_to_merge,
133 Some(gas_obj_id),
134 (20_000_000).to_string()
135 ];
136
137 let data = ctx
138 .build_transaction_remotely("unsafe_mergeCoins", params)
139 .await
140 .unwrap();
141
142 ctx.sign_and_execute(data, "coin merge").await
143 }
144
145 async fn split_coin(
146 ctx: &TestContext,
147 signer: IotaAddress,
148 primary_coin: ObjectID,
149 amounts: Vec<BigInt<u64>>,
150 gas_obj_id: ObjectID,
151 ) -> IotaTransactionBlockResponse {
152 let params = rpc_params![
153 signer,
154 primary_coin,
155 amounts,
156 Some(gas_obj_id),
157 (20_000_000).to_string()
158 ];
159
160 let data = ctx
161 .build_transaction_remotely("unsafe_splitCoin", params)
162 .await
163 .unwrap();
164
165 ctx.sign_and_execute(data, "coin merge").await
166 }
167}