Skip to content

Commit 339f440

Browse files
committed
cmd: add --group_key CLI flag to all channel commands
1 parent a9db5ea commit 339f440

File tree

1 file changed

+109
-77
lines changed

1 file changed

+109
-77
lines changed

cmd/litcli/ln.go

Lines changed: 109 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ import (
77
"encoding/hex"
88
"errors"
99
"fmt"
10-
"strconv"
1110

12-
"github.com/lightninglabs/taproot-assets/asset"
1311
"github.com/lightninglabs/taproot-assets/rfq"
1412
"github.com/lightninglabs/taproot-assets/rfqmath"
1513
"github.com/lightninglabs/taproot-assets/rpcutils"
@@ -35,8 +33,9 @@ const (
3533

3634
var lnCommands = []cli.Command{
3735
{
38-
Name: "ln",
39-
Usage: "Interact with the Lightning Network.",
36+
Name: "ln",
37+
Usage: "Interact with Taproot Assets on the Lightning " +
38+
"Network.",
4039
Category: "Taproot Assets on LN",
4140
Subcommands: []cli.Command{
4241
fundChannelCommand,
@@ -83,8 +82,16 @@ var fundChannelCommand = cli.Command{
8382
"channel.",
8483
},
8584
cli.StringFlag{
86-
Name: "asset_id",
87-
Usage: "The asset ID to commit to the channel.",
85+
Name: "asset_id",
86+
Usage: "The asset ID to commit to the channel; " +
87+
"cannot be used at the same time as " +
88+
"--group_key",
89+
},
90+
cli.StringFlag{
91+
Name: "group_key",
92+
Usage: "The group key to use for selecting assets to " +
93+
"commit to the channel; cannot be used at " +
94+
"the same time as --asset_id",
8895
},
8996
},
9097
Action: fundChannel,
@@ -106,9 +113,9 @@ func fundChannel(c *cli.Context) error {
106113
return fmt.Errorf("error fetching assets: %w", err)
107114
}
108115

109-
assetIDBytes, err := hex.DecodeString(c.String("asset_id"))
116+
assetIDBytes, groupKeyBytes, err := parseAssetIdentifier(c)
110117
if err != nil {
111-
return fmt.Errorf("error hex decoding asset ID: %w", err)
118+
return fmt.Errorf("unable to parse asset identifier: %w", err)
112119
}
113120

114121
requestedAmount := c.Uint64("asset_amount")
@@ -149,6 +156,7 @@ func fundChannel(c *cli.Context) error {
149156
ctx, &tchrpc.FundChannelRequest{
150157
AssetAmount: requestedAmount,
151158
AssetId: assetIDBytes,
159+
GroupKey: groupKeyBytes,
152160
PeerPubkey: nodePubBytes,
153161
FeeRateSatPerVbyte: uint32(c.Uint64("sat_per_vbyte")),
154162
PushSat: c.Int64("push_amt"),
@@ -167,7 +175,15 @@ var (
167175
assetIDFlag = cli.StringFlag{
168176
Name: "asset_id",
169177
Usage: "the asset ID of the asset to use when sending " +
170-
"payments with assets",
178+
"payments with assets; cannot be used at the same " +
179+
"time as --group_key",
180+
}
181+
182+
groupKeyFlag = cli.StringFlag{
183+
Name: "group_key",
184+
Usage: "the group key of the asset to use when sending " +
185+
"payments with assets; cannot be used at the same " +
186+
"time as --asset_id",
171187
}
172188

173189
assetAmountFlag = cli.Uint64Flag{
@@ -281,18 +297,19 @@ var sendPaymentCommand = cli.Command{
281297
Name: "sendpayment",
282298
Category: commands.SendPaymentCommand.Category,
283299
Usage: "Send a payment over Lightning, potentially using a " +
284-
"mulit-asset channel as the first hop",
300+
"multi-asset channel as the first hop.",
285301
Description: commands.SendPaymentCommand.Description + `
286302
To send an multi-asset LN payment to a single hop, the --asset_id=X
287-
argument should be used.
303+
or --group_key=X argument should be used.
288304
289305
Note that this will only work in concert with the --keysend argument.
290306
`,
291-
ArgsUsage: commands.SendPaymentCommand.ArgsUsage + " --asset_id=X " +
292-
"--asset_amount=Y [--rfq_peer_pubkey=Z]",
307+
ArgsUsage: commands.SendPaymentCommand.ArgsUsage + " [--asset_id=X " +
308+
" | --group_key=X] --asset_amount=Y [--rfq_peer_pubkey=Z] " +
309+
"[--allow_overpay]",
293310
Flags: append(
294-
commands.SendPaymentCommand.Flags, assetIDFlag, assetAmountFlag,
295-
rfqPeerPubKeyFlag, allowOverpayFlag,
311+
commands.SendPaymentCommand.Flags, assetIDFlag, groupKeyFlag,
312+
assetAmountFlag, rfqPeerPubKeyFlag, allowOverpayFlag,
296313
),
297314
Action: sendPayment,
298315
}
@@ -322,18 +339,15 @@ func sendPayment(cliCtx *cli.Context) error {
322339
defer cleanup()
323340

324341
switch {
325-
case !cliCtx.IsSet(assetIDFlag.Name):
326-
return fmt.Errorf("the --asset_id flag must be set")
327342
case !cliCtx.IsSet("keysend"):
328343
return fmt.Errorf("the --keysend flag must be set")
329344
case !cliCtx.IsSet(assetAmountFlag.Name):
330345
return fmt.Errorf("--asset_amount must be set")
331346
}
332347

333-
assetIDStr := cliCtx.String(assetIDFlag.Name)
334-
assetIDBytes, err := hex.DecodeString(assetIDStr)
348+
assetIDBytes, groupKeyBytes, err := parseAssetIdentifier(cliCtx)
335349
if err != nil {
336-
return fmt.Errorf("unable to decode assetID: %v", err)
350+
return fmt.Errorf("unable to parse asset identifier: %w", err)
337351
}
338352

339353
assetAmountToSend := cliCtx.Uint64(assetAmountFlag.Name)
@@ -363,7 +377,9 @@ func sendPayment(cliCtx *cli.Context) error {
363377
"is instead: %v", len(destNode))
364378
}
365379

366-
rfqPeerKey, err := hex.DecodeString(cliCtx.String(rfqPeerPubKeyFlag.Name))
380+
rfqPeerKey, err := hex.DecodeString(
381+
cliCtx.String(rfqPeerPubKeyFlag.Name),
382+
)
367383
if err != nil {
368384
return fmt.Errorf("unable to decode RFQ peer public key: "+
369385
"%w", err)
@@ -412,6 +428,7 @@ func sendPayment(cliCtx *cli.Context) error {
412428
stream, err := tchrpcClient.SendPayment(
413429
ctx, &tchrpc.SendPaymentRequest{
414430
AssetId: assetIDBytes,
431+
GroupKey: groupKeyBytes,
415432
AssetAmount: assetAmountToSend,
416433
PeerPubkey: rfqPeerKey,
417434
PaymentRequest: req,
@@ -432,22 +449,24 @@ func sendPayment(cliCtx *cli.Context) error {
432449
var payInvoiceCommand = cli.Command{
433450
Name: "payinvoice",
434451
Category: "Payments",
435-
Usage: "Pay an invoice over lightning using an asset.",
452+
Usage: "Pay an invoice over Lightning, potentially using a " +
453+
"multi-asset channel as the first hop.",
436454
Description: `
437455
This command attempts to pay an invoice using an asset channel as the
438456
source of the payment. The asset ID of the channel must be specified
439457
using the --asset_id flag.
458+
459+
This command is a shortcut for 'sendpayment --pay_req='.
440460
`,
441-
ArgsUsage: "pay_req --asset_id=X",
461+
ArgsUsage: "pay_req [--asset_id=X | --group_key=X] " +
462+
"[--rfq_peer_pubkey=y] [--allow_overpay]",
442463
Flags: append(commands.PaymentFlags(),
443464
cli.Int64Flag{
444465
Name: "amt",
445466
Usage: "(optional) number of satoshis to fulfill the " +
446467
"invoice",
447468
},
448-
assetIDFlag,
449-
rfqPeerPubKeyFlag,
450-
allowOverpayFlag,
469+
assetIDFlag, groupKeyFlag, rfqPeerPubKeyFlag, allowOverpayFlag,
451470
),
452471
Action: payInvoice,
453472
}
@@ -486,15 +505,9 @@ func payInvoice(cli *cli.Context) error {
486505
return err
487506
}
488507

489-
if !cli.IsSet(assetIDFlag.Name) {
490-
return fmt.Errorf("the --asset_id flag must be set")
491-
}
492-
493-
assetIDStr := cli.String(assetIDFlag.Name)
494-
495-
assetIDBytes, err := hex.DecodeString(assetIDStr)
508+
assetIDBytes, groupKeyBytes, err := parseAssetIdentifier(cli)
496509
if err != nil {
497-
return fmt.Errorf("unable to decode assetID: %v", err)
510+
return fmt.Errorf("unable to parse asset identifier: %w", err)
498511
}
499512

500513
rfqPeerKey, err := hex.DecodeString(cli.String(rfqPeerPubKeyFlag.Name))
@@ -521,6 +534,7 @@ func payInvoice(cli *cli.Context) error {
521534
stream, err := tchrpcClient.SendPayment(
522535
ctx, &tchrpc.SendPaymentRequest{
523536
AssetId: assetIDBytes,
537+
GroupKey: groupKeyBytes,
524538
PeerPubkey: rfqPeerKey,
525539
PaymentRequest: req,
526540
AllowOverpay: allowOverpay,
@@ -546,12 +560,19 @@ var addInvoiceCommand = cli.Command{
546560
Add a new invoice, expressing intent for a future payment, received in
547561
Taproot Assets.
548562
`,
549-
ArgsUsage: "asset_id asset_amount",
563+
ArgsUsage: "[--asset_id=X | --group_key=X] --asset_amount=Y " +
564+
"[--rfq_peer_pubkey=Z] ",
550565
Flags: append(
551566
commands.AddInvoiceCommand.Flags,
552567
cli.StringFlag{
553-
Name: "asset_id",
554-
Usage: "the asset ID of the asset to receive",
568+
Name: "asset_id",
569+
Usage: "the asset ID of the asset to receive; cannot " +
570+
"be used at the same time as --group_key",
571+
},
572+
cli.StringFlag{
573+
Name: "group_key",
574+
Usage: "the group key of the asset to receive; " +
575+
"cannot be used at the same time as --asset_id",
555576
},
556577
cli.Uint64Flag{
557578
Name: "asset_amount",
@@ -570,36 +591,15 @@ var addInvoiceCommand = cli.Command{
570591
}
571592

572593
func addInvoice(cli *cli.Context) error {
573-
args := cli.Args()
574594
ctx := getContext()
575595

576-
var assetIDStr string
577-
switch {
578-
case cli.IsSet("asset_id"):
579-
assetIDStr = cli.String("asset_id")
580-
case args.Present():
581-
assetIDStr = args.First()
582-
args = args.Tail()
583-
default:
584-
return fmt.Errorf("asset_id argument missing")
585-
}
586-
587596
var (
588-
assetAmount uint64
597+
assetAmount = cli.Uint64("asset_amount")
589598
preimage []byte
590599
descHash []byte
591600
err error
592601
)
593-
switch {
594-
case cli.IsSet("asset_amount"):
595-
assetAmount = cli.Uint64("asset_amount")
596-
case args.Present():
597-
assetAmount, err = strconv.ParseUint(args.First(), 10, 64)
598-
if err != nil {
599-
return fmt.Errorf("unable to parse asset amount %w",
600-
err)
601-
}
602-
default:
602+
if assetAmount == 0 {
603603
return fmt.Errorf("asset_amount argument missing")
604604
}
605605

@@ -620,14 +620,11 @@ func addInvoice(cli *cli.Context) error {
620620
expirySeconds = cli.Int64("expiry")
621621
}
622622

623-
assetIDBytes, err := hex.DecodeString(assetIDStr)
623+
assetIDBytes, groupKeyBytes, err := parseAssetIdentifier(cli)
624624
if err != nil {
625-
return fmt.Errorf("unable to decode assetID: %v", err)
625+
return fmt.Errorf("unable to parse asset identifier: %w", err)
626626
}
627627

628-
var assetID asset.ID
629-
copy(assetID[:], assetIDBytes)
630-
631628
rfqPeerKey, err := hex.DecodeString(cli.String(rfqPeerPubKeyFlag.Name))
632629
if err != nil {
633630
return fmt.Errorf("unable to decode RFQ peer public key: "+
@@ -643,6 +640,7 @@ func addInvoice(cli *cli.Context) error {
643640
channelsClient := tchrpc.NewTaprootAssetChannelsClient(tapdConn)
644641
resp, err := channelsClient.AddInvoice(ctx, &tchrpc.AddInvoiceRequest{
645642
AssetId: assetIDBytes,
643+
GroupKey: groupKeyBytes,
646644
AssetAmount: assetAmount,
647645
PeerPubkey: rfqPeerKey,
648646
InvoiceRequest: &lnrpc.Invoice{
@@ -668,43 +666,39 @@ var decodeAssetInvoiceCommand = cli.Command{
668666
Name: "decodeassetinvoice",
669667
Category: "Payments",
670668
Usage: "Decodes an LN invoice and displays the invoice's amount in " +
671-
"asset units specified by an asset ID",
669+
"asset units specified by an asset ID or group key.",
672670
Description: `
673671
This command can be used to display the information encoded in an
674672
invoice.
675-
Given a chosen asset_id, the invoice's amount expressed in units of the
676-
asset will be displayed.
673+
Given a chosen asset_id or group_key, the invoice's amount expressed in
674+
units of the asset will be displayed.
677675
678676
Other information such as the decimal display of an asset, and the asset
679677
group information (if applicable) are also shown.
680678
`,
681-
ArgsUsage: "--pay_req=X --asset_id=X",
679+
ArgsUsage: "--pay_req=X [--asset_id=X | --group_key=X]",
682680
Flags: []cli.Flag{
683681
cli.StringFlag{
684682
Name: "pay_req",
685683
Usage: "a zpay32 encoded payment request to fulfill",
686684
},
687-
assetIDFlag,
685+
assetIDFlag, groupKeyFlag,
688686
},
689687
Action: decodeAssetInvoice,
690688
}
691689

692690
func decodeAssetInvoice(cli *cli.Context) error {
693691
ctx := getContext()
694692

695-
switch {
696-
case !cli.IsSet("pay_req"):
693+
if !cli.IsSet("pay_req") {
697694
return fmt.Errorf("pay_req argument missing")
698-
case !cli.IsSet(assetIDFlag.Name):
699-
return fmt.Errorf("the --asset_id flag must be set")
700695
}
701696

702697
payReq := cli.String("pay_req")
703698

704-
assetIDStr := cli.String(assetIDFlag.Name)
705-
assetIDBytes, err := hex.DecodeString(assetIDStr)
699+
assetIDBytes, groupKeyBytes, err := parseAssetIdentifier(cli)
706700
if err != nil {
707-
return fmt.Errorf("unable to decode assetID: %v", err)
701+
return fmt.Errorf("unable to parse asset identifier: %w", err)
708702
}
709703

710704
tapdConn, cleanup, err := connectSuperMacClient(ctx, cli)
@@ -716,6 +710,7 @@ func decodeAssetInvoice(cli *cli.Context) error {
716710
channelsClient := tchrpc.NewTaprootAssetChannelsClient(tapdConn)
717711
resp, err := channelsClient.DecodeAssetPayReq(ctx, &tchrpc.AssetPayReq{
718712
AssetId: assetIDBytes,
713+
GroupKey: groupKeyBytes,
719714
PayReqString: payReq,
720715
})
721716
if err != nil {
@@ -726,3 +721,40 @@ func decodeAssetInvoice(cli *cli.Context) error {
726721

727722
return nil
728723
}
724+
725+
// parseAssetIdentifier parses either the asset ID or group key from the command
726+
// line arguments.
727+
func parseAssetIdentifier(cli *cli.Context) ([]byte, []byte, error) {
728+
if !cli.IsSet(assetIDFlag.Name) && !cli.IsSet(groupKeyFlag.Name) {
729+
return nil, nil, fmt.Errorf("either the --asset_id or " +
730+
"--group_key flag must be set")
731+
}
732+
733+
var (
734+
assetIDBytes []byte
735+
groupKeyBytes []byte
736+
err error
737+
)
738+
if cli.IsSet("asset_id") {
739+
assetIDBytes, err = hex.DecodeString(cli.String("asset_id"))
740+
if err != nil {
741+
return nil, nil, fmt.Errorf("error hex decoding asset "+
742+
"ID: %w", err)
743+
}
744+
}
745+
746+
if cli.IsSet("group_key") {
747+
groupKeyBytes, err = hex.DecodeString(cli.String("group_key"))
748+
if err != nil {
749+
return nil, nil, fmt.Errorf("error hex decoding group "+
750+
"key: %w", err)
751+
}
752+
}
753+
754+
if len(assetIDBytes) == 0 && len(groupKeyBytes) == 0 {
755+
return nil, nil, fmt.Errorf("only one of --asset_id and " +
756+
"--group_key can be set")
757+
}
758+
759+
return assetIDBytes, groupKeyBytes, nil
760+
}

0 commit comments

Comments
 (0)