Skip to content

Commit 12f979a

Browse files
committed
lnrpc: add filters to forwardhistoryrequest
This commit adds incoming and outgoing channel ids filter to forwarding history request to filter events received/forwarded from/to a particular channel
1 parent 6a3845b commit 12f979a

File tree

7 files changed

+1071
-802
lines changed

7 files changed

+1071
-802
lines changed

channeldb/forwarding_log.go

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"time"
88

99
"github.com/btcsuite/btcwallet/walletdb"
10+
"github.com/lightningnetwork/lnd/fn/v2"
1011
"github.com/lightningnetwork/lnd/kvdb"
1112
"github.com/lightningnetwork/lnd/lnwire"
1213
)
@@ -200,6 +201,16 @@ type ForwardingEventQuery struct {
200201

201202
// NumMaxEvents is the max number of events to return.
202203
NumMaxEvents uint32
204+
205+
// IncomingChanIds is the list of channels to filter HTLCs being
206+
// received from a particular channel.
207+
// If the list is empty, then it is ignored.
208+
IncomingChanIDs fn.Set[uint64]
209+
210+
// OutgoingChanIds is the list of channels to filter HTLCs being
211+
// forwarded to a particular channel.
212+
// If the list is empty, then it is ignored.
213+
OutgoingChanIDs fn.Set[uint64]
203214
}
204215

205216
// ForwardingLogTimeSlice is the response to a forwarding query. It includes
@@ -264,9 +275,12 @@ func (f *ForwardingLog) Query(q ForwardingEventQuery) (ForwardingLogTimeSlice, e
264275
return nil
265276
}
266277

267-
// If we're not yet past the user defined offset, then
278+
// If no incoming or outgoing channel IDs were provided
279+
// and we're not yet past the user defined offset, then
268280
// we'll continue to seek forward.
269-
if recordsToSkip > 0 {
281+
if recordsToSkip > 0 &&
282+
q.IncomingChanIDs.IsEmpty() &&
283+
q.OutgoingChanIDs.IsEmpty() {
270284
recordsToSkip--
271285
continue
272286
}
@@ -287,10 +301,42 @@ func (f *ForwardingLog) Query(q ForwardingEventQuery) (ForwardingLogTimeSlice, e
287301
return err
288302
}
289303

290-
event.Timestamp = currentTime
291-
resp.ForwardingEvents = append(resp.ForwardingEvents, event)
292-
293-
recordOffset++
304+
// Check if the incoming channel ID matches the
305+
// filter criteria.
306+
// Either no filtering is applied (IsEmpty), or
307+
// the ID is explicitly included.
308+
incomingMatch := q.IncomingChanIDs.IsEmpty() ||
309+
q.IncomingChanIDs.Contains(
310+
event.IncomingChanID.ToUint64(),
311+
)
312+
313+
// Check if the outgoing channel ID matches the
314+
// filter criteria.
315+
// Either no filtering is applied (IsEmpty), or
316+
// the ID is explicitly included.
317+
outgoingMatch := q.OutgoingChanIDs.IsEmpty() ||
318+
q.OutgoingChanIDs.Contains(
319+
event.OutgoingChanID.ToUint64(),
320+
)
321+
322+
// If both conditions are met, then we'll add
323+
// the event to our return payload.
324+
if incomingMatch && outgoingMatch {
325+
// If we're not yet past the user
326+
// defined offset , then we'll continue
327+
// to seek forward.
328+
if recordsToSkip > 0 {
329+
recordsToSkip--
330+
continue
331+
}
332+
333+
event.Timestamp = currentTime
334+
resp.ForwardingEvents = append(
335+
resp.ForwardingEvents,
336+
event,
337+
)
338+
recordOffset++
339+
}
294340
}
295341
}
296342

channeldb/forwarding_log_test.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"time"
88

99
"github.com/davecgh/go-spew/spew"
10+
"github.com/lightningnetwork/lnd/fn/v2"
1011
"github.com/lightningnetwork/lnd/lnwire"
1112
"github.com/stretchr/testify/assert"
1213
"github.com/stretchr/testify/require"
@@ -360,3 +361,131 @@ func TestForwardingLogStoreEvent(t *testing.T) {
360361
}
361362
}
362363
}
364+
365+
func TestForwardingLogQueryIncomingChanIDs(t *testing.T) {
366+
t.Parallel()
367+
368+
// Set up a test database.
369+
db, err := MakeTestDB(t)
370+
require.NoError(t, err, "unable to make test db")
371+
372+
log := ForwardingLog{
373+
db: db,
374+
}
375+
376+
initialTime := time.Unix(1234, 0)
377+
endTime := time.Unix(1234, 0)
378+
379+
// Create 10 random events with varying incoming ChanIDs.
380+
numEvents := 10
381+
events := make([]ForwardingEvent, numEvents)
382+
incomingChanIDs := []lnwire.ShortChannelID{
383+
lnwire.NewShortChanIDFromInt(2001),
384+
lnwire.NewShortChanIDFromInt(2002),
385+
lnwire.NewShortChanIDFromInt(2003),
386+
}
387+
388+
for i := 0; i < numEvents; i++ {
389+
events[i] = ForwardingEvent{
390+
Timestamp: endTime,
391+
IncomingChanID: incomingChanIDs[i%len(incomingChanIDs)],
392+
AmtIn: lnwire.MilliSatoshi(rand.Int63()),
393+
AmtOut: lnwire.MilliSatoshi(rand.Int63()),
394+
}
395+
endTime = endTime.Add(time.Minute * 10)
396+
}
397+
398+
// Add events to the database.
399+
require.NoError(t, log.AddForwardingEvents(events),
400+
"unable to add events")
401+
402+
// Query with multiple incoming channel IDs.
403+
eventQuery := ForwardingEventQuery{
404+
StartTime: initialTime,
405+
EndTime: endTime,
406+
IncomingChanIDs: fn.NewSet(incomingChanIDs[0].ToUint64(),
407+
incomingChanIDs[1].ToUint64()),
408+
IndexOffset: 0,
409+
NumMaxEvents: 10,
410+
}
411+
timeSlice, err := log.Query(eventQuery)
412+
require.NoError(t, err, "unable to query for events")
413+
414+
// Verify that only events with the specified incomingChanIDs are
415+
// returned.
416+
expectedEvents := []ForwardingEvent{}
417+
for _, e := range events {
418+
if e.IncomingChanID == incomingChanIDs[0] ||
419+
e.IncomingChanID == incomingChanIDs[1] {
420+
421+
expectedEvents = append(expectedEvents, e)
422+
}
423+
}
424+
425+
require.Equal(t, expectedEvents, timeSlice.ForwardingEvents,
426+
"unexpected events returned")
427+
}
428+
429+
func TestForwardingLogQueryOutgoingChanIDs(t *testing.T) {
430+
t.Parallel()
431+
432+
// Set up a test database.
433+
db, err := MakeTestDB(t)
434+
require.NoError(t, err, "unable to make test db")
435+
436+
log := ForwardingLog{
437+
db: db,
438+
}
439+
440+
initialTime := time.Unix(1234, 0)
441+
endTime := time.Unix(1234, 0)
442+
443+
// Create 10 random events with varying outgoing ChanIDs.
444+
numEvents := 10
445+
events := make([]ForwardingEvent, numEvents)
446+
outgoingChanIDs := []lnwire.ShortChannelID{
447+
lnwire.NewShortChanIDFromInt(2001),
448+
lnwire.NewShortChanIDFromInt(2002),
449+
lnwire.NewShortChanIDFromInt(2003),
450+
}
451+
452+
for i := 0; i < numEvents; i++ {
453+
events[i] = ForwardingEvent{
454+
Timestamp: endTime,
455+
OutgoingChanID: outgoingChanIDs[i%len(outgoingChanIDs)],
456+
AmtIn: lnwire.MilliSatoshi(rand.Int63()),
457+
AmtOut: lnwire.MilliSatoshi(rand.Int63()),
458+
}
459+
endTime = endTime.Add(time.Minute * 10)
460+
}
461+
462+
// Add events to the database.
463+
require.NoError(t, log.AddForwardingEvents(events),
464+
"unable to add events")
465+
466+
// Query with multiple outgoing channel IDs.
467+
eventQuery := ForwardingEventQuery{
468+
StartTime: initialTime,
469+
EndTime: endTime,
470+
OutgoingChanIDs: fn.NewSet(outgoingChanIDs[0].ToUint64(),
471+
outgoingChanIDs[1].ToUint64()),
472+
IndexOffset: 0,
473+
NumMaxEvents: 10,
474+
}
475+
timeSlice, err := log.Query(eventQuery)
476+
require.NoError(t, err, "unable to query for events")
477+
478+
// Verify that only events with the specified outgoingChanIDs are
479+
// returned.
480+
expectedEvents := []ForwardingEvent{}
481+
for _, e := range events {
482+
if e.OutgoingChanID == outgoingChanIDs[0] ||
483+
e.OutgoingChanID == outgoingChanIDs[1] {
484+
485+
expectedEvents = append(expectedEvents, e)
486+
}
487+
}
488+
489+
require.Equal(t, expectedEvents, timeSlice.ForwardingEvents,
490+
"unexpected events returned")
491+
}

cmd/commands/cmd_payments.go

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1471,10 +1471,11 @@ func listPayments(ctx *cli.Context) error {
14711471
}
14721472

14731473
var forwardingHistoryCommand = cli.Command{
1474-
Name: "fwdinghistory",
1475-
Category: "Payments",
1476-
Usage: "Query the history of all forwarded HTLCs.",
1477-
ArgsUsage: "start_time [end_time] [index_offset] [max_events]",
1474+
Name: "fwdinghistory",
1475+
Category: "Payments",
1476+
Usage: "Query the history of all forwarded HTLCs.",
1477+
ArgsUsage: "start_time [end_time] [index_offset] [max_events] " +
1478+
"[incoming_channel_ids] [outgoing_channel_ids]",
14781479
Description: `
14791480
Query the HTLC switch's internal forwarding log for all completed
14801481
payment circuits (HTLCs) over a particular time range (--start_time and
@@ -1489,6 +1490,9 @@ var forwardingHistoryCommand = cli.Command{
14891490
The max number of events returned is 50k. The default number is 100,
14901491
callers can use the --max_events param to modify this value.
14911492
1493+
Incoming and outgoing channel IDs can be provided to further filter
1494+
the events. If not provided, all events will be returned.
1495+
14921496
Finally, callers can skip a series of events using the --index_offset
14931497
parameter. Each response will contain the offset index of the last
14941498
entry. Using this callers can manually paginate within a time slice.
@@ -1517,6 +1521,16 @@ var forwardingHistoryCommand = cli.Command{
15171521
Usage: "skip the peer alias lookup per forwarding " +
15181522
"event in order to improve performance",
15191523
},
1524+
cli.Int64SliceFlag{
1525+
Name: "incoming_chan_ids",
1526+
Usage: "the short channel ids of the incoming " +
1527+
"channels to filter events by",
1528+
},
1529+
cli.Int64SliceFlag{
1530+
Name: "outgoing_chan_ids",
1531+
Usage: "the short channel ids of the outgoing " +
1532+
"channels to filter events by",
1533+
},
15201534
},
15211535
Action: actionDecorator(forwardingHistory),
15221536
}
@@ -1597,6 +1611,21 @@ func forwardingHistory(ctx *cli.Context) error {
15971611
NumMaxEvents: maxEvents,
15981612
PeerAliasLookup: lookupPeerAlias,
15991613
}
1614+
outChan := ctx.Int64Slice("outgoing_chan_ids")
1615+
if len(outChan) != 0 {
1616+
req.OutgoingChanIds = make([]uint64, len(outChan))
1617+
for i, c := range outChan {
1618+
req.OutgoingChanIds[i] = uint64(c)
1619+
}
1620+
}
1621+
1622+
inChan := ctx.Int64Slice("incoming_chan_ids")
1623+
if len(inChan) != 0 {
1624+
req.IncomingChanIds = make([]uint64, len(inChan))
1625+
for i, c := range inChan {
1626+
req.IncomingChanIds[i] = uint64(c)
1627+
}
1628+
}
16001629
resp, err := client.ForwardingHistory(ctxc, req)
16011630
if err != nil {
16021631
return err

0 commit comments

Comments
 (0)