Skip to content

Update litcli for multi-rfq send #1125

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: tapd-main-branch
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 52 additions & 47 deletions cmd/litcli/ln.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import (
"context"
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"time"

"github.com/lightninglabs/taproot-assets/rfq"
"github.com/lightninglabs/taproot-assets/rfqmath"
"github.com/lightninglabs/taproot-assets/rpcutils"
"github.com/lightninglabs/taproot-assets/taprpc"
"github.com/lightninglabs/taproot-assets/taprpc/rfqrpc"
tchrpc "github.com/lightninglabs/taproot-assets/taprpc/tapchannelrpc"
"github.com/lightningnetwork/lnd/cmd/commands"
"github.com/lightningnetwork/lnd/lnrpc"
Expand Down Expand Up @@ -210,9 +210,8 @@ var (
rfqPeerPubKeyFlag = cli.StringFlag{
Name: "rfq_peer_pubkey",
Usage: "(optional) the public key of the peer to ask for a " +
"quote when converting from assets to sats; must be " +
"set if there are multiple channels with the same " +
"asset ID present",
"quote when converting from assets to sats; if left " +
"unset then rfq peers will be picked automatically",
}

allowOverpayFlag = cli.BoolFlag{
Expand All @@ -237,74 +236,80 @@ type resultStreamWrapper struct {
//
// NOTE: This method is part of the PaymentResultStream interface.
func (w *resultStreamWrapper) Recv() (*lnrpc.Payment, error) {
resp, err := w.stream.Recv()
if err != nil {
return nil, err
}

res := resp.Result
switch r := res.(type) {
// The very first response might be an accepted sell order, which we
// just print out.
case *tchrpc.SendPaymentResponse_AcceptedSellOrder:
quote := r.AcceptedSellOrder
// printQuote unmarshals and prints an accepted quote.
printQuote := func(quote *rfqrpc.PeerAcceptedSellQuote) error {
rpcRate := quote.BidAssetRate
rate, err := rpcutils.UnmarshalRfqFixedPoint(rpcRate)
if err != nil {
return nil, fmt.Errorf("unable to unmarshal fixed "+
"point: %w", err)
return fmt.Errorf("unable to unmarshal fixed point: %w",
err)
}

amountMsat := lnwire.MilliSatoshi(w.amountMsat)
milliSatsFP := rfqmath.MilliSatoshiToUnits(amountMsat, *rate)
numUnits := milliSatsFP.ScaleTo(0).ToUint64()

// If the calculated number of units is 0 then the asset rate
// was not sufficient to represent the value of this payment.
// The purpose of this function is just to print, so let's avoid
// dividing by zero or reporting an invalid msat/unit rate.
if numUnits == 0 {
// We will calculate the minimum amount that can be
// effectively sent with this asset by calculating the
// value of a single asset unit, based on the provided
// asset rate.

// We create the single unit.
unit := rfqmath.FixedPointFromUint64[rfqmath.BigInt](
1, 0,
)

// We derive the minimum amount.
minAmt := rfqmath.UnitsToMilliSatoshi(unit, *rate)

// We return the error to the user.
return nil, fmt.Errorf("smallest payment with asset "+
"rate %v is %v, cannot send %v",
rate.ToUint64(), minAmt, amountMsat)
return nil
}

msatPerUnit := uint64(w.amountMsat) / numUnits

fmt.Printf("Got quote for %v asset units at %v msat/unit from "+
"peer %s with SCID %d\n", numUnits, msatPerUnit,
" peer %s with SCID %d\n", numUnits, msatPerUnit,
quote.Peer, quote.Scid)

resp, err = w.stream.Recv()
return nil
}

// A boolean to indicate whether the first quote was printed via the
// legacy single-rfq response field.
legacyFirstPrint := false

for {
resp, err := w.stream.Recv()
if err != nil {
return nil, err
}

if resp == nil || resp.Result == nil ||
resp.GetPaymentResult() == nil {
res := resp.Result

return nil, errors.New("unexpected nil result")
}
switch r := res.(type) {
case *tchrpc.SendPaymentResponse_AcceptedSellOrder:
err := printQuote(r.AcceptedSellOrder)
if err != nil {
return nil, err
}

return resp.GetPaymentResult(), nil
legacyFirstPrint = true

case *tchrpc.SendPaymentResponse_PaymentResult:
return r.PaymentResult, nil
case *tchrpc.SendPaymentResponse_AcceptedSellOrders:
quotes := r.AcceptedSellOrders.AcceptedSellOrders

default:
return nil, fmt.Errorf("unexpected response type: %T", r)
for _, quote := range quotes {
// If the first item was returned via the legacy
// field then skip printing it again here. This
// skip only applies to the first element.
if legacyFirstPrint {
legacyFirstPrint = false
continue
}

err := printQuote(quote)
if err != nil {
return nil, err
}
}

case *tchrpc.SendPaymentResponse_PaymentResult:
return r.PaymentResult, nil

default:
return nil, fmt.Errorf("unexpected response type: %T",
r)
}
}
}

Expand Down