faraday/cmd/frcli/forwarding_ability.go
bitromortac ec5d4338b9 frcli: add forwardingability command
Add the frcli forwardingability command to query the RPC over a time range
and optional liquidity floor. It decodes the sparse response and prints each
pair with its raw effective uptime and forwarded volume, plus uptime fraction
and velocity derived from the reported window.
2026-06-11 12:40:32 +02:00

122 lines
3 KiB
Go

package main
import (
"context"
"sort"
"github.com/lightninglabs/faraday/frdrpc"
"github.com/urfave/cli"
)
var forwardingAbilityCommand = cli.Command{
Name: "forwardingability",
Category: "insights",
Usage: "Get forwarding ability analysis of peer pairs.",
Flags: []cli.Flag{
cli.Uint64Flag{
Name: "start_time",
Usage: "start time of the query range as a unix " +
"timestamp",
},
cli.Uint64Flag{
Name: "end_time",
Usage: "end time of the query range as a unix " +
"timestamp; zero defaults to the server's " +
"current time",
},
cli.Uint64Flag{
Name: "liquidity_floor_sat",
Usage: "the minimum directional liquidity in " +
"satoshis for a pair to count as " +
"economically forwardable; zero uses the " +
"server default",
},
cli.Float64Flag{
Name: "uptime_threshold",
Usage: "the uptime fraction in [0,1] at or above " +
"which a non-forwarding pair is reported as " +
"up but idle; zero uses the server default",
},
},
Action: queryForwardingAbility,
}
type pairView struct {
PeerIn string `json:"peer_in"`
PeerOut string `json:"peer_out"`
EffectiveUptimeS int64 `json:"effective_uptime_s"`
ForwardedSat int64 `json:"forwarded_sat"`
UptimeFraction float64 `json:"uptime_fraction"`
Velocity float64 `json:"velocity"`
}
func queryForwardingAbility(ctx *cli.Context) error {
client, cleanup := getClient(ctx)
defer cleanup()
req := &frdrpc.ForwardingAbilityRequest{
StartTime: ctx.Uint64("start_time"),
EndTime: ctx.Uint64("end_time"),
LiquidityFloorSat: ctx.Uint64("liquidity_floor_sat"),
UptimeThreshold: ctx.Float64("uptime_threshold"),
}
rpcCtx := context.Background()
resp, err := client.ForwardingAbility(rpcCtx, req)
if err != nil {
return err
}
abilities, err := frdrpc.DecodeForwardingAbility(resp)
if err != nil {
return err
}
// The metrics are raw, so derive uptime fraction and velocity here from
// the window the server reported.
windowSeconds := resp.EndTime - resp.StartTime
var views []pairView
for inPeer, outMap := range abilities {
for outPeer, ability := range outMap {
var uptimeFraction, velocity float64
if windowSeconds > 0 {
uptimeFraction = float64(
ability.EffectiveUptimeS,
) / float64(windowSeconds)
}
if ability.EffectiveUptimeS > 0 {
velocity = float64(ability.ForwardedSat) /
float64(ability.EffectiveUptimeS)
}
views = append(
views, pairView{
PeerIn: inPeer,
PeerOut: outPeer,
EffectiveUptimeS: ability.EffectiveUptimeS,
ForwardedSat: ability.ForwardedSat,
UptimeFraction: uptimeFraction,
Velocity: velocity,
},
)
}
}
// Stable sort by PeerIn, then PeerOut.
sort.SliceStable(
views,
func(i, j int) bool {
if views[i].PeerIn != views[j].PeerIn {
return views[i].PeerIn < views[j].PeerIn
}
return views[i].PeerOut < views[j].PeerOut
},
)
printJSON(views)
return nil
}