From 08d09a6da66a44459669c463a9745ace91cc198f Mon Sep 17 00:00:00 2001 From: carla Date: Fri, 21 Feb 2020 08:52:21 +0200 Subject: [PATCH 1/8] recommend: use channel insights in recommendations Channel insights calcualte the values required for recommendations, pass insights in rather than raw rpc channels so that we do not need to calcualte values (uptime, revenue etc) in the recommend package and can focus on producing recommendations. --- recommend/recommend.go | 83 ++++++++++++++++-------------- recommend/recommend_test.go | 90 ++++++++++++++++++--------------- trmrpc/close_recommendations.go | 5 +- 3 files changed, 97 insertions(+), 81 deletions(-) diff --git a/recommend/recommend.go b/recommend/recommend.go index df3efdd..0c15d92 100644 --- a/recommend/recommend.go +++ b/recommend/recommend.go @@ -4,7 +4,7 @@ // time will be considered for closing. // // Channels will be assessed based on the following data points: -// - Uptime percentage +// - Uptime ratio // // Channels that are outliers within the set of channels that are eligible for // close recommendation will be recommended for closure. @@ -15,7 +15,7 @@ import ( "time" "github.com/lightninglabs/terminator/dataset" - "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightninglabs/terminator/insights" ) var ( @@ -33,9 +33,9 @@ var ( // CloseRecommendationConfig provides the functions and parameters required to // provide close recommendations. type CloseRecommendationConfig struct { - // OpenChannels is a function which returns all of our currently open, - // public channels. - OpenChannels func() ([]*lnrpc.Channel, error) + // ChannelInsights is a function which returns a set of channel insights + // for our current set of channels. + ChannelInsights func() ([]*insights.ChannelInfo, error) // OutlierMultiplier is the number of inter quartile ranges a value // should be away from the lower/upper quartile to be considered an @@ -43,9 +43,9 @@ type CloseRecommendationConfig struct { // recommendations and 3 for more cautious recommendations. OutlierMultiplier float64 - // UptimeThreshold is the uptime percentage over the channel's observed + // UptimeThreshold is the uptime ratio over the channel's observed // lifetime beneath which channels will be recommended for close. This - // value is expressed as a percentage in [0,1], and will default to 0 if + // value is expressed as a ratio in [0,1], and will default to 0 if // it is not set. UptimeThreshold float64 @@ -85,16 +85,16 @@ type Report struct { // CloseRecommendations returns a report which contains information about the // channels that were considered and a list of close recommendations. Channels -// are considered for close if their uptime percentage is a lower outlier in -// uptime percentage dataset. +// are considered for close if their uptime ratio is a lower outlier in +// uptime ratio dataset. func CloseRecommendations(cfg *CloseRecommendationConfig) (*Report, error) { // Check that the minimum wait time is non-zero. if cfg.MinimumMonitored == 0 { return nil, errZeroMinMonitored } - // Get the set of currently open channels. - channels, err := cfg.OpenChannels() + // Get the set of insights for our currently open channels. + channels, err := cfg.ChannelInsights() if err != nil { return nil, err } @@ -102,13 +102,13 @@ func CloseRecommendations(cfg *CloseRecommendationConfig) (*Report, error) { // Filter out channels that are below the minimum required age. filtered := filterChannels(channels, cfg.MinimumMonitored) - // Produce a dataset containing uptime percentage for channels that have + // Produce a dataset containing uptime ratio for channels that have // been monitored for longer than the minimum time. uptime := getUptimeDataset(filtered) report := &Report{ TotalChannels: len(channels), - ConsideredChannels: len(uptime), + ConsideredChannels: len(filtered), } // Get close recommendations based on outliers. @@ -174,50 +174,57 @@ func getOutlierRecs(uptime dataset.Dataset, return recommendations, nil } -// filterChannels filters out channels that are beneath the minimum age and -// produces a map of channel outpoint strings to rpc channels which contains -// the channels that are eligible for close recommendation. -func filterChannels(openChannels []*lnrpc.Channel, - minimumAge time.Duration) map[string]*lnrpc.Channel { +// filterChannels filters out channels that are beneath the minimum age, or +// are private and returns a set of channels that are eligible for close +// recommendations. +func filterChannels(channelInsights []*insights.ChannelInfo, + minimumAge time.Duration) []*insights.ChannelInfo { - // Create a map which will hold channel point labels to uptime - // percentage. - channels := make(map[string]*lnrpc.Channel) + filteredChannels := make( + []*insights.ChannelInfo, 0, len(channelInsights), + ) + + for _, channel := range channelInsights { + if channel.MonitoredFor < minimumAge { + log.Tracef("Channel: %v has not been "+ + "monitored for long enough, excluding it "+ + "from consideration", channel.ChannelPoint) - for _, channel := range openChannels { - if channel.Lifetime < int64(minimumAge.Seconds()) { - log.Tracef("Channel: %v has not been monitored for "+ - "long enough, excluding it from consideration", - channel.ChannelPoint) continue } - channels[channel.ChannelPoint] = channel + if channel.Private { + log.Tracef("Channel: %v is private, excluding "+ + "it from consideration", channel.ChannelPoint) + + continue + } + + filteredChannels = append(filteredChannels, channel) } log.Debugf("considering: %v channels for close out of %v", - len(channels), len(openChannels)) + len(filteredChannels), len(channelInsights)) - return channels + return filteredChannels } // getUptimeDataset takes a set of channels that are eligible for close and // produces an uptime dataset. func getUptimeDataset( - eligibleChannels map[string]*lnrpc.Channel) dataset.Dataset { + eligibleChannels []*insights.ChannelInfo) dataset.Dataset { // Create a map which will hold channel point string label to uptime - // percentage. - var channels = make(map[string]float64) + // ratio. + var channels = make(map[string]float64, len(eligibleChannels)) - for outpoint, channel := range eligibleChannels { - // Calculate the uptime percentage for the channel and add it + for _, channel := range eligibleChannels { + // Calculate the uptime ratio for the channel and add it // to the channel -> uptime map. - uptimePercentage := float64(channel.Uptime) / float64(channel.Lifetime) - channels[outpoint] = uptimePercentage + uptimeRatio := float64(channel.Uptime) / + float64(channel.MonitoredFor) - log.Tracef("channel: %v has uptime percentage: %v", - outpoint, uptimePercentage) + channels[channel.ChannelPoint] = uptimeRatio } // Create a dataset for the uptime values we have collected. diff --git a/recommend/recommend_test.go b/recommend/recommend_test.go index 1691628..1cb9046 100644 --- a/recommend/recommend_test.go +++ b/recommend/recommend_test.go @@ -6,7 +6,7 @@ import ( "time" "github.com/lightninglabs/terminator/dataset" - "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightninglabs/terminator/insights" ) // TestCloseRecommendations tests CloseRecommendations for error cases where @@ -15,28 +15,25 @@ import ( // the minimum acceptable number of channels. It does not test the report // provided, because that will be covered by further tests. func TestCloseRecommendations(t *testing.T) { - var ( - openChanErr = errors.New("intentional test err") - hourSeconds = int64(time.Hour.Seconds()) - ) + var openChanErr = errors.New("intentional test err") tests := []struct { name string - OpenChannels func() ([]*lnrpc.Channel, error) + ChanInsights func() ([]*insights.ChannelInfo, error) MinMonitored time.Duration expectedErr error }{ { name: "no channels", - OpenChannels: func() ([]*lnrpc.Channel, error) { + ChanInsights: func() ([]*insights.ChannelInfo, error) { return nil, nil }, MinMonitored: time.Hour, expectedErr: nil, }, { - name: "open channels fails", - OpenChannels: func() ([]*lnrpc.Channel, error) { + name: "channel insights fails", + ChanInsights: func() ([]*insights.ChannelInfo, error) { return nil, openChanErr }, MinMonitored: time.Hour, @@ -44,7 +41,7 @@ func TestCloseRecommendations(t *testing.T) { }, { name: "zero min monitored", - OpenChannels: func() ([]*lnrpc.Channel, error) { + ChanInsights: func() ([]*insights.ChannelInfo, error) { return nil, nil }, MinMonitored: 0, @@ -52,19 +49,19 @@ func TestCloseRecommendations(t *testing.T) { }, { name: "enough channels", - OpenChannels: func() ([]*lnrpc.Channel, error) { - return []*lnrpc.Channel{ + ChanInsights: func() ([]*insights.ChannelInfo, error) { + return []*insights.ChannelInfo{ { ChannelPoint: "a:1", - Lifetime: hourSeconds, + MonitoredFor: time.Hour, }, { ChannelPoint: "b:2", - Lifetime: hourSeconds, + MonitoredFor: time.Hour, }, { ChannelPoint: "c:3", - Lifetime: hourSeconds, + MonitoredFor: time.Hour, }, }, nil }, @@ -81,11 +78,10 @@ func TestCloseRecommendations(t *testing.T) { _, err := CloseRecommendations( &CloseRecommendationConfig{ - OpenChannels: test.OpenChannels, + ChannelInsights: test.ChanInsights, OutlierMultiplier: 3, MinimumMonitored: test.MinMonitored, - }, - ) + }) if err != test.expectedErr { t.Fatalf("expected: %v, got: %v", test.expectedErr, err) @@ -227,46 +223,55 @@ func TestGetCloseRecs(t *testing.T) { // TestFilterChannels tests filtering of channels based on their lifetime. func TestFilterChannels(t *testing.T) { - openChannels := []*lnrpc.Channel{ + chanInsights := []*insights.ChannelInfo{ { ChannelPoint: "a:0", - Lifetime: 10, + MonitoredFor: 10, Uptime: 1, }, { ChannelPoint: "a:1", - Lifetime: 100, + MonitoredFor: 100, Uptime: 1, }, { ChannelPoint: "a:2", - Lifetime: 100, + MonitoredFor: 100, Uptime: 1, }, { ChannelPoint: "a:3", - Lifetime: 100, + MonitoredFor: 100, Uptime: 1, }, } tests := []struct { - name string - openChannels []*lnrpc.Channel - minAge time.Duration - expectedChanPoints []string + name string + chanInsights []*insights.ChannelInfo + minAge time.Duration + expectedChannels map[string]bool }{ { - name: "one filtered - monitored time", - openChannels: openChannels, - minAge: time.Second * 15, - expectedChanPoints: []string{"a:1", "a:2", "a:3"}, + name: "one filtered - monitored time", + chanInsights: chanInsights, + minAge: 15, + expectedChannels: map[string]bool{ + "a:1": true, + "a:2": true, + "a:3": true, + }, }, { - name: "all channels included", - openChannels: openChannels, - minAge: time.Second * 5, - expectedChanPoints: []string{"a:0", "a:1", "a:2", "a:3"}, + name: "all channels included", + chanInsights: chanInsights, + minAge: 5, + expectedChannels: map[string]bool{ + "a:0": true, + "a:1": true, + "a:2": true, + "a:3": true, + }, }, } @@ -276,18 +281,19 @@ func TestFilterChannels(t *testing.T) { t.Run(test.name, func(t *testing.T) { t.Parallel() - filtered := filterChannels(test.openChannels, test.minAge) + filtered := filterChannels(test.chanInsights, test.minAge) - if len(test.expectedChanPoints) != len(filtered) { + if len(test.expectedChannels) != len(filtered) { t.Fatalf("expected: %v channels, got: %v", - len(test.expectedChanPoints), + len(test.expectedChannels), len(filtered)) } - for _, expected := range test.expectedChanPoints { - if _, ok := filtered[expected]; !ok { - t.Fatalf("expected channel: %v to "+ - "be present", expected) + for _, filteredChan := range filtered { + _, ok := test.expectedChannels[filteredChan.ChannelPoint] + if !ok { + t.Fatalf("unexpected channel: %v", + filteredChan) } } }) diff --git a/trmrpc/close_recommendations.go b/trmrpc/close_recommendations.go index 3a84c6b..4389268 100644 --- a/trmrpc/close_recommendations.go +++ b/trmrpc/close_recommendations.go @@ -4,6 +4,7 @@ import ( "context" "time" + "github.com/lightninglabs/terminator/insights" "github.com/lightninglabs/terminator/recommend" ) @@ -15,7 +16,9 @@ func parseRequest(ctx context.Context, cfg *Config, // Create a close recommendations config with the minimum monitored // value provided in the request and the default outlier multiplier. recConfig := &recommend.CloseRecommendationConfig{ - OpenChannels: cfg.wrapListChannels(ctx, true), + ChannelInsights: func() ([]*insights.ChannelInfo, error) { + return channelInsights(ctx, cfg) + }, MinimumMonitored: time.Second * time.Duration(req.MinimumMonitored), OutlierMultiplier: recommend.DefaultOutlierMultiplier, From 106ba36d0fe8cef357cc5b1af9d8a86acbb82e54 Mon Sep 17 00:00:00 2001 From: carla Date: Fri, 21 Feb 2020 08:55:36 +0200 Subject: [PATCH 2/8] recommend: make outliers and threshold more configurable This change passes in lower outlier and below threshold values to allow for more flexible recommendations; future metrics used may want to recommend channels with upper values for close (eg recommend high peer flap rate). --- recommend/recommend.go | 54 ++++++++++----- recommend/recommend_test.go | 131 +++++++++++++++++++++++++++++++++--- 2 files changed, 158 insertions(+), 27 deletions(-) diff --git a/recommend/recommend.go b/recommend/recommend.go index 0c15d92..4fdbd85 100644 --- a/recommend/recommend.go +++ b/recommend/recommend.go @@ -113,7 +113,7 @@ func CloseRecommendations(cfg *CloseRecommendationConfig) (*Report, error) { // Get close recommendations based on outliers. report.OutlierRecommendations, err = getOutlierRecs( - uptime, cfg.OutlierMultiplier, + uptime, cfg.OutlierMultiplier, false, ) if err != nil { return nil, err @@ -121,29 +121,29 @@ func CloseRecommendations(cfg *CloseRecommendationConfig) (*Report, error) { // Get close recommendations based on threshold. report.ThresholdRecommendations = getThresholdRecs( - uptime, cfg.UptimeThreshold, + uptime, cfg.UptimeThreshold, true, ) return report, nil } -// getThresholdRecs returns a map of channel points to values that are below a -// given threshold. -func getThresholdRecs(uptime dataset.Dataset, - threshold float64) map[string]Recommendation { +// getThresholdRecs returns a map of channel points to values that are above +// or below a given threshold. +func getThresholdRecs(values dataset.Dataset, + threshold float64, belowThreshold bool) map[string]Recommendation { // Get a map of channel labels to a boolean indicating whether // they are beneath the threshold. - thresholdValues := uptime.GetThreshold(threshold, true) + thresholdValues := values.GetThreshold(threshold, belowThreshold) recommendations := make( map[string]Recommendation, len(thresholdValues), ) - for chanPoint, belowThrehsold := range thresholdValues { + for chanPoint, crossesThreshold := range thresholdValues { recommendations[chanPoint] = Recommendation{ - Value: uptime.Value(chanPoint), - RecommendClose: belowThrehsold, + Value: values.Value(chanPoint), + RecommendClose: crossesThreshold, } } @@ -151,23 +151,43 @@ func getThresholdRecs(uptime dataset.Dataset, } // getOutlierRecs generates map of channel outpoint strings to booleans -// indicating whether we recommend closing a channel. -func getOutlierRecs(uptime dataset.Dataset, - outlierMultiplier float64) (map[string]Recommendation, error) { +// indicating whether we recommend closing a channel. It takes a outlier +// multiplier which scales the degree to which we want to calculate outliers, +// and an upper outlier boolean which determines whether we want to identify +// upper or lower outliers. +func getOutlierRecs(values dataset.Dataset, + outlierMultiplier float64, + upperOutlier bool) (map[string]Recommendation, error) { recommendations := make(map[string]Recommendation) - outliers, err := uptime.GetOutliers(outlierMultiplier) + outliers, err := values.GetOutliers(outlierMultiplier) if err != nil { return nil, err } // Add a recommendation for each channel to our set of recommendations. - // If the channel is a lower outlier, we recommend it for close. + // RecommendClose in the recommendation will be set to true if the + // channel matches the outlier type we are looking for (upper or + // lower). for chanPoint, outlier := range outliers { + var recommendClose bool + + // If we want to detect upper outliers, and the channel is a + // upper outlier, set recommend close to true. + if upperOutlier && outlier.UpperOutlier { + recommendClose = true + } + + // If we want to detect lower outliers, and the channel is a + // lower outlier, set recommend close to true. + if !upperOutlier && outlier.LowerOutlier { + recommendClose = true + } + recommendations[chanPoint] = Recommendation{ - Value: uptime.Value(chanPoint), - RecommendClose: outlier.LowerOutlier, + Value: values.Value(chanPoint), + RecommendClose: recommendClose, } } diff --git a/recommend/recommend_test.go b/recommend/recommend_test.go index 1cb9046..7cfb67b 100644 --- a/recommend/recommend_test.go +++ b/recommend/recommend_test.go @@ -90,19 +90,21 @@ func TestCloseRecommendations(t *testing.T) { } } -// TestGetCloseRecs tests the generating of close recommendations for a set of -// channels. It also contains a test case for when there are too few channels -// to calculate outliers, to test that the error is silenced and no -// recommendations are provided. -func TestGetCloseRecs(t *testing.T) { +// TestOutlierRecommendations tests the generating of close recommendations +// for a set of channels based on whether they are outliers. It also contains +// a test case for when there are too few channels to calculate outliers, to +// test that the error is silenced and no recommendations are provided. +func TestOutlierRecommendations(t *testing.T) { tests := []struct { name string + upperOutlier bool channelUptimes map[string]float64 expectedRecs map[string]Recommendation outlierMultiplier float64 }{ { - name: "not enough values, all false", + name: "not enough values, all false", + upperOutlier: false, channelUptimes: map[string]float64{ "a:0": 0.7, }, @@ -115,7 +117,9 @@ func TestGetCloseRecs(t *testing.T) { outlierMultiplier: 2, }, { - name: "similar values, weak outlier no recommendations", + name: "similar values, weak outlier no " + + "recommendations", + upperOutlier: false, channelUptimes: map[string]float64{ "a:0": 0.7, "a:1": 0.6, @@ -129,7 +133,9 @@ func TestGetCloseRecs(t *testing.T) { }, }, { - name: "similar values, strong outlier no recommendations", + name: "similar values, strong outlier no " + + "make linrecommendations", + upperOutlier: false, channelUptimes: map[string]float64{ "a:0": 0.7, "a:1": 0.6, @@ -143,7 +149,8 @@ func TestGetCloseRecs(t *testing.T) { }, }, { - name: "lower outlier recommended for close", + name: "lower outlier recommended for close", + upperOutlier: false, channelUptimes: map[string]float64{ "a:0": 0.6, "a:1": 0.6, @@ -162,7 +169,27 @@ func TestGetCloseRecs(t *testing.T) { "a:5": {Value: 0.1, RecommendClose: true}, }, }, - + { + name: "upper outlier recommended for close", + upperOutlier: true, + channelUptimes: map[string]float64{ + "a:0": 0.9, + "a:1": 0.2, + "a:2": 0.2, + "a:3": 0.2, + "a:4": 0.1, + "a:5": 0.1, + }, + outlierMultiplier: 3, + expectedRecs: map[string]Recommendation{ + "a:0": {Value: 0.9, RecommendClose: true}, + "a:1": {Value: 0.2, RecommendClose: false}, + "a:2": {Value: 0.2, RecommendClose: false}, + "a:3": {Value: 0.2, RecommendClose: false}, + "a:4": {Value: 0.1, RecommendClose: false}, + "a:5": {Value: 0.1, RecommendClose: false}, + }, + }, { name: "zero multiplier replaced with default", channelUptimes: map[string]float64{ @@ -195,6 +222,7 @@ func TestGetCloseRecs(t *testing.T) { recs, err := getOutlierRecs( uptimeData, test.outlierMultiplier, + test.upperOutlier, ) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -221,6 +249,89 @@ func TestGetCloseRecs(t *testing.T) { } } +// TestThresholdRecommendations tests getting of recommendations above and +// below a threshold. +func TestThresholdRecommendations(t *testing.T) { + tests := []struct { + name string + belowThreshold bool + threshold float64 + values map[string]float64 + expectedRecs map[string]Recommendation + }{ + { + name: "nothing below threshold", + belowThreshold: true, + threshold: 0.4, + values: map[string]float64{ + "a:0": 0.8, + "a:1": 0.6, + }, + expectedRecs: map[string]Recommendation{ + "a:0": {Value: 0.8, RecommendClose: false}, + "a:1": {Value: 0.6, RecommendClose: false}, + }, + }, + { + name: "one below threshold", + belowThreshold: true, + threshold: 0.7, + values: map[string]float64{ + "a:0": 0.8, + "a:1": 0.6, + }, + expectedRecs: map[string]Recommendation{ + "a:0": {Value: 0.8, RecommendClose: false}, + "a:1": {Value: 0.6, RecommendClose: true}, + }, + }, + { + name: "one above threshold", + belowThreshold: false, + threshold: 0.7, + values: map[string]float64{ + "a:0": 0.8, + "a:1": 0.6, + }, + expectedRecs: map[string]Recommendation{ + "a:0": {Value: 0.8, RecommendClose: true}, + "a:1": {Value: 0.6, RecommendClose: false}, + }, + }, + } + + for _, test := range tests { + test := test + + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + recs := getThresholdRecs( + dataset.New(test.values), test.threshold, + test.belowThreshold, + ) + + if len(test.expectedRecs) != len(recs) { + t.Fatalf("expected: %v recommendations, "+ + "got: %v", len(test.expectedRecs), + len(recs)) + } + + // Run through our expected set of true recommendations + // and check that they match the set returned in the + // report. + for channel, expectClose := range test.expectedRecs { + recClose := recs[channel] + if recClose != expectClose { + t.Fatalf("expected close rec: %v"+ + " for channel: %v, got: %v", + expectClose, channel, recClose) + } + } + }) + } +} + // TestFilterChannels tests filtering of channels based on their lifetime. func TestFilterChannels(t *testing.T) { chanInsights := []*insights.ChannelInfo{ From eca6b62c90dc3c9d30e7505f5fb81f1a68031b83 Mon Sep 17 00:00:00 2001 From: carla Date: Fri, 21 Feb 2020 09:01:56 +0200 Subject: [PATCH 3/8] mutli: split uptime and threshold recommendations into separate calls This commit separates uptime based and threshold based recommendations into two separate calls. This decreases the matrix of possible values that users have to specify as we introduce more datasets that close decisions can be made based --- cmd/trmcli/close_recommendations.go | 118 ++++++--- cmd/trmcli/main.go | 3 +- recommend/recommend.go | 78 +++--- recommend/recommend_test.go | 34 ++- trmrpc/close_recommendations.go | 74 +++--- trmrpc/rpc.pb.go | 380 +++++++++++++++++----------- trmrpc/rpc.proto | 60 ++--- trmrpc/rpcserver.go | 31 ++- 8 files changed, 476 insertions(+), 302 deletions(-) diff --git a/cmd/trmcli/close_recommendations.go b/cmd/trmcli/close_recommendations.go index 05bb978..dcab464 100644 --- a/cmd/trmcli/close_recommendations.go +++ b/cmd/trmcli/close_recommendations.go @@ -11,19 +11,27 @@ import ( var ( defaultMinMonitored = time.Hour * 24 * 7 * 4 // four weeks in hours defaultOutlierMultiplier = 3 -) -var closeRecommendationCommand = cli.Command{ - Name: "closerecs", - Category: "channel", - Usage: "Get close recommendations for currently open channels.", - Flags: []cli.Flag{ - cli.Int64Flag{ - Name: "min_monitored", - Usage: "amount of time in seconds a channel should " + - "be monitored for to be eligible for close", - Value: int64(defaultMinMonitored.Seconds()), + // monitoredFlag is common to recommendation requests. + monitoredFlag = cli.Int64Flag{ + Name: "min_monitored", + Usage: "amount of time in seconds a channel should be monitored " + + "for to be eligible for close", + Value: int64(defaultMinMonitored.Seconds()), + } + + // Flags required for threshold close recommendations. + thresholdFlags = []cli.Flag{ + cli.Float64Flag{ + Name: "uptime_threshold", + Usage: "Ratio of uptime to time monitored, expressed" + + "in [0;1].", }, + monitoredFlag, + } + + // Flags required for outlier close recommendations. + outlierFlags = []cli.Flag{ cli.StringFlag{ Name: "outlier_mult", Usage: "(optional with outlier strategy) Number of " + @@ -32,43 +40,81 @@ var closeRecommendationCommand = cli.Command{ "Recommended values are 1.5 for aggressive " + "recommendations and 3 for conservative ones.", }, - cli.StringFlag{ - Name: "uptime_threshold", - Usage: "(optional) Uptime percentage threshold " + - "underneath which a channel will be recommended " + - "for close.", - }, - }, - Action: queryCloseRecommendations, + monitoredFlag, + } +) + +var thresholdRecommendationCommand = cli.Command{ + Name: "threshold", + Category: "recommendations", + Usage: "Get close recommendations for currently open channels " + + "based whether they are below a set threshold.", + Flags: thresholdFlags, + Action: queryThresholdRecommendations, } -func queryCloseRecommendations(ctx *cli.Context) error { +func queryThresholdRecommendations(ctx *cli.Context) error { client, cleanup := getClient(ctx) defer cleanup() - // Set monitored value from cli and default outlier multiplier. The - // outlier multiplier will be overwritten if the user provided it. - req := &trmrpc.CloseRecommendationsRequest{ - MinimumMonitored: ctx.Int64("min_monitored"), - OutlierMultiplier: float32(defaultOutlierMultiplier), + // Set monitored value from cli values, this value will always be + // non-zero because the flag has a default. + req := &trmrpc.ThresholdRecommendationsRequest{ + RecRequest: &trmrpc.CloseRecommendationRequest{ + MinimumMonitored: ctx.Int64("min_monitored"), + }, } - // If an a custom outlier multiple was set, use it. - if ctx.IsSet("outlier_mult") { - req.OutlierMultiplier = float32(ctx.Float64("outlier_mult")) - } - - // If an uptime threshold was set, use it. + // If an uptime threshold was set, use it, otherwise allow the call + // to proceed with 0 threshold, because we assess lower outlier <= the + // threshold so 0 is a valid value. if ctx.IsSet("uptime_threshold") { uptimeThreshold := float32(ctx.Float64("uptime_threshold")) - req.Threshold = - &trmrpc.CloseRecommendationsRequest_UptimeThreshold{ - UptimeThreshold: uptimeThreshold, - } + req.ThresholdValue = uptimeThreshold } rpcCtx := context.Background() - recs, err := client.CloseRecommendations(rpcCtx, req) + recs, err := client.ThresholdRecommendations(rpcCtx, req) + if err != nil { + return err + } + + printRespJSON(recs) + + return nil +} + +var outlierRecommendationCommand = cli.Command{ + Name: "outliers", + Category: "recommendations", + Usage: "Get close recommendations for currently open channels " + + "based on whether it is an outlier.", + Flags: outlierFlags, + Action: queryOutlierRecommendations, +} + +func queryOutlierRecommendations(ctx *cli.Context) error { + client, cleanup := getClient(ctx) + defer cleanup() + + // Set monitored value from cli and default outlier multiplier. The + // outlier multiplier will be overwritten if the user provided it, and + // the monitored value will always be non-zero because the flag has a + // default value. + req := &trmrpc.OutlierRecommendationsRequest{ + RecRequest: &trmrpc.CloseRecommendationRequest{ + MinimumMonitored: ctx.Int64("min_monitored"), + }, + OutlierMultiplier: float32(defaultOutlierMultiplier), + } + + // If an a custom outlier multiple was set, use it. + if ctx.IsSet("outlier_mult") { + req.OutlierMultiplier = float32(ctx.Float64("outlier_mult")) + } + + rpcCtx := context.Background() + recs, err := client.OutlierRecommendations(rpcCtx, req) if err != nil { return err } diff --git a/cmd/trmcli/main.go b/cmd/trmcli/main.go index b49b748..59b11f2 100644 --- a/cmd/trmcli/main.go +++ b/cmd/trmcli/main.go @@ -23,7 +23,8 @@ func main() { }, } app.Commands = []cli.Command{ - closeRecommendationCommand, + thresholdRecommendationCommand, + outlierRecommendationCommand, revenueReportCommand, channelInsightsCommand, } diff --git a/recommend/recommend.go b/recommend/recommend.go index 4fdbd85..d592567 100644 --- a/recommend/recommend.go +++ b/recommend/recommend.go @@ -31,24 +31,13 @@ var ( ) // CloseRecommendationConfig provides the functions and parameters required to -// provide close recommendations. +// provide close recommendations. This struct holds fields which are common to +// all recommendation calculation strategies. type CloseRecommendationConfig struct { // ChannelInsights is a function which returns a set of channel insights // for our current set of channels. ChannelInsights func() ([]*insights.ChannelInfo, error) - // OutlierMultiplier is the number of inter quartile ranges a value - // should be away from the lower/upper quartile to be considered an - // outlier. Recommended values are 1.5 for more aggressive - // recommendations and 3 for more cautious recommendations. - OutlierMultiplier float64 - - // UptimeThreshold is the uptime ratio over the channel's observed - // lifetime beneath which channels will be recommended for close. This - // value is expressed as a ratio in [0,1], and will default to 0 if - // it is not set. - UptimeThreshold float64 - // MinimumMonitored is the minimum amount of time that a channel must // have been monitored for before it is considered for closing. MinimumMonitored time.Duration @@ -72,22 +61,46 @@ type Report struct { // for long enough to be considered for close. ConsideredChannels int - // OutlierRecommendations is a map of chanel outpoints to a bool which - // indicates whether we should close the channel based on whether it is - // an outlier. - OutlierRecommendations map[string]Recommendation - - // ThresholdRecommendations is a map of chanel outpoints to a bool which - // indicates whether we should close the channel based on whether it is - // below a user provided threshold. - ThresholdRecommendations map[string]Recommendation + // Recommendations is a map of chanel outpoints to a bool which + // indicates whether we should close the channel. + Recommendations map[string]Recommendation } -// CloseRecommendations returns a report which contains information about the -// channels that were considered and a list of close recommendations. Channels -// are considered for close if their uptime ratio is a lower outlier in -// uptime ratio dataset. -func CloseRecommendations(cfg *CloseRecommendationConfig) (*Report, error) { +// OutlierRecommendations returns recommendations based on whether a value is a +// lower outlier within its current dataset. It takes an outlier multiplier value +// which is the number of inter quartile ranges a value should be away from the +// lower/upper quartile to be considered an outlier. Recommended values are 1.5 +// for more aggressive recommendations and 3 for more cautious recommendations. +func OutlierRecommendations(cfg *CloseRecommendationConfig, + outlierMultiplier float64) (*Report, error) { + + getRecs := func(dataset dataset.Dataset) (map[string]Recommendation, error) { + return getOutlierRecs(dataset, outlierMultiplier, false) + } + + return closeRecommendations(cfg, getRecs) +} + +// ThresholdRecommendations returns a recommendations based on whether a value is +// below a given threshold. +func ThresholdRecommendations(cfg *CloseRecommendationConfig, + threshold float64) (*Report, error) { + + getRecs := func(dataset dataset.Dataset) (map[string]Recommendation, error) { + return getThresholdRecs(dataset, threshold, true), nil + } + + return closeRecommendations(cfg, getRecs) +} + +// closeRecommendations returns a report which contains information about the +// channels that were considered and a list of close recommendations. It takes +// a function which can produce the relevant dataset from a set of channel +// insights and a function which can produces recommendations as parameters. +func closeRecommendations(cfg *CloseRecommendationConfig, + getRecommendations func(data dataset.Dataset) ( + map[string]Recommendation, error)) (*Report, error) { + // Check that the minimum wait time is non-zero. if cfg.MinimumMonitored == 0 { return nil, errZeroMinMonitored @@ -104,7 +117,7 @@ func CloseRecommendations(cfg *CloseRecommendationConfig) (*Report, error) { // Produce a dataset containing uptime ratio for channels that have // been monitored for longer than the minimum time. - uptime := getUptimeDataset(filtered) + data := getUptimeDataset(filtered) report := &Report{ TotalChannels: len(channels), @@ -112,18 +125,11 @@ func CloseRecommendations(cfg *CloseRecommendationConfig) (*Report, error) { } // Get close recommendations based on outliers. - report.OutlierRecommendations, err = getOutlierRecs( - uptime, cfg.OutlierMultiplier, false, - ) + report.Recommendations, err = getRecommendations(data) if err != nil { return nil, err } - // Get close recommendations based on threshold. - report.ThresholdRecommendations = getThresholdRecs( - uptime, cfg.UptimeThreshold, true, - ) - return report, nil } diff --git a/recommend/recommend_test.go b/recommend/recommend_test.go index 7cfb67b..ef6dc39 100644 --- a/recommend/recommend_test.go +++ b/recommend/recommend_test.go @@ -19,12 +19,14 @@ func TestCloseRecommendations(t *testing.T) { tests := []struct { name string + upperOutlier bool ChanInsights func() ([]*insights.ChannelInfo, error) MinMonitored time.Duration expectedErr error }{ { - name: "no channels", + name: "no channels", + upperOutlier: false, ChanInsights: func() ([]*insights.ChannelInfo, error) { return nil, nil }, @@ -32,7 +34,8 @@ func TestCloseRecommendations(t *testing.T) { expectedErr: nil, }, { - name: "channel insights fails", + name: "channel insights fails", + upperOutlier: false, ChanInsights: func() ([]*insights.ChannelInfo, error) { return nil, openChanErr }, @@ -40,7 +43,8 @@ func TestCloseRecommendations(t *testing.T) { expectedErr: openChanErr, }, { - name: "zero min monitored", + name: "zero min monitored", + upperOutlier: false, ChanInsights: func() ([]*insights.ChannelInfo, error) { return nil, nil }, @@ -48,7 +52,8 @@ func TestCloseRecommendations(t *testing.T) { expectedErr: errZeroMinMonitored, }, { - name: "enough channels", + name: "enough channels", + upperOutlier: false, ChanInsights: func() ([]*insights.ChannelInfo, error) { return []*insights.ChannelInfo{ { @@ -76,12 +81,23 @@ func TestCloseRecommendations(t *testing.T) { t.Run(test.name, func(t *testing.T) { t.Parallel() - _, err := CloseRecommendations( + recFunc := func(data dataset.Dataset) ( + m map[string]Recommendation, err error) { + + return getOutlierRecs( + data, DefaultOutlierMultiplier, + test.upperOutlier, + ) + } + + _, err := closeRecommendations( &CloseRecommendationConfig{ - ChannelInsights: test.ChanInsights, - OutlierMultiplier: 3, - MinimumMonitored: test.MinMonitored, - }) + ChannelInsights: test.ChanInsights, + MinimumMonitored: test.MinMonitored, + }, + recFunc, + ) + if err != test.expectedErr { t.Fatalf("expected: %v, got: %v", test.expectedErr, err) diff --git a/trmrpc/close_recommendations.go b/trmrpc/close_recommendations.go index 4389268..4977ce1 100644 --- a/trmrpc/close_recommendations.go +++ b/trmrpc/close_recommendations.go @@ -2,52 +2,66 @@ package trmrpc import ( "context" + "sort" "time" "github.com/lightninglabs/terminator/insights" "github.com/lightninglabs/terminator/recommend" ) -// parseRequest parses a rpc close recommendation request and returns the -// close recommendation config that the request requires. -func parseRequest(ctx context.Context, cfg *Config, - req *CloseRecommendationsRequest) *recommend.CloseRecommendationConfig { +// parseRecommendationRequest parses a close recommendation request and +// returns the config required to get recommendations. +func parseRecommendationRequest(ctx context.Context, cfg *Config, + req *CloseRecommendationRequest) *recommend.CloseRecommendationConfig { // Create a close recommendations config with the minimum monitored // value provided in the request and the default outlier multiplier. - recConfig := &recommend.CloseRecommendationConfig{ + return &recommend.CloseRecommendationConfig{ ChannelInsights: func() ([]*insights.ChannelInfo, error) { return channelInsights(ctx, cfg) }, MinimumMonitored: time.Second * time.Duration(req.MinimumMonitored), - OutlierMultiplier: recommend.DefaultOutlierMultiplier, } - - // If a non-zero outlier multiple was provided, set it on the config. - if req.OutlierMultiplier != 0 { - recConfig.OutlierMultiplier = float64(req.OutlierMultiplier) - } - - threshold, ok := req.Threshold.(*CloseRecommendationsRequest_UptimeThreshold) - if ok { - recConfig.UptimeThreshold = float64(threshold.UptimeThreshold) - } - - return recConfig } -// parseResponse parses the response obtained getting a close recommendation +// parseOutlierRequest parses a rpc outlier recommendation request and returns +// the close recommendation config and multiplier required. +func parseOutlierRequest(ctx context.Context, cfg *Config, + req *OutlierRecommendationsRequest) ( + *recommend.CloseRecommendationConfig, float64) { + + multiplier := recommend.DefaultOutlierMultiplier + if req.OutlierMultiplier != 0 { + multiplier = float64(req.OutlierMultiplier) + } + + return parseRecommendationRequest(ctx, cfg, req.RecRequest), multiplier +} + +// parseThresholdRequest parses a rpc threshold recommendation request and +// returns the close recommendation config and threshold required. The above +// threshold boolean is inverted to allow for +// a default that returns values below a threshold. +func parseThresholdRequest(ctx context.Context, cfg *Config, + req *ThresholdRecommendationsRequest) ( + *recommend.CloseRecommendationConfig, float64) { + + return parseRecommendationRequest(ctx, cfg, req.RecRequest), + float64(req.ThresholdValue) +} + +// rpcResponse parses the response obtained getting a close recommendation // and converts it to a close recommendation response. -func parseResponse(report *recommend.Report) *CloseRecommendationsResponse { +func rpcResponse(report *recommend.Report) *CloseRecommendationsResponse { resp := &CloseRecommendationsResponse{ TotalChannels: int32(report.TotalChannels), ConsideredChannels: int32(report.ConsideredChannels), } - for chanPoint, rec := range report.OutlierRecommendations { - resp.OutlierRecommendations = append( - resp.OutlierRecommendations, &Recommendation{ + for chanPoint, rec := range report.Recommendations { + resp.Recommendations = append( + resp.Recommendations, &Recommendation{ ChanPoint: chanPoint, Value: float32(rec.Value), RecommendClose: rec.RecommendClose, @@ -55,15 +69,11 @@ func parseResponse(report *recommend.Report) *CloseRecommendationsResponse { ) } - for chanPoint, rec := range report.ThresholdRecommendations { - resp.ThresholdRecommendations = append( - resp.ThresholdRecommendations, &Recommendation{ - ChanPoint: chanPoint, - Value: float32(rec.Value), - RecommendClose: rec.RecommendClose, - }, - ) - } + // Sort the recommendations returned by value. + sort.SliceStable(resp.Recommendations, func(i, j int) bool { + return resp.Recommendations[i].Value < + resp.Recommendations[j].Value + }) return resp } diff --git a/trmrpc/rpc.pb.go b/trmrpc/rpc.pb.go index 01b170b..911102c 100644 --- a/trmrpc/rpc.pb.go +++ b/trmrpc/rpc.pb.go @@ -23,101 +23,157 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package -type CloseRecommendationsRequest struct { +type CloseRecommendationRequest struct { // //The minimum amount of time in seconds that a channel should have been //monitored by lnd to be eligible for close. This value is in place to //protect against closing of newer channels. - MinimumMonitored int64 `protobuf:"varint,1,opt,name=minimum_monitored,json=minimumMonitored,proto3" json:"minimum_monitored,omitempty"` - // - //The number of inter-quartile ranges a value needs to be beneath the lower - //quartile/ above the upper quartile to be considered a lower/upper outlier. - //Lower values will be more aggressive in recommending channel closes, and - //upper values will be more conservative. Recommended values are 1.5 for - //aggressive recommendations and 3 for conservative recommendations. - OutlierMultiplier float32 `protobuf:"fixed32,2,opt,name=outlier_multiplier,json=outlierMultiplier,proto3" json:"outlier_multiplier,omitempty"` - // - //Threshold contains the threshold value that is used to recommend channels - //for closure. - // - // Types that are valid to be assigned to Threshold: - // *CloseRecommendationsRequest_UptimeThreshold - Threshold isCloseRecommendationsRequest_Threshold `protobuf_oneof:"threshold"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + MinimumMonitored int64 `protobuf:"varint,1,opt,name=minimum_monitored,json=minimumMonitored,proto3" json:"minimum_monitored,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *CloseRecommendationsRequest) Reset() { *m = CloseRecommendationsRequest{} } -func (m *CloseRecommendationsRequest) String() string { return proto.CompactTextString(m) } -func (*CloseRecommendationsRequest) ProtoMessage() {} -func (*CloseRecommendationsRequest) Descriptor() ([]byte, []int) { +func (m *CloseRecommendationRequest) Reset() { *m = CloseRecommendationRequest{} } +func (m *CloseRecommendationRequest) String() string { return proto.CompactTextString(m) } +func (*CloseRecommendationRequest) ProtoMessage() {} +func (*CloseRecommendationRequest) Descriptor() ([]byte, []int) { return fileDescriptor_77a6da22d6a3feb1, []int{0} } -func (m *CloseRecommendationsRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_CloseRecommendationsRequest.Unmarshal(m, b) +func (m *CloseRecommendationRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CloseRecommendationRequest.Unmarshal(m, b) } -func (m *CloseRecommendationsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_CloseRecommendationsRequest.Marshal(b, m, deterministic) +func (m *CloseRecommendationRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CloseRecommendationRequest.Marshal(b, m, deterministic) } -func (m *CloseRecommendationsRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_CloseRecommendationsRequest.Merge(m, src) +func (m *CloseRecommendationRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CloseRecommendationRequest.Merge(m, src) } -func (m *CloseRecommendationsRequest) XXX_Size() int { - return xxx_messageInfo_CloseRecommendationsRequest.Size(m) +func (m *CloseRecommendationRequest) XXX_Size() int { + return xxx_messageInfo_CloseRecommendationRequest.Size(m) } -func (m *CloseRecommendationsRequest) XXX_DiscardUnknown() { - xxx_messageInfo_CloseRecommendationsRequest.DiscardUnknown(m) +func (m *CloseRecommendationRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CloseRecommendationRequest.DiscardUnknown(m) } -var xxx_messageInfo_CloseRecommendationsRequest proto.InternalMessageInfo +var xxx_messageInfo_CloseRecommendationRequest proto.InternalMessageInfo -func (m *CloseRecommendationsRequest) GetMinimumMonitored() int64 { +func (m *CloseRecommendationRequest) GetMinimumMonitored() int64 { if m != nil { return m.MinimumMonitored } return 0 } -func (m *CloseRecommendationsRequest) GetOutlierMultiplier() float32 { +type OutlierRecommendationsRequest struct { + // + //The parameters that are common to all close recommendations. + RecRequest *CloseRecommendationRequest `protobuf:"bytes,1,opt,name=rec_request,json=recRequest,proto3" json:"rec_request,omitempty"` + // + //The number of inter-quartile ranges a value needs to be beneath the lower + //quartile/ above the upper quartile to be considered a lower/upper outlier. + //Lower values will be more aggressive in recommending channel closes, and + //upper values will be more conservative. Recommended values are 1.5 for + //aggressive recommendations and 3 for conservative recommendations. + OutlierMultiplier float32 `protobuf:"fixed32,2,opt,name=outlier_multiplier,json=outlierMultiplier,proto3" json:"outlier_multiplier,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *OutlierRecommendationsRequest) Reset() { *m = OutlierRecommendationsRequest{} } +func (m *OutlierRecommendationsRequest) String() string { return proto.CompactTextString(m) } +func (*OutlierRecommendationsRequest) ProtoMessage() {} +func (*OutlierRecommendationsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_77a6da22d6a3feb1, []int{1} +} + +func (m *OutlierRecommendationsRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_OutlierRecommendationsRequest.Unmarshal(m, b) +} +func (m *OutlierRecommendationsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_OutlierRecommendationsRequest.Marshal(b, m, deterministic) +} +func (m *OutlierRecommendationsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_OutlierRecommendationsRequest.Merge(m, src) +} +func (m *OutlierRecommendationsRequest) XXX_Size() int { + return xxx_messageInfo_OutlierRecommendationsRequest.Size(m) +} +func (m *OutlierRecommendationsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_OutlierRecommendationsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_OutlierRecommendationsRequest proto.InternalMessageInfo + +func (m *OutlierRecommendationsRequest) GetRecRequest() *CloseRecommendationRequest { + if m != nil { + return m.RecRequest + } + return nil +} + +func (m *OutlierRecommendationsRequest) GetOutlierMultiplier() float32 { if m != nil { return m.OutlierMultiplier } return 0 } -type isCloseRecommendationsRequest_Threshold interface { - isCloseRecommendationsRequest_Threshold() +type ThresholdRecommendationsRequest struct { + // + //The parameters that are common to all close recommendations. + RecRequest *CloseRecommendationRequest `protobuf:"bytes,1,opt,name=rec_request,json=recRequest,proto3" json:"rec_request,omitempty"` + // + //The threshold that recommendations will be calculated based on. + //For uptime: ratio of uptime to observed lifetime beneath which channels + //will be recommended for closure. + ThresholdValue float32 `protobuf:"fixed32,2,opt,name=threshold_value,json=thresholdValue,proto3" json:"threshold_value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -type CloseRecommendationsRequest_UptimeThreshold struct { - UptimeThreshold float32 `protobuf:"fixed32,3,opt,name=uptime_threshold,json=uptimeThreshold,proto3,oneof"` +func (m *ThresholdRecommendationsRequest) Reset() { *m = ThresholdRecommendationsRequest{} } +func (m *ThresholdRecommendationsRequest) String() string { return proto.CompactTextString(m) } +func (*ThresholdRecommendationsRequest) ProtoMessage() {} +func (*ThresholdRecommendationsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_77a6da22d6a3feb1, []int{2} } -func (*CloseRecommendationsRequest_UptimeThreshold) isCloseRecommendationsRequest_Threshold() {} +func (m *ThresholdRecommendationsRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ThresholdRecommendationsRequest.Unmarshal(m, b) +} +func (m *ThresholdRecommendationsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ThresholdRecommendationsRequest.Marshal(b, m, deterministic) +} +func (m *ThresholdRecommendationsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ThresholdRecommendationsRequest.Merge(m, src) +} +func (m *ThresholdRecommendationsRequest) XXX_Size() int { + return xxx_messageInfo_ThresholdRecommendationsRequest.Size(m) +} +func (m *ThresholdRecommendationsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ThresholdRecommendationsRequest.DiscardUnknown(m) +} -func (m *CloseRecommendationsRequest) GetThreshold() isCloseRecommendationsRequest_Threshold { +var xxx_messageInfo_ThresholdRecommendationsRequest proto.InternalMessageInfo + +func (m *ThresholdRecommendationsRequest) GetRecRequest() *CloseRecommendationRequest { if m != nil { - return m.Threshold + return m.RecRequest } return nil } -func (m *CloseRecommendationsRequest) GetUptimeThreshold() float32 { - if x, ok := m.GetThreshold().(*CloseRecommendationsRequest_UptimeThreshold); ok { - return x.UptimeThreshold +func (m *ThresholdRecommendationsRequest) GetThresholdValue() float32 { + if m != nil { + return m.ThresholdValue } return 0 } -// XXX_OneofWrappers is for the internal use of the proto package. -func (*CloseRecommendationsRequest) XXX_OneofWrappers() []interface{} { - return []interface{}{ - (*CloseRecommendationsRequest_UptimeThreshold)(nil), - } -} - type CloseRecommendationsResponse struct { // //The total number of channels, before filtering out channels that are @@ -127,29 +183,21 @@ type CloseRecommendationsResponse struct { //The number of channels that were considered for close recommendations. ConsideredChannels int32 `protobuf:"varint,2,opt,name=considered_channels,json=consideredChannels,proto3" json:"considered_channels,omitempty"` // - //A map of channels to close recommendations, based out whether they are - //outliers in the uptime dataset. The absence of a channel in this set - //implies that it was not considered for close because it did not meet - //the criteria for close (it is private, or has not been monitored for - //long enough to make a decision). - OutlierRecommendations []*Recommendation `protobuf:"bytes,3,rep,name=outlier_recommendations,json=outlierRecommendations,proto3" json:"outlier_recommendations,omitempty"` - // - //A set of channel close recommendations, based out whether they are - //beneath the threshold provided in the request. The absence of a channel - //in this set implies that it was not considered for close because it - //did not meet the criteria for close (it is private, or has not been - //monitored for long enough to make a decision). - ThresholdRecommendations []*Recommendation `protobuf:"bytes,4,rep,name=threshold_recommendations,json=thresholdRecommendations,proto3" json:"threshold_recommendations,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + //A set of channel close recommendations. The absence of a channel in this + //set implies that it was not considered for close because it did not meet + //the criteria for close recommendations (it is private, or has not been + //monitored for long enough). + Recommendations []*Recommendation `protobuf:"bytes,3,rep,name=recommendations,proto3" json:"recommendations,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *CloseRecommendationsResponse) Reset() { *m = CloseRecommendationsResponse{} } func (m *CloseRecommendationsResponse) String() string { return proto.CompactTextString(m) } func (*CloseRecommendationsResponse) ProtoMessage() {} func (*CloseRecommendationsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{1} + return fileDescriptor_77a6da22d6a3feb1, []int{3} } func (m *CloseRecommendationsResponse) XXX_Unmarshal(b []byte) error { @@ -184,16 +232,9 @@ func (m *CloseRecommendationsResponse) GetConsideredChannels() int32 { return 0 } -func (m *CloseRecommendationsResponse) GetOutlierRecommendations() []*Recommendation { +func (m *CloseRecommendationsResponse) GetRecommendations() []*Recommendation { if m != nil { - return m.OutlierRecommendations - } - return nil -} - -func (m *CloseRecommendationsResponse) GetThresholdRecommendations() []*Recommendation { - if m != nil { - return m.ThresholdRecommendations + return m.Recommendations } return nil } @@ -216,7 +257,7 @@ func (m *Recommendation) Reset() { *m = Recommendation{} } func (m *Recommendation) String() string { return proto.CompactTextString(m) } func (*Recommendation) ProtoMessage() {} func (*Recommendation) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{2} + return fileDescriptor_77a6da22d6a3feb1, []int{4} } func (m *Recommendation) XXX_Unmarshal(b []byte) error { @@ -282,7 +323,7 @@ func (m *RevenueReportRequest) Reset() { *m = RevenueReportRequest{} } func (m *RevenueReportRequest) String() string { return proto.CompactTextString(m) } func (*RevenueReportRequest) ProtoMessage() {} func (*RevenueReportRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{3} + return fileDescriptor_77a6da22d6a3feb1, []int{5} } func (m *RevenueReportRequest) XXX_Unmarshal(b []byte) error { @@ -338,7 +379,7 @@ func (m *RevenueReportResponse) Reset() { *m = RevenueReportResponse{} } func (m *RevenueReportResponse) String() string { return proto.CompactTextString(m) } func (*RevenueReportResponse) ProtoMessage() {} func (*RevenueReportResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{4} + return fileDescriptor_77a6da22d6a3feb1, []int{6} } func (m *RevenueReportResponse) XXX_Unmarshal(b []byte) error { @@ -386,7 +427,7 @@ func (m *RevenueReport) Reset() { *m = RevenueReport{} } func (m *RevenueReport) String() string { return proto.CompactTextString(m) } func (*RevenueReport) ProtoMessage() {} func (*RevenueReport) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{5} + return fileDescriptor_77a6da22d6a3feb1, []int{7} } func (m *RevenueReport) XXX_Unmarshal(b []byte) error { @@ -449,7 +490,7 @@ func (m *PairReport) Reset() { *m = PairReport{} } func (m *PairReport) String() string { return proto.CompactTextString(m) } func (*PairReport) ProtoMessage() {} func (*PairReport) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{6} + return fileDescriptor_77a6da22d6a3feb1, []int{8} } func (m *PairReport) XXX_Unmarshal(b []byte) error { @@ -508,7 +549,7 @@ func (m *ChannelInsightsRequest) Reset() { *m = ChannelInsightsRequest{} func (m *ChannelInsightsRequest) String() string { return proto.CompactTextString(m) } func (*ChannelInsightsRequest) ProtoMessage() {} func (*ChannelInsightsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{7} + return fileDescriptor_77a6da22d6a3feb1, []int{9} } func (m *ChannelInsightsRequest) XXX_Unmarshal(b []byte) error { @@ -541,7 +582,7 @@ func (m *ChannelInsightsResponse) Reset() { *m = ChannelInsightsResponse func (m *ChannelInsightsResponse) String() string { return proto.CompactTextString(m) } func (*ChannelInsightsResponse) ProtoMessage() {} func (*ChannelInsightsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{8} + return fileDescriptor_77a6da22d6a3feb1, []int{10} } func (m *ChannelInsightsResponse) XXX_Unmarshal(b []byte) error { @@ -606,7 +647,7 @@ func (m *ChannelInsight) Reset() { *m = ChannelInsight{} } func (m *ChannelInsight) String() string { return proto.CompactTextString(m) } func (*ChannelInsight) ProtoMessage() {} func (*ChannelInsight) Descriptor() ([]byte, []int) { - return fileDescriptor_77a6da22d6a3feb1, []int{9} + return fileDescriptor_77a6da22d6a3feb1, []int{11} } func (m *ChannelInsight) XXX_Unmarshal(b []byte) error { @@ -684,7 +725,9 @@ func (m *ChannelInsight) GetPrivate() bool { } func init() { - proto.RegisterType((*CloseRecommendationsRequest)(nil), "trmrpc.CloseRecommendationsRequest") + proto.RegisterType((*CloseRecommendationRequest)(nil), "trmrpc.CloseRecommendationRequest") + proto.RegisterType((*OutlierRecommendationsRequest)(nil), "trmrpc.OutlierRecommendationsRequest") + proto.RegisterType((*ThresholdRecommendationsRequest)(nil), "trmrpc.ThresholdRecommendationsRequest") proto.RegisterType((*CloseRecommendationsResponse)(nil), "trmrpc.CloseRecommendationsResponse") proto.RegisterType((*Recommendation)(nil), "trmrpc.Recommendation") proto.RegisterType((*RevenueReportRequest)(nil), "trmrpc.RevenueReportRequest") @@ -700,58 +743,60 @@ func init() { func init() { proto.RegisterFile("rpc.proto", fileDescriptor_77a6da22d6a3feb1) } var fileDescriptor_77a6da22d6a3feb1 = []byte{ - // 814 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x55, 0xcd, 0x6e, 0xdb, 0x46, - 0x10, 0xae, 0x28, 0xff, 0x69, 0x54, 0xc9, 0xca, 0x56, 0x71, 0x14, 0xd7, 0x69, 0x0c, 0x36, 0x6d, - 0x05, 0xa4, 0xb5, 0x03, 0xf7, 0x52, 0xf4, 0xd6, 0x06, 0x01, 0x62, 0xa0, 0x46, 0x82, 0xb5, 0x8f, - 0x05, 0x88, 0x2d, 0x35, 0x91, 0x17, 0xe5, 0xee, 0x32, 0xbb, 0x4b, 0x01, 0x79, 0x92, 0x5e, 0xfa, - 0x16, 0x7d, 0x8d, 0x5e, 0x7a, 0xe8, 0xfb, 0x14, 0xfb, 0x43, 0x52, 0x54, 0x64, 0xf7, 0x46, 0x7e, - 0xdf, 0xb7, 0x33, 0xc3, 0x99, 0x6f, 0x87, 0x30, 0xd0, 0x65, 0x7e, 0x56, 0x6a, 0x65, 0x15, 0xd9, - 0xb3, 0x5a, 0xe8, 0x32, 0x3f, 0x3e, 0x59, 0x2a, 0xb5, 0x2c, 0xf0, 0x9c, 0x95, 0xfc, 0x9c, 0x49, - 0xa9, 0x2c, 0xb3, 0x5c, 0x49, 0x13, 0x54, 0xe9, 0x5f, 0x3d, 0xf8, 0xfc, 0x65, 0xa1, 0x0c, 0x52, - 0xcc, 0x95, 0x10, 0x28, 0x17, 0x81, 0xa6, 0xf8, 0xbe, 0x42, 0x63, 0xc9, 0x73, 0x78, 0x20, 0xb8, - 0xe4, 0xa2, 0x12, 0x99, 0x50, 0x92, 0x5b, 0xa5, 0x71, 0x31, 0xeb, 0x9d, 0xf6, 0xe6, 0x7d, 0x3a, - 0x89, 0xc4, 0x55, 0x8d, 0x93, 0xef, 0x80, 0xa8, 0xca, 0x16, 0x1c, 0x75, 0x26, 0xaa, 0xc2, 0xf2, - 0xd2, 0x3d, 0xce, 0x92, 0xd3, 0xde, 0x3c, 0xa1, 0x0f, 0x22, 0x73, 0xd5, 0x10, 0xe4, 0x39, 0x4c, - 0xaa, 0xd2, 0x72, 0x81, 0x99, 0xbd, 0xd5, 0x68, 0x6e, 0x55, 0xb1, 0x98, 0xf5, 0x9d, 0xf8, 0xf5, - 0x27, 0xf4, 0x30, 0x30, 0x37, 0x35, 0xf1, 0xf3, 0x10, 0x06, 0x8d, 0x2a, 0xfd, 0x33, 0x81, 0x93, - 0xed, 0x55, 0x9b, 0x52, 0x49, 0x83, 0xe4, 0x2b, 0x18, 0x5b, 0x65, 0x59, 0x91, 0xe5, 0xb7, 0x4c, - 0x4a, 0x2c, 0x8c, 0xaf, 0x79, 0x97, 0x8e, 0x3c, 0xfa, 0x32, 0x82, 0xe4, 0x1c, 0x3e, 0xcb, 0x95, - 0x34, 0x7c, 0x81, 0x1a, 0x17, 0xad, 0x36, 0xf1, 0x5a, 0xd2, 0x52, 0xcd, 0x81, 0x37, 0xf0, 0xa8, - 0xfe, 0x42, 0xdd, 0x4d, 0x3d, 0xeb, 0x9f, 0xf6, 0xe7, 0xc3, 0x8b, 0xa3, 0xb3, 0xd0, 0xf6, 0xb3, - 0x6e, 0x65, 0xf4, 0x28, 0x1e, 0xdb, 0x28, 0x98, 0x5c, 0xc3, 0xe3, 0xe6, 0xb3, 0x3e, 0x0a, 0xb9, - 0x73, 0x6f, 0xc8, 0x59, 0x73, 0x70, 0x23, 0x68, 0x2a, 0x61, 0xdc, 0x85, 0xc8, 0x13, 0x00, 0xf7, - 0x75, 0x59, 0xa9, 0xb8, 0xb4, 0xbe, 0x17, 0x03, 0x3a, 0x70, 0xc8, 0x5b, 0x07, 0x90, 0x29, 0xec, - 0xae, 0x58, 0x51, 0x61, 0x9c, 0x55, 0x78, 0x21, 0xdf, 0xc0, 0x61, 0x53, 0x51, 0x96, 0xbb, 0x76, - 0xfb, 0xf1, 0x1c, 0xd0, 0x71, 0x03, 0xfb, 0x21, 0xa4, 0xef, 0x61, 0x4a, 0x71, 0x85, 0xb2, 0x42, - 0x8a, 0xa5, 0xd2, 0xb6, 0x36, 0xcf, 0x53, 0x18, 0xb6, 0x59, 0xdd, 0x08, 0xfa, 0xf3, 0x01, 0x85, - 0x26, 0xad, 0x71, 0x65, 0x19, 0xcb, 0xb4, 0xcd, 0xdc, 0xac, 0x7d, 0xf2, 0x1d, 0x3a, 0xf0, 0xc8, - 0x0d, 0x17, 0x48, 0x1e, 0xc3, 0x81, 0x4b, 0xed, 0xc9, 0xbe, 0x27, 0xf7, 0x51, 0x2e, 0x1c, 0x95, - 0xbe, 0x86, 0x87, 0x1b, 0x29, 0xe3, 0xe4, 0xcf, 0x61, 0x5f, 0x7b, 0x24, 0xe4, 0x1b, 0x5e, 0x3c, - 0x6c, 0xdb, 0xb7, 0xae, 0xaf, 0x55, 0xe9, 0xbf, 0x3d, 0x18, 0x75, 0x28, 0x6f, 0x1e, 0xa6, 0x97, - 0x68, 0x6b, 0x47, 0xc4, 0x86, 0x8d, 0x02, 0x1a, 0xcd, 0x40, 0x2e, 0xe1, 0xd3, 0x92, 0x71, 0x67, - 0x84, 0x90, 0x2e, 0xf1, 0xe9, 0xbe, 0xde, 0x9a, 0xee, 0xec, 0x2d, 0xe3, 0x3a, 0x3c, 0x9a, 0x57, - 0xd2, 0xea, 0x0f, 0x74, 0x58, 0xb6, 0xc8, 0x31, 0x85, 0xc9, 0xa6, 0x80, 0x4c, 0xa0, 0xff, 0x3b, - 0x7e, 0x88, 0xa9, 0xdd, 0x23, 0x99, 0xaf, 0x4f, 0x69, 0x78, 0x41, 0xea, 0x4c, 0xed, 0xd1, 0x38, - 0xb9, 0x1f, 0x93, 0x1f, 0x7a, 0xe9, 0xdf, 0x3d, 0x80, 0x96, 0x21, 0x2f, 0x60, 0xca, 0x84, 0xaa, - 0xa4, 0xcd, 0x54, 0x65, 0x97, 0x8a, 0xcb, 0x65, 0x26, 0x0c, 0xb3, 0xf1, 0x2e, 0x93, 0xc0, 0xbd, - 0x89, 0xd4, 0x95, 0x61, 0x96, 0x7c, 0x0b, 0xe4, 0x1d, 0xa2, 0xd9, 0xd0, 0x27, 0xe1, 0xee, 0x3b, - 0xa6, 0xa3, 0x6e, 0xe3, 0x73, 0x99, 0x2b, 0xd1, 0xe8, 0xfb, 0xeb, 0xf1, 0x2f, 0x23, 0xd5, 0x89, - 0xdf, 0xd5, 0xef, 0xb4, 0xf1, 0xd7, 0xd5, 0xe9, 0x0c, 0x8e, 0x62, 0xe3, 0x2f, 0xa5, 0xe1, 0xcb, - 0x5b, 0x5b, 0xaf, 0xa8, 0xf4, 0x57, 0x78, 0xf4, 0x11, 0x13, 0xcd, 0xf0, 0x13, 0x4c, 0xe2, 0x08, - 0x33, 0x1e, 0xb9, 0xe8, 0x8a, 0xe6, 0x52, 0x75, 0x8f, 0xd2, 0xc3, 0xbc, 0x1b, 0x2a, 0xfd, 0x27, - 0x81, 0x71, 0x57, 0xf3, 0x7f, 0x97, 0xc9, 0xad, 0xcc, 0x7a, 0x25, 0x66, 0x06, 0x73, 0x25, 0x17, - 0x26, 0x7a, 0x7b, 0xd2, 0x10, 0xd7, 0x01, 0x77, 0x5e, 0x8b, 0x3b, 0xb0, 0x56, 0x06, 0xa3, 0x8f, - 0x02, 0x5a, 0xcb, 0x5e, 0xc0, 0x74, 0xa5, 0x8a, 0x4a, 0xe0, 0xd6, 0x6e, 0x91, 0xc0, 0x75, 0xba, - 0xdb, 0x9e, 0xe8, 0xce, 0x6f, 0x77, 0xfd, 0x44, 0x67, 0x82, 0x73, 0xf0, 0x5d, 0xcf, 0x90, 0x69, - 0x89, 0x8b, 0xa0, 0xde, 0xf3, 0xea, 0xb1, 0xc3, 0x5f, 0x79, 0xd8, 0x2b, 0x9f, 0xc1, 0x28, 0x57, - 0xf2, 0x1d, 0xd7, 0x22, 0x2e, 0xaa, 0xfd, 0xd3, 0xde, 0x7c, 0x44, 0xbb, 0x20, 0x99, 0xc1, 0x7e, - 0xa9, 0xf9, 0x8a, 0x59, 0x9c, 0x1d, 0xf8, 0xb5, 0x51, 0xbf, 0x5e, 0xfc, 0x91, 0xc0, 0xe4, 0x06, - 0xb5, 0xe0, 0x92, 0x59, 0xa5, 0xaf, 0x51, 0xaf, 0x50, 0x13, 0x06, 0xd3, 0x6d, 0x2b, 0x9d, 0x7c, - 0xd9, 0x4c, 0xea, 0xee, 0xdf, 0xd4, 0xf1, 0xb3, 0xfb, 0x45, 0xd1, 0x0e, 0xbf, 0x6c, 0xde, 0xf4, - 0x93, 0xed, 0xbb, 0x21, 0x06, 0x7d, 0x72, 0x07, 0x1b, 0xa3, 0x51, 0x38, 0xdc, 0xf0, 0x1d, 0xf9, - 0x62, 0xbb, 0xab, 0x9a, 0x32, 0x9f, 0xde, 0xc9, 0x87, 0x98, 0xbf, 0xed, 0xf9, 0xbf, 0xf2, 0xf7, - 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0x9d, 0x67, 0xb7, 0xb2, 0xc8, 0x07, 0x00, 0x00, + // 841 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcd, 0x6e, 0xdb, 0x46, + 0x10, 0x06, 0xc5, 0xf8, 0x47, 0xa3, 0x4a, 0x56, 0xb6, 0x8e, 0xab, 0x1a, 0x76, 0x2d, 0x10, 0x49, + 0x23, 0xa0, 0xad, 0x1d, 0xa8, 0x97, 0xa2, 0xa7, 0x16, 0x46, 0x80, 0x1a, 0xa8, 0x91, 0x60, 0x63, + 0xf4, 0x54, 0x80, 0x60, 0xa9, 0x89, 0xbc, 0x28, 0xb9, 0xcb, 0xec, 0x2e, 0x05, 0xe4, 0x05, 0x7a, + 0xed, 0xa1, 0x2f, 0xd3, 0x87, 0xe8, 0xa5, 0x87, 0xbe, 0x4f, 0xb1, 0x7f, 0xa4, 0xa8, 0x4a, 0x49, + 0x2f, 0xb9, 0x91, 0xdf, 0xf7, 0xed, 0x7c, 0xa3, 0x99, 0xe1, 0xac, 0xa0, 0x2f, 0xab, 0xfc, 0xb2, + 0x92, 0x42, 0x0b, 0xb2, 0xaf, 0x65, 0x29, 0xab, 0xfc, 0xf4, 0x6c, 0x29, 0xc4, 0xb2, 0xc0, 0xab, + 0xac, 0x62, 0x57, 0x19, 0xe7, 0x42, 0x67, 0x9a, 0x09, 0xae, 0x9c, 0x2a, 0xb9, 0x81, 0xd3, 0xeb, + 0x42, 0x28, 0xa4, 0x98, 0x8b, 0xb2, 0x44, 0xbe, 0xb0, 0x2c, 0xc5, 0x37, 0x35, 0x2a, 0x4d, 0xbe, + 0x80, 0x87, 0x25, 0xe3, 0xac, 0xac, 0xcb, 0xb4, 0x14, 0x9c, 0x69, 0x21, 0x71, 0x31, 0x89, 0xa6, + 0xd1, 0x2c, 0xa6, 0x63, 0x4f, 0xdc, 0x06, 0x3c, 0xf9, 0x23, 0x82, 0xf3, 0x17, 0xb5, 0x2e, 0x18, + 0xca, 0x6e, 0x34, 0x15, 0xc2, 0x5d, 0xc3, 0x40, 0x62, 0x9e, 0x4a, 0xf7, 0x6a, 0x03, 0x0d, 0xe6, + 0xc9, 0xa5, 0x4b, 0xf4, 0x72, 0x77, 0x1e, 0x14, 0x24, 0xe6, 0x21, 0xc8, 0x57, 0x40, 0x84, 0x73, + 0x49, 0xcb, 0xba, 0xd0, 0xac, 0x32, 0x8f, 0x93, 0xde, 0x34, 0x9a, 0xf5, 0xe8, 0x43, 0xcf, 0xdc, + 0x36, 0x44, 0xf2, 0x7b, 0x04, 0x17, 0x77, 0xf7, 0x12, 0xd5, 0xbd, 0x28, 0x16, 0x1f, 0x32, 0xaf, + 0xa7, 0x70, 0xa4, 0x83, 0x4f, 0xba, 0xca, 0x8a, 0x1a, 0x7d, 0x52, 0xa3, 0x06, 0xfe, 0xc9, 0xa0, + 0xc9, 0x9f, 0x11, 0x9c, 0x6d, 0x89, 0xa9, 0x28, 0xaa, 0x4a, 0x70, 0x85, 0xe4, 0x09, 0x8c, 0xb4, + 0xd0, 0x59, 0x91, 0xe6, 0xf7, 0x19, 0xe7, 0x58, 0x28, 0x9b, 0xd1, 0x1e, 0x1d, 0x5a, 0xf4, 0xda, + 0x83, 0xe4, 0x0a, 0x3e, 0xce, 0x05, 0x57, 0x6c, 0x81, 0x12, 0x17, 0xad, 0xb6, 0x67, 0xb5, 0xa4, + 0xa5, 0x9a, 0x03, 0xdf, 0xc1, 0x91, 0xec, 0x5a, 0x4e, 0xe2, 0x69, 0x3c, 0x1b, 0xcc, 0x4f, 0xc2, + 0x4f, 0xdd, 0xf8, 0x95, 0x9b, 0xf2, 0x84, 0xc3, 0xa8, 0x2b, 0x21, 0xe7, 0x00, 0xc6, 0x39, 0xad, + 0x04, 0xe3, 0xae, 0x72, 0x7d, 0xda, 0x37, 0xc8, 0x4b, 0x03, 0x90, 0x63, 0xd8, 0x5b, 0x2f, 0x85, + 0x7b, 0x31, 0xa5, 0x6a, 0x22, 0xa7, 0xb9, 0x29, 0xc5, 0x24, 0x9e, 0x46, 0xb3, 0x43, 0x3a, 0x6a, + 0x60, 0x5b, 0xa0, 0xe4, 0x0d, 0x1c, 0x53, 0x5c, 0x21, 0xaf, 0x91, 0x62, 0x25, 0xa4, 0x0e, 0xb5, + 0xbe, 0x80, 0x41, 0xeb, 0x6a, 0xca, 0x13, 0xcf, 0xfa, 0x14, 0x1a, 0x5b, 0x65, 0xd2, 0x52, 0x3a, + 0x93, 0x3a, 0xd5, 0xac, 0x74, 0xe6, 0x0f, 0x68, 0xdf, 0x22, 0x77, 0xac, 0x44, 0xf2, 0x29, 0x1c, + 0x1a, 0x6b, 0x4b, 0xc6, 0x96, 0x3c, 0x40, 0xbe, 0x30, 0x54, 0xf2, 0x03, 0x3c, 0xda, 0xb0, 0xf4, + 0x5d, 0xb9, 0x82, 0x03, 0x69, 0x11, 0xe7, 0x37, 0x98, 0x3f, 0x6a, 0xab, 0xb6, 0xae, 0x0f, 0xaa, + 0xe4, 0x9f, 0x08, 0x86, 0x1d, 0xca, 0x36, 0x36, 0x93, 0x4b, 0xd4, 0xa1, 0x5b, 0xbe, 0x60, 0x43, + 0x87, 0xfa, 0x46, 0x91, 0x1b, 0xf8, 0xa8, 0xca, 0x98, 0x4c, 0x83, 0x5d, 0xcf, 0xda, 0x7d, 0xbe, + 0xd5, 0xee, 0xf2, 0x65, 0xc6, 0xa4, 0x7b, 0x54, 0xcf, 0xb9, 0x96, 0x6f, 0xe9, 0xa0, 0x6a, 0x91, + 0x53, 0x0a, 0xe3, 0x4d, 0x01, 0x19, 0x43, 0xfc, 0x2b, 0xbe, 0xf5, 0xd6, 0xe6, 0x91, 0xcc, 0xd6, + 0xbb, 0x34, 0x98, 0x93, 0xe0, 0xd4, 0x1e, 0xf5, 0x9d, 0xfb, 0xb6, 0xf7, 0x4d, 0x94, 0xfc, 0x15, + 0x01, 0xb4, 0x0c, 0x79, 0x06, 0xc7, 0x59, 0x29, 0x6a, 0xae, 0x53, 0x51, 0xeb, 0xa5, 0x60, 0x7c, + 0x99, 0x96, 0x2a, 0xd3, 0x7e, 0x4d, 0x10, 0xc7, 0xbd, 0xf0, 0xd4, 0xad, 0xca, 0x34, 0xf9, 0x12, + 0xc8, 0x6b, 0x44, 0xb5, 0xa1, 0xef, 0xb9, 0xb5, 0x62, 0x98, 0x8e, 0xba, 0x8d, 0xcf, 0x78, 0x2e, + 0xca, 0x46, 0x1f, 0xaf, 0xc7, 0xbf, 0xf1, 0x54, 0x27, 0x7e, 0x57, 0xff, 0xa0, 0x8d, 0xbf, 0xae, + 0x4e, 0x26, 0x70, 0xe2, 0x0b, 0x7f, 0xc3, 0x15, 0x5b, 0xde, 0xeb, 0xb0, 0x16, 0x92, 0x9f, 0xe1, + 0x93, 0xff, 0x30, 0x7e, 0x18, 0xbe, 0x87, 0xb1, 0x6f, 0x61, 0xca, 0x3c, 0xe7, 0xa7, 0xa2, 0xf9, + 0x96, 0xba, 0x47, 0xe9, 0x51, 0xde, 0x0d, 0x95, 0xfc, 0xdd, 0x83, 0x51, 0x57, 0xf3, 0xbe, 0x8f, + 0xc9, 0x6c, 0xe3, 0xb0, 0x6d, 0x53, 0x85, 0xb9, 0xe0, 0x0b, 0xe5, 0x67, 0x7b, 0xdc, 0x10, 0xaf, + 0x1c, 0x6e, 0x66, 0xad, 0xae, 0xcc, 0x80, 0x37, 0x4a, 0x37, 0xe8, 0x43, 0x87, 0x06, 0xd9, 0x33, + 0x38, 0x5e, 0x89, 0xa2, 0x2e, 0x71, 0x6b, 0xb5, 0x88, 0xe3, 0x3a, 0xd5, 0x6d, 0x4f, 0x74, 0xfb, + 0xb7, 0xb7, 0x7e, 0xa2, 0xd3, 0xc1, 0x19, 0xd8, 0xaa, 0xa7, 0x98, 0x49, 0x8e, 0x0b, 0xa7, 0xde, + 0xb7, 0xea, 0x91, 0xc1, 0x9f, 0x5b, 0xd8, 0x2a, 0x1f, 0xc3, 0x30, 0x17, 0xfc, 0x35, 0x93, 0xa5, + 0xdf, 0x4f, 0x07, 0xd3, 0x68, 0x36, 0xa4, 0x5d, 0x90, 0x4c, 0xe0, 0xa0, 0x92, 0x6c, 0x95, 0x69, + 0x9c, 0x1c, 0xda, 0xb5, 0x11, 0x5e, 0xe7, 0xbf, 0xc5, 0x30, 0xbe, 0x43, 0x59, 0x32, 0x9e, 0x69, + 0x21, 0x5f, 0xa1, 0x5c, 0xa1, 0x24, 0x08, 0x27, 0xdb, 0xaf, 0x25, 0xf2, 0x24, 0xf4, 0xea, 0x9d, + 0xd7, 0xd6, 0xe9, 0xe3, 0x77, 0xdc, 0x04, 0xed, 0x48, 0x30, 0x98, 0xec, 0xba, 0x67, 0xc8, 0xd3, + 0x10, 0xe1, 0x3d, 0x37, 0xd1, 0xff, 0xb4, 0xfa, 0x71, 0x73, 0xb1, 0x9c, 0x6d, 0x5f, 0x45, 0x3e, + 0xe8, 0xf9, 0x0e, 0xd6, 0x47, 0xa3, 0x70, 0xb4, 0x31, 0xe6, 0xe4, 0xb3, 0xed, 0x43, 0xdc, 0xa4, + 0x79, 0xb1, 0x93, 0x77, 0x31, 0x7f, 0xd9, 0xb7, 0xff, 0x2e, 0xbe, 0xfe, 0x37, 0x00, 0x00, 0xff, + 0xff, 0xce, 0x57, 0xd9, 0x5b, 0x90, 0x08, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -766,7 +811,8 @@ const _ = grpc.SupportPackageIsVersion4 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type TerminatorServerClient interface { - CloseRecommendations(ctx context.Context, in *CloseRecommendationsRequest, opts ...grpc.CallOption) (*CloseRecommendationsResponse, error) + OutlierRecommendations(ctx context.Context, in *OutlierRecommendationsRequest, opts ...grpc.CallOption) (*CloseRecommendationsResponse, error) + ThresholdRecommendations(ctx context.Context, in *ThresholdRecommendationsRequest, opts ...grpc.CallOption) (*CloseRecommendationsResponse, error) RevenueReport(ctx context.Context, in *RevenueReportRequest, opts ...grpc.CallOption) (*RevenueReportResponse, error) ChannelInsights(ctx context.Context, in *ChannelInsightsRequest, opts ...grpc.CallOption) (*ChannelInsightsResponse, error) } @@ -779,9 +825,18 @@ func NewTerminatorServerClient(cc *grpc.ClientConn) TerminatorServerClient { return &terminatorServerClient{cc} } -func (c *terminatorServerClient) CloseRecommendations(ctx context.Context, in *CloseRecommendationsRequest, opts ...grpc.CallOption) (*CloseRecommendationsResponse, error) { +func (c *terminatorServerClient) OutlierRecommendations(ctx context.Context, in *OutlierRecommendationsRequest, opts ...grpc.CallOption) (*CloseRecommendationsResponse, error) { out := new(CloseRecommendationsResponse) - err := c.cc.Invoke(ctx, "/trmrpc.TerminatorServer/CloseRecommendations", in, out, opts...) + err := c.cc.Invoke(ctx, "/trmrpc.TerminatorServer/OutlierRecommendations", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *terminatorServerClient) ThresholdRecommendations(ctx context.Context, in *ThresholdRecommendationsRequest, opts ...grpc.CallOption) (*CloseRecommendationsResponse, error) { + out := new(CloseRecommendationsResponse) + err := c.cc.Invoke(ctx, "/trmrpc.TerminatorServer/ThresholdRecommendations", in, out, opts...) if err != nil { return nil, err } @@ -808,7 +863,8 @@ func (c *terminatorServerClient) ChannelInsights(ctx context.Context, in *Channe // TerminatorServerServer is the server API for TerminatorServer service. type TerminatorServerServer interface { - CloseRecommendations(context.Context, *CloseRecommendationsRequest) (*CloseRecommendationsResponse, error) + OutlierRecommendations(context.Context, *OutlierRecommendationsRequest) (*CloseRecommendationsResponse, error) + ThresholdRecommendations(context.Context, *ThresholdRecommendationsRequest) (*CloseRecommendationsResponse, error) RevenueReport(context.Context, *RevenueReportRequest) (*RevenueReportResponse, error) ChannelInsights(context.Context, *ChannelInsightsRequest) (*ChannelInsightsResponse, error) } @@ -817,20 +873,38 @@ func RegisterTerminatorServerServer(s *grpc.Server, srv TerminatorServerServer) s.RegisterService(&_TerminatorServer_serviceDesc, srv) } -func _TerminatorServer_CloseRecommendations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CloseRecommendationsRequest) +func _TerminatorServer_OutlierRecommendations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(OutlierRecommendationsRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(TerminatorServerServer).CloseRecommendations(ctx, in) + return srv.(TerminatorServerServer).OutlierRecommendations(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/trmrpc.TerminatorServer/CloseRecommendations", + FullMethod: "/trmrpc.TerminatorServer/OutlierRecommendations", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(TerminatorServerServer).CloseRecommendations(ctx, req.(*CloseRecommendationsRequest)) + return srv.(TerminatorServerServer).OutlierRecommendations(ctx, req.(*OutlierRecommendationsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TerminatorServer_ThresholdRecommendations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ThresholdRecommendationsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TerminatorServerServer).ThresholdRecommendations(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/trmrpc.TerminatorServer/ThresholdRecommendations", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TerminatorServerServer).ThresholdRecommendations(ctx, req.(*ThresholdRecommendationsRequest)) } return interceptor(ctx, in, info, handler) } @@ -876,8 +950,12 @@ var _TerminatorServer_serviceDesc = grpc.ServiceDesc{ HandlerType: (*TerminatorServerServer)(nil), Methods: []grpc.MethodDesc{ { - MethodName: "CloseRecommendations", - Handler: _TerminatorServer_CloseRecommendations_Handler, + MethodName: "OutlierRecommendations", + Handler: _TerminatorServer_OutlierRecommendations_Handler, + }, + { + MethodName: "ThresholdRecommendations", + Handler: _TerminatorServer_ThresholdRecommendations_Handler, }, { MethodName: "RevenueReport", diff --git a/trmrpc/rpc.proto b/trmrpc/rpc.proto index 363f986..3220ae5 100644 --- a/trmrpc/rpc.proto +++ b/trmrpc/rpc.proto @@ -5,18 +5,26 @@ import "google/api/annotations.proto"; package trmrpc; service TerminatorServer { - rpc CloseRecommendations (CloseRecommendationsRequest) returns (CloseRecommendationsResponse); + rpc OutlierRecommendations (OutlierRecommendationsRequest) returns (CloseRecommendationsResponse); + rpc ThresholdRecommendations (ThresholdRecommendationsRequest) returns (CloseRecommendationsResponse); rpc RevenueReport (RevenueReportRequest) returns (RevenueReportResponse); rpc ChannelInsights (ChannelInsightsRequest) returns (ChannelInsightsResponse); } -message CloseRecommendationsRequest { +message CloseRecommendationRequest { /* The minimum amount of time in seconds that a channel should have been monitored by lnd to be eligible for close. This value is in place to protect against closing of newer channels. */ int64 minimum_monitored = 1; +} + +message OutlierRecommendationsRequest { + /* + The parameters that are common to all close recommendations. + */ + CloseRecommendationRequest rec_request = 1; /* The number of inter-quartile ranges a value needs to be beneath the lower @@ -26,21 +34,23 @@ message CloseRecommendationsRequest { aggressive recommendations and 3 for conservative recommendations. */ float outlier_multiplier = 2; +} + +message ThresholdRecommendationsRequest { + /* + The parameters that are common to all close recommendations. + */ + CloseRecommendationRequest rec_request = 1; /* - Threshold contains the threshold value that is used to recommend channels - for closure. + The threshold that recommendations will be calculated based on. + For uptime: ratio of uptime to observed lifetime beneath which channels + will be recommended for closure. */ - oneof threshold{ - /* - The threshold percentage uptime over observed lifetime beneath which - channels will be recommended for closure. - */ - float uptime_threshold = 3; - } - } + float threshold_value = 2; +} -message CloseRecommendationsResponse{ +message CloseRecommendationsResponse { /* The total number of channels, before filtering out channels that are not eligible for close recommendations. @@ -53,25 +63,15 @@ message CloseRecommendationsResponse{ int32 considered_channels = 2; /* - A map of channels to close recommendations, based out whether they are - outliers in the uptime dataset. The absence of a channel in this set - implies that it was not considered for close because it did not meet - the criteria for close (it is private, or has not been monitored for - long enough to make a decision). + A set of channel close recommendations. The absence of a channel in this + set implies that it was not considered for close because it did not meet + the criteria for close recommendations (it is private, or has not been + monitored for long enough). */ - repeated Recommendation outlier_recommendations = 3; - - /* - A set of channel close recommendations, based out whether they are - beneath the threshold provided in the request. The absence of a channel - in this set implies that it was not considered for close because it - did not meet the criteria for close (it is private, or has not been - monitored for long enough to make a decision). - */ - repeated Recommendation threshold_recommendations = 4; + repeated Recommendation recommendations = 3; } -message Recommendation{ +message Recommendation { /* The channel point [funding txid: outpoint] of the channel being considered for close. @@ -115,7 +115,7 @@ message RevenueReportResponse { repeated RevenueReport reports = 1; } -message RevenueReport{ +message RevenueReport { /* Target channel is the channel that the report is generated for; incoming fields in the report mean that this channel was the incoming channel, diff --git a/trmrpc/rpcserver.go b/trmrpc/rpcserver.go index 545bd8b..a10b044 100644 --- a/trmrpc/rpcserver.go +++ b/trmrpc/rpcserver.go @@ -129,20 +129,37 @@ func (s *RPCServer) Stop() error { return nil } -// CloseRecommendations provides a set of close recommendations for the -// current set of open channels. -func (s *RPCServer) CloseRecommendations(ctx context.Context, - req *CloseRecommendationsRequest) (*CloseRecommendationsResponse, +// OutlierRecommendations provides a set of close recommendations for the +// current set of open channels based on whether they are outliers. +func (s *RPCServer) OutlierRecommendations(ctx context.Context, + req *OutlierRecommendationsRequest) (*CloseRecommendationsResponse, error) { - cfg := parseRequest(ctx, s.cfg, req) + cfg, multiplier := parseOutlierRequest(ctx, s.cfg, req) - report, err := recommend.CloseRecommendations(cfg) + report, err := recommend.OutlierRecommendations(cfg, multiplier) if err != nil { return nil, err } - return parseResponse(report), nil + return rpcResponse(report), nil +} + +// ThresholdRecommendations provides a set of close recommendations for the +// current set of open channels based on whether they are above or below a +// given threshold. +func (s *RPCServer) ThresholdRecommendations(ctx context.Context, + req *ThresholdRecommendationsRequest) (*CloseRecommendationsResponse, + error) { + + cfg, threshold := parseThresholdRequest(ctx, s.cfg, req) + + report, err := recommend.ThresholdRecommendations(cfg, threshold) + if err != nil { + return nil, err + } + + return rpcResponse(report), nil } // RevenueReport returns a pairwise revenue report for a channel From b9c1e62ae3a0e3a3087013f1ea5a255b27d40c1a Mon Sep 17 00:00:00 2001 From: carla Date: Tue, 25 Feb 2020 10:25:18 -0300 Subject: [PATCH 4/8] recommend: add revenue based recommendations This commit adds revenue based close recommendations which use the fees a channel has generated, scaled by the number of confirmations it has to compare channels. We do not have opening timestamps for channels at present, so we cannot compare channels over a time range (because we do not know whether the channel was open or not). A metric enum is used to identify the datapoint that we wish to use. In this commit, the rpc server is set to default to uptime calculations so that the rpc/cli changes can be made in a separate commit. --- recommend/recommend.go | 66 +++++++++++++++++++++++++++++-- recommend/recommend_test.go | 69 +++++++++++++++++++++++++++++++++ trmrpc/close_recommendations.go | 1 + 3 files changed, 132 insertions(+), 4 deletions(-) diff --git a/recommend/recommend.go b/recommend/recommend.go index d592567..23b445b 100644 --- a/recommend/recommend.go +++ b/recommend/recommend.go @@ -5,6 +5,7 @@ // // Channels will be assessed based on the following data points: // - Uptime ratio +// - Fee revenue per block capital has been committed for // // Channels that are outliers within the set of channels that are eligible for // close recommendation will be recommended for closure. @@ -24,12 +25,33 @@ var ( errZeroMinMonitored = errors.New("must provide a non-zero minimum " + "monitor time for channel exclusion") + // ErrNoMetric is returned when a close recommendations with no chosen + // metric is provided. + ErrNoMetric = errors.New("metric required for close " + + "recommendations") + // DefaultOutlierMultiplier is the default value used in close // recommendations based on outliers when there is no user provided // value. DefaultOutlierMultiplier float64 = 3 ) +// Metric is an enum which indicate what data point our recommendations should +// be based on. +type Metric int + +const ( + invalidMetric Metric = iota + + // UptimeMetric bases recommendations on the uptime of the channel's + // remote peer. + UptimeMetric + + // RevenueMetric bases recommendations on the revenue that the channel + // has generated per block that our capital has been committed for. + RevenueMetric +) + // CloseRecommendationConfig provides the functions and parameters required to // provide close recommendations. This struct holds fields which are common to // all recommendation calculation strategies. @@ -38,6 +60,10 @@ type CloseRecommendationConfig struct { // for our current set of channels. ChannelInsights func() ([]*insights.ChannelInfo, error) + // Metric defines the metric that we will use to provide close + // recommendations. Calls will fail if no value is provided. + Metric Metric + // MinimumMonitored is the minimum amount of time that a channel must // have been monitored for before it is considered for closing. MinimumMonitored time.Duration @@ -115,15 +141,23 @@ func closeRecommendations(cfg *CloseRecommendationConfig, // Filter out channels that are below the minimum required age. filtered := filterChannels(channels, cfg.MinimumMonitored) - // Produce a dataset containing uptime ratio for channels that have - // been monitored for longer than the minimum time. - data := getUptimeDataset(filtered) - report := &Report{ TotalChannels: len(channels), ConsideredChannels: len(filtered), } + var data dataset.Dataset + switch cfg.Metric { + case UptimeMetric: + data = getUptimeDataset(filtered) + + case RevenueMetric: + data = getRevenueDataset(filtered) + + default: + return nil, ErrNoMetric + } + // Get close recommendations based on outliers. report.Recommendations, err = getRecommendations(data) if err != nil { @@ -256,3 +290,27 @@ func getUptimeDataset( // Create a dataset for the uptime values we have collected. return dataset.New(channels) } + +// getRevenueDataset returns a dataset that scales revenue by the number of +// confirmations that a channel's opening transaction has. This allows for +// comparing of channels that have been open for different periods of time. +func getRevenueDataset( + eligibleChannels []*insights.ChannelInfo) dataset.Dataset { + + // Create a map which will hold channel point string label to revenue + // per block that we have had revenue committed for. + var channels = make(map[string]float64, len(eligibleChannels)) + + for _, channel := range eligibleChannels { + // Channels cannot have zero confirmations because we are + // dealing with open (ie confirmed) channels, so we can + // calculate fees per confirmation for every channel. + feesPerConfirmation := + float64(channel.FeesEarned) / + float64(channel.Confirmations) + + channels[channel.ChannelPoint] = feesPerConfirmation + } + + return channels +} diff --git a/recommend/recommend_test.go b/recommend/recommend_test.go index ef6dc39..7112b5d 100644 --- a/recommend/recommend_test.go +++ b/recommend/recommend_test.go @@ -20,6 +20,7 @@ func TestCloseRecommendations(t *testing.T) { tests := []struct { name string upperOutlier bool + metric Metric ChanInsights func() ([]*insights.ChannelInfo, error) MinMonitored time.Duration expectedErr error @@ -27,6 +28,7 @@ func TestCloseRecommendations(t *testing.T) { { name: "no channels", upperOutlier: false, + metric: UptimeMetric, ChanInsights: func() ([]*insights.ChannelInfo, error) { return nil, nil }, @@ -36,6 +38,16 @@ func TestCloseRecommendations(t *testing.T) { { name: "channel insights fails", upperOutlier: false, + metric: invalidMetric, + ChanInsights: func() ([]*insights.ChannelInfo, error) { + return nil, nil + }, + MinMonitored: time.Hour, + expectedErr: ErrNoMetric, + }, + { + name: "channel insights fails", + metric: UptimeMetric, ChanInsights: func() ([]*insights.ChannelInfo, error) { return nil, openChanErr }, @@ -45,6 +57,7 @@ func TestCloseRecommendations(t *testing.T) { { name: "zero min monitored", upperOutlier: false, + metric: UptimeMetric, ChanInsights: func() ([]*insights.ChannelInfo, error) { return nil, nil }, @@ -54,6 +67,7 @@ func TestCloseRecommendations(t *testing.T) { { name: "enough channels", upperOutlier: false, + metric: UptimeMetric, ChanInsights: func() ([]*insights.ChannelInfo, error) { return []*insights.ChannelInfo{ { @@ -94,6 +108,7 @@ func TestCloseRecommendations(t *testing.T) { &CloseRecommendationConfig{ ChannelInsights: test.ChanInsights, MinimumMonitored: test.MinMonitored, + Metric: test.metric, }, recFunc, ) @@ -426,3 +441,57 @@ func TestFilterChannels(t *testing.T) { }) } } + +// TestGetRevenueDataset tests scaling of revenue by the number of confirmations +// that a channel has. +func TestGetRevenueDataset(t *testing.T) { + tests := []struct { + name string + insights []*insights.ChannelInfo + expectedValues map[string]float64 + }{ + { + name: "no channels", + insights: []*insights.ChannelInfo{}, + }, + { + name: "two channels", + insights: []*insights.ChannelInfo{ + { + ChannelPoint: "a:0", + FeesEarned: 7, + Confirmations: 2, + }, + { + ChannelPoint: "a:1", + FeesEarned: 10, + Confirmations: 1, + }, + }, + expectedValues: map[string]float64{ + "a:0": 3.5, + "a:1": 10, + }, + }, + } + + for _, test := range tests { + test := test + + t.Run(test.name, func(t *testing.T) { + data := getRevenueDataset(test.insights) + if len(data) != len(test.expectedValues) { + t.Fatalf("expected: %v, got: %v", + len(test.expectedValues), len(data)) + } + + for chanPoint, value := range test.expectedValues { + if data.Value(chanPoint) != value { + t.Fatalf("expected: %v to "+ + "have value %v, got %v", chanPoint, + value, data.Value(chanPoint)) + } + } + }) + } +} diff --git a/trmrpc/close_recommendations.go b/trmrpc/close_recommendations.go index 4977ce1..a4d720c 100644 --- a/trmrpc/close_recommendations.go +++ b/trmrpc/close_recommendations.go @@ -20,6 +20,7 @@ func parseRecommendationRequest(ctx context.Context, cfg *Config, ChannelInsights: func() ([]*insights.ChannelInfo, error) { return channelInsights(ctx, cfg) }, + Metric: recommend.UptimeMetric, MinimumMonitored: time.Second * time.Duration(req.MinimumMonitored), } From d6fbefaf0f9ccae5f1cd209695ba6913db63557d Mon Sep 17 00:00:00 2001 From: carla Date: Fri, 21 Feb 2020 09:05:42 +0200 Subject: [PATCH 5/8] trmrpc+trmcli: add revenue based recommendations --- cmd/trmcli/close_recommendations.go | 49 +++++++- trmrpc/close_recommendations.go | 15 ++- trmrpc/rpc.pb.go | 167 ++++++++++++++++++---------- trmrpc/rpc.proto | 19 ++++ 4 files changed, 184 insertions(+), 66 deletions(-) diff --git a/cmd/trmcli/close_recommendations.go b/cmd/trmcli/close_recommendations.go index dcab464..134ed97 100644 --- a/cmd/trmcli/close_recommendations.go +++ b/cmd/trmcli/close_recommendations.go @@ -2,6 +2,7 @@ package main import ( "context" + "fmt" "time" "github.com/lightninglabs/terminator/trmrpc" @@ -27,6 +28,12 @@ var ( Usage: "Ratio of uptime to time monitored, expressed" + "in [0;1].", }, + cli.Float64Flag{ + Name: "revenue_threshold", + Usage: "threshold revenue (in msat) per confirmation " + + "beneath which channels will be identified " + + "for close.", + }, monitoredFlag, } @@ -40,6 +47,17 @@ var ( "Recommended values are 1.5 for aggressive " + "recommendations and 3 for conservative ones.", }, + cli.BoolFlag{ + Name: "uptime", + Usage: "set to get recommendations based on the " + + "channel's peer's ratio of uptime to time " + + "monitored", + }, + cli.BoolFlag{ + Name: "revenue", + Usage: "get recommendations based on the " + + "channel's revenue per confirmation", + }, monitoredFlag, } ) @@ -65,12 +83,19 @@ func queryThresholdRecommendations(ctx *cli.Context) error { }, } - // If an uptime threshold was set, use it, otherwise allow the call - // to proceed with 0 threshold, because we assess lower outlier <= the - // threshold so 0 is a valid value. - if ctx.IsSet("uptime_threshold") { - uptimeThreshold := float32(ctx.Float64("uptime_threshold")) - req.ThresholdValue = uptimeThreshold + // Set threshold and metric based on uptime/revenue flags. + switch { + case ctx.IsSet("uptime_threshold"): + req.ThresholdValue = float32(ctx.Float64("uptime_threshold")) + req.RecRequest.Metric = trmrpc.CloseRecommendationRequest_UPTIME + + case ctx.IsSet("revenue_threshold"): + req.ThresholdValue = float32(ctx.Float64("revenue_threshold")) + req.RecRequest.Metric = trmrpc.CloseRecommendationRequest_REVENUE + + default: + return fmt.Errorf("uptime_threshold or " + + "revenue_threshold required") } rpcCtx := context.Background() @@ -113,6 +138,18 @@ func queryOutlierRecommendations(ctx *cli.Context) error { req.OutlierMultiplier = float32(ctx.Float64("outlier_mult")) } + // Set metric based on uptime or revenue flags. + switch { + case ctx.IsSet("uptime"): + req.RecRequest.Metric = trmrpc.CloseRecommendationRequest_UPTIME + + case ctx.IsSet("revenue"): + req.RecRequest.Metric = trmrpc.CloseRecommendationRequest_REVENUE + + default: + return fmt.Errorf("uptime or revenue flag required") + } + rpcCtx := context.Background() recs, err := client.OutlierRecommendations(rpcCtx, req) if err != nil { diff --git a/trmrpc/close_recommendations.go b/trmrpc/close_recommendations.go index a4d720c..f7bfebe 100644 --- a/trmrpc/close_recommendations.go +++ b/trmrpc/close_recommendations.go @@ -16,14 +16,25 @@ func parseRecommendationRequest(ctx context.Context, cfg *Config, // Create a close recommendations config with the minimum monitored // value provided in the request and the default outlier multiplier. - return &recommend.CloseRecommendationConfig{ + recCfg := &recommend.CloseRecommendationConfig{ ChannelInsights: func() ([]*insights.ChannelInfo, error) { return channelInsights(ctx, cfg) }, - Metric: recommend.UptimeMetric, MinimumMonitored: time.Second * time.Duration(req.MinimumMonitored), } + + // Get the metric that the recommendations are being calculated based + // on. + switch req.Metric { + case CloseRecommendationRequest_UPTIME: + recCfg.Metric = recommend.UptimeMetric + + case CloseRecommendationRequest_REVENUE: + recCfg.Metric = recommend.RevenueMetric + } + + return recCfg } // parseOutlierRequest parses a rpc outlier recommendation request and returns diff --git a/trmrpc/rpc.pb.go b/trmrpc/rpc.pb.go index 911102c..8fa287d 100644 --- a/trmrpc/rpc.pb.go +++ b/trmrpc/rpc.pb.go @@ -23,15 +23,50 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package +type CloseRecommendationRequest_Metric int32 + +const ( + CloseRecommendationRequest_UNKNOWN CloseRecommendationRequest_Metric = 0 + CloseRecommendationRequest_UPTIME CloseRecommendationRequest_Metric = 1 + CloseRecommendationRequest_REVENUE CloseRecommendationRequest_Metric = 2 +) + +var CloseRecommendationRequest_Metric_name = map[int32]string{ + 0: "UNKNOWN", + 1: "UPTIME", + 2: "REVENUE", +} + +var CloseRecommendationRequest_Metric_value = map[string]int32{ + "UNKNOWN": 0, + "UPTIME": 1, + "REVENUE": 2, +} + +func (x CloseRecommendationRequest_Metric) String() string { + return proto.EnumName(CloseRecommendationRequest_Metric_name, int32(x)) +} + +func (CloseRecommendationRequest_Metric) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_77a6da22d6a3feb1, []int{0, 0} +} + type CloseRecommendationRequest struct { // //The minimum amount of time in seconds that a channel should have been //monitored by lnd to be eligible for close. This value is in place to //protect against closing of newer channels. - MinimumMonitored int64 `protobuf:"varint,1,opt,name=minimum_monitored,json=minimumMonitored,proto3" json:"minimum_monitored,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + MinimumMonitored int64 `protobuf:"varint,1,opt,name=minimum_monitored,json=minimumMonitored,proto3" json:"minimum_monitored,omitempty"` + // + //The data point base close recommendations on. Available options are: + //Uptime: ratio of channel peer's uptime to the period they have been + //monitored to. + //Revenue: the revenue that the channel has produced per block that its + //funding transaction has been confirmed for. + Metric CloseRecommendationRequest_Metric `protobuf:"varint,2,opt,name=metric,proto3,enum=trmrpc.CloseRecommendationRequest_Metric" json:"metric,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *CloseRecommendationRequest) Reset() { *m = CloseRecommendationRequest{} } @@ -66,6 +101,13 @@ func (m *CloseRecommendationRequest) GetMinimumMonitored() int64 { return 0 } +func (m *CloseRecommendationRequest) GetMetric() CloseRecommendationRequest_Metric { + if m != nil { + return m.Metric + } + return CloseRecommendationRequest_UNKNOWN +} + type OutlierRecommendationsRequest struct { // //The parameters that are common to all close recommendations. @@ -129,6 +171,10 @@ type ThresholdRecommendationsRequest struct { //The threshold that recommendations will be calculated based on. //For uptime: ratio of uptime to observed lifetime beneath which channels //will be recommended for closure. + //For revenue: revenue per block that capital has been committed to the + //channel beneath which channels will be recommended for closure. This + //value is provided per block so that channels that have been open for + //different periods of time can be compared. ThresholdValue float32 `protobuf:"fixed32,2,opt,name=threshold_value,json=thresholdValue,proto3" json:"threshold_value,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -725,6 +771,7 @@ func (m *ChannelInsight) GetPrivate() bool { } func init() { + proto.RegisterEnum("trmrpc.CloseRecommendationRequest_Metric", CloseRecommendationRequest_Metric_name, CloseRecommendationRequest_Metric_value) proto.RegisterType((*CloseRecommendationRequest)(nil), "trmrpc.CloseRecommendationRequest") proto.RegisterType((*OutlierRecommendationsRequest)(nil), "trmrpc.OutlierRecommendationsRequest") proto.RegisterType((*ThresholdRecommendationsRequest)(nil), "trmrpc.ThresholdRecommendationsRequest") @@ -743,60 +790,64 @@ func init() { func init() { proto.RegisterFile("rpc.proto", fileDescriptor_77a6da22d6a3feb1) } var fileDescriptor_77a6da22d6a3feb1 = []byte{ - // 841 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcd, 0x6e, 0xdb, 0x46, - 0x10, 0x06, 0xc5, 0xf8, 0x47, 0xa3, 0x4a, 0x56, 0xb6, 0x8e, 0xab, 0x1a, 0x76, 0x2d, 0x10, 0x49, - 0x23, 0xa0, 0xad, 0x1d, 0xa8, 0x97, 0xa2, 0xa7, 0x16, 0x46, 0x80, 0x1a, 0xa8, 0x91, 0x60, 0x63, - 0xf4, 0x54, 0x80, 0x60, 0xa9, 0x89, 0xbc, 0x28, 0xb9, 0xcb, 0xec, 0x2e, 0x05, 0xe4, 0x05, 0x7a, - 0xed, 0xa1, 0x2f, 0xd3, 0x87, 0xe8, 0xa5, 0x87, 0xbe, 0x4f, 0xb1, 0x7f, 0xa4, 0xa8, 0x4a, 0x49, - 0x2f, 0xb9, 0x91, 0xdf, 0xf7, 0xed, 0x7c, 0xa3, 0x99, 0xe1, 0xac, 0xa0, 0x2f, 0xab, 0xfc, 0xb2, - 0x92, 0x42, 0x0b, 0xb2, 0xaf, 0x65, 0x29, 0xab, 0xfc, 0xf4, 0x6c, 0x29, 0xc4, 0xb2, 0xc0, 0xab, - 0xac, 0x62, 0x57, 0x19, 0xe7, 0x42, 0x67, 0x9a, 0x09, 0xae, 0x9c, 0x2a, 0xb9, 0x81, 0xd3, 0xeb, - 0x42, 0x28, 0xa4, 0x98, 0x8b, 0xb2, 0x44, 0xbe, 0xb0, 0x2c, 0xc5, 0x37, 0x35, 0x2a, 0x4d, 0xbe, - 0x80, 0x87, 0x25, 0xe3, 0xac, 0xac, 0xcb, 0xb4, 0x14, 0x9c, 0x69, 0x21, 0x71, 0x31, 0x89, 0xa6, - 0xd1, 0x2c, 0xa6, 0x63, 0x4f, 0xdc, 0x06, 0x3c, 0xf9, 0x23, 0x82, 0xf3, 0x17, 0xb5, 0x2e, 0x18, - 0xca, 0x6e, 0x34, 0x15, 0xc2, 0x5d, 0xc3, 0x40, 0x62, 0x9e, 0x4a, 0xf7, 0x6a, 0x03, 0x0d, 0xe6, - 0xc9, 0xa5, 0x4b, 0xf4, 0x72, 0x77, 0x1e, 0x14, 0x24, 0xe6, 0x21, 0xc8, 0x57, 0x40, 0x84, 0x73, - 0x49, 0xcb, 0xba, 0xd0, 0xac, 0x32, 0x8f, 0x93, 0xde, 0x34, 0x9a, 0xf5, 0xe8, 0x43, 0xcf, 0xdc, - 0x36, 0x44, 0xf2, 0x7b, 0x04, 0x17, 0x77, 0xf7, 0x12, 0xd5, 0xbd, 0x28, 0x16, 0x1f, 0x32, 0xaf, - 0xa7, 0x70, 0xa4, 0x83, 0x4f, 0xba, 0xca, 0x8a, 0x1a, 0x7d, 0x52, 0xa3, 0x06, 0xfe, 0xc9, 0xa0, - 0xc9, 0x9f, 0x11, 0x9c, 0x6d, 0x89, 0xa9, 0x28, 0xaa, 0x4a, 0x70, 0x85, 0xe4, 0x09, 0x8c, 0xb4, - 0xd0, 0x59, 0x91, 0xe6, 0xf7, 0x19, 0xe7, 0x58, 0x28, 0x9b, 0xd1, 0x1e, 0x1d, 0x5a, 0xf4, 0xda, - 0x83, 0xe4, 0x0a, 0x3e, 0xce, 0x05, 0x57, 0x6c, 0x81, 0x12, 0x17, 0xad, 0xb6, 0x67, 0xb5, 0xa4, - 0xa5, 0x9a, 0x03, 0xdf, 0xc1, 0x91, 0xec, 0x5a, 0x4e, 0xe2, 0x69, 0x3c, 0x1b, 0xcc, 0x4f, 0xc2, - 0x4f, 0xdd, 0xf8, 0x95, 0x9b, 0xf2, 0x84, 0xc3, 0xa8, 0x2b, 0x21, 0xe7, 0x00, 0xc6, 0x39, 0xad, - 0x04, 0xe3, 0xae, 0x72, 0x7d, 0xda, 0x37, 0xc8, 0x4b, 0x03, 0x90, 0x63, 0xd8, 0x5b, 0x2f, 0x85, - 0x7b, 0x31, 0xa5, 0x6a, 0x22, 0xa7, 0xb9, 0x29, 0xc5, 0x24, 0x9e, 0x46, 0xb3, 0x43, 0x3a, 0x6a, - 0x60, 0x5b, 0xa0, 0xe4, 0x0d, 0x1c, 0x53, 0x5c, 0x21, 0xaf, 0x91, 0x62, 0x25, 0xa4, 0x0e, 0xb5, - 0xbe, 0x80, 0x41, 0xeb, 0x6a, 0xca, 0x13, 0xcf, 0xfa, 0x14, 0x1a, 0x5b, 0x65, 0xd2, 0x52, 0x3a, - 0x93, 0x3a, 0xd5, 0xac, 0x74, 0xe6, 0x0f, 0x68, 0xdf, 0x22, 0x77, 0xac, 0x44, 0xf2, 0x29, 0x1c, - 0x1a, 0x6b, 0x4b, 0xc6, 0x96, 0x3c, 0x40, 0xbe, 0x30, 0x54, 0xf2, 0x03, 0x3c, 0xda, 0xb0, 0xf4, - 0x5d, 0xb9, 0x82, 0x03, 0x69, 0x11, 0xe7, 0x37, 0x98, 0x3f, 0x6a, 0xab, 0xb6, 0xae, 0x0f, 0xaa, - 0xe4, 0x9f, 0x08, 0x86, 0x1d, 0xca, 0x36, 0x36, 0x93, 0x4b, 0xd4, 0xa1, 0x5b, 0xbe, 0x60, 0x43, - 0x87, 0xfa, 0x46, 0x91, 0x1b, 0xf8, 0xa8, 0xca, 0x98, 0x4c, 0x83, 0x5d, 0xcf, 0xda, 0x7d, 0xbe, - 0xd5, 0xee, 0xf2, 0x65, 0xc6, 0xa4, 0x7b, 0x54, 0xcf, 0xb9, 0x96, 0x6f, 0xe9, 0xa0, 0x6a, 0x91, - 0x53, 0x0a, 0xe3, 0x4d, 0x01, 0x19, 0x43, 0xfc, 0x2b, 0xbe, 0xf5, 0xd6, 0xe6, 0x91, 0xcc, 0xd6, - 0xbb, 0x34, 0x98, 0x93, 0xe0, 0xd4, 0x1e, 0xf5, 0x9d, 0xfb, 0xb6, 0xf7, 0x4d, 0x94, 0xfc, 0x15, - 0x01, 0xb4, 0x0c, 0x79, 0x06, 0xc7, 0x59, 0x29, 0x6a, 0xae, 0x53, 0x51, 0xeb, 0xa5, 0x60, 0x7c, - 0x99, 0x96, 0x2a, 0xd3, 0x7e, 0x4d, 0x10, 0xc7, 0xbd, 0xf0, 0xd4, 0xad, 0xca, 0x34, 0xf9, 0x12, - 0xc8, 0x6b, 0x44, 0xb5, 0xa1, 0xef, 0xb9, 0xb5, 0x62, 0x98, 0x8e, 0xba, 0x8d, 0xcf, 0x78, 0x2e, - 0xca, 0x46, 0x1f, 0xaf, 0xc7, 0xbf, 0xf1, 0x54, 0x27, 0x7e, 0x57, 0xff, 0xa0, 0x8d, 0xbf, 0xae, - 0x4e, 0x26, 0x70, 0xe2, 0x0b, 0x7f, 0xc3, 0x15, 0x5b, 0xde, 0xeb, 0xb0, 0x16, 0x92, 0x9f, 0xe1, - 0x93, 0xff, 0x30, 0x7e, 0x18, 0xbe, 0x87, 0xb1, 0x6f, 0x61, 0xca, 0x3c, 0xe7, 0xa7, 0xa2, 0xf9, - 0x96, 0xba, 0x47, 0xe9, 0x51, 0xde, 0x0d, 0x95, 0xfc, 0xdd, 0x83, 0x51, 0x57, 0xf3, 0xbe, 0x8f, - 0xc9, 0x6c, 0xe3, 0xb0, 0x6d, 0x53, 0x85, 0xb9, 0xe0, 0x0b, 0xe5, 0x67, 0x7b, 0xdc, 0x10, 0xaf, - 0x1c, 0x6e, 0x66, 0xad, 0xae, 0xcc, 0x80, 0x37, 0x4a, 0x37, 0xe8, 0x43, 0x87, 0x06, 0xd9, 0x33, - 0x38, 0x5e, 0x89, 0xa2, 0x2e, 0x71, 0x6b, 0xb5, 0x88, 0xe3, 0x3a, 0xd5, 0x6d, 0x4f, 0x74, 0xfb, - 0xb7, 0xb7, 0x7e, 0xa2, 0xd3, 0xc1, 0x19, 0xd8, 0xaa, 0xa7, 0x98, 0x49, 0x8e, 0x0b, 0xa7, 0xde, - 0xb7, 0xea, 0x91, 0xc1, 0x9f, 0x5b, 0xd8, 0x2a, 0x1f, 0xc3, 0x30, 0x17, 0xfc, 0x35, 0x93, 0xa5, - 0xdf, 0x4f, 0x07, 0xd3, 0x68, 0x36, 0xa4, 0x5d, 0x90, 0x4c, 0xe0, 0xa0, 0x92, 0x6c, 0x95, 0x69, - 0x9c, 0x1c, 0xda, 0xb5, 0x11, 0x5e, 0xe7, 0xbf, 0xc5, 0x30, 0xbe, 0x43, 0x59, 0x32, 0x9e, 0x69, - 0x21, 0x5f, 0xa1, 0x5c, 0xa1, 0x24, 0x08, 0x27, 0xdb, 0xaf, 0x25, 0xf2, 0x24, 0xf4, 0xea, 0x9d, - 0xd7, 0xd6, 0xe9, 0xe3, 0x77, 0xdc, 0x04, 0xed, 0x48, 0x30, 0x98, 0xec, 0xba, 0x67, 0xc8, 0xd3, - 0x10, 0xe1, 0x3d, 0x37, 0xd1, 0xff, 0xb4, 0xfa, 0x71, 0x73, 0xb1, 0x9c, 0x6d, 0x5f, 0x45, 0x3e, - 0xe8, 0xf9, 0x0e, 0xd6, 0x47, 0xa3, 0x70, 0xb4, 0x31, 0xe6, 0xe4, 0xb3, 0xed, 0x43, 0xdc, 0xa4, - 0x79, 0xb1, 0x93, 0x77, 0x31, 0x7f, 0xd9, 0xb7, 0xff, 0x2e, 0xbe, 0xfe, 0x37, 0x00, 0x00, 0xff, - 0xff, 0xce, 0x57, 0xd9, 0x5b, 0x90, 0x08, 0x00, 0x00, + // 903 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x41, 0x6f, 0xe3, 0x44, + 0x14, 0xc6, 0xc9, 0x36, 0x69, 0x5e, 0x48, 0x9a, 0x1d, 0xba, 0x25, 0x54, 0x2d, 0x8d, 0xac, 0x5d, + 0x36, 0x08, 0x48, 0x57, 0xe1, 0x82, 0x38, 0xb1, 0xaa, 0x22, 0x51, 0x41, 0xda, 0x6a, 0xb6, 0x5d, + 0x2e, 0x48, 0x96, 0x71, 0xde, 0xa6, 0x23, 0xec, 0x19, 0xef, 0xcc, 0x38, 0xd2, 0xfe, 0x01, 0xae, + 0x1c, 0xf8, 0x33, 0x1c, 0xf8, 0x09, 0x5c, 0x38, 0xf0, 0x7f, 0x90, 0x67, 0xc6, 0x76, 0x1c, 0xd2, + 0x96, 0x0b, 0x37, 0xfb, 0xfb, 0xbe, 0x79, 0xdf, 0xf3, 0x7b, 0x2f, 0x6f, 0x02, 0x1d, 0x99, 0x46, + 0x93, 0x54, 0x0a, 0x2d, 0x48, 0x4b, 0xcb, 0x44, 0xa6, 0xd1, 0xe1, 0xd1, 0x52, 0x88, 0x65, 0x8c, + 0xa7, 0x61, 0xca, 0x4e, 0x43, 0xce, 0x85, 0x0e, 0x35, 0x13, 0x5c, 0x59, 0x95, 0xff, 0x87, 0x07, + 0x87, 0x67, 0xb1, 0x50, 0x48, 0x31, 0x12, 0x49, 0x82, 0x7c, 0x61, 0x68, 0x8a, 0x6f, 0x33, 0x54, + 0x9a, 0x7c, 0x06, 0x8f, 0x13, 0xc6, 0x59, 0x92, 0x25, 0x41, 0x22, 0x38, 0xd3, 0x42, 0xe2, 0x62, + 0xe8, 0x8d, 0xbc, 0x71, 0x93, 0x0e, 0x1c, 0x31, 0x2f, 0x70, 0xf2, 0x12, 0x5a, 0x09, 0x6a, 0xc9, + 0xa2, 0x61, 0x63, 0xe4, 0x8d, 0xfb, 0xd3, 0x4f, 0x27, 0x36, 0x85, 0xc9, 0xdd, 0x06, 0x93, 0xb9, + 0x39, 0x40, 0xdd, 0x41, 0x7f, 0x02, 0x2d, 0x8b, 0x90, 0x2e, 0xb4, 0x6f, 0x2e, 0xbe, 0xbb, 0xb8, + 0xfc, 0xe1, 0x62, 0xf0, 0x1e, 0x01, 0x68, 0xdd, 0x5c, 0x5d, 0x9f, 0xcf, 0x67, 0x03, 0x2f, 0x27, + 0xe8, 0xec, 0xf5, 0xec, 0xe2, 0x66, 0x36, 0x68, 0xf8, 0xbf, 0x79, 0x70, 0x7c, 0x99, 0xe9, 0x98, + 0xa1, 0xac, 0xc7, 0x57, 0xc5, 0x17, 0x9c, 0x41, 0x57, 0x62, 0x14, 0x48, 0xfb, 0x6a, 0x72, 0xef, + 0x4e, 0xfd, 0x87, 0x33, 0xa3, 0x20, 0x31, 0x2a, 0x82, 0x7c, 0x01, 0x44, 0x58, 0x97, 0x20, 0xc9, + 0x62, 0xcd, 0xd2, 0xfc, 0xd1, 0x7c, 0x65, 0x83, 0x3e, 0x76, 0xcc, 0xbc, 0x24, 0xfc, 0x5f, 0x3d, + 0x38, 0xb9, 0xbe, 0x95, 0xa8, 0x6e, 0x45, 0xbc, 0xf8, 0x3f, 0xf3, 0x7a, 0x0e, 0x7b, 0xba, 0xf0, + 0x09, 0x56, 0x61, 0x9c, 0xa1, 0x4b, 0xaa, 0x5f, 0xc2, 0xaf, 0x73, 0xd4, 0xff, 0xdd, 0x83, 0xa3, + 0x2d, 0x31, 0x15, 0x45, 0x95, 0x0a, 0xae, 0x90, 0x3c, 0x83, 0xbe, 0x16, 0x3a, 0x8c, 0x83, 0xe8, + 0x36, 0xe4, 0x1c, 0x63, 0x65, 0x32, 0xda, 0xa1, 0x3d, 0x83, 0x9e, 0x39, 0x90, 0x9c, 0xc2, 0x07, + 0x91, 0xe0, 0x8a, 0x2d, 0x50, 0xe2, 0xa2, 0xd2, 0x36, 0x8c, 0x96, 0x54, 0x54, 0x79, 0xe0, 0x1b, + 0xd8, 0x93, 0x75, 0xcb, 0x61, 0x73, 0xd4, 0x1c, 0x77, 0xa7, 0x07, 0xc5, 0xa7, 0x6e, 0x7c, 0xe5, + 0xa6, 0xdc, 0xe7, 0xd0, 0xaf, 0x4b, 0xc8, 0x31, 0x40, 0xee, 0x1c, 0xa4, 0x82, 0x71, 0x5b, 0xb9, + 0x0e, 0xed, 0xe4, 0xc8, 0x55, 0x0e, 0x90, 0x7d, 0xd8, 0x59, 0x2f, 0x85, 0x7d, 0xc9, 0x4b, 0x55, + 0x46, 0x0e, 0xa2, 0xbc, 0x14, 0xc3, 0xe6, 0xc8, 0x1b, 0xef, 0xd2, 0x7e, 0x09, 0x9b, 0x02, 0xf9, + 0x6f, 0x61, 0x9f, 0xe2, 0x0a, 0x79, 0x86, 0x14, 0x53, 0x21, 0x75, 0x51, 0xeb, 0x13, 0xe8, 0x56, + 0xae, 0x79, 0x79, 0x9a, 0xe3, 0x0e, 0x85, 0xd2, 0x56, 0xe5, 0x69, 0x29, 0x1d, 0x4a, 0x1d, 0x68, + 0x96, 0x58, 0xf3, 0x47, 0xb4, 0x63, 0x90, 0x6b, 0x96, 0x20, 0xf9, 0x08, 0x76, 0x73, 0x6b, 0x43, + 0x36, 0x0d, 0xd9, 0x46, 0xbe, 0xc8, 0x29, 0xff, 0x5b, 0x78, 0xb2, 0x61, 0xe9, 0xba, 0x72, 0x0a, + 0x6d, 0x69, 0x10, 0xeb, 0xd7, 0x9d, 0x3e, 0xa9, 0xaa, 0xb6, 0xae, 0x2f, 0x54, 0xfe, 0xdf, 0x1e, + 0xf4, 0x6a, 0x94, 0x69, 0x6c, 0x28, 0x97, 0xa8, 0x8b, 0x6e, 0xb9, 0x82, 0xf5, 0x2c, 0xea, 0x1a, + 0x45, 0xce, 0xe1, 0xfd, 0x34, 0x64, 0x32, 0x28, 0xec, 0x1a, 0xc6, 0xee, 0x93, 0xad, 0x76, 0x93, + 0xab, 0x90, 0x49, 0xfb, 0xa8, 0x66, 0x5c, 0xcb, 0x77, 0xb4, 0x9b, 0x56, 0xc8, 0x21, 0x85, 0xc1, + 0xa6, 0x80, 0x0c, 0xa0, 0xf9, 0x33, 0xbe, 0x73, 0xd6, 0xf9, 0x23, 0x19, 0xaf, 0x77, 0xa9, 0x3b, + 0x25, 0x85, 0x53, 0x75, 0xd4, 0x75, 0xee, 0xeb, 0xc6, 0x57, 0x9e, 0xff, 0xa7, 0x07, 0x50, 0x31, + 0xe4, 0x05, 0xec, 0x87, 0x89, 0xc8, 0xb8, 0x0e, 0x44, 0xa6, 0x97, 0x82, 0xf1, 0x65, 0x90, 0xa8, + 0x50, 0xbb, 0xcd, 0x44, 0x2c, 0x77, 0xe9, 0xa8, 0xb9, 0x0a, 0x35, 0xf9, 0x1c, 0xc8, 0x1b, 0x44, + 0xb5, 0xa1, 0x6f, 0xd8, 0x4d, 0x96, 0x33, 0x35, 0x75, 0x15, 0x9f, 0xf1, 0x48, 0x24, 0xa5, 0xbe, + 0xb9, 0x1e, 0xff, 0xdc, 0x51, 0xb5, 0xf8, 0x75, 0xfd, 0xa3, 0x2a, 0xfe, 0xba, 0xda, 0x1f, 0xc2, + 0x81, 0x2b, 0xfc, 0x39, 0x57, 0x6c, 0x79, 0xab, 0x8b, 0xb5, 0xe0, 0xff, 0x08, 0x1f, 0xfe, 0x8b, + 0x71, 0xc3, 0xf0, 0x12, 0x06, 0xae, 0x85, 0x01, 0x73, 0x9c, 0x9b, 0x8a, 0xf2, 0xb7, 0x54, 0x3f, + 0x4a, 0xf7, 0xa2, 0x7a, 0x28, 0xff, 0xaf, 0x06, 0xf4, 0xeb, 0x9a, 0x87, 0x7e, 0x4c, 0xf9, 0x05, + 0x50, 0x2c, 0xf8, 0x40, 0x61, 0x24, 0xf8, 0x42, 0xb9, 0xd9, 0x1e, 0x94, 0xc4, 0x2b, 0x8b, 0xe7, + 0xb3, 0x96, 0xa5, 0xf9, 0x80, 0x97, 0x4a, 0x3b, 0xe8, 0x3d, 0x8b, 0x16, 0xb2, 0x17, 0xb0, 0xbf, + 0x12, 0x71, 0x96, 0xe0, 0xd6, 0x6a, 0x11, 0xcb, 0xd5, 0xaa, 0x5b, 0x9d, 0xa8, 0xf7, 0x6f, 0x67, + 0xfd, 0x44, 0xad, 0x83, 0x63, 0x30, 0x55, 0x0f, 0x30, 0x94, 0x1c, 0x17, 0x56, 0xdd, 0x32, 0xea, + 0x7e, 0x8e, 0xcf, 0x0c, 0x6c, 0x94, 0x4f, 0xa1, 0x17, 0x09, 0xfe, 0x86, 0xc9, 0xc4, 0xed, 0xa7, + 0xf6, 0xc8, 0x1b, 0xf7, 0x68, 0x1d, 0x24, 0x43, 0x68, 0xa7, 0x92, 0xad, 0x42, 0x8d, 0xc3, 0x5d, + 0xb3, 0x36, 0x8a, 0xd7, 0xe9, 0x2f, 0x4d, 0x18, 0x5c, 0xa3, 0x4c, 0x18, 0x0f, 0xb5, 0x90, 0xaf, + 0x50, 0xae, 0x50, 0x12, 0x84, 0x83, 0xed, 0xd7, 0x12, 0x79, 0x56, 0xf4, 0xea, 0xde, 0x6b, 0xeb, + 0xf0, 0xe9, 0x3d, 0x37, 0x41, 0x35, 0x12, 0x0c, 0x86, 0x77, 0xdd, 0x33, 0xe4, 0x79, 0x11, 0xe1, + 0x81, 0x9b, 0xe8, 0x3f, 0x5a, 0x7d, 0xbf, 0xb9, 0x58, 0x8e, 0xb6, 0xaf, 0x22, 0x17, 0xf4, 0xf8, + 0x0e, 0xd6, 0x45, 0xa3, 0xb0, 0xb7, 0x31, 0xe6, 0xe4, 0xe3, 0xed, 0x43, 0x5c, 0xa6, 0x79, 0x72, + 0x27, 0x6f, 0x63, 0xfe, 0xd4, 0x32, 0xff, 0x68, 0xbe, 0xfc, 0x27, 0x00, 0x00, 0xff, 0xff, 0x67, + 0x57, 0x82, 0x18, 0x04, 0x09, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/trmrpc/rpc.proto b/trmrpc/rpc.proto index 3220ae5..9b17e00 100644 --- a/trmrpc/rpc.proto +++ b/trmrpc/rpc.proto @@ -18,6 +18,21 @@ message CloseRecommendationRequest { protect against closing of newer channels. */ int64 minimum_monitored = 1; + + enum Metric{ + UNKNOWN = 0; + UPTIME = 1; + REVENUE = 2; + } + + /* + The data point base close recommendations on. Available options are: + Uptime: ratio of channel peer's uptime to the period they have been + monitored to. + Revenue: the revenue that the channel has produced per block that its + funding transaction has been confirmed for. + */ + Metric metric = 2; } message OutlierRecommendationsRequest { @@ -46,6 +61,10 @@ message ThresholdRecommendationsRequest { The threshold that recommendations will be calculated based on. For uptime: ratio of uptime to observed lifetime beneath which channels will be recommended for closure. + For revenue: revenue per block that capital has been committed to the + channel beneath which channels will be recommended for closure. This + value is provided per block so that channels that have been open for + different periods of time can be compared. */ float threshold_value = 2; } From 24cdd6bed45c089825d01b8f5d095ba811b84b75 Mon Sep 17 00:00:00 2001 From: carla Date: Fri, 21 Feb 2020 09:36:06 +0200 Subject: [PATCH 6/8] recommend: add volume based recommendations --- recommend/recommend.go | 70 ++++++++++++++++++++++++++++++++----- recommend/recommend_test.go | 60 ++++++++++++++++++++++++++++--- 2 files changed, 116 insertions(+), 14 deletions(-) diff --git a/recommend/recommend.go b/recommend/recommend.go index 23b445b..a632bbc 100644 --- a/recommend/recommend.go +++ b/recommend/recommend.go @@ -6,6 +6,9 @@ // Channels will be assessed based on the following data points: // - Uptime ratio // - Fee revenue per block capital has been committed for +// - Incoming volume per block capital has been committed for +// - Outgoing volume per block capital has been committed for +// - Total volume per block capital has been committed for // // Channels that are outliers within the set of channels that are eligible for // close recommendation will be recommended for closure. @@ -50,6 +53,18 @@ const ( // RevenueMetric bases recommendations on the revenue that the channel // has generated per block that our capital has been committed for. RevenueMetric + + // IncomingVolume bases recommendations on the incoming volume that the + // channel has processed, scaled by funding transaction confirmations. + IncomingVolume + + // IncomingVolume bases recommendations on the incoming volume that the + // channel has processed, scaled by funding transaction confirmations. + OutgoingVolume + + // Volume bases recommendations on the total volume that the + // channel has processed, scaled by funding transaction confirmations. + Volume ) // CloseRecommendationConfig provides the functions and parameters required to @@ -152,7 +167,20 @@ func closeRecommendations(cfg *CloseRecommendationConfig, data = getUptimeDataset(filtered) case RevenueMetric: - data = getRevenueDataset(filtered) + data = getConfirmationScaledDataset(revenueValue, filtered) + + case IncomingVolume: + data = getConfirmationScaledDataset( + incomingVolumeValue, filtered, + ) + + case OutgoingVolume: + data = getConfirmationScaledDataset( + outgoingVolumeValue, filtered, + ) + + case Volume: + data = getConfirmationScaledDataset(totalVolumeValue, filtered) default: return nil, ErrNoMetric @@ -291,10 +319,10 @@ func getUptimeDataset( return dataset.New(channels) } -// getRevenueDataset returns a dataset that scales revenue by the number of -// confirmations that a channel's opening transaction has. This allows for -// comparing of channels that have been open for different periods of time. -func getRevenueDataset( +// getConfirmationScaledDataset returns a dataset that scales a value by the +// number of confirmations its funding transaction has. It takes a function +// which gets the relevant value from the channel insight as input. +func getConfirmationScaledDataset(getValue perConfirmationValue, eligibleChannels []*insights.ChannelInfo) dataset.Dataset { // Create a map which will hold channel point string label to revenue @@ -304,13 +332,37 @@ func getRevenueDataset( for _, channel := range eligibleChannels { // Channels cannot have zero confirmations because we are // dealing with open (ie confirmed) channels, so we can - // calculate fees per confirmation for every channel. - feesPerConfirmation := - float64(channel.FeesEarned) / + // get the value and scale it by our confirmation total. + valuePerConfirmation := + getValue(channel) / float64(channel.Confirmations) - channels[channel.ChannelPoint] = feesPerConfirmation + channels[channel.ChannelPoint] = valuePerConfirmation } return channels } + +// perConfirmationValue is a function which gets a value from a channel insight +// that needs to be scaled by its number of confirmations. +type perConfirmationValue func(channel *insights.ChannelInfo) float64 + +// revenueValue gets total revenue for a channel. +func revenueValue(channel *insights.ChannelInfo) float64 { + return float64(channel.FeesEarned) +} + +// incomingVolumeValue gets total incoming volume for a channel. +func incomingVolumeValue(channel *insights.ChannelInfo) float64 { + return float64(channel.VolumeIncoming) +} + +// outgoingVolumeValue gets total outgoing volume for a channel. +func outgoingVolumeValue(channel *insights.ChannelInfo) float64 { + return float64(channel.VolumeOutgoing) +} + +// totalVolumeValue gets total volume for a channel. +func totalVolumeValue(channel *insights.ChannelInfo) float64 { + return float64(channel.VolumeIncoming + channel.VolumeOutgoing) +} diff --git a/recommend/recommend_test.go b/recommend/recommend_test.go index 7112b5d..26fde27 100644 --- a/recommend/recommend_test.go +++ b/recommend/recommend_test.go @@ -442,20 +442,23 @@ func TestFilterChannels(t *testing.T) { } } -// TestGetRevenueDataset tests scaling of revenue by the number of confirmations -// that a channel has. -func TestGetRevenueDataset(t *testing.T) { +// TestGetConfirmationScaledDataset tests scaling of data by the number of +// confirmations that a channel has. +func TestGetConfirmationScaledDataset(t *testing.T) { tests := []struct { name string insights []*insights.ChannelInfo + getValue perConfirmationValue expectedValues map[string]float64 }{ { name: "no channels", + getValue: revenueValue, insights: []*insights.ChannelInfo{}, }, { - name: "two channels", + name: "revenue scaled", + getValue: revenueValue, insights: []*insights.ChannelInfo{ { ChannelPoint: "a:0", @@ -473,13 +476,60 @@ func TestGetRevenueDataset(t *testing.T) { "a:1": 10, }, }, + { + name: "total volume", + getValue: totalVolumeValue, + insights: []*insights.ChannelInfo{ + { + ChannelPoint: "a:0", + VolumeIncoming: 10, + VolumeOutgoing: 2, + Confirmations: 2, + }, + }, + expectedValues: map[string]float64{ + "a:0": 6, + }, + }, + { + name: "incoming volume", + getValue: incomingVolumeValue, + insights: []*insights.ChannelInfo{ + { + ChannelPoint: "a:0", + VolumeIncoming: 10, + VolumeOutgoing: 2, + Confirmations: 2, + }, + }, + expectedValues: map[string]float64{ + "a:0": 5, + }, + }, + { + name: "outgoing volume", + getValue: outgoingVolumeValue, + insights: []*insights.ChannelInfo{ + { + ChannelPoint: "a:0", + VolumeIncoming: 10, + VolumeOutgoing: 2, + Confirmations: 2, + }, + }, + expectedValues: map[string]float64{ + "a:0": 1, + }, + }, } for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { - data := getRevenueDataset(test.insights) + data := getConfirmationScaledDataset( + test.getValue, test.insights, + ) if len(data) != len(test.expectedValues) { t.Fatalf("expected: %v, got: %v", len(test.expectedValues), len(data)) From 170ffdf1a4d76ebb17f43175524a86c414b41cac Mon Sep 17 00:00:00 2001 From: carla Date: Fri, 21 Feb 2020 09:50:36 +0200 Subject: [PATCH 7/8] trmcli: display incoming, outgoing and total volume per confirmation --- cmd/trmcli/channel_insights.go | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/cmd/trmcli/channel_insights.go b/cmd/trmcli/channel_insights.go index 0350987..9b41b21 100644 --- a/cmd/trmcli/channel_insights.go +++ b/cmd/trmcli/channel_insights.go @@ -15,6 +15,17 @@ var channelInsightsCommand = cli.Command{ Action: queryChannelInsights, } +// insightsResp is used to display additional information that is calculated +// from the channel insight in the cli response. +type insightsResp struct { + *trmrpc.ChannelInsight + UptimeRatio float64 `json:"uptime_ratio"` + RevenuePerConfirmation float64 `json:"revenue_per_conf_msat"` + VolumePerConfirmation float64 `json:"volume_per_conf_msat"` + IncomingVolumePerConfirmation float64 `json:"incoming_vol_per_conf_msat"` + OutgoingVolumePerConfirmation float64 `json:"outgoing_vol_per_conf_msat"` +} + func queryChannelInsights(ctx *cli.Context) error { client, cleanup := getClient(ctx) defer cleanup() @@ -27,17 +38,14 @@ func queryChannelInsights(ctx *cli.Context) error { return err } - type insightsResp struct { - *trmrpc.ChannelInsight - UptimeRatio float64 `json:"uptime_ratio"` - RevenuePerConfirmation float64 `json:"revenue_per_confirmation_msat"` - } insights := make([]insightsResp, len(resp.ChannelInsights)) for i, channel := range resp.ChannelInsights { + confirmations := float64(channel.Confirmations) + insight := insightsResp{ ChannelInsight: channel, RevenuePerConfirmation: float64(channel.FeesEarnedMsat) / - float64(channel.Confirmations), + confirmations, } if channel.MonitoredSeconds != 0 { @@ -45,6 +53,20 @@ func queryChannelInsights(ctx *cli.Context) error { float64(channel.MonitoredSeconds) } + // Calculate incoming, outgoing and total volume per + // confirmation. + insight.IncomingVolumePerConfirmation = + float64(channel.VolumeIncomingMsat) / + confirmations + + insight.OutgoingVolumePerConfirmation = + float64(channel.VolumeOutgoingMsat) / + confirmations + + insight.VolumePerConfirmation = + insight.IncomingVolumePerConfirmation + + confirmations + insights[i] = insight } From 5d409db9d2b1ac3a1873e87b68de9f5e42c99450 Mon Sep 17 00:00:00 2001 From: carla Date: Fri, 21 Feb 2020 09:51:10 +0200 Subject: [PATCH 8/8] trmrpc+trmcli: add volume based recommendations --- cmd/trmcli/close_recommendations.go | 72 +++++++++++-- trmrpc/close_recommendations.go | 9 ++ trmrpc/rpc.pb.go | 155 ++++++++++++++++------------ trmrpc/rpc.proto | 19 ++++ 4 files changed, 182 insertions(+), 73 deletions(-) diff --git a/cmd/trmcli/close_recommendations.go b/cmd/trmcli/close_recommendations.go index 134ed97..2ca27e8 100644 --- a/cmd/trmcli/close_recommendations.go +++ b/cmd/trmcli/close_recommendations.go @@ -24,16 +24,34 @@ var ( // Flags required for threshold close recommendations. thresholdFlags = []cli.Flag{ cli.Float64Flag{ - Name: "uptime_threshold", + Name: "uptime", Usage: "Ratio of uptime to time monitored, expressed" + "in [0;1].", }, cli.Float64Flag{ - Name: "revenue_threshold", + Name: "revenue", Usage: "threshold revenue (in msat) per confirmation " + "beneath which channels will be identified " + "for close.", }, + cli.Float64Flag{ + Name: "incoming", + Usage: "threshold incoming volume (in msat) per " + + "confirmation beneath which channels will be " + + "identified for close", + }, + cli.Float64Flag{ + Name: "outgoing", + Usage: "threshold outgoing volume (in msat) per " + + "confirmation beneath which channels will be " + + "identified for close", + }, + cli.Float64Flag{ + Name: "volume", + Usage: "threshold total volume (in msat) per " + + "confirmation beneath which channels will be " + + "identified for close", + }, monitoredFlag, } @@ -58,6 +76,21 @@ var ( Usage: "get recommendations based on the " + "channel's revenue per confirmation", }, + cli.BoolFlag{ + Name: "incoming_volume", + Usage: "get recommendations based on the " + + "channel's incoming volume per confirmation", + }, + cli.BoolFlag{ + Name: "outgoing_volume", + Usage: "get recommendations based on the " + + "channel's outgoing volume per confirmation", + }, + cli.BoolFlag{ + Name: "volume", + Usage: "get recommendations based on the " + + "channel's total volume per confirmation", + }, monitoredFlag, } ) @@ -85,17 +118,28 @@ func queryThresholdRecommendations(ctx *cli.Context) error { // Set threshold and metric based on uptime/revenue flags. switch { - case ctx.IsSet("uptime_threshold"): - req.ThresholdValue = float32(ctx.Float64("uptime_threshold")) + case ctx.IsSet("uptime"): + req.ThresholdValue = float32(ctx.Float64("uptime")) req.RecRequest.Metric = trmrpc.CloseRecommendationRequest_UPTIME - case ctx.IsSet("revenue_threshold"): - req.ThresholdValue = float32(ctx.Float64("revenue_threshold")) + case ctx.IsSet("revenue"): + req.ThresholdValue = float32(ctx.Float64("revenue")) req.RecRequest.Metric = trmrpc.CloseRecommendationRequest_REVENUE + case ctx.IsSet("incoming"): + req.ThresholdValue = float32(ctx.Float64("incoming")) + req.RecRequest.Metric = trmrpc.CloseRecommendationRequest_INCOMING_VOLUME + + case ctx.IsSet("outgoing"): + req.ThresholdValue = float32(ctx.Float64("outgoing")) + req.RecRequest.Metric = trmrpc.CloseRecommendationRequest_OUTGOING_VOLUME + + case ctx.IsSet("volume"): + req.ThresholdValue = float32(ctx.Float64("volume")) + req.RecRequest.Metric = trmrpc.CloseRecommendationRequest_TOTAL_VOLUME + default: - return fmt.Errorf("uptime_threshold or " + - "revenue_threshold required") + return fmt.Errorf("threshold required") } rpcCtx := context.Background() @@ -146,8 +190,18 @@ func queryOutlierRecommendations(ctx *cli.Context) error { case ctx.IsSet("revenue"): req.RecRequest.Metric = trmrpc.CloseRecommendationRequest_REVENUE + case ctx.IsSet("incoming_volume"): + req.RecRequest.Metric = trmrpc.CloseRecommendationRequest_INCOMING_VOLUME + + case ctx.IsSet("outgoing_volume"): + req.RecRequest.Metric = trmrpc.CloseRecommendationRequest_OUTGOING_VOLUME + + case ctx.IsSet("volume"): + req.RecRequest.Metric = trmrpc.CloseRecommendationRequest_TOTAL_VOLUME + default: - return fmt.Errorf("uptime or revenue flag required") + return fmt.Errorf("uptime, revenue or volume realted flag " + + "required") } rpcCtx := context.Background() diff --git a/trmrpc/close_recommendations.go b/trmrpc/close_recommendations.go index f7bfebe..2ec043d 100644 --- a/trmrpc/close_recommendations.go +++ b/trmrpc/close_recommendations.go @@ -32,6 +32,15 @@ func parseRecommendationRequest(ctx context.Context, cfg *Config, case CloseRecommendationRequest_REVENUE: recCfg.Metric = recommend.RevenueMetric + + case CloseRecommendationRequest_INCOMING_VOLUME: + recCfg.Metric = recommend.IncomingVolume + + case CloseRecommendationRequest_OUTGOING_VOLUME: + recCfg.Metric = recommend.OutgoingVolume + + case CloseRecommendationRequest_TOTAL_VOLUME: + recCfg.Metric = recommend.Volume } return recCfg diff --git a/trmrpc/rpc.pb.go b/trmrpc/rpc.pb.go index 8fa287d..d4b7c8b 100644 --- a/trmrpc/rpc.pb.go +++ b/trmrpc/rpc.pb.go @@ -26,21 +26,30 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package type CloseRecommendationRequest_Metric int32 const ( - CloseRecommendationRequest_UNKNOWN CloseRecommendationRequest_Metric = 0 - CloseRecommendationRequest_UPTIME CloseRecommendationRequest_Metric = 1 - CloseRecommendationRequest_REVENUE CloseRecommendationRequest_Metric = 2 + CloseRecommendationRequest_UNKNOWN CloseRecommendationRequest_Metric = 0 + CloseRecommendationRequest_UPTIME CloseRecommendationRequest_Metric = 1 + CloseRecommendationRequest_REVENUE CloseRecommendationRequest_Metric = 2 + CloseRecommendationRequest_INCOMING_VOLUME CloseRecommendationRequest_Metric = 3 + CloseRecommendationRequest_OUTGOING_VOLUME CloseRecommendationRequest_Metric = 4 + CloseRecommendationRequest_TOTAL_VOLUME CloseRecommendationRequest_Metric = 5 ) var CloseRecommendationRequest_Metric_name = map[int32]string{ 0: "UNKNOWN", 1: "UPTIME", 2: "REVENUE", + 3: "INCOMING_VOLUME", + 4: "OUTGOING_VOLUME", + 5: "TOTAL_VOLUME", } var CloseRecommendationRequest_Metric_value = map[string]int32{ - "UNKNOWN": 0, - "UPTIME": 1, - "REVENUE": 2, + "UNKNOWN": 0, + "UPTIME": 1, + "REVENUE": 2, + "INCOMING_VOLUME": 3, + "OUTGOING_VOLUME": 4, + "TOTAL_VOLUME": 5, } func (x CloseRecommendationRequest_Metric) String() string { @@ -171,10 +180,26 @@ type ThresholdRecommendationsRequest struct { //The threshold that recommendations will be calculated based on. //For uptime: ratio of uptime to observed lifetime beneath which channels //will be recommended for closure. + // //For revenue: revenue per block that capital has been committed to the //channel beneath which channels will be recommended for closure. This //value is provided per block so that channels that have been open for //different periods of time can be compared. + // + //For incoming volume: The incoming volume per block that capital has + //been committed to the channel beneath which channels will be recommended + //for closure. This value is provided per block so that channels that have + //been open for different periods of time can be compared. + // + //For outgoing volume: The outgoing volume per block that capital has been + //committed to the channel beneath which channels will be recommended for + //closure. This value is provided per block so that channels that have been + //open for different periods of time can be compared. + // + //For total volume: The total volume per block that capital has been + //committed to the channel beneath which channels will be recommended for + //closure. This value is provided per block so that channels that have been + //open for different periods of time can be compared. ThresholdValue float32 `protobuf:"fixed32,2,opt,name=threshold_value,json=thresholdValue,proto3" json:"threshold_value,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -790,64 +815,66 @@ func init() { func init() { proto.RegisterFile("rpc.proto", fileDescriptor_77a6da22d6a3feb1) } var fileDescriptor_77a6da22d6a3feb1 = []byte{ - // 903 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x41, 0x6f, 0xe3, 0x44, - 0x14, 0xc6, 0xc9, 0x36, 0x69, 0x5e, 0x48, 0x9a, 0x1d, 0xba, 0x25, 0x54, 0x2d, 0x8d, 0xac, 0x5d, - 0x36, 0x08, 0x48, 0x57, 0xe1, 0x82, 0x38, 0xb1, 0xaa, 0x22, 0x51, 0x41, 0xda, 0x6a, 0xb6, 0x5d, - 0x2e, 0x48, 0x96, 0x71, 0xde, 0xa6, 0x23, 0xec, 0x19, 0xef, 0xcc, 0x38, 0xd2, 0xfe, 0x01, 0xae, - 0x1c, 0xf8, 0x33, 0x1c, 0xf8, 0x09, 0x5c, 0x38, 0xf0, 0x7f, 0x90, 0x67, 0xc6, 0x76, 0x1c, 0xd2, - 0x96, 0x0b, 0x37, 0xfb, 0xfb, 0xbe, 0x79, 0xdf, 0xf3, 0x7b, 0x2f, 0x6f, 0x02, 0x1d, 0x99, 0x46, - 0x93, 0x54, 0x0a, 0x2d, 0x48, 0x4b, 0xcb, 0x44, 0xa6, 0xd1, 0xe1, 0xd1, 0x52, 0x88, 0x65, 0x8c, - 0xa7, 0x61, 0xca, 0x4e, 0x43, 0xce, 0x85, 0x0e, 0x35, 0x13, 0x5c, 0x59, 0x95, 0xff, 0x87, 0x07, - 0x87, 0x67, 0xb1, 0x50, 0x48, 0x31, 0x12, 0x49, 0x82, 0x7c, 0x61, 0x68, 0x8a, 0x6f, 0x33, 0x54, - 0x9a, 0x7c, 0x06, 0x8f, 0x13, 0xc6, 0x59, 0x92, 0x25, 0x41, 0x22, 0x38, 0xd3, 0x42, 0xe2, 0x62, - 0xe8, 0x8d, 0xbc, 0x71, 0x93, 0x0e, 0x1c, 0x31, 0x2f, 0x70, 0xf2, 0x12, 0x5a, 0x09, 0x6a, 0xc9, - 0xa2, 0x61, 0x63, 0xe4, 0x8d, 0xfb, 0xd3, 0x4f, 0x27, 0x36, 0x85, 0xc9, 0xdd, 0x06, 0x93, 0xb9, - 0x39, 0x40, 0xdd, 0x41, 0x7f, 0x02, 0x2d, 0x8b, 0x90, 0x2e, 0xb4, 0x6f, 0x2e, 0xbe, 0xbb, 0xb8, - 0xfc, 0xe1, 0x62, 0xf0, 0x1e, 0x01, 0x68, 0xdd, 0x5c, 0x5d, 0x9f, 0xcf, 0x67, 0x03, 0x2f, 0x27, - 0xe8, 0xec, 0xf5, 0xec, 0xe2, 0x66, 0x36, 0x68, 0xf8, 0xbf, 0x79, 0x70, 0x7c, 0x99, 0xe9, 0x98, - 0xa1, 0xac, 0xc7, 0x57, 0xc5, 0x17, 0x9c, 0x41, 0x57, 0x62, 0x14, 0x48, 0xfb, 0x6a, 0x72, 0xef, - 0x4e, 0xfd, 0x87, 0x33, 0xa3, 0x20, 0x31, 0x2a, 0x82, 0x7c, 0x01, 0x44, 0x58, 0x97, 0x20, 0xc9, - 0x62, 0xcd, 0xd2, 0xfc, 0xd1, 0x7c, 0x65, 0x83, 0x3e, 0x76, 0xcc, 0xbc, 0x24, 0xfc, 0x5f, 0x3d, - 0x38, 0xb9, 0xbe, 0x95, 0xa8, 0x6e, 0x45, 0xbc, 0xf8, 0x3f, 0xf3, 0x7a, 0x0e, 0x7b, 0xba, 0xf0, - 0x09, 0x56, 0x61, 0x9c, 0xa1, 0x4b, 0xaa, 0x5f, 0xc2, 0xaf, 0x73, 0xd4, 0xff, 0xdd, 0x83, 0xa3, - 0x2d, 0x31, 0x15, 0x45, 0x95, 0x0a, 0xae, 0x90, 0x3c, 0x83, 0xbe, 0x16, 0x3a, 0x8c, 0x83, 0xe8, - 0x36, 0xe4, 0x1c, 0x63, 0x65, 0x32, 0xda, 0xa1, 0x3d, 0x83, 0x9e, 0x39, 0x90, 0x9c, 0xc2, 0x07, - 0x91, 0xe0, 0x8a, 0x2d, 0x50, 0xe2, 0xa2, 0xd2, 0x36, 0x8c, 0x96, 0x54, 0x54, 0x79, 0xe0, 0x1b, - 0xd8, 0x93, 0x75, 0xcb, 0x61, 0x73, 0xd4, 0x1c, 0x77, 0xa7, 0x07, 0xc5, 0xa7, 0x6e, 0x7c, 0xe5, - 0xa6, 0xdc, 0xe7, 0xd0, 0xaf, 0x4b, 0xc8, 0x31, 0x40, 0xee, 0x1c, 0xa4, 0x82, 0x71, 0x5b, 0xb9, - 0x0e, 0xed, 0xe4, 0xc8, 0x55, 0x0e, 0x90, 0x7d, 0xd8, 0x59, 0x2f, 0x85, 0x7d, 0xc9, 0x4b, 0x55, - 0x46, 0x0e, 0xa2, 0xbc, 0x14, 0xc3, 0xe6, 0xc8, 0x1b, 0xef, 0xd2, 0x7e, 0x09, 0x9b, 0x02, 0xf9, - 0x6f, 0x61, 0x9f, 0xe2, 0x0a, 0x79, 0x86, 0x14, 0x53, 0x21, 0x75, 0x51, 0xeb, 0x13, 0xe8, 0x56, - 0xae, 0x79, 0x79, 0x9a, 0xe3, 0x0e, 0x85, 0xd2, 0x56, 0xe5, 0x69, 0x29, 0x1d, 0x4a, 0x1d, 0x68, - 0x96, 0x58, 0xf3, 0x47, 0xb4, 0x63, 0x90, 0x6b, 0x96, 0x20, 0xf9, 0x08, 0x76, 0x73, 0x6b, 0x43, - 0x36, 0x0d, 0xd9, 0x46, 0xbe, 0xc8, 0x29, 0xff, 0x5b, 0x78, 0xb2, 0x61, 0xe9, 0xba, 0x72, 0x0a, - 0x6d, 0x69, 0x10, 0xeb, 0xd7, 0x9d, 0x3e, 0xa9, 0xaa, 0xb6, 0xae, 0x2f, 0x54, 0xfe, 0xdf, 0x1e, - 0xf4, 0x6a, 0x94, 0x69, 0x6c, 0x28, 0x97, 0xa8, 0x8b, 0x6e, 0xb9, 0x82, 0xf5, 0x2c, 0xea, 0x1a, - 0x45, 0xce, 0xe1, 0xfd, 0x34, 0x64, 0x32, 0x28, 0xec, 0x1a, 0xc6, 0xee, 0x93, 0xad, 0x76, 0x93, - 0xab, 0x90, 0x49, 0xfb, 0xa8, 0x66, 0x5c, 0xcb, 0x77, 0xb4, 0x9b, 0x56, 0xc8, 0x21, 0x85, 0xc1, - 0xa6, 0x80, 0x0c, 0xa0, 0xf9, 0x33, 0xbe, 0x73, 0xd6, 0xf9, 0x23, 0x19, 0xaf, 0x77, 0xa9, 0x3b, - 0x25, 0x85, 0x53, 0x75, 0xd4, 0x75, 0xee, 0xeb, 0xc6, 0x57, 0x9e, 0xff, 0xa7, 0x07, 0x50, 0x31, - 0xe4, 0x05, 0xec, 0x87, 0x89, 0xc8, 0xb8, 0x0e, 0x44, 0xa6, 0x97, 0x82, 0xf1, 0x65, 0x90, 0xa8, - 0x50, 0xbb, 0xcd, 0x44, 0x2c, 0x77, 0xe9, 0xa8, 0xb9, 0x0a, 0x35, 0xf9, 0x1c, 0xc8, 0x1b, 0x44, - 0xb5, 0xa1, 0x6f, 0xd8, 0x4d, 0x96, 0x33, 0x35, 0x75, 0x15, 0x9f, 0xf1, 0x48, 0x24, 0xa5, 0xbe, - 0xb9, 0x1e, 0xff, 0xdc, 0x51, 0xb5, 0xf8, 0x75, 0xfd, 0xa3, 0x2a, 0xfe, 0xba, 0xda, 0x1f, 0xc2, - 0x81, 0x2b, 0xfc, 0x39, 0x57, 0x6c, 0x79, 0xab, 0x8b, 0xb5, 0xe0, 0xff, 0x08, 0x1f, 0xfe, 0x8b, - 0x71, 0xc3, 0xf0, 0x12, 0x06, 0xae, 0x85, 0x01, 0x73, 0x9c, 0x9b, 0x8a, 0xf2, 0xb7, 0x54, 0x3f, - 0x4a, 0xf7, 0xa2, 0x7a, 0x28, 0xff, 0xaf, 0x06, 0xf4, 0xeb, 0x9a, 0x87, 0x7e, 0x4c, 0xf9, 0x05, - 0x50, 0x2c, 0xf8, 0x40, 0x61, 0x24, 0xf8, 0x42, 0xb9, 0xd9, 0x1e, 0x94, 0xc4, 0x2b, 0x8b, 0xe7, - 0xb3, 0x96, 0xa5, 0xf9, 0x80, 0x97, 0x4a, 0x3b, 0xe8, 0x3d, 0x8b, 0x16, 0xb2, 0x17, 0xb0, 0xbf, - 0x12, 0x71, 0x96, 0xe0, 0xd6, 0x6a, 0x11, 0xcb, 0xd5, 0xaa, 0x5b, 0x9d, 0xa8, 0xf7, 0x6f, 0x67, - 0xfd, 0x44, 0xad, 0x83, 0x63, 0x30, 0x55, 0x0f, 0x30, 0x94, 0x1c, 0x17, 0x56, 0xdd, 0x32, 0xea, - 0x7e, 0x8e, 0xcf, 0x0c, 0x6c, 0x94, 0x4f, 0xa1, 0x17, 0x09, 0xfe, 0x86, 0xc9, 0xc4, 0xed, 0xa7, - 0xf6, 0xc8, 0x1b, 0xf7, 0x68, 0x1d, 0x24, 0x43, 0x68, 0xa7, 0x92, 0xad, 0x42, 0x8d, 0xc3, 0x5d, - 0xb3, 0x36, 0x8a, 0xd7, 0xe9, 0x2f, 0x4d, 0x18, 0x5c, 0xa3, 0x4c, 0x18, 0x0f, 0xb5, 0x90, 0xaf, - 0x50, 0xae, 0x50, 0x12, 0x84, 0x83, 0xed, 0xd7, 0x12, 0x79, 0x56, 0xf4, 0xea, 0xde, 0x6b, 0xeb, - 0xf0, 0xe9, 0x3d, 0x37, 0x41, 0x35, 0x12, 0x0c, 0x86, 0x77, 0xdd, 0x33, 0xe4, 0x79, 0x11, 0xe1, - 0x81, 0x9b, 0xe8, 0x3f, 0x5a, 0x7d, 0xbf, 0xb9, 0x58, 0x8e, 0xb6, 0xaf, 0x22, 0x17, 0xf4, 0xf8, - 0x0e, 0xd6, 0x45, 0xa3, 0xb0, 0xb7, 0x31, 0xe6, 0xe4, 0xe3, 0xed, 0x43, 0x5c, 0xa6, 0x79, 0x72, - 0x27, 0x6f, 0x63, 0xfe, 0xd4, 0x32, 0xff, 0x68, 0xbe, 0xfc, 0x27, 0x00, 0x00, 0xff, 0xff, 0x67, - 0x57, 0x82, 0x18, 0x04, 0x09, 0x00, 0x00, + // 944 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x4f, 0x6f, 0xe3, 0x44, + 0x14, 0xc7, 0x71, 0x9b, 0xb6, 0x2f, 0x9b, 0xd4, 0x3b, 0xdb, 0x2d, 0xa1, 0x6a, 0x69, 0x64, 0xed, + 0xb2, 0x41, 0x40, 0xba, 0x0a, 0x17, 0xc4, 0x89, 0xaa, 0x8a, 0x96, 0x88, 0x26, 0xa9, 0x66, 0x93, + 0x72, 0x41, 0xb2, 0x8c, 0x33, 0x9b, 0x0e, 0xc4, 0x33, 0xde, 0xf1, 0x38, 0xd2, 0x7e, 0x01, 0xae, + 0x1c, 0xf8, 0x32, 0x7c, 0x08, 0x2e, 0x1c, 0xf8, 0x2e, 0x1c, 0xd1, 0xfc, 0xb1, 0x1d, 0x87, 0x74, + 0xcb, 0x85, 0x9b, 0xe7, 0xf7, 0xfb, 0xcd, 0xfb, 0xcd, 0xbc, 0xf7, 0x32, 0x2f, 0x70, 0x20, 0x92, + 0xa8, 0x97, 0x08, 0x2e, 0x39, 0xaa, 0x4b, 0x11, 0x8b, 0x24, 0x3a, 0x39, 0x5d, 0x70, 0xbe, 0x58, + 0x92, 0x8b, 0x30, 0xa1, 0x17, 0x21, 0x63, 0x5c, 0x86, 0x92, 0x72, 0x96, 0x1a, 0x95, 0xff, 0xb7, + 0x03, 0x27, 0x57, 0x4b, 0x9e, 0x12, 0x4c, 0x22, 0x1e, 0xc7, 0x84, 0xcd, 0x35, 0x8d, 0xc9, 0xdb, + 0x8c, 0xa4, 0x12, 0x7d, 0x06, 0x8f, 0x63, 0xca, 0x68, 0x9c, 0xc5, 0x41, 0xcc, 0x19, 0x95, 0x5c, + 0x90, 0x79, 0xdb, 0xe9, 0x38, 0x5d, 0x17, 0x7b, 0x96, 0x18, 0xe5, 0x38, 0xba, 0x84, 0x7a, 0x4c, + 0xa4, 0xa0, 0x51, 0xbb, 0xd6, 0x71, 0xba, 0xad, 0xfe, 0xa7, 0x3d, 0x73, 0x84, 0xde, 0xfd, 0x06, + 0xbd, 0x91, 0xde, 0x80, 0xed, 0x46, 0xff, 0x27, 0xa8, 0x1b, 0x04, 0x35, 0x60, 0x6f, 0x36, 0xfe, + 0x6e, 0x3c, 0xf9, 0x7e, 0xec, 0x7d, 0x80, 0x00, 0xea, 0xb3, 0x9b, 0xe9, 0x70, 0x34, 0xf0, 0x1c, + 0x45, 0xe0, 0xc1, 0xed, 0x60, 0x3c, 0x1b, 0x78, 0x35, 0xf4, 0x04, 0x0e, 0x87, 0xe3, 0xab, 0xc9, + 0x68, 0x38, 0x7e, 0x15, 0xdc, 0x4e, 0xae, 0x67, 0xa3, 0x81, 0xe7, 0x2a, 0x70, 0x32, 0x9b, 0xbe, + 0x9a, 0xac, 0x81, 0x3b, 0xc8, 0x83, 0x47, 0xd3, 0xc9, 0xf4, 0xf2, 0x3a, 0x47, 0x76, 0xfd, 0xdf, + 0x1c, 0x38, 0x9b, 0x64, 0x72, 0x49, 0x89, 0xa8, 0x9e, 0x2d, 0xcd, 0x6f, 0x7f, 0x05, 0x0d, 0x41, + 0xa2, 0x40, 0x98, 0xa5, 0xbe, 0x77, 0xa3, 0xef, 0x3f, 0x7c, 0x2b, 0x0c, 0x82, 0x44, 0x79, 0x90, + 0x2f, 0x00, 0x71, 0xe3, 0x12, 0xc4, 0xd9, 0x52, 0xd2, 0x44, 0x7d, 0xea, 0x0c, 0xd5, 0xf0, 0x63, + 0xcb, 0x8c, 0x0a, 0xc2, 0xff, 0xd5, 0x81, 0xf3, 0xe9, 0x9d, 0x20, 0xe9, 0x1d, 0x5f, 0xce, 0xff, + 0xcf, 0x73, 0xbd, 0x80, 0x43, 0x99, 0xfb, 0x04, 0xab, 0x70, 0x99, 0x11, 0x7b, 0xa8, 0x56, 0x01, + 0xdf, 0x2a, 0xd4, 0xff, 0xdd, 0x81, 0xd3, 0x2d, 0x31, 0x53, 0x4c, 0xd2, 0x84, 0xb3, 0x94, 0xa0, + 0xe7, 0xd0, 0x92, 0x5c, 0x86, 0xcb, 0x20, 0xba, 0x0b, 0x19, 0x23, 0xcb, 0x54, 0x9f, 0x68, 0x17, + 0x37, 0x35, 0x7a, 0x65, 0x41, 0x74, 0x01, 0x4f, 0x22, 0xce, 0x52, 0x3a, 0x27, 0x82, 0xcc, 0x4b, + 0x6d, 0x4d, 0x6b, 0x51, 0x49, 0x15, 0x1b, 0xbe, 0x81, 0x43, 0x51, 0xb5, 0x6c, 0xbb, 0x1d, 0xb7, + 0xdb, 0xe8, 0x1f, 0xe7, 0x57, 0xdd, 0xb8, 0xe5, 0xa6, 0xdc, 0x67, 0xd0, 0xaa, 0x4a, 0xd0, 0x19, + 0x80, 0x72, 0x0e, 0x12, 0x4e, 0x99, 0xc9, 0xdc, 0x01, 0x3e, 0x50, 0xc8, 0x8d, 0x02, 0xd0, 0x11, + 0xec, 0xae, 0xa7, 0xc2, 0x2c, 0x54, 0xaa, 0x8a, 0xc8, 0x41, 0xa4, 0x52, 0xd1, 0x76, 0x3b, 0x4e, + 0x77, 0x1f, 0xb7, 0x0a, 0x58, 0x27, 0xc8, 0x7f, 0x0b, 0x47, 0x98, 0xac, 0x08, 0xcb, 0x08, 0x26, + 0x09, 0x17, 0x32, 0xcf, 0xf5, 0x39, 0x34, 0x4a, 0x57, 0x95, 0x1e, 0xb7, 0x7b, 0x80, 0xa1, 0xb0, + 0x4d, 0xd5, 0xb1, 0x52, 0x19, 0x0a, 0x19, 0x48, 0x1a, 0x1b, 0xf3, 0x1d, 0x7c, 0xa0, 0x91, 0x29, + 0x8d, 0x09, 0xfa, 0x08, 0xf6, 0x95, 0xb5, 0x26, 0x5d, 0x4d, 0xee, 0x11, 0x36, 0x57, 0x94, 0xff, + 0x2d, 0x3c, 0xdd, 0xb0, 0xb4, 0x55, 0xb9, 0x80, 0x3d, 0xa1, 0x11, 0xe3, 0xd7, 0xe8, 0x3f, 0x2d, + 0xb3, 0xb6, 0xae, 0xcf, 0x55, 0xfe, 0x5f, 0x0e, 0x34, 0x2b, 0x94, 0x2e, 0x6c, 0x28, 0x16, 0x44, + 0xe6, 0xd5, 0xb2, 0x09, 0x6b, 0x1a, 0xd4, 0x16, 0x0a, 0x0d, 0xe1, 0x51, 0x12, 0x52, 0x11, 0xe4, + 0x76, 0x35, 0x6d, 0xf7, 0xc9, 0x56, 0xbb, 0xde, 0x4d, 0x48, 0x85, 0xf9, 0x4c, 0x07, 0x4c, 0x8a, + 0x77, 0xb8, 0x91, 0x94, 0xc8, 0x09, 0x06, 0x6f, 0x53, 0x80, 0x3c, 0x70, 0x7f, 0x26, 0xef, 0xac, + 0xb5, 0xfa, 0x44, 0xdd, 0xf5, 0x2a, 0x35, 0xfa, 0x28, 0x77, 0x2a, 0xb7, 0xda, 0xca, 0x7d, 0x5d, + 0xfb, 0xca, 0xf1, 0xff, 0x70, 0x00, 0x4a, 0x06, 0xbd, 0x84, 0xa3, 0x30, 0xe6, 0x19, 0x93, 0x01, + 0xcf, 0xe4, 0x82, 0x53, 0xb6, 0x08, 0xe2, 0x34, 0x94, 0xf6, 0x55, 0x43, 0x86, 0x9b, 0x58, 0x6a, + 0x94, 0x86, 0x12, 0x7d, 0x0e, 0xe8, 0x0d, 0x21, 0xe9, 0x86, 0xbe, 0x66, 0x5e, 0x41, 0xc5, 0x54, + 0xd4, 0x65, 0x7c, 0xca, 0x22, 0x1e, 0x17, 0x7a, 0x77, 0x3d, 0xfe, 0xd0, 0x52, 0x95, 0xf8, 0x55, + 0xfd, 0x4e, 0x19, 0x7f, 0x5d, 0xed, 0xb7, 0xe1, 0xd8, 0x26, 0x7e, 0xc8, 0x52, 0xba, 0xb8, 0x93, + 0xf9, 0xb3, 0xe0, 0xff, 0x00, 0x1f, 0xfe, 0x8b, 0xb1, 0xcd, 0x70, 0x09, 0x9e, 0x2d, 0x61, 0x40, + 0x2d, 0x67, 0xbb, 0xa2, 0xf8, 0x2d, 0x55, 0xb7, 0xe2, 0xc3, 0xa8, 0x1a, 0xca, 0xff, 0xb3, 0x06, + 0xad, 0xaa, 0xe6, 0xa1, 0x1f, 0x93, 0x1a, 0x1e, 0xf9, 0x70, 0x08, 0x52, 0x12, 0x71, 0x36, 0x4f, + 0x6d, 0x6f, 0x7b, 0x05, 0xf1, 0xda, 0xe0, 0xaa, 0xd7, 0xb2, 0x44, 0x35, 0x78, 0xa1, 0x34, 0x8d, + 0xde, 0x34, 0x68, 0x2e, 0x7b, 0x09, 0x47, 0x2b, 0xbe, 0xcc, 0x62, 0xb2, 0x35, 0x5b, 0xc8, 0x70, + 0x95, 0xec, 0x96, 0x3b, 0xaa, 0xf5, 0xdb, 0x5d, 0xdf, 0x51, 0xa9, 0x60, 0x17, 0x74, 0xd6, 0x03, + 0x12, 0x0a, 0x46, 0xe6, 0x46, 0x5d, 0xd7, 0xea, 0x96, 0xc2, 0x07, 0x1a, 0xd6, 0xca, 0x67, 0xd0, + 0x8c, 0x38, 0x7b, 0x43, 0x45, 0x6c, 0xdf, 0xa7, 0xbd, 0x8e, 0xd3, 0x6d, 0xe2, 0x2a, 0x88, 0xda, + 0xb0, 0x97, 0x08, 0xba, 0x0a, 0x25, 0x69, 0xef, 0xeb, 0x67, 0x23, 0x5f, 0xf6, 0x7f, 0x71, 0xc1, + 0x9b, 0x12, 0x11, 0x53, 0x16, 0x4a, 0x2e, 0x5e, 0x13, 0xb1, 0x22, 0x02, 0x11, 0x38, 0xde, 0x3e, + 0x96, 0xd0, 0xf3, 0xbc, 0x56, 0xef, 0x1d, 0x5b, 0x27, 0xcf, 0xde, 0x33, 0x09, 0xca, 0x96, 0xa0, + 0xd0, 0xbe, 0x6f, 0xce, 0xa0, 0x17, 0x79, 0x84, 0x07, 0x26, 0xd1, 0x7f, 0xb4, 0xba, 0xde, 0x7c, + 0x58, 0x4e, 0xb7, 0x3f, 0x45, 0x36, 0xe8, 0xd9, 0x3d, 0xac, 0x8d, 0x86, 0xe1, 0x70, 0xa3, 0xcd, + 0xd1, 0xc7, 0xdb, 0x9b, 0xb8, 0x38, 0xe6, 0xf9, 0xbd, 0xbc, 0x89, 0xf9, 0x63, 0x5d, 0xff, 0x1b, + 0xfa, 0xf2, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0b, 0xdf, 0x60, 0xc4, 0x40, 0x09, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/trmrpc/rpc.proto b/trmrpc/rpc.proto index 9b17e00..c6338fd 100644 --- a/trmrpc/rpc.proto +++ b/trmrpc/rpc.proto @@ -23,6 +23,9 @@ message CloseRecommendationRequest { UNKNOWN = 0; UPTIME = 1; REVENUE = 2; + INCOMING_VOLUME = 3; + OUTGOING_VOLUME = 4; + TOTAL_VOLUME = 5; } /* @@ -61,10 +64,26 @@ message ThresholdRecommendationsRequest { The threshold that recommendations will be calculated based on. For uptime: ratio of uptime to observed lifetime beneath which channels will be recommended for closure. + For revenue: revenue per block that capital has been committed to the channel beneath which channels will be recommended for closure. This value is provided per block so that channels that have been open for different periods of time can be compared. + + For incoming volume: The incoming volume per block that capital has + been committed to the channel beneath which channels will be recommended + for closure. This value is provided per block so that channels that have + been open for different periods of time can be compared. + + For outgoing volume: The outgoing volume per block that capital has been + committed to the channel beneath which channels will be recommended for + closure. This value is provided per block so that channels that have been + open for different periods of time can be compared. + + For total volume: The total volume per block that capital has been + committed to the channel beneath which channels will be recommended for + closure. This value is provided per block so that channels that have been + open for different periods of time can be compared. */ float threshold_value = 2; }