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
3634var 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}
@@ -321,19 +338,9 @@ func sendPayment(cliCtx *cli.Context) error {
321338 }
322339 defer cleanup ()
323340
324- switch {
325- case ! cliCtx .IsSet (assetIDFlag .Name ):
326- return fmt .Errorf ("the --asset_id flag must be set" )
327- case ! cliCtx .IsSet ("keysend" ):
328- return fmt .Errorf ("the --keysend flag must be set" )
329- case ! cliCtx .IsSet (assetAmountFlag .Name ):
330- return fmt .Errorf ("--asset_amount must be set" )
331- }
332-
333- assetIDStr := cliCtx .String (assetIDFlag .Name )
334- assetIDBytes , err := hex .DecodeString (assetIDStr )
341+ assetIDBytes , groupKeyBytes , err := parseAssetIdentifier (cliCtx )
335342 if err != nil {
336- return fmt .Errorf ("unable to decode assetID : %v " , err )
343+ return fmt .Errorf ("unable to parse asset identifier : %w " , err )
337344 }
338345
339346 assetAmountToSend := cliCtx .Uint64 (assetAmountFlag .Name )
@@ -363,7 +370,9 @@ func sendPayment(cliCtx *cli.Context) error {
363370 "is instead: %v" , len (destNode ))
364371 }
365372
366- rfqPeerKey , err := hex .DecodeString (cliCtx .String (rfqPeerPubKeyFlag .Name ))
373+ rfqPeerKey , err := hex .DecodeString (
374+ cliCtx .String (rfqPeerPubKeyFlag .Name ),
375+ )
367376 if err != nil {
368377 return fmt .Errorf ("unable to decode RFQ peer public key: " +
369378 "%w" , err )
@@ -412,6 +421,7 @@ func sendPayment(cliCtx *cli.Context) error {
412421 stream , err := tchrpcClient .SendPayment (
413422 ctx , & tchrpc.SendPaymentRequest {
414423 AssetId : assetIDBytes ,
424+ GroupKey : groupKeyBytes ,
415425 AssetAmount : assetAmountToSend ,
416426 PeerPubkey : rfqPeerKey ,
417427 PaymentRequest : req ,
@@ -432,22 +442,24 @@ func sendPayment(cliCtx *cli.Context) error {
432442var payInvoiceCommand = cli.Command {
433443 Name : "payinvoice" ,
434444 Category : "Payments" ,
435- Usage : "Pay an invoice over lightning using an asset." ,
445+ Usage : "Pay an invoice over Lightning, potentially using a " +
446+ "multi-asset channel as the first hop." ,
436447 Description : `
437448 This command attempts to pay an invoice using an asset channel as the
438- source of the payment. The asset ID of the channel must be specified
439- using the --asset_id flag.
449+ source of the payment. The asset of the channel must be specified using
450+ the --asset_id or --group_key flag.
451+
452+ This command is a shortcut for 'sendpayment --pay_req='.
440453 ` ,
441- ArgsUsage : "pay_req --asset_id=X" ,
454+ ArgsUsage : "pay_req [--asset_id=X | --group_key=X] " +
455+ "[--rfq_peer_pubkey=y] [--allow_overpay]" ,
442456 Flags : append (commands .PaymentFlags (),
443457 cli.Int64Flag {
444458 Name : "amt" ,
445459 Usage : "(optional) number of satoshis to fulfill the " +
446460 "invoice" ,
447461 },
448- assetIDFlag ,
449- rfqPeerPubKeyFlag ,
450- allowOverpayFlag ,
462+ assetIDFlag , groupKeyFlag , rfqPeerPubKeyFlag , allowOverpayFlag ,
451463 ),
452464 Action : payInvoice ,
453465}
@@ -486,15 +498,9 @@ func payInvoice(cli *cli.Context) error {
486498 return err
487499 }
488500
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 )
501+ assetIDBytes , groupKeyBytes , err := parseAssetIdentifier (cli )
496502 if err != nil {
497- return fmt .Errorf ("unable to decode assetID : %v " , err )
503+ return fmt .Errorf ("unable to parse asset identifier : %w " , err )
498504 }
499505
500506 rfqPeerKey , err := hex .DecodeString (cli .String (rfqPeerPubKeyFlag .Name ))
@@ -521,6 +527,7 @@ func payInvoice(cli *cli.Context) error {
521527 stream , err := tchrpcClient .SendPayment (
522528 ctx , & tchrpc.SendPaymentRequest {
523529 AssetId : assetIDBytes ,
530+ GroupKey : groupKeyBytes ,
524531 PeerPubkey : rfqPeerKey ,
525532 PaymentRequest : req ,
526533 AllowOverpay : allowOverpay ,
@@ -546,12 +553,19 @@ var addInvoiceCommand = cli.Command{
546553 Add a new invoice, expressing intent for a future payment, received in
547554 Taproot Assets.
548555 ` ,
549- ArgsUsage : "asset_id asset_amount" ,
556+ ArgsUsage : "[--asset_id=X | --group_key=X] --asset_amount=Y " +
557+ "[--rfq_peer_pubkey=Z] " ,
550558 Flags : append (
551559 commands .AddInvoiceCommand .Flags ,
552560 cli.StringFlag {
553- Name : "asset_id" ,
554- Usage : "the asset ID of the asset to receive" ,
561+ Name : "asset_id" ,
562+ Usage : "the asset ID of the asset to receive; cannot " +
563+ "be used at the same time as --group_key" ,
564+ },
565+ cli.StringFlag {
566+ Name : "group_key" ,
567+ Usage : "the group key of the asset to receive; " +
568+ "cannot be used at the same time as --asset_id" ,
555569 },
556570 cli.Uint64Flag {
557571 Name : "asset_amount" ,
@@ -570,36 +584,15 @@ var addInvoiceCommand = cli.Command{
570584}
571585
572586func addInvoice (cli * cli.Context ) error {
573- args := cli .Args ()
574587 ctx := getContext ()
575588
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-
587589 var (
588- assetAmount uint64
590+ assetAmount = cli . Uint64 ( "asset_amount" )
589591 preimage []byte
590592 descHash []byte
591593 err error
592594 )
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 :
595+ if assetAmount == 0 {
603596 return fmt .Errorf ("asset_amount argument missing" )
604597 }
605598
@@ -620,14 +613,11 @@ func addInvoice(cli *cli.Context) error {
620613 expirySeconds = cli .Int64 ("expiry" )
621614 }
622615
623- assetIDBytes , err := hex . DecodeString ( assetIDStr )
616+ assetIDBytes , groupKeyBytes , err := parseAssetIdentifier ( cli )
624617 if err != nil {
625- return fmt .Errorf ("unable to decode assetID : %v " , err )
618+ return fmt .Errorf ("unable to parse asset identifier : %w " , err )
626619 }
627620
628- var assetID asset.ID
629- copy (assetID [:], assetIDBytes )
630-
631621 rfqPeerKey , err := hex .DecodeString (cli .String (rfqPeerPubKeyFlag .Name ))
632622 if err != nil {
633623 return fmt .Errorf ("unable to decode RFQ peer public key: " +
@@ -643,6 +633,7 @@ func addInvoice(cli *cli.Context) error {
643633 channelsClient := tchrpc .NewTaprootAssetChannelsClient (tapdConn )
644634 resp , err := channelsClient .AddInvoice (ctx , & tchrpc.AddInvoiceRequest {
645635 AssetId : assetIDBytes ,
636+ GroupKey : groupKeyBytes ,
646637 AssetAmount : assetAmount ,
647638 PeerPubkey : rfqPeerKey ,
648639 InvoiceRequest : & lnrpc.Invoice {
@@ -668,43 +659,39 @@ var decodeAssetInvoiceCommand = cli.Command{
668659 Name : "decodeassetinvoice" ,
669660 Category : "Payments" ,
670661 Usage : "Decodes an LN invoice and displays the invoice's amount in " +
671- "asset units specified by an asset ID" ,
662+ "asset units specified by an asset ID or group key. " ,
672663 Description : `
673664 This command can be used to display the information encoded in an
674665 invoice.
675- Given a chosen asset_id, the invoice's amount expressed in units of the
676- asset will be displayed.
666+ Given a chosen asset_id or group_key , the invoice's amount expressed in
667+ units of the asset will be displayed.
677668
678669 Other information such as the decimal display of an asset, and the asset
679670 group information (if applicable) are also shown.
680671 ` ,
681- ArgsUsage : "--pay_req=X --asset_id=X" ,
672+ ArgsUsage : "--pay_req=X [ --asset_id=X | --group_key=X] " ,
682673 Flags : []cli.Flag {
683674 cli.StringFlag {
684675 Name : "pay_req" ,
685676 Usage : "a zpay32 encoded payment request to fulfill" ,
686677 },
687- assetIDFlag ,
678+ assetIDFlag , groupKeyFlag ,
688679 },
689680 Action : decodeAssetInvoice ,
690681}
691682
692683func decodeAssetInvoice (cli * cli.Context ) error {
693684 ctx := getContext ()
694685
695- switch {
696- case ! cli .IsSet ("pay_req" ):
686+ if ! cli .IsSet ("pay_req" ) {
697687 return fmt .Errorf ("pay_req argument missing" )
698- case ! cli .IsSet (assetIDFlag .Name ):
699- return fmt .Errorf ("the --asset_id flag must be set" )
700688 }
701689
702690 payReq := cli .String ("pay_req" )
703691
704- assetIDStr := cli .String (assetIDFlag .Name )
705- assetIDBytes , err := hex .DecodeString (assetIDStr )
692+ assetIDBytes , groupKeyBytes , err := parseAssetIdentifier (cli )
706693 if err != nil {
707- return fmt .Errorf ("unable to decode assetID : %v " , err )
694+ return fmt .Errorf ("unable to parse asset identifier : %w " , err )
708695 }
709696
710697 tapdConn , cleanup , err := connectSuperMacClient (ctx , cli )
@@ -716,6 +703,7 @@ func decodeAssetInvoice(cli *cli.Context) error {
716703 channelsClient := tchrpc .NewTaprootAssetChannelsClient (tapdConn )
717704 resp , err := channelsClient .DecodeAssetPayReq (ctx , & tchrpc.AssetPayReq {
718705 AssetId : assetIDBytes ,
706+ GroupKey : groupKeyBytes ,
719707 PayReqString : payReq ,
720708 })
721709 if err != nil {
@@ -726,3 +714,40 @@ func decodeAssetInvoice(cli *cli.Context) error {
726714
727715 return nil
728716}
717+
718+ // parseAssetIdentifier parses either the asset ID or group key from the command
719+ // line arguments.
720+ func parseAssetIdentifier (cli * cli.Context ) ([]byte , []byte , error ) {
721+ if ! cli .IsSet (assetIDFlag .Name ) && ! cli .IsSet (groupKeyFlag .Name ) {
722+ return nil , nil , fmt .Errorf ("either the --asset_id or " +
723+ "--group_key flag must be set" )
724+ }
725+
726+ var (
727+ assetIDBytes []byte
728+ groupKeyBytes []byte
729+ err error
730+ )
731+ if cli .IsSet ("asset_id" ) {
732+ assetIDBytes , err = hex .DecodeString (cli .String ("asset_id" ))
733+ if err != nil {
734+ return nil , nil , fmt .Errorf ("error hex decoding asset " +
735+ "ID: %w" , err )
736+ }
737+ }
738+
739+ if cli .IsSet ("group_key" ) {
740+ groupKeyBytes , err = hex .DecodeString (cli .String ("group_key" ))
741+ if err != nil {
742+ return nil , nil , fmt .Errorf ("error hex decoding group " +
743+ "key: %w" , err )
744+ }
745+ }
746+
747+ if len (assetIDBytes ) == 0 && len (groupKeyBytes ) == 0 {
748+ return nil , nil , fmt .Errorf ("only one of --asset_id and " +
749+ "--group_key can be set" )
750+ }
751+
752+ return assetIDBytes , groupKeyBytes , nil
753+ }
0 commit comments