Skip to content

Commit b229664

Browse files
committed
rpcserver: allow adding invoice with sats amount
The previously ignored value/value_msat fields are now properly being accounted for. We now accept only one value related field to be set.
1 parent 4c6e254 commit b229664

File tree

1 file changed

+104
-17
lines changed

1 file changed

+104
-17
lines changed

rpcserver.go

Lines changed: 104 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7637,6 +7637,30 @@ func (r *rpcServer) AddInvoice(ctx context.Context,
76377637
if len(req.AssetId) != sha256.Size {
76387638
return nil, fmt.Errorf("asset ID must be 32 bytes")
76397639
}
7640+
7641+
amtMsat, err := lnrpc.UnmarshallAmt(
7642+
req.InvoiceRequest.Value, req.InvoiceRequest.ValueMsat,
7643+
)
7644+
if err != nil {
7645+
return nil, err
7646+
}
7647+
7648+
// Let's make sure that only one type of amount is set, in order to
7649+
// avoid ambiguous behavior. This field dictates the actual value of the
7650+
// invoice so let's be strict and only allow one possible value to be
7651+
// set.
7652+
if req.AssetAmount > 0 && amtMsat != 0 {
7653+
return nil, fmt.Errorf("cannot set both asset amount and sats" +
7654+
"amount")
7655+
}
7656+
7657+
// In order to avoid repeating the following check let's assign it to a
7658+
// boolean for easier access.
7659+
var satsMode bool
7660+
if amtMsat != 0 {
7661+
satsMode = true
7662+
}
7663+
76407664
var assetID asset.ID
76417665
copy(assetID[:], req.AssetId)
76427666

@@ -7676,13 +7700,40 @@ func (r *rpcServer) AddInvoice(ctx context.Context,
76767700
time.Duration(expirySeconds) * time.Second,
76777701
)
76787702

7703+
maxUnits := req.AssetAmount
7704+
7705+
// If the invoice defines the desired amount in satoshis, we need to
7706+
// query our oracle first to get an estimation on the asset rate. This
7707+
// will help us establish a quote with the correct amount of asset
7708+
// units.
7709+
if satsMode {
7710+
oracleRes, err := r.cfg.PriceOracle.QueryBidPrice(
7711+
ctx, specifier, fn.None[uint64](), fn.Some(amtMsat),
7712+
fn.None[rfqmsg.AssetRate](),
7713+
)
7714+
if err != nil {
7715+
return nil, err
7716+
}
7717+
7718+
if oracleRes.Err != nil {
7719+
return nil, fmt.Errorf("cannot query oracle: %v",
7720+
oracleRes.Err.Error())
7721+
}
7722+
7723+
assetUnits := rfqmath.MilliSatoshiToUnits(
7724+
amtMsat, oracleRes.AssetRate.Rate,
7725+
)
7726+
7727+
maxUnits = assetUnits.ToUint64()
7728+
}
7729+
76797730
resp, err := r.AddAssetBuyOrder(ctx, &rfqrpc.AddAssetBuyOrderRequest{
76807731
AssetSpecifier: &rfqrpc.AssetSpecifier{
76817732
Id: &rfqrpc.AssetSpecifier_AssetId{
76827733
AssetId: assetID[:],
76837734
},
76847735
},
7685-
AssetMaxAmt: req.AssetAmount,
7736+
AssetMaxAmt: maxUnits,
76867737
Expiry: uint64(expiryTimestamp.Unix()),
76877738
PeerPubKey: peerPubKey[:],
76887739
TimeoutSeconds: uint32(
@@ -7712,17 +7763,6 @@ func (r *rpcServer) AddInvoice(ctx context.Context,
77127763
return nil, fmt.Errorf("unexpected response type: %T", r)
77137764
}
77147765

7715-
// If the invoice is for an asset unit amount smaller than the minimal
7716-
// transportable amount, we'll return an error, as it wouldn't be
7717-
// payable by the network.
7718-
if acceptedQuote.MinTransportableUnits > req.AssetAmount {
7719-
return nil, fmt.Errorf("cannot create invoice over %d asset "+
7720-
"units, as the minimal transportable amount is %d "+
7721-
"units with the current rate of %v units/BTC",
7722-
req.AssetAmount, acceptedQuote.MinTransportableUnits,
7723-
acceptedQuote.AskAssetRate)
7724-
}
7725-
77267766
// Now that we have the accepted quote, we know the amount in Satoshi
77277767
// that we need to pay. We can now update the invoice with this amount.
77287768
//
@@ -7735,12 +7775,59 @@ func (r *rpcServer) AddInvoice(ctx context.Context,
77357775
err)
77367776
}
77377777

7738-
// Convert the asset amount into a fixed-point.
7739-
assetAmount := rfqmath.NewBigIntFixedPoint(req.AssetAmount, 0)
7778+
invUnits := req.AssetAmount
7779+
7780+
if satsMode {
7781+
// If the invoice was created over a satoshi amount, we need to
7782+
// calculate the units.
7783+
invUnits = rfqmath.MilliSatoshiToUnits(
7784+
amtMsat, *askAssetRate,
7785+
).ScaleTo(0).ToUint64()
7786+
7787+
// Now let's see if the negotiated quote can actually route the
7788+
// amount we need in msat.
7789+
maxFixedUnits := rfqmath.NewBigIntFixedPoint(
7790+
acceptedQuote.AssetMaxAmount, 0,
7791+
)
7792+
maxRoutableMsat := rfqmath.UnitsToMilliSatoshi(
7793+
maxFixedUnits, *askAssetRate,
7794+
)
7795+
7796+
if maxRoutableMsat <= amtMsat {
7797+
return nil, fmt.Errorf("cannot create invoice for %v "+
7798+
"msat, max routable amount is %v msat", amtMsat,
7799+
maxRoutableMsat)
7800+
}
7801+
}
77407802

7741-
// Calculate the invoice amount in msat.
7742-
valMsat := rfqmath.UnitsToMilliSatoshi(assetAmount, *askAssetRate)
7743-
iReq.ValueMsat = int64(valMsat)
7803+
// If the invoice is for an asset unit amount smaller than the minimal
7804+
// transportable amount, we'll return an error, as it wouldn't be
7805+
// payable by the network.
7806+
if acceptedQuote.MinTransportableUnits > invUnits {
7807+
return nil, fmt.Errorf("cannot create invoice over %d asset "+
7808+
"units, as the minimal transportable amount is %d "+
7809+
"units with the current rate of %v units/BTC",
7810+
invUnits, acceptedQuote.MinTransportableUnits,
7811+
acceptedQuote.AskAssetRate)
7812+
}
7813+
7814+
switch {
7815+
case satsMode:
7816+
amtMsat := lnwire.MilliSatoshi(req.InvoiceRequest.ValueMsat +
7817+
req.InvoiceRequest.Value*(1000))
7818+
7819+
iReq.ValueMsat = int64(amtMsat)
7820+
7821+
default:
7822+
// Convert the asset amount into a fixed-point.
7823+
assetAmount := rfqmath.NewBigIntFixedPoint(req.AssetAmount, 0)
7824+
7825+
// Calculate the invoice amount in msat.
7826+
valMsat := rfqmath.UnitsToMilliSatoshi(
7827+
assetAmount, *askAssetRate,
7828+
)
7829+
iReq.ValueMsat = int64(valMsat)
7830+
}
77447831

77457832
// The last step is to create a hop hint that includes the fake SCID of
77467833
// the quote, alongside the channel's routing policy. We need to choose

0 commit comments

Comments
 (0)