Skip to content

Commit

Permalink
rpc: for DecodeAssetPayReq hit the oracle directly
Browse files Browse the repository at this point in the history
We just want to map the amount in BTC to asset units. So we rely on our
local oracle. Otherwise if we hit the peer directly, then it's more
similar to `EstimateRouteFee`.
  • Loading branch information
Roasbeef committed Dec 18, 2024
1 parent 25289f2 commit 57c2454
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 29 deletions.
2 changes: 2 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ type Config struct {

RfqManager *rfq.Manager

PriceOracle rfq.PriceOracle

UniverseStats universe.Telemetry

AuxLeafSigner *tapchannel.AuxLeafSigner
Expand Down
68 changes: 39 additions & 29 deletions rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -7732,25 +7732,40 @@ func (r *rpcServer) getInboundPolicy(ctx context.Context, chanID uint64,

// assetInvoiceAmt calculates the amount of asset units to pay for an invoice
// which is expressed in sats.
func (r *rpcServer) assetInvoiceAmt(ctx context.Context, assetID asset.ID,
invoiceAmt lnwire.MilliSatoshi, peerPubKey *route.Vertex,
expiryTimestamp time.Time) (uint64, error) {
func (r *rpcServer) assetInvoiceAmt(ctx context.Context, targetAsset asset.Specifier,
invoiceAmt lnwire.MilliSatoshi) (uint64, error) {

oracle := r.cfg.PriceOracle

acceptedQuote, err := r.fetchSendRfqQuote(
ctx, assetID, invoiceAmt, peerPubKey, expiryTimestamp,
oracleResp, err := oracle.QueryAskPrice(
ctx, targetAsset, fn.None[uint64](), fn.Some(invoiceAmt),
fn.None[rfqmsg.AssetRate](),
)
if err != nil {
return 0, fmt.Errorf("error sending RFQ quote: %w", err)
return 0, fmt.Errorf("error querying ask price: %w", err)
}
if oracleResp.Err != nil {
return 0, fmt.Errorf("error querying ask price: %w", err)
}

return acceptedQuote.AssetAmount, nil
assetRate := oracleResp.AssetRate.Rate

numAssetUnits := rfqmath.MilliSatoshiToUnits(
invoiceAmt, assetRate,
).ScaleTo(0)

return numAssetUnits.ToUint64(), nil
}

// DecodeAssetPayReq decodes an incoming invoice, then uses the RFQ system to
// map the BTC amount to the amount of asset units for the specified asset ID.
func (r *rpcServer) DecodeAssetPayReq(ctx context.Context,
payReq *tchrpc.AssetPayReq) (*tchrpc.AssetPayReqResponse, error) {

if r.cfg.PriceOracle == nil {
return nil, fmt.Errorf("price oracle is not set")
}

// First, we'll perform some basic input validation.
switch {
case len(payReq.AssetId) == 0:
Expand Down Expand Up @@ -7783,28 +7798,6 @@ func (r *rpcServer) DecodeAssetPayReq(ctx context.Context,

resp.PayReq = payReqInfo

// TODO(roasbeef): add dry run mode?
// * obtains quote, but doesn't actually treat as standing order

// Now that we have the basic invoice information, we'll query the RFQ
// system to obtain a quote to send this amount of BTC. Note that this
// doesn't factor in the fee limit, so this attempts just to map the
// sats amount to an asset unit.
timestamp := time.Unix(payReqInfo.Timestamp, 0)
expiryTimestamp := timestamp.Add(time.Duration(payReqInfo.Expiry))
numMsat := lnwire.NewMSatFromSatoshis(
btcutil.Amount(payReqInfo.NumSatoshis),
)
invoiceAmt, err := r.assetInvoiceAmt(
ctx, assetID, numMsat, nil,
expiryTimestamp,
)
if err != nil {
return nil, fmt.Errorf("error deriving asset amount: %w", err)
}

resp.AssetAmount = invoiceAmt

// Next, we'll fetch the information for this asset ID through the addr
// book. This'll automatically fetch the asset if needed.
assetGroup, err := r.cfg.AddrBook.QueryAssetInfo(ctx, assetID)
Expand Down Expand Up @@ -7843,6 +7836,23 @@ func (r *rpcServer) DecodeAssetPayReq(ctx context.Context,
}
}

// Now that we have the basic invoice information, we'll query the RFQ
// system to obtain a quote to send this amount of BTC. Note that this
// doesn't factor in the fee limit, so this attempts just to map the
// sats amount to an asset unit.
numMsat := lnwire.NewMSatFromSatoshis(
btcutil.Amount(payReqInfo.NumSatoshis),
)
targetAsset := asset.NewSpecifierOptionalGroupKey(
assetGroup.ID(), assetGroup.GroupKey,
)
invoiceAmt, err := r.assetInvoiceAmt(ctx, targetAsset, numMsat)
if err != nil {
return nil, fmt.Errorf("error deriving asset amount: %w", err)
}

resp.AssetAmount = invoiceAmt

// The final piece of information we need is the decimal display
// information for this asset ID.
decDisplay, err := r.DecDisplayForAssetID(ctx, assetID)
Expand Down
1 change: 1 addition & 0 deletions tapcfg/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,7 @@ func genServerConfig(cfg *Config, cfgLogger btclog.Logger,
UniverseQueriesPerSecond: cfg.Universe.UniverseQueriesPerSecond,
UniverseQueriesBurst: cfg.Universe.UniverseQueriesBurst,
RfqManager: rfqManager,
PriceOracle: priceOracle,
AuxLeafSigner: auxLeafSigner,
AuxFundingController: auxFundingController,
AuxChanCloser: auxChanCloser,
Expand Down

0 comments on commit 57c2454

Please sign in to comment.