addition of tx statuses to sort fields, fixes to total amount queries, inclusion of bigint, addition of unit tests to exercise filtering

This commit is contained in:
Yeastplume 2022-11-28 15:10:29 +00:00
parent d29b64248a
commit 13b1fc8e01
5 changed files with 259 additions and 9 deletions

1
Cargo.lock generated
View file

@ -1599,6 +1599,7 @@ dependencies = [
"grin_wallet_util",
"lazy_static",
"log",
"num-bigint",
"rand 0.6.5",
"regex",
"secrecy 0.6.0",

View file

@ -22,6 +22,7 @@ extern crate grin_wallet_libwallet as libwallet;
use grin_core as core;
use grin_keychain as keychain;
use grin_util as util;
use libwallet::{RetrieveTxQueryArgs, RetrieveTxQuerySortField};
use self::libwallet::{InitTxArgs, Slate};
use impls::test_framework::{self, LocalWalletClient};
@ -54,10 +55,150 @@ fn test_wallet_tx_filtering(
mask: Option<&SecretKey>,
) -> Result<(), libwallet::Error> {
wallet::controller::owner_single_use(Some(wallet.clone()), mask, None, |api, _m| {
let tx_results = api.retrieve_txs(mask, true, None, None, None)?.1;
for entry in tx_results.iter() {
let mut tx_query_args = RetrieveTxQueryArgs::default();
tx_query_args.min_id_inc = Some(5);
// Min ID
let tx_results = api
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
.1;
assert_eq!(tx_results[0].id, 5);
assert_eq!(tx_results[tx_results.len() - 1].id, 33);
// Max ID
let mut tx_query_args = RetrieveTxQueryArgs::default();
tx_query_args.min_id_inc = Some(5);
tx_query_args.max_id_inc = Some(20);
let tx_results = api
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
.1;
assert_eq!(tx_results[0].id, 5);
assert_eq!(tx_results[tx_results.len() - 1].id, 20);
// Exclude 1 cancelled
let mut tx_query_args = RetrieveTxQueryArgs::default();
tx_query_args.exclude_cancelled = Some(true);
tx_query_args.min_id_inc = Some(5);
tx_query_args.max_id_inc = Some(50);
let tx_results = api
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
.1;
assert_eq!(tx_results.len(), 28);
// Exclude 1 cancelled, show confirmed only
let mut tx_query_args = RetrieveTxQueryArgs::default();
tx_query_args.exclude_cancelled = Some(true);
tx_query_args.include_confirmed_only = Some(true);
tx_query_args.min_id_inc = Some(5);
tx_query_args.max_id_inc = Some(50);
let tx_results = api
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
.1;
assert_eq!(tx_results.len(), 14);
// show outstanding only (including cancelled)
let mut tx_query_args = RetrieveTxQueryArgs::default();
tx_query_args.exclude_cancelled = Some(false);
tx_query_args.include_outstanding_only = Some(true);
tx_query_args.min_id_inc = Some(5);
tx_query_args.max_id_inc = Some(50);
let tx_results = api
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
.1;
assert_eq!(tx_results.len(), 15);
// outstanding only and confirmed only should give empty set
let mut tx_query_args = RetrieveTxQueryArgs::default();
tx_query_args.exclude_cancelled = Some(false);
tx_query_args.include_outstanding_only = Some(true);
tx_query_args.include_confirmed_only = Some(true);
tx_query_args.min_id_inc = Some(5);
tx_query_args.max_id_inc = Some(50);
let tx_results = api
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
.1;
assert_eq!(tx_results.len(), 0);
// include sent only
let mut tx_query_args = RetrieveTxQueryArgs::default();
tx_query_args.include_sent_only = Some(true);
let tx_results = api
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
.1;
assert_eq!(tx_results.len(), 15);
// include received only (none in this set)
let mut tx_query_args = RetrieveTxQueryArgs::default();
tx_query_args.include_received_only = Some(true);
let tx_results = api
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
.1;
assert_eq!(tx_results.len(), 0);
// include reverted only (none in this set)
let mut tx_query_args = RetrieveTxQueryArgs::default();
tx_query_args.include_reverted_only = Some(true);
let tx_results = api
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
.1;
assert_eq!(tx_results.len(), 0);
// include coinbase only
let mut tx_query_args = RetrieveTxQueryArgs::default();
tx_query_args.include_coinbase_only = Some(true);
let tx_results = api
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
.1;
assert_eq!(tx_results.len(), 19);
// Amounts
let mut tx_query_args = RetrieveTxQueryArgs::default();
tx_query_args.min_amount_inc = Some(60_000_000_000 - 59_963_300_000);
let tx_results = api
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
.1;
assert_eq!(tx_results.len(), 27);
// amount, should see as above with coinbases excluded
let mut tx_query_args = RetrieveTxQueryArgs::default();
tx_query_args.min_amount_inc = Some(60_000_000_000 - 59_963_300_000);
tx_query_args.max_amount_inc = Some(60_000_000_000 - 1);
let tx_results = api
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
.1;
assert_eq!(tx_results.len(), 8);
// Amount - should only see coinbase (incoming)
let mut tx_query_args = RetrieveTxQueryArgs::default();
tx_query_args.min_amount_inc = Some(60_000_000_000);
let tx_results = api
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
.1;
assert_eq!(tx_results.len(), 19);
// sort order
let mut tx_query_args = RetrieveTxQueryArgs::default();
tx_query_args.sort_order = Some(libwallet::RetrieveTxQuerySortOrder::Desc);
let tx_results = api
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
.1;
assert_eq!(tx_results[0].id, 33);
assert_eq!(tx_results[tx_results.len() - 1].id, 0);
// change sort field to amount desc, should have coinbases first
let mut tx_query_args = RetrieveTxQueryArgs::default();
tx_query_args.sort_order = Some(libwallet::RetrieveTxQuerySortOrder::Desc);
tx_query_args.sort_field = Some(RetrieveTxQuerySortField::TotalAmount);
let tx_results = api
.retrieve_txs(mask, true, None, None, Some(tx_query_args))?
.1;
assert_eq!(tx_results[0].amount_credited, 60_000_000_000);
/*for entry in tx_results.iter() {
println!("{:?}", entry);
}
}*/
Ok(())
})?;
Ok(())
@ -176,6 +317,28 @@ fn build_chain_for_tx_filtering(
}
}
// Cancel a tx for filtering testing
let amount: u64 = 1_000_000;
let mut slate = Slate::blank(1, false);
debug!("Creating TX for {}", amount);
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| {
// note this will increment the block count as part of the transaction "Posting"
let args = InitTxArgs {
src_acct_name: None,
amount: amount,
minimum_confirmations: 1,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: false,
..Default::default()
};
let slate_i = sender_api.init_send_tx(m, args)?;
slate = client1.send_tx_slate_direct("wallet2", &slate_i)?;
sender_api.tx_lock_outputs(m, &slate)?;
sender_api.cancel_tx(m, Some(33), None)?;
Ok(())
})?;
// Perform actual testing
test_wallet_tx_filtering(wallet1, mask1)?;

View file

@ -34,6 +34,7 @@ curve25519-dalek = "2.1"
secrecy = "0.6"
bech32 = "0.7"
byteorder = "1.3"
num-bigint = "0.2"
grin_wallet_util = { path = "../util", version = "5.2.0-alpha.1" }
grin_wallet_config = { path = "../config", version = "5.2.0-alpha.1" }

View file

@ -194,6 +194,14 @@ pub struct RetrieveTxQueryArgs {
pub include_outstanding_only: Option<bool>,
/// whether to only consider confirmed-only transactions
pub include_confirmed_only: Option<bool>,
/// whether to only consider sent transactions
pub include_sent_only: Option<bool>,
/// whether to only consider received transactions
pub include_received_only: Option<bool>,
/// whether to only consider coinbase transactions
pub include_coinbase_only: Option<bool>,
/// whether to only consider reverted transactions
pub include_reverted_only: Option<bool>,
/// lower bound on the total amount (amount_credited - amount_debited), inclusive
pub min_amount_inc: Option<u64>,
/// higher bound on the total amount (amount_credited - amount_debited), inclusive
@ -222,6 +230,10 @@ impl Default for RetrieveTxQueryArgs {
exclude_cancelled: Some(false),
include_outstanding_only: Some(false),
include_confirmed_only: Some(false),
include_sent_only: Some(false),
include_received_only: Some(false),
include_coinbase_only: Some(false),
include_reverted_only: Some(false),
min_amount_inc: None,
max_amount_inc: None,
min_creation_timestamp_inc: None,

View file

@ -38,6 +38,8 @@ use crate::{
RetrieveTxQuerySortOrder,
};
use num_bigint::BigInt;
/// Retrieve all of the outputs (doesn't attempt to update from node)
pub fn retrieve_outputs<'a, T: ?Sized, C, K>(
wallet: &mut T,
@ -121,9 +123,6 @@ where
if let Some(v) = query_args.include_outstanding_only {
if v {
!tx_entry.confirmed
&& (tx_entry.tx_type == TxLogEntryType::TxReceived
|| tx_entry.tx_type == TxLogEntryType::TxSent
|| tx_entry.tx_type == TxLogEntryType::TxReverted)
} else {
true
}
@ -142,6 +141,52 @@ where
true
}
})
.filter(|tx_entry| {
if let Some(v) = query_args.include_sent_only {
if v {
tx_entry.tx_type == TxLogEntryType::TxSent
|| tx_entry.tx_type == TxLogEntryType::TxSentCancelled
} else {
true
}
} else {
true
}
})
.filter(|tx_entry| {
if let Some(v) = query_args.include_received_only {
if v {
tx_entry.tx_type == TxLogEntryType::TxReceived
|| tx_entry.tx_type == TxLogEntryType::TxReceivedCancelled
} else {
true
}
} else {
true
}
})
.filter(|tx_entry| {
if let Some(v) = query_args.include_coinbase_only {
if v {
tx_entry.tx_type == TxLogEntryType::ConfirmedCoinbase
} else {
true
}
} else {
true
}
})
.filter(|tx_entry| {
if let Some(v) = query_args.include_reverted_only {
if v {
tx_entry.tx_type == TxLogEntryType::TxReverted
} else {
true
}
} else {
true
}
})
.filter(|tx_entry| {
if let Some(v) = query_args.min_id_inc {
tx_entry.id >= v
@ -158,14 +203,34 @@ where
})
.filter(|tx_entry| {
if let Some(v) = query_args.min_amount_inc {
v >= tx_entry.amount_credited - tx_entry.amount_debited
if tx_entry.tx_type == TxLogEntryType::TxSent
|| tx_entry.tx_type == TxLogEntryType::TxSentCancelled
{
BigInt::from(tx_entry.amount_debited)
- BigInt::from(tx_entry.amount_credited)
>= BigInt::from(v)
} else {
BigInt::from(tx_entry.amount_credited)
- BigInt::from(tx_entry.amount_debited)
>= BigInt::from(v)
}
} else {
true
}
})
.filter(|tx_entry| {
if let Some(v) = query_args.max_amount_inc {
v <= tx_entry.amount_credited - tx_entry.amount_debited
if tx_entry.tx_type == TxLogEntryType::TxSent
|| tx_entry.tx_type == TxLogEntryType::TxSentCancelled
{
BigInt::from(tx_entry.amount_debited)
- BigInt::from(tx_entry.amount_credited)
<= BigInt::from(v)
} else {
BigInt::from(tx_entry.amount_credited)
- BigInt::from(tx_entry.amount_debited)
<= BigInt::from(v)
}
} else {
true
}
@ -228,7 +293,15 @@ where
return_txs.sort_by_key(|tx| tx.confirmation_ts);
}
RetrieveTxQuerySortField::TotalAmount => {
return_txs.sort_by_key(|tx| tx.amount_credited - tx.amount_debited);
return_txs.sort_by_key(|tx| {
if tx.tx_type == TxLogEntryType::TxSent
|| tx.tx_type == TxLogEntryType::TxSentCancelled
{
BigInt::from(tx.amount_debited) - BigInt::from(tx.amount_credited)
} else {
BigInt::from(tx.amount_credited) - BigInt::from(tx.amount_debited)
}
});
}
RetrieveTxQuerySortField::AmountCredited => {
return_txs.sort_by_key(|tx| tx.amount_credited);