Skip to content

chanevents: implement GetChannelEvents rpc#239

Merged
bitromortac merged 8 commits into
lightninglabs:faraday-forwarding-abilityfrom
bitromortac:2604-fwd-prep-9
May 12, 2026
Merged

chanevents: implement GetChannelEvents rpc#239
bitromortac merged 8 commits into
lightninglabs:faraday-forwarding-abilityfrom
bitromortac:2604-fwd-prep-9

Conversation

@bitromortac
Copy link
Copy Markdown
Contributor

Exposes recorded channel events via RPC and adds an itest to test correct channel update recording.

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a new RPC endpoint, GetChannelEvents, which allows users to retrieve a detailed history of channel lifecycle events such as online/offline status changes and balance updates. The changes include the necessary database query updates, RPC service definitions, CLI command implementation, and a comprehensive integration test to ensure the functionality works as expected.

Highlights

  • RPC Implementation: Implemented the GetChannelEvents RPC method to expose recorded channel lifecycle events.
  • API Expansion: Added new protobuf definitions for ChannelEventsRequest, ChannelEventsResponse, and ChannelEvent types.
  • CLI Support: Added a new 'chanevents' command to the frcli tool to allow users to query channel events.
  • Testing: Added an integration test to verify the correct recording and retrieval of channel events.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new GetChannelEvents RPC endpoint and a corresponding chanevents CLI command to retrieve lifecycle events for a specific Lightning channel. The implementation includes database updates for pagination support, server-side validation, and integration tests. Feedback highlights a potential issue with the pagination strategy where second-precision timestamps and inclusive queries could lead to duplicate events or infinite loops. Additionally, a contradiction in the integration tests was noted regarding exact event count assertions.

Comment thread frdrpc/faraday.proto Outdated
Comment thread itest/channel_events_test.go
@ViktorT-11 ViktorT-11 self-requested a review April 30, 2026 15:22
@lightninglabs-deploy
Copy link
Copy Markdown

@bitromortac, remember to re-request review from reviewers when ready

@bitromortac bitromortac force-pushed the 2604-fwd-prep-9 branch 3 times, most recently from 5fe1fb4 to d7a9e4e Compare May 7, 2026 09:23
Copy link
Copy Markdown
Contributor

@ViktorT-11 ViktorT-11 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally this PR looks great, so mainly leaving a question 🔥.

Additionally, you should add details to commit messages before this is merged.

WHERE channel_id = $1 AND timestamp >= $2 AND timestamp < $3
ORDER BY timestamp ASC, id ASC;
WHERE channel_id = $1
AND id > $2
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general this PR looks great, so I think this is my main comment for the PR:
Is it for performance reasons or that you think it's a better UI that you use the lastId approach over using an offset (i.e. something like LIMIT $5 OFFSET $6)? The approach choosen effects the rest of the PR.

I think the offset approach is much more common, and is what we use in litd for actions and I think potentially more natural for a user at it leaks no internals of how the ChannelEvent is stored (i.e. the id), and is also independent of the store used (i.e. not requiring that an incremental id is used).

Would therefore be interesting to hear your motivation behind this approach, which I do see some pros with as well!

Copy link
Copy Markdown
Contributor Author

@bitromortac bitromortac May 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Previously I had time-based pagination, but that didn't work well because of timestamp precision. I didn't really consider offset pagination, but a good point that this would be more consistent with other projects and doesn't expose internal state. However, I see that in the future we may want to delete older events, because I expect a constant stream of updates. In a new terminal web feature I think we also want to sync balances. With deletion I guess that it's easier to maintain the sync state by using the id. Let me know if you prefer the offset way, though I think the id approach has also speed advantages.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, ultimately I'll let you decide on which approach you'd want to use!

Personally I have no strong preference. I do think the offset approach is usually standard practise though, even when deletion is possible. I think the main drawback with the current approach is that it'll bind the store to always use an incremental id for the rows in the db.

But ultimately I think there are pros with the current approach as well, so I'll let you decide :)

Copy link
Copy Markdown
Contributor Author

@bitromortac bitromortac May 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I thought about it a bit more, I'd accept the small implementation leak for the performance and for incremental sync that survives head deletions (which I think isn't possible in a straightforward manner with an offset unless one introduces an additional absolute sync watermark value like a timestamp). Thanks for the input here!

Comment thread itest/channel_events_test.go
Comment thread chanevents/store.go
Logs every AddChannelEvent at trace level so operators can confirm
which lnd events are being persisted.
Switches the GetChannelEvents query from a timestamp-ordered scan to an
id-keyset cursor (WHERE id > $cursor ORDER BY id ASC LIMIT $n). The
keyset cursor is stable under concurrent inserts and survives a future
retention job that prunes the oldest rows: a positional OFFSET would
silently skip events whenever rows below the cursor are deleted, while
"id > $cursor" keeps advancing past whatever the caller has already
seen. The id field is documented in the proto as a server-assigned
monotonic identity, so callers persist last_id as their sync watermark.

Adds a (channel_id, id) composite index to back the new query; the
existing (channel_id, timestamp) index does not cover it and would
force a per-channel filter after a global id scan.
Adds the GetChannelEvents RPC to the proto and regenerates the gRPC,
gateway, swagger, and JSON stubs. The request carries a chan_point,
inclusive start_time and exclusive end_time bounds, a max_events cap,
and a last_id keyset cursor; the response echoes last_id and a has_more
flag so callers can drive pagination without server-side state.
Threads the chanevents.Store the daemon already constructs into the
frdrpcserver.Config so the upcoming GetChannelEvents handler has a
read path. Both standalone and subserver startup wire the same store
instance.
Implements the handler against the chanevents store. A zero end_time
defaults to the server's current wall clock so callers can omit it for
"up to now" queries. max_events is clamped to a 10000-row hard cap that
also serves as the implicit default when the caller leaves the field at
zero. An unknown chan_point maps to NotFound; negative time bounds and
start_time after end_time map to InvalidArgument. The response sets
has_more whenever the page filled to the requested limit so the client
knows to keep paginating.

Also registers the new endpoint in the macaroon permissions table
under the channels:read entitlement.
Drives a regtest channel through open, payment, and force-close and
asserts that GetChannelEvents surfaces the expected mix of online,
offline, and balance-update events. The same window is then walked
with a small page size to verify last_id round-trip, has_more
termination, and MaxEvents clamping in one pass.
Adds a `chanevents` subcommand that wraps GetChannelEvents and exposes
chan_point, start/end time, max_events, and last_id flags. The help
text documents the manual pagination contract: keep re-running with
--last_id set to the previous response's last_id until has_more is
false, while leaving --start_time and --end_time fixed across calls.
Copy link
Copy Markdown
Contributor

@ViktorT-11 ViktorT-11 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update and responding to my previous questions. I'm good to merge this as is if you prefer the current approach. uTACK LGTM 🔥

WHERE channel_id = $1 AND timestamp >= $2 AND timestamp < $3
ORDER BY timestamp ASC, id ASC;
WHERE channel_id = $1
AND id > $2
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, ultimately I'll let you decide on which approach you'd want to use!

Personally I have no strong preference. I do think the offset approach is usually standard practise though, even when deletion is possible. I think the main drawback with the current approach is that it'll bind the store to always use an incremental id for the rows in the db.

But ultimately I think there are pros with the current approach as well, so I'll let you decide :)

Comment thread itest/channel_events_test.go
@bitromortac bitromortac merged commit a568dfe into lightninglabs:faraday-forwarding-ability May 12, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants