Merge pull request #818 from shocknet/lnd-forwading-events

Lnd forwading events
This commit is contained in:
Justin (shocknet) 2025-06-17 14:59:32 -04:00 committed by GitHub
commit 9f9a66a513
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 253 additions and 4 deletions

View file

@ -133,6 +133,11 @@ The nostr server will send back a message response, and inside the body there wi
- This methods has an __empty__ __request__ body - This methods has an __empty__ __request__ body
- output: [LiveUserOperation](#LiveUserOperation) - output: [LiveUserOperation](#LiveUserOperation)
- GetLndForwardingMetrics
- auth type: __Metrics__
- input: [LndMetricsRequest](#LndMetricsRequest)
- output: [LndForwardingMetrics](#LndForwardingMetrics)
- GetLndMetrics - GetLndMetrics
- auth type: __Metrics__ - auth type: __Metrics__
- input: [LndMetricsRequest](#LndMetricsRequest) - input: [LndMetricsRequest](#LndMetricsRequest)
@ -567,6 +572,13 @@ The nostr server will send back a message response, and inside the body there wi
- This methods has an __empty__ __request__ body - This methods has an __empty__ __request__ body
- output: [LiveUserOperation](#LiveUserOperation) - output: [LiveUserOperation](#LiveUserOperation)
- GetLndForwardingMetrics
- auth type: __Metrics__
- http method: __post__
- http route: __/api/reports/lnd/forwarding__
- input: [LndMetricsRequest](#LndMetricsRequest)
- output: [LndForwardingMetrics](#LndForwardingMetrics)
- GetLndMetrics - GetLndMetrics
- auth type: __Metrics__ - auth type: __Metrics__
- http method: __post__ - http method: __post__
@ -1210,6 +1222,18 @@ The nostr server will send back a message response, and inside the body there wi
### LndChannels ### LndChannels
- __open_channels__: ARRAY of: _[OpenChannel](#OpenChannel)_ - __open_channels__: ARRAY of: _[OpenChannel](#OpenChannel)_
### LndForwardingEvent
- __amt_in__: _number_
- __amt_out__: _number_
- __at_unix__: _number_
- __chan_id_in__: _string_
- __chan_id_out__: _string_
- __fee__: _number_
### LndForwardingMetrics
- __events__: ARRAY of: _[LndForwardingEvent](#LndForwardingEvent)_
- __total_fees__: _number_
### LndGetInfoRequest ### LndGetInfoRequest
- __nodeId__: _number_ - __nodeId__: _number_

View file

@ -85,6 +85,7 @@ type Client struct {
GetLNURLChannelLink func() (*LnurlLinkResponse, error) GetLNURLChannelLink func() (*LnurlLinkResponse, error)
GetLiveDebitRequests func() (*LiveDebitRequest, error) GetLiveDebitRequests func() (*LiveDebitRequest, error)
GetLiveUserOperations func() (*LiveUserOperation, error) GetLiveUserOperations func() (*LiveUserOperation, error)
GetLndForwardingMetrics func(req LndMetricsRequest) (*LndForwardingMetrics, error)
GetLndMetrics func(req LndMetricsRequest) (*LndMetrics, error) GetLndMetrics func(req LndMetricsRequest) (*LndMetrics, error)
GetLnurlPayInfo func(query GetLnurlPayInfo_Query) (*LnurlPayInfoResponse, error) GetLnurlPayInfo func(query GetLnurlPayInfo_Query) (*LnurlPayInfoResponse, error)
GetLnurlPayLink func() (*LnurlLinkResponse, error) GetLnurlPayLink func() (*LnurlLinkResponse, error)
@ -906,6 +907,35 @@ func NewClient(params ClientParams) *Client {
}, },
// server streaming method: GetLiveDebitRequests not implemented // server streaming method: GetLiveDebitRequests not implemented
// server streaming method: GetLiveUserOperations not implemented // server streaming method: GetLiveUserOperations not implemented
GetLndForwardingMetrics: func(req LndMetricsRequest) (*LndForwardingMetrics, error) {
auth, err := params.RetrieveMetricsAuth()
if err != nil {
return nil, err
}
finalRoute := "/api/reports/lnd/forwarding"
body, err := json.Marshal(req)
if err != nil {
return nil, err
}
resBody, err := doPostRequest(params.BaseURL+finalRoute, body, auth)
if err != nil {
return nil, err
}
result := ResultError{}
err = json.Unmarshal(resBody, &result)
if err != nil {
return nil, err
}
if result.Status == "ERROR" {
return nil, fmt.Errorf(result.Reason)
}
res := LndForwardingMetrics{}
err = json.Unmarshal(resBody, &res)
if err != nil {
return nil, err
}
return &res, nil
},
GetLndMetrics: func(req LndMetricsRequest) (*LndMetrics, error) { GetLndMetrics: func(req LndMetricsRequest) (*LndMetrics, error) {
auth, err := params.RetrieveMetricsAuth() auth, err := params.RetrieveMetricsAuth()
if err != nil { if err != nil {

View file

@ -361,6 +361,18 @@ type LiveUserOperation struct {
type LndChannels struct { type LndChannels struct {
Open_channels []OpenChannel `json:"open_channels"` Open_channels []OpenChannel `json:"open_channels"`
} }
type LndForwardingEvent struct {
Amt_in int64 `json:"amt_in"`
Amt_out int64 `json:"amt_out"`
At_unix int64 `json:"at_unix"`
Chan_id_in string `json:"chan_id_in"`
Chan_id_out string `json:"chan_id_out"`
Fee int64 `json:"fee"`
}
type LndForwardingMetrics struct {
Events []LndForwardingEvent `json:"events"`
Total_fees int64 `json:"total_fees"`
}
type LndGetInfoRequest struct { type LndGetInfoRequest struct {
Nodeid int64 `json:"nodeId"` Nodeid int64 `json:"nodeId"`
} }

View file

@ -996,6 +996,28 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
opts.metricsCallback([{ ...info, ...stats, ...authContext }]) opts.metricsCallback([{ ...info, ...stats, ...authContext }])
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e } } catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
}) })
if (!opts.allowNotImplementedMethods && !methods.GetLndForwardingMetrics) throw new Error('method: GetLndForwardingMetrics is not implemented')
app.post('/api/reports/lnd/forwarding', async (req, res) => {
const info: Types.RequestInfo = { rpcName: 'GetLndForwardingMetrics', batch: false, nostr: false, batchSize: 0}
const stats: Types.RequestStats = { startMs:req.startTimeMs || 0, start:req.startTime || 0n, parse: process.hrtime.bigint(), guard: 0n, validate: 0n, handle: 0n }
let authCtx: Types.AuthContext = {}
try {
if (!methods.GetLndForwardingMetrics) throw new Error('method: GetLndForwardingMetrics is not implemented')
const authContext = await opts.MetricsAuthGuard(req.headers['authorization'])
authCtx = authContext
stats.guard = process.hrtime.bigint()
const request = req.body
const error = Types.LndMetricsRequestValidate(request)
stats.validate = process.hrtime.bigint()
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authContext }, opts.metricsCallback)
const query = req.query
const params = req.params
const response = await methods.GetLndForwardingMetrics({rpcName:'GetLndForwardingMetrics', ctx:authContext , req: request})
stats.handle = process.hrtime.bigint()
res.json({status: 'OK', ...response})
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
})
if (!opts.allowNotImplementedMethods && !methods.GetLndMetrics) throw new Error('method: GetLndMetrics is not implemented') if (!opts.allowNotImplementedMethods && !methods.GetLndMetrics) throw new Error('method: GetLndMetrics is not implemented')
app.post('/api/reports/lnd', async (req, res) => { app.post('/api/reports/lnd', async (req, res) => {
const info: Types.RequestInfo = { rpcName: 'GetLndMetrics', batch: false, nostr: false, batchSize: 0} const info: Types.RequestInfo = { rpcName: 'GetLndMetrics', batch: false, nostr: false, batchSize: 0}

View file

@ -404,6 +404,20 @@ export default (params: ClientParams) => ({
}, },
GetLiveDebitRequests: async (cb: (v:ResultError | ({ status: 'OK' }& Types.LiveDebitRequest)) => void): Promise<void> => { throw new Error('http streams are not supported')}, GetLiveDebitRequests: async (cb: (v:ResultError | ({ status: 'OK' }& Types.LiveDebitRequest)) => void): Promise<void> => { throw new Error('http streams are not supported')},
GetLiveUserOperations: async (cb: (v:ResultError | ({ status: 'OK' }& Types.LiveUserOperation)) => void): Promise<void> => { throw new Error('http streams are not supported')}, GetLiveUserOperations: async (cb: (v:ResultError | ({ status: 'OK' }& Types.LiveUserOperation)) => void): Promise<void> => { throw new Error('http streams are not supported')},
GetLndForwardingMetrics: async (request: Types.LndMetricsRequest): Promise<ResultError | ({ status: 'OK' }& Types.LndForwardingMetrics)> => {
const auth = await params.retrieveMetricsAuth()
if (auth === null) throw new Error('retrieveMetricsAuth() returned null')
let finalRoute = '/api/reports/lnd/forwarding'
const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } })
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
if (data.status === 'OK') {
const result = data
if(!params.checkResult) return { status: 'OK', ...result }
const error = Types.LndForwardingMetricsValidate(result)
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
}
return { status: 'ERROR', reason: 'invalid response' }
},
GetLndMetrics: async (request: Types.LndMetricsRequest): Promise<ResultError | ({ status: 'OK' }& Types.LndMetrics)> => { GetLndMetrics: async (request: Types.LndMetricsRequest): Promise<ResultError | ({ status: 'OK' }& Types.LndMetrics)> => {
const auth = await params.retrieveMetricsAuth() const auth = await params.retrieveMetricsAuth()
if (auth === null) throw new Error('retrieveMetricsAuth() returned null') if (auth === null) throw new Error('retrieveMetricsAuth() returned null')

View file

@ -349,6 +349,21 @@ export default (params: NostrClientParams, send: (to:string, message: NostrRequ
return cb({ status: 'ERROR', reason: 'invalid response' }) return cb({ status: 'ERROR', reason: 'invalid response' })
}) })
}, },
GetLndForwardingMetrics: async (request: Types.LndMetricsRequest): Promise<ResultError | ({ status: 'OK' }& Types.LndForwardingMetrics)> => {
const auth = await params.retrieveNostrMetricsAuth()
if (auth === null) throw new Error('retrieveNostrMetricsAuth() returned null')
const nostrRequest: NostrRequest = {}
nostrRequest.body = request
const data = await send(params.pubDestination, {rpcName:'GetLndForwardingMetrics',authIdentifier:auth, ...nostrRequest })
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
if (data.status === 'OK') {
const result = data
if(!params.checkResult) return { status: 'OK', ...result }
const error = Types.LndForwardingMetricsValidate(result)
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
}
return { status: 'ERROR', reason: 'invalid response' }
},
GetLndMetrics: async (request: Types.LndMetricsRequest): Promise<ResultError | ({ status: 'OK' }& Types.LndMetrics)> => { GetLndMetrics: async (request: Types.LndMetricsRequest): Promise<ResultError | ({ status: 'OK' }& Types.LndMetrics)> => {
const auth = await params.retrieveNostrMetricsAuth() const auth = await params.retrieveNostrMetricsAuth()
if (auth === null) throw new Error('retrieveNostrMetricsAuth() returned null') if (auth === null) throw new Error('retrieveNostrMetricsAuth() returned null')

View file

@ -741,6 +741,22 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
}}) }})
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e } }catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
break break
case 'GetLndForwardingMetrics':
try {
if (!methods.GetLndForwardingMetrics) throw new Error('method: GetLndForwardingMetrics is not implemented')
const authContext = await opts.NostrMetricsAuthGuard(req.appId, req.authIdentifier)
stats.guard = process.hrtime.bigint()
authCtx = authContext
const request = req.body
const error = Types.LndMetricsRequestValidate(request)
stats.validate = process.hrtime.bigint()
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback)
const response = await methods.GetLndForwardingMetrics({rpcName:'GetLndForwardingMetrics', ctx:authContext , req: request})
stats.handle = process.hrtime.bigint()
res({status: 'OK', ...response})
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
break
case 'GetLndMetrics': case 'GetLndMetrics':
try { try {
if (!methods.GetLndMetrics) throw new Error('method: GetLndMetrics is not implemented') if (!methods.GetLndMetrics) throw new Error('method: GetLndMetrics is not implemented')

View file

@ -28,8 +28,8 @@ export type MetricsContext = {
app_id: string app_id: string
operator_id: string operator_id: string
} }
export type MetricsMethodInputs = GetAppsMetrics_Input | GetBundleMetrics_Input | GetErrorStats_Input | GetLndMetrics_Input | GetProvidersDisruption_Input | GetSingleBundleMetrics_Input | GetSingleUsageMetrics_Input | GetUsageMetrics_Input | PingSubProcesses_Input | ResetMetricsStorages_Input | SubmitWebRtcMessage_Input | ZipMetricsStorages_Input export type MetricsMethodInputs = GetAppsMetrics_Input | GetBundleMetrics_Input | GetErrorStats_Input | GetLndForwardingMetrics_Input | GetLndMetrics_Input | GetProvidersDisruption_Input | GetSingleBundleMetrics_Input | GetSingleUsageMetrics_Input | GetUsageMetrics_Input | PingSubProcesses_Input | ResetMetricsStorages_Input | SubmitWebRtcMessage_Input | ZipMetricsStorages_Input
export type MetricsMethodOutputs = GetAppsMetrics_Output | GetBundleMetrics_Output | GetErrorStats_Output | GetLndMetrics_Output | GetProvidersDisruption_Output | GetSingleBundleMetrics_Output | GetSingleUsageMetrics_Output | GetUsageMetrics_Output | PingSubProcesses_Output | ResetMetricsStorages_Output | SubmitWebRtcMessage_Output | ZipMetricsStorages_Output export type MetricsMethodOutputs = GetAppsMetrics_Output | GetBundleMetrics_Output | GetErrorStats_Output | GetLndForwardingMetrics_Output | GetLndMetrics_Output | GetProvidersDisruption_Output | GetSingleBundleMetrics_Output | GetSingleUsageMetrics_Output | GetUsageMetrics_Output | PingSubProcesses_Output | ResetMetricsStorages_Output | SubmitWebRtcMessage_Output | ZipMetricsStorages_Output
export type UserContext = { export type UserContext = {
app_id: string app_id: string
app_user_id: string app_user_id: string
@ -132,6 +132,9 @@ export type GetLiveDebitRequests_Output = ResultError | { status: 'OK' }
export type GetLiveUserOperations_Input = {rpcName:'GetLiveUserOperations', cb:(res: LiveUserOperation, err:Error|null)=> void} export type GetLiveUserOperations_Input = {rpcName:'GetLiveUserOperations', cb:(res: LiveUserOperation, err:Error|null)=> void}
export type GetLiveUserOperations_Output = ResultError | { status: 'OK' } export type GetLiveUserOperations_Output = ResultError | { status: 'OK' }
export type GetLndForwardingMetrics_Input = {rpcName:'GetLndForwardingMetrics', req: LndMetricsRequest}
export type GetLndForwardingMetrics_Output = ResultError | ({ status: 'OK' } & LndForwardingMetrics)
export type GetLndMetrics_Input = {rpcName:'GetLndMetrics', req: LndMetricsRequest} export type GetLndMetrics_Input = {rpcName:'GetLndMetrics', req: LndMetricsRequest}
export type GetLndMetrics_Output = ResultError | ({ status: 'OK' } & LndMetrics) export type GetLndMetrics_Output = ResultError | ({ status: 'OK' } & LndMetrics)
@ -338,6 +341,7 @@ export type ServerMethods = {
GetLNURLChannelLink?: (req: GetLNURLChannelLink_Input & {ctx: UserContext }) => Promise<LnurlLinkResponse> GetLNURLChannelLink?: (req: GetLNURLChannelLink_Input & {ctx: UserContext }) => Promise<LnurlLinkResponse>
GetLiveDebitRequests?: (req: GetLiveDebitRequests_Input & {ctx: UserContext }) => Promise<void> GetLiveDebitRequests?: (req: GetLiveDebitRequests_Input & {ctx: UserContext }) => Promise<void>
GetLiveUserOperations?: (req: GetLiveUserOperations_Input & {ctx: UserContext }) => Promise<void> GetLiveUserOperations?: (req: GetLiveUserOperations_Input & {ctx: UserContext }) => Promise<void>
GetLndForwardingMetrics?: (req: GetLndForwardingMetrics_Input & {ctx: MetricsContext }) => Promise<LndForwardingMetrics>
GetLndMetrics?: (req: GetLndMetrics_Input & {ctx: MetricsContext }) => Promise<LndMetrics> GetLndMetrics?: (req: GetLndMetrics_Input & {ctx: MetricsContext }) => Promise<LndMetrics>
GetLnurlPayInfo?: (req: GetLnurlPayInfo_Input & {ctx: GuestContext }) => Promise<LnurlPayInfoResponse> GetLnurlPayInfo?: (req: GetLnurlPayInfo_Input & {ctx: GuestContext }) => Promise<LnurlPayInfoResponse>
GetLnurlPayLink?: (req: GetLnurlPayLink_Input & {ctx: UserContext }) => Promise<LnurlLinkResponse> GetLnurlPayLink?: (req: GetLnurlPayLink_Input & {ctx: UserContext }) => Promise<LnurlLinkResponse>
@ -2049,6 +2053,77 @@ export const LndChannelsValidate = (o?: LndChannels, opts: LndChannelsOptions =
return null return null
} }
export type LndForwardingEvent = {
amt_in: number
amt_out: number
at_unix: number
chan_id_in: string
chan_id_out: string
fee: number
}
export const LndForwardingEventOptionalFields: [] = []
export type LndForwardingEventOptions = OptionsBaseMessage & {
checkOptionalsAreSet?: []
amt_in_CustomCheck?: (v: number) => boolean
amt_out_CustomCheck?: (v: number) => boolean
at_unix_CustomCheck?: (v: number) => boolean
chan_id_in_CustomCheck?: (v: string) => boolean
chan_id_out_CustomCheck?: (v: string) => boolean
fee_CustomCheck?: (v: number) => boolean
}
export const LndForwardingEventValidate = (o?: LndForwardingEvent, opts: LndForwardingEventOptions = {}, path: string = 'LndForwardingEvent::root.'): Error | null => {
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
if (typeof o.amt_in !== 'number') return new Error(`${path}.amt_in: is not a number`)
if (opts.amt_in_CustomCheck && !opts.amt_in_CustomCheck(o.amt_in)) return new Error(`${path}.amt_in: custom check failed`)
if (typeof o.amt_out !== 'number') return new Error(`${path}.amt_out: is not a number`)
if (opts.amt_out_CustomCheck && !opts.amt_out_CustomCheck(o.amt_out)) return new Error(`${path}.amt_out: custom check failed`)
if (typeof o.at_unix !== 'number') return new Error(`${path}.at_unix: is not a number`)
if (opts.at_unix_CustomCheck && !opts.at_unix_CustomCheck(o.at_unix)) return new Error(`${path}.at_unix: custom check failed`)
if (typeof o.chan_id_in !== 'string') return new Error(`${path}.chan_id_in: is not a string`)
if (opts.chan_id_in_CustomCheck && !opts.chan_id_in_CustomCheck(o.chan_id_in)) return new Error(`${path}.chan_id_in: custom check failed`)
if (typeof o.chan_id_out !== 'string') return new Error(`${path}.chan_id_out: is not a string`)
if (opts.chan_id_out_CustomCheck && !opts.chan_id_out_CustomCheck(o.chan_id_out)) return new Error(`${path}.chan_id_out: custom check failed`)
if (typeof o.fee !== 'number') return new Error(`${path}.fee: is not a number`)
if (opts.fee_CustomCheck && !opts.fee_CustomCheck(o.fee)) return new Error(`${path}.fee: custom check failed`)
return null
}
export type LndForwardingMetrics = {
events: LndForwardingEvent[]
total_fees: number
}
export const LndForwardingMetricsOptionalFields: [] = []
export type LndForwardingMetricsOptions = OptionsBaseMessage & {
checkOptionalsAreSet?: []
events_ItemOptions?: LndForwardingEventOptions
events_CustomCheck?: (v: LndForwardingEvent[]) => boolean
total_fees_CustomCheck?: (v: number) => boolean
}
export const LndForwardingMetricsValidate = (o?: LndForwardingMetrics, opts: LndForwardingMetricsOptions = {}, path: string = 'LndForwardingMetrics::root.'): Error | null => {
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
if (!Array.isArray(o.events)) return new Error(`${path}.events: is not an array`)
for (let index = 0; index < o.events.length; index++) {
const eventsErr = LndForwardingEventValidate(o.events[index], opts.events_ItemOptions, `${path}.events[${index}]`)
if (eventsErr !== null) return eventsErr
}
if (opts.events_CustomCheck && !opts.events_CustomCheck(o.events)) return new Error(`${path}.events: custom check failed`)
if (typeof o.total_fees !== 'number') return new Error(`${path}.total_fees: is not a number`)
if (opts.total_fees_CustomCheck && !opts.total_fees_CustomCheck(o.total_fees)) return new Error(`${path}.total_fees: custom check failed`)
return null
}
export type LndGetInfoRequest = { export type LndGetInfoRequest = {
nodeId: number nodeId: number
} }

View file

@ -221,6 +221,15 @@ service LightningPub {
option (http_route) = "/api/reports/lnd"; option (http_route) = "/api/reports/lnd";
option (nostr) = true; option (nostr) = true;
} }
rpc GetLndForwardingMetrics(structs.LndMetricsRequest) returns (structs.LndForwardingMetrics) {
option (auth_type) = "Metrics";
option (http_method) = "post";
option (http_route) = "/api/reports/lnd/forwarding";
option (nostr) = true;
}
rpc SubmitWebRtcMessage(structs.WebRtcMessage) returns (structs.WebRtcAnswer) { rpc SubmitWebRtcMessage(structs.WebRtcMessage) returns (structs.WebRtcAnswer) {
option (auth_type) = "Metrics"; option (auth_type) = "Metrics";
option (http_method) = "post"; option (http_method) = "post";

View file

@ -213,6 +213,20 @@ message RootOperation {
int64 created_at_unix = 4; int64 created_at_unix = 4;
} }
message LndForwardingEvent {
string chan_id_in = 1;
string chan_id_out = 2;
int64 amt_in = 3;
int64 amt_out = 4;
int64 fee = 5;
int64 at_unix = 6;
}
message LndForwardingMetrics {
int64 total_fees = 1;
repeated LndForwardingEvent events = 2;
}
message LndNodeMetrics { message LndNodeMetrics {
repeated GraphPoint chain_balance = 1; repeated GraphPoint chain_balance = 1;
repeated GraphPoint channel_balance = 2; repeated GraphPoint channel_balance = 2;

View file

@ -448,8 +448,8 @@ export default class {
return { confirmedBalance: Number(confirmedBalance), unconfirmedBalance: Number(unconfirmedBalance), totalBalance: Number(totalBalance), channelsBalance } return { confirmedBalance: Number(confirmedBalance), unconfirmedBalance: Number(unconfirmedBalance), totalBalance: Number(totalBalance), channelsBalance }
} }
async GetForwardingHistory(indexOffset: number, startTime = 0): Promise<ForwardingHistoryResponse> { async GetForwardingHistory(indexOffset: number, startTime = 0, endTime = 0): Promise<ForwardingHistoryResponse> {
const { response } = await this.lightning.forwardingHistory({ indexOffset, numMaxEvents: 0, startTime: BigInt(startTime), endTime: 0n, peerAliasLookup: false }, DeadLineMetadata()) const { response } = await this.lightning.forwardingHistory({ indexOffset, numMaxEvents: 0, startTime: BigInt(startTime), endTime: BigInt(endTime), peerAliasLookup: false }, DeadLineMetadata())
return response return response
} }

View file

@ -309,6 +309,21 @@ export default class Handler {
} }
async GetLndForwardingMetrics(req: Types.LndMetricsRequest): Promise<Types.LndForwardingMetrics> {
const fwEvents = await this.lnd.GetForwardingHistory(0, req.from_unix, req.to_unix)
let totalFees = 0
const events: Types.LndForwardingEvent[] = fwEvents.forwardingEvents.map(e => {
totalFees += Number(e.fee)
return {
chan_id_in: e.chanIdIn, chan_id_out: e.chanIdOut, amt_in: Number(e.amtIn), amt_out: Number(e.amtOut), fee: Number(e.fee), at_unix: Number(e.timestampNs)
}
})
return {
total_fees: totalFees,
events: events
}
}
async GetLndMetrics(req: Types.LndMetricsRequest): Promise<Types.LndMetrics> { async GetLndMetrics(req: Types.LndMetricsRequest): Promise<Types.LndMetrics> {
const [chansInfo, pendingChansInfo, closedChansInfo, routing, rootOps] = await Promise.all([ const [chansInfo, pendingChansInfo, closedChansInfo, routing, rootOps] = await Promise.all([

View file

@ -46,6 +46,9 @@ export default (mainHandler: Main): Types.ServerMethods => {
GetLndMetrics: async ({ ctx, req }) => { GetLndMetrics: async ({ ctx, req }) => {
return mainHandler.metricsManager.GetLndMetrics(req) return mainHandler.metricsManager.GetLndMetrics(req)
}, },
GetLndForwardingMetrics: async ({ ctx, req }) => {
return mainHandler.metricsManager.GetLndForwardingMetrics(req)
},
ResetMetricsStorages: async ({ ctx }) => { ResetMetricsStorages: async ({ ctx }) => {
return mainHandler.utils.tlvStorageFactory.ResetStorages() return mainHandler.utils.tlvStorageFactory.ResetStorages()
}, },