-
Notifications
You must be signed in to change notification settings - Fork 133
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Transaction pagination + full query arguments #666
Conversation
api calls throughout
…allet test to be filled out
…, inclusion of bigint, addition of unit tests to exercise filtering
This is now ready for review, changed original comments to reflect changes |
/// to the before and earlier set | ||
pub limit: Option<u32>, | ||
/// whether to exclude cancelled transactions in the returned set | ||
pub exclude_cancelled: Option<bool>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a specific reason why we wrap all bools into options? Would removing these simplify the logical branching in the filtering code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if they weren't options, you'd need to specify each and every one when passing in the struct from JSON, this allows json callers to leave them out
libwallet/src/api_impl/types.rs
Outdated
/// lower bound on the total amount (amount_credited - amount_debited), inclusive | ||
#[serde(with = "secp_ser::opt_string_or_u64")] | ||
#[serde(default)] | ||
pub min_amount_inc: Option<u64>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably makes sense to remove the _inc
here as well since the max_amount
doesn't have it either
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missed that, thank you
libwallet/src/internal/updater.rs
Outdated
.collect(); | ||
txs.sort_by_key(|tx| tx.creation_ts); | ||
let mut txs; | ||
// Adding in new tranasction list query logic. If `tx_id` or `tx_slate_id` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: typo tranasction
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
let mut txs; | ||
// Adding in new tranasction list query logic. If `tx_id` or `tx_slate_id` | ||
// is provided, then `query_args` is ignored and old logic is followed. | ||
if query_args.is_some() && tx_id.is_none() && tx_slate_id.is_none() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FUTURE: Maybe we could consider returning an error if query_args is provided as well as a tx_id or slate_id i.e. you might filter to exclude cancelled txs, but the tx_id is a cancelled tx and will thus be returned.
|
||
let mut return_txs: Vec<TxLogEntry> = txs_iter.collect(); | ||
|
||
// Now apply requested sorting |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should apply sorting before we limit otherwise we end up with what I would consider an incorrect logic i.e. we sort things by credited amount, but only after we have cut off most of the txs meaning we won't get the most credited txs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I'm with you here, sorting the entire data set on every query is very inefficient and should be avoided, I think. Can you give an example of where you'd get incorrect logic here? If you're interested in credited amount, you filter for it then optionally sort the results.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree this is slower because the sorting takes place on a larger sequence. Here's an example of what I had in mind. Let's say I have 60 transactions in my transaction log. The first 20 entries have amount credited +1, next 20 entries have amount credited +20 and the last 20 entries have amount credited +10.
I make the following call
api.query_txs(limit=10, sort_order="Desc", sort_field="AmountCredited")
I would assume this returns the top10 transactions with the largest credited amount.
My understanding is that the limit will be applied on a sequence of all tx logs in this case and will take only the last 10 from the sequence (likely take the last 10 transactions) and then sort these. The result returned will be 10 transactions with amount credited +10. The user would probably expect to get 10 transactions with +20 instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I'm with you now. Good catch, I've updated the logic to apply limiting after sorting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job! I played a bit with the python api call wrapper by adding the following:
def query_txs(
self, min_id=None, max_id=None, limit=None, exclude_cancelled=None, include_outstanding_only=None,
include_confirmed_only=None, include_sent_only=None, include_received_only=None,
include_coinbase_only=None, include_reverted_only=None,min_amount=None, max_amount=None,
min_creation_ts=None, max_creation_ts=None, min_confirmed_ts=None, max_confirmed_ts=None,
sort_field=None, sort_order=None, refresh=True
):
params = {
'token': self.token,
'refresh_from_node': refresh,
'query': {
"min_id": min_id,
"max_id": max_id,
"limit": limit,
"exclude_cancelled": exclude_cancelled,
"include_outstanding_only": include_outstanding_only,
"include_confirmed_only": include_confirmed_only,
"include_sent_only": include_sent_only,
"include_received_only": include_received_only,
"include_coinbase_only": include_coinbase_only,
"include_reverted_only": include_reverted_only,
"min_amount_inc": min_amount,
"max_amount": max_amount,
"min_creation_timestamp": min_creation_ts,
"max_creation_timestamp": max_creation_ts,
"min_confirmed_timestamp": min_confirmed_ts,
"max_confirmed_timestamp": max_confirmed_ts,
"sort_field": sort_field,
"sort_order": sort_order,
},
}
resp = self.post_encrypted('query_txs', params)
if refresh and not resp["result"]["Ok"][0]:
# We requested refresh but data was not successfully refreshed
raise WalletError("query_txs", params, None, "Failed to refresh data from the node")
return resp["result"]["Ok"][1]
print([x["id"] for x in wallet_api.query_txs(min_id=100, max_id=200, limit=10, sort_order="Desc", sort_field="AmountCredited")])
I didn't manage to test all the fields, but from what I found, I'd say only the last comment is more relevant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Everything has been addressed 👍
RetrieveTxQueryArgs
,RetrieveTxQuerySortOrder
,RetrieveTxQuerySortField
retrieve_txs
API function to optionally accept aRetrieveTxQueryArgs
structapply_advanced_tx_list_filtering
fnretrieve_tx
implementation to use advanced filfering function if optional query args are suppliedquery_txs
, (note it was necessary to provide a separate function to avoid breaking existing json calls toretrieve_txs
While creating https://github.com/mimblewimble/grin-gui, it's become apparent that we need to implement much more granular transaction querying to better support pagination, sorting, etc. Instead of just implementing basic pagination, I'm taking the opportunity to add a reasonable number of advanced query options via a new query structure.
Details posted here for review and comment, I think this should cover most needs but happy to hear from anyone with suggestions or anything else that should be included in the query arguments:
Sort fields (and sort order) are: