Skip to content

Commit

Permalink
test/trans_token: more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
tzemanovic committed Sep 20, 2024
1 parent a65549f commit 5d51203
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 5 deletions.
6 changes: 6 additions & 0 deletions crates/core/src/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,12 @@ pub mod testing {
.expect("The token address decoding shouldn't fail")
}

/// A sampled established address for tests
pub fn established_address_5() -> Address {
Address::decode("tnam1qyftuue8fq25ezm0s8vj75d3qz759r2225ug7hll")
.expect("The token address decoding shouldn't fail")
}

/// Generate an arbitrary [`Address`] (established or implicit).
pub fn arb_non_internal_address() -> impl Strategy<Value = Address> {
prop_oneof![
Expand Down
135 changes: 130 additions & 5 deletions crates/trans_token/src/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,15 +260,41 @@ mod test {

amount in arb_amount(),
) {
test_valid_transfer_tx_aux(src, dest, token, amount)
// Test via `fn transfer`
test_valid_transfer_tx_aux(src.clone(), dest.clone(), token.clone(), amount, || {
transfer(ctx(), &src, &dest, &token, amount, EVENT_DESC).unwrap();
});

// Clean-up tx env before running next test
let _old_env = tx_host_env::take();

// Test via `fn multi_transfer`
test_valid_transfer_tx_aux(src.clone(), dest.clone(), token.clone(), amount, || {
let sources =
BTreeMap::from_iter([((src.clone(), token.clone()), amount)]);

let targets =
BTreeMap::from_iter([((dest.clone(), token.clone()), amount)]);

let debited_accounts =
multi_transfer(ctx(), sources, targets, EVENT_DESC).unwrap();

if amount.is_zero() {
assert!(debited_accounts.is_empty());
} else {
assert_eq!(debited_accounts.len(), 1);
assert!(debited_accounts.contains(&src));
}
});
}
}

fn test_valid_transfer_tx_aux(
fn test_valid_transfer_tx_aux<F: FnOnce()>(
src: Address,
dest: Address,
token: Address,
amount: Amount,
apply_transfer: F,
) {
tx_host_env::init();

Expand All @@ -278,7 +304,7 @@ mod test {
});
assert_eq!(read_balance(ctx(), &token, &src).unwrap(), amount);

transfer(ctx(), &src, &dest, &token, amount, EVENT_DESC).unwrap();
apply_transfer();

// Dest received the amount
assert_eq!(read_balance(ctx(), &token, &dest).unwrap(), amount);
Expand Down Expand Up @@ -460,6 +486,97 @@ mod test {
});
}

/// Test a 3-way transfer between three participants:
///
/// 1. (p1, token1, amount1) -> p2
/// 2. (p2, token1, amount2) -> p3
/// 3. (p3, token2, amount3) -> p1
#[test]
fn test_three_way_multi_transfer_tx() {
tx_host_env::init();

let p1 = address::testing::established_address_1();
let p2 = address::testing::established_address_2();
let p3 = address::testing::established_address_3();
let token1 = address::testing::established_address_4();
let token2 = address::testing::established_address_5();
let amount1 = token::Amount::native_whole(10);
let amount2 = token::Amount::native_whole(3);
let amount3 = token::Amount::native_whole(90);

tx_host_env::with(|tx_env| {
tx_env.spawn_accounts([&p1, &p2, &p3, &token1, &token2]);
tx_env.credit_tokens(&p1, &token1, amount1);
tx_env.credit_tokens(&p3, &token2, amount3);
});
assert_eq!(read_balance(ctx(), &token1, &p1).unwrap(), amount1);
assert_eq!(read_balance(ctx(), &token1, &p2).unwrap(), Amount::zero());
assert_eq!(read_balance(ctx(), &token1, &p3).unwrap(), Amount::zero());
assert_eq!(read_balance(ctx(), &token2, &p1).unwrap(), Amount::zero());
assert_eq!(read_balance(ctx(), &token2, &p2).unwrap(), Amount::zero());
assert_eq!(read_balance(ctx(), &token2, &p3).unwrap(), amount3);

let sources = BTreeMap::from_iter([
((p1.clone(), token1.clone()), amount1),
((p2.clone(), token1.clone()), amount2),
((p3.clone(), token2.clone()), amount3),
]);

let targets = BTreeMap::from_iter([
((p2.clone(), token1.clone()), amount1),
((p3.clone(), token1.clone()), amount2),
((p1.clone(), token2.clone()), amount3),
]);

let debited_accounts =
multi_transfer(ctx(), sources, targets, EVENT_DESC).unwrap();

// p2 is not debited as it received more of token1 than it spent
assert_eq!(debited_accounts.len(), 2);
assert!(debited_accounts.contains(&p1));
assert!(debited_accounts.contains(&p3));

// p1 spent all token1
assert_eq!(read_balance(ctx(), &token1, &p1).unwrap(), Amount::zero());
// p1 received token2
assert_eq!(read_balance(ctx(), &token2, &p1).unwrap(), amount3);

// p2 received amount1 and spent amount2 of token1
assert_eq!(
read_balance(ctx(), &token1, &p2).unwrap(),
amount1 - amount2
);
// p2 doesn't have any token2
assert_eq!(read_balance(ctx(), &token2, &p2).unwrap(), Amount::zero());

// p3 received token1
assert_eq!(read_balance(ctx(), &token1, &p3).unwrap(), amount2);
// p3 spent token2
assert_eq!(read_balance(ctx(), &token2, &p3).unwrap(), Amount::zero());

tx_host_env::with(|tx_env| {
// All parties should always verify
assert!(tx_env.verifiers.contains(&p1));
assert!(tx_env.verifiers.contains(&p2));
assert!(tx_env.verifiers.contains(&p3));
});

// The transfer must emit an event
tx_host_env::with(|tx_env| {
let events: Vec<_> = tx_env
.state
.write_log()
.get_events_of::<TokenEvent>()
.collect();
assert_eq!(events.len(), 1);
let event = events[0].clone();
assert_eq!(event.level(), &EventLevel::Tx);
assert_eq!(event.kind(), &crate::event::types::TRANSFER);

dbg!(event.into_attributes());
})
}

#[test]
fn test_multi_transfer_to_self_is_no_op() {
tx_host_env::init();
Expand All @@ -485,7 +602,11 @@ mod test {
let targets =
BTreeMap::from_iter([((addr.clone(), token.clone()), pre_balance)]);

multi_transfer(ctx(), sources, targets, EVENT_DESC).unwrap();
let debited_accounts =
multi_transfer(ctx(), sources, targets, EVENT_DESC).unwrap();

// No account has been debited
assert!(debited_accounts.is_empty());

// Balance is the same
let post_balance_check = read_balance(ctx(), &token, &addr).unwrap();
Expand Down Expand Up @@ -528,7 +649,11 @@ mod test {
let targets =
BTreeMap::from_iter([((src.clone(), token.clone()), amount)]);

multi_transfer(ctx(), sources, targets, EVENT_DESC).unwrap();
let debited_accounts =
multi_transfer(ctx(), sources, targets, EVENT_DESC).unwrap();

// No account has been debited
assert!(debited_accounts.is_empty());

// Src balance is still the same
assert_eq!(read_balance(ctx(), &token, &src).unwrap(), src_balance);
Expand Down

0 comments on commit 5d51203

Please sign in to comment.