@@ -5,22 +5,18 @@ import (
55	"context" 
66	"crypto/rand" 
77	"encoding/hex" 
8- 	"encoding/json" 
98	"errors" 
109	"fmt" 
1110	"strconv" 
12- 	"time" 
1311
1412	"github.com/lightninglabs/taproot-assets/asset" 
15- 	"github.com/lightninglabs/taproot-assets/rfqmsg " 
13+ 	"github.com/lightninglabs/taproot-assets/rfq " 
1614	"github.com/lightninglabs/taproot-assets/taprpc" 
17- 	"github.com/lightninglabs/taproot-assets/taprpc/rfqrpc" 
1815	tchrpc "github.com/lightninglabs/taproot-assets/taprpc/tapchannelrpc" 
1916	"github.com/lightningnetwork/lnd/cmd/commands" 
2017	"github.com/lightningnetwork/lnd/lnrpc" 
2118	"github.com/lightningnetwork/lnd/lnrpc/routerrpc" 
2219	"github.com/lightningnetwork/lnd/lntypes" 
23- 	"github.com/lightningnetwork/lnd/lnwire" 
2420	"github.com/lightningnetwork/lnd/record" 
2521	"github.com/urfave/cli" 
2622	"google.golang.org/grpc" 
@@ -160,64 +156,6 @@ func fundChannel(c *cli.Context) error {
160156	return  nil 
161157}
162158
163- type  assetBalance  struct  {
164- 	AssetID        string 
165- 	Name           string 
166- 	LocalBalance   uint64 
167- 	RemoteBalance  uint64 
168- 	Channel        * lnrpc.Channel 
169- }
170- 
171- type  channelBalResp  struct  {
172- 	Assets  map [string ]* assetBalance  `json:"assets"` 
173- }
174- 
175- func  computeAssetBalances (lnd  lnrpc.LightningClient ) (* channelBalResp , error ) {
176- 	ctxb  :=  context .Background ()
177- 	openChans , err  :=  lnd .ListChannels (
178- 		ctxb , & lnrpc.ListChannelsRequest {},
179- 	)
180- 	if  err  !=  nil  {
181- 		return  nil , fmt .Errorf ("unable to fetch channels: %w" , err )
182- 	}
183- 
184- 	balanceResp  :=  & channelBalResp {
185- 		Assets : make (map [string ]* assetBalance ),
186- 	}
187- 	for  _ , openChan  :=  range  openChans .Channels  {
188- 		if  len (openChan .CustomChannelData ) ==  0  {
189- 			continue 
190- 		}
191- 
192- 		var  assetData  rfqmsg.JsonAssetChannel 
193- 		err  =  json .Unmarshal (openChan .CustomChannelData , & assetData )
194- 		if  err  !=  nil  {
195- 			return  nil , fmt .Errorf ("unable to unmarshal asset " + 
196- 				"data: %w" , err )
197- 		}
198- 
199- 		for  _ , assetOutput  :=  range  assetData .Assets  {
200- 			assetID  :=  assetOutput .AssetInfo .AssetGenesis .AssetID 
201- 			assetName  :=  assetOutput .AssetInfo .AssetGenesis .Name 
202- 
203- 			balance , ok  :=  balanceResp .Assets [assetID ]
204- 			if  ! ok  {
205- 				balance  =  & assetBalance {
206- 					AssetID : assetID ,
207- 					Name :    assetName ,
208- 					Channel : openChan ,
209- 				}
210- 				balanceResp .Assets [assetID ] =  balance 
211- 			}
212- 
213- 			balance .LocalBalance  +=  assetOutput .LocalBalance 
214- 			balance .RemoteBalance  +=  assetOutput .RemoteBalance 
215- 		}
216- 	}
217- 
218- 	return  balanceResp , nil 
219- }
220- 
221159var  (
222160	assetIDFlag  =  cli.StringFlag {
223161		Name : "asset_id" ,
@@ -517,7 +455,8 @@ func payInvoice(ctx *cli.Context) error {
517455
518456			stream , err  :=  tchrpcClient .SendPayment (
519457				ctx , & tchrpc.SendPaymentRequest {
520- 					AssetId : assetIDBytes ,
458+ 					AssetId :        assetIDBytes ,
459+ 					PaymentRequest : req ,
521460				},
522461			)
523462			if  err  !=  nil  {
@@ -551,6 +490,14 @@ var addInvoiceCommand = cli.Command{
551490			Name :  "asset_amount" ,
552491			Usage : "the amount of assets to receive" ,
553492		},
493+ 		cli.StringFlag {
494+ 			Name : "rfq_peer_pubkey" ,
495+ 			Usage : "(optional) the public key of the peer to ask "  + 
496+ 				"for a quote when converting from assets to "  + 
497+ 				"sats for the invoice; must be set if there "  + 
498+ 				"are multiple channels with the same "  + 
499+ 				"asset ID present" ,
500+ 		},
554501	),
555502	Action : addInvoice ,
556503}
@@ -572,6 +519,8 @@ func addInvoice(ctx *cli.Context) error {
572519
573520	var  (
574521		assetAmount  uint64 
522+ 		preimage     []byte 
523+ 		descHash     []byte 
575524		err          error 
576525	)
577526	switch  {
@@ -587,167 +536,63 @@ func addInvoice(ctx *cli.Context) error {
587536		return  fmt .Errorf ("asset_amount argument missing" )
588537	}
589538
590- 	expiry  :=  time .Now ().Add (300  *  time .Second )
591- 	if  ctx .IsSet ("expiry" ) {
592- 		expirySeconds  :=  ctx .Uint64 ("expiry" )
593- 		expiry  =  time .Now ().Add (
594- 			time .Duration (expirySeconds ) *  time .Second ,
595- 		)
539+ 	if  ctx .IsSet ("preimage" ) {
540+ 		preimage , err  =  hex .DecodeString (ctx .String ("preimage" ))
541+ 		if  err  !=  nil  {
542+ 			return  fmt .Errorf ("unable to parse preimage: %w" , err )
543+ 		}
596544	}
597545
598- 	lndConn ,  cleanup ,  err  :=   connectClient (ctx ,  false )
546+ 	descHash ,  err  =   hex . DecodeString (ctx . String ( "description_hash" ) )
599547	if  err  !=  nil  {
600- 		return  fmt .Errorf ("unable to make rpc con : %w" , err )
548+ 		return  fmt .Errorf ("unable to parse description_hash : %w" , err )
601549	}
602550
603- 	defer  cleanup ()
604- 
605- 	lndClient  :=  lnrpc .NewLightningClient (lndConn )
551+ 	expirySeconds  :=  int64 (rfq .DefaultInvoiceExpiry .Seconds ())
552+ 	if  ctx .IsSet ("expiry" ) {
553+ 		expirySeconds  =  ctx .Int64 ("expiry" )
554+ 	}
606555
607556	assetIDBytes , err  :=  hex .DecodeString (assetIDStr )
608557	if  err  !=  nil  {
609558		return  fmt .Errorf ("unable to decode assetID: %v" , err )
610559	}
611560
612- 	// First, based on the asset ID and amount, we'll make sure that this 
613- 	// channel even has enough funds to send. 
614- 	assetBalances , err  :=  computeAssetBalances (lndClient )
615- 	if  err  !=  nil  {
616- 		return  fmt .Errorf ("unable to compute asset balances: %w" , err )
617- 	}
618- 
619- 	balance , ok  :=  assetBalances .Assets [assetIDStr ]
620- 	if  ! ok  {
621- 		return  fmt .Errorf ("unable to send asset_id=%v, not in " + 
622- 			"channel" , assetIDStr )
623- 	}
624- 
625- 	if  balance .RemoteBalance  ==  0  {
626- 		return  fmt .Errorf ("no remote asset balance available for " + 
627- 			"receiving asset_id=%v" , assetIDStr )
628- 	}
629- 
630561	var  assetID  asset.ID 
631562	copy (assetID [:], assetIDBytes )
632563
633- 	tapdConn , cleanup , err  :=  connectTapdClient (ctx )
634- 	if  err  !=  nil  {
635- 		return  fmt .Errorf ("error creating tapd connection: %w" , err )
636- 	}
637- 
638- 	defer  cleanup ()
639- 
640- 	peerPubKey , err  :=  hex .DecodeString (balance .Channel .RemotePubkey )
641- 	if  err  !=  nil  {
642- 		return  fmt .Errorf ("unable to decode peer pubkey: %w" , err )
643- 	}
644- 
645- 	rfqClient  :=  rfqrpc .NewRfqClient (tapdConn )
646- 
647- 	timeoutSeconds  :=  uint32 (60 )
648- 	fmt .Printf ("Asking peer %x for quote to buy assets to receive for " + 
649- 		"invoice over %d units; waiting up to %ds\n " , peerPubKey ,
650- 		assetAmount , timeoutSeconds )
651- 
652- 	resp , err  :=  rfqClient .AddAssetBuyOrder (
653- 		ctxb , & rfqrpc.AddAssetBuyOrderRequest {
654- 			AssetSpecifier : & rfqrpc.AssetSpecifier {
655- 				Id : & rfqrpc.AssetSpecifier_AssetIdStr {
656- 					AssetIdStr : assetIDStr ,
657- 				},
658- 			},
659- 			MinAssetAmount : assetAmount ,
660- 			Expiry :         uint64 (expiry .Unix ()),
661- 			PeerPubKey :     peerPubKey ,
662- 			TimeoutSeconds : timeoutSeconds ,
663- 		},
664- 	)
564+ 	rfqPeerKey , err  :=  hex .DecodeString (ctx .String (rfqPeerPubKeyFlag .Name ))
665565	if  err  !=  nil  {
666- 		return  fmt .Errorf ("error adding sell order: %w" , err )
667- 	}
668- 
669- 	var  acceptedQuote  * rfqrpc.PeerAcceptedBuyQuote 
670- 	switch  r  :=  resp .Response .(type ) {
671- 	case  * rfqrpc.AddAssetBuyOrderResponse_AcceptedQuote :
672- 		acceptedQuote  =  r .AcceptedQuote 
673- 
674- 	case  * rfqrpc.AddAssetBuyOrderResponse_InvalidQuote :
675- 		return  fmt .Errorf ("peer %v sent back an invalid quote, " + 
676- 			"status: %v" , r .InvalidQuote .Peer ,
677- 			r .InvalidQuote .Status .String ())
678- 
679- 	case  * rfqrpc.AddAssetBuyOrderResponse_RejectedQuote :
680- 		return  fmt .Errorf ("peer %v rejected the quote, code: %v, " + 
681- 			"error message: %v" , r .RejectedQuote .Peer ,
682- 			r .RejectedQuote .ErrorCode , r .RejectedQuote .ErrorMessage )
683- 
684- 	default :
685- 		return  fmt .Errorf ("unexpected response type: %T" , r )
566+ 		return  fmt .Errorf ("unable to decode RFQ peer public key: " + 
567+ 			"%w" , err )
686568	}
687569
688- 	msatPerUnit  :=  acceptedQuote .AskPrice 
689- 	numMSats  :=  lnwire .MilliSatoshi (assetAmount  *  msatPerUnit )
690- 
691- 	descHash , err  :=  hex .DecodeString (ctx .String ("description_hash" ))
570+ 	tapdConn , cleanup , err  :=  connectTapdClient (ctx )
692571	if  err  !=  nil  {
693- 		return  fmt .Errorf ("unable to parse description_hash : %w" , err )
572+ 		return  fmt .Errorf ("error creating tapd connection : %w" , err )
694573	}
574+ 	defer  cleanup ()
695575
696- 	ourPolicy , err  :=  getOurPolicy (
697- 		lndClient , balance .Channel .ChanId , balance .Channel .RemotePubkey ,
698- 	)
699- 	if  err  !=  nil  {
700- 		return  fmt .Errorf ("unable to get our policy: %w" , err )
701- 	}
702- 
703- 	hopHint  :=  & lnrpc.HopHint {
704- 		NodeId :                    balance .Channel .RemotePubkey ,
705- 		ChanId :                    acceptedQuote .Scid ,
706- 		FeeBaseMsat :               uint32 (ourPolicy .FeeBaseMsat ),
707- 		FeeProportionalMillionths : uint32 (ourPolicy .FeeRateMilliMsat ),
708- 		CltvExpiryDelta :           ourPolicy .TimeLockDelta ,
709- 	}
710- 
711- 	invoice  :=  & lnrpc.Invoice {
712- 		Memo :            ctx .String ("memo" ),
713- 		ValueMsat :       int64 (numMSats ),
714- 		DescriptionHash : descHash ,
715- 		FallbackAddr :    ctx .String ("fallback_addr" ),
716- 		Expiry :          int64 (ctx .Uint64 ("expiry" )),
717- 		Private :         ctx .Bool ("private" ),
718- 		IsAmp :           ctx .Bool ("amp" ),
719- 		RouteHints : []* lnrpc.RouteHint {
720- 			{
721- 				HopHints : []* lnrpc.HopHint {hopHint },
722- 			},
576+ 	channelsClient  :=  tchrpc .NewTaprootAssetChannelsClient (tapdConn )
577+ 	resp , err  :=  channelsClient .AddInvoice (ctxb , & tchrpc.AddInvoiceRequest {
578+ 		AssetId :     assetIDBytes ,
579+ 		AssetAmount : assetAmount ,
580+ 		PeerPubkey :  rfqPeerKey ,
581+ 		InvoiceRequest : & lnrpc.Invoice {
582+ 			Memo :            ctx .String ("memo" ),
583+ 			RPreimage :       preimage ,
584+ 			DescriptionHash : descHash ,
585+ 			FallbackAddr :    ctx .String ("fallback_addr" ),
586+ 			Expiry :          expirySeconds ,
587+ 			Private :         ctx .Bool ("private" ),
588+ 			IsAmp :           ctx .Bool ("amp" ),
723589		},
724- 	}
725- 
726- 	invoiceResp , err  :=  lndClient .AddInvoice (ctxb , invoice )
727- 	if  err  !=  nil  {
728- 		return  err 
729- 	}
730- 
731- 	printRespJSON (invoiceResp )
732- 
733- 	return  nil 
734- }
735- 
736- func  getOurPolicy (lndClient  lnrpc.LightningClient , chanID  uint64 ,
737- 	remotePubKey  string ) (* lnrpc.RoutingPolicy , error ) {
738- 
739- 	ctxb  :=  context .Background ()
740- 	edge , err  :=  lndClient .GetChanInfo (ctxb , & lnrpc.ChanInfoRequest {
741- 		ChanId : chanID ,
742590	})
743591	if  err  !=  nil  {
744- 		return  nil ,  fmt .Errorf ("unable to fetch channel : %w" , err )
592+ 		return  fmt .Errorf ("error adding invoice : %w" , err )
745593	}
746594
747- 	policy  :=  edge .Node1Policy 
748- 	if  edge .Node1Pub  ==  remotePubKey  {
749- 		policy  =  edge .Node2Policy 
750- 	}
595+ 	printRespJSON (resp )
751596
752- 	return  policy ,  nil 
597+ 	return  nil 
753598}
0 commit comments