zcash_client_sqlite: Add a test that demonstrates the expected behavior of `get_memo` for empty-memo situations.
This commit is contained in:
parent
eade2acab9
commit
e718e76989
|
@ -371,7 +371,7 @@ pub(crate) fn put_received_note<T: ReceivedSaplingOutput>(
|
|||
#[cfg(test)]
|
||||
#[allow(deprecated)]
|
||||
pub(crate) mod tests {
|
||||
use std::num::NonZeroU32;
|
||||
use std::{convert::Infallible, num::NonZeroU32};
|
||||
|
||||
use rusqlite::Connection;
|
||||
use secrecy::Secret;
|
||||
|
@ -383,8 +383,13 @@ pub(crate) mod tests {
|
|||
block::BlockHash,
|
||||
consensus::{BlockHeight, BranchId},
|
||||
legacy::TransparentAddress,
|
||||
memo::Memo,
|
||||
sapling::{note_encryption::try_sapling_output_recovery, prover::TxProver},
|
||||
transaction::{components::Amount, fees::zip317::FeeRule as Zip317FeeRule, Transaction},
|
||||
transaction::{
|
||||
components::Amount,
|
||||
fees::{fixed::FeeRule as FixedFeeRule, zip317::FeeRule as Zip317FeeRule},
|
||||
Transaction,
|
||||
},
|
||||
zip32::{sapling::ExtendedSpendingKey, Scope},
|
||||
};
|
||||
|
||||
|
@ -394,13 +399,17 @@ pub(crate) mod tests {
|
|||
self,
|
||||
chain::scan_cached_blocks,
|
||||
error::Error,
|
||||
wallet::{create_spend_to_address, input_selection::GreedyInputSelector, spend},
|
||||
wallet::{
|
||||
create_proposed_transaction, create_spend_to_address,
|
||||
input_selection::GreedyInputSelector, propose_transfer, spend,
|
||||
},
|
||||
WalletRead, WalletWrite,
|
||||
},
|
||||
fees::{zip317, DustOutputPolicy},
|
||||
decrypt_transaction,
|
||||
fees::{fixed, zip317, DustOutputPolicy},
|
||||
keys::UnifiedSpendingKey,
|
||||
wallet::OvkPolicy,
|
||||
zip321::{Payment, TransactionRequest},
|
||||
zip321::{self, Payment, TransactionRequest},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -413,21 +422,17 @@ pub(crate) mod tests {
|
|||
get_balance, get_balance_at,
|
||||
init::{init_blocks_table, init_wallet_db},
|
||||
},
|
||||
AccountId, BlockDb, WalletDb,
|
||||
AccountId, BlockDb, NoteId, WalletDb,
|
||||
};
|
||||
|
||||
#[cfg(feature = "transparent-inputs")]
|
||||
use {
|
||||
zcash_client_backend::{
|
||||
data_api::wallet::shield_transparent_funds, fees::fixed,
|
||||
wallet::WalletTransparentOutput,
|
||||
data_api::wallet::shield_transparent_funds, wallet::WalletTransparentOutput,
|
||||
},
|
||||
zcash_primitives::{
|
||||
memo::MemoBytes,
|
||||
transaction::{
|
||||
components::{amount::NonNegativeAmount, OutPoint, TxOut},
|
||||
fees::fixed::FeeRule as FixedFeeRule,
|
||||
},
|
||||
transaction::components::{amount::NonNegativeAmount, OutPoint, TxOut},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -440,6 +445,157 @@ pub(crate) mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_proposed_transfer() {
|
||||
let cache_file = NamedTempFile::new().unwrap();
|
||||
let db_cache = BlockDb(Connection::open(cache_file.path()).unwrap());
|
||||
init_cache_database(&db_cache).unwrap();
|
||||
|
||||
let data_file = NamedTempFile::new().unwrap();
|
||||
let mut db_data = WalletDb::for_path(data_file.path(), tests::network()).unwrap();
|
||||
init_wallet_db(&mut db_data, None).unwrap();
|
||||
|
||||
// Add an account to the wallet
|
||||
let seed = Secret::new([0u8; 32].to_vec());
|
||||
let (account, usk) = db_data.create_account(&seed).unwrap();
|
||||
let dfvk = usk.sapling().to_diversifiable_full_viewing_key();
|
||||
|
||||
// Add funds to the wallet in a single note
|
||||
let value = Amount::from_u64(60000).unwrap();
|
||||
let (cb, _) = fake_compact_block(
|
||||
sapling_activation_height(),
|
||||
BlockHash([0; 32]),
|
||||
&dfvk,
|
||||
AddressType::DefaultExternal,
|
||||
value,
|
||||
0,
|
||||
);
|
||||
insert_into_cache(&db_cache, &cb);
|
||||
scan_cached_blocks(
|
||||
&tests::network(),
|
||||
&db_cache,
|
||||
&mut db_data,
|
||||
sapling_activation_height(),
|
||||
1,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Verified balance matches total balance
|
||||
let (_, anchor_height) = db_data
|
||||
.get_target_and_anchor_heights(NonZeroU32::new(1).unwrap())
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
get_balance(&db_data.conn, AccountId::from(0)).unwrap(),
|
||||
value
|
||||
);
|
||||
assert_eq!(
|
||||
get_balance_at(&db_data.conn, AccountId::from(0), anchor_height).unwrap(),
|
||||
value
|
||||
);
|
||||
|
||||
let to_extsk = ExtendedSpendingKey::master(&[]);
|
||||
let to: RecipientAddress = to_extsk.default_address().1.into();
|
||||
let request = zip321::TransactionRequest::new(vec![Payment {
|
||||
recipient_address: to,
|
||||
amount: Amount::from_u64(10000).unwrap(),
|
||||
memo: None, // this should result in the creation of an empty memo
|
||||
label: None,
|
||||
message: None,
|
||||
other_params: vec![],
|
||||
}])
|
||||
.unwrap();
|
||||
|
||||
let fee_rule = FixedFeeRule::standard();
|
||||
let change_strategy = fixed::SingleOutputChangeStrategy::new(fee_rule);
|
||||
let input_selector =
|
||||
&GreedyInputSelector::new(change_strategy, DustOutputPolicy::default());
|
||||
let proposal_result = propose_transfer::<_, _, _, Infallible>(
|
||||
&mut db_data,
|
||||
&tests::network(),
|
||||
account,
|
||||
input_selector,
|
||||
request,
|
||||
NonZeroU32::new(1).unwrap(),
|
||||
);
|
||||
assert_matches!(proposal_result, Ok(_));
|
||||
|
||||
let change_memo = "Test change memo".parse::<Memo>().unwrap();
|
||||
let create_proposed_result = create_proposed_transaction::<_, _, Infallible, _>(
|
||||
&mut db_data,
|
||||
&tests::network(),
|
||||
test_prover(),
|
||||
&usk,
|
||||
OvkPolicy::Sender,
|
||||
proposal_result.unwrap(),
|
||||
NonZeroU32::new(1).unwrap(),
|
||||
Some(change_memo.clone().into()),
|
||||
);
|
||||
assert_matches!(create_proposed_result, Ok(_));
|
||||
|
||||
let sent_tx_id = create_proposed_result.unwrap();
|
||||
|
||||
// Verify that the sent transaction was stored and that we can decrypt the memos
|
||||
let tx = db_data
|
||||
.get_transaction(sent_tx_id)
|
||||
.expect("Created transaction was stored.");
|
||||
let ufvks = [(account, usk.to_unified_full_viewing_key())]
|
||||
.into_iter()
|
||||
.collect();
|
||||
let decrypted_outputs = decrypt_transaction(
|
||||
&tests::network(),
|
||||
sapling_activation_height() + 1,
|
||||
&tx,
|
||||
&ufvks,
|
||||
);
|
||||
|
||||
let mut found_tx_change_memo = false;
|
||||
let mut found_tx_empty_memo = false;
|
||||
for output in decrypted_outputs {
|
||||
if output.memo == change_memo.clone().into() {
|
||||
found_tx_change_memo = true
|
||||
}
|
||||
if output.memo == Memo::Empty.into() {
|
||||
found_tx_empty_memo = true
|
||||
}
|
||||
}
|
||||
assert!(found_tx_change_memo);
|
||||
assert!(found_tx_empty_memo);
|
||||
|
||||
// Verify that the stored sent notes match what we're expecting
|
||||
let mut stmt_sent_notes = db_data
|
||||
.conn
|
||||
.prepare("SELECT id_note FROM sent_notes WHERE tx = ?")
|
||||
.unwrap();
|
||||
|
||||
let sent_note_ids = stmt_sent_notes
|
||||
.query(rusqlite::params![sent_tx_id])
|
||||
.unwrap()
|
||||
.mapped(|row| row.get::<_, i64>(0).map(NoteId::SentNoteId))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(sent_note_ids.len(), 2);
|
||||
|
||||
// The sent memo should be the empty memo for both the sent output and change
|
||||
let mut found_sent_change_memo = false;
|
||||
let mut found_sent_empty_memo = false;
|
||||
for sent_note_id in sent_note_ids {
|
||||
let memo = db_data.get_memo(sent_note_id).expect("Note id is valid");
|
||||
if memo.as_ref() == Some(&change_memo) {
|
||||
found_sent_change_memo = true
|
||||
}
|
||||
if memo == Some(Memo::Empty) {
|
||||
found_sent_empty_memo = true
|
||||
}
|
||||
}
|
||||
assert!(found_sent_change_memo);
|
||||
assert!(found_sent_empty_memo);
|
||||
|
||||
// Check that querying for a nonexistent sent note returns an error
|
||||
assert_matches!(db_data.get_memo(NoteId::SentNoteId(12345)), Err(_));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_to_address_fails_on_incorrect_usk() {
|
||||
let data_file = NamedTempFile::new().unwrap();
|
||||
|
|
Loading…
Reference in New Issue