looprpc: add simple pagination to the ListSwaps command

This commit adds the ability to set a max_swaps and an index_offset
flag to the ListSwaps command. These new fields are applied AFTER
the initial filtering step. The response also now passes additional
information about the index count and total swaps filtered.
This commit is contained in:
Sam Korn 2025-03-31 21:31:48 -06:00
parent 84820f2d4f
commit b55521cb41
No known key found for this signature in database
GPG key ID: C54D890495E29E91
5 changed files with 993 additions and 864 deletions

View file

@ -35,6 +35,15 @@ var listSwapsCommand = cli.Command{
labelFlag,
channelFlag,
lastHopFlag,
cli.Uint64Flag{
Name: "max_swaps",
Usage: "Max number of swaps to return after filtering",
},
cli.Int64Flag{
Name: "start_time_ns",
Usage: "Unix timestamp in nanoseconds to select swaps initiated " +
"after this time",
},
},
}
@ -99,9 +108,19 @@ func listSwaps(ctx *cli.Context) error {
filter.Label = ctx.String(labelFlag.Name)
}
// Parse start timestamp if set.
if ctx.IsSet("start_time_ns") {
startTimestamp, err := strconv.ParseInt(ctx.String("start_time_ns"), 10, 64)
if err != nil {
return fmt.Errorf("error parsing start timestamp: %w", err)
}
filter.StartTimestampNs = startTimestamp
}
resp, err := client.ListSwaps(
context.Background(), &looprpc.ListSwapsRequest{
ListSwapFilter: filter,
MaxSwaps: ctx.Uint64("max_swaps"),
},
)
if err != nil {

View file

@ -2,11 +2,13 @@ package loopd
import (
"bytes"
"cmp"
"context"
"encoding/hex"
"errors"
"fmt"
"reflect"
"slices"
"sort"
"strings"
"sync"
@ -563,8 +565,11 @@ func (s *swapClientServer) ListSwaps(ctx context.Context,
req *looprpc.ListSwapsRequest) (*looprpc.ListSwapsResponse, error) {
var (
rpcSwaps = []*looprpc.SwapStatus{}
idx = 0
rpcSwaps = []*looprpc.SwapStatus{}
swapInfos = []*loop.SwapInfo{}
maxSwaps = int(req.MaxSwaps)
nextStartTime = int64(0)
canPage = false
)
s.swapsLock.Lock()
@ -580,14 +585,43 @@ func (s *swapClientServer) ListSwaps(ctx context.Context,
continue
}
rpcSwap, err := s.marshallSwap(ctx, &swp)
swapInfos = append(swapInfos, &swp)
}
// Sort the swaps by initiation time in ascending order (oldest first).
slices.SortFunc(swapInfos, func(a, b *loop.SwapInfo) int {
return cmp.Compare(
a.InitiationTime.UnixNano(),
b.InitiationTime.UnixNano(),
)
})
// Apply the maxSwaps limit if specified.
if maxSwaps > 0 && len(swapInfos) > maxSwaps {
canPage = true
swapInfos = swapInfos[:maxSwaps]
}
// Marshal the filtered and limited swaps.
for _, swp := range swapInfos {
rpcSwap, err := s.marshallSwap(ctx, swp)
if err != nil {
return nil, err
}
rpcSwaps = append(rpcSwaps, rpcSwap)
idx++
}
return &looprpc.ListSwapsResponse{Swaps: rpcSwaps}, nil
// Set the next start time for pagination if needed.
if canPage && len(rpcSwaps) > 0 {
// Use the initiation time of the last swap plus 1 nanosecond.
nextStartTime = rpcSwaps[len(rpcSwaps)-1].InitiationTime + 1
}
response := looprpc.ListSwapsResponse{
Swaps: rpcSwaps,
NextStartTime: nextStartTime,
}
return &response, nil
}
// filterSwap filters the given swap based on the provided filter.
@ -617,6 +651,13 @@ func filterSwap(swapInfo *loop.SwapInfo, filter *looprpc.ListSwapsFilter) bool {
return false
}
// If timestamp filters are set, only return swaps within the specified time range.
if filter.StartTimestampNs > 0 &&
swapInfo.InitiationTime.UnixNano() < filter.StartTimestampNs {
return false
}
// If the swap is of type loop out and the outgoing channel filter is
// set, we only return swaps that match the filter.
if swapInfo.SwapType == swap.TypeOut && filter.OutgoingChanSet != nil {

File diff suppressed because it is too large Load diff

View file

@ -675,6 +675,9 @@ enum FailureReason {
message ListSwapsRequest {
// Optional filter to only return swaps that match the filter.
ListSwapsFilter list_swap_filter = 1;
// Set a maximum number of swaps to return in the response.
uint64 max_swaps = 2;
}
message ListSwapsFilter {
@ -704,6 +707,9 @@ message ListSwapsFilter {
// If specified, only returns asset swaps.
bool asset_swap_only = 6;
// If specified, returns swaps initiated after this Unix (ns) timestamp.
int64 start_timestamp_ns = 7;
}
message ListSwapsResponse {
@ -711,6 +717,9 @@ message ListSwapsResponse {
The list of all currently known swaps and their status.
*/
repeated SwapStatus swaps = 1;
// Timestamp to use for paging start_timestamp_ns.
int64 next_start_time = 2;
}
message SwapInfoRequest {

View file

@ -625,6 +625,22 @@
"in": "query",
"required": false,
"type": "boolean"
},
{
"name": "list_swap_filter.start_timestamp_ns",
"description": "If specified, returns swaps initiated after this Unix (ns) timestamp.",
"in": "query",
"required": false,
"type": "string",
"format": "int64"
},
{
"name": "max_swaps",
"description": "Set a maximum number of swaps to return in the response.",
"in": "query",
"required": false,
"type": "string",
"format": "uint64"
}
],
"tags": [
@ -1403,6 +1419,11 @@
"asset_swap_only": {
"type": "boolean",
"description": "If specified, only returns asset swaps."
},
"start_timestamp_ns": {
"type": "string",
"format": "int64",
"description": "If specified, returns swaps initiated after this Unix (ns) timestamp."
}
}
},
@ -1415,6 +1436,11 @@
"$ref": "#/definitions/looprpcSwapStatus"
},
"description": "The list of all currently known swaps and their status."
},
"next_start_time": {
"type": "string",
"format": "int64",
"description": "Timestamp of the last swap returned."
}
}
},