use zcash_client_backend::data_api::transparent_ledger::{
DetailCompleteness, FeeState, HistoryClassification,
};
use zcash_primitives::transaction::Transaction;
use super::public_fixtures::*;
use super::*;
fn movement(st: &State, account: AccountUuid, tx: &Transaction) -> (i64, i64, i64) {
conn(st)
.query_row(
"SELECT account_balance_delta, total_spent, total_received FROM v_transactions
WHERE txid = ?1 AND account_uuid = ?2",
rusqlite::params![tx.txid().as_ref(), account.expose_uuid().as_bytes()],
|row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)),
)
.unwrap()
}
fn send_with_change(st: &State, account: AccountUuid) -> (Transaction, Transaction) {
let parent = funding(0xa0, external_of(st, account), 1_000_000);
let send = transaction(
vec![outpoint(&parent, 0)],
vec![(EXTERNAL, 600_000), (internal_of(st, account), 390_000)],
);
(parent, send)
}
#[test]
fn a_send_with_own_change_moves_the_account_once() {
let (mut st, accounts) = public_wallet(0);
let account = accounts[0];
let (parent, send) = send_with_change(&st, account);
store(&mut st, &parent);
store(&mut st, &send);
assert_eq!(
sent_outputs(&st, &send),
vec![
(account, 0, None, 600_000),
(account, 1, Some(account), 390_000)
]
);
assert_eq!(
movement(&st, account, &send),
(-610_000, 1_000_000, 390_000)
);
}
#[test]
fn a_transfer_to_another_account_moves_each_account_once() {
let (mut st, accounts) = public_wallet(1);
let (sender, recipient) = (accounts[0], accounts[1]);
let parent = funding(0xa2, external_of(&st, sender), 1_000_000);
let transfer = transaction(
vec![outpoint(&parent, 0)],
vec![
(external_of(&st, recipient), 400_000),
(internal_of(&st, sender), 590_000),
],
);
store(&mut st, &parent);
store(&mut st, &transfer);
assert_eq!(
sent_outputs(&st, &transfer),
vec![
(sender, 0, Some(recipient), 400_000),
(sender, 1, Some(sender), 590_000)
]
);
assert_eq!(
movement(&st, sender, &transfer),
(-410_000, 1_000_000, 590_000)
);
assert_eq!(movement(&st, recipient, &transfer), (400_000, 0, 400_000));
}
fn reading(
st: &State,
account: AccountUuid,
tx: &Transaction,
) -> (HistoryClassification, DetailCompleteness, FeeState) {
let entry = history(st, account, tx);
(entry.classification, entry.payment_details, entry.fee)
}
fn send_paying_self_and_other(st: &State, account: AccountUuid) -> (Transaction, Transaction) {
let parent = funding(0xb0, external_of(st, account), 1_000_000);
let own = derived(st, account, TransparentKeyScope::EXTERNAL, 1);
let send = transaction(
vec![outpoint(&parent, 0)],
vec![(EXTERNAL, 600_000), (own, 390_000)],
);
(parent, send)
}
fn assert_attributed_send(st: &State, account: AccountUuid, send: &Transaction) {
assert_eq!(
sent_outputs(st, send),
vec![
(account, 0, None, 600_000),
(account, 1, Some(account), 390_000)
]
);
assert_eq!(movement(st, account, send), (-610_000, 1_000_000, 390_000));
}
fn assert_reconstructed(st: &State, account: AccountUuid, send: &Transaction) {
assert_eq!(
reading(st, account, send),
(
HistoryClassification::Reconstructed,
DetailCompleteness::Complete,
FeeState::Known(zat(10_000))
)
);
}
#[test]
fn a_send_stored_before_its_input_is_attributed_once_the_input_is_stored() {
let (mut st, accounts) = public_wallet(0);
let account = accounts[0];
let (parent, send) = send_paying_self_and_other(&st, account);
store(&mut st, &send);
assert_eq!(sent_outputs(&st, &send), vec![]);
assert_eq!(
reading(&st, account, &send).0,
HistoryClassification::Provisional
);
store(&mut st, &parent);
assert_attributed_send(&st, account, &send);
assert_reconstructed(&st, account, &send);
}
#[test]
fn a_send_stored_before_its_input_is_attributed_once_the_input_is_a_received_utxo() {
let (mut st, accounts) = public_wallet(0);
let account = accounts[0];
let (parent, send) = send_paying_self_and_other(&st, account);
store(&mut st, &send);
let height = st.wallet().chain_height().unwrap().unwrap();
let utxo = zcash_client_backend::wallet::WalletTransparentOutput::from_parts(
outpoint(&parent, 0),
parent.transparent_bundle().unwrap().vout[0].clone(),
Some(height),
Some(account),
Some(TransparentKeyScope::EXTERNAL),
None,
)
.unwrap();
st.wallet_mut()
.db_mut()
.put_received_transparent_utxo(&utxo)
.unwrap();
assert_attributed_send(&st, account, &send);
assert_eq!(
reading(&st, account, &send).0,
HistoryClassification::Provisional
);
store(&mut st, &parent);
assert_attributed_send(&st, account, &send);
assert_reconstructed(&st, account, &send);
}
#[test]
fn a_send_an_outside_party_helped_fund_stays_unattributed() {
let (mut st, accounts) = public_wallet(0);
let account = accounts[0];
let parent = funding(0xb2, external_of(&st, account), 1_000_000);
let own = derived(&st, account, TransparentKeyScope::EXTERNAL, 1);
let send = transaction(
vec![outpoint(&parent, 0), OutPoint::new([0xb3; 32], 0)],
vec![(EXTERNAL, 1_600_000), (own, 390_000)],
);
store(&mut st, &send);
store(&mut st, &parent);
assert_eq!(sent_outputs(&st, &send), vec![]);
let (classification, details, _) = reading(&st, account, &send);
assert_eq!(classification, HistoryClassification::Provisional);
assert_eq!(details, DetailCompleteness::Incomplete);
}
#[test]
fn a_send_another_account_helped_fund_stays_unattributed() {
let (mut st, accounts) = public_wallet(1);
let (first, second) = (accounts[0], accounts[1]);
let first_parent = funding(0xb4, external_of(&st, first), 1_000_000);
let second_parent = funding(0xb5, external_of(&st, second), 1_000_000);
let send = transaction(
vec![outpoint(&first_parent, 0), outpoint(&second_parent, 0)],
vec![(EXTERNAL, 1_600_000), (internal_of(&st, first), 390_000)],
);
store(&mut st, &send);
store(&mut st, &first_parent);
store(&mut st, &second_parent);
assert_eq!(sent_outputs(&st, &send), vec![]);
for account in [first, second] {
assert_eq!(
reading(&st, account, &send).0,
HistoryClassification::Provisional
);
}
}
#[test]
fn a_send_paying_another_account_records_the_transfer_once_attributed() {
let (mut st, accounts) = public_wallet(1);
let (sender, recipient) = (accounts[0], accounts[1]);
let parent = funding(0xb6, external_of(&st, sender), 1_000_000);
let send = transaction(
vec![outpoint(&parent, 0)],
vec![(EXTERNAL, 200_000), (external_of(&st, recipient), 790_000)],
);
store(&mut st, &send);
store(&mut st, &parent);
assert_eq!(
sent_outputs(&st, &send),
vec![
(sender, 0, None, 200_000),
(sender, 1, Some(recipient), 790_000)
]
);
assert_eq!(movement(&st, sender, &send), (-1_000_000, 1_000_000, 0));
assert_eq!(movement(&st, recipient, &send), (790_000, 0, 790_000));
}