multi: return txns first and last indices

In this commit we introduce first and last indices for the
tranasctions returned which can be used to seek for further
transactions in a pagination style.

Signed-off-by: Abdullahi Yunus <abdoollahikbk@gmail.com>
This commit is contained in:
Abdullahi Yunus 2024-08-11 16:31:26 +01:00
parent cd1df4ac34
commit 762d01536b
13 changed files with 3135 additions and 3044 deletions

View File

@ -2074,6 +2074,20 @@ var listChainTxnsCommand = cli.Command{
"transactions until the chain tip, including " + "transactions until the chain tip, including " +
"unconfirmed, set this value to -1", "unconfirmed, set this value to -1",
}, },
cli.UintFlag{
Name: "index_offset",
Usage: "the index of a transaction that will be " +
"used in a query to determine which " +
"transaction should be returned in the " +
"response",
},
cli.IntFlag{
Name: "max_transactions",
Usage: "(optional) the max number of transactions to " +
"return; leave at default of 0 to return " +
"all transactions",
Value: 0,
},
}, },
Description: ` Description: `
List all transactions an address of the wallet was involved in. List all transactions an address of the wallet was involved in.
@ -2096,7 +2110,10 @@ func listChainTxns(ctx *cli.Context) error {
client, cleanUp := getClient(ctx) client, cleanUp := getClient(ctx)
defer cleanUp() defer cleanUp()
req := &lnrpc.GetTransactionsRequest{} req := &lnrpc.GetTransactionsRequest{
IndexOffset: uint32(ctx.Uint64("index_offset")),
MaxTransactions: uint32(ctx.Uint64("max_transactions")),
}
if ctx.IsSet("start_height") { if ctx.IsSet("start_height") {
req.StartHeight = int32(ctx.Int64("start_height")) req.StartHeight = int32(ctx.Int64("start_height"))

File diff suppressed because it is too large Load Diff

View File

@ -774,6 +774,18 @@ message GetTransactionsRequest {
message TransactionDetails { message TransactionDetails {
// The list of transactions relevant to the wallet. // The list of transactions relevant to the wallet.
repeated Transaction transactions = 1; repeated Transaction transactions = 1;
/*
The index of the last item in the set of returned transactions. This can be
used to seek further, pagination style.
*/
uint64 last_index = 2;
/*
The index of the last item in the set of returned transactions. This can be
used to seek backwards, pagination style.
*/
uint64 first_index = 3;
} }
message FeeLimit { message FeeLimit {

View File

@ -7520,6 +7520,16 @@
"$ref": "#/definitions/lnrpcTransaction" "$ref": "#/definitions/lnrpcTransaction"
}, },
"description": "The list of transactions relevant to the wallet." "description": "The list of transactions relevant to the wallet."
},
"last_index": {
"type": "string",
"format": "uint64",
"description": "The index of the last item in the set of returned transactions. This can be\nused to seek further, pagination style."
},
"first_index": {
"type": "string",
"format": "uint64",
"description": "The index of the last item in the set of returned transactions. This can be\nused to seek backwards, pagination style."
} }
} }
}, },

View File

@ -117,9 +117,13 @@ func RPCTransaction(tx *lnwallet.TransactionDetail) *Transaction {
} }
// RPCTransactionDetails returns a set of rpc transaction details. // RPCTransactionDetails returns a set of rpc transaction details.
func RPCTransactionDetails(txns []*lnwallet.TransactionDetail) *TransactionDetails { func RPCTransactionDetails(txns []*lnwallet.TransactionDetail, firstIdx,
lastIdx uint64) *TransactionDetails {
txDetails := &TransactionDetails{ txDetails := &TransactionDetails{
Transactions: make([]*Transaction, len(txns)), Transactions: make([]*Transaction, len(txns)),
FirstIndex: firstIdx,
LastIndex: lastIdx,
} }
for i, tx := range txns { for i, tx := range txns {

View File

@ -1166,6 +1166,16 @@
"$ref": "#/definitions/lnrpcTransaction" "$ref": "#/definitions/lnrpcTransaction"
}, },
"description": "The list of transactions relevant to the wallet." "description": "The list of transactions relevant to the wallet."
},
"last_index": {
"type": "string",
"format": "uint64",
"description": "The index of the last item in the set of returned transactions. This can be\nused to seek further, pagination style."
},
"first_index": {
"type": "string",
"format": "uint64",
"description": "The index of the last item in the set of returned transactions. This can be\nused to seek backwards, pagination style."
} }
} }
}, },

View File

@ -1376,7 +1376,7 @@ func (w *WalletKit) ListSweeps(ctx context.Context,
// can match our list of sweeps against the list of transactions that // can match our list of sweeps against the list of transactions that
// the wallet is still tracking. Sweeps are currently always swept to // the wallet is still tracking. Sweeps are currently always swept to
// the default wallet account. // the default wallet account.
transactions, err := w.cfg.Wallet.ListTransactionDetails( txns, firstIdx, lastIdx, err := w.cfg.Wallet.ListTransactionDetails(
in.StartHeight, btcwallet.UnconfirmedHeight, in.StartHeight, btcwallet.UnconfirmedHeight,
lnwallet.DefaultAccountName, 0, 0, lnwallet.DefaultAccountName, 0, 0,
) )
@ -1389,7 +1389,7 @@ func (w *WalletKit) ListSweeps(ctx context.Context,
txDetails []*lnwallet.TransactionDetail txDetails []*lnwallet.TransactionDetail
) )
for _, tx := range transactions { for _, tx := range txns {
_, ok := sweepTxns[tx.Hash.String()] _, ok := sweepTxns[tx.Hash.String()]
if !ok { if !ok {
continue continue
@ -1408,7 +1408,7 @@ func (w *WalletKit) ListSweeps(ctx context.Context,
return &ListSweepsResponse{ return &ListSweepsResponse{
Sweeps: &ListSweepsResponse_TransactionDetails{ Sweeps: &ListSweepsResponse_TransactionDetails{
TransactionDetails: lnrpc.RPCTransactionDetails( TransactionDetails: lnrpc.RPCTransactionDetails(
txDetails, txDetails, firstIdx, lastIdx,
), ),
}, },
}, nil }, nil

View File

@ -187,9 +187,10 @@ func (w *WalletController) ListUnspentWitness(int32, int32,
// ListTransactionDetails currently returns dummy values. // ListTransactionDetails currently returns dummy values.
func (w *WalletController) ListTransactionDetails(int32, int32, func (w *WalletController) ListTransactionDetails(int32, int32,
string, uint32, uint32) ([]*lnwallet.TransactionDetail, error) { string, uint32, uint32) ([]*lnwallet.TransactionDetail,
uint64, uint64, error) {
return nil, nil return nil, 0, 0, nil
} }
// LeaseOutput returns the current time and a nil error. // LeaseOutput returns the current time and a nil error.

View File

@ -1555,7 +1555,8 @@ func unminedTransactionsToDetail(
// This is a part of the WalletController interface. // This is a part of the WalletController interface.
func (b *BtcWallet) ListTransactionDetails(startHeight, endHeight int32, func (b *BtcWallet) ListTransactionDetails(startHeight, endHeight int32,
accountFilter string, indexOffset uint32, accountFilter string, indexOffset uint32,
maxTransactons uint32) ([]*lnwallet.TransactionDetail, error) { maxTransactions uint32) ([]*lnwallet.TransactionDetail, uint64, uint64,
error) {
// Grab the best block the wallet knows of, we'll use this to calculate // Grab the best block the wallet knows of, we'll use this to calculate
// # of confirmations shortly below. // # of confirmations shortly below.
@ -1567,7 +1568,7 @@ func (b *BtcWallet) ListTransactionDetails(startHeight, endHeight int32,
stop := base.NewBlockIdentifierFromHeight(endHeight) stop := base.NewBlockIdentifierFromHeight(endHeight)
txns, err := b.wallet.GetTransactions(start, stop, accountFilter, nil) txns, err := b.wallet.GetTransactions(start, stop, accountFilter, nil)
if err != nil { if err != nil {
return nil, err return nil, 0, 0, err
} }
txDetails := make([]*lnwallet.TransactionDetail, 0, txDetails := make([]*lnwallet.TransactionDetail, 0,
@ -1581,7 +1582,7 @@ func (b *BtcWallet) ListTransactionDetails(startHeight, endHeight int32,
currentHeight, blockPackage, b.netParams, currentHeight, blockPackage, b.netParams,
) )
if err != nil { if err != nil {
return nil, err return nil, 0, 0, err
} }
txDetails = append(txDetails, details...) txDetails = append(txDetails, details...)
@ -1589,7 +1590,7 @@ func (b *BtcWallet) ListTransactionDetails(startHeight, endHeight int32,
for _, tx := range txns.UnminedTransactions { for _, tx := range txns.UnminedTransactions {
detail, err := unminedTransactionsToDetail(tx, b.netParams) detail, err := unminedTransactionsToDetail(tx, b.netParams)
if err != nil { if err != nil {
return nil, err return nil, 0, 0, err
} }
txDetails = append(txDetails, detail) txDetails = append(txDetails, detail)
@ -1598,19 +1599,29 @@ func (b *BtcWallet) ListTransactionDetails(startHeight, endHeight int32,
// Return empty transaction list, if offset is more than all // Return empty transaction list, if offset is more than all
// transactions. // transactions.
if int(indexOffset) >= len(txDetails) { if int(indexOffset) >= len(txDetails) {
return []*lnwallet.TransactionDetail{}, nil txDetails = []*lnwallet.TransactionDetail{}
return txDetails, 0, 0, nil
} }
if maxTransactons == 0 { end := indexOffset + maxTransactions
return txDetails[indexOffset:], nil
// If maxTransactions is set to 0, then we'll return all transactions
// starting from the offset.
if maxTransactions == 0 {
end = uint32(len(txDetails))
txDetails = txDetails[indexOffset:end]
return txDetails, uint64(indexOffset), uint64(end - 1), nil
} }
end := indexOffset + maxTransactons if end > uint32(len(txDetails)) {
if int(end) > len(txDetails) {
end = uint32(len(txDetails)) end = uint32(len(txDetails))
} }
return txDetails[indexOffset:end], nil txDetails = txDetails[indexOffset:end]
return txDetails, uint64(indexOffset), uint64(end - 1), nil
} }
// txSubscriptionClient encapsulates the transaction notification client from // txSubscriptionClient encapsulates the transaction notification client from

View File

@ -403,7 +403,8 @@ type WalletController interface {
// empty, transactions of all wallet accounts are returned. // empty, transactions of all wallet accounts are returned.
ListTransactionDetails(startHeight, endHeight int32, ListTransactionDetails(startHeight, endHeight int32,
accountFilter string, indexOffset uint32, accountFilter string, indexOffset uint32,
maxTransactions uint32) ([]*TransactionDetail, error) maxTransactions uint32) ([]*TransactionDetail, uint64, uint64,
error)
// LeaseOutput locks an output to the given ID, preventing it from being // LeaseOutput locks an output to the given ID, preventing it from being
// available for any future coin selection attempts. The absolute time // available for any future coin selection attempts. The absolute time

View File

@ -198,9 +198,9 @@ func (w *mockWalletController) ListUnspentWitness(int32, int32,
// ListTransactionDetails currently returns dummy values. // ListTransactionDetails currently returns dummy values.
func (w *mockWalletController) ListTransactionDetails(int32, int32, func (w *mockWalletController) ListTransactionDetails(int32, int32,
string, uint32, uint32) ([]*TransactionDetail, error) { string, uint32, uint32) ([]*TransactionDetail, uint64, uint64, error) {
return nil, nil return nil, 0, 0, nil
} }
// LeaseOutput returns the current time and a nil error. // LeaseOutput returns the current time and a nil error.

View File

@ -199,7 +199,7 @@ func assertTxInWallet(t *testing.T, w *lnwallet.LightningWallet,
// We'll fetch all of our transaction and go through each one until // We'll fetch all of our transaction and go through each one until
// finding the expected transaction with its expected confirmation // finding the expected transaction with its expected confirmation
// status. // status.
txs, err := w.ListTransactionDetails( txs, _, _, err := w.ListTransactionDetails(
0, btcwallet.UnconfirmedHeight, "", 0, 1000, 0, btcwallet.UnconfirmedHeight, "", 0, 1000,
) )
require.NoError(t, err, "unable to retrieve transactions") require.NoError(t, err, "unable to retrieve transactions")
@ -1103,7 +1103,7 @@ func testListTransactionDetails(miner *rpctest.Harness,
// should be confirmed. // should be confirmed.
err = waitForWalletSync(miner, alice) err = waitForWalletSync(miner, alice)
require.NoError(t, err, "Couldn't sync Alice's wallet") require.NoError(t, err, "Couldn't sync Alice's wallet")
txDetails, err := alice.ListTransactionDetails( txDetails, _, _, err := alice.ListTransactionDetails(
startHeight, chainTip, "", 0, 1000, startHeight, chainTip, "", 0, 1000,
) )
require.NoError(t, err, "unable to fetch tx details") require.NoError(t, err, "unable to fetch tx details")
@ -1215,7 +1215,7 @@ func testListTransactionDetails(miner *rpctest.Harness,
// unconfirmed transactions. The transaction above should be included // unconfirmed transactions. The transaction above should be included
// with a confirmation height of 0, indicating that it has not been // with a confirmation height of 0, indicating that it has not been
// mined yet. // mined yet.
txDetails, err = alice.ListTransactionDetails( txDetails, _, _, err = alice.ListTransactionDetails(
chainTip, btcwallet.UnconfirmedHeight, "", 0, 1000, chainTip, btcwallet.UnconfirmedHeight, "", 0, 1000,
) )
require.NoError(t, err, "unable to fetch tx details") require.NoError(t, err, "unable to fetch tx details")
@ -1268,7 +1268,7 @@ func testListTransactionDetails(miner *rpctest.Harness,
// transactions from the last block. // transactions from the last block.
err = waitForWalletSync(miner, alice) err = waitForWalletSync(miner, alice)
require.NoError(t, err, "Couldn't sync Alice's wallet") require.NoError(t, err, "Couldn't sync Alice's wallet")
txDetails, err = alice.ListTransactionDetails( txDetails, _, _, err = alice.ListTransactionDetails(
chainTip, chainTip, "", 0, 1000, chainTip, chainTip, "", 0, 1000,
) )
require.NoError(t, err, "unable to fetch tx details") require.NoError(t, err, "unable to fetch tx details")
@ -1311,7 +1311,7 @@ func testListTransactionDetails(miner *rpctest.Harness,
// Query for transactions only in the latest block. We do not expect // Query for transactions only in the latest block. We do not expect
// any transactions to be returned. // any transactions to be returned.
txDetails, err = alice.ListTransactionDetails( txDetails, _, _, err = alice.ListTransactionDetails(
chainTip, chainTip, "", 0, 1000, chainTip, chainTip, "", 0, 1000,
) )
require.NoError(t, err, "unexpected error") require.NoError(t, err, "unexpected error")

View File

@ -6476,15 +6476,16 @@ func (r *rpcServer) GetTransactions(ctx context.Context,
endHeight = req.EndHeight endHeight = req.EndHeight
} }
transactions, err := r.server.cc.Wallet.ListTransactionDetails( txns, firstIdx, lastIdx, err :=
req.StartHeight, endHeight, req.Account, req.IndexOffset, r.server.cc.Wallet.ListTransactionDetails(
req.MaxTransactions, req.StartHeight, endHeight, req.Account,
req.IndexOffset, req.MaxTransactions,
) )
if err != nil { if err != nil {
return nil, err return nil, err
} }
return lnrpc.RPCTransactionDetails(transactions), nil return lnrpc.RPCTransactionDetails(txns, firstIdx, lastIdx), nil
} }
// DescribeGraph returns a description of the latest graph state from the PoV // DescribeGraph returns a description of the latest graph state from the PoV