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}
@@ -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 {
432449var 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
572593func 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
692690func 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