Skip to content

Commit 3600d5d

Browse files
authored
Merge pull request #1253 from Roasbeef/decode-asset-invoice
rpc: implement DecodeAssetInvoice
2 parents 34a3aaf + ecba8f8 commit 3600d5d

File tree

11 files changed

+1007
-166
lines changed

11 files changed

+1007
-166
lines changed

config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,8 @@ type Config struct {
195195

196196
RfqManager *rfq.Manager
197197

198+
PriceOracle rfq.PriceOracle
199+
198200
UniverseStats universe.Telemetry
199201

200202
AuxLeafSigner *tapchannel.AuxLeafSigner

perms/perms.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,10 @@ var (
280280
Entity: "channels",
281281
Action: "write",
282282
}},
283+
"/tapchannelrpc.TaprootAssetChannels/DecodeAssetPayReq": {{
284+
Entity: "channels",
285+
Action: "read",
286+
}},
283287
"/tapchannelrpc.TaprootAssetChannels/EncodeCustomRecords": {
284288
// This RPC is completely stateless and doesn't require
285289
// any permissions to use.

rpcserver.go

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7822,3 +7822,146 @@ func (r *rpcServer) getInboundPolicy(ctx context.Context, chanID uint64,
78227822

78237823
return policy, nil
78247824
}
7825+
7826+
// assetInvoiceAmt calculates the amount of asset units to pay for an invoice
7827+
// which is expressed in sats.
7828+
func (r *rpcServer) assetInvoiceAmt(ctx context.Context,
7829+
targetAsset asset.Specifier,
7830+
invoiceAmt lnwire.MilliSatoshi) (uint64, error) {
7831+
7832+
oracle := r.cfg.PriceOracle
7833+
7834+
oracleResp, err := oracle.QueryAskPrice(
7835+
ctx, targetAsset, fn.None[uint64](), fn.Some(invoiceAmt),
7836+
fn.None[rfqmsg.AssetRate](),
7837+
)
7838+
if err != nil {
7839+
return 0, fmt.Errorf("error querying ask price: %w", err)
7840+
}
7841+
if oracleResp.Err != nil {
7842+
return 0, fmt.Errorf("error querying ask price: %w",
7843+
oracleResp.Err)
7844+
}
7845+
7846+
assetRate := oracleResp.AssetRate.Rate
7847+
7848+
numAssetUnits := rfqmath.MilliSatoshiToUnits(
7849+
invoiceAmt, assetRate,
7850+
).ScaleTo(0)
7851+
7852+
return numAssetUnits.ToUint64(), nil
7853+
}
7854+
7855+
// DecodeAssetPayReq decodes an incoming invoice, then uses the RFQ system to
7856+
// map the BTC amount to the amount of asset units for the specified asset ID.
7857+
func (r *rpcServer) DecodeAssetPayReq(ctx context.Context,
7858+
payReq *tchrpc.AssetPayReq) (*tchrpc.AssetPayReqResponse, error) {
7859+
7860+
if r.cfg.PriceOracle == nil {
7861+
return nil, fmt.Errorf("price oracle is not set")
7862+
}
7863+
7864+
// First, we'll perform some basic input validation.
7865+
switch {
7866+
case len(payReq.AssetId) == 0:
7867+
return nil, fmt.Errorf("asset ID must be specified")
7868+
7869+
case len(payReq.AssetId) != 32:
7870+
return nil, fmt.Errorf("asset ID must be 32 bytes, "+
7871+
"was %d", len(payReq.AssetId))
7872+
7873+
case len(payReq.PayReqString) == 0:
7874+
return nil, fmt.Errorf("payment request must be specified")
7875+
}
7876+
7877+
var (
7878+
resp tchrpc.AssetPayReqResponse
7879+
assetID asset.ID
7880+
)
7881+
7882+
copy(assetID[:], payReq.AssetId)
7883+
7884+
// With the inputs validated, we'll first call out to lnd to decode the
7885+
// payment request.
7886+
rpcCtx, _, rawClient := r.cfg.Lnd.Client.RawClientWithMacAuth(ctx)
7887+
payReqInfo, err := rawClient.DecodePayReq(rpcCtx, &lnrpc.PayReqString{
7888+
PayReq: payReq.PayReqString,
7889+
})
7890+
if err != nil {
7891+
return nil, fmt.Errorf("unable to fetch channel: %w", err)
7892+
}
7893+
7894+
resp.PayReq = payReqInfo
7895+
7896+
// Next, we'll fetch the information for this asset ID through the addr
7897+
// book. This'll automatically fetch the asset if needed.
7898+
assetGroup, err := r.cfg.AddrBook.QueryAssetInfo(ctx, assetID)
7899+
if err != nil {
7900+
return nil, fmt.Errorf("unable to fetch asset info for "+
7901+
"asset_id=%x: %w", assetID[:], err)
7902+
}
7903+
7904+
resp.GenesisInfo = &taprpc.GenesisInfo{
7905+
GenesisPoint: assetGroup.FirstPrevOut.String(),
7906+
AssetType: taprpc.AssetType(assetGroup.Type),
7907+
Name: assetGroup.Tag,
7908+
MetaHash: assetGroup.MetaHash[:],
7909+
AssetId: assetID[:],
7910+
}
7911+
7912+
// If this asset ID belongs to an asset group, then we'll display thiat
7913+
// information as well.
7914+
//
7915+
// nolint:lll
7916+
if assetGroup.GroupKey != nil {
7917+
groupInfo := assetGroup.GroupKey
7918+
resp.AssetGroup = &taprpc.AssetGroup{
7919+
RawGroupKey: groupInfo.RawKey.PubKey.SerializeCompressed(),
7920+
TweakedGroupKey: groupInfo.GroupPubKey.SerializeCompressed(),
7921+
TapscriptRoot: groupInfo.TapscriptRoot,
7922+
}
7923+
7924+
if len(groupInfo.Witness) != 0 {
7925+
resp.AssetGroup.AssetWitness, err = asset.SerializeGroupWitness(
7926+
groupInfo.Witness,
7927+
)
7928+
if err != nil {
7929+
return nil, err
7930+
}
7931+
}
7932+
}
7933+
7934+
// Now that we have the basic invoice information, we'll query the RFQ
7935+
// system to obtain a quote to send this amount of BTC. Note that this
7936+
// doesn't factor in the fee limit, so this attempts just to map the
7937+
// sats amount to an asset unit.
7938+
numMsat := lnwire.NewMSatFromSatoshis(
7939+
btcutil.Amount(payReqInfo.NumSatoshis),
7940+
)
7941+
targetAsset := asset.NewSpecifierOptionalGroupKey(
7942+
assetGroup.ID(), assetGroup.GroupKey,
7943+
)
7944+
invoiceAmt, err := r.assetInvoiceAmt(ctx, targetAsset, numMsat)
7945+
if err != nil {
7946+
return nil, fmt.Errorf("error deriving asset amount: %w", err)
7947+
}
7948+
7949+
resp.AssetAmount = invoiceAmt
7950+
7951+
// The final piece of information we need is the decimal display
7952+
// information for this asset ID.
7953+
decDisplay, err := r.DecDisplayForAssetID(ctx, assetID)
7954+
if err != nil {
7955+
return nil, err
7956+
}
7957+
7958+
resp.DecimalDisplay = fn.MapOptionZ(
7959+
decDisplay, func(d uint32) *taprpc.DecimalDisplay {
7960+
return &taprpc.DecimalDisplay{
7961+
DecimalDisplay: d,
7962+
}
7963+
},
7964+
)
7965+
7966+
return &resp, nil
7967+
}

tapcfg/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,7 @@ func genServerConfig(cfg *Config, cfgLogger btclog.Logger,
582582
UniverseQueriesPerSecond: cfg.Universe.UniverseQueriesPerSecond,
583583
UniverseQueriesBurst: cfg.Universe.UniverseQueriesBurst,
584584
RfqManager: rfqManager,
585+
PriceOracle: priceOracle,
585586
AuxLeafSigner: auxLeafSigner,
586587
AuxFundingController: auxFundingController,
587588
AuxChanCloser: auxChanCloser,

0 commit comments

Comments
 (0)