|
|
@@ -29,8 +29,6 @@
|
|
|
|
|
|
use darkfi::Result;
|
|
|
use darkfi_contract_test_harness::{init_logger, Holder, TestHarness};
|
|
|
-use darkfi_sdk::crypto::BaseBlind;
|
|
|
-use rand::rngs::OsRng;
|
|
|
use tracing::info;
|
|
|
|
|
|
#[test]
|
|
|
@@ -38,395 +36,69 @@ fn mint_pay_swap() -> Result<()> {
|
|
|
smol::block_on(async {
|
|
|
init_logger();
|
|
|
|
|
|
- // Holders this test will use
|
|
|
- const HOLDERS: [Holder; 2] = [Holder::Alice, Holder::Bob];
|
|
|
+ use Holder::{Alice, Bob};
|
|
|
|
|
|
- // Some numbers we want to assert
|
|
|
const ALICE_INITIAL: u64 = 100;
|
|
|
const BOB_INITIAL: u64 = 200;
|
|
|
-
|
|
|
- // Alice = 50 ALICE
|
|
|
- // Bob = 200 BOB + 50 ALICE
|
|
|
- const ALICE_FIRST_SEND: u64 = ALICE_INITIAL - 50;
|
|
|
- // Alice = 50 ALICE + 180 BOB
|
|
|
- // Bob = 20 BOB + 50 ALICE
|
|
|
- const BOB_FIRST_SEND: u64 = BOB_INITIAL - 20;
|
|
|
-
|
|
|
- // Block height to verify against
|
|
|
- let current_block_height = 0;
|
|
|
-
|
|
|
- // Initialize harness
|
|
|
- let mut th = TestHarness::new(&HOLDERS, false).await?;
|
|
|
-
|
|
|
- info!(target: "money", "[Alice] ================================");
|
|
|
- info!(target: "money", "[Alice] Building token mint tx for Alice");
|
|
|
- info!(target: "money", "[Alice] ================================");
|
|
|
- let alice_token_blind = BaseBlind::random(&mut OsRng);
|
|
|
- let (mint_tx, mint_params, mint_auth_params, fee_params) = th
|
|
|
- .token_mint(
|
|
|
- ALICE_INITIAL,
|
|
|
- &Holder::Alice,
|
|
|
- &Holder::Alice,
|
|
|
- alice_token_blind,
|
|
|
- None,
|
|
|
- None,
|
|
|
- current_block_height,
|
|
|
- )
|
|
|
- .await?;
|
|
|
-
|
|
|
- for holder in &HOLDERS {
|
|
|
- info!(target: "money", "[{holder:?}] ==============================");
|
|
|
- info!(target: "money", "[{holder:?}] Executing Alice token mint tx");
|
|
|
- info!(target: "money", "[{holder:?}] ==============================");
|
|
|
- th.execute_token_mint_tx(
|
|
|
- holder,
|
|
|
- mint_tx.clone(),
|
|
|
- &mint_params,
|
|
|
- &mint_auth_params,
|
|
|
- &fee_params,
|
|
|
- current_block_height,
|
|
|
- true,
|
|
|
- )
|
|
|
- .await?;
|
|
|
- }
|
|
|
-
|
|
|
- th.assert_trees(&HOLDERS);
|
|
|
-
|
|
|
- info!(target: "money", "[Bob] ==============================");
|
|
|
- info!(target: "money", "[Bob] Building token mint tx for Bob");
|
|
|
- info!(target: "money", "[Bob] ==============================");
|
|
|
- let bob_token_blind = BaseBlind::random(&mut OsRng);
|
|
|
- let (mint_tx, mint_params, mint_auth_params, fee_params) = th
|
|
|
- .token_mint(
|
|
|
- BOB_INITIAL,
|
|
|
- &Holder::Bob,
|
|
|
- &Holder::Bob,
|
|
|
- bob_token_blind,
|
|
|
- None,
|
|
|
- None,
|
|
|
- current_block_height,
|
|
|
- )
|
|
|
- .await?;
|
|
|
-
|
|
|
- for holder in &HOLDERS {
|
|
|
- info!(target: "money", "[{holder:?}] ===========================");
|
|
|
- info!(target: "money", "[{holder:?}] Executing Bob token mint tx");
|
|
|
- info!(target: "money", "[{holder:?}] ===========================");
|
|
|
- th.execute_token_mint_tx(
|
|
|
- holder,
|
|
|
- mint_tx.clone(),
|
|
|
- &mint_params,
|
|
|
- &mint_auth_params,
|
|
|
- &fee_params,
|
|
|
- current_block_height,
|
|
|
- true,
|
|
|
- )
|
|
|
- .await?;
|
|
|
- }
|
|
|
-
|
|
|
- th.assert_trees(&HOLDERS);
|
|
|
-
|
|
|
- // Now Alice can send a little bit of funds to Bob
|
|
|
- info!(target: "money", "[Alice] ====================================================");
|
|
|
- info!(target: "money", "[Alice] Building Money::Transfer params for a payment to Bob");
|
|
|
- info!(target: "money", "[Alice] ====================================================");
|
|
|
- let mut alice_owncoins =
|
|
|
- th.holders.get(&Holder::Alice).unwrap().unspent_money_coins.clone();
|
|
|
- let alice_token_id = alice_owncoins[0].note.token_id;
|
|
|
-
|
|
|
- let (transfer_tx, (transfer_params, fee_params), spent_coins) = th
|
|
|
- .transfer(
|
|
|
- ALICE_FIRST_SEND,
|
|
|
- &Holder::Alice,
|
|
|
- &Holder::Bob,
|
|
|
- &alice_owncoins,
|
|
|
- alice_token_id,
|
|
|
- current_block_height,
|
|
|
- false,
|
|
|
- )
|
|
|
- .await?;
|
|
|
-
|
|
|
- // Validating transfer params
|
|
|
- assert!(transfer_params.inputs.len() == 1);
|
|
|
- assert!(transfer_params.outputs.len() == 2);
|
|
|
- assert!(spent_coins.len() == 1);
|
|
|
- alice_owncoins.retain(|x| x != &spent_coins[0]);
|
|
|
- assert!(alice_owncoins.is_empty());
|
|
|
-
|
|
|
- for holder in &HOLDERS {
|
|
|
- info!(target: "money", "[{holder:?}] ==============================");
|
|
|
- info!(target: "money", "[{holder:?}] Executing Alice2Bob payment tx");
|
|
|
- info!(target: "money", "[{holder:?}] ==============================");
|
|
|
- th.execute_transfer_tx(
|
|
|
- holder,
|
|
|
- transfer_tx.clone(),
|
|
|
- &transfer_params,
|
|
|
- &fee_params,
|
|
|
- current_block_height,
|
|
|
- true,
|
|
|
- )
|
|
|
- .await?;
|
|
|
- }
|
|
|
-
|
|
|
- // Alice should now have one OwnCoin with the change from the above transfer.
|
|
|
- // Bob should now have a new OwnCoin.
|
|
|
- let alice_owncoins = &th.holders.get(&Holder::Alice).unwrap().unspent_money_coins;
|
|
|
- let mut bob_owncoins = th.holders.get(&Holder::Bob).unwrap().unspent_money_coins.clone();
|
|
|
- assert!(alice_owncoins.len() == 1);
|
|
|
- assert!(bob_owncoins.len() == 2);
|
|
|
-
|
|
|
- th.assert_trees(&HOLDERS);
|
|
|
-
|
|
|
- // Bob can send a little bit to Alice as well
|
|
|
- info!(target: "money", "[Bob] ======================================================");
|
|
|
- info!(target: "money", "[Bob] Building Money::Transfer params for a payment to Alice");
|
|
|
- info!(target: "money", "[Bob] ======================================================");
|
|
|
- let bob_token_id = bob_owncoins[0].note.token_id;
|
|
|
- let mut bob_owncoins_tmp = bob_owncoins.clone();
|
|
|
- bob_owncoins_tmp.retain(|x| x.note.token_id == bob_token_id);
|
|
|
- let (transfer_tx, (transfer_params, fee_params), spent_coins) = th
|
|
|
- .transfer(
|
|
|
- BOB_FIRST_SEND,
|
|
|
- &Holder::Bob,
|
|
|
- &Holder::Alice,
|
|
|
- &bob_owncoins_tmp,
|
|
|
- bob_token_id,
|
|
|
- current_block_height,
|
|
|
- false,
|
|
|
- )
|
|
|
- .await?;
|
|
|
-
|
|
|
- // Validating transfer params
|
|
|
- assert!(transfer_params.inputs.len() == 1);
|
|
|
- assert!(transfer_params.outputs.len() == 2);
|
|
|
- assert!(spent_coins.len() == 1);
|
|
|
- bob_owncoins.retain(|x| x != &spent_coins[0]);
|
|
|
- assert!(bob_owncoins.len() == 1);
|
|
|
-
|
|
|
- for holder in &HOLDERS {
|
|
|
- info!(target: "money", "[{holder:?}] ==============================");
|
|
|
- info!(target: "money", "[{holder:?}] Executing Bob2Alice payment tx");
|
|
|
- info!(target: "money", "[{holder:?}] ==============================");
|
|
|
- th.execute_transfer_tx(
|
|
|
- holder,
|
|
|
- transfer_tx.clone(),
|
|
|
- &transfer_params,
|
|
|
- &fee_params,
|
|
|
- current_block_height,
|
|
|
- true,
|
|
|
- )
|
|
|
- .await?;
|
|
|
- }
|
|
|
-
|
|
|
- // Alice should now have two OwnCoins
|
|
|
- // Bob should have two with the change from the above tx
|
|
|
- let mut alice_owncoins =
|
|
|
- th.holders.get(&Holder::Alice).unwrap().unspent_money_coins.clone();
|
|
|
- let mut bob_owncoins = th.holders.get(&Holder::Bob).unwrap().unspent_money_coins.clone();
|
|
|
-
|
|
|
- assert!(alice_owncoins.len() == 2);
|
|
|
- assert!(bob_owncoins.len() == 2);
|
|
|
-
|
|
|
- assert!(alice_owncoins[0].note.value == ALICE_INITIAL - ALICE_FIRST_SEND);
|
|
|
- assert!(alice_owncoins[0].note.token_id == alice_token_id);
|
|
|
- assert!(alice_owncoins[1].note.value == BOB_FIRST_SEND);
|
|
|
- assert!(alice_owncoins[1].note.token_id == bob_token_id);
|
|
|
-
|
|
|
- assert!(bob_owncoins[0].note.value == ALICE_FIRST_SEND);
|
|
|
- assert!(bob_owncoins[0].note.token_id == alice_token_id);
|
|
|
- assert!(bob_owncoins[1].note.value == BOB_INITIAL - BOB_FIRST_SEND);
|
|
|
- assert!(bob_owncoins[1].note.token_id == bob_token_id);
|
|
|
-
|
|
|
- th.assert_trees(&HOLDERS);
|
|
|
-
|
|
|
- // Alice and Bob decide to swap back their tokens so Alice gets back her initial
|
|
|
- // tokens and Bob gets his.
|
|
|
- info!(target: "money", "[Alice, Bob] ================");
|
|
|
- info!(target: "money", "[Alice, Bob] Building OtcSwap");
|
|
|
- info!(target: "money", "[Alice, Bob] ================");
|
|
|
- let alice_oc = alice_owncoins[1].clone();
|
|
|
- alice_owncoins.remove(1);
|
|
|
- assert!(alice_owncoins.len() == 1);
|
|
|
- let bob_oc = bob_owncoins[0].clone();
|
|
|
- bob_owncoins.remove(0);
|
|
|
- assert!(bob_owncoins.len() == 1);
|
|
|
-
|
|
|
- let (otc_swap_tx, otc_swap_params, fee_params) = th
|
|
|
- .otc_swap(&Holder::Alice, &alice_oc, &Holder::Bob, &bob_oc, current_block_height)
|
|
|
- .await?;
|
|
|
-
|
|
|
- for holder in &HOLDERS {
|
|
|
- info!(target: "money", "[{holder:?}] ==========================");
|
|
|
- info!(target: "money", "[{holder:?}] Executing AliceBob swap tx");
|
|
|
- info!(target: "money", "[{holder:?}] ==========================");
|
|
|
- th.execute_otc_swap_tx(
|
|
|
- holder,
|
|
|
- otc_swap_tx.clone(),
|
|
|
- &otc_swap_params,
|
|
|
- &fee_params,
|
|
|
- current_block_height,
|
|
|
- true,
|
|
|
- )
|
|
|
- .await?;
|
|
|
- }
|
|
|
-
|
|
|
- // Alice should now have two OwnCoins with the same token ID (ALICE)
|
|
|
- let mut alice_owncoins =
|
|
|
- th.holders.get(&Holder::Alice).unwrap().unspent_money_coins.clone();
|
|
|
- let mut bob_owncoins = th.holders.get(&Holder::Bob).unwrap().unspent_money_coins.clone();
|
|
|
-
|
|
|
- assert!(alice_owncoins.len() == 2);
|
|
|
- assert!(alice_owncoins[0].note.token_id == alice_token_id);
|
|
|
- assert!(alice_owncoins[1].note.token_id == alice_token_id);
|
|
|
-
|
|
|
- // Same for Bob with BOB tokens
|
|
|
- assert!(bob_owncoins.len() == 2);
|
|
|
- assert!(bob_owncoins[0].note.token_id == bob_token_id);
|
|
|
- assert!(bob_owncoins[1].note.token_id == bob_token_id);
|
|
|
-
|
|
|
- th.assert_trees(&HOLDERS);
|
|
|
-
|
|
|
- // Now Alice will create a new coin for herself to combine the two owncoins.
|
|
|
- info!(target: "money", "[Alice] ======================================================");
|
|
|
- info!(target: "money", "[Alice] Building Money::Transfer params for a payment to Alice");
|
|
|
- info!(target: "money", "[Alice] ======================================================");
|
|
|
- let (tx, (params, fee_params), spent_coins) = th
|
|
|
- .transfer(
|
|
|
- ALICE_INITIAL,
|
|
|
- &Holder::Alice,
|
|
|
- &Holder::Alice,
|
|
|
- &alice_owncoins,
|
|
|
- alice_token_id,
|
|
|
- current_block_height,
|
|
|
- false,
|
|
|
- )
|
|
|
- .await?;
|
|
|
-
|
|
|
- for coin in spent_coins {
|
|
|
- alice_owncoins.retain(|x| x != &coin);
|
|
|
- }
|
|
|
- assert!(alice_owncoins.is_empty());
|
|
|
- assert!(params.inputs.len() == 2);
|
|
|
- assert!(params.outputs.len() == 1);
|
|
|
-
|
|
|
- for holder in &HOLDERS {
|
|
|
- info!(target: "money", "[{holder:?}] ================================");
|
|
|
- info!(target: "money", "[{holder:?}] Executing Alice2Alice payment tx");
|
|
|
- info!(target: "money", "[{holder:?}] ================================");
|
|
|
- th.execute_transfer_tx(
|
|
|
- holder,
|
|
|
- tx.clone(),
|
|
|
- ¶ms,
|
|
|
- &fee_params,
|
|
|
- current_block_height,
|
|
|
- true,
|
|
|
- )
|
|
|
- .await?;
|
|
|
- }
|
|
|
-
|
|
|
- th.assert_trees(&HOLDERS);
|
|
|
-
|
|
|
- // Alice should now have a single OwnCoin with her initial airdrop
|
|
|
- let alice_owncoins = th.holders.get(&Holder::Alice).unwrap().unspent_money_coins.clone();
|
|
|
- assert!(alice_owncoins.len() == 1);
|
|
|
- assert!(alice_owncoins[0].note.value == ALICE_INITIAL);
|
|
|
- assert!(alice_owncoins[0].note.token_id == alice_token_id);
|
|
|
-
|
|
|
- // Bob does the same
|
|
|
- info!(target: "money", "[Bob] ====================================================");
|
|
|
- info!(target: "money", "[Bob] Building Money::Transfer params for a payment to Bob");
|
|
|
- info!(target: "money", "[Bob] ====================================================");
|
|
|
- let (tx, (params, fee_params), spent_coins) = th
|
|
|
- .transfer(
|
|
|
- BOB_INITIAL,
|
|
|
- &Holder::Bob,
|
|
|
- &Holder::Bob,
|
|
|
- &bob_owncoins,
|
|
|
- bob_token_id,
|
|
|
- current_block_height,
|
|
|
- false,
|
|
|
- )
|
|
|
- .await?;
|
|
|
-
|
|
|
- for coin in spent_coins {
|
|
|
- bob_owncoins.retain(|x| x != &coin);
|
|
|
- }
|
|
|
- assert!(bob_owncoins.is_empty());
|
|
|
- assert!(params.inputs.len() == 2);
|
|
|
- assert!(params.outputs.len() == 1);
|
|
|
-
|
|
|
- for holder in &HOLDERS {
|
|
|
- info!(target: "money", "[{holder:?}] ============================");
|
|
|
- info!(target: "money", "[{holder:?}] Executing Bob2Bob payment tx");
|
|
|
- info!(target: "money", "[{holder:?}] ============================");
|
|
|
- th.execute_transfer_tx(
|
|
|
- holder,
|
|
|
- tx.clone(),
|
|
|
- ¶ms,
|
|
|
- &fee_params,
|
|
|
- current_block_height,
|
|
|
- true,
|
|
|
- )
|
|
|
- .await?;
|
|
|
- }
|
|
|
-
|
|
|
- th.assert_trees(&HOLDERS);
|
|
|
-
|
|
|
- // Bob should now have a single OwnCoin with his initial airdrop
|
|
|
- let bob_owncoins = th.holders.get(&Holder::Bob).unwrap().unspent_money_coins.clone();
|
|
|
- assert!(bob_owncoins.len() == 1);
|
|
|
- assert!(bob_owncoins[0].note.value == BOB_INITIAL);
|
|
|
- assert!(bob_owncoins[0].note.token_id == bob_token_id);
|
|
|
-
|
|
|
- // Now they decide to swap all of their tokens
|
|
|
- info!(target: "money", "[Alice, Bob] ================");
|
|
|
- info!(target: "money", "[Alice, Bob] Building OtcSwap");
|
|
|
- info!(target: "money", "[Alice, Bob] ================");
|
|
|
- let mut alice_owncoins =
|
|
|
- th.holders.get(&Holder::Alice).unwrap().unspent_money_coins.clone();
|
|
|
- let mut bob_owncoins = th.holders.get(&Holder::Bob).unwrap().unspent_money_coins.clone();
|
|
|
-
|
|
|
- let alice_oc = alice_owncoins[0].clone();
|
|
|
- alice_owncoins.remove(0);
|
|
|
- assert!(alice_owncoins.is_empty());
|
|
|
- let bob_oc = bob_owncoins[0].clone();
|
|
|
- bob_owncoins.remove(0);
|
|
|
- assert!(bob_owncoins.is_empty());
|
|
|
-
|
|
|
- let (otc_swap_tx, otc_swap_params, fee_params) = th
|
|
|
- .otc_swap(&Holder::Alice, &alice_oc, &Holder::Bob, &bob_oc, current_block_height)
|
|
|
- .await?;
|
|
|
-
|
|
|
- for holder in &HOLDERS {
|
|
|
- info!(target: "money", "[{holder:?}] ==========================");
|
|
|
- info!(target: "money", "[{holder:?}] Executing AliceBob swap tx");
|
|
|
- info!(target: "money", "[{holder:?}] ==========================");
|
|
|
- th.execute_otc_swap_tx(
|
|
|
- holder,
|
|
|
- otc_swap_tx.clone(),
|
|
|
- &otc_swap_params,
|
|
|
- &fee_params,
|
|
|
- current_block_height,
|
|
|
- true,
|
|
|
- )
|
|
|
- .await?;
|
|
|
- }
|
|
|
-
|
|
|
- assert_eq!(otc_swap_params.outputs.len(), 2);
|
|
|
-
|
|
|
- // Alice should now have Bob's BOB tokens
|
|
|
- let alice_owncoins = th.holders.get(&Holder::Alice).unwrap().unspent_money_coins.clone();
|
|
|
- assert!(alice_owncoins.len() == 1);
|
|
|
- assert!(alice_owncoins[0].note.value == BOB_INITIAL);
|
|
|
- assert!(alice_owncoins[0].note.token_id == bob_token_id);
|
|
|
-
|
|
|
- // And Bob should have Alice's ALICE tokens
|
|
|
- let bob_owncoins = th.holders.get(&Holder::Bob).unwrap().unspent_money_coins.clone();
|
|
|
- assert!(bob_owncoins.len() == 1);
|
|
|
- assert!(bob_owncoins[0].note.value == ALICE_INITIAL);
|
|
|
- assert!(bob_owncoins[0].note.token_id == alice_token_id);
|
|
|
-
|
|
|
- th.assert_trees(&HOLDERS);
|
|
|
+ const ALICE_SEND: u64 = 50;
|
|
|
+ const BOB_SEND: u64 = 180;
|
|
|
+ let block_height = 0;
|
|
|
+
|
|
|
+ let mut th = TestHarness::new(&[Alice, Bob], false).await?;
|
|
|
+
|
|
|
+ // Mint tokens for Alice and Bob
|
|
|
+ info!(target: "money", "Minting tokens for Alice and Bob");
|
|
|
+ let alice_token = th.token_mint_to_all(ALICE_INITIAL, &Alice, &Alice, block_height).await?;
|
|
|
+ let bob_token = th.token_mint_to_all(BOB_INITIAL, &Bob, &Bob, block_height).await?;
|
|
|
+
|
|
|
+ // Alice sends some tokens to Bob
|
|
|
+ info!(target: "money", "Alice sends {ALICE_SEND} to Bob");
|
|
|
+ th.transfer_to_all(ALICE_SEND, &Alice, &Bob, alice_token, block_height).await?;
|
|
|
+
|
|
|
+ assert_eq!(th.coins(&Alice).len(), 1); // change coin
|
|
|
+ assert_eq!(th.coins(&Bob).len(), 2); // original BOB + received ALICE
|
|
|
+ assert_eq!(th.balance(&Alice, alice_token), ALICE_INITIAL - ALICE_SEND);
|
|
|
+ assert_eq!(th.balance(&Bob, alice_token), ALICE_SEND);
|
|
|
+
|
|
|
+ // Bob sends some tokens to Alice
|
|
|
+ info!(target: "money", "Bob sends {BOB_SEND} to Alice");
|
|
|
+ th.transfer_to_all(BOB_SEND, &Bob, &Alice, bob_token, block_height).await?;
|
|
|
+
|
|
|
+ assert_eq!(th.balance(&Alice, alice_token), ALICE_INITIAL - ALICE_SEND);
|
|
|
+ assert_eq!(th.balance(&Alice, bob_token), BOB_SEND);
|
|
|
+ assert_eq!(th.balance(&Bob, alice_token), ALICE_SEND);
|
|
|
+ assert_eq!(th.balance(&Bob, bob_token), BOB_INITIAL - BOB_SEND);
|
|
|
+
|
|
|
+ // Alice and Bob swap back their foreign tokens
|
|
|
+ info!(target: "money", "Alice and Bob swap foreign tokens");
|
|
|
+ let alice_bob_coin = th.coins_by_token(&Alice, bob_token)[0].clone();
|
|
|
+ let bob_alice_coin = th.coins_by_token(&Bob, alice_token)[0].clone();
|
|
|
+ th.otc_swap_to_all(&Alice, &alice_bob_coin, &Bob, &bob_alice_coin, block_height).await?;
|
|
|
+
|
|
|
+ // Both now hold only their own token types
|
|
|
+ assert!(th.coins(&Alice).iter().all(|c| c.note.token_id == alice_token));
|
|
|
+ assert!(th.coins(&Bob).iter().all(|c| c.note.token_id == bob_token));
|
|
|
+
|
|
|
+ // Consolidate fragmented coins
|
|
|
+ info!(target: "money", "Consolidating coins");
|
|
|
+ th.consolidate_to_all(&Alice, alice_token, block_height).await?;
|
|
|
+ th.consolidate_to_all(&Bob, bob_token, block_height).await?;
|
|
|
+
|
|
|
+ assert_eq!(th.coins(&Alice).len(), 1);
|
|
|
+ assert_eq!(th.coins(&Alice)[0].note.value, ALICE_INITIAL);
|
|
|
+ assert_eq!(th.coins(&Bob).len(), 1);
|
|
|
+ assert_eq!(th.coins(&Bob)[0].note.value, BOB_INITIAL);
|
|
|
+
|
|
|
+ // Final swap: Alice and Bob exchange everything
|
|
|
+ info!(target: "money", "Final swap: Alice and Bob exchange all tokens");
|
|
|
+ let alice_coin = th.coins(&Alice)[0].clone();
|
|
|
+ let bob_coin = th.coins(&Bob)[0].clone();
|
|
|
+ th.otc_swap_to_all(&Alice, &alice_coin, &Bob, &bob_coin, block_height).await?;
|
|
|
+
|
|
|
+ assert_eq!(th.balance(&Alice, bob_token), BOB_INITIAL);
|
|
|
+ assert_eq!(th.balance(&Bob, alice_token), ALICE_INITIAL);
|
|
|
+ assert_eq!(th.coins(&Alice).len(), 1);
|
|
|
+ assert_eq!(th.coins(&Bob).len(), 1);
|
|
|
|
|
|
// Thanks for reading
|
|
|
Ok(())
|