@@ -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" ,
@@ -512,7 +450,8 @@ func payInvoice(ctx *cli.Context) error {
512450
513451			stream , err  :=  tchrpcClient .SendPayment (
514452				ctx , & tchrpc.SendPaymentRequest {
515- 					AssetId : assetIDBytes ,
453+ 					AssetId :        assetIDBytes ,
454+ 					PaymentRequest : req ,
516455				},
517456			)
518457			if  err  !=  nil  {
@@ -546,6 +485,14 @@ var addInvoiceCommand = cli.Command{
546485			Name :  "asset_amount" ,
547486			Usage : "the amount of assets to receive" ,
548487		},
488+ 		cli.StringFlag {
489+ 			Name : "rfq_peer_pubkey" ,
490+ 			Usage : "(optional) the public key of the peer to ask "  + 
491+ 				"for a quote when converting from assets to "  + 
492+ 				"sats for the invoice; must be set if there "  + 
493+ 				"are multiple channels with the same "  + 
494+ 				"asset ID present" ,
495+ 		},
549496	),
550497	Action : addInvoice ,
551498}
@@ -567,6 +514,8 @@ func addInvoice(ctx *cli.Context) error {
567514
568515	var  (
569516		assetAmount  uint64 
517+ 		preimage     []byte 
518+ 		descHash     []byte 
570519		err          error 
571520	)
572521	switch  {
@@ -582,167 +531,63 @@ func addInvoice(ctx *cli.Context) error {
582531		return  fmt .Errorf ("asset_amount argument missing" )
583532	}
584533
585- 	expiry  :=  time .Now ().Add (300  *  time .Second )
586- 	if  ctx .IsSet ("expiry" ) {
587- 		expirySeconds  :=  ctx .Uint64 ("expiry" )
588- 		expiry  =  time .Now ().Add (
589- 			time .Duration (expirySeconds ) *  time .Second ,
590- 		)
534+ 	if  ctx .IsSet ("preimage" ) {
535+ 		preimage , err  =  hex .DecodeString (ctx .String ("preimage" ))
536+ 		if  err  !=  nil  {
537+ 			return  fmt .Errorf ("unable to parse preimage: %w" , err )
538+ 		}
591539	}
592540
593- 	lndConn ,  cleanup ,  err  :=   connectClient (ctx ,  false )
541+ 	descHash ,  err  =   hex . DecodeString (ctx . String ( "description_hash" ) )
594542	if  err  !=  nil  {
595- 		return  fmt .Errorf ("unable to make rpc con : %w" , err )
543+ 		return  fmt .Errorf ("unable to parse description_hash : %w" , err )
596544	}
597545
598- 	defer  cleanup ()
599- 
600- 	lndClient  :=  lnrpc .NewLightningClient (lndConn )
546+ 	expirySeconds  :=  int64 (rfq .DefaultInvoiceExpiry .Seconds ())
547+ 	if  ctx .IsSet ("expiry" ) {
548+ 		expirySeconds  =  ctx .Int64 ("expiry" )
549+ 	}
601550
602551	assetIDBytes , err  :=  hex .DecodeString (assetIDStr )
603552	if  err  !=  nil  {
604553		return  fmt .Errorf ("unable to decode assetID: %v" , err )
605554	}
606555
607- 	// First, based on the asset ID and amount, we'll make sure that this 
608- 	// channel even has enough funds to send. 
609- 	assetBalances , err  :=  computeAssetBalances (lndClient )
610- 	if  err  !=  nil  {
611- 		return  fmt .Errorf ("unable to compute asset balances: %w" , err )
612- 	}
613- 
614- 	balance , ok  :=  assetBalances .Assets [assetIDStr ]
615- 	if  ! ok  {
616- 		return  fmt .Errorf ("unable to send asset_id=%v, not in " + 
617- 			"channel" , assetIDStr )
618- 	}
619- 
620- 	if  balance .RemoteBalance  ==  0  {
621- 		return  fmt .Errorf ("no remote asset balance available for " + 
622- 			"receiving asset_id=%v" , assetIDStr )
623- 	}
624- 
625556	var  assetID  asset.ID 
626557	copy (assetID [:], assetIDBytes )
627558
628- 	tapdConn , cleanup , err  :=  connectTapdClient (ctx )
629- 	if  err  !=  nil  {
630- 		return  fmt .Errorf ("error creating tapd connection: %w" , err )
631- 	}
632- 
633- 	defer  cleanup ()
634- 
635- 	peerPubKey , err  :=  hex .DecodeString (balance .Channel .RemotePubkey )
636- 	if  err  !=  nil  {
637- 		return  fmt .Errorf ("unable to decode peer pubkey: %w" , err )
638- 	}
639- 
640- 	rfqClient  :=  rfqrpc .NewRfqClient (tapdConn )
641- 
642- 	timeoutSeconds  :=  uint32 (60 )
643- 	fmt .Printf ("Asking peer %x for quote to buy assets to receive for " + 
644- 		"invoice over %d units; waiting up to %ds\n " , peerPubKey ,
645- 		assetAmount , timeoutSeconds )
646- 
647- 	resp , err  :=  rfqClient .AddAssetBuyOrder (
648- 		ctxb , & rfqrpc.AddAssetBuyOrderRequest {
649- 			AssetSpecifier : & rfqrpc.AssetSpecifier {
650- 				Id : & rfqrpc.AssetSpecifier_AssetIdStr {
651- 					AssetIdStr : assetIDStr ,
652- 				},
653- 			},
654- 			MinAssetAmount : assetAmount ,
655- 			Expiry :         uint64 (expiry .Unix ()),
656- 			PeerPubKey :     peerPubKey ,
657- 			TimeoutSeconds : timeoutSeconds ,
658- 		},
659- 	)
559+ 	rfqPeerKey , err  :=  hex .DecodeString (ctx .String (rfqPeerPubKeyFlag .Name ))
660560	if  err  !=  nil  {
661- 		return  fmt .Errorf ("error adding sell order: %w" , err )
662- 	}
663- 
664- 	var  acceptedQuote  * rfqrpc.PeerAcceptedBuyQuote 
665- 	switch  r  :=  resp .Response .(type ) {
666- 	case  * rfqrpc.AddAssetBuyOrderResponse_AcceptedQuote :
667- 		acceptedQuote  =  r .AcceptedQuote 
668- 
669- 	case  * rfqrpc.AddAssetBuyOrderResponse_InvalidQuote :
670- 		return  fmt .Errorf ("peer %v sent back an invalid quote, " + 
671- 			"status: %v" , r .InvalidQuote .Peer ,
672- 			r .InvalidQuote .Status .String ())
673- 
674- 	case  * rfqrpc.AddAssetBuyOrderResponse_RejectedQuote :
675- 		return  fmt .Errorf ("peer %v rejected the quote, code: %v, " + 
676- 			"error message: %v" , r .RejectedQuote .Peer ,
677- 			r .RejectedQuote .ErrorCode , r .RejectedQuote .ErrorMessage )
678- 
679- 	default :
680- 		return  fmt .Errorf ("unexpected response type: %T" , r )
561+ 		return  fmt .Errorf ("unable to decode RFQ peer public key: " + 
562+ 			"%w" , err )
681563	}
682564
683- 	msatPerUnit  :=  acceptedQuote .AskPrice 
684- 	numMSats  :=  lnwire .MilliSatoshi (assetAmount  *  msatPerUnit )
685- 
686- 	descHash , err  :=  hex .DecodeString (ctx .String ("description_hash" ))
565+ 	tapdConn , cleanup , err  :=  connectTapdClient (ctx )
687566	if  err  !=  nil  {
688- 		return  fmt .Errorf ("unable to parse description_hash : %w" , err )
567+ 		return  fmt .Errorf ("error creating tapd connection : %w" , err )
689568	}
569+ 	defer  cleanup ()
690570
691- 	ourPolicy , err  :=  getOurPolicy (
692- 		lndClient , balance .Channel .ChanId , balance .Channel .RemotePubkey ,
693- 	)
694- 	if  err  !=  nil  {
695- 		return  fmt .Errorf ("unable to get our policy: %w" , err )
696- 	}
697- 
698- 	hopHint  :=  & lnrpc.HopHint {
699- 		NodeId :                    balance .Channel .RemotePubkey ,
700- 		ChanId :                    acceptedQuote .Scid ,
701- 		FeeBaseMsat :               uint32 (ourPolicy .FeeBaseMsat ),
702- 		FeeProportionalMillionths : uint32 (ourPolicy .FeeRateMilliMsat ),
703- 		CltvExpiryDelta :           ourPolicy .TimeLockDelta ,
704- 	}
705- 
706- 	invoice  :=  & lnrpc.Invoice {
707- 		Memo :            ctx .String ("memo" ),
708- 		ValueMsat :       int64 (numMSats ),
709- 		DescriptionHash : descHash ,
710- 		FallbackAddr :    ctx .String ("fallback_addr" ),
711- 		Expiry :          int64 (ctx .Uint64 ("expiry" )),
712- 		Private :         ctx .Bool ("private" ),
713- 		IsAmp :           ctx .Bool ("amp" ),
714- 		RouteHints : []* lnrpc.RouteHint {
715- 			{
716- 				HopHints : []* lnrpc.HopHint {hopHint },
717- 			},
571+ 	channelsClient  :=  tchrpc .NewTaprootAssetChannelsClient (tapdConn )
572+ 	resp , err  :=  channelsClient .AddInvoice (ctxb , & tchrpc.AddInvoiceRequest {
573+ 		AssetId :     assetIDBytes ,
574+ 		AssetAmount : assetAmount ,
575+ 		PeerPubkey :  rfqPeerKey ,
576+ 		InvoiceRequest : & lnrpc.Invoice {
577+ 			Memo :            ctx .String ("memo" ),
578+ 			RPreimage :       preimage ,
579+ 			DescriptionHash : descHash ,
580+ 			FallbackAddr :    ctx .String ("fallback_addr" ),
581+ 			Expiry :          expirySeconds ,
582+ 			Private :         ctx .Bool ("private" ),
583+ 			IsAmp :           ctx .Bool ("amp" ),
718584		},
719- 	}
720- 
721- 	invoiceResp , err  :=  lndClient .AddInvoice (ctxb , invoice )
722- 	if  err  !=  nil  {
723- 		return  err 
724- 	}
725- 
726- 	printRespJSON (invoiceResp )
727- 
728- 	return  nil 
729- }
730- 
731- func  getOurPolicy (lndClient  lnrpc.LightningClient , chanID  uint64 ,
732- 	remotePubKey  string ) (* lnrpc.RoutingPolicy , error ) {
733- 
734- 	ctxb  :=  context .Background ()
735- 	edge , err  :=  lndClient .GetChanInfo (ctxb , & lnrpc.ChanInfoRequest {
736- 		ChanId : chanID ,
737585	})
738586	if  err  !=  nil  {
739- 		return  nil ,  fmt .Errorf ("unable to fetch channel : %w" , err )
587+ 		return  fmt .Errorf ("error adding invoice : %w" , err )
740588	}
741589
742- 	policy  :=  edge .Node1Policy 
743- 	if  edge .Node1Pub  ==  remotePubKey  {
744- 		policy  =  edge .Node2Policy 
745- 	}
590+ 	printRespJSON (resp )
746591
747- 	return  policy ,  nil 
592+ 	return  nil 
748593}
0 commit comments