Skip to content

Commit b9ee326

Browse files
committed
wip
1 parent be483e5 commit b9ee326

24 files changed

+1909
-1168
lines changed

assets/client.go

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ package assets
22

33
import (
44
"context"
5+
"encoding/hex"
56
"fmt"
67
"math/big"
78
"os"
9+
"sync"
810
"time"
911

1012
"github.com/btcsuite/btcd/btcutil"
@@ -13,6 +15,7 @@ import (
1315
"github.com/lightninglabs/taproot-assets/taprpc/priceoraclerpc"
1416
"github.com/lightninglabs/taproot-assets/taprpc/rfqrpc"
1517
"github.com/lightninglabs/taproot-assets/taprpc/tapchannelrpc"
18+
"github.com/lightninglabs/taproot-assets/taprpc/universerpc"
1619
"github.com/lightningnetwork/lnd/lnrpc"
1720
"github.com/lightningnetwork/lnd/macaroons"
1821
"google.golang.org/grpc"
@@ -44,11 +47,14 @@ func DefaultTapdConfig() *TapdConfig {
4447

4548
// TapdClient is a client for the Tap daemon.
4649
type TapdClient struct {
47-
cc *grpc.ClientConn
50+
sync.Mutex
51+
assetNameCache map[string]string
52+
cc *grpc.ClientConn
4853
taprpc.TaprootAssetsClient
4954
tapchannelrpc.TaprootAssetChannelsClient
5055
priceoraclerpc.PriceOracleClient
5156
rfqrpc.RfqClient
57+
universerpc.UniverseClient
5258
}
5359

5460
// NewTapdClient retusn a new taproot assets client.
@@ -61,11 +67,13 @@ func NewTapdClient(config *TapdConfig) (*TapdClient, error) {
6167

6268
// Create the TapdClient.
6369
client := &TapdClient{
70+
assetNameCache: make(map[string]string),
6471
cc: conn,
6572
TaprootAssetsClient: taprpc.NewTaprootAssetsClient(conn),
6673
TaprootAssetChannelsClient: tapchannelrpc.NewTaprootAssetChannelsClient(conn),
6774
PriceOracleClient: priceoraclerpc.NewPriceOracleClient(conn),
6875
RfqClient: rfqrpc.NewRfqClient(conn),
76+
UniverseClient: universerpc.NewUniverseClient(conn),
6977
}
7078

7179
return client, nil
@@ -104,12 +112,12 @@ func GetSatAmtFromRfq(assetAmt btcutil.Amount,
104112
// GetRfqForAsset returns a RFQ for the given asset with the given amount and
105113
// to the given peer.
106114
func (c *TapdClient) GetRfqForAsset(ctx context.Context,
107-
amt btcutil.Amount, assetId, peerPubkey []byte) (
115+
satAmount btcutil.Amount, assetId, peerPubkey []byte) (
108116
*rfqrpc.PeerAcceptedSellQuote, error) {
109117

110118
// TODO(sputn1ck): magic value, should be configurable?
111119
feeLimit, err := lnrpc.UnmarshallAmt(
112-
int64(amt)+int64(amt.MulF64(1.1)), 0,
120+
int64(satAmount)+int64(satAmount.MulF64(1.1)), 0,
113121
)
114122
if err != nil {
115123
return nil, err
@@ -148,6 +156,44 @@ func (c *TapdClient) GetRfqForAsset(ctx context.Context,
148156
return nil, fmt.Errorf("no accepted quote")
149157
}
150158

159+
// GetAssetName returns the human readable name of the asset.
160+
func (c *TapdClient) GetAssetName(ctx context.Context,
161+
assetId []byte) (string, error) {
162+
163+
c.Lock()
164+
defer c.Unlock()
165+
assetIdStr := hex.EncodeToString(assetId)
166+
if name, ok := c.assetNameCache[assetIdStr]; ok {
167+
return name, nil
168+
}
169+
170+
assetStats, err := c.UniverseClient.QueryAssetStats(
171+
ctx, &universerpc.AssetStatsQuery{
172+
AssetIdFilter: assetId,
173+
},
174+
)
175+
if err != nil {
176+
return "", err
177+
}
178+
179+
if len(assetStats.AssetStats) == 0 {
180+
return "", fmt.Errorf("asset not found")
181+
}
182+
183+
var assetName string
184+
185+
// If the asset belongs to a group, return the group name.
186+
if assetStats.AssetStats[0].GroupAnchor != nil {
187+
assetName = assetStats.AssetStats[0].GroupAnchor.AssetName
188+
} else {
189+
assetName = assetStats.AssetStats[0].Asset.AssetName
190+
}
191+
192+
c.assetNameCache[assetIdStr] = assetName
193+
194+
return assetName, nil
195+
}
196+
151197
func getClientConn(config *TapdConfig) (*grpc.ClientConn, error) {
152198
// Load the specified TLS certificate and build transport credentials.
153199
creds, err := credentials.NewClientTLSFromFile(config.TLSPath, "")

client.go

Lines changed: 82 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,9 @@ type Client struct {
9797
executor *executor
9898
assetClient *assets.TapdClient
9999

100-
resumeReady chan struct{}
101-
wg sync.WaitGroup
100+
resumeReady chan struct{}
101+
wg sync.WaitGroup
102+
assetNameCache map[string]string
102103

103104
clientConfig
104105
}
@@ -342,6 +343,10 @@ func (s *Client) FetchSwaps(ctx context.Context) ([]*SwapInfo, error) {
342343
return nil, swap.ErrInvalidOutputType
343344
}
344345

346+
if swp.Contract.AssetSwapInfo != nil {
347+
swapInfo.AssetSwapInfo = swp.Contract.AssetSwapInfo
348+
}
349+
345350
swaps = append(swaps, swapInfo)
346351
}
347352

@@ -507,43 +512,31 @@ func (s *Client) LoopOut(globalCtx context.Context,
507512
request *OutRequest) (*LoopOutSwapInfo, error) {
508513

509514
if request.AssetId != nil {
510-
if request.AssetEdgeNode == nil {
511-
return nil, errors.New("asset edge node must be set " +
512-
"when using an asset id")
515+
if request.AssetPrepayRfqId == nil ||
516+
request.AssetSwapRfqId == nil {
517+
518+
return nil, errors.New("asset prepay and swap rfq ids " +
519+
"must be set when using an asset id")
513520
}
521+
514522
// Verify that if we have an asset id set, we have a valid asset
515523
// client to use.
516524
if s.assetClient == nil {
517525
return nil, errors.New("asset client must be set " +
518526
"when using an asset id")
519527
}
520528

521-
rfq, err := s.assetClient.GetRfqForAsset(
522-
globalCtx, request.AssetAmount, request.AssetId,
523-
request.AssetEdgeNode,
524-
)
525-
if err != nil {
526-
return nil, err
527-
}
528-
529-
satAmt, err := assets.GetSatAmtFromRfq(
530-
request.AssetAmount, rfq.BidAssetRate,
531-
)
532-
if err != nil {
533-
return nil, err
534-
}
535-
536529
log.Infof("LoopOut %v sats to %v (channels: %v) with asset %x",
537-
satAmt, request.DestAddr, request.OutgoingChanSet,
538-
request.AssetId,
530+
request.Amount, request.DestAddr,
531+
request.OutgoingChanSet, request.AssetId,
532+
)
533+
} else {
534+
log.Infof("LoopOut %v to %v (channels: %v)",
535+
request.Amount, request.DestAddr,
536+
request.OutgoingChanSet,
539537
)
540-
request.Amount = satAmt
541538
}
542539

543-
log.Infof("LoopOut %v to %v (channels: %v)",
544-
request.Amount, request.DestAddr, request.OutgoingChanSet,
545-
)
546-
547540
if err := s.waitForInitialized(globalCtx); err != nil {
548541
return nil, err
549542
}
@@ -563,7 +556,9 @@ func (s *Client) LoopOut(globalCtx context.Context,
563556
}
564557

565558
// Create a new swap object for this swap.
566-
swapCfg := newSwapConfig(s.lndServices, s.Store, s.Server, s.assetClient)
559+
swapCfg := newSwapConfig(
560+
s.lndServices, s.Store, s.Server, s.assetClient,
561+
)
567562

568563
initResult, err := newLoopOutSwap(
569564
globalCtx, swapCfg, initiationHeight, request,
@@ -609,36 +604,26 @@ func (s *Client) getExpiry(height int32, terms *LoopOutTerms,
609604
func (s *Client) LoopOutQuote(ctx context.Context,
610605
request *LoopOutQuoteRequest) (*LoopOutQuote, error) {
611606

612-
terms, err := s.Server.GetLoopOutTerms(ctx, request.Initiator)
613-
if err != nil {
614-
return nil, err
607+
if request.AssetId != nil && request.AssetEdgeNode == nil {
608+
return nil, errors.New("asset edge node must be set " +
609+
"when using an asset id")
615610
}
616611

617-
invoiceAmountSat := request.Amount
618-
// If we use an Asset we'll rfq to check if the sat amount meets the
619-
// min swap amount criteria.
620-
if request.AssetId != nil {
621-
rfq, err := s.assetClient.GetRfqForAsset(
622-
ctx, request.Amount, request.AssetId,
623-
request.AssetEdgeNode,
624-
)
625-
if err != nil {
626-
return nil, err
627-
}
612+
if request.AssetEdgeNode != nil && request.AssetId == nil {
613+
return nil, errors.New("asset id must be set " +
614+
"when using an asset edge node")
615+
}
628616

629-
invoiceAmountSat, err = assets.GetSatAmtFromRfq(
630-
request.Amount, rfq.BidAssetRate,
631-
)
632-
if err != nil {
633-
return nil, err
634-
}
617+
terms, err := s.Server.GetLoopOutTerms(ctx, request.Initiator)
618+
if err != nil {
619+
return nil, err
635620
}
636621

637-
if invoiceAmountSat < terms.MinSwapAmount {
622+
if request.Amount < terms.MinSwapAmount {
638623
return nil, ErrSwapAmountTooLow
639624
}
640625

641-
if invoiceAmountSat > terms.MaxSwapAmount {
626+
if request.Amount > terms.MaxSwapAmount {
642627
return nil, ErrSwapAmountTooHigh
643628
}
644629

@@ -649,7 +634,7 @@ func (s *Client) LoopOutQuote(ctx context.Context,
649634
}
650635

651636
quote, err := s.Server.GetLoopOutQuote(
652-
ctx, invoiceAmountSat, expiry, request.SwapPublicationDeadline,
637+
ctx, request.Amount, expiry, request.SwapPublicationDeadline,
653638
request.Initiator,
654639
)
655640
if err != nil {
@@ -663,13 +648,56 @@ func (s *Client) LoopOutQuote(ctx context.Context,
663648
return nil, err
664649
}
665650

666-
return &LoopOutQuote{
651+
loopOutQuote := &LoopOutQuote{
667652
SwapFee: quote.SwapFee,
668653
MinerFee: minerFee,
669654
PrepayAmount: quote.PrepayAmount,
670655
SwapPaymentDest: quote.SwapPaymentDest,
671-
InvoiceAmtSat: invoiceAmountSat,
672-
}, nil
656+
}
657+
658+
// If we use an Asset we'll rfq to get the asset amounts to use for
659+
// the swap.
660+
if request.AssetId != nil {
661+
// First we'll get the prepay prepayRfq.
662+
prepayRfq, err := s.assetClient.GetRfqForAsset(
663+
ctx, quote.PrepayAmount, request.AssetId,
664+
request.AssetEdgeNode,
665+
)
666+
if err != nil {
667+
return nil, err
668+
}
669+
log.Infof("Prepay RFQ: %v", prepayRfq)
670+
671+
invoiceAmt := request.Amount + quote.SwapFee -
672+
quote.PrepayAmount
673+
674+
swapRfq, err := s.assetClient.GetRfqForAsset(
675+
ctx, invoiceAmt, request.AssetId, request.AssetEdgeNode,
676+
)
677+
if err != nil {
678+
return nil, err
679+
}
680+
log.Infof("Swap RFQ: %v", swapRfq)
681+
682+
// We'll also want the asset name to verify for the client.
683+
assetName, err := s.assetClient.GetAssetName(
684+
ctx, request.AssetId,
685+
)
686+
if err != nil {
687+
return nil, err
688+
}
689+
690+
loopOutQuote.LoopOutRfq = &LoopOutRfq{
691+
PrepayRfqId: prepayRfq.Id,
692+
PrepayAssetAmt: btcutil.Amount(prepayRfq.AssetAmount),
693+
SwapRfqId: swapRfq.Id,
694+
SwapAssetAmt: btcutil.Amount(swapRfq.AssetAmount),
695+
AssetName: assetName,
696+
}
697+
698+
}
699+
700+
return loopOutQuote, nil
673701
}
674702

675703
// getLoopOutSweepFee is a helper method to estimate the loop out htlc sweep

cmd/loop/loopout.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ func loopOut(ctx *cli.Context) error {
149149
// element.
150150
var outgoingChanSet []uint64
151151
if ctx.IsSet("channel") {
152+
if ctx.IsSet("asset_id") {
153+
return fmt.Errorf("channel flag is not supported when " +
154+
"looping out assets")
155+
}
152156
chanStrings := strings.Split(ctx.String("channel"), ",")
153157
for _, chanString := range chanStrings {
154158
chanID, err := strconv.ParseUint(chanString, 10, 64)
@@ -201,7 +205,7 @@ func loopOut(ctx *cli.Context) error {
201205
}
202206
}
203207

204-
var assetLoopOutInfo *looprpc.AssetLoopOutInfo
208+
var assetLoopOutInfo *looprpc.AssetLoopOutRequest
205209

206210
var assetId []byte
207211
if ctx.IsSet("asset_id") {
@@ -222,7 +226,7 @@ func loopOut(ctx *cli.Context) error {
222226
return err
223227
}
224228

225-
assetLoopOutInfo = &looprpc.AssetLoopOutInfo{
229+
assetLoopOutInfo = &looprpc.AssetLoopOutRequest{
226230
AssetId: assetId,
227231
AssetEdgeNode: assetEdgeNode,
228232
}
@@ -325,6 +329,7 @@ func loopOut(ctx *cli.Context) error {
325329
Initiator: defaultInitiator,
326330
PaymentTimeout: uint32(paymentTimeout),
327331
AssetInfo: assetLoopOutInfo,
332+
AssetRfqInfo: quote.AssetRfqInfo,
328333
})
329334
if err != nil {
330335
return err

cmd/loop/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@ const (
9696
// Estimated on-chain fee: 7262 sat
9797
satAmtFmt = "%-36s %12d sat\n"
9898

99+
// assetAmtFormat formats a value into a one line string, intended to
100+
// prettify the terminal output. For Instance,
101+
// fmt.Printf(f, "Amount:", amt, "USD")
102+
// prints out as,
103+
// Amount: 50 USD
104+
assetAmtFmt = "%-36s %12d %s\n"
105+
99106
// blkFmt formats the number of blocks into a one line string, intended
100107
// to prettify the terminal output. For Instance,
101108
// fmt.Printf(f, "Conf target", target)

0 commit comments

Comments
 (0)