github.com/decred/dcrlnd@v0.7.6/cmd/dcrlncli/wtclient.go (about) 1 package main 2 3 import ( 4 "encoding/hex" 5 "errors" 6 "fmt" 7 "strings" 8 9 "github.com/decred/dcrlnd/lnrpc/wtclientrpc" 10 "github.com/urfave/cli" 11 ) 12 13 // wtclientCommands will return nil for non-wtclientrpc builds. 14 func wtclientCommands() []cli.Command { 15 return []cli.Command{ 16 { 17 Name: "wtclient", 18 Usage: "Interact with the watchtower client.", 19 Category: "Watchtower", 20 Subcommands: []cli.Command{ 21 addTowerCommand, 22 removeTowerCommand, 23 listTowersCommand, 24 getTowerCommand, 25 statsCommand, 26 policyCommand, 27 }, 28 }, 29 } 30 } 31 32 // getWtclient initializes a connection to the watchtower client RPC in order to 33 // interact with it. 34 func getWtclient(ctx *cli.Context) (wtclientrpc.WatchtowerClientClient, func()) { 35 conn := getClientConn(ctx, false) 36 cleanUp := func() { 37 conn.Close() 38 } 39 return wtclientrpc.NewWatchtowerClientClient(conn), cleanUp 40 } 41 42 var addTowerCommand = cli.Command{ 43 Name: "add", 44 Usage: "Register a watchtower to use for future sessions/backups.", 45 Description: "If the watchtower has already been registered, then " + 46 "this command serves as a way of updating the watchtower " + 47 "with new addresses it is reachable over.", 48 ArgsUsage: "pubkey@address", 49 Action: actionDecorator(addTower), 50 } 51 52 func addTower(ctx *cli.Context) error { 53 ctxc := getContext() 54 55 // Display the command's help message if the number of arguments/flags 56 // is not what we expect. 57 if ctx.NArg() != 1 || ctx.NumFlags() > 0 { 58 return cli.ShowCommandHelp(ctx, "add") 59 } 60 61 parts := strings.Split(ctx.Args().First(), "@") 62 if len(parts) != 2 { 63 return errors.New("expected tower of format pubkey@address") 64 } 65 pubKey, err := hex.DecodeString(parts[0]) 66 if err != nil { 67 return fmt.Errorf("invalid public key: %v", err) 68 } 69 address := parts[1] 70 71 client, cleanUp := getWtclient(ctx) 72 defer cleanUp() 73 74 req := &wtclientrpc.AddTowerRequest{ 75 Pubkey: pubKey, 76 Address: address, 77 } 78 resp, err := client.AddTower(ctxc, req) 79 if err != nil { 80 return err 81 } 82 83 printRespJSON(resp) 84 return nil 85 } 86 87 var removeTowerCommand = cli.Command{ 88 Name: "remove", 89 Usage: "Remove a watchtower to prevent its use for future " + 90 "sessions/backups.", 91 Description: "An optional address can be provided to remove, " + 92 "indicating that the watchtower is no longer reachable at " + 93 "this address. If an address isn't provided, then the " + 94 "watchtower will no longer be used for future sessions/backups.", 95 ArgsUsage: "pubkey | pubkey@address", 96 Action: actionDecorator(removeTower), 97 } 98 99 func removeTower(ctx *cli.Context) error { 100 ctxc := getContext() 101 102 // Display the command's help message if the number of arguments/flags 103 // is not what we expect. 104 if ctx.NArg() != 1 || ctx.NumFlags() > 0 { 105 return cli.ShowCommandHelp(ctx, "remove") 106 } 107 108 // The command can have only one argument, but it can be interpreted in 109 // either of the following formats: 110 // 111 // pubkey or pubkey@address 112 // 113 // The hex-encoded public key of the watchtower is always required, 114 // while the second is an optional address we'll remove from the 115 // watchtower's database record. 116 parts := strings.Split(ctx.Args().First(), "@") 117 if len(parts) > 2 { 118 return errors.New("expected tower of format pubkey@address") 119 } 120 pubKey, err := hex.DecodeString(parts[0]) 121 if err != nil { 122 return fmt.Errorf("invalid public key: %v", err) 123 } 124 var address string 125 if len(parts) == 2 { 126 address = parts[1] 127 } 128 129 client, cleanUp := getWtclient(ctx) 130 defer cleanUp() 131 132 req := &wtclientrpc.RemoveTowerRequest{ 133 Pubkey: pubKey, 134 Address: address, 135 } 136 resp, err := client.RemoveTower(ctxc, req) 137 if err != nil { 138 return err 139 } 140 141 printRespJSON(resp) 142 return nil 143 } 144 145 var listTowersCommand = cli.Command{ 146 Name: "towers", 147 Usage: "Display information about all registered watchtowers.", 148 Flags: []cli.Flag{ 149 cli.BoolFlag{ 150 Name: "include_sessions", 151 Usage: "include sessions with the watchtower in the " + 152 "response", 153 }, 154 }, 155 Action: actionDecorator(listTowers), 156 } 157 158 func listTowers(ctx *cli.Context) error { 159 ctxc := getContext() 160 161 // Display the command's help message if the number of arguments/flags 162 // is not what we expect. 163 if ctx.NArg() > 0 || ctx.NumFlags() > 1 { 164 return cli.ShowCommandHelp(ctx, "towers") 165 } 166 167 client, cleanUp := getWtclient(ctx) 168 defer cleanUp() 169 170 req := &wtclientrpc.ListTowersRequest{ 171 IncludeSessions: ctx.Bool("include_sessions"), 172 } 173 resp, err := client.ListTowers(ctxc, req) 174 if err != nil { 175 return err 176 } 177 178 printRespJSON(resp) 179 180 return nil 181 } 182 183 var getTowerCommand = cli.Command{ 184 Name: "tower", 185 Usage: "Display information about a specific registered watchtower.", 186 ArgsUsage: "pubkey", 187 Flags: []cli.Flag{ 188 cli.BoolFlag{ 189 Name: "include_sessions", 190 Usage: "include sessions with the watchtower in the " + 191 "response", 192 }, 193 }, 194 Action: actionDecorator(getTower), 195 } 196 197 func getTower(ctx *cli.Context) error { 198 ctxc := getContext() 199 200 // Display the command's help message if the number of arguments/flags 201 // is not what we expect. 202 if ctx.NArg() != 1 || ctx.NumFlags() > 1 { 203 return cli.ShowCommandHelp(ctx, "tower") 204 } 205 206 // The command only has one argument, which we expect to be the 207 // hex-encoded public key of the watchtower we'll display information 208 // about. 209 pubKey, err := hex.DecodeString(ctx.Args().Get(0)) 210 if err != nil { 211 return fmt.Errorf("invalid public key: %v", err) 212 } 213 214 client, cleanUp := getWtclient(ctx) 215 defer cleanUp() 216 217 req := &wtclientrpc.GetTowerInfoRequest{ 218 Pubkey: pubKey, 219 IncludeSessions: ctx.Bool("include_sessions"), 220 } 221 resp, err := client.GetTowerInfo(ctxc, req) 222 if err != nil { 223 return err 224 } 225 226 printRespJSON(resp) 227 return nil 228 } 229 230 var statsCommand = cli.Command{ 231 Name: "stats", 232 Usage: "Display the session stats of the watchtower client.", 233 Action: actionDecorator(stats), 234 } 235 236 func stats(ctx *cli.Context) error { 237 ctxc := getContext() 238 239 // Display the command's help message if the number of arguments/flags 240 // is not what we expect. 241 if ctx.NArg() > 0 || ctx.NumFlags() > 0 { 242 return cli.ShowCommandHelp(ctx, "stats") 243 } 244 245 client, cleanUp := getWtclient(ctx) 246 defer cleanUp() 247 248 req := &wtclientrpc.StatsRequest{} 249 resp, err := client.Stats(ctxc, req) 250 if err != nil { 251 return err 252 } 253 254 printRespJSON(resp) 255 return nil 256 } 257 258 var policyCommand = cli.Command{ 259 Name: "policy", 260 Usage: "Display the active watchtower client policy configuration.", 261 Action: actionDecorator(policy), 262 Flags: []cli.Flag{ 263 cli.BoolFlag{ 264 Name: "legacy", 265 Usage: "Retrieve the legacy tower client's current " + 266 "policy. (default)", 267 }, 268 cli.BoolFlag{ 269 Name: "anchor", 270 Usage: "Retrieve the anchor tower client's current policy.", 271 }, 272 }, 273 } 274 275 func policy(ctx *cli.Context) error { 276 ctxc := getContext() 277 278 // Display the command's help message if the number of arguments/flags 279 // is not what we expect. 280 if ctx.NArg() > 0 || ctx.NumFlags() > 1 { 281 return cli.ShowCommandHelp(ctx, "policy") 282 } 283 284 var policyType wtclientrpc.PolicyType 285 switch { 286 case ctx.Bool("anchor"): 287 policyType = wtclientrpc.PolicyType_ANCHOR 288 case ctx.Bool("legacy"): 289 policyType = wtclientrpc.PolicyType_LEGACY 290 291 // For backwards compatibility with original rpc behavior. 292 default: 293 policyType = wtclientrpc.PolicyType_LEGACY 294 } 295 296 client, cleanUp := getWtclient(ctx) 297 defer cleanUp() 298 299 req := &wtclientrpc.PolicyRequest{ 300 PolicyType: policyType, 301 } 302 resp, err := client.Policy(ctxc, req) 303 if err != nil { 304 return err 305 } 306 307 printRespJSON(resp) 308 return nil 309 }