Skip to content

Commit 8853d2c

Browse files
committed
cmd+itest: use new tapchannelrpc.AddInvoice RPC
1 parent a641b68 commit 8853d2c

File tree

2 files changed

+67
-272
lines changed

2 files changed

+67
-272
lines changed

cmd/litcli/ln.go

Lines changed: 46 additions & 201 deletions
Original file line numberDiff line numberDiff line change
@@ -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-
221159
var (
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

Comments
 (0)