Skip to content

Commit cbc0e81

Browse files
committed
cmd/litcli: update account CLI with label changes
1 parent 568d7ad commit cbc0e81

File tree

1 file changed

+126
-41
lines changed

1 file changed

+126
-41
lines changed

cmd/litcli/accounts.go

Lines changed: 126 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,17 @@ import (
77
"os"
88
"strconv"
99

10+
"github.com/lightninglabs/lightning-terminal/accounts"
1011
"github.com/lightninglabs/lightning-terminal/litrpc"
1112
"github.com/lightningnetwork/lnd/lncfg"
1213
"github.com/urfave/cli"
1314
)
1415

16+
const (
17+
idName = "id"
18+
labelName = "label"
19+
)
20+
1521
var accountsCommands = []cli.Command{
1622
{
1723
Name: "accounts",
@@ -22,6 +28,7 @@ var accountsCommands = []cli.Command{
2228
createAccountCommand,
2329
updateAccountCommand,
2430
listAccountsCommand,
31+
accountInfoCommand,
2532
removeAccountCommand,
2633
},
2734
},
@@ -31,7 +38,7 @@ var createAccountCommand = cli.Command{
3138
Name: "create",
3239
ShortName: "c",
3340
Usage: "Create a new off-chain account with a balance.",
34-
ArgsUsage: "balance [expiration_date]",
41+
ArgsUsage: "balance [expiration_date] [--label=LABEL] [--save_to=FILE]",
3542
Description: `
3643
Adds an entry to the account database. This entry represents an amount
3744
of satoshis (account balance) that can be spent using off-chain
@@ -61,6 +68,10 @@ var createAccountCommand = cli.Command{
6168
Usage: "store the account macaroon created for the " +
6269
"account to the given file",
6370
},
71+
cli.StringFlag{
72+
Name: labelName,
73+
Usage: "(optional) the unique label of the account",
74+
},
6475
},
6576
Action: createAccount,
6677
}
@@ -111,6 +122,7 @@ func createAccount(ctx *cli.Context) error {
111122
req := &litrpc.CreateAccountRequest{
112123
AccountBalance: initialBalance,
113124
ExpirationDate: expirationDate,
125+
Label: ctx.String(labelName),
114126
}
115127
resp, err := client.CreateAccount(ctxb, req)
116128
if err != nil {
@@ -139,16 +151,20 @@ var updateAccountCommand = cli.Command{
139151
Name: "update",
140152
ShortName: "u",
141153
Usage: "Update an existing off-chain account.",
142-
ArgsUsage: "id new_balance [new_expiration_date] [--save_to=]",
154+
ArgsUsage: "[id | label] new_balance [new_expiration_date] [--save_to=]",
143155
Description: `
144156
Updates an existing off-chain account and sets either a new balance or
145157
new expiration date or both.
146158
`,
147159
Flags: []cli.Flag{
148160
cli.StringFlag{
149-
Name: "id",
161+
Name: idName,
150162
Usage: "the ID of the account to update",
151163
},
164+
cli.StringFlag{
165+
Name: labelName,
166+
Usage: "(optional) the unique label of the account",
167+
},
152168
cli.Int64Flag{
153169
Name: "new_balance",
154170
Usage: "the new balance of the account; -1 means do " +
@@ -176,29 +192,15 @@ func updateAccount(ctx *cli.Context) error {
176192
defer cleanup()
177193
client := litrpc.NewAccountsClient(clientConn)
178194

195+
id, label, args, err := parseIDOrLabel(ctx)
196+
if err != nil {
197+
return err
198+
}
199+
179200
var (
180-
id []byte
181201
newBalance int64
182202
expirationDate int64
183203
)
184-
args := ctx.Args()
185-
186-
// We parse the ID as hex even though we're supposed to send it as a hex
187-
// encoded string over the RPC. But that way we can verify it's actually
188-
// the id.
189-
switch {
190-
case ctx.IsSet("id"):
191-
id, err = hex.DecodeString(ctx.String("id"))
192-
case args.Present():
193-
id, err = hex.DecodeString(args.First())
194-
args = args.Tail()
195-
default:
196-
return fmt.Errorf("id is missing")
197-
}
198-
if err != nil {
199-
return fmt.Errorf("error decoding id: %v", err)
200-
}
201-
202204
switch {
203205
case ctx.IsSet("new_balance"):
204206
newBalance = ctx.Int64("new_balance")
@@ -224,7 +226,8 @@ func updateAccount(ctx *cli.Context) error {
224226
}
225227

226228
req := &litrpc.UpdateAccountRequest{
227-
Id: hex.EncodeToString(id),
229+
Id: id,
230+
Label: label,
228231
AccountBalance: newBalance,
229232
ExpirationDate: expirationDate,
230233
}
@@ -267,19 +270,71 @@ func listAccounts(ctx *cli.Context) error {
267270
return nil
268271
}
269272

273+
var accountInfoCommand = cli.Command{
274+
Name: "info",
275+
ShortName: "i",
276+
Usage: "Show information about a single off-chain account.",
277+
ArgsUsage: "[id | label]",
278+
Description: `
279+
Returns a single account entry from the account database.
280+
`,
281+
Flags: []cli.Flag{
282+
cli.StringFlag{
283+
Name: idName,
284+
Usage: "the ID of the account",
285+
},
286+
cli.StringFlag{
287+
Name: labelName,
288+
Usage: "(optional) the unique label of the account",
289+
},
290+
},
291+
Action: accountInfo,
292+
}
293+
294+
func accountInfo(ctx *cli.Context) error {
295+
ctxb := context.Background()
296+
clientConn, cleanup, err := connectClient(ctx)
297+
if err != nil {
298+
return err
299+
}
300+
defer cleanup()
301+
client := litrpc.NewAccountsClient(clientConn)
302+
303+
id, label, _, err := parseIDOrLabel(ctx)
304+
if err != nil {
305+
return err
306+
}
307+
308+
req := &litrpc.AccountInfoRequest{
309+
Id: id,
310+
Label: label,
311+
}
312+
resp, err := client.AccountInfo(ctxb, req)
313+
if err != nil {
314+
return err
315+
}
316+
317+
printRespJSON(resp)
318+
return nil
319+
}
320+
270321
var removeAccountCommand = cli.Command{
271322
Name: "remove",
272323
ShortName: "r",
273324
Usage: "Removes an off-chain account from the database.",
274-
ArgsUsage: "id",
325+
ArgsUsage: "[id | label]",
275326
Description: `
276327
Removes an account entry from the account database.
277328
`,
278329
Flags: []cli.Flag{
279330
cli.StringFlag{
280-
Name: "id",
331+
Name: idName,
281332
Usage: "the ID of the account",
282333
},
334+
cli.StringFlag{
335+
Name: labelName,
336+
Usage: "(optional) the unique label of the account",
337+
},
283338
},
284339
Action: removeAccount,
285340
}
@@ -293,29 +348,59 @@ func removeAccount(ctx *cli.Context) error {
293348
defer cleanup()
294349
client := litrpc.NewAccountsClient(clientConn)
295350

296-
var accountID string
351+
id, label, _, err := parseIDOrLabel(ctx)
352+
if err != nil {
353+
return err
354+
}
355+
356+
req := &litrpc.RemoveAccountRequest{
357+
Id: id,
358+
Label: label,
359+
}
360+
_, err = client.RemoveAccount(ctxb, req)
361+
return err
362+
}
363+
364+
// parseIDOrLabel parses either the id or label from the command line.
365+
func parseIDOrLabel(ctx *cli.Context) (string, string, cli.Args, error) {
366+
var (
367+
accountID string
368+
label string
369+
)
297370
args := ctx.Args()
298371

299372
switch {
300-
case ctx.IsSet("id"):
301-
accountID = ctx.String("id")
373+
case ctx.IsSet(idName) && ctx.IsSet(labelName):
374+
return "", "", nil, fmt.Errorf("either account ID or label " +
375+
"must be specified, not both")
376+
377+
case ctx.IsSet(idName):
378+
accountID = ctx.String(idName)
379+
380+
case ctx.IsSet(labelName):
381+
label = ctx.String(labelName)
382+
302383
case args.Present():
303384
accountID = args.First()
304385
args = args.Tail()
305-
default:
306-
return fmt.Errorf("id argument missing")
307-
}
308386

309-
if len(accountID) == 0 {
310-
return fmt.Errorf("id argument missing")
311-
}
312-
if _, err := hex.DecodeString(accountID); err != nil {
313-
return err
314-
}
387+
// Since we have a positional argument, we cannot be sure it's
388+
// an ID. So we check if it's an ID by trying to hex decode it
389+
// and by checking the length. This will break if the user
390+
// chooses labels that are also valid hex encoded IDs. But since
391+
// the label is supposed to be human-readable, this should be
392+
// unlikely.
393+
_, err := hex.DecodeString(accountID)
394+
if len(accountID) != hex.EncodedLen(accounts.AccountIDLen) ||
395+
err != nil {
396+
397+
label = accountID
398+
accountID = ""
399+
}
315400

316-
req := &litrpc.RemoveAccountRequest{
317-
Id: accountID,
401+
default:
402+
return "", "", nil, fmt.Errorf("id argument missing")
318403
}
319-
_, err = client.RemoveAccount(ctxb, req)
320-
return err
404+
405+
return accountID, label, args, nil
321406
}

0 commit comments

Comments
 (0)