From 26bdf9151bad7d0d910d9301a2d849f96977f3c8 Mon Sep 17 00:00:00 2001 From: bitromortac Date: Tue, 28 Apr 2026 15:32:19 +0200 Subject: [PATCH 1/8] chanevents: export channel error --- chanevents/monitor.go | 2 +- chanevents/store.go | 9 ++++++--- chanevents/store_test.go | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/chanevents/monitor.go b/chanevents/monitor.go index c83a65c..9e1c93e 100644 --- a/chanevents/monitor.go +++ b/chanevents/monitor.go @@ -333,7 +333,7 @@ func (m *Monitor) addChannel(ctx context.Context, pubKeyBytes route.Vertex, // Check if the channel already exists. channel, err := m.store.GetChannel(ctx, channelPoint) - if err != nil && !errors.Is(err, errUnknownChannel) { + if err != nil && !errors.Is(err, ErrUnknownChannel) { return fmt.Errorf("error getting channel %s: %w", channelPoint, err) } diff --git a/chanevents/store.go b/chanevents/store.go index 36f96d8..a73bb65 100644 --- a/chanevents/store.go +++ b/chanevents/store.go @@ -15,8 +15,11 @@ import ( ) var ( - errUnknownPeer = errors.New("unknown peer") - errUnknownChannel = errors.New("unknown channel") + errUnknownPeer = errors.New("unknown peer") + + // ErrUnknownChannel signals that the requested channel is not + // present in the store. + ErrUnknownChannel = errors.New("unknown channel") ) // Queries is a subset of the sqlc.Queries interface that can be used to @@ -167,7 +170,7 @@ func (s *Store) GetChannel(ctx context.Context, channelPoint string) (*Channel, dbChannel, err := s.db.GetChannelByChanPoint(ctx, channelPoint) if err != nil { if errors.Is(err, sql.ErrNoRows) { - return nil, errUnknownChannel + return nil, ErrUnknownChannel } return nil, fmt.Errorf("failed to get channel: %w", err) diff --git a/chanevents/store_test.go b/chanevents/store_test.go index 3446c5f..62cd848 100644 --- a/chanevents/store_test.go +++ b/chanevents/store_test.go @@ -83,7 +83,7 @@ func TestStore(t *testing.T) { // Get a non-existent channel and assert an error is returned. dbChannel, err := store.GetChannel(ctx, "non-existent-chan-point") - require.ErrorIs(t, err, errUnknownChannel) + require.ErrorIs(t, err, ErrUnknownChannel) require.Nil(t, dbChannel) // Get the channel and assert it is the same. From 629047fcb798781614ac73eaa2c7ad972013b079 Mon Sep 17 00:00:00 2001 From: bitromortac Date: Thu, 7 May 2026 10:07:15 +0200 Subject: [PATCH 2/8] chanevents: add trace logs Logs every AddChannelEvent at trace level so operators can confirm which lnd events are being persisted. --- chanevents/store.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/chanevents/store.go b/chanevents/store.go index a73bb65..bb470c9 100644 --- a/chanevents/store.go +++ b/chanevents/store.go @@ -188,6 +188,8 @@ func (s *Store) GetChannel(ctx context.Context, channelPoint string) (*Channel, func (s *Store) AddChannelEvent(ctx context.Context, event *ChannelEvent) error { + log.Tracef("Adding channel event: %+v", event) + var localBalance sql.NullInt64 event.LocalBalance.WhenSome( func(b btcutil.Amount) { From c1ada7a4a32548a37bf0c641c4214e8b7f212f52 Mon Sep 17 00:00:00 2001 From: bitromortac Date: Thu, 7 May 2026 10:07:10 +0200 Subject: [PATCH 3/8] db+chanevents: change to paginated GetChannelEvents 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. --- chanevents/store.go | 15 +++--- chanevents/store_test.go | 51 ++++++++++++++++++- db/sqlc/chanevents.sql.go | 18 +++++-- db/sqlc/migrations/000001_chanevents.down.sql | 1 + db/sqlc/migrations/000001_chanevents.up.sql | 13 +++-- db/sqlc/queries/chanevents.sql | 8 ++- 6 files changed, 89 insertions(+), 17 deletions(-) diff --git a/chanevents/store.go b/chanevents/store.go index bb470c9..e652faa 100644 --- a/chanevents/store.go +++ b/chanevents/store.go @@ -228,18 +228,21 @@ func (s *Store) AddChannelEvent(ctx context.Context, return nil } -// GetChannelEvents retrieves all events for a channel within a given time -// range. -// TODO: Add pagination support (LIMIT/OFFSET) to prevent OOM on high-traffic -// channels. -func (s *Store) GetChannelEvents(ctx context.Context, channelID int64, - startTime, endTime time.Time) ([]*ChannelEvent, error) { +// GetChannelEvents returns up to limit events for a channel where +// id > afterID AND startTime <= timestamp < endTime, ordered by id ASC. +// Pass afterID = 0 on the first call; for subsequent calls pass the +// previous page's last event id. The (startTime, endTime) bounds are +// independent filters and do not need to advance between pages. +func (s *Store) GetChannelEvents(ctx context.Context, channelID, afterID int64, + startTime, endTime time.Time, limit int32) ([]*ChannelEvent, error) { dbEvents, err := s.db.GetChannelEvents( ctx, sqlc.GetChannelEventsParams{ ChannelID: channelID, + ID: afterID, Timestamp: startTime.UTC(), Timestamp_2: endTime.UTC(), + Limit: limit, }, ) if err != nil { diff --git a/chanevents/store_test.go b/chanevents/store_test.go index 62cd848..86f025a 100644 --- a/chanevents/store_test.go +++ b/chanevents/store_test.go @@ -125,7 +125,7 @@ func TestStore(t *testing.T) { // Get the channel events and assert they are correct. events, err := store.GetChannelEvents( - ctx, channelID, time.Unix(0, 0), time.Unix(3, 0), + ctx, channelID, 0, time.Unix(0, 0), time.Unix(3, 0), 100, ) require.NoError(t, err) require.Len(t, events, 2) @@ -148,7 +148,7 @@ func TestStore(t *testing.T) { require.NoError(t, err) events, err = store.GetChannelEvents( - ctx, channelID, time.Unix(0, 0), time.Unix(4, 0), + ctx, channelID, 0, time.Unix(0, 0), time.Unix(4, 0), 100, ) require.NoError(t, err) require.Len(t, events, 3) @@ -157,3 +157,50 @@ func TestStore(t *testing.T) { t, syncEvent, testTime.Add(2*time.Second), events[2], ) } + +// TestPagination verifies that the keyset cursor advances correctly across +// events sharing one second-resolution timestamp. +func TestPagination(t *testing.T) { + t.Parallel() + + clock := clock.NewTestClock(testTime) + store := NewTestDB(t, clock) + ctx := context.Background() + + peerID, err := store.AddPeer(ctx, testPubKey) + require.NoError(t, err) + + channelID, err := store.AddChannel( + ctx, testChanPoint1, testShortChanID1, peerID, + ) + require.NoError(t, err) + + sameTime := testTime.Add(10 * time.Second) + for i := 0; i < 5; i++ { + err = store.AddChannelEvent(ctx, &ChannelEvent{ + ChannelID: channelID, + EventType: EventTypeUpdate, + Timestamp: sameTime, + LocalBalance: fn.Some(btcutil.Amount(i)), + }) + require.NoError(t, err) + } + + endTime := sameTime.Add(time.Hour) + + page1, err := store.GetChannelEvents( + ctx, channelID, 0, time.Unix(0, 0), endTime, 3, + ) + require.NoError(t, err) + require.Len(t, page1, 3) + + page2, err := store.GetChannelEvents( + ctx, channelID, page1[len(page1)-1].ID, + time.Unix(0, 0), endTime, 3, + ) + require.NoError(t, err) + require.Len(t, page2, 2) + + require.Equal(t, btcutil.Amount(3), page2[0].LocalBalance.UnwrapOr(0)) + require.Equal(t, btcutil.Amount(4), page2[1].LocalBalance.UnwrapOr(0)) +} diff --git a/db/sqlc/chanevents.sql.go b/db/sqlc/chanevents.sql.go index 5c7d5a5..9a24296 100644 --- a/db/sqlc/chanevents.sql.go +++ b/db/sqlc/chanevents.sql.go @@ -45,18 +45,30 @@ func (q *Queries) GetChannelByShortChanID(ctx context.Context, shortChannelID in const getChannelEvents = `-- name: GetChannelEvents :many SELECT id, channel_id, event_type, timestamp, local_balance_sat, remote_balance_sat, is_sync FROM channel_events -WHERE channel_id = $1 AND timestamp >= $2 AND timestamp < $3 -ORDER BY timestamp ASC, id ASC +WHERE channel_id = $1 + AND id > $2 + AND timestamp >= $3 + AND timestamp < $4 +ORDER BY id ASC +LIMIT $5 ` type GetChannelEventsParams struct { ChannelID int64 + ID int64 Timestamp time.Time Timestamp_2 time.Time + Limit int32 } func (q *Queries) GetChannelEvents(ctx context.Context, arg GetChannelEventsParams) ([]ChannelEvent, error) { - rows, err := q.db.QueryContext(ctx, getChannelEvents, arg.ChannelID, arg.Timestamp, arg.Timestamp_2) + rows, err := q.db.QueryContext(ctx, getChannelEvents, + arg.ChannelID, + arg.ID, + arg.Timestamp, + arg.Timestamp_2, + arg.Limit, + ) if err != nil { return nil, err } diff --git a/db/sqlc/migrations/000001_chanevents.down.sql b/db/sqlc/migrations/000001_chanevents.down.sql index df2c627..c755663 100644 --- a/db/sqlc/migrations/000001_chanevents.down.sql +++ b/db/sqlc/migrations/000001_chanevents.down.sql @@ -1,3 +1,4 @@ +DROP INDEX IF EXISTS channel_events_chan_id_id_idx; DROP INDEX IF EXISTS channel_events_chan_id_ts_idx; DROP TABLE IF EXISTS channel_events; DROP INDEX IF EXISTS channel_peer_idx; diff --git a/db/sqlc/migrations/000001_chanevents.up.sql b/db/sqlc/migrations/000001_chanevents.up.sql index c4cf35e..9ed6995 100644 --- a/db/sqlc/migrations/000001_chanevents.up.sql +++ b/db/sqlc/migrations/000001_chanevents.up.sql @@ -41,8 +41,13 @@ CREATE TABLE IF NOT EXISTS channel_events ( is_sync BOOLEAN NOT NULL DEFAULT FALSE ); --- This composite index is crucial for efficiently querying the event history --- of a specific channel. It allows the database to quickly locate relevant rows --- for a given channel, sorted by time. This is useful for fetching events --- within a time range, and for finding the latest event before a certain time. +-- This composite index supports the chronological access patterns +-- (GetChannelEventsIter, GetLatestChannelEventBefore): events for a given +-- channel sorted by time, with a per-channel time-range scan. CREATE INDEX IF NOT EXISTS channel_events_chan_id_ts_idx ON channel_events (channel_id, timestamp); + +-- This composite index supports the public GetChannelEvents query, which +-- walks events for a given channel by id-keyset cursor (ORDER BY id ASC, +-- WHERE id > $cursor). Without it, the planner would scan every event with +-- id > $cursor across all channels and filter by channel_id afterwards. +CREATE INDEX IF NOT EXISTS channel_events_chan_id_id_idx ON channel_events (channel_id, id); diff --git a/db/sqlc/queries/chanevents.sql b/db/sqlc/queries/chanevents.sql index c615421..186c8d7 100644 --- a/db/sqlc/queries/chanevents.sql +++ b/db/sqlc/queries/chanevents.sql @@ -21,5 +21,9 @@ INSERT INTO channel_events ( -- name: GetChannelEvents :many SELECT * FROM channel_events -WHERE channel_id = $1 AND timestamp >= $2 AND timestamp < $3 -ORDER BY timestamp ASC, id ASC; +WHERE channel_id = $1 + AND id > $2 + AND timestamp >= $3 + AND timestamp < $4 +ORDER BY id ASC +LIMIT $5; From 9e10faa805814f893ed473295c0de825476d45f9 Mon Sep 17 00:00:00 2001 From: bitromortac Date: Wed, 6 May 2026 12:04:37 +0200 Subject: [PATCH 4/8] frdrpc: add GetChannelEvents 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. --- frdrpc/faraday.pb.go | 660 +++++++++++++++++++++++++------- frdrpc/faraday.pb.gw.go | 77 ++++ frdrpc/faraday.proto | 79 ++++ frdrpc/faraday.swagger.json | 124 ++++++ frdrpc/faraday.yaml | 3 + frdrpc/faraday_grpc.pb.go | 40 ++ frdrpc/faradayserver.pb.json.go | 25 ++ go.mod | 3 + go.sum | 2 - 9 files changed, 873 insertions(+), 140 deletions(-) diff --git a/frdrpc/faraday.pb.go b/frdrpc/faraday.pb.go index dfcb0bb..5164bde 100644 --- a/frdrpc/faraday.pb.go +++ b/frdrpc/faraday.pb.go @@ -267,6 +267,62 @@ func (EntryType) EnumDescriptor() ([]byte, []int) { return file_faraday_proto_rawDescGZIP(), []int{2} } +type ChannelEventType int32 + +const ( + // An unknown event type. + ChannelEventType_CHAN_EVENT_UNKNOWN ChannelEventType = 0 + // An online event. + ChannelEventType_CHAN_EVENT_ONLINE ChannelEventType = 1 + // An offline event. + ChannelEventType_CHAN_EVENT_OFFLINE ChannelEventType = 2 + // A channel balance update event. + ChannelEventType_CHAN_EVENT_UPDATE ChannelEventType = 3 +) + +// Enum value maps for ChannelEventType. +var ( + ChannelEventType_name = map[int32]string{ + 0: "CHAN_EVENT_UNKNOWN", + 1: "CHAN_EVENT_ONLINE", + 2: "CHAN_EVENT_OFFLINE", + 3: "CHAN_EVENT_UPDATE", + } + ChannelEventType_value = map[string]int32{ + "CHAN_EVENT_UNKNOWN": 0, + "CHAN_EVENT_ONLINE": 1, + "CHAN_EVENT_OFFLINE": 2, + "CHAN_EVENT_UPDATE": 3, + } +) + +func (x ChannelEventType) Enum() *ChannelEventType { + p := new(ChannelEventType) + *p = x + return p +} + +func (x ChannelEventType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ChannelEventType) Descriptor() protoreflect.EnumDescriptor { + return file_faraday_proto_enumTypes[3].Descriptor() +} + +func (ChannelEventType) Type() protoreflect.EnumType { + return &file_faraday_proto_enumTypes[3] +} + +func (x ChannelEventType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ChannelEventType.Descriptor instead. +func (ChannelEventType) EnumDescriptor() ([]byte, []int) { + return file_faraday_proto_rawDescGZIP(), []int{3} +} + type CloseRecommendationRequest_Metric int32 const ( @@ -309,11 +365,11 @@ func (x CloseRecommendationRequest_Metric) String() string { } func (CloseRecommendationRequest_Metric) Descriptor() protoreflect.EnumDescriptor { - return file_faraday_proto_enumTypes[3].Descriptor() + return file_faraday_proto_enumTypes[4].Descriptor() } func (CloseRecommendationRequest_Metric) Type() protoreflect.EnumType { - return &file_faraday_proto_enumTypes[3] + return &file_faraday_proto_enumTypes[4] } func (x CloseRecommendationRequest_Metric) Number() protoreflect.EnumNumber { @@ -1912,6 +1968,248 @@ func (x *CloseReportResponse) GetCloseFee() string { return "" } +type ChannelEventsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The channel point of the channel to get events for, formatted txid:outpoint. + ChanPoint string `protobuf:"bytes,1,opt,name=chan_point,json=chanPoint,proto3" json:"chan_point,omitempty"` + // Lower time bound, inclusive, as Unix seconds. Independent filter — does + // not need to advance between paginated calls. Zero means no lower bound. + StartTime int64 `protobuf:"varint,2,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + // Upper time bound, exclusive, as Unix seconds. Must be greater than or + // equal to start_time. Zero means "use the server's current time". + EndTime int64 `protobuf:"varint,3,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` + // The maximum number of events to return. If zero, the server default is + // used. The server enforces a hard cap; values above the cap are clamped. + MaxEvents uint32 `protobuf:"varint,4,opt,name=max_events,json=maxEvents,proto3" json:"max_events,omitempty"` + // Pagination cursor: id of the last event from the previous response. + // Pass zero on the first call; for subsequent calls pass the response's + // last_id to resume past already-returned events. + LastId int64 `protobuf:"varint,5,opt,name=last_id,json=lastId,proto3" json:"last_id,omitempty"` +} + +func (x *ChannelEventsRequest) Reset() { + *x = ChannelEventsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_faraday_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ChannelEventsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ChannelEventsRequest) ProtoMessage() {} + +func (x *ChannelEventsRequest) ProtoReflect() protoreflect.Message { + mi := &file_faraday_proto_msgTypes[22] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ChannelEventsRequest.ProtoReflect.Descriptor instead. +func (*ChannelEventsRequest) Descriptor() ([]byte, []int) { + return file_faraday_proto_rawDescGZIP(), []int{22} +} + +func (x *ChannelEventsRequest) GetChanPoint() string { + if x != nil { + return x.ChanPoint + } + return "" +} + +func (x *ChannelEventsRequest) GetStartTime() int64 { + if x != nil { + return x.StartTime + } + return 0 +} + +func (x *ChannelEventsRequest) GetEndTime() int64 { + if x != nil { + return x.EndTime + } + return 0 +} + +func (x *ChannelEventsRequest) GetMaxEvents() uint32 { + if x != nil { + return x.MaxEvents + } + return 0 +} + +func (x *ChannelEventsRequest) GetLastId() int64 { + if x != nil { + return x.LastId + } + return 0 +} + +type ChannelEventsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The list of channel events. + Events []*ChannelEvent `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"` + // Id of the last event returned, suitable as the next request's last_id. + // Zero when events is empty. + LastId int64 `protobuf:"varint,2,opt,name=last_id,json=lastId,proto3" json:"last_id,omitempty"` + // True when the page filled to the requested limit and more events may be + // available; callers should keep paginating until this is false. + HasMore bool `protobuf:"varint,3,opt,name=has_more,json=hasMore,proto3" json:"has_more,omitempty"` +} + +func (x *ChannelEventsResponse) Reset() { + *x = ChannelEventsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_faraday_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ChannelEventsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ChannelEventsResponse) ProtoMessage() {} + +func (x *ChannelEventsResponse) ProtoReflect() protoreflect.Message { + mi := &file_faraday_proto_msgTypes[23] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ChannelEventsResponse.ProtoReflect.Descriptor instead. +func (*ChannelEventsResponse) Descriptor() ([]byte, []int) { + return file_faraday_proto_rawDescGZIP(), []int{23} +} + +func (x *ChannelEventsResponse) GetEvents() []*ChannelEvent { + if x != nil { + return x.Events + } + return nil +} + +func (x *ChannelEventsResponse) GetLastId() int64 { + if x != nil { + return x.LastId + } + return 0 +} + +func (x *ChannelEventsResponse) GetHasMore() bool { + if x != nil { + return x.HasMore + } + return false +} + +type ChannelEvent struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The timestamp of the event, as Unix seconds. + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + // The type of the event. + EventType ChannelEventType `protobuf:"varint,2,opt,name=event_type,json=eventType,proto3,enum=frdrpc.ChannelEventType" json:"event_type,omitempty"` + // The channel's local balance at the time of the event in sat. + LocalBalance uint64 `protobuf:"varint,3,opt,name=local_balance,json=localBalance,proto3" json:"local_balance,omitempty"` + // The channel's remote balance at the time of the event in sat. + RemoteBalance uint64 `protobuf:"varint,4,opt,name=remote_balance,json=remoteBalance,proto3" json:"remote_balance,omitempty"` + // Server-assigned monotonic identity. Echo this back as the next + // request's last_id when paginating. + Id int64 `protobuf:"varint,5,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *ChannelEvent) Reset() { + *x = ChannelEvent{} + if protoimpl.UnsafeEnabled { + mi := &file_faraday_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ChannelEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ChannelEvent) ProtoMessage() {} + +func (x *ChannelEvent) ProtoReflect() protoreflect.Message { + mi := &file_faraday_proto_msgTypes[24] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ChannelEvent.ProtoReflect.Descriptor instead. +func (*ChannelEvent) Descriptor() ([]byte, []int) { + return file_faraday_proto_rawDescGZIP(), []int{24} +} + +func (x *ChannelEvent) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *ChannelEvent) GetEventType() ChannelEventType { + if x != nil { + return x.EventType + } + return ChannelEventType_CHAN_EVENT_UNKNOWN +} + +func (x *ChannelEvent) GetLocalBalance() uint64 { + if x != nil { + return x.LocalBalance + } + return 0 +} + +func (x *ChannelEvent) GetRemoteBalance() uint64 { + if x != nil { + return x.RemoteBalance + } + return 0 +} + +func (x *ChannelEvent) GetId() int64 { + if x != nil { + return x.Id + } + return 0 +} + var File_faraday_proto protoreflect.FileDescriptor var file_faraday_proto_rawDesc = []byte{ @@ -2144,83 +2442,125 @@ var file_faraday_proto_rawDesc = []byte{ 0x6e, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x70, 0x65, 0x6e, 0x46, 0x65, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x46, 0x65, - 0x65, 0x2a, 0xa1, 0x01, 0x0a, 0x0b, 0x47, 0x72, 0x61, 0x6e, 0x75, 0x6c, 0x61, 0x72, 0x69, 0x74, - 0x79, 0x12, 0x17, 0x0a, 0x13, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x47, 0x52, 0x41, - 0x4e, 0x55, 0x4c, 0x41, 0x52, 0x49, 0x54, 0x59, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x49, - 0x4e, 0x55, 0x54, 0x45, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x46, 0x49, 0x56, 0x45, 0x5f, 0x4d, - 0x49, 0x4e, 0x55, 0x54, 0x45, 0x53, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x46, 0x49, 0x46, 0x54, - 0x45, 0x45, 0x4e, 0x5f, 0x4d, 0x49, 0x4e, 0x55, 0x54, 0x45, 0x53, 0x10, 0x03, 0x12, 0x12, 0x0a, - 0x0e, 0x54, 0x48, 0x49, 0x52, 0x54, 0x59, 0x5f, 0x4d, 0x49, 0x4e, 0x55, 0x54, 0x45, 0x53, 0x10, - 0x04, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x4f, 0x55, 0x52, 0x10, 0x05, 0x12, 0x0d, 0x0a, 0x09, 0x53, - 0x49, 0x58, 0x5f, 0x48, 0x4f, 0x55, 0x52, 0x53, 0x10, 0x06, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x57, - 0x45, 0x4c, 0x56, 0x45, 0x5f, 0x48, 0x4f, 0x55, 0x52, 0x53, 0x10, 0x07, 0x12, 0x07, 0x0a, 0x03, - 0x44, 0x41, 0x59, 0x10, 0x08, 0x2a, 0x6a, 0x0a, 0x0b, 0x46, 0x69, 0x61, 0x74, 0x42, 0x61, 0x63, - 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x17, 0x0a, 0x13, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, - 0x46, 0x49, 0x41, 0x54, 0x42, 0x41, 0x43, 0x4b, 0x45, 0x4e, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, - 0x07, 0x43, 0x4f, 0x49, 0x4e, 0x43, 0x41, 0x50, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x4f, - 0x49, 0x4e, 0x44, 0x45, 0x53, 0x4b, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x55, 0x53, 0x54, - 0x4f, 0x4d, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x49, 0x4e, 0x47, 0x45, 0x43, 0x4b, - 0x4f, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x42, 0x49, 0x54, 0x46, 0x49, 0x4e, 0x45, 0x58, 0x10, - 0x05, 0x2a, 0xa2, 0x02, 0x0a, 0x09, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, - 0x4c, 0x4f, 0x43, 0x41, 0x4c, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, 0x4f, 0x50, - 0x45, 0x4e, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x43, - 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, 0x4f, 0x50, 0x45, 0x4e, 0x10, 0x02, 0x12, 0x14, 0x0a, - 0x10, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, 0x4f, 0x50, 0x45, 0x4e, 0x5f, 0x46, 0x45, - 0x45, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, 0x43, - 0x4c, 0x4f, 0x53, 0x45, 0x10, 0x04, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x43, 0x45, 0x49, 0x50, - 0x54, 0x10, 0x05, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x06, - 0x12, 0x07, 0x0a, 0x03, 0x46, 0x45, 0x45, 0x10, 0x07, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x49, 0x52, - 0x43, 0x55, 0x4c, 0x41, 0x52, 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, 0x50, 0x54, 0x10, 0x08, 0x12, - 0x0b, 0x0a, 0x07, 0x46, 0x4f, 0x52, 0x57, 0x41, 0x52, 0x44, 0x10, 0x09, 0x12, 0x0f, 0x0a, 0x0b, - 0x46, 0x4f, 0x52, 0x57, 0x41, 0x52, 0x44, 0x5f, 0x46, 0x45, 0x45, 0x10, 0x0a, 0x12, 0x14, 0x0a, - 0x10, 0x43, 0x49, 0x52, 0x43, 0x55, 0x4c, 0x41, 0x52, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, - 0x54, 0x10, 0x0b, 0x12, 0x10, 0x0a, 0x0c, 0x43, 0x49, 0x52, 0x43, 0x55, 0x4c, 0x41, 0x52, 0x5f, - 0x46, 0x45, 0x45, 0x10, 0x0c, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x57, 0x45, 0x45, 0x50, 0x10, 0x0d, - 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x57, 0x45, 0x45, 0x50, 0x5f, 0x46, 0x45, 0x45, 0x10, 0x0e, 0x12, - 0x15, 0x0a, 0x11, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, 0x43, 0x4c, 0x4f, 0x53, 0x45, - 0x5f, 0x46, 0x45, 0x45, 0x10, 0x0f, 0x32, 0xd8, 0x04, 0x0a, 0x0d, 0x46, 0x61, 0x72, 0x61, 0x64, - 0x61, 0x79, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x65, 0x0a, 0x16, 0x4f, 0x75, 0x74, 0x6c, - 0x69, 0x65, 0x72, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x12, 0x25, 0x2e, 0x66, 0x72, 0x64, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x6c, - 0x69, 0x65, 0x72, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x66, 0x72, 0x64, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, - 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x69, 0x0a, 0x18, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x52, 0x65, 0x63, 0x6f, - 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x27, 0x2e, 0x66, 0x72, - 0x64, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x52, 0x65, + 0x65, 0x22, 0xa7, 0x01, 0x0a, 0x14, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, + 0x61, 0x6e, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x63, 0x68, 0x61, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, + 0x69, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6d, 0x61, 0x78, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x06, 0x6c, 0x61, 0x73, 0x74, 0x49, 0x64, 0x22, 0x79, 0x0a, 0x15, 0x43, + 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x66, 0x72, 0x64, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, + 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x06, 0x6c, 0x61, 0x73, 0x74, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x68, + 0x61, 0x73, 0x5f, 0x6d, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x68, + 0x61, 0x73, 0x4d, 0x6f, 0x72, 0x65, 0x22, 0xc1, 0x01, 0x0a, 0x0c, 0x43, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x37, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x66, 0x72, 0x64, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x23, + 0x0a, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x42, 0x61, 0x6c, 0x61, + 0x6e, 0x63, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x62, 0x61, + 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x72, 0x65, 0x6d, + 0x6f, 0x74, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x2a, 0xa1, 0x01, 0x0a, 0x0b, 0x47, + 0x72, 0x61, 0x6e, 0x75, 0x6c, 0x61, 0x72, 0x69, 0x74, 0x79, 0x12, 0x17, 0x0a, 0x13, 0x55, 0x4e, + 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x47, 0x52, 0x41, 0x4e, 0x55, 0x4c, 0x41, 0x52, 0x49, 0x54, + 0x59, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x49, 0x4e, 0x55, 0x54, 0x45, 0x10, 0x01, 0x12, + 0x10, 0x0a, 0x0c, 0x46, 0x49, 0x56, 0x45, 0x5f, 0x4d, 0x49, 0x4e, 0x55, 0x54, 0x45, 0x53, 0x10, + 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x46, 0x49, 0x46, 0x54, 0x45, 0x45, 0x4e, 0x5f, 0x4d, 0x49, 0x4e, + 0x55, 0x54, 0x45, 0x53, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, 0x54, 0x48, 0x49, 0x52, 0x54, 0x59, + 0x5f, 0x4d, 0x49, 0x4e, 0x55, 0x54, 0x45, 0x53, 0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x4f, + 0x55, 0x52, 0x10, 0x05, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x49, 0x58, 0x5f, 0x48, 0x4f, 0x55, 0x52, + 0x53, 0x10, 0x06, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x57, 0x45, 0x4c, 0x56, 0x45, 0x5f, 0x48, 0x4f, + 0x55, 0x52, 0x53, 0x10, 0x07, 0x12, 0x07, 0x0a, 0x03, 0x44, 0x41, 0x59, 0x10, 0x08, 0x2a, 0x6a, + 0x0a, 0x0b, 0x46, 0x69, 0x61, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x17, 0x0a, + 0x13, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x46, 0x49, 0x41, 0x54, 0x42, 0x41, 0x43, + 0x4b, 0x45, 0x4e, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x49, 0x4e, 0x43, 0x41, + 0x50, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x4f, 0x49, 0x4e, 0x44, 0x45, 0x53, 0x4b, 0x10, + 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x10, 0x03, 0x12, 0x0d, 0x0a, + 0x09, 0x43, 0x4f, 0x49, 0x4e, 0x47, 0x45, 0x43, 0x4b, 0x4f, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, + 0x42, 0x49, 0x54, 0x46, 0x49, 0x4e, 0x45, 0x58, 0x10, 0x05, 0x2a, 0xa2, 0x02, 0x0a, 0x09, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, + 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x4c, 0x4f, 0x43, 0x41, 0x4c, 0x5f, 0x43, + 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, 0x4f, 0x50, 0x45, 0x4e, 0x10, 0x01, 0x12, 0x17, 0x0a, + 0x13, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, + 0x4f, 0x50, 0x45, 0x4e, 0x10, 0x02, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, + 0x4c, 0x5f, 0x4f, 0x50, 0x45, 0x4e, 0x5f, 0x46, 0x45, 0x45, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, + 0x43, 0x48, 0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x5f, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x10, 0x04, 0x12, + 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x43, 0x45, 0x49, 0x50, 0x54, 0x10, 0x05, 0x12, 0x0b, 0x0a, 0x07, + 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x06, 0x12, 0x07, 0x0a, 0x03, 0x46, 0x45, 0x45, + 0x10, 0x07, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x49, 0x52, 0x43, 0x55, 0x4c, 0x41, 0x52, 0x5f, 0x52, + 0x45, 0x43, 0x45, 0x49, 0x50, 0x54, 0x10, 0x08, 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x4f, 0x52, 0x57, + 0x41, 0x52, 0x44, 0x10, 0x09, 0x12, 0x0f, 0x0a, 0x0b, 0x46, 0x4f, 0x52, 0x57, 0x41, 0x52, 0x44, + 0x5f, 0x46, 0x45, 0x45, 0x10, 0x0a, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x49, 0x52, 0x43, 0x55, 0x4c, + 0x41, 0x52, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x0b, 0x12, 0x10, 0x0a, 0x0c, + 0x43, 0x49, 0x52, 0x43, 0x55, 0x4c, 0x41, 0x52, 0x5f, 0x46, 0x45, 0x45, 0x10, 0x0c, 0x12, 0x09, + 0x0a, 0x05, 0x53, 0x57, 0x45, 0x45, 0x50, 0x10, 0x0d, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x57, 0x45, + 0x45, 0x50, 0x5f, 0x46, 0x45, 0x45, 0x10, 0x0e, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x48, 0x41, 0x4e, + 0x4e, 0x45, 0x4c, 0x5f, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x5f, 0x46, 0x45, 0x45, 0x10, 0x0f, 0x2a, + 0x70, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x43, 0x48, 0x41, 0x4e, 0x5f, 0x45, 0x56, 0x45, 0x4e, + 0x54, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x43, + 0x48, 0x41, 0x4e, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x4f, 0x4e, 0x4c, 0x49, 0x4e, 0x45, + 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x43, 0x48, 0x41, 0x4e, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, + 0x5f, 0x4f, 0x46, 0x46, 0x4c, 0x49, 0x4e, 0x45, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x43, 0x48, + 0x41, 0x4e, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, + 0x03, 0x32, 0xa9, 0x05, 0x0a, 0x0d, 0x46, 0x61, 0x72, 0x61, 0x64, 0x61, 0x79, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x12, 0x65, 0x0a, 0x16, 0x4f, 0x75, 0x74, 0x6c, 0x69, 0x65, 0x72, 0x52, 0x65, + 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x2e, + 0x66, 0x72, 0x64, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x6c, 0x69, 0x65, 0x72, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x66, 0x72, 0x64, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0d, 0x52, 0x65, - 0x76, 0x65, 0x6e, 0x75, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1c, 0x2e, 0x66, 0x72, - 0x64, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x76, 0x65, 0x6e, 0x75, 0x65, 0x52, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x66, 0x72, 0x64, 0x72, - 0x70, 0x63, 0x2e, 0x52, 0x65, 0x76, 0x65, 0x6e, 0x75, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0f, 0x43, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x73, 0x69, 0x67, 0x68, 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x66, 0x72, - 0x64, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x73, 0x69, - 0x67, 0x68, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x66, 0x72, - 0x64, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x73, 0x69, - 0x67, 0x68, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0c, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x2e, 0x66, - 0x72, 0x64, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x66, 0x72, 0x64, 0x72, - 0x70, 0x63, 0x2e, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x09, 0x4e, 0x6f, 0x64, 0x65, 0x41, - 0x75, 0x64, 0x69, 0x74, 0x12, 0x18, 0x2e, 0x66, 0x72, 0x64, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, - 0x64, 0x65, 0x41, 0x75, 0x64, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, - 0x2e, 0x66, 0x72, 0x64, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x75, 0x64, 0x69, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x43, 0x6c, 0x6f, - 0x73, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1a, 0x2e, 0x66, 0x72, 0x64, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x66, 0x72, 0x64, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, - 0x6f, 0x73, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x66, 0x61, - 0x72, 0x61, 0x64, 0x61, 0x79, 0x2f, 0x66, 0x72, 0x64, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a, 0x18, 0x54, 0x68, + 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x27, 0x2e, 0x66, 0x72, 0x64, 0x72, 0x70, 0x63, 0x2e, + 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x52, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, + 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x24, 0x2e, 0x66, 0x72, 0x64, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, + 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0d, 0x52, 0x65, 0x76, 0x65, 0x6e, 0x75, 0x65, + 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1c, 0x2e, 0x66, 0x72, 0x64, 0x72, 0x70, 0x63, 0x2e, + 0x52, 0x65, 0x76, 0x65, 0x6e, 0x75, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x66, 0x72, 0x64, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, + 0x76, 0x65, 0x6e, 0x75, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x6e, + 0x73, 0x69, 0x67, 0x68, 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x66, 0x72, 0x64, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x73, 0x69, 0x67, 0x68, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x66, 0x72, 0x64, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x73, 0x69, 0x67, 0x68, 0x74, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0c, 0x45, 0x78, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x52, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x2e, 0x66, 0x72, 0x64, 0x72, 0x70, 0x63, + 0x2e, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x66, 0x72, 0x64, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x40, 0x0a, 0x09, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x75, 0x64, 0x69, 0x74, 0x12, + 0x18, 0x2e, 0x66, 0x72, 0x64, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x75, 0x64, + 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x66, 0x72, 0x64, 0x72, + 0x70, 0x63, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x75, 0x64, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0b, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x12, 0x1a, 0x2e, 0x66, 0x72, 0x64, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, + 0x73, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1b, 0x2e, 0x66, 0x72, 0x64, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x10, + 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x12, 0x1c, 0x2e, 0x66, 0x72, 0x64, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, + 0x2e, 0x66, 0x72, 0x64, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x29, 0x5a, + 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, + 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x66, 0x61, 0x72, 0x61, 0x64, 0x61, + 0x79, 0x2f, 0x66, 0x72, 0x64, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2235,77 +2575,85 @@ func file_faraday_proto_rawDescGZIP() []byte { return file_faraday_proto_rawDescData } -var file_faraday_proto_enumTypes = make([]protoimpl.EnumInfo, 4) -var file_faraday_proto_msgTypes = make([]protoimpl.MessageInfo, 23) +var file_faraday_proto_enumTypes = make([]protoimpl.EnumInfo, 5) +var file_faraday_proto_msgTypes = make([]protoimpl.MessageInfo, 26) var file_faraday_proto_goTypes = []any{ (Granularity)(0), // 0: frdrpc.Granularity (FiatBackend)(0), // 1: frdrpc.FiatBackend (EntryType)(0), // 2: frdrpc.EntryType - (CloseRecommendationRequest_Metric)(0), // 3: frdrpc.CloseRecommendationRequest.Metric - (*CloseRecommendationRequest)(nil), // 4: frdrpc.CloseRecommendationRequest - (*OutlierRecommendationsRequest)(nil), // 5: frdrpc.OutlierRecommendationsRequest - (*ThresholdRecommendationsRequest)(nil), // 6: frdrpc.ThresholdRecommendationsRequest - (*CloseRecommendationsResponse)(nil), // 7: frdrpc.CloseRecommendationsResponse - (*Recommendation)(nil), // 8: frdrpc.Recommendation - (*RevenueReportRequest)(nil), // 9: frdrpc.RevenueReportRequest - (*RevenueReportResponse)(nil), // 10: frdrpc.RevenueReportResponse - (*RevenueReport)(nil), // 11: frdrpc.RevenueReport - (*PairReport)(nil), // 12: frdrpc.PairReport - (*ChannelInsightsRequest)(nil), // 13: frdrpc.ChannelInsightsRequest - (*ChannelInsightsResponse)(nil), // 14: frdrpc.ChannelInsightsResponse - (*ChannelInsight)(nil), // 15: frdrpc.ChannelInsight - (*ExchangeRateRequest)(nil), // 16: frdrpc.ExchangeRateRequest - (*ExchangeRateResponse)(nil), // 17: frdrpc.ExchangeRateResponse - (*BitcoinPrice)(nil), // 18: frdrpc.BitcoinPrice - (*ExchangeRate)(nil), // 19: frdrpc.ExchangeRate - (*NodeAuditRequest)(nil), // 20: frdrpc.NodeAuditRequest - (*CustomCategory)(nil), // 21: frdrpc.CustomCategory - (*ReportEntry)(nil), // 22: frdrpc.ReportEntry - (*NodeAuditResponse)(nil), // 23: frdrpc.NodeAuditResponse - (*CloseReportRequest)(nil), // 24: frdrpc.CloseReportRequest - (*CloseReportResponse)(nil), // 25: frdrpc.CloseReportResponse - nil, // 26: frdrpc.RevenueReport.PairReportsEntry + (ChannelEventType)(0), // 3: frdrpc.ChannelEventType + (CloseRecommendationRequest_Metric)(0), // 4: frdrpc.CloseRecommendationRequest.Metric + (*CloseRecommendationRequest)(nil), // 5: frdrpc.CloseRecommendationRequest + (*OutlierRecommendationsRequest)(nil), // 6: frdrpc.OutlierRecommendationsRequest + (*ThresholdRecommendationsRequest)(nil), // 7: frdrpc.ThresholdRecommendationsRequest + (*CloseRecommendationsResponse)(nil), // 8: frdrpc.CloseRecommendationsResponse + (*Recommendation)(nil), // 9: frdrpc.Recommendation + (*RevenueReportRequest)(nil), // 10: frdrpc.RevenueReportRequest + (*RevenueReportResponse)(nil), // 11: frdrpc.RevenueReportResponse + (*RevenueReport)(nil), // 12: frdrpc.RevenueReport + (*PairReport)(nil), // 13: frdrpc.PairReport + (*ChannelInsightsRequest)(nil), // 14: frdrpc.ChannelInsightsRequest + (*ChannelInsightsResponse)(nil), // 15: frdrpc.ChannelInsightsResponse + (*ChannelInsight)(nil), // 16: frdrpc.ChannelInsight + (*ExchangeRateRequest)(nil), // 17: frdrpc.ExchangeRateRequest + (*ExchangeRateResponse)(nil), // 18: frdrpc.ExchangeRateResponse + (*BitcoinPrice)(nil), // 19: frdrpc.BitcoinPrice + (*ExchangeRate)(nil), // 20: frdrpc.ExchangeRate + (*NodeAuditRequest)(nil), // 21: frdrpc.NodeAuditRequest + (*CustomCategory)(nil), // 22: frdrpc.CustomCategory + (*ReportEntry)(nil), // 23: frdrpc.ReportEntry + (*NodeAuditResponse)(nil), // 24: frdrpc.NodeAuditResponse + (*CloseReportRequest)(nil), // 25: frdrpc.CloseReportRequest + (*CloseReportResponse)(nil), // 26: frdrpc.CloseReportResponse + (*ChannelEventsRequest)(nil), // 27: frdrpc.ChannelEventsRequest + (*ChannelEventsResponse)(nil), // 28: frdrpc.ChannelEventsResponse + (*ChannelEvent)(nil), // 29: frdrpc.ChannelEvent + nil, // 30: frdrpc.RevenueReport.PairReportsEntry } var file_faraday_proto_depIdxs = []int32{ - 3, // 0: frdrpc.CloseRecommendationRequest.metric:type_name -> frdrpc.CloseRecommendationRequest.Metric - 4, // 1: frdrpc.OutlierRecommendationsRequest.rec_request:type_name -> frdrpc.CloseRecommendationRequest - 4, // 2: frdrpc.ThresholdRecommendationsRequest.rec_request:type_name -> frdrpc.CloseRecommendationRequest - 8, // 3: frdrpc.CloseRecommendationsResponse.recommendations:type_name -> frdrpc.Recommendation - 11, // 4: frdrpc.RevenueReportResponse.reports:type_name -> frdrpc.RevenueReport - 26, // 5: frdrpc.RevenueReport.pair_reports:type_name -> frdrpc.RevenueReport.PairReportsEntry - 15, // 6: frdrpc.ChannelInsightsResponse.channel_insights:type_name -> frdrpc.ChannelInsight + 4, // 0: frdrpc.CloseRecommendationRequest.metric:type_name -> frdrpc.CloseRecommendationRequest.Metric + 5, // 1: frdrpc.OutlierRecommendationsRequest.rec_request:type_name -> frdrpc.CloseRecommendationRequest + 5, // 2: frdrpc.ThresholdRecommendationsRequest.rec_request:type_name -> frdrpc.CloseRecommendationRequest + 9, // 3: frdrpc.CloseRecommendationsResponse.recommendations:type_name -> frdrpc.Recommendation + 12, // 4: frdrpc.RevenueReportResponse.reports:type_name -> frdrpc.RevenueReport + 30, // 5: frdrpc.RevenueReport.pair_reports:type_name -> frdrpc.RevenueReport.PairReportsEntry + 16, // 6: frdrpc.ChannelInsightsResponse.channel_insights:type_name -> frdrpc.ChannelInsight 0, // 7: frdrpc.ExchangeRateRequest.granularity:type_name -> frdrpc.Granularity 1, // 8: frdrpc.ExchangeRateRequest.fiat_backend:type_name -> frdrpc.FiatBackend - 18, // 9: frdrpc.ExchangeRateRequest.custom_prices:type_name -> frdrpc.BitcoinPrice - 19, // 10: frdrpc.ExchangeRateResponse.rates:type_name -> frdrpc.ExchangeRate - 18, // 11: frdrpc.ExchangeRate.btc_price:type_name -> frdrpc.BitcoinPrice + 19, // 9: frdrpc.ExchangeRateRequest.custom_prices:type_name -> frdrpc.BitcoinPrice + 20, // 10: frdrpc.ExchangeRateResponse.rates:type_name -> frdrpc.ExchangeRate + 19, // 11: frdrpc.ExchangeRate.btc_price:type_name -> frdrpc.BitcoinPrice 0, // 12: frdrpc.NodeAuditRequest.granularity:type_name -> frdrpc.Granularity - 21, // 13: frdrpc.NodeAuditRequest.custom_categories:type_name -> frdrpc.CustomCategory + 22, // 13: frdrpc.NodeAuditRequest.custom_categories:type_name -> frdrpc.CustomCategory 1, // 14: frdrpc.NodeAuditRequest.fiat_backend:type_name -> frdrpc.FiatBackend - 18, // 15: frdrpc.NodeAuditRequest.custom_prices:type_name -> frdrpc.BitcoinPrice + 19, // 15: frdrpc.NodeAuditRequest.custom_prices:type_name -> frdrpc.BitcoinPrice 2, // 16: frdrpc.ReportEntry.type:type_name -> frdrpc.EntryType - 18, // 17: frdrpc.ReportEntry.btc_price:type_name -> frdrpc.BitcoinPrice - 22, // 18: frdrpc.NodeAuditResponse.reports:type_name -> frdrpc.ReportEntry - 12, // 19: frdrpc.RevenueReport.PairReportsEntry.value:type_name -> frdrpc.PairReport - 5, // 20: frdrpc.FaradayServer.OutlierRecommendations:input_type -> frdrpc.OutlierRecommendationsRequest - 6, // 21: frdrpc.FaradayServer.ThresholdRecommendations:input_type -> frdrpc.ThresholdRecommendationsRequest - 9, // 22: frdrpc.FaradayServer.RevenueReport:input_type -> frdrpc.RevenueReportRequest - 13, // 23: frdrpc.FaradayServer.ChannelInsights:input_type -> frdrpc.ChannelInsightsRequest - 16, // 24: frdrpc.FaradayServer.ExchangeRate:input_type -> frdrpc.ExchangeRateRequest - 20, // 25: frdrpc.FaradayServer.NodeAudit:input_type -> frdrpc.NodeAuditRequest - 24, // 26: frdrpc.FaradayServer.CloseReport:input_type -> frdrpc.CloseReportRequest - 7, // 27: frdrpc.FaradayServer.OutlierRecommendations:output_type -> frdrpc.CloseRecommendationsResponse - 7, // 28: frdrpc.FaradayServer.ThresholdRecommendations:output_type -> frdrpc.CloseRecommendationsResponse - 10, // 29: frdrpc.FaradayServer.RevenueReport:output_type -> frdrpc.RevenueReportResponse - 14, // 30: frdrpc.FaradayServer.ChannelInsights:output_type -> frdrpc.ChannelInsightsResponse - 17, // 31: frdrpc.FaradayServer.ExchangeRate:output_type -> frdrpc.ExchangeRateResponse - 23, // 32: frdrpc.FaradayServer.NodeAudit:output_type -> frdrpc.NodeAuditResponse - 25, // 33: frdrpc.FaradayServer.CloseReport:output_type -> frdrpc.CloseReportResponse - 27, // [27:34] is the sub-list for method output_type - 20, // [20:27] is the sub-list for method input_type - 20, // [20:20] is the sub-list for extension type_name - 20, // [20:20] is the sub-list for extension extendee - 0, // [0:20] is the sub-list for field type_name + 19, // 17: frdrpc.ReportEntry.btc_price:type_name -> frdrpc.BitcoinPrice + 23, // 18: frdrpc.NodeAuditResponse.reports:type_name -> frdrpc.ReportEntry + 29, // 19: frdrpc.ChannelEventsResponse.events:type_name -> frdrpc.ChannelEvent + 3, // 20: frdrpc.ChannelEvent.event_type:type_name -> frdrpc.ChannelEventType + 13, // 21: frdrpc.RevenueReport.PairReportsEntry.value:type_name -> frdrpc.PairReport + 6, // 22: frdrpc.FaradayServer.OutlierRecommendations:input_type -> frdrpc.OutlierRecommendationsRequest + 7, // 23: frdrpc.FaradayServer.ThresholdRecommendations:input_type -> frdrpc.ThresholdRecommendationsRequest + 10, // 24: frdrpc.FaradayServer.RevenueReport:input_type -> frdrpc.RevenueReportRequest + 14, // 25: frdrpc.FaradayServer.ChannelInsights:input_type -> frdrpc.ChannelInsightsRequest + 17, // 26: frdrpc.FaradayServer.ExchangeRate:input_type -> frdrpc.ExchangeRateRequest + 21, // 27: frdrpc.FaradayServer.NodeAudit:input_type -> frdrpc.NodeAuditRequest + 25, // 28: frdrpc.FaradayServer.CloseReport:input_type -> frdrpc.CloseReportRequest + 27, // 29: frdrpc.FaradayServer.GetChannelEvents:input_type -> frdrpc.ChannelEventsRequest + 8, // 30: frdrpc.FaradayServer.OutlierRecommendations:output_type -> frdrpc.CloseRecommendationsResponse + 8, // 31: frdrpc.FaradayServer.ThresholdRecommendations:output_type -> frdrpc.CloseRecommendationsResponse + 11, // 32: frdrpc.FaradayServer.RevenueReport:output_type -> frdrpc.RevenueReportResponse + 15, // 33: frdrpc.FaradayServer.ChannelInsights:output_type -> frdrpc.ChannelInsightsResponse + 18, // 34: frdrpc.FaradayServer.ExchangeRate:output_type -> frdrpc.ExchangeRateResponse + 24, // 35: frdrpc.FaradayServer.NodeAudit:output_type -> frdrpc.NodeAuditResponse + 26, // 36: frdrpc.FaradayServer.CloseReport:output_type -> frdrpc.CloseReportResponse + 28, // 37: frdrpc.FaradayServer.GetChannelEvents:output_type -> frdrpc.ChannelEventsResponse + 30, // [30:38] is the sub-list for method output_type + 22, // [22:30] is the sub-list for method input_type + 22, // [22:22] is the sub-list for extension type_name + 22, // [22:22] is the sub-list for extension extendee + 0, // [0:22] is the sub-list for field type_name } func init() { file_faraday_proto_init() } @@ -2578,14 +2926,50 @@ func file_faraday_proto_init() { return nil } } + file_faraday_proto_msgTypes[22].Exporter = func(v any, i int) any { + switch v := v.(*ChannelEventsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_faraday_proto_msgTypes[23].Exporter = func(v any, i int) any { + switch v := v.(*ChannelEventsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_faraday_proto_msgTypes[24].Exporter = func(v any, i int) any { + switch v := v.(*ChannelEvent); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_faraday_proto_rawDesc, - NumEnums: 4, - NumMessages: 23, + NumEnums: 5, + NumMessages: 26, NumExtensions: 0, NumServices: 1, }, diff --git a/frdrpc/faraday.pb.gw.go b/frdrpc/faraday.pb.gw.go index e03ba8f..539fe5b 100644 --- a/frdrpc/faraday.pb.gw.go +++ b/frdrpc/faraday.pb.gw.go @@ -595,6 +595,32 @@ func local_request_FaradayServer_CloseReport_0(ctx context.Context, marshaler ru } +func request_FaradayServer_GetChannelEvents_0(ctx context.Context, marshaler runtime.Marshaler, client FaradayServerClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ChannelEventsRequest + var metadata runtime.ServerMetadata + + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GetChannelEvents(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_FaradayServer_GetChannelEvents_0(ctx context.Context, marshaler runtime.Marshaler, server FaradayServerServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ChannelEventsRequest + var metadata runtime.ServerMetadata + + if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.GetChannelEvents(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterFaradayServerHandlerServer registers the http handlers for service FaradayServer to "mux". // UnaryRPC :call FaradayServerServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -902,6 +928,31 @@ func RegisterFaradayServerHandlerServer(ctx context.Context, mux *runtime.ServeM }) + mux.Handle("POST", pattern_FaradayServer_GetChannelEvents_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/frdrpc.FaradayServer/GetChannelEvents", runtime.WithHTTPPathPattern("/v1/faraday/getchannelevents")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_FaradayServer_GetChannelEvents_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_FaradayServer_GetChannelEvents_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -1207,6 +1258,28 @@ func RegisterFaradayServerHandlerClient(ctx context.Context, mux *runtime.ServeM }) + mux.Handle("POST", pattern_FaradayServer_GetChannelEvents_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/frdrpc.FaradayServer/GetChannelEvents", runtime.WithHTTPPathPattern("/v1/faraday/getchannelevents")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_FaradayServer_GetChannelEvents_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_FaradayServer_GetChannelEvents_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -1234,6 +1307,8 @@ var ( pattern_FaradayServer_NodeAudit_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "faraday", "nodeaudit"}, "")) pattern_FaradayServer_CloseReport_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "faraday", "closereport"}, "")) + + pattern_FaradayServer_GetChannelEvents_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "faraday", "getchannelevents"}, "")) ) var ( @@ -1260,4 +1335,6 @@ var ( forward_FaradayServer_NodeAudit_1 = runtime.ForwardResponseMessage forward_FaradayServer_CloseReport_0 = runtime.ForwardResponseMessage + + forward_FaradayServer_GetChannelEvents_0 = runtime.ForwardResponseMessage ) diff --git a/frdrpc/faraday.proto b/frdrpc/faraday.proto index e30558c..453805d 100644 --- a/frdrpc/faraday.proto +++ b/frdrpc/faraday.proto @@ -65,6 +65,11 @@ service FaradayServer { http://localhost:8466/v1/faraday/closereport */ rpc CloseReport (CloseReportRequest) returns (CloseReportResponse); + + /** + Get a list of channel events that occurred for a given channel. + */ + rpc GetChannelEvents (ChannelEventsRequest) returns (ChannelEventsResponse); } message CloseRecommendationRequest { @@ -596,3 +601,77 @@ message CloseReportResponse { */ string close_fee = 6; } + +message ChannelEventsRequest { + /* + The channel point of the channel to get events for, formatted txid:outpoint. + */ + string chan_point = 1; + + /* + Lower time bound, inclusive, as Unix seconds. Independent filter — does + not need to advance between paginated calls. Zero means no lower bound. + */ + int64 start_time = 2; + + /* + Upper time bound, exclusive, as Unix seconds. Must be greater than or + equal to start_time. Zero means "use the server's current time". + */ + int64 end_time = 3; + + /* + The maximum number of events to return. If zero, the server default is + used. The server enforces a hard cap; values above the cap are clamped. + */ + uint32 max_events = 4; + + /* + Pagination cursor: id of the last event from the previous response. + Pass zero on the first call; for subsequent calls pass the response's + last_id to resume past already-returned events. + */ + int64 last_id = 5; +} + +enum ChannelEventType { + // An unknown event type. + CHAN_EVENT_UNKNOWN = 0; + // An online event. + CHAN_EVENT_ONLINE = 1; + // An offline event. + CHAN_EVENT_OFFLINE = 2; + // A channel balance update event. + CHAN_EVENT_UPDATE = 3; +} + +message ChannelEventsResponse { + // The list of channel events. + repeated ChannelEvent events = 1; + + // Id of the last event returned, suitable as the next request's last_id. + // Zero when events is empty. + int64 last_id = 2; + + // True when the page filled to the requested limit and more events may be + // available; callers should keep paginating until this is false. + bool has_more = 3; +} + +message ChannelEvent { + // The timestamp of the event, as Unix seconds. + int64 timestamp = 1; + + // The type of the event. + ChannelEventType event_type = 2; + + // The channel's local balance at the time of the event in sat. + uint64 local_balance = 3; + + // The channel's remote balance at the time of the event in sat. + uint64 remote_balance = 4; + + // Server-assigned monotonic identity. Echo this back as the next + // request's last_id when paginating. + int64 id = 5; +} diff --git a/frdrpc/faraday.swagger.json b/frdrpc/faraday.swagger.json index b408304..feeb137 100644 --- a/frdrpc/faraday.swagger.json +++ b/frdrpc/faraday.swagger.json @@ -154,6 +154,39 @@ ] } }, + "/v1/faraday/getchannelevents": { + "post": { + "summary": "*\nGet a list of channel events that occurred for a given channel.", + "operationId": "FaradayServer_GetChannelEvents", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/frdrpcChannelEventsResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/frdrpcChannelEventsRequest" + } + } + ], + "tags": [ + "FaradayServer" + ] + } + }, "/v1/faraday/insights": { "get": { "summary": "* frcli: `insights`\nList currently open channel with routing and uptime information.", @@ -663,6 +696,97 @@ } } }, + "frdrpcChannelEvent": { + "type": "object", + "properties": { + "timestamp": { + "type": "string", + "format": "int64", + "description": "The timestamp of the event, as Unix seconds." + }, + "event_type": { + "$ref": "#/definitions/frdrpcChannelEventType", + "description": "The type of the event." + }, + "local_balance": { + "type": "string", + "format": "uint64", + "description": "The channel's local balance at the time of the event in sat." + }, + "remote_balance": { + "type": "string", + "format": "uint64", + "description": "The channel's remote balance at the time of the event in sat." + }, + "id": { + "type": "string", + "format": "int64", + "description": "Server-assigned monotonic identity. Echo this back as the next\nrequest's last_id when paginating." + } + } + }, + "frdrpcChannelEventType": { + "type": "string", + "enum": [ + "CHAN_EVENT_UNKNOWN", + "CHAN_EVENT_ONLINE", + "CHAN_EVENT_OFFLINE", + "CHAN_EVENT_UPDATE" + ], + "default": "CHAN_EVENT_UNKNOWN", + "description": " - CHAN_EVENT_UNKNOWN: An unknown event type.\n - CHAN_EVENT_ONLINE: An online event.\n - CHAN_EVENT_OFFLINE: An offline event.\n - CHAN_EVENT_UPDATE: A channel balance update event." + }, + "frdrpcChannelEventsRequest": { + "type": "object", + "properties": { + "chan_point": { + "type": "string", + "description": "The channel point of the channel to get events for, formatted txid:outpoint." + }, + "start_time": { + "type": "string", + "format": "int64", + "description": "Lower time bound, inclusive, as Unix seconds. Independent filter — does\nnot need to advance between paginated calls. Zero means no lower bound." + }, + "end_time": { + "type": "string", + "format": "int64", + "description": "Upper time bound, exclusive, as Unix seconds. Must be greater than or\nequal to start_time. Zero means \"use the server's current time\"." + }, + "max_events": { + "type": "integer", + "format": "int64", + "description": "The maximum number of events to return. If zero, the server default is\nused. The server enforces a hard cap; values above the cap are clamped." + }, + "last_id": { + "type": "string", + "format": "int64", + "description": "Pagination cursor: id of the last event from the previous response.\nPass zero on the first call; for subsequent calls pass the response's\nlast_id to resume past already-returned events." + } + } + }, + "frdrpcChannelEventsResponse": { + "type": "object", + "properties": { + "events": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/frdrpcChannelEvent" + }, + "description": "The list of channel events." + }, + "last_id": { + "type": "string", + "format": "int64", + "description": "Id of the last event returned, suitable as the next request's last_id.\nZero when events is empty." + }, + "has_more": { + "type": "boolean", + "description": "True when the page filled to the requested limit and more events may be\navailable; callers should keep paginating until this is false." + } + } + }, "frdrpcChannelInsight": { "type": "object", "properties": { diff --git a/frdrpc/faraday.yaml b/frdrpc/faraday.yaml index 42734db..c289975 100644 --- a/frdrpc/faraday.yaml +++ b/frdrpc/faraday.yaml @@ -33,3 +33,6 @@ http: body: "*" - selector: frdrpc.FaradayServer.CloseReport get: "/v1/faraday/closereport" + - selector: frdrpc.FaradayServer.GetChannelEvents + post: "/v1/faraday/getchannelevents" + body: "*" diff --git a/frdrpc/faraday_grpc.pb.go b/frdrpc/faraday_grpc.pb.go index cf25f5a..0fc3aca 100644 --- a/frdrpc/faraday_grpc.pb.go +++ b/frdrpc/faraday_grpc.pb.go @@ -62,6 +62,9 @@ type FaradayServerClient interface { // Example request: // http://localhost:8466/v1/faraday/closereport CloseReport(ctx context.Context, in *CloseReportRequest, opts ...grpc.CallOption) (*CloseReportResponse, error) + // * + // Get a list of channel events that occurred for a given channel. + GetChannelEvents(ctx context.Context, in *ChannelEventsRequest, opts ...grpc.CallOption) (*ChannelEventsResponse, error) } type faradayServerClient struct { @@ -135,6 +138,15 @@ func (c *faradayServerClient) CloseReport(ctx context.Context, in *CloseReportRe return out, nil } +func (c *faradayServerClient) GetChannelEvents(ctx context.Context, in *ChannelEventsRequest, opts ...grpc.CallOption) (*ChannelEventsResponse, error) { + out := new(ChannelEventsResponse) + err := c.cc.Invoke(ctx, "/frdrpc.FaradayServer/GetChannelEvents", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // FaradayServerServer is the server API for FaradayServer service. // All implementations must embed UnimplementedFaradayServerServer // for forward compatibility @@ -183,6 +195,9 @@ type FaradayServerServer interface { // Example request: // http://localhost:8466/v1/faraday/closereport CloseReport(context.Context, *CloseReportRequest) (*CloseReportResponse, error) + // * + // Get a list of channel events that occurred for a given channel. + GetChannelEvents(context.Context, *ChannelEventsRequest) (*ChannelEventsResponse, error) mustEmbedUnimplementedFaradayServerServer() } @@ -211,6 +226,9 @@ func (UnimplementedFaradayServerServer) NodeAudit(context.Context, *NodeAuditReq func (UnimplementedFaradayServerServer) CloseReport(context.Context, *CloseReportRequest) (*CloseReportResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CloseReport not implemented") } +func (UnimplementedFaradayServerServer) GetChannelEvents(context.Context, *ChannelEventsRequest) (*ChannelEventsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetChannelEvents not implemented") +} func (UnimplementedFaradayServerServer) mustEmbedUnimplementedFaradayServerServer() {} // UnsafeFaradayServerServer may be embedded to opt out of forward compatibility for this service. @@ -350,6 +368,24 @@ func _FaradayServer_CloseReport_Handler(srv interface{}, ctx context.Context, de return interceptor(ctx, in, info, handler) } +func _FaradayServer_GetChannelEvents_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ChannelEventsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FaradayServerServer).GetChannelEvents(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/frdrpc.FaradayServer/GetChannelEvents", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FaradayServerServer).GetChannelEvents(ctx, req.(*ChannelEventsRequest)) + } + return interceptor(ctx, in, info, handler) +} + // FaradayServer_ServiceDesc is the grpc.ServiceDesc for FaradayServer service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -385,6 +421,10 @@ var FaradayServer_ServiceDesc = grpc.ServiceDesc{ MethodName: "CloseReport", Handler: _FaradayServer_CloseReport_Handler, }, + { + MethodName: "GetChannelEvents", + Handler: _FaradayServer_GetChannelEvents_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "faraday.proto", diff --git a/frdrpc/faradayserver.pb.json.go b/frdrpc/faradayserver.pb.json.go index 09854b4..4ee11e0 100644 --- a/frdrpc/faradayserver.pb.json.go +++ b/frdrpc/faradayserver.pb.json.go @@ -195,4 +195,29 @@ func RegisterFaradayServerJSONCallbacks(registry map[string]func(ctx context.Con } callback(string(respBytes), nil) } + + registry["frdrpc.FaradayServer.GetChannelEvents"] = func(ctx context.Context, + conn *grpc.ClientConn, reqJSON string, callback func(string, error)) { + + req := &ChannelEventsRequest{} + err := marshaler.Unmarshal([]byte(reqJSON), req) + if err != nil { + callback("", err) + return + } + + client := NewFaradayServerClient(conn) + resp, err := client.GetChannelEvents(ctx, req) + if err != nil { + callback("", err) + return + } + + respBytes, err := marshaler.Marshal(resp) + if err != nil { + callback("", err) + return + } + callback(string(respBytes), nil) + } } diff --git a/go.mod b/go.mod index 37054d3..f0459a0 100644 --- a/go.mod +++ b/go.mod @@ -191,4 +191,7 @@ replace google.golang.org/protobuf => github.com/lightninglabs/protobuf-go-hex-d // We are using a fork of the migration library. replace github.com/golang-migrate/migrate/v4 => github.com/lightninglabs/migrate/v4 v4.18.2-9023d66a-fork-pr-2.0.20251211093704-71c1eef09789 +// We need to replace frdrpc locally until we have this PR merged. +replace github.com/lightninglabs/faraday/frdrpc => ./frdrpc + go 1.25.5 diff --git a/go.sum b/go.sum index 6be782b..22e447c 100644 --- a/go.sum +++ b/go.sum @@ -340,8 +340,6 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lightninglabs/faraday/frdrpc v1.0.1 h1:3YlP9UwT0bmT468oAdn4dxwsaJBI4QDBDSsAzq+LnGA= -github.com/lightninglabs/faraday/frdrpc v1.0.1/go.mod h1:ot1R/RGzk61d3qCrZPL36jI5ziGmKbvvE7UQKsJKuvk= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI/f/O0Avg7t8sqkPo78HFzjmeYFl6DPnc= github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk= github.com/lightninglabs/lndclient v1.0.1-0.20260224134629-de7b65bb4c60 h1:ycLVFR0tUZ8oWg/qI5ShWhzEk8lvCjHVCjx0x6E/yUc= From 44bb23186a756a582fdcb93c06831f24b7404832 Mon Sep 17 00:00:00 2001 From: bitromortac Date: Wed, 8 Oct 2025 12:51:16 +0200 Subject: [PATCH 5/8] faraday+frdrpcserver: pass in chan events store 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. --- faraday.go | 2 ++ frdrpcserver/rpcserver.go | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/faraday.go b/faraday.go index 0f15d38..314c0cf 100644 --- a/faraday.go +++ b/faraday.go @@ -179,6 +179,7 @@ func (f *Faraday) Start() error { cfg := &frdrpcserver.Config{ Lnd: f.lnd.LndServices, + ChanEvents: f.stores.ChanEventsStore, BitcoinClient: f.bitcoinClient, } @@ -401,6 +402,7 @@ func (f *Faraday) StartAsSubserver(lndGrpc *lndclient.GrpcLndServices, cfg := &frdrpcserver.Config{ Lnd: lndGrpc.LndServices, + ChanEvents: f.stores.ChanEventsStore, BitcoinClient: f.bitcoinClient, } diff --git a/frdrpcserver/rpcserver.go b/frdrpcserver/rpcserver.go index ebc8080..f248490 100644 --- a/frdrpcserver/rpcserver.go +++ b/frdrpcserver/rpcserver.go @@ -15,6 +15,7 @@ import ( "github.com/lightninglabs/faraday/accounting" "github.com/lightninglabs/faraday/chain" + "github.com/lightninglabs/faraday/chanevents" "github.com/lightninglabs/faraday/fiat" "github.com/lightninglabs/faraday/frdrpc" "github.com/lightninglabs/faraday/recommend" @@ -57,6 +58,9 @@ type Config struct { // Lnd is a client which can be used to query lnd. Lnd lndclient.LndServices + // ChanEvents is a database of channel events. + ChanEvents *chanevents.Store + // BitcoinClient is an optional client which can be used to query // on-chain data from a connected bitcoin node. If nil, faraday will // not be able to serve endpoints which require on-chain data. From dc9b6388af0081677df5306b5a143a3912abf136 Mon Sep 17 00:00:00 2001 From: bitromortac Date: Tue, 28 Apr 2026 17:00:50 +0200 Subject: [PATCH 6/8] frdrpcserver: implement GetChannelEvents 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. --- frdrpcserver/getchanevents.go | 148 ++++++++++++++++++++++++++++++++++ frdrpcserver/perms/perms.go | 4 + 2 files changed, 152 insertions(+) create mode 100644 frdrpcserver/getchanevents.go diff --git a/frdrpcserver/getchanevents.go b/frdrpcserver/getchanevents.go new file mode 100644 index 0000000..9e26ac0 --- /dev/null +++ b/frdrpcserver/getchanevents.go @@ -0,0 +1,148 @@ +package frdrpcserver + +import ( + "context" + "errors" + "time" + + "github.com/btcsuite/btcd/btcutil" + "github.com/lightninglabs/faraday/chanevents" + "github.com/lightninglabs/faraday/frdrpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// maxChannelEventsLimit is the hard cap the server will return in a single +// GetChannelEvents response, regardless of what the client asks for. It also +// serves as the default when the request leaves max_events at zero. +const maxChannelEventsLimit = 10000 + +// GetChannelEvents serves a paginated read of a channel's events. A zero +// end_time defaults to the server's current time, max_events is clamped to +// the server's hard cap, and an unknown channel point yields NotFound. +func (s *RPCServer) GetChannelEvents(ctx context.Context, + req *frdrpc.ChannelEventsRequest) (*frdrpc.ChannelEventsResponse, + error) { + + log.Debugf("[GetChannelEvents]: chan_point=%s, start_time=%d, "+ + "end_time=%d, max_events=%d, last_id=%d", req.ChanPoint, + req.StartTime, req.EndTime, req.MaxEvents, req.LastId) + + if req.ChanPoint == "" { + return nil, status.Error( + codes.InvalidArgument, "channel point required", + ) + } + + if req.StartTime < 0 || req.EndTime < 0 { + return nil, status.Error( + codes.InvalidArgument, + "start_time and end_time must be >= 0", + ) + } + + startTime := time.Unix(req.StartTime, 0) + endTime := time.Now() + if req.EndTime != 0 { + endTime = time.Unix(req.EndTime, 0) + } + + if startTime.After(endTime) { + return nil, status.Error( + codes.InvalidArgument, "start_time must be <= end_time", + ) + } + + if req.LastId < 0 { + return nil, status.Error( + codes.InvalidArgument, "last_id must be >= 0", + ) + } + + channel, err := s.cfg.ChanEvents.GetChannel(ctx, req.ChanPoint) + if err != nil { + if errors.Is(err, chanevents.ErrUnknownChannel) { + return nil, status.Errorf(codes.NotFound, "channel %s "+ + "not found", req.ChanPoint) + } + log.Errorf("GetChannel(%s): %v", req.ChanPoint, err) + + return nil, status.Error( + codes.Internal, "failed to look up channel", + ) + } + + limit := int32(maxChannelEventsLimit) + if req.MaxEvents != 0 && req.MaxEvents < maxChannelEventsLimit { + limit = int32(req.MaxEvents) + } + + events, err := s.cfg.ChanEvents.GetChannelEvents( + ctx, channel.ID, req.LastId, startTime, endTime, limit, + ) + if err != nil { + log.Errorf("GetChannelEvents(%s): %v", req.ChanPoint, err) + + return nil, status.Error( + codes.Internal, "failed to query channel events", + ) + } + + resp := &frdrpc.ChannelEventsResponse{ + Events: marshalRPCChannelEvents(events), + HasMore: int32(len(events)) == limit, + } + if n := len(events); n > 0 { + resp.LastId = events[n-1].ID + } + + return resp, nil +} + +// marshalRPCChannelEvents converts a slice of chanevents.ChannelEvent into a +// slice of frdrpc.ChannelEvent. +func marshalRPCChannelEvents( + events []*chanevents.ChannelEvent) []*frdrpc.ChannelEvent { + + rpcEvents := make([]*frdrpc.ChannelEvent, len(events)) + + for i, event := range events { + rpcEvent := &frdrpc.ChannelEvent{ + Id: event.ID, + Timestamp: event.Timestamp.Unix(), + EventType: rpcEventType(event.EventType), + } + + event.LocalBalance.WhenSome( + func(b btcutil.Amount) { + rpcEvent.LocalBalance = uint64(b) + }, + ) + event.RemoteBalance.WhenSome( + func(b btcutil.Amount) { + rpcEvent.RemoteBalance = uint64(b) + }, + ) + + rpcEvents[i] = rpcEvent + } + + return rpcEvents +} + +// rpcEventType maps a stored chanevents.EventType to its proto counterpart. +func rpcEventType(e chanevents.EventType) frdrpc.ChannelEventType { + switch e { + case chanevents.EventTypeOnline: + return frdrpc.ChannelEventType_CHAN_EVENT_ONLINE + + case chanevents.EventTypeOffline: + return frdrpc.ChannelEventType_CHAN_EVENT_OFFLINE + + case chanevents.EventTypeUpdate: + return frdrpc.ChannelEventType_CHAN_EVENT_UPDATE + + default: + return frdrpc.ChannelEventType_CHAN_EVENT_UNKNOWN + } +} diff --git a/frdrpcserver/perms/perms.go b/frdrpcserver/perms/perms.go index fe95829..c17c541 100644 --- a/frdrpcserver/perms/perms.go +++ b/frdrpcserver/perms/perms.go @@ -33,4 +33,8 @@ var RequiredPermissions = map[string][]bakery.Op{ Entity: "report", Action: "read", }}, + "/frdrpc.FaradayServer/GetChannelEvents": {{ + Entity: "events", + Action: "read", + }}, } From b0e07dca5f2aeaf4e45a3e86b0a428df9ffe2770 Mon Sep 17 00:00:00 2001 From: bitromortac Date: Wed, 8 Oct 2025 13:06:37 +0200 Subject: [PATCH 7/8] itest: add itest for channel events store 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. --- itest/channel_events_test.go | 148 +++++++++++++++++++++++++++++++++++ itest/test_context.go | 21 +++++ 2 files changed, 169 insertions(+) create mode 100644 itest/channel_events_test.go diff --git a/itest/channel_events_test.go b/itest/channel_events_test.go new file mode 100644 index 0000000..3100bdb --- /dev/null +++ b/itest/channel_events_test.go @@ -0,0 +1,148 @@ +package itest + +import ( + "context" + "testing" + "time" + + "github.com/btcsuite/btcd/btcutil" + "github.com/lightninglabs/faraday/frdrpc" + "github.com/lightninglabs/lndclient" + "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/stretchr/testify/require" +) + +// TestGetChannelEvents pins the GetChannelEvents RPC contract: a regtest +// channel lifecycle surfaces the expected event-type counts, and a paginated +// walk over the same window matches the unpaginated result event-for-event. +func TestGetChannelEvents(t *testing.T) { + c := newTestContext(t) + defer c.stop() + + ctx := context.Background() + + // We will start by opening a channel from alice to bob. + var aliceChannelAmt = btcutil.Amount(500000) + + err := c.aliceClient.Client.Connect( + ctx, c.bobPubkey, "localhost:10012", true, + ) + require.NoError(c.t, err, "could not connect nodes") + + aliceChannel, _ := c.openChannel( + c.aliceClient.Client, c.bobPubkey, aliceChannelAmt, + ) + + // Wait until alice can route a payment to bob through the new channel. + var paymentAmount lnwire.MilliSatoshi = 20000000 + c.eventuallyf(func() bool { + return c.channelRoutable(c.bobPubkey, paymentAmount) + }, "channel did not become routable") + + // Now we'll send a payment from alice to bob to generate a balance + // update event. + hash, payreq := c.addInvoice(c.bobClient.Client, paymentAmount) + c.makePayment( + c.aliceClient.LndServices, c.bobClient.LndServices, + lndclient.SendPaymentRequest{ + Invoice: payreq, + PaymentHash: &hash, + Timeout: paymentTimeout, + }, lnrpc.Payment_SUCCEEDED, + ) + + // We now close the channel to generate an offline event. + c.closeChannel(c.aliceClient.Client, aliceChannel, true) + + endTime := time.Now().Add(time.Second).Unix() + + events, err := c.faradayClient.GetChannelEvents( + ctx, &frdrpc.ChannelEventsRequest{ + ChanPoint: aliceChannel.String(), + EndTime: endTime, + }, + ) + require.NoError(c.t, err, "could not get channel events") + + // Check that we have the expected event types. + var ( + onlineEvents int + updateEvents int + offlineEvents int + ) + + for _, event := range events.Events { + switch event.EventType { + case frdrpc.ChannelEventType_CHAN_EVENT_ONLINE: + onlineEvents++ + + case frdrpc.ChannelEventType_CHAN_EVENT_UPDATE: + updateEvents++ + + case frdrpc.ChannelEventType_CHAN_EVENT_OFFLINE: + offlineEvents++ + } + } + + // We expect exactly these events for this channel: + // 1. Channel Open: online, update (initial balance) + // 2. Channel Active: online + // 3. Payment sent: two updates (update_add, update_fulfill) + // 4. Channel Offline: offline + // 5. Channel Close: offline + require.Len(t, events.Events, 7) + require.Equal(t, 2, onlineEvents) + require.Equal(t, 3, updateEvents) + require.Equal(t, 2, offlineEvents) + + // Walk the same window with a small page size and assert the + // concatenated pages match the unpaginated result. Catches + // last_id round-trip, has_more termination, and MaxEvents + // clamping in one pass. + const pageSize = 2 + var ( + paged []*frdrpc.ChannelEvent + lastID int64 + pages int + ) + for { + page, err := c.faradayClient.GetChannelEvents( + ctx, &frdrpc.ChannelEventsRequest{ + ChanPoint: aliceChannel.String(), + EndTime: endTime, + MaxEvents: pageSize, + LastId: lastID, + }, + ) + require.NoError(c.t, err, "could not get paginated events") + + // Non-final pages must fill exactly pageSize; the final + // page (HasMore == false) holds the remainder. + if page.HasMore { + require.Len(t, page.Events, pageSize, + "non-final page %d not full", pages) + } else { + require.LessOrEqual(t, len(page.Events), pageSize, + "final page %d exceeds pageSize", pages) + } + + paged = append(paged, page.Events...) + pages++ + if !page.HasMore { + break + } + + lastID = page.LastId + } + + // With 7 events and pageSize 2 we expect ceil(7/2) = 4 pages. + require.Equal(t, 4, pages, "unexpected page count") + + require.Equal(t, len(events.Events), len(paged), + "paginated and unpaginated counts differ") + for i, e := range events.Events { + require.Equal(t, e.Id, paged[i].Id, + "event order mismatch at index %d", i) + } +} diff --git a/itest/test_context.go b/itest/test_context.go index 4b1e364..85fa2eb 100644 --- a/itest/test_context.go +++ b/itest/test_context.go @@ -125,6 +125,10 @@ func newTestContext(t *testing.T) *testContext { // Start faraday. ctx.startFaraday() + // Wait for faraday's channel events monitor to finish its initial + // chain-sync. + time.Sleep(5 * time.Second) + return ctx } @@ -445,6 +449,23 @@ func (c *testContext) waitForChannelOpen(targetChannel *wire.OutPoint) { ) } +// channelRoutable reports whether alice's router can build a route to dest +// for amount. It gates on QueryRoutes rather than channel activation: lnd +// marks a channel Active on channel_ready, but the local channel_update the +// router needs lands a moment later. +func (c *testContext) channelRoutable(dest route.Vertex, + amount lnwire.MilliSatoshi) bool { + + _, err := c.aliceClient.Client.QueryRoutes( + context.Background(), lndclient.QueryRoutesRequest{ + PubKey: dest, + AmtMsat: amount, + }, + ) + + return err == nil +} + // findChannel finds a channel in a set of open channels, returning nil if it // is not found. // nolint:interfacer From f1186409c3a2abc01b49f76381c7b39ea0295a48 Mon Sep 17 00:00:00 2001 From: bitromortac Date: Mon, 17 Nov 2025 14:08:41 +0100 Subject: [PATCH 8/8] frcli: add chan events command 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. --- cmd/frcli/chan_events.go | 97 ++++++++++++++++++++++++++++++++++++++++ cmd/frcli/main.go | 1 + 2 files changed, 98 insertions(+) create mode 100644 cmd/frcli/chan_events.go diff --git a/cmd/frcli/chan_events.go b/cmd/frcli/chan_events.go new file mode 100644 index 0000000..3cf771b --- /dev/null +++ b/cmd/frcli/chan_events.go @@ -0,0 +1,97 @@ +package main + +import ( + "context" + "fmt" + + "github.com/lightninglabs/faraday/frdrpc" + "github.com/urfave/cli" +) + +var chanEventsCommand = cli.Command{ + Name: "chanevents", + Category: "reporting", + Usage: "Get a report of channel events.", + Description: ` + Get a report for a channel which provides a detailed account of its + lifecycle events. The server caps each response; if has_more is true, + fetch the next page by re-running with --last_id set to the previous + response's last_id. Stop when has_more is false. --start_time and + --end_time are independent filters and do not need to advance between + paginated calls.`, + ArgsUsage: "funding_txid [output_index]", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "funding_txid", + Usage: "the txid of the channel's funding transaction", + }, + cli.IntFlag{ + Name: "output_index", + Usage: "the output index for the funding output of " + + "the funding transaction", + }, + cli.Int64Flag{ + Name: "start_time", + Usage: "start time of the query range as a unix timestamp", + }, + cli.Int64Flag{ + Name: "end_time", + Usage: "end time of the query range as a unix " + + "timestamp; zero defaults to the server's " + + "current time", + }, + cli.UintFlag{ + Name: "max_events", + Usage: "maximum number of events to return; zero " + + "uses the server default (capped server-side)", + }, + cli.Int64Flag{ + Name: "last_id", + Usage: "pagination cursor; pass the previous " + + "response's last_id to continue, or zero " + + "for the first page", + }, + }, + Action: queryChanEvents, +} + +func queryChanEvents(ctx *cli.Context) error { + client, cleanup := getClient(ctx) + defer cleanup() + + // Show command help if the channel point was not provided. + if ctx.NArg() == 0 && ctx.String("funding_txid") == "" { + return cli.ShowCommandHelp(ctx, "chanevents") + } + + outpoint, err := parseChannelPoint(ctx) + if err != nil { + return err + } + + startTime := ctx.Int64("start_time") + endTime := ctx.Int64("end_time") + if startTime < 0 || endTime < 0 { + return fmt.Errorf("start_time and end_time must be >= 0") + } + if endTime != 0 && startTime > endTime { + return fmt.Errorf("start_time must be <= end_time") + } + + req := &frdrpc.ChannelEventsRequest{ + ChanPoint: outpoint.String(), + StartTime: startTime, + EndTime: endTime, + MaxEvents: uint32(ctx.Uint("max_events")), + LastId: ctx.Int64("last_id"), + } + + rpcCtx := context.Background() + report, err := client.GetChannelEvents(rpcCtx, req) + if err != nil { + return err + } + + printRespJSON(report) + return nil +} diff --git a/cmd/frcli/main.go b/cmd/frcli/main.go index 7cbff99..208520f 100644 --- a/cmd/frcli/main.go +++ b/cmd/frcli/main.go @@ -57,6 +57,7 @@ func main() { fiatEstimateCommand, onChainReportCommand, closeReportCommand, + chanEventsCommand, } if err := app.Run(os.Args); err != nil {