@@ -11,6 +11,7 @@ import (
1111
1212 "github.com/lightninglabs/lightning-terminal/litrpc"
1313 "github.com/lightninglabs/lightning-terminal/rules"
14+ "github.com/lightningnetwork/lnd/lnrpc"
1415 "github.com/urfave/cli"
1516)
1617
@@ -39,10 +40,48 @@ var addAutopilotSessionCmd = cli.Command{
3940 Name : "add" ,
4041 ShortName : "a" ,
4142 Usage : "Initialize an Autopilot session." ,
42- Description : "Initialize an Autopilot session.\n \n " +
43- " If set for any feature, configuration flags need to be " +
44- "repeated for each feature that is registered, corresponding " +
45- "to the order of features." ,
43+ Description : `
44+ Initialize an Autopilot session.
45+
46+ If one of the 'feature-' flags is set for any 'feature', then that flag
47+ must be provided for each 'feature'.
48+
49+ The rules and configuration options available for each feature can be
50+ seen in the 'autopilot features' output.
51+
52+ An example call reads:
53+
54+ ./litcli autopilot add --label=customRules \
55+ --feature=SampleFeature \
56+ --feature-rules='{
57+ "rules": {
58+ "rate-limit": {
59+ "rate_limit": {
60+ "read_limit": {
61+ "iterations": 10,
62+ "num_hours": 1
63+ },
64+ "write_limit": {
65+ "iterations": 1,
66+ "num_hours": 1
67+ }
68+ }
69+ },
70+ "channel-restriction": {
71+ "channel_restrict": {
72+ "channel_ids": [
73+ 18283782789,
74+ 22891928322,
75+ 31878293727
76+ ]
77+ }
78+ }
79+ }
80+ }' \
81+ --feature-config='{
82+ "version": 0,
83+ "complexity": "complex"
84+ }'` ,
4685 Action : initAutopilotSession ,
4786 Flags : []cli.Flag {
4887 labelFlag ,
@@ -55,17 +94,19 @@ var addAutopilotSessionCmd = cli.Command{
5594 },
5695 cli.StringFlag {
5796 Name : "channel-restrict-list" ,
58- Usage : "List of channel IDs that the " +
97+ Usage : "[deprecated] List of channel IDs that the " +
5998 "Autopilot server should not " +
6099 "perform actions on. In the " +
61100 "form of: chanID1,chanID2,..." ,
101+ Hidden : true ,
62102 },
63103 cli.StringFlag {
64104 Name : "peer-restrict-list" ,
65- Usage : "List of peer IDs that the " +
105+ Usage : "[deprecated] List of peer IDs that the " +
66106 "Autopilot server should not " +
67107 "perform actions on. In the " +
68108 "form of: peerID1,peerID2,..." ,
109+ Hidden : true ,
69110 },
70111 cli.StringFlag {
71112 Name : "group_id" ,
@@ -81,6 +122,13 @@ var addAutopilotSessionCmd = cli.Command{
81122 "configuration is allowed with {} to use the " +
82123 "default configuration." ,
83124 },
125+ cli.StringSliceFlag {
126+ Name : "feature-rules" ,
127+ Usage : `JSON-serialized rule map (see main ` +
128+ `description for a format example).` +
129+ `An empty rule map is allowed with {} to ` +
130+ `use the default rules.` ,
131+ },
84132 },
85133}
86134
@@ -190,74 +238,137 @@ func initAutopilotSession(ctx *cli.Context) error {
190238 defer cleanup ()
191239 client := litrpc .NewAutopilotClient (clientConn )
192240
193- ruleMap := & litrpc.RulesMap {
194- Rules : make (map [string ]* litrpc.RuleValue ),
195- }
241+ features := ctx .StringSlice ("feature" )
196242
243+ rulesFlags := ctx .StringSlice ("feature-rules" )
197244 chanRestrictList := ctx .String ("channel-restrict-list" )
198- if chanRestrictList != "" {
199- var chanIDs []uint64
200- chans := strings .Split (chanRestrictList , "," )
201- for _ , c := range chans {
202- i , err := strconv .ParseUint (c , 10 , 64 )
203- if err != nil {
204- return err
205- }
206- chanIDs = append (chanIDs , i )
245+ peerRestrictList := ctx .String ("peer-restrict-list" )
246+
247+ // rulesMap stores the rules per each feature.
248+ rulesMap := make (map [string ]* litrpc.RulesMap )
249+
250+ // For legacy flags, we allow setting the channel and peer restrict
251+ // lists when only a single feature is added.
252+ if chanRestrictList != "" || peerRestrictList != "" {
253+ // Check that the user did not set both the legacy flags and the
254+ // generic rules flags together.
255+ if len (rulesFlags ) > 0 {
256+ return fmt .Errorf ("either set channel-restrict-list/" +
257+ "peer-restrict-list or feature-rules, not both" )
258+ }
259+
260+ if len (features ) > 1 {
261+ return fmt .Errorf ("cannot set channel-restrict-list/" +
262+ "peer-restrict-list when multiple features " +
263+ "are set" )
207264 }
208265
209- ruleMap .Rules [rules .ChannelRestrictName ] = & litrpc.RuleValue {
210- Value : & litrpc.RuleValue_ChannelRestrict {
211- ChannelRestrict : & litrpc.ChannelRestrict {
212- ChannelIds : chanIDs ,
266+ feature := features [0 ]
267+
268+ // Init the rule map for this feature.
269+ ruleMap := make (map [string ]* litrpc.RuleValue )
270+
271+ if chanRestrictList != "" {
272+ var chanIDs []uint64
273+ chans := strings .Split (chanRestrictList , "," )
274+ for _ , c := range chans {
275+ i , err := strconv .ParseUint (c , 10 , 64 )
276+ if err != nil {
277+ return err
278+ }
279+ chanIDs = append (chanIDs , i )
280+ }
281+
282+ channelRestrict := & litrpc.ChannelRestrict {
283+ ChannelIds : chanIDs ,
284+ }
285+
286+ ruleMap [rules .ChannelRestrictName ] = & litrpc.RuleValue {
287+ Value : & litrpc.RuleValue_ChannelRestrict {
288+ ChannelRestrict : channelRestrict ,
213289 },
214- },
290+ }
215291 }
216- }
217292
218- peerRestrictList := ctx .String ("peer-restrict-list" )
219- if peerRestrictList != "" {
220- peerIDs := strings .Split (peerRestrictList , "," )
293+ if peerRestrictList != "" {
294+ peerIDs := strings .Split (peerRestrictList , "," )
221295
222- ruleMap .Rules [rules .PeersRestrictName ] = & litrpc.RuleValue {
223- Value : & litrpc.RuleValue_PeerRestrict {
224- PeerRestrict : & litrpc.PeerRestrict {
225- PeerIds : peerIDs ,
296+ ruleMap [rules .PeersRestrictName ] = & litrpc.RuleValue {
297+ Value : & litrpc.RuleValue_PeerRestrict {
298+ PeerRestrict : & litrpc.PeerRestrict {
299+ PeerIds : peerIDs ,
300+ },
226301 },
227- },
302+ }
303+ }
304+
305+ rulesMap [feature ] = & litrpc.RulesMap {Rules : ruleMap }
306+
307+ } else {
308+ // We make sure that if the rules or configs flags are set, they
309+ // are set for all features, to avoid ambiguity.
310+ if len (rulesFlags ) > 0 && len (features ) != len (rulesFlags ) {
311+ return fmt .Errorf ("number of features (%v) and rules " +
312+ "(%v) must match" , len (features ),
313+ len (rulesFlags ))
314+ }
315+
316+ // Parse the rules and store them in the rulesMap.
317+ for i , rulesFlag := range rulesFlags {
318+ var ruleMap litrpc.RulesMap
319+
320+ // We allow empty rules, to signal the usage of the
321+ // default rules when the session is registered.
322+ if rulesFlag != "{}" {
323+ err = lnrpc .ProtoJSONUnmarshalOpts .Unmarshal (
324+ []byte (rulesFlag ), & ruleMap ,
325+ )
326+ if err != nil {
327+ return err
328+ }
329+ }
330+
331+ rulesMap [features [i ]] = & ruleMap
228332 }
229333 }
230334
231- features := ctx .StringSlice ("feature" )
232335 configs := ctx .StringSlice ("feature-config" )
233336 if len (configs ) > 0 && len (features ) != len (configs ) {
234337 return fmt .Errorf ("number of features (%v) and configurations " +
235338 "(%v) must match" , len (features ), len (configs ))
236339 }
237340
238- featureMap := make (map [string ]* litrpc.FeatureConfig )
239- for i , feature := range ctx .StringSlice ("feature" ) {
341+ // Parse the configs and store them in the configsMap.
342+ configsMap := make (map [string ][]byte )
343+ for i , configFlag := range configs {
240344 var config []byte
241345
242346 // We allow empty configs, to signal the usage of the default
243347 // configuration when the session is registered.
244- if len ( configs ) > 0 && configs [ i ] != "{}" {
348+ if configFlag != "{}" {
245349 // We expect the config to be a JSON dictionary, so we
246350 // unmarshal it into a map to do a first validation.
247351 var configMap map [string ]interface {}
248352 err := json .Unmarshal ([]byte (configs [i ]), & configMap )
249353 if err != nil {
250354 return fmt .Errorf ("could not parse " +
251355 "configuration for feature %v: %v" ,
252- feature , err )
356+ features [ i ] , err )
253357 }
254358
255359 config = []byte (configs [i ])
256360 }
257361
362+ configsMap [features [i ]] = config
363+ }
364+
365+ featureMap := make (map [string ]* litrpc.FeatureConfig )
366+ for _ , feature := range features {
367+ // Map access for unknown features will return their zero value
368+ // if not set, which is what we want to signal default usage.
258369 featureMap [feature ] = & litrpc.FeatureConfig {
259- Rules : ruleMap ,
260- Config : config ,
370+ Rules : rulesMap [ feature ] ,
371+ Config : configsMap [ feature ] ,
261372 }
262373 }
263374
0 commit comments