Skip to content

Commit

Permalink
Merge b3c9739 into fe0a7b6
Browse files Browse the repository at this point in the history
  • Loading branch information
djseeds committed Sep 7, 2017
2 parents fe0a7b6 + b3c9739 commit cdcc86f
Show file tree
Hide file tree
Showing 9 changed files with 497 additions and 302 deletions.
707 changes: 423 additions & 284 deletions lnrpc/rpc.pb.go

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions lnrpc/rpc.proto
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,16 @@ message SendRequest {
payment to the recipient.
*/
string payment_request = 6;

// Cut off specifying a fee value above which path finding will be short circuited
oneof fee_cut_off {
// Abandon the route if fees greater than amt
bool absolute_cut_off = 7 [json_name = "absolute_cut_off"];
// Abandon the route if fees greater than limit_satoshis
uint64 limit_satoshis = 8 [json_name = "limit_satoshis"];
//Abandon the route if fees greater than limit_ratio * amt
double limit_ratio = 9 [json_name = "limit_ratio"];
}
}
message SendResponse {
string payment_error = 1 [json_name = "payment_error"];
Expand Down
15 changes: 15 additions & 0 deletions lnrpc/rpc.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -1775,6 +1775,21 @@
"payment_request": {
"type": "string",
"description": "*\nA bare-bones invoice for a payment within the Lightning Network. With the\ndetails of the invoice, the sender has all the data necessary to send a\npayment to the recipient."
},
"absolute_cut_off": {
"type": "boolean",
"format": "boolean",
"title": "Abandon the route if fees greater than amt"
},
"limit_satoshis": {
"type": "string",
"format": "uint64",
"title": "Abandon the route if fees greater than limit_satoshis"
},
"limit_ratio": {
"type": "number",
"format": "double",
"title": "Abandon the route if fees greater than limit_ratio * amt"
}
}
},
Expand Down
4 changes: 4 additions & 0 deletions routing/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ const (
// this update can't bring us something new, or because a node
// announcement was given for node not found in any channel.
ErrIgnored

// ErrFeeCutoffExceeded is returned when the fees within a route
// exceed a user-specified fee cutoff.
ErrFeeCutoffExceeded
)

// routerError is a structure that represent the error inside the routing package,
Expand Down
16 changes: 13 additions & 3 deletions routing/pathfind.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,14 @@ func (s sortableRoutes) Swap(i, j int) {

// newRoute returns a fully valid route between the source and target that's
// capable of supporting a payment of `amtToSend` after fees are fully
// computed. If the route is too long, or the selected path cannot support the
// fully payment including fees, then a non-nil error is returned.
// computed. If the route is too long, the selected path cannot support the
// fully payment including fees, or the path requires more than *maxFee fees,
// then a non-nil error is returned.
//
// NOTE: The passed slice of ChannelHops MUST be sorted in forward order: from
// the source to the target node of the path finding attempt.
func newRoute(amtToSend lnwire.MilliSatoshi, pathEdges []*ChannelHop,
currentHeight uint32) (*Route, error) {
currentHeight uint32, maxFee *btcutil.Amount) (*Route, error) {

// First, we'll create a new empty route with enough hops to match the
// amount of path edges. We set the TotalTimeLock to the current block
Expand Down Expand Up @@ -268,6 +269,15 @@ func newRoute(amtToSend lnwire.MilliSatoshi, pathEdges []*ChannelHop,
nextHop.Fee = 0
}

// After we calculate the total fees in the route, we must check
// if we have exceeded the user-specified fee threshold.
// If we have, we should abandon this route and return an error.
if maxFee != nil && route.TotalFees.ToSatoshis() > *maxFee {
err := fmt.Sprintf("route fee of %v exceeds threshold of %v",
route.TotalFees, *maxFee)
return nil, newErrf(ErrFeeCutoffExceeded, err)
}

// Next, increment the total timelock of the entire route such
// that each hops time lock increases as we walk backwards in
// the route, using the delta of the previous hop.
Expand Down
4 changes: 2 additions & 2 deletions routing/pathfind_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ func TestBasicGraphPathFinding(t *testing.T) {
if err != nil {
t.Fatalf("unable to find path: %v", err)
}
route, err := newRoute(paymentAmt, path, startingHeight)
route, err := newRoute(paymentAmt, path, startingHeight, nil)
if err != nil {
t.Fatalf("unable to create path: %v", err)
}
Expand Down Expand Up @@ -416,7 +416,7 @@ func TestBasicGraphPathFinding(t *testing.T) {
if err != nil {
t.Fatalf("unable to find route: %v", err)
}
route, err = newRoute(paymentAmt, path, startingHeight)
route, err = newRoute(paymentAmt, path, startingHeight, nil)
if err != nil {
t.Fatalf("unable to create path: %v", err)
}
Expand Down
15 changes: 9 additions & 6 deletions routing/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -813,9 +813,10 @@ type routingMsg struct {
// inner loop. Once we have a set of candidate routes, we calculate the
// required fee and time lock values running backwards along the route. The
// route that will be ranked the highest is the one with the lowest cumulative
// fee along the route.
// fee along the route. If maxFee is not nil, routes with fees greater than
// *maxFee will be abandoned.
func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey,
amt lnwire.MilliSatoshi) ([]*Route, error) {
amt lnwire.MilliSatoshi, maxFee *btcutil.Amount) ([]*Route, error) {

dest := target.SerializeCompressed()
log.Debugf("Searching for path to %x, sending %v", dest, amt)
Expand Down Expand Up @@ -854,7 +855,7 @@ func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey,
// Attempt to make the path into a route. We snip off the first
// hop in the path as it contains a "self-hop" that is inserted
// by our KSP algorithm.
route, err := newRoute(amt, path[1:], uint32(currentHeight))
route, err := newRoute(amt, path[1:], uint32(currentHeight), maxFee)
if err != nil {
continue
}
Expand Down Expand Up @@ -967,13 +968,15 @@ type LightningPayment struct {
}

// SendPayment attempts to send a payment as described within the passed
// LightningPayment. This function is blocking and will return either: when the
// LightningPayment. If maxFee is not nil, SendPayment will attempt to
// find a route that requires a fee of less than *maxFee satoshis.
// This function is blocking and will return either: when the
// payment is successful, or all candidates routes have been attempted and
// resulted in a failed payment. If the payment succeeds, then a non-nil Route
// will be returned which describes the path the successful payment traversed
// within the network to reach the destination. Additionally, the payment
// preimage will also be returned.
func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *Route, error) {
func (r *ChannelRouter) SendPayment(payment *LightningPayment, maxFee *btcutil.Amount) ([32]byte, *Route, error) {
log.Tracef("Dispatching route for lightning payment: %v",
newLogClosure(func() string {
payment.Target.Curve = nil
Expand Down Expand Up @@ -1002,7 +1005,7 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *Route
// payment amount. If no such routes can be found then an error will be
// returned.
if !ok {
freshRoutes, err := r.FindRoutes(payment.Target, payment.Amount)
freshRoutes, err := r.FindRoutes(payment.Target, payment.Amount, maxFee)
if err != nil {
return preImage, nil, err
}
Expand Down
8 changes: 4 additions & 4 deletions routing/router_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func TestFindRoutesFeeSorting(t *testing.T) {
// Execute a query for all possible routes between roasbeef and luo ji.
paymentAmt := lnwire.NewMSatFromSatoshis(100)
target := ctx.aliases["luoji"]
routes, err := ctx.router.FindRoutes(target, paymentAmt)
routes, err := ctx.router.FindRoutes(target, paymentAmt, nil)
if err != nil {
t.Fatalf("unable to find any routes: %v", err)
}
Expand Down Expand Up @@ -188,7 +188,7 @@ func TestSendPaymentRouteFailureFallback(t *testing.T) {

// Send off the payment request to the router, route through satoshi
// should've been selected as a fall back and succeeded correctly.
paymentPreImage, route, err := ctx.router.SendPayment(&payment)
paymentPreImage, route, err := ctx.router.SendPayment(&payment, nil)
if err != nil {
t.Fatalf("unable to send payment: %v", err)
}
Expand Down Expand Up @@ -494,7 +494,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
// We should now be able to find one route to node 2.
paymentAmt := lnwire.NewMSatFromSatoshis(100)
targetNode := priv2.PubKey()
routes, err := ctx.router.FindRoutes(targetNode, paymentAmt)
routes, err := ctx.router.FindRoutes(targetNode, paymentAmt, nil)
if err != nil {
t.Fatalf("unable to find any routes: %v", err)
}
Expand Down Expand Up @@ -536,7 +536,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {

// Should still be able to find the route, and the info should be
// updated.
routes, err = ctx.router.FindRoutes(targetNode, paymentAmt)
routes, err = ctx.router.FindRoutes(targetNode, paymentAmt, nil)
if err != nil {
t.Fatalf("unable to find any routes: %v", err)
}
Expand Down
20 changes: 17 additions & 3 deletions rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1530,6 +1530,20 @@ func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer)
htlcSema <- struct{}{}
}()

// Calculate the user-specified fee cut-off
// using the FeeCutOff oneof field.
var maxFee *btcutil.Amount
switch nextPayment.GetFeeCutOff().(type) {
case *lnrpc.SendRequest_LimitSatoshis:
*maxFee = btcutil.Amount(nextPayment.GetLimitSatoshis())
case *lnrpc.SendRequest_LimitRatio:
*maxFee = btcutil.Amount(nextPayment.GetLimitRatio()) * amt
case *lnrpc.SendRequest_AbsoluteCutOff:
if nextPayment.GetAbsoluteCutOff() {
*maxFee = amt
}
}

// Construct a payment request to send to the
// channel router. If the payment is
// successful, the route chosen will be
Expand All @@ -1540,7 +1554,7 @@ func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer)
Amount: amtMSat,
PaymentHash: rHash,
}
preImage, route, err := r.server.chanRouter.SendPayment(payment)
preImage, route, err := r.server.chanRouter.SendPayment(payment, maxFee)
if err != nil {
// If we receive payment error than,
// instead of terminating the stream,
Expand Down Expand Up @@ -1663,7 +1677,7 @@ func (r *rpcServer) SendPaymentSync(ctx context.Context,
Target: destPub,
Amount: amtMSat,
PaymentHash: rHash,
})
}, nil)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -2276,7 +2290,7 @@ func (r *rpcServer) QueryRoutes(ctx context.Context,
// 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.
routes, err := r.server.chanRouter.FindRoutes(pubKey, amtMSat)
routes, err := r.server.chanRouter.FindRoutes(pubKey, amtMSat, nil)
if err != nil {
return nil, err
}
Expand Down

0 comments on commit cdcc86f

Please sign in to comment.