package routerrpc import ( "context" "encoding/hex" "errors" "fmt" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing/route" ) // legacyTrackPaymentServer is a wrapper struct that transforms a stream of main // rpc payment structs into the legacy PaymentStatus format. type legacyTrackPaymentServer struct { Router_TrackPaymentServer } // Send converts a Payment object and sends it as a PaymentStatus object on the // embedded stream. func (i *legacyTrackPaymentServer) Send(p *lnrpc.Payment) error { var state PaymentState switch p.Status { case lnrpc.Payment_IN_FLIGHT: state = PaymentState_IN_FLIGHT case lnrpc.Payment_SUCCEEDED: state = PaymentState_SUCCEEDED case lnrpc.Payment_FAILED: switch p.FailureReason { case lnrpc.PaymentFailureReason_FAILURE_REASON_NONE: return fmt.Errorf("expected fail reason") case lnrpc.PaymentFailureReason_FAILURE_REASON_TIMEOUT: state = PaymentState_FAILED_TIMEOUT case lnrpc.PaymentFailureReason_FAILURE_REASON_NO_ROUTE: state = PaymentState_FAILED_NO_ROUTE case lnrpc.PaymentFailureReason_FAILURE_REASON_ERROR: state = PaymentState_FAILED_ERROR case lnrpc.PaymentFailureReason_FAILURE_REASON_INCORRECT_PAYMENT_DETAILS: state = PaymentState_FAILED_INCORRECT_PAYMENT_DETAILS case lnrpc.PaymentFailureReason_FAILURE_REASON_INSUFFICIENT_BALANCE: state = PaymentState_FAILED_INSUFFICIENT_BALANCE default: return fmt.Errorf("unknown failure reason %v", p.FailureReason) } default: return fmt.Errorf("unknown state %v", p.Status) } preimage, err := hex.DecodeString(p.PaymentPreimage) if err != nil { return err } legacyState := PaymentStatus{ State: state, Preimage: preimage, Htlcs: p.Htlcs, } return i.Router_TrackPaymentServer.Send(&legacyState) } // TrackPayment returns a stream of payment state updates. The stream is // closed when the payment completes. func (s *Server) TrackPayment(request *TrackPaymentRequest, stream Router_TrackPaymentServer) error { legacyStream := legacyTrackPaymentServer{ Router_TrackPaymentServer: stream, } return s.TrackPaymentV2(request, &legacyStream) } // SendPayment attempts to route a payment described by the passed // PaymentRequest to the final destination. If we are unable to route the // payment, or cannot find a route that satisfies the constraints in the // PaymentRequest, then an error will be returned. Otherwise, the payment // pre-image, along with the final route will be returned. func (s *Server) SendPayment(request *SendPaymentRequest, stream Router_SendPaymentServer) error { if request.MaxParts > 1 { return errors.New("for multi-part payments, use SendPaymentV2") } legacyStream := legacyTrackPaymentServer{ Router_TrackPaymentServer: stream, } return s.SendPaymentV2(request, &legacyStream) } // SendToRoute sends a payment through a predefined route. The response of this // call contains structured error information. func (s *Server) SendToRoute(ctx context.Context, req *SendToRouteRequest) (*SendToRouteResponse, error) { resp, err := s.SendToRouteV2(ctx, req) if err != nil { return nil, err } if resp == nil { return nil, nil } // Need to convert to legacy response message because proto identifiers // don't line up. legacyResp := &SendToRouteResponse{ Preimage: resp.Preimage, Failure: resp.Failure, } return legacyResp, err } // QueryProbability returns the current success probability estimate for a // given node pair and amount. func (s *Server) QueryProbability(ctx context.Context, req *QueryProbabilityRequest) (*QueryProbabilityResponse, error) { fromNode, err := route.NewVertexFromBytes(req.FromNode) if err != nil { return nil, err } toNode, err := route.NewVertexFromBytes(req.ToNode) if err != nil { return nil, err } amt := lnwire.MilliSatoshi(req.AmtMsat) // Compute the probability. var prob float64 mc := s.cfg.RouterBackend.MissionControl capacity, err := s.cfg.RouterBackend.FetchAmountPairCapacity( fromNode, toNode, amt, ) // If we cannot query the capacity this means that either we don't have // information available or that the channel fails min/maxHtlc // constraints, so we return a zero probability. if err != nil { log.Errorf("Cannot fetch capacity: %v", err) } else { prob = mc.GetProbability(fromNode, toNode, amt, capacity) } history := mc.GetPairHistorySnapshot(fromNode, toNode) return &QueryProbabilityResponse{ Probability: prob, History: toRPCPairData(&history), }, nil }