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
|
||||
}
|
||||
|
||||
// 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
|
||||
// through the Lightning Network. A single RPC invocation creates a persistent
|
||||
// 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.
|
||||
type rpcPaymentIntent struct {
|
||||
msat lnwire.MilliSatoshi
|
||||
feeLimit lnwire.MilliSatoshi
|
||||
dest *btcec.PublicKey
|
||||
rHash [32]byte
|
||||
cltvDelta uint16
|
||||
@ -1897,57 +1919,68 @@ func extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error
|
||||
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[:])
|
||||
payIntent.dest = payReq.Destination
|
||||
payIntent.cltvDelta = uint16(payReq.MinFinalCLTVExpiry())
|
||||
payIntent.routeHints = payReq.RouteHints
|
||||
|
||||
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 {
|
||||
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),
|
||||
// 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(),
|
||||
)
|
||||
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 err != nil {
|
||||
return payIntent, err
|
||||
}
|
||||
|
||||
} 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
|
||||
@ -1993,6 +2026,7 @@ func (r *rpcServer) dispatchPaymentIntent(payIntent *rpcPaymentIntent) (*routing
|
||||
payment := &routing.LightningPayment{
|
||||
Target: payIntent.dest,
|
||||
Amount: payIntent.msat,
|
||||
FeeLimit: payIntent.feeLimit,
|
||||
PaymentHash: payIntent.rHash,
|
||||
RouteHints: payIntent.routeHints,
|
||||
}
|
||||
@ -2052,9 +2086,6 @@ func (r *rpcServer) sendPayment(stream *paymentStream) error {
|
||||
payChan := make(chan *rpcPaymentIntent)
|
||||
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
|
||||
// syncing as we may be trying to sent a payment over a "stale"
|
||||
// channel.
|
||||
@ -2241,9 +2272,6 @@ func (r *rpcServer) SendToRouteSync(ctx context.Context,
|
||||
func (r *rpcServer) sendPaymentSync(ctx context.Context,
|
||||
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
|
||||
// syncing as we may be trying to sent a payment over a "stale"
|
||||
// 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
|
||||
// satoshis.
|
||||
amt := btcutil.Amount(in.Amt)
|
||||
feeLimit := btcutil.Amount(in.FeeLimit)
|
||||
amtMSat := lnwire.NewMSatFromSatoshis(amt)
|
||||
if amtMSat > maxPaymentMSat {
|
||||
return nil, fmt.Errorf("payment of %v is too large, max payment "+
|
||||
"allowed is %v", amt, maxPaymentMSat.ToSatoshis())
|
||||
}
|
||||
|
||||
feeLimit := calculateFeeLimit(in.FeeLimit, amtMSat)
|
||||
|
||||
// Query the channel router for a possible path to the destination that
|
||||
// can carry `in.Amt` satoshis _including_ the total fee required on
|
||||
// the route.
|
||||
@ -3063,11 +3092,12 @@ func (r *rpcServer) QueryRoutes(ctx context.Context,
|
||||
)
|
||||
if in.FinalCltvDelta == 0 {
|
||||
routes, findErr = r.server.chanRouter.FindRoutes(
|
||||
pubKey, amtMSat, uint32(in.NumRoutes),
|
||||
pubKey, amtMSat, feeLimit, uint32(in.NumRoutes),
|
||||
)
|
||||
} else {
|
||||
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 {
|
||||
|
Loading…
Reference in New Issue
Block a user