@@ -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" ,
@@ -550,6 +488,8 @@ func addInvoice(ctx *cli.Context) error {
550488
551489 var (
552490 assetAmount uint64
491+ preimage []byte
492+ descHash []byte
553493 err error
554494 )
555495 switch {
@@ -565,167 +505,56 @@ func addInvoice(ctx *cli.Context) error {
565505 return fmt .Errorf ("asset_amount argument missing" )
566506 }
567507
568- expiry := time .Now ().Add (300 * time .Second )
569- if ctx .IsSet ("expiry" ) {
570- expirySeconds := ctx .Uint64 ("expiry" )
571- expiry = time .Now ().Add (
572- time .Duration (expirySeconds ) * time .Second ,
573- )
508+ if ctx .IsSet ("preimage" ) {
509+ preimage , err = hex .DecodeString (ctx .String ("preimage" ))
510+ if err != nil {
511+ return fmt .Errorf ("unable to parse preimage: %w" , err )
512+ }
574513 }
575514
576- lndConn , cleanup , err := connectClient (ctx , false )
515+ descHash , err = hex . DecodeString (ctx . String ( "description_hash" ) )
577516 if err != nil {
578- return fmt .Errorf ("unable to make rpc con : %w" , err )
517+ return fmt .Errorf ("unable to parse description_hash : %w" , err )
579518 }
580519
581- defer cleanup ()
582-
583- lndClient := lnrpc .NewLightningClient (lndConn )
520+ expirySeconds := int64 (rfq .DefaultInvoiceExpiry .Seconds ())
521+ if ctx .IsSet ("expiry" ) {
522+ expirySeconds = ctx .Int64 ("expiry" )
523+ }
584524
585525 assetIDBytes , err := hex .DecodeString (assetIDStr )
586526 if err != nil {
587527 return fmt .Errorf ("unable to decode assetID: %v" , err )
588528 }
589529
590- // First, based on the asset ID and amount, we'll make sure that this
591- // channel even has enough funds to send.
592- assetBalances , err := computeAssetBalances (lndClient )
593- if err != nil {
594- return fmt .Errorf ("unable to compute asset balances: %w" , err )
595- }
596-
597- balance , ok := assetBalances .Assets [assetIDStr ]
598- if ! ok {
599- return fmt .Errorf ("unable to send asset_id=%v, not in " +
600- "channel" , assetIDStr )
601- }
602-
603- if balance .RemoteBalance == 0 {
604- return fmt .Errorf ("no remote asset balance available for " +
605- "receiving asset_id=%v" , assetIDStr )
606- }
607-
608530 var assetID asset.ID
609531 copy (assetID [:], assetIDBytes )
610532
611533 tapdConn , cleanup , err := connectTapdClient (ctx )
612534 if err != nil {
613535 return fmt .Errorf ("error creating tapd connection: %w" , err )
614536 }
615-
616537 defer cleanup ()
617538
618- peerPubKey , err := hex .DecodeString (balance .Channel .RemotePubkey )
619- if err != nil {
620- return fmt .Errorf ("unable to decode peer pubkey: %w" , err )
621- }
622-
623- rfqClient := rfqrpc .NewRfqClient (tapdConn )
624-
625- timeoutSeconds := uint32 (60 )
626- fmt .Printf ("Asking peer %x for quote to buy assets to receive for " +
627- "invoice over %d units; waiting up to %ds\n " , peerPubKey ,
628- assetAmount , timeoutSeconds )
629-
630- resp , err := rfqClient .AddAssetBuyOrder (
631- ctxb , & rfqrpc.AddAssetBuyOrderRequest {
632- AssetSpecifier : & rfqrpc.AssetSpecifier {
633- Id : & rfqrpc.AssetSpecifier_AssetIdStr {
634- AssetIdStr : assetIDStr ,
635- },
636- },
637- MinAssetAmount : assetAmount ,
638- Expiry : uint64 (expiry .Unix ()),
639- PeerPubKey : peerPubKey ,
640- TimeoutSeconds : timeoutSeconds ,
641- },
642- )
643- if err != nil {
644- return fmt .Errorf ("error adding sell order: %w" , err )
645- }
646-
647- var acceptedQuote * rfqrpc.PeerAcceptedBuyQuote
648- switch r := resp .Response .(type ) {
649- case * rfqrpc.AddAssetBuyOrderResponse_AcceptedQuote :
650- acceptedQuote = r .AcceptedQuote
651-
652- case * rfqrpc.AddAssetBuyOrderResponse_InvalidQuote :
653- return fmt .Errorf ("peer %v sent back an invalid quote, " +
654- "status: %v" , r .InvalidQuote .Peer ,
655- r .InvalidQuote .Status .String ())
656-
657- case * rfqrpc.AddAssetBuyOrderResponse_RejectedQuote :
658- return fmt .Errorf ("peer %v rejected the quote, code: %v, " +
659- "error message: %v" , r .RejectedQuote .Peer ,
660- r .RejectedQuote .ErrorCode , r .RejectedQuote .ErrorMessage )
661-
662- default :
663- return fmt .Errorf ("unexpected response type: %T" , r )
664- }
665-
666- msatPerUnit := acceptedQuote .AskPrice
667- numMSats := lnwire .MilliSatoshi (assetAmount * msatPerUnit )
668-
669- descHash , err := hex .DecodeString (ctx .String ("description_hash" ))
670- if err != nil {
671- return fmt .Errorf ("unable to parse description_hash: %w" , err )
672- }
673-
674- ourPolicy , err := getOurPolicy (
675- lndClient , balance .Channel .ChanId , balance .Channel .RemotePubkey ,
676- )
677- if err != nil {
678- return fmt .Errorf ("unable to get our policy: %w" , err )
679- }
680-
681- hopHint := & lnrpc.HopHint {
682- NodeId : balance .Channel .RemotePubkey ,
683- ChanId : acceptedQuote .Scid ,
684- FeeBaseMsat : uint32 (ourPolicy .FeeBaseMsat ),
685- FeeProportionalMillionths : uint32 (ourPolicy .FeeRateMilliMsat ),
686- CltvExpiryDelta : ourPolicy .TimeLockDelta ,
687- }
688-
689- invoice := & lnrpc.Invoice {
690- Memo : ctx .String ("memo" ),
691- ValueMsat : int64 (numMSats ),
692- DescriptionHash : descHash ,
693- FallbackAddr : ctx .String ("fallback_addr" ),
694- Expiry : int64 (ctx .Uint64 ("expiry" )),
695- Private : ctx .Bool ("private" ),
696- IsAmp : ctx .Bool ("amp" ),
697- RouteHints : []* lnrpc.RouteHint {
698- {
699- HopHints : []* lnrpc.HopHint {hopHint },
700- },
539+ channelsClient := tchrpc .NewTaprootAssetChannelsClient (tapdConn )
540+ resp , err := channelsClient .AddInvoice (ctxb , & tchrpc.AddInvoiceRequest {
541+ AssetId : assetIDBytes ,
542+ AssetAmount : assetAmount ,
543+ InvoiceRequest : & lnrpc.Invoice {
544+ Memo : ctx .String ("memo" ),
545+ RPreimage : preimage ,
546+ DescriptionHash : descHash ,
547+ FallbackAddr : ctx .String ("fallback_addr" ),
548+ Expiry : expirySeconds ,
549+ Private : ctx .Bool ("private" ),
550+ IsAmp : ctx .Bool ("amp" ),
701551 },
702- }
703-
704- invoiceResp , err := lndClient .AddInvoice (ctxb , invoice )
705- if err != nil {
706- return err
707- }
708-
709- printRespJSON (invoiceResp )
710-
711- return nil
712- }
713-
714- func getOurPolicy (lndClient lnrpc.LightningClient , chanID uint64 ,
715- remotePubKey string ) (* lnrpc.RoutingPolicy , error ) {
716-
717- ctxb := context .Background ()
718- edge , err := lndClient .GetChanInfo (ctxb , & lnrpc.ChanInfoRequest {
719- ChanId : chanID ,
720552 })
721553 if err != nil {
722- return nil , fmt .Errorf ("unable to fetch channel : %w" , err )
554+ return fmt .Errorf ("error adding invoice : %w" , err )
723555 }
724556
725- policy := edge .Node1Policy
726- if edge .Node1Pub == remotePubKey {
727- policy = edge .Node2Policy
728- }
557+ printRespJSON (resp )
729558
730- return policy , nil
559+ return nil
731560}
0 commit comments