mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-06-06 13:09:52 +02:00
rpcserver: add fee limit support for routing related RPCs
This commit is contained in:
parent
ddf8f2cb01
commit
63f079d1a4
132
rpcserver.go
132
rpcserver.go
@ -1765,6 +1765,27 @@ type rpcPaymentRequest struct {
|
|||||||
routes []*routing.Route
|
routes []*routing.Route
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// calculateFeeLimit returns the fee limit in millisatoshis. If a percentage
|
||||||
|
// based fee limit has been requested, we'll factor in the ratio provided with
|
||||||
|
// the amount of the payment.
|
||||||
|
func calculateFeeLimit(feeLimit *lnrpc.FeeLimit,
|
||||||
|
amount lnwire.MilliSatoshi) lnwire.MilliSatoshi {
|
||||||
|
|
||||||
|
switch feeLimit.GetLimit().(type) {
|
||||||
|
case *lnrpc.FeeLimit_Fixed:
|
||||||
|
return lnwire.NewMSatFromSatoshis(
|
||||||
|
btcutil.Amount(feeLimit.GetFixed()),
|
||||||
|
)
|
||||||
|
case *lnrpc.FeeLimit_Percent:
|
||||||
|
return amount * lnwire.MilliSatoshi(feeLimit.GetPercent()) / 100
|
||||||
|
default:
|
||||||
|
// If a fee limit was not specified, we'll use the payment's
|
||||||
|
// amount as an upper bound in order to avoid payment attempts
|
||||||
|
// from incurring fees higher than the payment amount itself.
|
||||||
|
return amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SendPayment dispatches a bi-directional streaming RPC for sending payments
|
// SendPayment dispatches a bi-directional streaming RPC for sending payments
|
||||||
// through the Lightning Network. A single RPC invocation creates a persistent
|
// through the Lightning Network. A single RPC invocation creates a persistent
|
||||||
// bi-directional stream allowing clients to rapidly send payments through the
|
// bi-directional stream allowing clients to rapidly send payments through the
|
||||||
@ -1831,6 +1852,7 @@ func (r *rpcServer) SendToRoute(stream lnrpc.Lightning_SendToRouteServer) error
|
|||||||
// directly to the channel router for dispatching.
|
// directly to the channel router for dispatching.
|
||||||
type rpcPaymentIntent struct {
|
type rpcPaymentIntent struct {
|
||||||
msat lnwire.MilliSatoshi
|
msat lnwire.MilliSatoshi
|
||||||
|
feeLimit lnwire.MilliSatoshi
|
||||||
dest *btcec.PublicKey
|
dest *btcec.PublicKey
|
||||||
rHash [32]byte
|
rHash [32]byte
|
||||||
cltvDelta uint16
|
cltvDelta uint16
|
||||||
@ -1897,57 +1919,68 @@ func extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error
|
|||||||
payIntent.msat = *payReq.MilliSat
|
payIntent.msat = *payReq.MilliSat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate the fee limit that should be used for this payment.
|
||||||
|
payIntent.feeLimit = calculateFeeLimit(
|
||||||
|
rpcPayReq.FeeLimit, payIntent.msat,
|
||||||
|
)
|
||||||
|
|
||||||
copy(payIntent.rHash[:], payReq.PaymentHash[:])
|
copy(payIntent.rHash[:], payReq.PaymentHash[:])
|
||||||
payIntent.dest = payReq.Destination
|
payIntent.dest = payReq.Destination
|
||||||
payIntent.cltvDelta = uint16(payReq.MinFinalCLTVExpiry())
|
payIntent.cltvDelta = uint16(payReq.MinFinalCLTVExpiry())
|
||||||
payIntent.routeHints = payReq.RouteHints
|
payIntent.routeHints = payReq.RouteHints
|
||||||
|
|
||||||
return payIntent, nil
|
return payIntent, nil
|
||||||
} else {
|
}
|
||||||
// At this point, a destination MUST be specified, so we'll convert it
|
|
||||||
// into the proper representation now. The destination will either be
|
|
||||||
// encoded as raw bytes, or via a hex string.
|
|
||||||
if len(rpcPayReq.Dest) != 0 {
|
|
||||||
payIntent.dest, err = btcec.ParsePubKey(
|
|
||||||
rpcPayReq.Dest, btcec.S256(),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return payIntent, err
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
// At this point, a destination MUST be specified, so we'll convert it
|
||||||
pubBytes, err := hex.DecodeString(rpcPayReq.DestString)
|
// into the proper representation now. The destination will either be
|
||||||
if err != nil {
|
// encoded as raw bytes, or via a hex string.
|
||||||
return payIntent, err
|
if len(rpcPayReq.Dest) != 0 {
|
||||||
}
|
payIntent.dest, err = btcec.ParsePubKey(
|
||||||
payIntent.dest, err = btcec.ParsePubKey(pubBytes, btcec.S256())
|
rpcPayReq.Dest, btcec.S256(),
|
||||||
if err != nil {
|
|
||||||
return payIntent, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, If the payment request field was not specified
|
|
||||||
// (and a custom route wasn't specified), construct the payment
|
|
||||||
// from the other fields.
|
|
||||||
payIntent.msat = lnwire.NewMSatFromSatoshis(
|
|
||||||
btcutil.Amount(rpcPayReq.Amt),
|
|
||||||
)
|
)
|
||||||
payIntent.cltvDelta = uint16(rpcPayReq.FinalCltvDelta)
|
if err != nil {
|
||||||
|
return payIntent, err
|
||||||
// If the user is manually specifying payment details, then the
|
|
||||||
// payment hash may be encoded as a string.
|
|
||||||
if rpcPayReq.PaymentHashString != "" {
|
|
||||||
paymentHash, err := hex.DecodeString(
|
|
||||||
rpcPayReq.PaymentHashString,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return payIntent, err
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(payIntent.rHash[:], paymentHash)
|
|
||||||
} else {
|
|
||||||
copy(payIntent.rHash[:], rpcPayReq.PaymentHash)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
pubBytes, err := hex.DecodeString(rpcPayReq.DestString)
|
||||||
|
if err != nil {
|
||||||
|
return payIntent, err
|
||||||
|
}
|
||||||
|
payIntent.dest, err = btcec.ParsePubKey(pubBytes, btcec.S256())
|
||||||
|
if err != nil {
|
||||||
|
return payIntent, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, If the payment request field was not specified
|
||||||
|
// (and a custom route wasn't specified), construct the payment
|
||||||
|
// from the other fields.
|
||||||
|
payIntent.msat = lnwire.NewMSatFromSatoshis(
|
||||||
|
btcutil.Amount(rpcPayReq.Amt),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Calculate the fee limit that should be used for this payment.
|
||||||
|
payIntent.feeLimit = calculateFeeLimit(
|
||||||
|
rpcPayReq.FeeLimit, payIntent.msat,
|
||||||
|
)
|
||||||
|
|
||||||
|
payIntent.cltvDelta = uint16(rpcPayReq.FinalCltvDelta)
|
||||||
|
|
||||||
|
// If the user is manually specifying payment details, then the
|
||||||
|
// payment hash may be encoded as a string.
|
||||||
|
if rpcPayReq.PaymentHashString != "" {
|
||||||
|
paymentHash, err := hex.DecodeString(
|
||||||
|
rpcPayReq.PaymentHashString,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return payIntent, err
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(payIntent.rHash[:], paymentHash)
|
||||||
|
} else {
|
||||||
|
copy(payIntent.rHash[:], rpcPayReq.PaymentHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're in debug HTLC mode, then all outgoing HTLCs will pay to the
|
// If we're in debug HTLC mode, then all outgoing HTLCs will pay to the
|
||||||
@ -1993,6 +2026,7 @@ func (r *rpcServer) dispatchPaymentIntent(payIntent *rpcPaymentIntent) (*routing
|
|||||||
payment := &routing.LightningPayment{
|
payment := &routing.LightningPayment{
|
||||||
Target: payIntent.dest,
|
Target: payIntent.dest,
|
||||||
Amount: payIntent.msat,
|
Amount: payIntent.msat,
|
||||||
|
FeeLimit: payIntent.feeLimit,
|
||||||
PaymentHash: payIntent.rHash,
|
PaymentHash: payIntent.rHash,
|
||||||
RouteHints: payIntent.routeHints,
|
RouteHints: payIntent.routeHints,
|
||||||
}
|
}
|
||||||
@ -2052,9 +2086,6 @@ func (r *rpcServer) sendPayment(stream *paymentStream) error {
|
|||||||
payChan := make(chan *rpcPaymentIntent)
|
payChan := make(chan *rpcPaymentIntent)
|
||||||
errChan := make(chan error, 1)
|
errChan := make(chan error, 1)
|
||||||
|
|
||||||
// TODO(roasbeef): enforce fee limits, pass into router, ditch if exceed limit
|
|
||||||
// * limit either a %, or absolute, or iff more than sending
|
|
||||||
|
|
||||||
// We don't allow payments to be sent while the daemon itself is still
|
// We don't allow payments to be sent while the daemon itself is still
|
||||||
// syncing as we may be trying to sent a payment over a "stale"
|
// syncing as we may be trying to sent a payment over a "stale"
|
||||||
// channel.
|
// channel.
|
||||||
@ -2241,9 +2272,6 @@ func (r *rpcServer) SendToRouteSync(ctx context.Context,
|
|||||||
func (r *rpcServer) sendPaymentSync(ctx context.Context,
|
func (r *rpcServer) sendPaymentSync(ctx context.Context,
|
||||||
nextPayment *rpcPaymentRequest) (*lnrpc.SendResponse, error) {
|
nextPayment *rpcPaymentRequest) (*lnrpc.SendResponse, error) {
|
||||||
|
|
||||||
// TODO(roasbeef): enforce fee limits, pass into router, ditch if exceed limit
|
|
||||||
// * limit either a %, or absolute, or iff more than sending
|
|
||||||
|
|
||||||
// We don't allow payments to be sent while the daemon itself is still
|
// We don't allow payments to be sent while the daemon itself is still
|
||||||
// syncing as we may be trying to sent a payment over a "stale"
|
// syncing as we may be trying to sent a payment over a "stale"
|
||||||
// channel.
|
// channel.
|
||||||
@ -3047,13 +3075,14 @@ func (r *rpcServer) QueryRoutes(ctx context.Context,
|
|||||||
// largest payment size allotted to (2^32) - 1 mSAT or 4.29 million
|
// largest payment size allotted to (2^32) - 1 mSAT or 4.29 million
|
||||||
// satoshis.
|
// satoshis.
|
||||||
amt := btcutil.Amount(in.Amt)
|
amt := btcutil.Amount(in.Amt)
|
||||||
feeLimit := btcutil.Amount(in.FeeLimit)
|
|
||||||
amtMSat := lnwire.NewMSatFromSatoshis(amt)
|
amtMSat := lnwire.NewMSatFromSatoshis(amt)
|
||||||
if amtMSat > maxPaymentMSat {
|
if amtMSat > maxPaymentMSat {
|
||||||
return nil, fmt.Errorf("payment of %v is too large, max payment "+
|
return nil, fmt.Errorf("payment of %v is too large, max payment "+
|
||||||
"allowed is %v", amt, maxPaymentMSat.ToSatoshis())
|
"allowed is %v", amt, maxPaymentMSat.ToSatoshis())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
feeLimit := calculateFeeLimit(in.FeeLimit, amtMSat)
|
||||||
|
|
||||||
// Query the channel router for a possible path to the destination that
|
// Query the channel router for a possible path to the destination that
|
||||||
// can carry `in.Amt` satoshis _including_ the total fee required on
|
// can carry `in.Amt` satoshis _including_ the total fee required on
|
||||||
// the route.
|
// the route.
|
||||||
@ -3063,11 +3092,12 @@ func (r *rpcServer) QueryRoutes(ctx context.Context,
|
|||||||
)
|
)
|
||||||
if in.FinalCltvDelta == 0 {
|
if in.FinalCltvDelta == 0 {
|
||||||
routes, findErr = r.server.chanRouter.FindRoutes(
|
routes, findErr = r.server.chanRouter.FindRoutes(
|
||||||
pubKey, amtMSat, uint32(in.NumRoutes),
|
pubKey, amtMSat, feeLimit, uint32(in.NumRoutes),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
routes, findErr = r.server.chanRouter.FindRoutes(
|
routes, findErr = r.server.chanRouter.FindRoutes(
|
||||||
pubKey, amtMSat, uint32(in.NumRoutes), uint16(in.FinalCltvDelta),
|
pubKey, amtMSat, feeLimit, uint32(in.NumRoutes),
|
||||||
|
uint16(in.FinalCltvDelta),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if findErr != nil {
|
if findErr != nil {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user