rpcserver: add fee limit support for routing related RPCs

This commit is contained in:
Wilmer Paulino 2018-04-19 10:27:37 -04:00
parent ddf8f2cb01
commit 63f079d1a4
No known key found for this signature in database
GPG Key ID: 6DF57B9F9514972F

View File

@ -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 {