@@ -5,22 +5,18 @@ import (
5
5
"context"
6
6
"crypto/rand"
7
7
"encoding/hex"
8
- "encoding/json"
9
8
"errors"
10
9
"fmt"
11
10
"strconv"
12
- "time"
13
11
14
12
"github.com/lightninglabs/taproot-assets/asset"
15
- "github.com/lightninglabs/taproot-assets/rfqmsg "
13
+ "github.com/lightninglabs/taproot-assets/rfq "
16
14
"github.com/lightninglabs/taproot-assets/taprpc"
17
- "github.com/lightninglabs/taproot-assets/taprpc/rfqrpc"
18
15
tchrpc "github.com/lightninglabs/taproot-assets/taprpc/tapchannelrpc"
19
16
"github.com/lightningnetwork/lnd/cmd/commands"
20
17
"github.com/lightningnetwork/lnd/lnrpc"
21
18
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
22
19
"github.com/lightningnetwork/lnd/lntypes"
23
- "github.com/lightningnetwork/lnd/lnwire"
24
20
"github.com/lightningnetwork/lnd/record"
25
21
"github.com/urfave/cli"
26
22
"google.golang.org/grpc"
@@ -160,64 +156,6 @@ func fundChannel(c *cli.Context) error {
160
156
return nil
161
157
}
162
158
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
-
221
159
var (
222
160
assetIDFlag = cli.StringFlag {
223
161
Name : "asset_id" ,
@@ -517,7 +455,8 @@ func payInvoice(ctx *cli.Context) error {
517
455
518
456
stream , err := tchrpcClient .SendPayment (
519
457
ctx , & tchrpc.SendPaymentRequest {
520
- AssetId : assetIDBytes ,
458
+ AssetId : assetIDBytes ,
459
+ PaymentRequest : req ,
521
460
},
522
461
)
523
462
if err != nil {
@@ -551,6 +490,14 @@ var addInvoiceCommand = cli.Command{
551
490
Name : "asset_amount" ,
552
491
Usage : "the amount of assets to receive" ,
553
492
},
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
+ },
554
501
),
555
502
Action : addInvoice ,
556
503
}
@@ -572,6 +519,8 @@ func addInvoice(ctx *cli.Context) error {
572
519
573
520
var (
574
521
assetAmount uint64
522
+ preimage []byte
523
+ descHash []byte
575
524
err error
576
525
)
577
526
switch {
@@ -587,167 +536,63 @@ func addInvoice(ctx *cli.Context) error {
587
536
return fmt .Errorf ("asset_amount argument missing" )
588
537
}
589
538
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
+ }
596
544
}
597
545
598
- lndConn , cleanup , err := connectClient (ctx , false )
546
+ descHash , err = hex . DecodeString (ctx . String ( "description_hash" ) )
599
547
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 )
601
549
}
602
550
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
+ }
606
555
607
556
assetIDBytes , err := hex .DecodeString (assetIDStr )
608
557
if err != nil {
609
558
return fmt .Errorf ("unable to decode assetID: %v" , err )
610
559
}
611
560
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
-
630
561
var assetID asset.ID
631
562
copy (assetID [:], assetIDBytes )
632
563
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 ))
665
565
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 )
686
568
}
687
569
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 )
692
571
if err != nil {
693
- return fmt .Errorf ("unable to parse description_hash : %w" , err )
572
+ return fmt .Errorf ("error creating tapd connection : %w" , err )
694
573
}
574
+ defer cleanup ()
695
575
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" ),
723
589
},
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 ,
742
590
})
743
591
if err != nil {
744
- return nil , fmt .Errorf ("unable to fetch channel : %w" , err )
592
+ return fmt .Errorf ("error adding invoice : %w" , err )
745
593
}
746
594
747
- policy := edge .Node1Policy
748
- if edge .Node1Pub == remotePubKey {
749
- policy = edge .Node2Policy
750
- }
595
+ printRespJSON (resp )
751
596
752
- return policy , nil
597
+ return nil
753
598
}
0 commit comments