Skip to content

Commit a19321f

Browse files
committed
cmd+itest: use new tapchannelrpc.AddInvoice RPC
1 parent 93a4b4c commit a19321f

File tree

2 files changed

+67
-271
lines changed

2 files changed

+67
-271
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",
@@ -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

Comments
 (0)