rpc: refactor logic for SendPayment+SendToRoute

In this commit, we modify the logic for the two primary payment related
RPCs to reduce duplication, indentation, and consolidate logic. As a
result, we'll now accept rpcPaymentIntents, turn those into regular
paymentIntents (convert from proto) before finally dispatching via
unified function.
This commit is contained in:
Olaoluwa Osuntokun 2018-06-06 20:40:28 -07:00
parent eb65b0cd5a
commit bc9eca32ab
No known key found for this signature in database
GPG Key ID: 964EA263DD637C21

@ -1712,7 +1712,8 @@ func (r *rpcServer) ListChannels(ctx context.Context,
// savePayment saves a successfully completed payment to the database for // savePayment saves a successfully completed payment to the database for
// historical record keeping. // historical record keeping.
func (r *rpcServer) savePayment(route *routing.Route, amount lnwire.MilliSatoshi, preImage []byte) error { func (r *rpcServer) savePayment(route *routing.Route,
amount lnwire.MilliSatoshi, preImage []byte) error {
paymentPath := make([][33]byte, len(route.Hops)) paymentPath := make([][33]byte, len(route.Hops))
for i, hop := range route.Hops { for i, hop := range route.Hops {
@ -1748,18 +1749,18 @@ func validatePayReqExpiry(payReq *zpay32.Invoice) error {
return nil return nil
} }
// paymentStream enables different types of payment stream, such as: // paymentStream enables different types of payment streams, such as:
// lnrpc.Lightning_SendPaymentServer // lnrpc.Lightning_SendPaymentServer and lnrpc.Lightning_SendToRouteServer to
// lnrpc.Lightning_SendToRouteServer // execute sendPayment. We use this struct as a sort of bridge to enable code
// to execute sendPayment. // re-use between SendPayment and SendToRoute.
type paymentStream struct { type paymentStream struct {
recv func() (*paymentRequest, error) recv func() (*rpcPaymentRequest, error)
send func(*lnrpc.SendResponse) error send func(*lnrpc.SendResponse) error
} }
// paymentRequest wraps lnrpc.SendRequest so that routes from // rpcPaymentRequest wraps lnrpc.SendRequest so that routes from
// lnrpc.SendToRouteRequest can be passed to sendPayment // lnrpc.SendToRouteRequest can be passed to sendPayment.
type paymentRequest struct { type rpcPaymentRequest struct {
*lnrpc.SendRequest *lnrpc.SendRequest
routes []*routing.Route routes []*routing.Route
} }
@ -1770,13 +1771,13 @@ type paymentRequest struct {
// Lightning Network with a single persistent connection. // Lightning Network with a single persistent connection.
func (r *rpcServer) SendPayment(stream lnrpc.Lightning_SendPaymentServer) error { func (r *rpcServer) SendPayment(stream lnrpc.Lightning_SendPaymentServer) error {
return r.sendPayment(&paymentStream{ return r.sendPayment(&paymentStream{
recv: func() (*paymentRequest, error) { recv: func() (*rpcPaymentRequest, error) {
req, err := stream.Recv() req, err := stream.Recv()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &paymentRequest{ return &rpcPaymentRequest{
SendRequest: req, SendRequest: req,
}, nil }, nil
}, },
@ -1787,11 +1788,11 @@ func (r *rpcServer) SendPayment(stream lnrpc.Lightning_SendPaymentServer) error
// SendToRoute dispatches a bi-directional streaming RPC for sending payments // SendToRoute dispatches a bi-directional streaming RPC for sending payments
// through the Lightning Network via predefined routes passed in. A single RPC // through the Lightning Network via predefined routes passed in. A single RPC
// invocation creates a persistent bi-directional stream allowing clients to // invocation creates a persistent bi-directional stream allowing clients to
// rapidly send payments through the Lightning Network with a single // rapidly send payments through the Lightning Network with a single persistent
// persistent connection. // connection.
func (r *rpcServer) SendToRoute(stream lnrpc.Lightning_SendToRouteServer) error { func (r *rpcServer) SendToRoute(stream lnrpc.Lightning_SendToRouteServer) error {
return r.sendPayment(&paymentStream{ return r.sendPayment(&paymentStream{
recv: func() (*paymentRequest, error) { recv: func() (*rpcPaymentRequest, error) {
req, err := stream.Recv() req, err := stream.Recv()
if err != nil { if err != nil {
return nil, err return nil, err
@ -1812,7 +1813,7 @@ func (r *rpcServer) SendToRoute(stream lnrpc.Lightning_SendToRouteServer) error
routes[i] = route routes[i] = route
} }
return &paymentRequest{ return &rpcPaymentRequest{
SendRequest: &lnrpc.SendRequest{ SendRequest: &lnrpc.SendRequest{
PaymentHash: req.PaymentHash, PaymentHash: req.PaymentHash,
}, },
@ -1823,18 +1824,232 @@ func (r *rpcServer) SendToRoute(stream lnrpc.Lightning_SendToRouteServer) error
}) })
} }
func (r *rpcServer) sendPayment(stream *paymentStream) error { // rpcPaymentIntent is a small wrapper struct around the of values we can
// For each payment we need to know the msat amount, the destination // receive from a client over RPC if they wish to send a payment. We'll either
// public key, the payment hash, and the optional route hints. // extract these fields from a payment request (which may include routing
type payment struct { // hints), or we'll get a fully populated route from the user that we'll pass
msat lnwire.MilliSatoshi // directly to the channel router for dispatching.
dest []byte type rpcPaymentIntent struct {
pHash []byte msat lnwire.MilliSatoshi
cltvDelta uint16 dest *btcec.PublicKey
routeHints [][]routing.HopHint rHash [32]byte
routes []*routing.Route cltvDelta uint16
routeHints [][]routing.HopHint
routes []*routing.Route
}
// extractPaymentIntent attempts to parse the complete details required to
// dispatch a client from the information presented by an RPC client. There are
// three ways a client can specify their payment details: a payment request,
// via manual details, or via a complete route.
func extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error) {
var err error
payIntent := rpcPaymentIntent{}
// If a route was specified, then we can use that directly.
if len(rpcPayReq.routes) != 0 {
// If the user is using the REST interface, then they'll be
// passing the payment hash as a hex encoded 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)
}
payIntent.routes = rpcPayReq.routes
return payIntent, nil
} }
payChan := make(chan *payment)
// If the payment request field isn't blank, then the details of the
// invoice are encoded entirely within the encoded payReq. So we'll
// attempt to decode it, populating the payment accordingly.
if rpcPayReq.PaymentRequest != "" {
payReq, err := zpay32.Decode(
rpcPayReq.PaymentRequest, activeNetParams.Params,
)
if err != nil {
return payIntent, err
}
// Next, we'll ensure that this payreq hasn't already expired.
err = validatePayReqExpiry(payReq)
if err != nil {
return payIntent, err
}
// If the amount was not included in the invoice, then we let
// the payee specify the amount of satoshis they wish to send.
// We override the amount to pay with the amount provided from
// the payment request.
if payReq.MilliSat == nil {
payIntent.msat = lnwire.NewMSatFromSatoshis(
btcutil.Amount(rpcPayReq.Amt),
)
} else {
payIntent.msat = *payReq.MilliSat
}
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),
)
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
// same debug rHash. Otherwise, we pay to the rHash specified within
// the RPC request.
if cfg.DebugHTLC && len(payIntent.rHash) == 0 {
copy(payIntent.rHash[:], debugHash[:])
}
// Currently, within the bootstrap phase of the network, we limit the
// largest payment size allotted to (2^32) - 1 mSAT or 4.29 million
// satoshis.
if payIntent.msat > maxPaymentMSat {
// In this case, we'll send an error to the caller, but
// continue our loop for the next payment.
return payIntent, fmt.Errorf("payment of %v is too large, "+
"max payment allowed is %v", payIntent.msat,
maxPaymentMSat)
}
return payIntent, nil
}
// dispatchPaymentIntent attempts to fully dispatch an RPC payment intent.
// We'll either pass the payment as a whole to the channel router, or give it a
// pre-built route. The first error this method returns denotes if we were
// unable to save the payment. The second error returned denotes if the payment
// didn't succeed.
func (r *rpcServer) dispatchPaymentIntent(payIntent *rpcPaymentIntent) (*routing.Route, [32]byte, error, error) {
// Construct a payment request to send to the channel router. If the
// payment is successful, the route chosen will be returned. Otherwise,
// we'll get a non-nil error.
var (
preImage [32]byte
route *routing.Route
routerErr error
)
// If a route was specified, then we'll pass the route directly to the
// router, otherwise we'll create a payment session to execute it.
if len(payIntent.routes) == 0 {
payment := &routing.LightningPayment{
Target: payIntent.dest,
Amount: payIntent.msat,
PaymentHash: payIntent.rHash,
RouteHints: payIntent.routeHints,
}
// If the final CLTV value was specified, then we'll use that
// rather than the default.
if payIntent.cltvDelta != 0 {
payment.FinalCLTVDelta = &payIntent.cltvDelta
}
preImage, route, routerErr = r.server.chanRouter.SendPayment(
payment,
)
} else {
payment := &routing.LightningPayment{
PaymentHash: payIntent.rHash,
}
preImage, route, routerErr = r.server.chanRouter.SendToRoute(
payIntent.routes, payment,
)
}
// If the route failed, then we'll return a nil save err, but a non-nil
// routing err.
if routerErr != nil {
return nil, preImage, nil, routerErr
}
// If a route was used to complete this payment, then we'll need to
// compute the final amount sent
var amt lnwire.MilliSatoshi
if len(payIntent.routes) > 0 {
amt = route.TotalAmount - route.TotalFees
} else {
amt = payIntent.msat
}
// Save the completed payment to the database for record keeping
// purposes.
err := r.savePayment(route, amt, preImage[:])
if err != nil {
// We weren't able to save the payment, so we return the save
// err, but a nil routing err.
return nil, preImage, err, nil
}
return route, preImage, nil, nil
}
// sendPayment takes a paymentStream (a source of pre-built routes or payment
// requests) and continually attempt to dispatch payment requests written to
// the write end of the stream. Responses will also be streamed back to the
// client via the write end of the stream. This method is by both SendToRoute
// and SendPayment as the logic is virtually identical.
func (r *rpcServer) sendPayment(stream *paymentStream) error {
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 // TODO(roasbeef): enforce fee limits, pass into router, ditch if exceed limit
@ -1894,76 +2109,28 @@ func (r *rpcServer) sendPayment(stream *paymentStream) error {
// Populate the next payment, either from the // Populate the next payment, either from the
// payment request, or from the explicitly set // payment request, or from the explicitly set
// fields. // fields. If the payment proto wasn't well
p := &payment{} // formed, then we'll send an error reply and
// wait for the next payment.
if len(nextPayment.routes) == 0 { payIntent, err := extractPaymentIntent(nextPayment)
// If the payment request field isn't blank, if err != nil {
// then the details of the invoice are encoded if err := stream.send(&lnrpc.SendResponse{
// entirely within the encoded payReq. So we'll PaymentError: err.Error(),
// attempt to decode it, populating the }); err != nil {
// payment accordingly. select {
if nextPayment.PaymentRequest != "" { case errChan <- err:
payReq, err := zpay32.Decode(nextPayment.PaymentRequest, case <-reqQuit:
activeNetParams.Params)
if err != nil {
select {
case errChan <- err:
case <-reqQuit:
}
return return
} }
// TODO(roasbeef): eliminate necessary
// encode/decode
// We first check that this payment
// request has not expired.
err = validatePayReqExpiry(payReq)
if err != nil {
select {
case errChan <- err:
case <-reqQuit:
}
return
}
p.dest = payReq.Destination.SerializeCompressed()
// If the amount was not included in the
// invoice, then we let the payee
// specify the amount of satoshis they
// wish to send. We override the amount
// to pay with the amount provided from
// the payment request.
if payReq.MilliSat == nil {
p.msat = lnwire.NewMSatFromSatoshis(
btcutil.Amount(nextPayment.Amt),
)
} else {
p.msat = *payReq.MilliSat
}
p.pHash = payReq.PaymentHash[:]
p.cltvDelta = uint16(payReq.MinFinalCLTVExpiry())
p.routeHints = payReq.RouteHints
} else {
// If the payment request field was not
// specified, construct the payment from
// the other fields.
p.msat = lnwire.NewMSatFromSatoshis(
btcutil.Amount(nextPayment.Amt),
)
p.dest = nextPayment.Dest
p.pHash = nextPayment.PaymentHash
p.cltvDelta = uint16(nextPayment.FinalCltvDelta)
} }
} else { continue
p.pHash = nextPayment.PaymentHash
p.routes = nextPayment.routes
} }
// If the payment was well formed, then we'll
// send to the dispatch goroutine, or exit,
// which ever comes first
select { select {
case payChan <- p: case payChan <- &payIntent:
case <-reqQuit: case <-reqQuit:
return return
} }
@ -1975,47 +2142,8 @@ func (r *rpcServer) sendPayment(stream *paymentStream) error {
select { select {
case err := <-errChan: case err := <-errChan:
return err return err
case p := <-payChan:
// Currently, within the bootstrap phase of the
// network, we limit the largest payment size allotted
// to (2^32) - 1 mSAT or 4.29 million satoshis.
if p.msat > maxPaymentMSat {
// In this case, we'll send an error to the
// caller, but continue our loop for the next
// payment.
pErr := fmt.Errorf("payment of %v is too "+
"large, max payment allowed is %v",
p.msat, maxPaymentMSat)
if err := stream.send(&lnrpc.SendResponse{
PaymentError: pErr.Error(),
}); err != nil {
return err
}
continue
}
// Parse the details of the payment which include the
// pubkey of the destination and the payment amount.
var destNode *btcec.PublicKey
var pubKeyErr error
if len(p.routes) == 0 {
destNode, pubKeyErr = btcec.ParsePubKey(p.dest, btcec.S256())
if pubKeyErr != nil {
return pubKeyErr
}
}
// If we're in debug HTLC mode, then all outgoing HTLCs
// will pay to the same debug rHash. Otherwise, we pay
// to the rHash specified within the RPC request.
var rHash [32]byte
if cfg.DebugHTLC && len(p.pHash) == 0 {
rHash = debugHash
} else {
copy(rHash[:], p.pHash)
}
case payIntent := <-payChan:
// We launch a new goroutine to execute the current // We launch a new goroutine to execute the current
// payment so we can continue to serve requests while // payment so we can continue to serve requests while
// this payment is being dispatched. // this payment is being dispatched.
@ -2028,62 +2156,28 @@ func (r *rpcServer) sendPayment(stream *paymentStream) error {
htlcSema <- struct{}{} htlcSema <- struct{}{}
}() }()
// Construct a payment request to send to the route, preImage, saveErr, routeErr := r.dispatchPaymentIntent(
// channel router. If the payment is payIntent,
// successful, the route chosen will be
// returned. Otherwise, we'll get a non-nil
// error.
var (
preImage [32]byte
route *routing.Route
routerErr error
) )
if len(p.routes) == 0 { switch {
payment := &routing.LightningPayment{ // If we receive payment error than, instead of
Target: destNode, // terminating the stream, send error response
Amount: p.msat, // to the user.
PaymentHash: rHash, case routeErr != nil:
RouteHints: p.routeHints,
}
if p.cltvDelta != 0 {
payment.FinalCLTVDelta = &p.cltvDelta
}
preImage, route, routerErr = r.server.chanRouter.SendPayment(payment)
} else {
payment := &routing.LightningPayment{
PaymentHash: rHash,
}
preImage, route, routerErr = r.server.chanRouter.SendToRoute(p.routes, payment)
}
if routerErr != nil {
// If we receive payment error than,
// instead of terminating the stream,
// send error response to the user.
err := stream.send(&lnrpc.SendResponse{ err := stream.send(&lnrpc.SendResponse{
PaymentError: routerErr.Error(), PaymentError: routeErr.Error(),
}) })
if err != nil { if err != nil {
errChan <- err errChan <- err
} }
return return
}
// Calculate the original amount of funds which was sent through routes // If we were unable to save the state of the
// without total fee. // payment, then we'll return the error to the
var amt lnwire.MilliSatoshi // user, and terminate.
if len(p.routes) > 0 { case saveErr != nil:
amt = route.TotalAmount - route.TotalFees errChan <- saveErr
} else {
amt = p.msat
}
// Save the completed payment to the database
// for record keeping purposes.
if err := r.savePayment(route, amt, preImage[:]); err != nil {
errChan <- err
return return
} }
@ -2092,6 +2186,7 @@ func (r *rpcServer) sendPayment(stream *paymentStream) error {
PaymentRoute: marshallRoute(route), PaymentRoute: marshallRoute(route),
}) })
if err != nil { if err != nil {
rpcsLog.Infof("sender rrrrr: ", err)
errChan <- err errChan <- err
return return
} }
@ -2107,15 +2202,15 @@ func (r *rpcServer) sendPayment(stream *paymentStream) error {
func (r *rpcServer) SendPaymentSync(ctx context.Context, func (r *rpcServer) SendPaymentSync(ctx context.Context,
nextPayment *lnrpc.SendRequest) (*lnrpc.SendResponse, error) { nextPayment *lnrpc.SendRequest) (*lnrpc.SendResponse, error) {
return r.sendPaymentSync(ctx, &paymentRequest{ return r.sendPaymentSync(ctx, &rpcPaymentRequest{
SendRequest: nextPayment, SendRequest: nextPayment,
}) })
} }
// SendToRouteSync is the synchronous non-streaming version of SendToRoute. // SendToRouteSync is the synchronous non-streaming version of SendToRoute.
// This RPC is intended to be consumed by clients of the REST proxy. // This RPC is intended to be consumed by clients of the REST proxy.
// Additionally, this RPC expects the payment hash (if any) to be encoded // Additionally, this RPC expects the payment hash (if any) to be encoded as
// as hex strings. // hex strings.
func (r *rpcServer) SendToRouteSync(ctx context.Context, func (r *rpcServer) SendToRouteSync(ctx context.Context,
req *lnrpc.SendToRouteRequest) (*lnrpc.SendResponse, error) { req *lnrpc.SendToRouteRequest) (*lnrpc.SendResponse, error) {
@ -2134,7 +2229,7 @@ func (r *rpcServer) SendToRouteSync(ctx context.Context,
routes[i] = route routes[i] = route
} }
return r.sendPaymentSync(ctx, &paymentRequest{ return r.sendPaymentSync(ctx, &rpcPaymentRequest{
SendRequest: &lnrpc.SendRequest{ SendRequest: &lnrpc.SendRequest{
PaymentHashString: req.PaymentHashString, PaymentHashString: req.PaymentHashString,
}, },
@ -2142,8 +2237,10 @@ func (r *rpcServer) SendToRouteSync(ctx context.Context,
}) })
} }
// sendPaymentSync is the synchronous variant of sendPayment. It will block and
// wait until the payment has been fully completed.
func (r *rpcServer) sendPaymentSync(ctx context.Context, func (r *rpcServer) sendPaymentSync(ctx context.Context,
nextPayment *paymentRequest) (*lnrpc.SendResponse, error) { nextPayment *rpcPaymentRequest) (*lnrpc.SendResponse, error) {
// TODO(roasbeef): enforce fee limits, pass into router, ditch if exceed limit // TODO(roasbeef): enforce fee limits, pass into router, ditch if exceed limit
// * limit either a %, or absolute, or iff more than sending // * limit either a %, or absolute, or iff more than sending
@ -2156,150 +2253,23 @@ func (r *rpcServer) sendPaymentSync(ctx context.Context,
"not active yet") "not active yet")
} }
var ( // First we'll attempt to map the proto describing the next payment to
destPub *btcec.PublicKey // an intent that we can pass to local sub-systems.
amtMSat lnwire.MilliSatoshi payIntent, err := extractPaymentIntent(nextPayment)
rHash [32]byte if err != nil {
cltvDelta uint16 return nil, err
routeHints [][]routing.HopHint
)
if len(nextPayment.routes) == 0 {
// If the proto request has an encoded payment request, then we we'll
// use that solely to dispatch the payment.
if nextPayment.PaymentRequest != "" {
payReq, err := zpay32.Decode(nextPayment.PaymentRequest,
activeNetParams.Params)
if err != nil {
return nil, err
}
// We first check that this payment request has not expired.
if err := validatePayReqExpiry(payReq); err != nil {
return nil, err
}
destPub = payReq.Destination
// If the amount was not included in the invoice, then we let
// the payee specify the amount of satoshis they wish to send.
// We override the amount to pay with the amount provided from
// the payment request.
if payReq.MilliSat == nil {
amtMSat = lnwire.NewMSatFromSatoshis(
btcutil.Amount(nextPayment.Amt),
)
} else {
amtMSat = *payReq.MilliSat
}
rHash = *payReq.PaymentHash
cltvDelta = uint16(payReq.MinFinalCLTVExpiry())
routeHints = payReq.RouteHints
// Otherwise, the payment conditions have been manually
// specified in the proto.
} else {
// If we're in debug HTLC mode, then all outgoing HTLCs will
// pay to the same debug rHash. Otherwise, we pay to the rHash
// specified within the RPC request.
if cfg.DebugHTLC && nextPayment.PaymentHashString == "" {
rHash = debugHash
} else {
paymentHash, err := hex.DecodeString(nextPayment.PaymentHashString)
if err != nil {
return nil, err
}
copy(rHash[:], paymentHash)
}
pubBytes, err := hex.DecodeString(nextPayment.DestString)
if err != nil {
return nil, err
}
destPub, err = btcec.ParsePubKey(pubBytes, btcec.S256())
if err != nil {
return nil, err
}
amtMSat = lnwire.NewMSatFromSatoshis(
btcutil.Amount(nextPayment.Amt),
)
}
} else {
// If we're in debug HTLC mode, then all outgoing HTLCs will
// pay to the same debug rHash. Otherwise, we pay to the rHash
// specified within the RPC request.
if cfg.DebugHTLC && nextPayment.PaymentHashString == "" {
rHash = debugHash
} else {
paymentHash, err := hex.DecodeString(nextPayment.PaymentHashString)
if err != nil {
return nil, err
}
copy(rHash[:], paymentHash)
}
} }
// Currently, within the bootstrap phase of the network, we limit the // With the payment validated, we'll now attempt to dispatch the
// largest payment size allotted to (2^32) - 1 mSAT or 4.29 million // payment.
// satoshis. route, preImage, saveErr, routeErr := r.dispatchPaymentIntent(&payIntent)
if amtMSat > maxPaymentMSat { switch {
err := fmt.Errorf("payment of %v is too large, max payment "+ case routeErr != nil:
"allowed is %v", nextPayment.Amt, maxPaymentMSat.ToSatoshis())
return &lnrpc.SendResponse{ return &lnrpc.SendResponse{
PaymentError: err.Error(), PaymentError: routeErr.Error(),
}, nil }, nil
}
// Finally, send a payment request to the channel router. If the case saveErr != nil:
// payment succeeds, then the returned route will be that was used
// successfully within the payment.
var (
preImage [32]byte
route *routing.Route
routerErr error
)
if len(nextPayment.routes) == 0 {
payment := &routing.LightningPayment{
Target: destPub,
Amount: amtMSat,
PaymentHash: rHash,
RouteHints: routeHints,
}
if cltvDelta != 0 {
payment.FinalCLTVDelta = &cltvDelta
}
preImage, route, routerErr = r.server.chanRouter.SendPayment(payment)
} else {
payment := &routing.LightningPayment{
PaymentHash: rHash,
}
preImage, route, routerErr = r.server.chanRouter.SendToRoute(
nextPayment.routes, payment)
}
if routerErr != nil {
return &lnrpc.SendResponse{
PaymentError: routerErr.Error(),
}, nil
}
// Calculate the original amount of funds which was sent through routes
// without total fee.
var amt lnwire.MilliSatoshi
if len(nextPayment.routes) > 0 {
amt = route.TotalAmount - route.TotalFees
} else {
amt = amtMSat
}
// With the payment completed successfully, we now ave the details of
// the completed payment to the database for historical record keeping.
if err := r.savePayment(route, amt, preImage[:]); err != nil {
return nil, err return nil, err
} }
@ -3153,8 +3123,6 @@ func marshallRoute(route *routing.Route) *lnrpc.Route {
func unmarshallRoute(rpcroute *lnrpc.Route, func unmarshallRoute(rpcroute *lnrpc.Route,
graph *channeldb.ChannelGraph) (*routing.Route, error) { graph *channeldb.ChannelGraph) (*routing.Route, error) {
rpcsLog.Infof("rpcroute: %v", spew.Sdump(rpcroute))
route := &routing.Route{ route := &routing.Route{
TotalTimeLock: rpcroute.TotalTimeLock, TotalTimeLock: rpcroute.TotalTimeLock,
TotalFees: lnwire.MilliSatoshi(rpcroute.TotalFeesMsat), TotalFees: lnwire.MilliSatoshi(rpcroute.TotalFeesMsat),