From afd86763ac08ef645f9ff127fa39bf8e49fc62d7 Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Tue, 28 May 2019 15:27:04 +0200 Subject: [PATCH 1/9] lntypes: add ZeroHash --- lntypes/hash.go | 3 +++ rpcserver.go | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lntypes/hash.go b/lntypes/hash.go index d2498f9c855..719d1cd4170 100644 --- a/lntypes/hash.go +++ b/lntypes/hash.go @@ -8,6 +8,9 @@ import ( // HashSize of array used to store hashes. const HashSize = 32 +// ZeroHash is a predefined hash containing all zeroes. +var ZeroHash Hash + // Hash is used in several of the lightning messages and common structures. It // typically represents a payment hash. type Hash [HashSize]byte diff --git a/rpcserver.go b/rpcserver.go index e2381987b67..66992551966 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -67,8 +67,6 @@ const ( ) var ( - zeroHash [32]byte - // MaxPaymentMSat is the maximum allowed payment currently permitted as // defined in BOLT-002. This value depends on which chain is active. // It is set to the value under the Bitcoin chain as default. @@ -3018,7 +3016,9 @@ func extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error // 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. - case cfg.DebugHTLC && bytes.Equal(payIntent.rHash[:], zeroHash[:]): + case cfg.DebugHTLC && + bytes.Equal(payIntent.rHash[:], lntypes.ZeroHash[:]): + copy(payIntent.rHash[:], invoices.DebugHash[:]) default: From 19d5f8f82c112dec3428005f08c890c024ec2f29 Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Thu, 18 Apr 2019 09:45:21 +0200 Subject: [PATCH 2/9] routing: move default cltv assignment out of router This commit lifts default setting up to the rpc server level, in line with other payment defaults. --- lnrpc/routerrpc/router_server.go | 2 +- routing/payment_session_test.go | 2 +- routing/router.go | 15 +++------------ rpcserver.go | 13 ++++++------- 4 files changed, 11 insertions(+), 21 deletions(-) diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index 9f1ca20854e..3a5ad2764c3 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -222,7 +222,7 @@ func (s *Server) SendPayment(ctx context.Context, Amount: *payReq.MilliSat, FeeLimit: lnwire.MilliSatoshi(req.FeeLimitSat), PaymentHash: *payReq.PaymentHash, - FinalCLTVDelta: &finalDelta, + FinalCLTVDelta: finalDelta, PayAttemptTimeout: time.Second * time.Duration(req.TimeoutSeconds), RouteHints: payReq.RouteHints, } diff --git a/routing/payment_session_test.go b/routing/payment_session_test.go index a8748a624fd..a5ee7889cae 100644 --- a/routing/payment_session_test.go +++ b/routing/payment_session_test.go @@ -45,7 +45,7 @@ func TestRequestRoute(t *testing.T) { payment := &LightningPayment{ CltvLimit: &cltvLimit, - FinalCLTVDelta: &finalCltvDelta, + FinalCLTVDelta: finalCltvDelta, } route, err := session.RequestRoute(payment, height, finalCltvDelta) diff --git a/routing/router.go b/routing/router.go index a28a1118176..88c894b7c8b 100644 --- a/routing/router.go +++ b/routing/router.go @@ -1565,10 +1565,8 @@ type LightningPayment struct { // FinalCLTVDelta is the CTLV expiry delta to use for the _final_ hop // in the route. This means that the final hop will have a CLTV delta - // of at least: currentHeight + FinalCLTVDelta. If this value is - // unspecified, then a default value of DefaultFinalCLTVDelta will be - // used. - FinalCLTVDelta *uint16 + // of at least: currentHeight + FinalCLTVDelta. + FinalCLTVDelta uint16 // PayAttemptTimeout is a timeout value that we'll use to determine // when we should should abandon the payment attempt after consecutive @@ -1732,13 +1730,6 @@ func (r *ChannelRouter) sendPayment( return [32]byte{}, nil, err } - var finalCLTVDelta uint16 - if payment.FinalCLTVDelta == nil { - finalCLTVDelta = zpay32.DefaultFinalCLTVDelta - } else { - finalCLTVDelta = *payment.FinalCLTVDelta - } - var payAttemptTimeout time.Duration if payment.PayAttemptTimeout == time.Duration(0) { payAttemptTimeout = defaultPayAttemptTimeout @@ -1756,7 +1747,7 @@ func (r *ChannelRouter) sendPayment( paySession: paySession, timeoutChan: timeoutChan, currentHeight: currentHeight, - finalCLTVDelta: finalCLTVDelta, + finalCLTVDelta: uint16(payment.FinalCLTVDelta), attempt: existingAttempt, circuit: nil, lastError: nil, diff --git a/rpcserver.go b/rpcserver.go index 66992551966..5258c623a7f 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -2998,7 +2998,11 @@ func extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error rpcPayReq.FeeLimit, payIntent.msat, ) - payIntent.cltvDelta = uint16(rpcPayReq.FinalCltvDelta) + if rpcPayReq.FinalCltvDelta != 0 { + payIntent.cltvDelta = uint16(rpcPayReq.FinalCltvDelta) + } else { + payIntent.cltvDelta = zpay32.DefaultFinalCLTVDelta + } // If the user is manually specifying payment details, then the payment // hash may be encoded as a string. @@ -3069,6 +3073,7 @@ func (r *rpcServer) dispatchPaymentIntent( payment := &routing.LightningPayment{ Target: payIntent.dest, Amount: payIntent.msat, + FinalCLTVDelta: payIntent.cltvDelta, FeeLimit: payIntent.feeLimit, CltvLimit: payIntent.cltvLimit, PaymentHash: payIntent.rHash, @@ -3077,12 +3082,6 @@ func (r *rpcServer) dispatchPaymentIntent( PaymentRequest: payIntent.payReq, } - // 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, ) From eb700d35e12eebd9f72badfeacdc6390fc74d9d3 Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Thu, 18 Apr 2019 09:34:28 +0200 Subject: [PATCH 3/9] routerrpc: extend payment request parameters Add missing parameters to routerrpc version of SendPayment. --- lnrpc/routerrpc/config_active.go | 9 +- lnrpc/routerrpc/router.pb.go | 278 +++++++++++++++++------------- lnrpc/routerrpc/router.proto | 52 ++++-- lnrpc/routerrpc/router_backend.go | 164 ++++++++++++++++++ lnrpc/routerrpc/router_server.go | 46 +---- rpcserver.go | 19 +- subrpcserver_config.go | 3 - 7 files changed, 366 insertions(+), 205 deletions(-) diff --git a/lnrpc/routerrpc/config_active.go b/lnrpc/routerrpc/config_active.go index c2ee3a23416..d04f5821c5f 100644 --- a/lnrpc/routerrpc/config_active.go +++ b/lnrpc/routerrpc/config_active.go @@ -5,10 +5,9 @@ package routerrpc import ( "time" - "github.com/lightningnetwork/lnd/lnwire" - "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcutil" + "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/routing" ) @@ -45,12 +44,6 @@ type Config struct { // server will find the macaroon named DefaultRouterMacFilename. NetworkDir string - // ActiveNetParams are the network parameters of the primary network - // that the route is operating on. This is necessary so we can ensure - // that we receive payment requests that send to destinations on our - // network. - ActiveNetParams *chaincfg.Params - // MacService is the main macaroon service that we'll use to handle // authentication for the Router rpc server. MacService *macaroons.Service diff --git a/lnrpc/routerrpc/router.pb.go b/lnrpc/routerrpc/router.pb.go index 63c4364469e..224bd4d01be 100644 --- a/lnrpc/routerrpc/router.pb.go +++ b/lnrpc/routerrpc/router.pb.go @@ -113,32 +113,43 @@ func (Failure_FailureCode) EnumDescriptor() ([]byte, []int) { } type PaymentRequest struct { + /// The identity pubkey of the payment recipient + Dest []byte `protobuf:"bytes,1,opt,name=dest,proto3" json:"dest,omitempty"` + /// Number of satoshis to send. + Amt int64 `protobuf:"varint,2,opt,name=amt,proto3" json:"amt,omitempty"` + /// The hash to use within the payment's HTLC + PaymentHash []byte `protobuf:"bytes,3,opt,name=payment_hash,json=paymentHash,proto3" json:"payment_hash,omitempty"` //* - //A serialized BOLT-11 payment request that contains all information - //required to dispatch the payment. If the pay req is invalid, or expired, - //an error will be returned. - PayReq string `protobuf:"bytes,1,opt,name=pay_req,json=payReq,proto3" json:"pay_req,omitempty"` - //* - //An absolute limit on the highest fee we should pay when looking for a route - //to the destination. Routes with fees higher than this will be ignored, if - //there are no routes with a fee below this amount, an error will be - //returned. - FeeLimitSat int64 `protobuf:"varint,2,opt,name=fee_limit_sat,json=feeLimitSat,proto3" json:"fee_limit_sat,omitempty"` + //The CLTV delta from the current height that should be used to set the + //timelock for the final hop. + FinalCltvDelta int32 `protobuf:"varint,4,opt,name=final_cltv_delta,json=finalCltvDelta,proto3" json:"final_cltv_delta,omitempty"` //* - //An absolute limit on the cumulative CLTV value along the route for this - //payment. Routes with total CLTV values higher than this will be ignored, - //if there are no routes with a CLTV value below this amount, an error will - //be returned. - CltvLimit int32 `protobuf:"varint,3,opt,name=cltv_limit,json=cltvLimit,proto3" json:"cltv_limit,omitempty"` + //A bare-bones invoice for a payment within the Lightning Network. With the + //details of the invoice, the sender has all the data necessary to send a + //payment to the recipient. The amount in the payment request may be zero. In + //that case it is required to set the amt field as well. If no payment request + //is specified, the following fields are required: dest, amt and payment_hash. + PaymentRequest string `protobuf:"bytes,5,opt,name=payment_request,json=paymentRequest,proto3" json:"payment_request,omitempty"` //* //An upper limit on the amount of time we should spend when attempting to //fulfill the payment. This is expressed in seconds. If we cannot make a //successful payment within this time frame, an error will be returned. - TimeoutSeconds int32 `protobuf:"varint,4,opt,name=timeout_seconds,json=timeoutSeconds,proto3" json:"timeout_seconds,omitempty"` + //This field must be non-zero. + TimeoutSeconds int32 `protobuf:"varint,6,opt,name=timeout_seconds,json=timeoutSeconds,proto3" json:"timeout_seconds,omitempty"` + //* + //The maximum number of satoshis that will be paid as a fee of the payment. + //If this field is left to the default value of 0, only zero-fee routes will + //be considered. This usually means single hop routes connecting directly to + //the destination. To send the payment without a fee limit, use max int here. + FeeLimitSat int64 `protobuf:"varint,7,opt,name=fee_limit_sat,json=feeLimitSat,proto3" json:"fee_limit_sat,omitempty"` //* //The channel id of the channel that must be taken to the first hop. If zero, //any channel may be used. - OutgoingChannelId int64 `protobuf:"varint,5,opt,name=outgoing_channel_id,json=outgoingChannelId,proto3" json:"outgoing_channel_id,omitempty"` + OutgoingChanId uint64 `protobuf:"varint,8,opt,name=outgoing_chan_id,json=outgoingChanId,proto3" json:"outgoing_chan_id,omitempty"` + //* + //An optional maximum total time lock for the route. If zero, there is no + //maximum enforced. + CltvLimit int32 `protobuf:"varint,9,opt,name=cltv_limit,json=cltvLimit,proto3" json:"cltv_limit,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -169,27 +180,41 @@ func (m *PaymentRequest) XXX_DiscardUnknown() { var xxx_messageInfo_PaymentRequest proto.InternalMessageInfo -func (m *PaymentRequest) GetPayReq() string { +func (m *PaymentRequest) GetDest() []byte { if m != nil { - return m.PayReq + return m.Dest } - return "" + return nil } -func (m *PaymentRequest) GetFeeLimitSat() int64 { +func (m *PaymentRequest) GetAmt() int64 { if m != nil { - return m.FeeLimitSat + return m.Amt } return 0 } -func (m *PaymentRequest) GetCltvLimit() int32 { +func (m *PaymentRequest) GetPaymentHash() []byte { if m != nil { - return m.CltvLimit + return m.PaymentHash + } + return nil +} + +func (m *PaymentRequest) GetFinalCltvDelta() int32 { + if m != nil { + return m.FinalCltvDelta } return 0 } +func (m *PaymentRequest) GetPaymentRequest() string { + if m != nil { + return m.PaymentRequest + } + return "" +} + func (m *PaymentRequest) GetTimeoutSeconds() int32 { if m != nil { return m.TimeoutSeconds @@ -197,9 +222,23 @@ func (m *PaymentRequest) GetTimeoutSeconds() int32 { return 0 } -func (m *PaymentRequest) GetOutgoingChannelId() int64 { +func (m *PaymentRequest) GetFeeLimitSat() int64 { + if m != nil { + return m.FeeLimitSat + } + return 0 +} + +func (m *PaymentRequest) GetOutgoingChanId() uint64 { if m != nil { - return m.OutgoingChannelId + return m.OutgoingChanId + } + return 0 +} + +func (m *PaymentRequest) GetCltvLimit() int32 { + if m != nil { + return m.CltvLimit } return 0 } @@ -1027,98 +1066,101 @@ func init() { func init() { proto.RegisterFile("routerrpc/router.proto", fileDescriptor_7a0613f69d37b0a5) } var fileDescriptor_7a0613f69d37b0a5 = []byte{ - // 1456 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x56, 0xdb, 0x72, 0x1a, 0x47, - 0x13, 0xfe, 0x31, 0x12, 0x87, 0xe6, 0xa0, 0xd5, 0xe8, 0x60, 0x84, 0x2c, 0x5b, 0xde, 0xff, 0xff, - 0x1d, 0x95, 0xcb, 0x25, 0x55, 0x48, 0xd9, 0x95, 0xab, 0xa4, 0x30, 0x2c, 0xd1, 0x96, 0x60, 0xc1, - 0x03, 0xc8, 0x56, 0x72, 0x31, 0x35, 0x62, 0x47, 0xb0, 0x11, 0xec, 0xae, 0x76, 0x87, 0xc4, 0xe4, - 0x01, 0xf2, 0x3a, 0xc9, 0x4d, 0x6e, 0x73, 0x97, 0x87, 0xc8, 0xdb, 0xa4, 0x66, 0x66, 0x97, 0x83, - 0x8c, 0x92, 0x5c, 0xc1, 0x7e, 0xdf, 0x37, 0xdd, 0xd3, 0x3d, 0xdd, 0x3d, 0x03, 0xfb, 0x81, 0x37, - 0xe5, 0x2c, 0x08, 0xfc, 0xc1, 0x99, 0xfa, 0x77, 0xea, 0x07, 0x1e, 0xf7, 0x50, 0x76, 0x8e, 0x97, - 0xb3, 0x81, 0x3f, 0x50, 0xa8, 0xfe, 0x47, 0x02, 0x8a, 0x1d, 0x3a, 0x9b, 0x30, 0x97, 0x63, 0x76, - 0x37, 0x65, 0x21, 0x47, 0x8f, 0x21, 0xed, 0xd3, 0x19, 0x09, 0xd8, 0x5d, 0x29, 0x71, 0x9c, 0x38, - 0xc9, 0xe2, 0x94, 0x4f, 0x67, 0x98, 0xdd, 0x21, 0x1d, 0x0a, 0x37, 0x8c, 0x91, 0xb1, 0x33, 0x71, - 0x38, 0x09, 0x29, 0x2f, 0x3d, 0x3a, 0x4e, 0x9c, 0x24, 0x71, 0xee, 0x86, 0xb1, 0xa6, 0xc0, 0xba, - 0x94, 0xa3, 0x23, 0x80, 0xc1, 0x98, 0xff, 0xa0, 0x44, 0xa5, 0xe4, 0x71, 0xe2, 0x64, 0x13, 0x67, - 0x05, 0x22, 0x15, 0xe8, 0x33, 0xd8, 0xe2, 0xce, 0x84, 0x79, 0x53, 0x4e, 0x42, 0x36, 0xf0, 0x5c, - 0x3b, 0x2c, 0x6d, 0x48, 0x4d, 0x31, 0x82, 0xbb, 0x0a, 0x45, 0xa7, 0xb0, 0xe3, 0x4d, 0xf9, 0xd0, - 0x73, 0xdc, 0x21, 0x19, 0x8c, 0xa8, 0xeb, 0xb2, 0x31, 0x71, 0xec, 0xd2, 0xa6, 0xf4, 0xb8, 0x1d, - 0x53, 0x35, 0xc5, 0x98, 0xb6, 0xfe, 0x3d, 0x6c, 0xcd, 0xc3, 0x08, 0x7d, 0xcf, 0x0d, 0x19, 0x3a, - 0x80, 0x8c, 0x88, 0x63, 0x44, 0xc3, 0x91, 0x0c, 0x24, 0x8f, 0x45, 0x5c, 0xe7, 0x34, 0x1c, 0xa1, - 0x43, 0xc8, 0xfa, 0x01, 0x23, 0xce, 0x84, 0x0e, 0x99, 0x8c, 0x22, 0x8f, 0x33, 0x7e, 0xc0, 0x4c, - 0xf1, 0x8d, 0x9e, 0x41, 0xce, 0x57, 0xa6, 0x08, 0x0b, 0x02, 0x19, 0x43, 0x16, 0x43, 0x04, 0x19, - 0x41, 0xa0, 0x7f, 0x05, 0x5b, 0x58, 0xe4, 0xb2, 0xc1, 0x58, 0x9c, 0x33, 0x04, 0x1b, 0x36, 0x0b, - 0x79, 0xe4, 0x47, 0xfe, 0x17, 0x79, 0xa4, 0x93, 0xe5, 0x44, 0xa5, 0xe8, 0x44, 0xe4, 0x48, 0xb7, - 0x41, 0x5b, 0xac, 0x8f, 0x36, 0x7b, 0x02, 0x9a, 0x38, 0x1f, 0x11, 0xae, 0xc8, 0xf1, 0x44, 0xac, - 0x4a, 0xc8, 0x55, 0xc5, 0x08, 0x6f, 0x30, 0xd6, 0x0a, 0x29, 0x47, 0x2f, 0x54, 0x0a, 0xc9, 0xd8, - 0x1b, 0xdc, 0x12, 0x9b, 0x8d, 0xe9, 0x2c, 0x32, 0x5f, 0x10, 0x70, 0xd3, 0x1b, 0xdc, 0xd6, 0x05, - 0xa8, 0x7f, 0x07, 0xa8, 0xcb, 0x5c, 0xbb, 0xe7, 0x49, 0x5f, 0xf1, 0x46, 0x9f, 0x43, 0x3e, 0x0e, - 0x6e, 0x29, 0x31, 0x71, 0xc0, 0x32, 0x39, 0x3a, 0x6c, 0xca, 0x52, 0x91, 0x66, 0x73, 0x95, 0xfc, - 0xe9, 0xd8, 0x15, 0xf5, 0xa2, 0xcc, 0x28, 0x4a, 0x27, 0xb0, 0xb3, 0x62, 0x3c, 0x8a, 0xa2, 0x0c, - 0x22, 0x8d, 0x2a, 0xad, 0x89, 0x79, 0x5a, 0xe5, 0x37, 0x7a, 0x05, 0xe9, 0x1b, 0xea, 0x8c, 0xa7, - 0x41, 0x6c, 0x18, 0x9d, 0xce, 0x2b, 0xf2, 0xb4, 0xa1, 0x18, 0x1c, 0x4b, 0xf4, 0x9f, 0xd3, 0x90, - 0x8e, 0x40, 0x54, 0x81, 0x8d, 0x81, 0x67, 0x2b, 0x8b, 0xc5, 0xca, 0xd3, 0x4f, 0x97, 0xc5, 0xbf, - 0x35, 0xcf, 0x66, 0x58, 0x6a, 0x51, 0x05, 0xf6, 0x22, 0x53, 0x24, 0xf4, 0xa6, 0xc1, 0x80, 0x11, - 0x7f, 0x7a, 0x7d, 0xcb, 0x66, 0xd1, 0x69, 0xef, 0x44, 0x64, 0x57, 0x72, 0x1d, 0x49, 0xa1, 0xaf, - 0xa1, 0x18, 0x97, 0xda, 0xd4, 0xb7, 0x29, 0x67, 0xf2, 0xec, 0x73, 0x95, 0xd2, 0x92, 0xc7, 0xa8, - 0xe2, 0xfa, 0x92, 0xc7, 0x85, 0xc1, 0xf2, 0xa7, 0x28, 0xab, 0x11, 0x1f, 0x0f, 0xd4, 0xe9, 0x89, - 0xba, 0xde, 0xc0, 0x19, 0x01, 0xc8, 0x73, 0xd3, 0xa1, 0xe0, 0xb9, 0x8e, 0xe7, 0x92, 0x70, 0x44, - 0x49, 0xe5, 0xf5, 0x1b, 0x59, 0xcb, 0x79, 0x9c, 0x93, 0x60, 0x77, 0x44, 0x2b, 0xaf, 0xdf, 0x88, - 0xd2, 0x93, 0xdd, 0xc3, 0x3e, 0xfa, 0x4e, 0x30, 0x2b, 0xa5, 0x8e, 0x13, 0x27, 0x05, 0x2c, 0x1b, - 0xca, 0x90, 0x08, 0xda, 0x85, 0xcd, 0x9b, 0x31, 0x1d, 0x86, 0xa5, 0xb4, 0xa4, 0xd4, 0x87, 0xfe, - 0xe7, 0x06, 0xe4, 0x96, 0x52, 0x80, 0xf2, 0x90, 0xc1, 0x46, 0xd7, 0xc0, 0x97, 0x46, 0x5d, 0xfb, - 0x0f, 0x2a, 0xc1, 0x6e, 0xdf, 0xba, 0xb0, 0xda, 0xef, 0x2d, 0xd2, 0xa9, 0x5e, 0xb5, 0x0c, 0xab, - 0x47, 0xce, 0xab, 0xdd, 0x73, 0x2d, 0x81, 0x9e, 0x40, 0xc9, 0xb4, 0x6a, 0x6d, 0x8c, 0x8d, 0x5a, - 0x6f, 0xce, 0x55, 0x5b, 0xed, 0xbe, 0xd5, 0xd3, 0x1e, 0xa1, 0x67, 0x70, 0xd8, 0x30, 0xad, 0x6a, - 0x93, 0x2c, 0x34, 0xb5, 0x66, 0xef, 0x92, 0x18, 0x1f, 0x3a, 0x26, 0xbe, 0xd2, 0x92, 0xeb, 0x04, - 0xe7, 0xbd, 0x66, 0x2d, 0xb6, 0xb0, 0x81, 0x0e, 0x60, 0x4f, 0x09, 0xd4, 0x12, 0xd2, 0x6b, 0xb7, - 0x49, 0xb7, 0xdd, 0xb6, 0xb4, 0x4d, 0xb4, 0x0d, 0x05, 0xd3, 0xba, 0xac, 0x36, 0xcd, 0x3a, 0xc1, - 0x46, 0xb5, 0xd9, 0xd2, 0x52, 0x68, 0x07, 0xb6, 0xee, 0xeb, 0xd2, 0xc2, 0x44, 0xac, 0x6b, 0x5b, - 0x66, 0xdb, 0x22, 0x97, 0x06, 0xee, 0x9a, 0x6d, 0x4b, 0xcb, 0xa0, 0x7d, 0x40, 0xab, 0xd4, 0x79, - 0xab, 0x5a, 0xd3, 0xb2, 0x68, 0x0f, 0xb6, 0x57, 0xf1, 0x0b, 0xe3, 0x4a, 0x03, 0x91, 0x06, 0xb5, - 0x31, 0xf2, 0xd6, 0x68, 0xb6, 0xdf, 0x93, 0x96, 0x69, 0x99, 0xad, 0x7e, 0x4b, 0xcb, 0xa1, 0x5d, - 0xd0, 0x1a, 0x86, 0x41, 0x4c, 0xab, 0xdb, 0x6f, 0x34, 0xcc, 0x9a, 0x69, 0x58, 0x3d, 0x2d, 0xaf, - 0x3c, 0xaf, 0x0b, 0xbc, 0x20, 0x16, 0xd4, 0xce, 0xab, 0x96, 0x65, 0x34, 0x49, 0xdd, 0xec, 0x56, - 0xdf, 0x36, 0x8d, 0xba, 0x56, 0x44, 0x47, 0x70, 0xd0, 0x33, 0x5a, 0x9d, 0x36, 0xae, 0xe2, 0x2b, - 0x12, 0xf3, 0x8d, 0xaa, 0xd9, 0xec, 0x63, 0x43, 0xdb, 0x42, 0xcf, 0xe1, 0x08, 0x1b, 0xef, 0xfa, - 0x26, 0x36, 0xea, 0xc4, 0x6a, 0xd7, 0x0d, 0xd2, 0x30, 0xaa, 0xbd, 0x3e, 0x36, 0x48, 0xcb, 0xec, - 0x76, 0x4d, 0xeb, 0x1b, 0x4d, 0x43, 0xff, 0x83, 0xe3, 0xb9, 0x64, 0x6e, 0xe0, 0x9e, 0x6a, 0x5b, - 0xc4, 0x17, 0x9f, 0xa7, 0x65, 0x7c, 0xe8, 0x91, 0x8e, 0x61, 0x60, 0x0d, 0xa1, 0x32, 0xec, 0x2f, - 0xdc, 0x2b, 0x07, 0x91, 0xef, 0x1d, 0xc1, 0x75, 0x0c, 0xdc, 0xaa, 0x5a, 0xe2, 0x80, 0x57, 0xb8, - 0x5d, 0xb1, 0xed, 0x05, 0x77, 0x7f, 0xdb, 0x7b, 0xfa, 0x2f, 0x49, 0x28, 0xac, 0x14, 0x3d, 0x7a, - 0x02, 0xd9, 0xd0, 0x19, 0xba, 0x94, 0x8b, 0x56, 0x56, 0x5d, 0xbe, 0x00, 0xe4, 0x05, 0x30, 0xa2, - 0x8e, 0xab, 0xc6, 0x8b, 0xea, 0xb6, 0xac, 0x44, 0xe4, 0x70, 0x79, 0x0c, 0x69, 0xd1, 0x33, 0x62, - 0x96, 0x27, 0x65, 0x83, 0xa4, 0xc4, 0xa7, 0x69, 0x0b, 0xab, 0x62, 0x7e, 0x85, 0x9c, 0x4e, 0x7c, - 0xd9, 0x3b, 0x05, 0xbc, 0x00, 0xd0, 0x7f, 0xa1, 0x30, 0x61, 0x61, 0x48, 0x87, 0x8c, 0xa8, 0xfa, - 0x07, 0xa9, 0xc8, 0x47, 0x60, 0x43, 0x60, 0x42, 0x14, 0xf7, 0xaf, 0x12, 0x6d, 0x2a, 0x51, 0x04, - 0x2a, 0xd1, 0xfd, 0xf1, 0xc9, 0x69, 0xd4, 0x66, 0xcb, 0xe3, 0x93, 0x53, 0xf4, 0x12, 0xb6, 0x55, - 0x2f, 0x3b, 0xae, 0x33, 0x99, 0x4e, 0x54, 0x4f, 0xa7, 0xe5, 0x96, 0xb7, 0x64, 0x4f, 0x2b, 0x5c, - 0xb6, 0xf6, 0x01, 0x64, 0xae, 0x69, 0xc8, 0xc4, 0xe4, 0x2e, 0x65, 0xa4, 0xb1, 0xb4, 0xf8, 0x6e, - 0x30, 0x79, 0x09, 0x89, 0x79, 0x1e, 0x88, 0x69, 0x92, 0x55, 0xd4, 0x0d, 0x63, 0x58, 0xe4, 0x71, - 0xee, 0x81, 0x7e, 0x5c, 0x78, 0xc8, 0x2d, 0x79, 0x50, 0xb8, 0xf4, 0xf0, 0x12, 0xb6, 0xd9, 0x47, - 0x1e, 0x50, 0xe2, 0xf9, 0xf4, 0x6e, 0xca, 0x88, 0x4d, 0x39, 0x2d, 0xe5, 0x65, 0x72, 0xb7, 0x24, - 0xd1, 0x96, 0x78, 0x9d, 0x72, 0xaa, 0x3f, 0x81, 0x32, 0x66, 0x21, 0xe3, 0x2d, 0x27, 0x0c, 0x1d, - 0xcf, 0xad, 0x79, 0x2e, 0x0f, 0xbc, 0x71, 0x74, 0x01, 0xe8, 0x47, 0x70, 0xb8, 0x96, 0x55, 0x13, - 0x5c, 0x2c, 0x7e, 0x37, 0x65, 0xc1, 0x6c, 0xfd, 0xe2, 0x0b, 0x38, 0x5c, 0xcb, 0x46, 0xe3, 0xff, - 0x15, 0x6c, 0xba, 0x9e, 0xcd, 0xc2, 0x52, 0xe2, 0x38, 0x79, 0x92, 0xab, 0xec, 0x2f, 0xcd, 0x4d, - 0xcb, 0xb3, 0xd9, 0xb9, 0x13, 0x72, 0x2f, 0x98, 0x61, 0x25, 0xd2, 0x7f, 0x4f, 0x40, 0x6e, 0x09, - 0x46, 0xfb, 0x90, 0x8a, 0x66, 0xb4, 0x2a, 0xaa, 0xe8, 0x0b, 0xbd, 0x80, 0xe2, 0x98, 0x86, 0x9c, - 0x88, 0x91, 0x4d, 0xc4, 0x21, 0x45, 0xf7, 0xdd, 0x3d, 0x14, 0x7d, 0x09, 0x8f, 0x3d, 0x3e, 0x62, - 0x81, 0x7c, 0x2f, 0x90, 0x70, 0x3a, 0x18, 0xb0, 0x30, 0x24, 0x7e, 0xe0, 0x5d, 0xcb, 0x52, 0x7b, - 0x84, 0x1f, 0xa2, 0xd1, 0x6b, 0xc8, 0x44, 0x35, 0x22, 0x9e, 0x23, 0x62, 0xeb, 0x07, 0x9f, 0x8e, - 0xfc, 0x78, 0xf7, 0x73, 0xa9, 0xfe, 0x6b, 0x02, 0x8a, 0xab, 0x24, 0x7a, 0x2a, 0xab, 0x3f, 0x7e, - 0xad, 0x24, 0xe4, 0x61, 0x2e, 0x21, 0xff, 0x3a, 0x96, 0x0a, 0xec, 0x4e, 0x1c, 0x97, 0xf8, 0xcc, - 0xa5, 0x63, 0xe7, 0x27, 0x46, 0xe2, 0x87, 0x44, 0x52, 0xaa, 0xd7, 0x72, 0x48, 0x87, 0xfc, 0x4a, - 0xd0, 0x1b, 0x32, 0xe8, 0x15, 0xac, 0xf2, 0x5b, 0x12, 0x52, 0xf2, 0xca, 0x0e, 0x50, 0x1d, 0x72, - 0xe2, 0x0a, 0x8f, 0x5e, 0x4d, 0x68, 0x39, 0xe2, 0xd5, 0x07, 0x61, 0xb9, 0xbc, 0x8e, 0x8a, 0x8e, - 0xfc, 0x02, 0x34, 0x23, 0xe4, 0xce, 0x44, 0xdc, 0x86, 0xd1, 0x9b, 0x06, 0x2d, 0xeb, 0xef, 0x3d, - 0x94, 0xca, 0x87, 0x6b, 0xb9, 0xc8, 0x58, 0x53, 0x6d, 0x29, 0x7a, 0x55, 0xa0, 0xa3, 0x25, 0xed, - 0xa7, 0x4f, 0x99, 0xf2, 0xd3, 0x87, 0xe8, 0xc8, 0x9a, 0x0d, 0x3b, 0x6b, 0x2a, 0x1d, 0xfd, 0x7f, - 0x79, 0x07, 0x0f, 0xf6, 0x49, 0xf9, 0xc5, 0x3f, 0xc9, 0x16, 0x5e, 0xd6, 0xb4, 0xc4, 0x8a, 0x97, - 0x87, 0x1b, 0x6a, 0xc5, 0xcb, 0xdf, 0x74, 0xd6, 0xdb, 0xcf, 0xbf, 0x3d, 0x1b, 0x3a, 0x7c, 0x34, - 0xbd, 0x3e, 0x1d, 0x78, 0x93, 0xb3, 0xb1, 0x33, 0x1c, 0x71, 0xd7, 0x71, 0x87, 0x2e, 0xe3, 0x3f, - 0x7a, 0xc1, 0xed, 0xd9, 0xd8, 0xb5, 0xcf, 0xe4, 0x2b, 0xed, 0x6c, 0x6e, 0xee, 0x3a, 0x25, 0x1f, - 0xf8, 0x5f, 0xfc, 0x15, 0x00, 0x00, 0xff, 0xff, 0xe1, 0x3e, 0x2a, 0xde, 0x10, 0x0c, 0x00, 0x00, + // 1503 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0xdd, 0x72, 0xda, 0xce, + 0x15, 0x2f, 0x06, 0x1b, 0x38, 0x7c, 0xc9, 0xeb, 0x8f, 0x60, 0x6c, 0xe7, 0xef, 0xa8, 0x6d, 0xc2, + 0x64, 0x32, 0xf6, 0x94, 0x4e, 0x32, 0xbd, 0x6a, 0x87, 0x80, 0xa8, 0x35, 0x06, 0xe1, 0x2c, 0xe0, + 0xc4, 0xed, 0xc5, 0xce, 0x1a, 0xad, 0x41, 0xb5, 0x90, 0x14, 0x69, 0x49, 0x43, 0x1f, 0xa0, 0xaf, + 0xd3, 0xde, 0xf4, 0xb6, 0x17, 0x7d, 0x8b, 0xbe, 0x4d, 0x67, 0x77, 0xc5, 0x97, 0x8d, 0xd3, 0xff, + 0x95, 0xd1, 0xef, 0xfc, 0xf6, 0x7c, 0x9f, 0xb3, 0x6b, 0x38, 0x0c, 0xfd, 0x29, 0x67, 0x61, 0x18, + 0x0c, 0x2f, 0xd4, 0xaf, 0xf3, 0x20, 0xf4, 0xb9, 0x8f, 0xb2, 0x0b, 0xbc, 0x92, 0x0d, 0x83, 0xa1, + 0x42, 0xf5, 0xff, 0x6c, 0x41, 0xf1, 0x9a, 0xce, 0x26, 0xcc, 0xe3, 0x98, 0x7d, 0x9d, 0xb2, 0x88, + 0x23, 0x04, 0x29, 0x9b, 0x45, 0xbc, 0x9c, 0x38, 0x4b, 0x54, 0xf3, 0x58, 0xfe, 0x46, 0x1a, 0x24, + 0xe9, 0x84, 0x97, 0xb7, 0xce, 0x12, 0xd5, 0x24, 0x16, 0x3f, 0xd1, 0x2b, 0xc8, 0x07, 0xea, 0x1c, + 0x19, 0xd3, 0x68, 0x5c, 0x4e, 0x4a, 0x76, 0x2e, 0xc6, 0x2e, 0x69, 0x34, 0x46, 0x55, 0xd0, 0xee, + 0x1d, 0x8f, 0xba, 0x64, 0xe8, 0xf2, 0x6f, 0xc4, 0x66, 0x2e, 0xa7, 0xe5, 0xd4, 0x59, 0xa2, 0xba, + 0x8d, 0x8b, 0x12, 0x6f, 0xb8, 0xfc, 0x5b, 0x53, 0xa0, 0xe8, 0x0d, 0x94, 0xe6, 0xca, 0x42, 0xe5, + 0x45, 0x79, 0xfb, 0x2c, 0x51, 0xcd, 0xe2, 0x62, 0xb0, 0xee, 0xdb, 0x1b, 0x28, 0x71, 0x67, 0xc2, + 0xfc, 0x29, 0x27, 0x11, 0x1b, 0xfa, 0x9e, 0x1d, 0x95, 0x77, 0x94, 0xc6, 0x18, 0xee, 0x29, 0x14, + 0xe9, 0x50, 0xb8, 0x67, 0x8c, 0xb8, 0xce, 0xc4, 0xe1, 0x24, 0xa2, 0xbc, 0x9c, 0x96, 0xae, 0xe7, + 0xee, 0x19, 0x6b, 0x0b, 0xac, 0x47, 0xb9, 0xf0, 0xcf, 0x9f, 0xf2, 0x91, 0xef, 0x78, 0x23, 0x32, + 0x1c, 0x53, 0x8f, 0x38, 0x76, 0x39, 0x73, 0x96, 0xa8, 0xa6, 0x70, 0x71, 0x8e, 0x37, 0xc6, 0xd4, + 0x33, 0x6d, 0x74, 0x0a, 0x20, 0x63, 0x90, 0xea, 0xca, 0x59, 0x69, 0x31, 0x2b, 0x10, 0xa9, 0x4b, + 0xff, 0x0b, 0x94, 0x16, 0x39, 0x8c, 0x02, 0xdf, 0x8b, 0x18, 0x3a, 0x82, 0x4c, 0x40, 0x67, 0x2a, + 0x35, 0x2a, 0x91, 0xe9, 0x80, 0xce, 0x64, 0x5a, 0x8e, 0x21, 0x1b, 0x84, 0x8c, 0x38, 0x13, 0x3a, + 0x62, 0x32, 0xa3, 0x79, 0x9c, 0x09, 0x42, 0x66, 0x8a, 0x6f, 0xf4, 0x13, 0xcc, 0x53, 0x48, 0x58, + 0x18, 0xca, 0xac, 0x66, 0x31, 0xc4, 0x90, 0x11, 0x86, 0xfa, 0xef, 0xa1, 0x84, 0x45, 0x21, 0x5b, + 0x8c, 0xfd, 0xa8, 0x60, 0x2f, 0x20, 0x4d, 0x27, 0x2a, 0x72, 0x55, 0xb4, 0x1d, 0x3a, 0x11, 0x41, + 0xeb, 0x36, 0x68, 0xcb, 0xf3, 0xb1, 0xb3, 0x55, 0xd0, 0x44, 0x73, 0x88, 0x3c, 0x88, 0xa4, 0x4d, + 0xc4, 0xa9, 0x84, 0x3c, 0x55, 0x8c, 0xf1, 0x16, 0x63, 0x9d, 0x88, 0x72, 0xf4, 0x5a, 0xe5, 0x9f, + 0xb8, 0xfe, 0xf0, 0x41, 0x54, 0x94, 0xce, 0x62, 0xf5, 0x05, 0x01, 0xb7, 0xfd, 0xe1, 0x43, 0x53, + 0x80, 0xfa, 0x9f, 0x01, 0xf5, 0x98, 0x67, 0xf7, 0x7d, 0x69, 0x6b, 0xee, 0xe8, 0xe3, 0x9e, 0x49, + 0x3c, 0xed, 0x19, 0x1d, 0xb6, 0x65, 0x9f, 0x4a, 0xb5, 0xb9, 0x5a, 0xfe, 0xdc, 0xf5, 0x44, 0xb3, + 0x2a, 0x35, 0x4a, 0xa4, 0x13, 0xd8, 0x5b, 0x53, 0x1e, 0x47, 0x51, 0x01, 0x91, 0x46, 0x95, 0xd6, + 0xc4, 0x22, 0xad, 0xf2, 0x1b, 0xbd, 0x83, 0xf4, 0x3d, 0x75, 0xdc, 0x69, 0x38, 0x57, 0x8c, 0xce, + 0x17, 0xe3, 0x70, 0xde, 0x52, 0x12, 0x3c, 0xa7, 0xe8, 0x7f, 0x4f, 0x43, 0x3a, 0x06, 0x51, 0x0d, + 0x52, 0x43, 0xdf, 0x56, 0x1a, 0x8b, 0xb5, 0x97, 0x4f, 0x8f, 0xcd, 0xff, 0x36, 0x7c, 0x9b, 0x61, + 0xc9, 0x45, 0x35, 0x38, 0x88, 0x55, 0x91, 0xc8, 0x9f, 0x86, 0x43, 0x46, 0x82, 0xe9, 0xdd, 0x03, + 0x9b, 0xc5, 0xd5, 0xde, 0x8b, 0x85, 0x3d, 0x29, 0xbb, 0x96, 0x22, 0xf4, 0x07, 0x28, 0x8a, 0x1e, + 0xf4, 0x98, 0x4b, 0xa6, 0x81, 0x4d, 0x39, 0x93, 0xb5, 0xcf, 0xd5, 0xca, 0x2b, 0x16, 0x1b, 0x8a, + 0x30, 0x90, 0x72, 0x5c, 0x18, 0xae, 0x7e, 0x8a, 0xb6, 0x1a, 0x73, 0x77, 0xa8, 0xaa, 0x97, 0x92, + 0x6d, 0x9c, 0x11, 0x80, 0xac, 0x9b, 0x0e, 0x05, 0xdf, 0x73, 0x7c, 0x8f, 0x44, 0x63, 0x4a, 0x6a, + 0xef, 0x3f, 0xc8, 0xf1, 0xca, 0xe3, 0x9c, 0x04, 0x7b, 0x63, 0x5a, 0x7b, 0xff, 0x41, 0xb4, 0x9e, + 0x6c, 0x72, 0xf6, 0x3d, 0x70, 0xc2, 0x99, 0x9c, 0xab, 0x02, 0x96, 0x7d, 0x6f, 0x48, 0x04, 0xed, + 0xc3, 0xf6, 0xbd, 0x4b, 0x47, 0x91, 0x9c, 0xa5, 0x02, 0x56, 0x1f, 0xfa, 0x7f, 0x53, 0x90, 0x5b, + 0x49, 0x01, 0xca, 0x43, 0x06, 0x1b, 0x3d, 0x03, 0xdf, 0x18, 0x4d, 0xed, 0x17, 0xa8, 0x0c, 0xfb, + 0x03, 0xeb, 0xca, 0xea, 0x7e, 0xb6, 0xc8, 0x75, 0xfd, 0xb6, 0x63, 0x58, 0x7d, 0x72, 0x59, 0xef, + 0x5d, 0x6a, 0x09, 0x74, 0x02, 0x65, 0xd3, 0x6a, 0x74, 0x31, 0x36, 0x1a, 0xfd, 0x85, 0xac, 0xde, + 0xe9, 0x0e, 0xac, 0xbe, 0xb6, 0x85, 0x7e, 0x82, 0xe3, 0x96, 0x69, 0xd5, 0xdb, 0x64, 0xc9, 0x69, + 0xb4, 0xfb, 0x37, 0xc4, 0xf8, 0x72, 0x6d, 0xe2, 0x5b, 0x2d, 0xb9, 0x89, 0x70, 0xd9, 0x6f, 0x37, + 0xe6, 0x1a, 0x52, 0xe8, 0x08, 0x0e, 0x14, 0x41, 0x1d, 0x21, 0xfd, 0x6e, 0x97, 0xf4, 0xba, 0x5d, + 0x4b, 0xdb, 0x46, 0xbb, 0x50, 0x30, 0xad, 0x9b, 0x7a, 0xdb, 0x6c, 0x12, 0x6c, 0xd4, 0xdb, 0x1d, + 0x6d, 0x07, 0xed, 0x41, 0xe9, 0x31, 0x2f, 0x2d, 0x54, 0xcc, 0x79, 0x5d, 0xcb, 0xec, 0x5a, 0xe4, + 0xc6, 0xc0, 0x3d, 0xb3, 0x6b, 0x69, 0x19, 0x74, 0x08, 0x68, 0x5d, 0x74, 0xd9, 0xa9, 0x37, 0xb4, + 0x2c, 0x3a, 0x80, 0xdd, 0x75, 0xfc, 0xca, 0xb8, 0xd5, 0x40, 0xa4, 0x41, 0x39, 0x46, 0x3e, 0x1a, + 0xed, 0xee, 0x67, 0xd2, 0x31, 0x2d, 0xb3, 0x33, 0xe8, 0x68, 0x39, 0xb4, 0x0f, 0x5a, 0xcb, 0x30, + 0x88, 0x69, 0xf5, 0x06, 0xad, 0x96, 0xd9, 0x30, 0x0d, 0xab, 0xaf, 0xe5, 0x95, 0xe5, 0x4d, 0x81, + 0x17, 0xc4, 0x81, 0xc6, 0x65, 0xdd, 0xb2, 0x8c, 0x36, 0x69, 0x9a, 0xbd, 0xfa, 0xc7, 0xb6, 0xd1, + 0xd4, 0x8a, 0xe8, 0x14, 0x8e, 0xfa, 0x46, 0xe7, 0xba, 0x8b, 0xeb, 0xf8, 0x96, 0xcc, 0xe5, 0xad, + 0xba, 0xd9, 0x1e, 0x60, 0x43, 0x2b, 0xa1, 0x57, 0x70, 0x8a, 0x8d, 0x4f, 0x03, 0x13, 0x1b, 0x4d, + 0x62, 0x75, 0x9b, 0x06, 0x69, 0x19, 0xf5, 0xfe, 0x00, 0x1b, 0xa4, 0x63, 0xf6, 0x7a, 0xa6, 0xf5, + 0x47, 0x4d, 0x43, 0xbf, 0x82, 0xb3, 0x05, 0x65, 0xa1, 0xe0, 0x11, 0x6b, 0x57, 0xc4, 0x37, 0xaf, + 0xa7, 0x65, 0x7c, 0xe9, 0x93, 0x6b, 0xc3, 0xc0, 0x1a, 0x42, 0x15, 0x38, 0x5c, 0x9a, 0x57, 0x06, + 0x62, 0xdb, 0x7b, 0x42, 0x76, 0x6d, 0xe0, 0x4e, 0xdd, 0x12, 0x05, 0x5e, 0x93, 0xed, 0x0b, 0xb7, + 0x97, 0xb2, 0xc7, 0x6e, 0x1f, 0xe8, 0xff, 0x48, 0x42, 0x61, 0xad, 0xe9, 0xd1, 0x09, 0x64, 0x23, + 0x67, 0xe4, 0x51, 0x2e, 0x46, 0x59, 0x4d, 0xf9, 0x12, 0x90, 0x7b, 0x7a, 0x4c, 0x1d, 0x4f, 0xad, + 0x17, 0x35, 0x6d, 0x59, 0x89, 0xc8, 0xe5, 0xf2, 0x02, 0xd2, 0xf3, 0x3d, 0x9f, 0x94, 0x03, 0xb2, + 0x33, 0x54, 0xfb, 0xfd, 0x04, 0xb2, 0x62, 0x7f, 0x45, 0x9c, 0x4e, 0x02, 0x39, 0x3b, 0x05, 0xbc, + 0x04, 0xd0, 0x2f, 0xa1, 0x30, 0x61, 0x51, 0x44, 0x47, 0x8c, 0xa8, 0xfe, 0x07, 0xc9, 0xc8, 0xc7, + 0x60, 0x4b, 0x60, 0x82, 0x34, 0x9f, 0x5f, 0x45, 0xda, 0x56, 0xa4, 0x18, 0x54, 0xa4, 0xc7, 0xeb, + 0x93, 0xd3, 0x78, 0xcc, 0x56, 0xd7, 0x27, 0xa7, 0xe8, 0x2d, 0xec, 0xaa, 0x59, 0x76, 0x3c, 0x67, + 0x32, 0x9d, 0xa8, 0x99, 0x4e, 0x4b, 0x97, 0x4b, 0x72, 0xa6, 0x15, 0x2e, 0x47, 0xfb, 0x08, 0x32, + 0x77, 0x34, 0x62, 0x62, 0x73, 0xcb, 0xdb, 0xab, 0x80, 0xd3, 0xe2, 0xbb, 0xc5, 0xe4, 0x25, 0x24, + 0xf6, 0x79, 0x28, 0xb6, 0x49, 0x56, 0x89, 0xee, 0x19, 0xc3, 0x22, 0x8f, 0x0b, 0x0b, 0xf4, 0xfb, + 0xd2, 0x42, 0x6e, 0xc5, 0x82, 0xc2, 0xa5, 0x85, 0xb7, 0xb0, 0xcb, 0xbe, 0xf3, 0x90, 0x12, 0x3f, + 0xa0, 0x5f, 0xa7, 0x8c, 0xd8, 0x94, 0xd3, 0x72, 0x5e, 0x26, 0xb7, 0x24, 0x05, 0x5d, 0x89, 0x37, + 0x29, 0xa7, 0xfa, 0x09, 0x54, 0x30, 0x8b, 0x18, 0xef, 0x38, 0x51, 0xe4, 0xf8, 0x5e, 0xc3, 0xf7, + 0x78, 0xe8, 0xbb, 0xf1, 0x05, 0xa0, 0x9f, 0xc2, 0xf1, 0x46, 0xa9, 0xda, 0xe0, 0xe2, 0xf0, 0xa7, + 0x29, 0x0b, 0x67, 0x9b, 0x0f, 0x5f, 0xc1, 0xf1, 0x46, 0x69, 0xbc, 0xfe, 0xdf, 0xc1, 0xb6, 0xe7, + 0xdb, 0x2c, 0x2a, 0x27, 0xce, 0x92, 0xd5, 0x5c, 0xed, 0x70, 0x65, 0x6f, 0x5a, 0xbe, 0xcd, 0x2e, + 0x9d, 0x88, 0xfb, 0xe1, 0x0c, 0x2b, 0x92, 0xfe, 0xef, 0x04, 0xe4, 0x56, 0x60, 0x74, 0x08, 0x3b, + 0xf1, 0x8e, 0x56, 0x4d, 0x15, 0x7f, 0xa1, 0xd7, 0x50, 0x74, 0x69, 0xc4, 0x89, 0x58, 0xd9, 0x44, + 0x14, 0x29, 0xbe, 0xef, 0x1e, 0xa1, 0xe8, 0x77, 0xf0, 0xc2, 0xe7, 0x63, 0x16, 0xaa, 0x87, 0x44, + 0x34, 0x1d, 0x0e, 0x59, 0x14, 0x91, 0x20, 0xf4, 0xef, 0x64, 0xab, 0x6d, 0xe1, 0xe7, 0xc4, 0xe8, + 0x3d, 0x64, 0xe2, 0x1e, 0x89, 0xca, 0x29, 0xe9, 0xfa, 0xd1, 0xd3, 0x95, 0x3f, 0xf7, 0x7e, 0x41, + 0xd5, 0xff, 0x99, 0x80, 0xe2, 0xba, 0x10, 0xbd, 0x94, 0xdd, 0x2f, 0x5b, 0xd0, 0xb1, 0x65, 0x1c, + 0x29, 0xbc, 0x82, 0xfc, 0xec, 0x58, 0x6a, 0xb0, 0x3f, 0x71, 0x3c, 0x12, 0x30, 0x8f, 0xba, 0xce, + 0xdf, 0x18, 0x99, 0x3f, 0x24, 0x92, 0x92, 0xbd, 0x51, 0x86, 0x74, 0xc8, 0xaf, 0x05, 0x9d, 0x92, + 0x41, 0xaf, 0x61, 0xb5, 0x7f, 0x25, 0x61, 0x47, 0x5e, 0xd9, 0x21, 0x6a, 0x42, 0x4e, 0x5c, 0xe1, + 0xf1, 0xab, 0x09, 0xad, 0x46, 0xbc, 0xfe, 0x1a, 0xad, 0x54, 0x36, 0x89, 0xe2, 0x92, 0x5f, 0x81, + 0x66, 0x44, 0xdc, 0x99, 0x88, 0xdb, 0x30, 0x7e, 0xd3, 0xa0, 0x55, 0xfe, 0xa3, 0x87, 0x52, 0xe5, + 0x78, 0xa3, 0x2c, 0x56, 0xd6, 0x56, 0x2e, 0xc5, 0xaf, 0x0a, 0x74, 0xba, 0xc2, 0x7d, 0xfa, 0x94, + 0xa9, 0xbc, 0x7c, 0x4e, 0x1c, 0x6b, 0xb3, 0x61, 0x6f, 0x43, 0xa7, 0xa3, 0x5f, 0xaf, 0x7a, 0xf0, + 0xec, 0x9c, 0x54, 0x5e, 0xff, 0x3f, 0xda, 0xd2, 0xca, 0x86, 0x91, 0x58, 0xb3, 0xf2, 0xfc, 0x40, + 0xad, 0x59, 0xf9, 0xc1, 0x64, 0x7d, 0xfc, 0xcd, 0x9f, 0x2e, 0x46, 0x0e, 0x1f, 0x4f, 0xef, 0xce, + 0x87, 0xfe, 0xe4, 0xc2, 0x75, 0x46, 0x63, 0xee, 0x39, 0xde, 0xc8, 0x63, 0xfc, 0xaf, 0x7e, 0xf8, + 0x70, 0xe1, 0x7a, 0xf6, 0x85, 0x7c, 0xa5, 0x5d, 0x2c, 0xd4, 0xdd, 0xed, 0xc8, 0xff, 0x2e, 0x7e, + 0xfb, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x15, 0x95, 0x7d, 0xcd, 0x8d, 0x0c, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/lnrpc/routerrpc/router.proto b/lnrpc/routerrpc/router.proto index f65a8c11eb2..f80f54c39fa 100644 --- a/lnrpc/routerrpc/router.proto +++ b/lnrpc/routerrpc/router.proto @@ -7,41 +7,57 @@ package routerrpc; option go_package = "github.com/lightningnetwork/lnd/lnrpc/routerrpc"; message PaymentRequest { - /** - A serialized BOLT-11 payment request that contains all information - required to dispatch the payment. If the pay req is invalid, or expired, - an error will be returned. - */ - string pay_req = 1; + /// The identity pubkey of the payment recipient + bytes dest = 1; + /// Number of satoshis to send. + int64 amt = 2; + + /// The hash to use within the payment's HTLC + bytes payment_hash = 3; + /** - An absolute limit on the highest fee we should pay when looking for a route - to the destination. Routes with fees higher than this will be ignored, if - there are no routes with a fee below this amount, an error will be - returned. + The CLTV delta from the current height that should be used to set the + timelock for the final hop. */ - int64 fee_limit_sat = 2; + int32 final_cltv_delta = 4; /** - An absolute limit on the cumulative CLTV value along the route for this - payment. Routes with total CLTV values higher than this will be ignored, - if there are no routes with a CLTV value below this amount, an error will - be returned. + A bare-bones invoice for a payment within the Lightning Network. With the + details of the invoice, the sender has all the data necessary to send a + payment to the recipient. The amount in the payment request may be zero. In + that case it is required to set the amt field as well. If no payment request + is specified, the following fields are required: dest, amt and payment_hash. */ - int32 cltv_limit = 3; + string payment_request = 5; /** An upper limit on the amount of time we should spend when attempting to fulfill the payment. This is expressed in seconds. If we cannot make a successful payment within this time frame, an error will be returned. + This field must be non-zero. */ - int32 timeout_seconds = 4; + int32 timeout_seconds = 6; + + /** + The maximum number of satoshis that will be paid as a fee of the payment. + If this field is left to the default value of 0, only zero-fee routes will + be considered. This usually means single hop routes connecting directly to + the destination. To send the payment without a fee limit, use max int here. + */ + int64 fee_limit_sat = 7; /** The channel id of the channel that must be taken to the first hop. If zero, any channel may be used. */ - int64 outgoing_channel_id = 5; + uint64 outgoing_chan_id = 8; + + /** + An optional maximum total time lock for the route. If zero, there is no + maximum enforced. + */ + int32 cltv_limit = 9; } message PaymentResponse { diff --git a/lnrpc/routerrpc/router_backend.go b/lnrpc/routerrpc/router_backend.go index 9e399e8fc7f..f1eb61f4217 100644 --- a/lnrpc/routerrpc/router_backend.go +++ b/lnrpc/routerrpc/router_backend.go @@ -4,12 +4,15 @@ import ( "encoding/hex" "errors" "fmt" + "time" + "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcutil" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing/route" + "github.com/lightningnetwork/lnd/zpay32" context "golang.org/x/net/context" ) @@ -38,6 +41,12 @@ type RouterBackend struct { finalExpiry ...uint16) (*route.Route, error) MissionControl *routing.MissionControl + + // ActiveNetParams are the network parameters of the primary network + // that the route is operating on. This is necessary so we can ensure + // that we receive payment requests that send to destinations on our + // network. + ActiveNetParams *chaincfg.Params } // QueryRoutes attempts to query the daemons' Channel Router for a possible @@ -336,3 +345,158 @@ func (r *RouterBackend) UnmarshallRoute(rpcroute *lnrpc.Route) ( return route, nil } + +// extractIntentFromSendRequest attempts to parse the SendRequest details +// required to dispatch a client from the information presented by an RPC +// client. +func (r *RouterBackend) extractIntentFromSendRequest(rpcPayReq *PaymentRequest) ( + *routing.LightningPayment, error) { + + payIntent := &routing.LightningPayment{} + + // Pass along an outgoing channel restriction if specified. + if rpcPayReq.OutgoingChanId != 0 { + payIntent.OutgoingChannelID = &rpcPayReq.OutgoingChanId + } + + // Take cltv limit from request if set. + if rpcPayReq.CltvLimit != 0 { + cltvLimit := uint32(rpcPayReq.CltvLimit) + payIntent.CltvLimit = &cltvLimit + } + + // Take fee limit from request. + payIntent.FeeLimit = lnwire.NewMSatFromSatoshis( + btcutil.Amount(rpcPayReq.FeeLimitSat), + ) + + // Set payment attempt timeout. + if rpcPayReq.TimeoutSeconds == 0 { + return nil, errors.New("timeout_seconds must be specified") + } + + payIntent.PayAttemptTimeout = time.Second * + time.Duration(rpcPayReq.TimeoutSeconds) + + // 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 != "" { + switch { + + case len(rpcPayReq.Dest) > 0: + return nil, errors.New("dest and payment_request " + + "cannot appear together") + + case len(rpcPayReq.PaymentHash) > 0: + return nil, errors.New("dest and payment_hash " + + "cannot appear together") + + case rpcPayReq.FinalCltvDelta != 0: + return nil, errors.New("dest and final_cltv_delta " + + "cannot appear together") + } + + payReq, err := zpay32.Decode( + rpcPayReq.PaymentRequest, r.ActiveNetParams, + ) + if err != nil { + return nil, err + } + + // Next, we'll ensure that this payreq hasn't already expired. + err = ValidatePayReqExpiry(payReq) + if err != nil { + return nil, 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 { + if rpcPayReq.Amt == 0 { + return nil, errors.New("amount must be " + + "specified when paying a zero amount " + + "invoice") + } + + payIntent.Amount = lnwire.NewMSatFromSatoshis( + btcutil.Amount(rpcPayReq.Amt), + ) + } else { + if rpcPayReq.Amt != 0 { + return nil, errors.New("amount must not be " + + "specified when paying a non-zero " + + " amount invoice") + } + + payIntent.Amount = *payReq.MilliSat + } + + copy(payIntent.PaymentHash[:], payReq.PaymentHash[:]) + destKey := payReq.Destination.SerializeCompressed() + copy(payIntent.Target[:], destKey) + + payIntent.FinalCLTVDelta = uint16(payReq.MinFinalCLTVExpiry()) + payIntent.RouteHints = payReq.RouteHints + } else { + // Otherwise, If the payment request field was not specified + // (and a custom route wasn't specified), construct the payment + // from the other fields. + + // Payment destination. + if len(rpcPayReq.Dest) != 33 { + return nil, errors.New("invalid key length") + + } + pubBytes := rpcPayReq.Dest + copy(payIntent.Target[:], pubBytes) + + // Final payment CLTV delta. + if rpcPayReq.FinalCltvDelta != 0 { + payIntent.FinalCLTVDelta = + uint16(rpcPayReq.FinalCltvDelta) + } else { + payIntent.FinalCLTVDelta = zpay32.DefaultFinalCLTVDelta + } + + // Amount. + if rpcPayReq.Amt == 0 { + return nil, errors.New("amount must be specified") + } + + payIntent.Amount = lnwire.NewMSatFromSatoshis( + btcutil.Amount(rpcPayReq.Amt), + ) + + // Payment hash. + copy(payIntent.PaymentHash[:], rpcPayReq.PaymentHash) + } + + // 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.Amount > r.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.Amount, + r.MaxPaymentMSat) + + } + + return payIntent, nil +} + +// ValidatePayReqExpiry checks if the passed payment request has expired. In +// the case it has expired, an error will be returned. +func ValidatePayReqExpiry(payReq *zpay32.Invoice) error { + expiry := payReq.Expiry() + validUntil := payReq.Timestamp.Add(expiry) + if time.Now().After(validUntil) { + return fmt.Errorf("invoice expired. Valid until %v", validUntil) + } + + return nil +} diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index 3a5ad2764c3..5047fc4e6e2 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -9,7 +9,6 @@ import ( "io/ioutil" "os" "path/filepath" - "time" "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/lntypes" @@ -19,7 +18,6 @@ import ( "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing/route" - "github.com/lightningnetwork/lnd/zpay32" "google.golang.org/grpc" "gopkg.in/macaroon-bakery.v2/bakery" ) @@ -190,56 +188,18 @@ func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error { func (s *Server) SendPayment(ctx context.Context, req *PaymentRequest) (*PaymentResponse, error) { - switch { - // If the payment request isn't populated, then we won't be able to - // even attempt a payment. - case req.PayReq == "": - return nil, fmt.Errorf("a valid payment request MUST be specified") - } - - // Now that we know the payment request is present, we'll attempt to - // decode it in order to parse out all the parameters for the route. - payReq, err := zpay32.Decode(req.PayReq, s.cfg.ActiveNetParams) + payment, err := s.cfg.RouterBackend.extractIntentFromSendRequest(req) if err != nil { return nil, err } - // Atm, this service does not support invoices that don't have their - // value fully specified. - if payReq.MilliSat == nil { - return nil, fmt.Errorf("zero value invoices are not supported") - } - - var destination route.Vertex - copy(destination[:], payReq.Destination.SerializeCompressed()) - - // Now that all the information we need has been parsed, we'll map this - // proto request into a proper request that our backing router can - // understand. - finalDelta := uint16(payReq.MinFinalCLTVExpiry()) - payment := routing.LightningPayment{ - Target: destination, - Amount: *payReq.MilliSat, - FeeLimit: lnwire.MilliSatoshi(req.FeeLimitSat), - PaymentHash: *payReq.PaymentHash, - FinalCLTVDelta: finalDelta, - PayAttemptTimeout: time.Second * time.Duration(req.TimeoutSeconds), - RouteHints: payReq.RouteHints, - } - - // Pin to an outgoing channel if specified. - if req.OutgoingChannelId != 0 { - chanID := uint64(req.OutgoingChannelId) - payment.OutgoingChannelID = &chanID - } - - preImage, _, err := s.cfg.Router.SendPayment(&payment) + preImage, _, err := s.cfg.Router.SendPayment(payment) if err != nil { return nil, err } return &PaymentResponse{ - PayHash: (*payReq.PaymentHash)[:], + PayHash: payment.PaymentHash[:], PreImage: preImage[:], }, nil } diff --git a/rpcserver.go b/rpcserver.go index 5258c623a7f..d5fb47d620e 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -474,8 +474,9 @@ func newRPCServer(s *server, macService *macaroons.Service, return info.NodeKey1Bytes, info.NodeKey2Bytes, nil }, - FindRoute: s.chanRouter.FindRoute, - MissionControl: s.missionControl, + FindRoute: s.chanRouter.FindRoute, + MissionControl: s.missionControl, + ActiveNetParams: activeNetParams.Params, } var ( @@ -2736,18 +2737,6 @@ func (r *rpcServer) SubscribeChannelEvents(req *lnrpc.ChannelEventSubscription, } } -// validatePayReqExpiry checks if the passed payment request has expired. In -// the case it has expired, an error will be returned. -func validatePayReqExpiry(payReq *zpay32.Invoice) error { - expiry := payReq.Expiry() - validUntil := payReq.Timestamp.Add(expiry) - if time.Now().After(validUntil) { - return fmt.Errorf("invoice expired. Valid until %v", validUntil) - } - - return nil -} - // paymentStream enables different types of payment streams, such as: // lnrpc.Lightning_SendPaymentServer and lnrpc.Lightning_SendToRouteServer to // execute sendPayment. We use this struct as a sort of bridge to enable code @@ -2930,7 +2919,7 @@ func extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error } // Next, we'll ensure that this payreq hasn't already expired. - err = validatePayReqExpiry(payReq) + err = routerrpc.ValidatePayReqExpiry(payReq) if err != nil { return payIntent, err } diff --git a/subrpcserver_config.go b/subrpcserver_config.go index 7dc116cc839..d99faeab629 100644 --- a/subrpcserver_config.go +++ b/subrpcserver_config.go @@ -191,9 +191,6 @@ func (s *subRPCServerConfigs) PopulateDependencies(cc *chainControl, subCfgValue.FieldByName("NetworkDir").Set( reflect.ValueOf(networkDir), ) - subCfgValue.FieldByName("ActiveNetParams").Set( - reflect.ValueOf(activeNetParams), - ) subCfgValue.FieldByName("MacService").Set( reflect.ValueOf(macService), ) From 87d3207baf70a401e47138c6525ec71b10dd5a3a Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Wed, 29 May 2019 08:57:04 +0200 Subject: [PATCH 4/9] channeldb+routing: move control tower interface to routing This commit creates an empty shall for control tower in the routing package. It is a preparation for adding event notification. --- .../{control_tower.go => payment_control.go} | 52 ++-------- ..._tower_test.go => payment_control_test.go} | 0 routing/control_tower.go | 94 +++++++++++++++++++ routing/mock_test.go | 2 +- routing/router.go | 2 +- server.go | 4 +- 6 files changed, 109 insertions(+), 45 deletions(-) rename channeldb/{control_tower.go => payment_control.go} (83%) rename channeldb/{control_tower_test.go => payment_control_test.go} (100%) create mode 100644 routing/control_tower.go diff --git a/channeldb/control_tower.go b/channeldb/payment_control.go similarity index 83% rename from channeldb/control_tower.go rename to channeldb/payment_control.go index 97b4418026c..1cbc0b81f83 100644 --- a/channeldb/control_tower.go +++ b/channeldb/payment_control.go @@ -35,46 +35,14 @@ var ( ErrUnknownPaymentStatus = errors.New("unknown payment status") ) -// ControlTower tracks all outgoing payments made, whose primary purpose is to -// prevent duplicate payments to the same payment hash. In production, a -// persistent implementation is preferred so that tracking can survive across -// restarts. Payments are transitioned through various payment states, and the -// ControlTower interface provides access to driving the state transitions. -type ControlTower interface { - // InitPayment atomically moves the payment into the InFlight state. - // This method checks that no suceeded payment exist for this payment - // hash. - InitPayment(lntypes.Hash, *PaymentCreationInfo) error - - // RegisterAttempt atomically records the provided PaymentAttemptInfo. - RegisterAttempt(lntypes.Hash, *PaymentAttemptInfo) error - - // Success transitions a payment into the Succeeded state. After - // invoking this method, InitPayment should always return an error to - // prevent us from making duplicate payments to the same payment hash. - // The provided preimage is atomically saved to the DB for record - // keeping. - Success(lntypes.Hash, lntypes.Preimage) error - - // Fail transitions a payment into the Failed state, and records the - // reason the payment failed. After invoking this method, InitPayment - // should return nil on its next call for this payment hash, allowing - // the switch to make a subsequent payment. - Fail(lntypes.Hash, FailureReason) error - - // FetchInFlightPayments returns all payments with status InFlight. - FetchInFlightPayments() ([]*InFlightPayment, error) -} - -// paymentControl is persistent implementation of ControlTower to restrict -// double payment sending. -type paymentControl struct { +// PaymentControl implements persistence for payments and payment attempts. +type PaymentControl struct { db *DB } -// NewPaymentControl creates a new instance of the paymentControl. -func NewPaymentControl(db *DB) ControlTower { - return &paymentControl{ +// NewPaymentControl creates a new instance of the PaymentControl. +func NewPaymentControl(db *DB) *PaymentControl { + return &PaymentControl{ db: db, } } @@ -83,7 +51,7 @@ func NewPaymentControl(db *DB) ControlTower { // making sure it does not already exist as an in-flight payment. Then this // method returns successfully, the payment is guranteeed to be in the InFlight // state. -func (p *paymentControl) InitPayment(paymentHash lntypes.Hash, +func (p *PaymentControl) InitPayment(paymentHash lntypes.Hash, info *PaymentCreationInfo) error { var b bytes.Buffer @@ -173,7 +141,7 @@ func (p *paymentControl) InitPayment(paymentHash lntypes.Hash, // RegisterAttempt atomically records the provided PaymentAttemptInfo to the // DB. -func (p *paymentControl) RegisterAttempt(paymentHash lntypes.Hash, +func (p *PaymentControl) RegisterAttempt(paymentHash lntypes.Hash, attempt *PaymentAttemptInfo) error { // Serialize the information before opening the db transaction. @@ -218,7 +186,7 @@ func (p *paymentControl) RegisterAttempt(paymentHash lntypes.Hash, // method, InitPayment should always return an error to prevent us from making // duplicate payments to the same payment hash. The provided preimage is // atomically saved to the DB for record keeping. -func (p *paymentControl) Success(paymentHash lntypes.Hash, +func (p *PaymentControl) Success(paymentHash lntypes.Hash, preimage lntypes.Preimage) error { var updateErr error @@ -257,7 +225,7 @@ func (p *paymentControl) Success(paymentHash lntypes.Hash, // payment failed. After invoking this method, InitPayment should return nil on // its next call for this payment hash, allowing the switch to make a // subsequent payment. -func (p *paymentControl) Fail(paymentHash lntypes.Hash, +func (p *PaymentControl) Fail(paymentHash lntypes.Hash, reason FailureReason) error { var updateErr error @@ -402,7 +370,7 @@ type InFlightPayment struct { } // FetchInFlightPayments returns all payments with status InFlight. -func (p *paymentControl) FetchInFlightPayments() ([]*InFlightPayment, error) { +func (p *PaymentControl) FetchInFlightPayments() ([]*InFlightPayment, error) { var inFlights []*InFlightPayment err := p.db.View(func(tx *bbolt.Tx) error { payments := tx.Bucket(paymentsRootBucket) diff --git a/channeldb/control_tower_test.go b/channeldb/payment_control_test.go similarity index 100% rename from channeldb/control_tower_test.go rename to channeldb/payment_control_test.go diff --git a/routing/control_tower.go b/routing/control_tower.go new file mode 100644 index 00000000000..c7a2216ae65 --- /dev/null +++ b/routing/control_tower.go @@ -0,0 +1,94 @@ +package routing + +import ( + "github.com/lightningnetwork/lnd/channeldb" + + "github.com/lightningnetwork/lnd/lntypes" +) + +// ControlTower tracks all outgoing payments made, whose primary purpose is to +// prevent duplicate payments to the same payment hash. In production, a +// persistent implementation is preferred so that tracking can survive across +// restarts. Payments are transitioned through various payment states, and the +// ControlTower interface provides access to driving the state transitions. +type ControlTower interface { + // InitPayment atomically moves the payment into the InFlight state. + // This method checks that no suceeded payment exist for this payment + // hash. + InitPayment(lntypes.Hash, *channeldb.PaymentCreationInfo) error + + // RegisterAttempt atomically records the provided PaymentAttemptInfo. + RegisterAttempt(lntypes.Hash, *channeldb.PaymentAttemptInfo) error + + // Success transitions a payment into the Succeeded state. After + // invoking this method, InitPayment should always return an error to + // prevent us from making duplicate payments to the same payment hash. + // The provided preimage is atomically saved to the DB for record + // keeping. + Success(lntypes.Hash, lntypes.Preimage) error + + // Fail transitions a payment into the Failed state, and records the + // reason the payment failed. After invoking this method, InitPayment + // should return nil on its next call for this payment hash, allowing + // the switch to make a subsequent payment. + Fail(lntypes.Hash, channeldb.FailureReason) error + + // FetchInFlightPayments returns all payments with status InFlight. + FetchInFlightPayments() ([]*channeldb.InFlightPayment, error) +} + +// controlTower is persistent implementation of ControlTower to restrict +// double payment sending. +type controlTower struct { + db *channeldb.PaymentControl +} + +// NewControlTower creates a new instance of the controlTower. +func NewControlTower(db *channeldb.PaymentControl) ControlTower { + return &controlTower{ + db: db, + } +} + +// InitPayment checks or records the given PaymentCreationInfo with the DB, +// making sure it does not already exist as an in-flight payment. Then this +// method returns successfully, the payment is guranteeed to be in the InFlight +// state. +func (p *controlTower) InitPayment(paymentHash lntypes.Hash, + info *channeldb.PaymentCreationInfo) error { + + return p.db.InitPayment(paymentHash, info) +} + +// RegisterAttempt atomically records the provided PaymentAttemptInfo to the +// DB. +func (p *controlTower) RegisterAttempt(paymentHash lntypes.Hash, + attempt *channeldb.PaymentAttemptInfo) error { + + return p.db.RegisterAttempt(paymentHash, attempt) +} + +// Success transitions a payment into the Succeeded state. After invoking this +// method, InitPayment should always return an error to prevent us from making +// duplicate payments to the same payment hash. The provided preimage is +// atomically saved to the DB for record keeping. +func (p *controlTower) Success(paymentHash lntypes.Hash, + preimage lntypes.Preimage) error { + + return p.db.Success(paymentHash, preimage) +} + +// Fail transitions a payment into the Failed state, and records the reason the +// payment failed. After invoking this method, InitPayment should return nil on +// its next call for this payment hash, allowing the switch to make a +// subsequent payment. +func (p *controlTower) Fail(paymentHash lntypes.Hash, + reason channeldb.FailureReason) error { + + return p.db.Fail(paymentHash, reason) +} + +// FetchInFlightPayments returns all payments with status InFlight. +func (p *controlTower) FetchInFlightPayments() ([]*channeldb.InFlightPayment, error) { + return p.db.FetchInFlightPayments() +} diff --git a/routing/mock_test.go b/routing/mock_test.go index c5314b8305a..e61b6677f96 100644 --- a/routing/mock_test.go +++ b/routing/mock_test.go @@ -182,7 +182,7 @@ type mockControlTower struct { sync.Mutex } -var _ channeldb.ControlTower = (*mockControlTower)(nil) +var _ ControlTower = (*mockControlTower)(nil) func makeMockControlTower() *mockControlTower { return &mockControlTower{ diff --git a/routing/router.go b/routing/router.go index 88c894b7c8b..72842003296 100644 --- a/routing/router.go +++ b/routing/router.go @@ -218,7 +218,7 @@ type Config struct { // Control keeps track of the status of ongoing payments, ensuring we // can properly resume them across restarts. - Control channeldb.ControlTower + Control ControlTower // MissionControl is a shared memory of sorts that executions of // payment path finding use in order to remember which vertexes/edges diff --git a/server.go b/server.go index 64293b3e029..6e85dc3da6b 100644 --- a/server.go +++ b/server.go @@ -649,12 +649,14 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, cc *chainControl, routerrpc.GetMissionControlConfig(cfg.SubRPCServers.RouterRPC), ) + paymentControl := channeldb.NewPaymentControl(chanDB) + s.chanRouter, err = routing.New(routing.Config{ Graph: chanGraph, Chain: cc.chainIO, ChainView: cc.chainView, Payer: s.htlcSwitch, - Control: channeldb.NewPaymentControl(chanDB), + Control: routing.NewControlTower(paymentControl), MissionControl: s.missionControl, ChannelPruneExpiry: routing.DefaultChannelPruneExpiry, GraphPruneInterval: time.Duration(time.Hour), From eb2647e8fc2eaa1dc2b0a03215ea4ee611c739ce Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Tue, 30 Apr 2019 13:24:37 +0200 Subject: [PATCH 5/9] channeldb: add subscription to control tower Allows other sub-systems to subscribe to payment success and fail events. --- channeldb/payment_control.go | 79 +++++++-- channeldb/payment_control_test.go | 14 +- routing/control_tower.go | 152 +++++++++++++++- routing/control_tower_test.go | 283 ++++++++++++++++++++++++++++++ routing/mock_test.go | 7 + 5 files changed, 512 insertions(+), 23 deletions(-) create mode 100644 routing/control_tower_test.go diff --git a/channeldb/payment_control.go b/channeldb/payment_control.go index 1cbc0b81f83..6bd094771ac 100644 --- a/channeldb/payment_control.go +++ b/channeldb/payment_control.go @@ -8,6 +8,7 @@ import ( "github.com/coreos/bbolt" "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/routing/route" ) var ( @@ -33,6 +34,10 @@ var ( // ErrUnknownPaymentStatus is returned when we do not recognize the // existing state of a payment. ErrUnknownPaymentStatus = errors.New("unknown payment status") + + // errNoAttemptInfo is returned when no attempt info is stored yet. + errNoAttemptInfo = errors.New("unable to find attempt info for " + + "inflight payment") ) // PaymentControl implements persistence for payments and payment attempts. @@ -187,9 +192,12 @@ func (p *PaymentControl) RegisterAttempt(paymentHash lntypes.Hash, // duplicate payments to the same payment hash. The provided preimage is // atomically saved to the DB for record keeping. func (p *PaymentControl) Success(paymentHash lntypes.Hash, - preimage lntypes.Preimage) error { + preimage lntypes.Preimage) (*route.Route, error) { - var updateErr error + var ( + updateErr error + route *route.Route + ) err := p.db.Batch(func(tx *bbolt.Tx) error { // Reset the update error, to avoid carrying over an error // from a previous execution of the batched db transaction. @@ -211,14 +219,26 @@ func (p *PaymentControl) Success(paymentHash lntypes.Hash, // Record the successful payment info atomically to the // payments record. - return bucket.Put(paymentSettleInfoKey, preimage[:]) + err = bucket.Put(paymentSettleInfoKey, preimage[:]) + if err != nil { + return err + } + + // Retrieve attempt info for the notification. + attempt, err := fetchPaymentAttempt(bucket) + if err != nil { + return err + } + + route = &attempt.Route + + return nil }) if err != nil { - return err + return nil, err } - return updateErr - + return route, updateErr } // Fail transitions a payment into the Failed state, and records the reason the @@ -259,6 +279,28 @@ func (p *PaymentControl) Fail(paymentHash lntypes.Hash, return updateErr } +// FetchPayment returns information about a payment from the database. +func (p *PaymentControl) FetchPayment(paymentHash lntypes.Hash) ( + *Payment, error) { + + var payment *Payment + err := p.db.View(func(tx *bbolt.Tx) error { + bucket, err := fetchPaymentBucket(tx, paymentHash) + if err != nil { + return err + } + + payment, err = fetchPayment(bucket) + + return err + }) + if err != nil { + return nil, err + } + + return payment, nil +} + // createPaymentBucket creates or fetches the sub-bucket assigned to this // payment hash. func createPaymentBucket(tx *bbolt.Tx, paymentHash lntypes.Hash) ( @@ -357,6 +399,17 @@ func ensureInFlight(bucket *bbolt.Bucket) error { } } +// fetchPaymentAttempt fetches the payment attempt from the bucket. +func fetchPaymentAttempt(bucket *bbolt.Bucket) (*PaymentAttemptInfo, error) { + attemptData := bucket.Get(paymentAttemptInfoKey) + if attemptData == nil { + return nil, errNoAttemptInfo + } + + r := bytes.NewReader(attemptData) + return deserializePaymentAttemptInfo(r) +} + // InFlightPayment is a wrapper around a payment that has status InFlight. type InFlightPayment struct { // Info is the PaymentCreationInfo of the in-flight payment. @@ -408,15 +461,11 @@ func (p *PaymentControl) FetchInFlightPayments() ([]*InFlightPayment, error) { return err } - // Now get the attempt info, which may or may not be - // available. - attempt := bucket.Get(paymentAttemptInfoKey) - if attempt != nil { - r = bytes.NewReader(attempt) - inFlight.Attempt, err = deserializePaymentAttemptInfo(r) - if err != nil { - return err - } + // Now get the attempt info. It could be that there is + // no attempt info yet. + inFlight.Attempt, err = fetchPaymentAttempt(bucket) + if err != nil && err != errNoAttemptInfo { + return err } inFlights = append(inFlights, inFlight) diff --git a/channeldb/payment_control_test.go b/channeldb/payment_control_test.go index 370300d73bd..ea69645f5a5 100644 --- a/channeldb/payment_control_test.go +++ b/channeldb/payment_control_test.go @@ -14,6 +14,7 @@ import ( "github.com/coreos/bbolt" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/routing/route" ) func initDB() (*DB, error) { @@ -131,9 +132,14 @@ func TestPaymentControlSwitchFail(t *testing.T) { ) // Verifies that status was changed to StatusSucceeded. - if err := pControl.Success(info.PaymentHash, preimg); err != nil { + var route *route.Route + route, err = pControl.Success(info.PaymentHash, preimg) + if err != nil { t.Fatalf("error shouldn't have been received, got: %v", err) } + if !reflect.DeepEqual(*route, attempt.Route) { + t.Fatalf("unexpected route returned") + } assertPaymentStatus(t, db, info.PaymentHash, StatusSucceeded) assertPaymentInfo(t, db, info.PaymentHash, info, attempt, preimg, nil) @@ -204,7 +210,7 @@ func TestPaymentControlSwitchDoubleSend(t *testing.T) { } // After settling, the error should be ErrAlreadyPaid. - if err := pControl.Success(info.PaymentHash, preimg); err != nil { + if _, err := pControl.Success(info.PaymentHash, preimg); err != nil { t.Fatalf("error shouldn't have been received, got: %v", err) } assertPaymentStatus(t, db, info.PaymentHash, StatusSucceeded) @@ -234,7 +240,7 @@ func TestPaymentControlSuccessesWithoutInFlight(t *testing.T) { } // Attempt to complete the payment should fail. - err = pControl.Success(info.PaymentHash, preimg) + _, err = pControl.Success(info.PaymentHash, preimg) if err != ErrPaymentNotInitiated { t.Fatalf("expected ErrPaymentNotInitiated, got %v", err) } @@ -337,7 +343,7 @@ func TestPaymentControlDeleteNonInFligt(t *testing.T) { ) } else if p.success { // Verifies that status was changed to StatusSucceeded. - err := pControl.Success(info.PaymentHash, preimg) + _, err := pControl.Success(info.PaymentHash, preimg) if err != nil { t.Fatalf("error shouldn't have been received, got: %v", err) } diff --git a/routing/control_tower.go b/routing/control_tower.go index c7a2216ae65..d8a1e0971c6 100644 --- a/routing/control_tower.go +++ b/routing/control_tower.go @@ -1,9 +1,12 @@ package routing import ( - "github.com/lightningnetwork/lnd/channeldb" + "errors" + "sync" + "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lntypes" + "github.com/lightningnetwork/lnd/routing/route" ) // ControlTower tracks all outgoing payments made, whose primary purpose is to @@ -35,18 +38,47 @@ type ControlTower interface { // FetchInFlightPayments returns all payments with status InFlight. FetchInFlightPayments() ([]*channeldb.InFlightPayment, error) + + // SubscribePayment subscribes to updates for the payment with the given + // hash. It returns a boolean indicating whether the payment is still in + // flight and a channel that provides the final outcome of the payment. + SubscribePayment(paymentHash lntypes.Hash) (bool, chan PaymentResult, + error) +} + +// PaymentResult is the struct describing the events received by payment +// subscribers. +type PaymentResult struct { + // Success indicates whether the payment was successful. + Success bool + + // Route is the (last) route attempted to send the HTLC. It is only set + // for successful payments. + Route *route.Route + + // PaymentPreimage is the preimage of a successful payment. This serves + // as a proof of payment. It is only set for successful payments. + Preimage lntypes.Preimage + + // Failure is a failure reason code indicating the reason the payment + // failed. It is only set for failed payments. + FailureReason channeldb.FailureReason } // controlTower is persistent implementation of ControlTower to restrict // double payment sending. type controlTower struct { db *channeldb.PaymentControl + + subscribers map[lntypes.Hash][]chan PaymentResult + subscribersMtx sync.Mutex } // NewControlTower creates a new instance of the controlTower. func NewControlTower(db *channeldb.PaymentControl) ControlTower { return &controlTower{ - db: db, + db: db, + subscribers: make(map[lntypes.Hash][]chan PaymentResult), } } @@ -75,7 +107,21 @@ func (p *controlTower) RegisterAttempt(paymentHash lntypes.Hash, func (p *controlTower) Success(paymentHash lntypes.Hash, preimage lntypes.Preimage) error { - return p.db.Success(paymentHash, preimage) + route, err := p.db.Success(paymentHash, preimage) + if err != nil { + return err + } + + // Notify subscribers of success event. + p.notifyFinalEvent( + paymentHash, PaymentResult{ + Success: true, + Preimage: preimage, + Route: route, + }, + ) + + return nil } // Fail transitions a payment into the Failed state, and records the reason the @@ -85,10 +131,108 @@ func (p *controlTower) Success(paymentHash lntypes.Hash, func (p *controlTower) Fail(paymentHash lntypes.Hash, reason channeldb.FailureReason) error { - return p.db.Fail(paymentHash, reason) + err := p.db.Fail(paymentHash, reason) + if err != nil { + return err + } + + // Notify subscribers of fail event. + p.notifyFinalEvent( + paymentHash, PaymentResult{ + Success: false, + FailureReason: reason, + }, + ) + + return nil } // FetchInFlightPayments returns all payments with status InFlight. func (p *controlTower) FetchInFlightPayments() ([]*channeldb.InFlightPayment, error) { return p.db.FetchInFlightPayments() } + +// SubscribePayment subscribes to updates for the payment with the given hash. +// It returns a boolean indicating whether the payment is still in flight and a +// channel that provides the final outcome of the payment. +func (p *controlTower) SubscribePayment(paymentHash lntypes.Hash) ( + bool, chan PaymentResult, error) { + + // Create a channel with buffer size 1. For every payment there will be + // exactly one event sent. + c := make(chan PaymentResult, 1) + + // Take lock before querying the db to prevent this scenario: + // FetchPayment returns us an in-flight state -> payment succeeds, but + // there is no subscriber to notify yet -> we add ourselves as a + // subscriber -> ... we will never receive a notification. + p.subscribersMtx.Lock() + defer p.subscribersMtx.Unlock() + + payment, err := p.db.FetchPayment(paymentHash) + if err != nil { + return false, nil, err + } + + var event PaymentResult + + switch payment.Status { + + // Payment is currently in flight. Register this subscriber and + // return without writing a result to the channel yet. + case channeldb.StatusInFlight: + p.subscribers[paymentHash] = append( + p.subscribers[paymentHash], c, + ) + + return true, c, nil + + // Payment already succeeded. It is not necessary to register as + // a subscriber, because we can send the result on the channel + // immediately. + case channeldb.StatusSucceeded: + event.Success = true + event.Preimage = *payment.PaymentPreimage + event.Route = &payment.Attempt.Route + + // Payment already failed. It is not necessary to register as a + // subscriber, because we can send the result on the channel + // immediately. + case channeldb.StatusFailed: + event.Success = false + event.FailureReason = *payment.Failure + + default: + return false, nil, errors.New("unknown payment status") + } + + // Write immediate result to the channel. + c <- event + close(c) + + return false, c, nil +} + +// notifyFinalEvent sends a final payment event to all subscribers of this +// payment. The channel will be closed after this. +func (p *controlTower) notifyFinalEvent(paymentHash lntypes.Hash, + event PaymentResult) { + + // Get all subscribers for this hash. As there is only a single outcome, + // the subscriber list can be cleared. + p.subscribersMtx.Lock() + list, ok := p.subscribers[paymentHash] + if !ok { + p.subscribersMtx.Unlock() + return + } + delete(p.subscribers, paymentHash) + p.subscribersMtx.Unlock() + + // Notify all subscribers of the event. The subscriber channel is + // buffered, so it cannot block here. + for _, subscriber := range list { + subscriber <- event + close(subscriber) + } +} diff --git a/routing/control_tower_test.go b/routing/control_tower_test.go new file mode 100644 index 00000000000..0a765fdbcb4 --- /dev/null +++ b/routing/control_tower_test.go @@ -0,0 +1,283 @@ +package routing + +import ( + "crypto/rand" + "crypto/sha256" + "fmt" + "io" + "io/ioutil" + "reflect" + "testing" + "time" + + "github.com/btcsuite/btcd/btcec" + "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/routing/route" + + "github.com/lightningnetwork/lnd/lntypes" +) + +var ( + priv, _ = btcec.NewPrivateKey(btcec.S256()) + pub = priv.PubKey() + + testHop = &route.Hop{ + PubKeyBytes: route.NewVertex(pub), + ChannelID: 12345, + OutgoingTimeLock: 111, + AmtToForward: 555, + } + + testRoute = route.Route{ + TotalTimeLock: 123, + TotalAmount: 1234567, + SourcePubKey: route.NewVertex(pub), + Hops: []*route.Hop{ + testHop, + testHop, + }, + } + + testTimeout = 5 * time.Second +) + +// TestControlTowerSubscribeUnknown tests that subscribing to an unknown +// payment fails. +func TestControlTowerSubscribeUnknown(t *testing.T) { + t.Parallel() + + db, err := initDB() + if err != nil { + t.Fatalf("unable to init db: %v", err) + } + + pControl := NewControlTower(channeldb.NewPaymentControl(db)) + + // Subscription should fail when the payment is not known. + _, _, err = pControl.SubscribePayment(lntypes.Hash{1}) + if err != channeldb.ErrPaymentNotInitiated { + t.Fatal("expected subscribe to fail for unknown payment") + } +} + +// TestControlTowerSubscribeSuccess tests that payment updates for a +// successful payment are properly sent to subscribers. +func TestControlTowerSubscribeSuccess(t *testing.T) { + t.Parallel() + + db, err := initDB() + if err != nil { + t.Fatalf("unable to init db: %v", err) + } + + pControl := NewControlTower(channeldb.NewPaymentControl(db)) + + // Initiate a payment. + info, attempt, preimg, err := genInfo() + if err != nil { + t.Fatal(err) + } + + err = pControl.InitPayment(info.PaymentHash, info) + if err != nil { + t.Fatal(err) + } + + // Subscription should succeed and immediately report the InFlight + // status. + inFlight, subscriber1, err := pControl.SubscribePayment(info.PaymentHash) + if err != nil { + t.Fatalf("expected subscribe to succeed, but got: %v", err) + } + if !inFlight { + t.Fatalf("unexpected payment to be in flight") + } + + // Register an attempt. + err = pControl.RegisterAttempt(info.PaymentHash, attempt) + if err != nil { + t.Fatal(err) + } + + // Register a second subscriber after the first attempt has started. + inFlight, subscriber2, err := pControl.SubscribePayment(info.PaymentHash) + if err != nil { + t.Fatalf("expected subscribe to succeed, but got: %v", err) + } + if !inFlight { + t.Fatalf("unexpected payment to be in flight") + } + + // Mark the payment as successful. + if err := pControl.Success(info.PaymentHash, preimg); err != nil { + t.Fatal(err) + } + + // Register a third subscriber after the payment succeeded. + inFlight, subscriber3, err := pControl.SubscribePayment(info.PaymentHash) + if err != nil { + t.Fatalf("expected subscribe to succeed, but got: %v", err) + } + if inFlight { + t.Fatalf("expected payment to be finished") + } + + // We expect all subscribers to now report the final outcome followed by + // no other events. + subscribers := []chan PaymentResult{ + subscriber1, subscriber2, subscriber3, + } + + for _, s := range subscribers { + var result PaymentResult + select { + case result = <-s: + case <-time.After(testTimeout): + t.Fatal("timeout waiting for payment result") + } + + if !result.Success { + t.Fatal("unexpected payment state") + } + if result.Preimage != preimg { + t.Fatal("unexpected preimage") + } + if !reflect.DeepEqual(result.Route, &attempt.Route) { + t.Fatal("unexpected route") + } + + // After the final event, we expect the channel to be closed. + select { + case _, ok := <-s: + if ok { + t.Fatal("expected channel to be closed") + } + case <-time.After(testTimeout): + t.Fatal("timeout waiting for result channel close") + } + } +} + +// TestPaymentControlSubscribeFail tests that payment updates for a +// failed payment are properly sent to subscribers. +func TestPaymentControlSubscribeFail(t *testing.T) { + t.Parallel() + + db, err := initDB() + if err != nil { + t.Fatalf("unable to init db: %v", err) + } + + pControl := NewControlTower(channeldb.NewPaymentControl(db)) + + // Initiate a payment. + info, _, _, err := genInfo() + if err != nil { + t.Fatal(err) + } + + err = pControl.InitPayment(info.PaymentHash, info) + if err != nil { + t.Fatal(err) + } + + // Subscription should succeed. + _, subscriber1, err := pControl.SubscribePayment(info.PaymentHash) + if err != nil { + t.Fatalf("expected subscribe to succeed, but got: %v", err) + } + + // Mark the payment as failed. + if err := pControl.Fail(info.PaymentHash, channeldb.FailureReasonTimeout); err != nil { + t.Fatal(err) + } + + // Register a second subscriber after the payment failed. + inFlight, subscriber2, err := pControl.SubscribePayment(info.PaymentHash) + if err != nil { + t.Fatalf("expected subscribe to succeed, but got: %v", err) + } + if inFlight { + t.Fatalf("expected payment to be finished") + } + + // We expect all subscribers to now report the final outcome followed by + // no other events. + subscribers := []chan PaymentResult{ + subscriber1, subscriber2, + } + + for _, s := range subscribers { + var result PaymentResult + select { + case result = <-s: + case <-time.After(testTimeout): + t.Fatal("timeout waiting for payment result") + } + + if result.Success { + t.Fatal("unexpected payment state") + } + if result.Route != nil { + t.Fatal("expected no route") + } + if result.FailureReason != channeldb.FailureReasonTimeout { + t.Fatal("unexpected failure reason") + } + + // After the final event, we expect the channel to be closed. + select { + case _, ok := <-s: + if ok { + t.Fatal("expected channel to be closed") + } + case <-time.After(testTimeout): + t.Fatal("timeout waiting for result channel close") + } + } +} + +func initDB() (*channeldb.DB, error) { + tempPath, err := ioutil.TempDir("", "routingdb") + if err != nil { + return nil, err + } + + db, err := channeldb.Open(tempPath) + if err != nil { + return nil, err + } + + return db, err +} + +func genInfo() (*channeldb.PaymentCreationInfo, *channeldb.PaymentAttemptInfo, + lntypes.Preimage, error) { + + preimage, err := genPreimage() + if err != nil { + return nil, nil, preimage, fmt.Errorf("unable to "+ + "generate preimage: %v", err) + } + + rhash := sha256.Sum256(preimage[:]) + return &channeldb.PaymentCreationInfo{ + PaymentHash: rhash, + Value: 1, + CreationDate: time.Unix(time.Now().Unix(), 0), + PaymentRequest: []byte("hola"), + }, + &channeldb.PaymentAttemptInfo{ + PaymentID: 1, + SessionKey: priv, + Route: testRoute, + }, preimage, nil +} + +func genPreimage() ([32]byte, error) { + var preimage [32]byte + if _, err := io.ReadFull(rand.Reader, preimage[:]); err != nil { + return preimage, err + } + return preimage, nil +} diff --git a/routing/mock_test.go b/routing/mock_test.go index e61b6677f96..600c55874be 100644 --- a/routing/mock_test.go +++ b/routing/mock_test.go @@ -4,6 +4,7 @@ import ( "fmt" "sync" + "github.com/go-errors/errors" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/lntypes" @@ -284,3 +285,9 @@ func (m *mockControlTower) FetchInFlightPayments() ( return fl, nil } + +func (m *mockControlTower) SubscribePayment(paymentHash lntypes.Hash) ( + bool, chan PaymentResult, error) { + + return false, nil, errors.New("not implemented") +} From 07d289c14e7ad9295c443ac75f4583b88d86202c Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Tue, 28 May 2019 16:36:08 +0200 Subject: [PATCH 6/9] routing: add SendPaymentAsync to router --- routing/router.go | 47 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/routing/router.go b/routing/router.go index 72842003296..fd0f689af89 100644 --- a/routing/router.go +++ b/routing/router.go @@ -1606,6 +1606,45 @@ type LightningPayment struct { func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *route.Route, error) { + paySession, err := r.preparePayment(payment) + if err != nil { + return [32]byte{}, nil, err + } + + // Since this is the first time this payment is being made, we pass nil + // for the existing attempt. + return r.sendPayment(nil, payment, paySession) +} + +// SendPaymentAsync is the non-blocking version of SendPayment. The payment +// result needs to be retrieved via the control tower. +func (r *ChannelRouter) SendPaymentAsync(payment *LightningPayment) error { + paySession, err := r.preparePayment(payment) + if err != nil { + return err + } + + // Since this is the first time this payment is being made, we pass nil + // for the existing attempt. + r.wg.Add(1) + go func() { + defer r.wg.Done() + + _, _, err := r.sendPayment(nil, payment, paySession) + if err != nil { + log.Errorf("Payment with hash %x failed: %v", + payment.PaymentHash, err) + } + }() + + return nil +} + +// preparePayment creates the payment session and registers the payment with the +// control tower. +func (r *ChannelRouter) preparePayment(payment *LightningPayment) ( + PaymentSession, error) { + // Before starting the HTLC routing attempt, we'll create a fresh // payment session which will report our errors back to mission // control. @@ -1613,7 +1652,7 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, payment.RouteHints, payment.Target, ) if err != nil { - return [32]byte{}, nil, err + return nil, err } // Record this payment hash with the ControlTower, ensuring it is not @@ -1627,12 +1666,10 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, err = r.cfg.Control.InitPayment(payment.PaymentHash, info) if err != nil { - return [32]byte{}, nil, err + return nil, err } - // Since this is the first time this payment is being made, we pass nil - // for the existing attempt. - return r.sendPayment(nil, payment, paySession) + return paySession, nil } // SendToRoute attempts to send a payment with the given hash through the From f03533c67a2b2dc62c81b7c7972316d5a98e32eb Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Fri, 22 Mar 2019 10:21:25 +0100 Subject: [PATCH 7/9] routerrpc: convert sendpayment to async Modify the routerrpc SendPayment api to asynchronous. This allows callers to pick up a payment after the rpc connection was lost or lnd was restarted. --- channeldb/payments.go | 12 + lnrpc/routerrpc/config_active.go | 1 - lnrpc/routerrpc/router.pb.go | 577 +++++++++++++++++++----------- lnrpc/routerrpc/router.proto | 60 +++- lnrpc/routerrpc/router_backend.go | 8 +- lnrpc/routerrpc/router_server.go | 129 ++++++- rpcserver.go | 1 + server.go | 6 +- 8 files changed, 563 insertions(+), 231 deletions(-) diff --git a/channeldb/payments.go b/channeldb/payments.go index 9eabe37dded..934a3946e5b 100644 --- a/channeldb/payments.go +++ b/channeldb/payments.go @@ -97,6 +97,18 @@ const ( // TODO(halseth): cancel state. ) +// String returns a human readable FailureReason +func (r FailureReason) String() string { + switch r { + case FailureReasonTimeout: + return "timeout" + case FailureReasonNoRoute: + return "no_route" + } + + return "unknown" +} + // PaymentStatus represent current status of payment type PaymentStatus byte diff --git a/lnrpc/routerrpc/config_active.go b/lnrpc/routerrpc/config_active.go index d04f5821c5f..36877c365e7 100644 --- a/lnrpc/routerrpc/config_active.go +++ b/lnrpc/routerrpc/config_active.go @@ -5,7 +5,6 @@ package routerrpc import ( "time" - "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcutil" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/macaroons" diff --git a/lnrpc/routerrpc/router.pb.go b/lnrpc/routerrpc/router.pb.go index 224bd4d01be..c38c5e9c352 100644 --- a/lnrpc/routerrpc/router.pb.go +++ b/lnrpc/routerrpc/router.pb.go @@ -23,6 +23,46 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package +type PaymentState int32 + +const ( + //* + //Payment is still in flight. + PaymentState_IN_FLIGHT PaymentState = 0 + //* + //Payment completed successfully. + PaymentState_SUCCEEDED PaymentState = 1 + //* + //There are more routes to try, but the payment timeout was exceeded. + PaymentState_FAILED_TIMEOUT PaymentState = 2 + //* + //All possible routes were tried and failed permanently. Or were no + //routes to the destination at all. + PaymentState_FAILED_NO_ROUTE PaymentState = 3 +) + +var PaymentState_name = map[int32]string{ + 0: "IN_FLIGHT", + 1: "SUCCEEDED", + 2: "FAILED_TIMEOUT", + 3: "FAILED_NO_ROUTE", +} + +var PaymentState_value = map[string]int32{ + "IN_FLIGHT": 0, + "SUCCEEDED": 1, + "FAILED_TIMEOUT": 2, + "FAILED_NO_ROUTE": 3, +} + +func (x PaymentState) String() string { + return proto.EnumName(PaymentState_name, int32(x)) +} + +func (PaymentState) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_7a0613f69d37b0a5, []int{0} +} + type Failure_FailureCode int32 const ( @@ -109,10 +149,10 @@ func (x Failure_FailureCode) String() string { } func (Failure_FailureCode) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_7a0613f69d37b0a5, []int{6, 0} + return fileDescriptor_7a0613f69d37b0a5, []int{7, 0} } -type PaymentRequest struct { +type SendPaymentRequest struct { /// The identity pubkey of the payment recipient Dest []byte `protobuf:"bytes,1,opt,name=dest,proto3" json:"dest,omitempty"` /// Number of satoshis to send. @@ -155,154 +195,192 @@ type PaymentRequest struct { XXX_sizecache int32 `json:"-"` } -func (m *PaymentRequest) Reset() { *m = PaymentRequest{} } -func (m *PaymentRequest) String() string { return proto.CompactTextString(m) } -func (*PaymentRequest) ProtoMessage() {} -func (*PaymentRequest) Descriptor() ([]byte, []int) { +func (m *SendPaymentRequest) Reset() { *m = SendPaymentRequest{} } +func (m *SendPaymentRequest) String() string { return proto.CompactTextString(m) } +func (*SendPaymentRequest) ProtoMessage() {} +func (*SendPaymentRequest) Descriptor() ([]byte, []int) { return fileDescriptor_7a0613f69d37b0a5, []int{0} } -func (m *PaymentRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PaymentRequest.Unmarshal(m, b) +func (m *SendPaymentRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SendPaymentRequest.Unmarshal(m, b) } -func (m *PaymentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PaymentRequest.Marshal(b, m, deterministic) +func (m *SendPaymentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SendPaymentRequest.Marshal(b, m, deterministic) } -func (m *PaymentRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_PaymentRequest.Merge(m, src) +func (m *SendPaymentRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SendPaymentRequest.Merge(m, src) } -func (m *PaymentRequest) XXX_Size() int { - return xxx_messageInfo_PaymentRequest.Size(m) +func (m *SendPaymentRequest) XXX_Size() int { + return xxx_messageInfo_SendPaymentRequest.Size(m) } -func (m *PaymentRequest) XXX_DiscardUnknown() { - xxx_messageInfo_PaymentRequest.DiscardUnknown(m) +func (m *SendPaymentRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SendPaymentRequest.DiscardUnknown(m) } -var xxx_messageInfo_PaymentRequest proto.InternalMessageInfo +var xxx_messageInfo_SendPaymentRequest proto.InternalMessageInfo -func (m *PaymentRequest) GetDest() []byte { +func (m *SendPaymentRequest) GetDest() []byte { if m != nil { return m.Dest } return nil } -func (m *PaymentRequest) GetAmt() int64 { +func (m *SendPaymentRequest) GetAmt() int64 { if m != nil { return m.Amt } return 0 } -func (m *PaymentRequest) GetPaymentHash() []byte { +func (m *SendPaymentRequest) GetPaymentHash() []byte { if m != nil { return m.PaymentHash } return nil } -func (m *PaymentRequest) GetFinalCltvDelta() int32 { +func (m *SendPaymentRequest) GetFinalCltvDelta() int32 { if m != nil { return m.FinalCltvDelta } return 0 } -func (m *PaymentRequest) GetPaymentRequest() string { +func (m *SendPaymentRequest) GetPaymentRequest() string { if m != nil { return m.PaymentRequest } return "" } -func (m *PaymentRequest) GetTimeoutSeconds() int32 { +func (m *SendPaymentRequest) GetTimeoutSeconds() int32 { if m != nil { return m.TimeoutSeconds } return 0 } -func (m *PaymentRequest) GetFeeLimitSat() int64 { +func (m *SendPaymentRequest) GetFeeLimitSat() int64 { if m != nil { return m.FeeLimitSat } return 0 } -func (m *PaymentRequest) GetOutgoingChanId() uint64 { +func (m *SendPaymentRequest) GetOutgoingChanId() uint64 { if m != nil { return m.OutgoingChanId } return 0 } -func (m *PaymentRequest) GetCltvLimit() int32 { +func (m *SendPaymentRequest) GetCltvLimit() int32 { if m != nil { return m.CltvLimit } return 0 } -type PaymentResponse struct { - //* - //The payment hash that we paid to. Provided so callers are able to map - //responses (which may be streaming) back to their original requests. - PayHash []byte `protobuf:"bytes,1,opt,name=pay_hash,json=payHash,proto3" json:"pay_hash,omitempty"` - //* - //The pre-image of the payment successfully completed. - PreImage []byte `protobuf:"bytes,2,opt,name=pre_image,json=preImage,proto3" json:"pre_image,omitempty"` - //* - //If not an empty string, then a string representation of the payment error. - PaymentErr string `protobuf:"bytes,3,opt,name=payment_err,json=paymentErr,proto3" json:"payment_err,omitempty"` +type TrackPaymentRequest struct { + /// The hash of the payment to look up. + PaymentHash []byte `protobuf:"bytes,1,opt,name=payment_hash,json=paymentHash,proto3" json:"payment_hash,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } -func (m *PaymentResponse) Reset() { *m = PaymentResponse{} } -func (m *PaymentResponse) String() string { return proto.CompactTextString(m) } -func (*PaymentResponse) ProtoMessage() {} -func (*PaymentResponse) Descriptor() ([]byte, []int) { +func (m *TrackPaymentRequest) Reset() { *m = TrackPaymentRequest{} } +func (m *TrackPaymentRequest) String() string { return proto.CompactTextString(m) } +func (*TrackPaymentRequest) ProtoMessage() {} +func (*TrackPaymentRequest) Descriptor() ([]byte, []int) { return fileDescriptor_7a0613f69d37b0a5, []int{1} } -func (m *PaymentResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PaymentResponse.Unmarshal(m, b) +func (m *TrackPaymentRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TrackPaymentRequest.Unmarshal(m, b) } -func (m *PaymentResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PaymentResponse.Marshal(b, m, deterministic) +func (m *TrackPaymentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TrackPaymentRequest.Marshal(b, m, deterministic) } -func (m *PaymentResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_PaymentResponse.Merge(m, src) +func (m *TrackPaymentRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_TrackPaymentRequest.Merge(m, src) } -func (m *PaymentResponse) XXX_Size() int { - return xxx_messageInfo_PaymentResponse.Size(m) +func (m *TrackPaymentRequest) XXX_Size() int { + return xxx_messageInfo_TrackPaymentRequest.Size(m) } -func (m *PaymentResponse) XXX_DiscardUnknown() { - xxx_messageInfo_PaymentResponse.DiscardUnknown(m) +func (m *TrackPaymentRequest) XXX_DiscardUnknown() { + xxx_messageInfo_TrackPaymentRequest.DiscardUnknown(m) } -var xxx_messageInfo_PaymentResponse proto.InternalMessageInfo +var xxx_messageInfo_TrackPaymentRequest proto.InternalMessageInfo -func (m *PaymentResponse) GetPayHash() []byte { +func (m *TrackPaymentRequest) GetPaymentHash() []byte { if m != nil { - return m.PayHash + return m.PaymentHash } return nil } -func (m *PaymentResponse) GetPreImage() []byte { +type PaymentStatus struct { + /// Current state the payment is in. + State PaymentState `protobuf:"varint,1,opt,name=state,proto3,enum=routerrpc.PaymentState" json:"state,omitempty"` + //* + //The pre-image of the payment when state is SUCCEEDED. + Preimage []byte `protobuf:"bytes,2,opt,name=preimage,proto3" json:"preimage,omitempty"` + //* + //The taken route when state is SUCCEEDED. + Route *lnrpc.Route `protobuf:"bytes,3,opt,name=route,proto3" json:"route,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PaymentStatus) Reset() { *m = PaymentStatus{} } +func (m *PaymentStatus) String() string { return proto.CompactTextString(m) } +func (*PaymentStatus) ProtoMessage() {} +func (*PaymentStatus) Descriptor() ([]byte, []int) { + return fileDescriptor_7a0613f69d37b0a5, []int{2} +} + +func (m *PaymentStatus) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PaymentStatus.Unmarshal(m, b) +} +func (m *PaymentStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PaymentStatus.Marshal(b, m, deterministic) +} +func (m *PaymentStatus) XXX_Merge(src proto.Message) { + xxx_messageInfo_PaymentStatus.Merge(m, src) +} +func (m *PaymentStatus) XXX_Size() int { + return xxx_messageInfo_PaymentStatus.Size(m) +} +func (m *PaymentStatus) XXX_DiscardUnknown() { + xxx_messageInfo_PaymentStatus.DiscardUnknown(m) +} + +var xxx_messageInfo_PaymentStatus proto.InternalMessageInfo + +func (m *PaymentStatus) GetState() PaymentState { if m != nil { - return m.PreImage + return m.State + } + return PaymentState_IN_FLIGHT +} + +func (m *PaymentStatus) GetPreimage() []byte { + if m != nil { + return m.Preimage } return nil } -func (m *PaymentResponse) GetPaymentErr() string { +func (m *PaymentStatus) GetRoute() *lnrpc.Route { if m != nil { - return m.PaymentErr + return m.Route } - return "" + return nil } type RouteFeeRequest struct { @@ -321,7 +399,7 @@ func (m *RouteFeeRequest) Reset() { *m = RouteFeeRequest{} } func (m *RouteFeeRequest) String() string { return proto.CompactTextString(m) } func (*RouteFeeRequest) ProtoMessage() {} func (*RouteFeeRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_7a0613f69d37b0a5, []int{2} + return fileDescriptor_7a0613f69d37b0a5, []int{3} } func (m *RouteFeeRequest) XXX_Unmarshal(b []byte) error { @@ -375,7 +453,7 @@ func (m *RouteFeeResponse) Reset() { *m = RouteFeeResponse{} } func (m *RouteFeeResponse) String() string { return proto.CompactTextString(m) } func (*RouteFeeResponse) ProtoMessage() {} func (*RouteFeeResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_7a0613f69d37b0a5, []int{3} + return fileDescriptor_7a0613f69d37b0a5, []int{4} } func (m *RouteFeeResponse) XXX_Unmarshal(b []byte) error { @@ -424,7 +502,7 @@ func (m *SendToRouteRequest) Reset() { *m = SendToRouteRequest{} } func (m *SendToRouteRequest) String() string { return proto.CompactTextString(m) } func (*SendToRouteRequest) ProtoMessage() {} func (*SendToRouteRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_7a0613f69d37b0a5, []int{4} + return fileDescriptor_7a0613f69d37b0a5, []int{5} } func (m *SendToRouteRequest) XXX_Unmarshal(b []byte) error { @@ -473,7 +551,7 @@ func (m *SendToRouteResponse) Reset() { *m = SendToRouteResponse{} } func (m *SendToRouteResponse) String() string { return proto.CompactTextString(m) } func (*SendToRouteResponse) ProtoMessage() {} func (*SendToRouteResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_7a0613f69d37b0a5, []int{5} + return fileDescriptor_7a0613f69d37b0a5, []int{6} } func (m *SendToRouteResponse) XXX_Unmarshal(b []byte) error { @@ -534,7 +612,7 @@ func (m *Failure) Reset() { *m = Failure{} } func (m *Failure) String() string { return proto.CompactTextString(m) } func (*Failure) ProtoMessage() {} func (*Failure) Descriptor() ([]byte, []int) { - return fileDescriptor_7a0613f69d37b0a5, []int{6} + return fileDescriptor_7a0613f69d37b0a5, []int{7} } func (m *Failure) XXX_Unmarshal(b []byte) error { @@ -671,7 +749,7 @@ func (m *ChannelUpdate) Reset() { *m = ChannelUpdate{} } func (m *ChannelUpdate) String() string { return proto.CompactTextString(m) } func (*ChannelUpdate) ProtoMessage() {} func (*ChannelUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_7a0613f69d37b0a5, []int{7} + return fileDescriptor_7a0613f69d37b0a5, []int{8} } func (m *ChannelUpdate) XXX_Unmarshal(b []byte) error { @@ -786,7 +864,7 @@ func (m *ResetMissionControlRequest) Reset() { *m = ResetMissionControlR func (m *ResetMissionControlRequest) String() string { return proto.CompactTextString(m) } func (*ResetMissionControlRequest) ProtoMessage() {} func (*ResetMissionControlRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_7a0613f69d37b0a5, []int{8} + return fileDescriptor_7a0613f69d37b0a5, []int{9} } func (m *ResetMissionControlRequest) XXX_Unmarshal(b []byte) error { @@ -817,7 +895,7 @@ func (m *ResetMissionControlResponse) Reset() { *m = ResetMissionControl func (m *ResetMissionControlResponse) String() string { return proto.CompactTextString(m) } func (*ResetMissionControlResponse) ProtoMessage() {} func (*ResetMissionControlResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_7a0613f69d37b0a5, []int{9} + return fileDescriptor_7a0613f69d37b0a5, []int{10} } func (m *ResetMissionControlResponse) XXX_Unmarshal(b []byte) error { @@ -848,7 +926,7 @@ func (m *QueryMissionControlRequest) Reset() { *m = QueryMissionControlR func (m *QueryMissionControlRequest) String() string { return proto.CompactTextString(m) } func (*QueryMissionControlRequest) ProtoMessage() {} func (*QueryMissionControlRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_7a0613f69d37b0a5, []int{10} + return fileDescriptor_7a0613f69d37b0a5, []int{11} } func (m *QueryMissionControlRequest) XXX_Unmarshal(b []byte) error { @@ -881,7 +959,7 @@ func (m *QueryMissionControlResponse) Reset() { *m = QueryMissionControl func (m *QueryMissionControlResponse) String() string { return proto.CompactTextString(m) } func (*QueryMissionControlResponse) ProtoMessage() {} func (*QueryMissionControlResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_7a0613f69d37b0a5, []int{11} + return fileDescriptor_7a0613f69d37b0a5, []int{12} } func (m *QueryMissionControlResponse) XXX_Unmarshal(b []byte) error { @@ -928,7 +1006,7 @@ func (m *NodeHistory) Reset() { *m = NodeHistory{} } func (m *NodeHistory) String() string { return proto.CompactTextString(m) } func (*NodeHistory) ProtoMessage() {} func (*NodeHistory) Descriptor() ([]byte, []int) { - return fileDescriptor_7a0613f69d37b0a5, []int{12} + return fileDescriptor_7a0613f69d37b0a5, []int{13} } func (m *NodeHistory) XXX_Unmarshal(b []byte) error { @@ -996,7 +1074,7 @@ func (m *ChannelHistory) Reset() { *m = ChannelHistory{} } func (m *ChannelHistory) String() string { return proto.CompactTextString(m) } func (*ChannelHistory) ProtoMessage() {} func (*ChannelHistory) Descriptor() ([]byte, []int) { - return fileDescriptor_7a0613f69d37b0a5, []int{13} + return fileDescriptor_7a0613f69d37b0a5, []int{14} } func (m *ChannelHistory) XXX_Unmarshal(b []byte) error { @@ -1046,9 +1124,11 @@ func (m *ChannelHistory) GetSuccessProb() float32 { } func init() { + proto.RegisterEnum("routerrpc.PaymentState", PaymentState_name, PaymentState_value) proto.RegisterEnum("routerrpc.Failure_FailureCode", Failure_FailureCode_name, Failure_FailureCode_value) - proto.RegisterType((*PaymentRequest)(nil), "routerrpc.PaymentRequest") - proto.RegisterType((*PaymentResponse)(nil), "routerrpc.PaymentResponse") + proto.RegisterType((*SendPaymentRequest)(nil), "routerrpc.SendPaymentRequest") + proto.RegisterType((*TrackPaymentRequest)(nil), "routerrpc.TrackPaymentRequest") + proto.RegisterType((*PaymentStatus)(nil), "routerrpc.PaymentStatus") proto.RegisterType((*RouteFeeRequest)(nil), "routerrpc.RouteFeeRequest") proto.RegisterType((*RouteFeeResponse)(nil), "routerrpc.RouteFeeResponse") proto.RegisterType((*SendToRouteRequest)(nil), "routerrpc.SendToRouteRequest") @@ -1066,101 +1146,106 @@ func init() { func init() { proto.RegisterFile("routerrpc/router.proto", fileDescriptor_7a0613f69d37b0a5) } var fileDescriptor_7a0613f69d37b0a5 = []byte{ - // 1503 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0xdd, 0x72, 0xda, 0xce, - 0x15, 0x2f, 0x06, 0x1b, 0x38, 0x7c, 0xc9, 0xeb, 0x8f, 0x60, 0x6c, 0xe7, 0xef, 0xa8, 0x6d, 0xc2, - 0x64, 0x32, 0xf6, 0x94, 0x4e, 0x32, 0xbd, 0x6a, 0x87, 0x80, 0xa8, 0x35, 0x06, 0xe1, 0x2c, 0xe0, - 0xc4, 0xed, 0xc5, 0xce, 0x1a, 0xad, 0x41, 0xb5, 0x90, 0x14, 0x69, 0x49, 0x43, 0x1f, 0xa0, 0xaf, - 0xd3, 0xde, 0xf4, 0xb6, 0x17, 0x7d, 0x8b, 0xbe, 0x4d, 0x67, 0x77, 0xc5, 0x97, 0x8d, 0xd3, 0xff, - 0x95, 0xd1, 0xef, 0xfc, 0xf6, 0x7c, 0x9f, 0xb3, 0x6b, 0x38, 0x0c, 0xfd, 0x29, 0x67, 0x61, 0x18, - 0x0c, 0x2f, 0xd4, 0xaf, 0xf3, 0x20, 0xf4, 0xb9, 0x8f, 0xb2, 0x0b, 0xbc, 0x92, 0x0d, 0x83, 0xa1, - 0x42, 0xf5, 0xff, 0x6c, 0x41, 0xf1, 0x9a, 0xce, 0x26, 0xcc, 0xe3, 0x98, 0x7d, 0x9d, 0xb2, 0x88, - 0x23, 0x04, 0x29, 0x9b, 0x45, 0xbc, 0x9c, 0x38, 0x4b, 0x54, 0xf3, 0x58, 0xfe, 0x46, 0x1a, 0x24, - 0xe9, 0x84, 0x97, 0xb7, 0xce, 0x12, 0xd5, 0x24, 0x16, 0x3f, 0xd1, 0x2b, 0xc8, 0x07, 0xea, 0x1c, - 0x19, 0xd3, 0x68, 0x5c, 0x4e, 0x4a, 0x76, 0x2e, 0xc6, 0x2e, 0x69, 0x34, 0x46, 0x55, 0xd0, 0xee, - 0x1d, 0x8f, 0xba, 0x64, 0xe8, 0xf2, 0x6f, 0xc4, 0x66, 0x2e, 0xa7, 0xe5, 0xd4, 0x59, 0xa2, 0xba, - 0x8d, 0x8b, 0x12, 0x6f, 0xb8, 0xfc, 0x5b, 0x53, 0xa0, 0xe8, 0x0d, 0x94, 0xe6, 0xca, 0x42, 0xe5, - 0x45, 0x79, 0xfb, 0x2c, 0x51, 0xcd, 0xe2, 0x62, 0xb0, 0xee, 0xdb, 0x1b, 0x28, 0x71, 0x67, 0xc2, - 0xfc, 0x29, 0x27, 0x11, 0x1b, 0xfa, 0x9e, 0x1d, 0x95, 0x77, 0x94, 0xc6, 0x18, 0xee, 0x29, 0x14, - 0xe9, 0x50, 0xb8, 0x67, 0x8c, 0xb8, 0xce, 0xc4, 0xe1, 0x24, 0xa2, 0xbc, 0x9c, 0x96, 0xae, 0xe7, - 0xee, 0x19, 0x6b, 0x0b, 0xac, 0x47, 0xb9, 0xf0, 0xcf, 0x9f, 0xf2, 0x91, 0xef, 0x78, 0x23, 0x32, - 0x1c, 0x53, 0x8f, 0x38, 0x76, 0x39, 0x73, 0x96, 0xa8, 0xa6, 0x70, 0x71, 0x8e, 0x37, 0xc6, 0xd4, - 0x33, 0x6d, 0x74, 0x0a, 0x20, 0x63, 0x90, 0xea, 0xca, 0x59, 0x69, 0x31, 0x2b, 0x10, 0xa9, 0x4b, - 0xff, 0x0b, 0x94, 0x16, 0x39, 0x8c, 0x02, 0xdf, 0x8b, 0x18, 0x3a, 0x82, 0x4c, 0x40, 0x67, 0x2a, - 0x35, 0x2a, 0x91, 0xe9, 0x80, 0xce, 0x64, 0x5a, 0x8e, 0x21, 0x1b, 0x84, 0x8c, 0x38, 0x13, 0x3a, - 0x62, 0x32, 0xa3, 0x79, 0x9c, 0x09, 0x42, 0x66, 0x8a, 0x6f, 0xf4, 0x13, 0xcc, 0x53, 0x48, 0x58, - 0x18, 0xca, 0xac, 0x66, 0x31, 0xc4, 0x90, 0x11, 0x86, 0xfa, 0xef, 0xa1, 0x84, 0x45, 0x21, 0x5b, - 0x8c, 0xfd, 0xa8, 0x60, 0x2f, 0x20, 0x4d, 0x27, 0x2a, 0x72, 0x55, 0xb4, 0x1d, 0x3a, 0x11, 0x41, - 0xeb, 0x36, 0x68, 0xcb, 0xf3, 0xb1, 0xb3, 0x55, 0xd0, 0x44, 0x73, 0x88, 0x3c, 0x88, 0xa4, 0x4d, - 0xc4, 0xa9, 0x84, 0x3c, 0x55, 0x8c, 0xf1, 0x16, 0x63, 0x9d, 0x88, 0x72, 0xf4, 0x5a, 0xe5, 0x9f, - 0xb8, 0xfe, 0xf0, 0x41, 0x54, 0x94, 0xce, 0x62, 0xf5, 0x05, 0x01, 0xb7, 0xfd, 0xe1, 0x43, 0x53, - 0x80, 0xfa, 0x9f, 0x01, 0xf5, 0x98, 0x67, 0xf7, 0x7d, 0x69, 0x6b, 0xee, 0xe8, 0xe3, 0x9e, 0x49, - 0x3c, 0xed, 0x19, 0x1d, 0xb6, 0x65, 0x9f, 0x4a, 0xb5, 0xb9, 0x5a, 0xfe, 0xdc, 0xf5, 0x44, 0xb3, - 0x2a, 0x35, 0x4a, 0xa4, 0x13, 0xd8, 0x5b, 0x53, 0x1e, 0x47, 0x51, 0x01, 0x91, 0x46, 0x95, 0xd6, - 0xc4, 0x22, 0xad, 0xf2, 0x1b, 0xbd, 0x83, 0xf4, 0x3d, 0x75, 0xdc, 0x69, 0x38, 0x57, 0x8c, 0xce, - 0x17, 0xe3, 0x70, 0xde, 0x52, 0x12, 0x3c, 0xa7, 0xe8, 0x7f, 0x4f, 0x43, 0x3a, 0x06, 0x51, 0x0d, - 0x52, 0x43, 0xdf, 0x56, 0x1a, 0x8b, 0xb5, 0x97, 0x4f, 0x8f, 0xcd, 0xff, 0x36, 0x7c, 0x9b, 0x61, - 0xc9, 0x45, 0x35, 0x38, 0x88, 0x55, 0x91, 0xc8, 0x9f, 0x86, 0x43, 0x46, 0x82, 0xe9, 0xdd, 0x03, - 0x9b, 0xc5, 0xd5, 0xde, 0x8b, 0x85, 0x3d, 0x29, 0xbb, 0x96, 0x22, 0xf4, 0x07, 0x28, 0x8a, 0x1e, - 0xf4, 0x98, 0x4b, 0xa6, 0x81, 0x4d, 0x39, 0x93, 0xb5, 0xcf, 0xd5, 0xca, 0x2b, 0x16, 0x1b, 0x8a, - 0x30, 0x90, 0x72, 0x5c, 0x18, 0xae, 0x7e, 0x8a, 0xb6, 0x1a, 0x73, 0x77, 0xa8, 0xaa, 0x97, 0x92, - 0x6d, 0x9c, 0x11, 0x80, 0xac, 0x9b, 0x0e, 0x05, 0xdf, 0x73, 0x7c, 0x8f, 0x44, 0x63, 0x4a, 0x6a, - 0xef, 0x3f, 0xc8, 0xf1, 0xca, 0xe3, 0x9c, 0x04, 0x7b, 0x63, 0x5a, 0x7b, 0xff, 0x41, 0xb4, 0x9e, - 0x6c, 0x72, 0xf6, 0x3d, 0x70, 0xc2, 0x99, 0x9c, 0xab, 0x02, 0x96, 0x7d, 0x6f, 0x48, 0x04, 0xed, - 0xc3, 0xf6, 0xbd, 0x4b, 0x47, 0x91, 0x9c, 0xa5, 0x02, 0x56, 0x1f, 0xfa, 0x7f, 0x53, 0x90, 0x5b, - 0x49, 0x01, 0xca, 0x43, 0x06, 0x1b, 0x3d, 0x03, 0xdf, 0x18, 0x4d, 0xed, 0x17, 0xa8, 0x0c, 0xfb, - 0x03, 0xeb, 0xca, 0xea, 0x7e, 0xb6, 0xc8, 0x75, 0xfd, 0xb6, 0x63, 0x58, 0x7d, 0x72, 0x59, 0xef, - 0x5d, 0x6a, 0x09, 0x74, 0x02, 0x65, 0xd3, 0x6a, 0x74, 0x31, 0x36, 0x1a, 0xfd, 0x85, 0xac, 0xde, - 0xe9, 0x0e, 0xac, 0xbe, 0xb6, 0x85, 0x7e, 0x82, 0xe3, 0x96, 0x69, 0xd5, 0xdb, 0x64, 0xc9, 0x69, - 0xb4, 0xfb, 0x37, 0xc4, 0xf8, 0x72, 0x6d, 0xe2, 0x5b, 0x2d, 0xb9, 0x89, 0x70, 0xd9, 0x6f, 0x37, - 0xe6, 0x1a, 0x52, 0xe8, 0x08, 0x0e, 0x14, 0x41, 0x1d, 0x21, 0xfd, 0x6e, 0x97, 0xf4, 0xba, 0x5d, - 0x4b, 0xdb, 0x46, 0xbb, 0x50, 0x30, 0xad, 0x9b, 0x7a, 0xdb, 0x6c, 0x12, 0x6c, 0xd4, 0xdb, 0x1d, - 0x6d, 0x07, 0xed, 0x41, 0xe9, 0x31, 0x2f, 0x2d, 0x54, 0xcc, 0x79, 0x5d, 0xcb, 0xec, 0x5a, 0xe4, - 0xc6, 0xc0, 0x3d, 0xb3, 0x6b, 0x69, 0x19, 0x74, 0x08, 0x68, 0x5d, 0x74, 0xd9, 0xa9, 0x37, 0xb4, - 0x2c, 0x3a, 0x80, 0xdd, 0x75, 0xfc, 0xca, 0xb8, 0xd5, 0x40, 0xa4, 0x41, 0x39, 0x46, 0x3e, 0x1a, - 0xed, 0xee, 0x67, 0xd2, 0x31, 0x2d, 0xb3, 0x33, 0xe8, 0x68, 0x39, 0xb4, 0x0f, 0x5a, 0xcb, 0x30, - 0x88, 0x69, 0xf5, 0x06, 0xad, 0x96, 0xd9, 0x30, 0x0d, 0xab, 0xaf, 0xe5, 0x95, 0xe5, 0x4d, 0x81, - 0x17, 0xc4, 0x81, 0xc6, 0x65, 0xdd, 0xb2, 0x8c, 0x36, 0x69, 0x9a, 0xbd, 0xfa, 0xc7, 0xb6, 0xd1, - 0xd4, 0x8a, 0xe8, 0x14, 0x8e, 0xfa, 0x46, 0xe7, 0xba, 0x8b, 0xeb, 0xf8, 0x96, 0xcc, 0xe5, 0xad, - 0xba, 0xd9, 0x1e, 0x60, 0x43, 0x2b, 0xa1, 0x57, 0x70, 0x8a, 0x8d, 0x4f, 0x03, 0x13, 0x1b, 0x4d, - 0x62, 0x75, 0x9b, 0x06, 0x69, 0x19, 0xf5, 0xfe, 0x00, 0x1b, 0xa4, 0x63, 0xf6, 0x7a, 0xa6, 0xf5, - 0x47, 0x4d, 0x43, 0xbf, 0x82, 0xb3, 0x05, 0x65, 0xa1, 0xe0, 0x11, 0x6b, 0x57, 0xc4, 0x37, 0xaf, - 0xa7, 0x65, 0x7c, 0xe9, 0x93, 0x6b, 0xc3, 0xc0, 0x1a, 0x42, 0x15, 0x38, 0x5c, 0x9a, 0x57, 0x06, - 0x62, 0xdb, 0x7b, 0x42, 0x76, 0x6d, 0xe0, 0x4e, 0xdd, 0x12, 0x05, 0x5e, 0x93, 0xed, 0x0b, 0xb7, - 0x97, 0xb2, 0xc7, 0x6e, 0x1f, 0xe8, 0xff, 0x48, 0x42, 0x61, 0xad, 0xe9, 0xd1, 0x09, 0x64, 0x23, - 0x67, 0xe4, 0x51, 0x2e, 0x46, 0x59, 0x4d, 0xf9, 0x12, 0x90, 0x7b, 0x7a, 0x4c, 0x1d, 0x4f, 0xad, - 0x17, 0x35, 0x6d, 0x59, 0x89, 0xc8, 0xe5, 0xf2, 0x02, 0xd2, 0xf3, 0x3d, 0x9f, 0x94, 0x03, 0xb2, - 0x33, 0x54, 0xfb, 0xfd, 0x04, 0xb2, 0x62, 0x7f, 0x45, 0x9c, 0x4e, 0x02, 0x39, 0x3b, 0x05, 0xbc, - 0x04, 0xd0, 0x2f, 0xa1, 0x30, 0x61, 0x51, 0x44, 0x47, 0x8c, 0xa8, 0xfe, 0x07, 0xc9, 0xc8, 0xc7, - 0x60, 0x4b, 0x60, 0x82, 0x34, 0x9f, 0x5f, 0x45, 0xda, 0x56, 0xa4, 0x18, 0x54, 0xa4, 0xc7, 0xeb, - 0x93, 0xd3, 0x78, 0xcc, 0x56, 0xd7, 0x27, 0xa7, 0xe8, 0x2d, 0xec, 0xaa, 0x59, 0x76, 0x3c, 0x67, - 0x32, 0x9d, 0xa8, 0x99, 0x4e, 0x4b, 0x97, 0x4b, 0x72, 0xa6, 0x15, 0x2e, 0x47, 0xfb, 0x08, 0x32, - 0x77, 0x34, 0x62, 0x62, 0x73, 0xcb, 0xdb, 0xab, 0x80, 0xd3, 0xe2, 0xbb, 0xc5, 0xe4, 0x25, 0x24, - 0xf6, 0x79, 0x28, 0xb6, 0x49, 0x56, 0x89, 0xee, 0x19, 0xc3, 0x22, 0x8f, 0x0b, 0x0b, 0xf4, 0xfb, - 0xd2, 0x42, 0x6e, 0xc5, 0x82, 0xc2, 0xa5, 0x85, 0xb7, 0xb0, 0xcb, 0xbe, 0xf3, 0x90, 0x12, 0x3f, - 0xa0, 0x5f, 0xa7, 0x8c, 0xd8, 0x94, 0xd3, 0x72, 0x5e, 0x26, 0xb7, 0x24, 0x05, 0x5d, 0x89, 0x37, - 0x29, 0xa7, 0xfa, 0x09, 0x54, 0x30, 0x8b, 0x18, 0xef, 0x38, 0x51, 0xe4, 0xf8, 0x5e, 0xc3, 0xf7, - 0x78, 0xe8, 0xbb, 0xf1, 0x05, 0xa0, 0x9f, 0xc2, 0xf1, 0x46, 0xa9, 0xda, 0xe0, 0xe2, 0xf0, 0xa7, - 0x29, 0x0b, 0x67, 0x9b, 0x0f, 0x5f, 0xc1, 0xf1, 0x46, 0x69, 0xbc, 0xfe, 0xdf, 0xc1, 0xb6, 0xe7, - 0xdb, 0x2c, 0x2a, 0x27, 0xce, 0x92, 0xd5, 0x5c, 0xed, 0x70, 0x65, 0x6f, 0x5a, 0xbe, 0xcd, 0x2e, - 0x9d, 0x88, 0xfb, 0xe1, 0x0c, 0x2b, 0x92, 0xfe, 0xef, 0x04, 0xe4, 0x56, 0x60, 0x74, 0x08, 0x3b, - 0xf1, 0x8e, 0x56, 0x4d, 0x15, 0x7f, 0xa1, 0xd7, 0x50, 0x74, 0x69, 0xc4, 0x89, 0x58, 0xd9, 0x44, - 0x14, 0x29, 0xbe, 0xef, 0x1e, 0xa1, 0xe8, 0x77, 0xf0, 0xc2, 0xe7, 0x63, 0x16, 0xaa, 0x87, 0x44, - 0x34, 0x1d, 0x0e, 0x59, 0x14, 0x91, 0x20, 0xf4, 0xef, 0x64, 0xab, 0x6d, 0xe1, 0xe7, 0xc4, 0xe8, - 0x3d, 0x64, 0xe2, 0x1e, 0x89, 0xca, 0x29, 0xe9, 0xfa, 0xd1, 0xd3, 0x95, 0x3f, 0xf7, 0x7e, 0x41, - 0xd5, 0xff, 0x99, 0x80, 0xe2, 0xba, 0x10, 0xbd, 0x94, 0xdd, 0x2f, 0x5b, 0xd0, 0xb1, 0x65, 0x1c, - 0x29, 0xbc, 0x82, 0xfc, 0xec, 0x58, 0x6a, 0xb0, 0x3f, 0x71, 0x3c, 0x12, 0x30, 0x8f, 0xba, 0xce, - 0xdf, 0x18, 0x99, 0x3f, 0x24, 0x92, 0x92, 0xbd, 0x51, 0x86, 0x74, 0xc8, 0xaf, 0x05, 0x9d, 0x92, - 0x41, 0xaf, 0x61, 0xb5, 0x7f, 0x25, 0x61, 0x47, 0x5e, 0xd9, 0x21, 0x6a, 0x42, 0x4e, 0x5c, 0xe1, - 0xf1, 0xab, 0x09, 0xad, 0x46, 0xbc, 0xfe, 0x1a, 0xad, 0x54, 0x36, 0x89, 0xe2, 0x92, 0x5f, 0x81, - 0x66, 0x44, 0xdc, 0x99, 0x88, 0xdb, 0x30, 0x7e, 0xd3, 0xa0, 0x55, 0xfe, 0xa3, 0x87, 0x52, 0xe5, - 0x78, 0xa3, 0x2c, 0x56, 0xd6, 0x56, 0x2e, 0xc5, 0xaf, 0x0a, 0x74, 0xba, 0xc2, 0x7d, 0xfa, 0x94, - 0xa9, 0xbc, 0x7c, 0x4e, 0x1c, 0x6b, 0xb3, 0x61, 0x6f, 0x43, 0xa7, 0xa3, 0x5f, 0xaf, 0x7a, 0xf0, - 0xec, 0x9c, 0x54, 0x5e, 0xff, 0x3f, 0xda, 0xd2, 0xca, 0x86, 0x91, 0x58, 0xb3, 0xf2, 0xfc, 0x40, - 0xad, 0x59, 0xf9, 0xc1, 0x64, 0x7d, 0xfc, 0xcd, 0x9f, 0x2e, 0x46, 0x0e, 0x1f, 0x4f, 0xef, 0xce, - 0x87, 0xfe, 0xe4, 0xc2, 0x75, 0x46, 0x63, 0xee, 0x39, 0xde, 0xc8, 0x63, 0xfc, 0xaf, 0x7e, 0xf8, - 0x70, 0xe1, 0x7a, 0xf6, 0x85, 0x7c, 0xa5, 0x5d, 0x2c, 0xd4, 0xdd, 0xed, 0xc8, 0xff, 0x2e, 0x7e, - 0xfb, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x15, 0x95, 0x7d, 0xcd, 0x8d, 0x0c, 0x00, 0x00, + // 1575 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0xdd, 0x72, 0x22, 0xc7, + 0x15, 0x36, 0x02, 0x09, 0x71, 0xf8, 0xd1, 0xa8, 0xa5, 0xd5, 0xb2, 0x68, 0xb5, 0x96, 0x27, 0xc9, + 0x5a, 0xb5, 0xe5, 0x48, 0x09, 0xa9, 0x75, 0xf9, 0x2a, 0x29, 0x16, 0x1a, 0x33, 0x59, 0x98, 0x91, + 0x1b, 0x58, 0x5b, 0xc9, 0x45, 0x57, 0x0b, 0x5a, 0x30, 0x25, 0x98, 0xc1, 0xd3, 0x8d, 0xb3, 0xca, + 0x45, 0xee, 0x92, 0xd7, 0x49, 0x9e, 0x20, 0x97, 0x79, 0x87, 0xbc, 0x4d, 0xaa, 0xbb, 0x07, 0x18, + 0x10, 0xda, 0xf8, 0x4a, 0xcc, 0x77, 0xbe, 0x3e, 0xe7, 0xf4, 0xf9, 0xeb, 0x23, 0x38, 0x89, 0xc2, + 0xb9, 0xe4, 0x51, 0x34, 0x1b, 0x5c, 0x99, 0x5f, 0x97, 0xb3, 0x28, 0x94, 0x21, 0xca, 0x2d, 0xf1, + 0x4a, 0x2e, 0x9a, 0x0d, 0x0c, 0x6a, 0xff, 0x67, 0x07, 0x50, 0x97, 0x07, 0xc3, 0x6b, 0xf6, 0x30, + 0xe5, 0x81, 0x24, 0xfc, 0xc7, 0x39, 0x17, 0x12, 0x21, 0xc8, 0x0c, 0xb9, 0x90, 0xe5, 0xd4, 0x79, + 0xea, 0xa2, 0x40, 0xf4, 0x6f, 0x64, 0x41, 0x9a, 0x4d, 0x65, 0x79, 0xe7, 0x3c, 0x75, 0x91, 0x26, + 0xea, 0x27, 0xfa, 0x02, 0x0a, 0x33, 0x73, 0x8e, 0x8e, 0x99, 0x18, 0x97, 0xd3, 0x9a, 0x9d, 0x8f, + 0xb1, 0x16, 0x13, 0x63, 0x74, 0x01, 0xd6, 0x9d, 0x1f, 0xb0, 0x09, 0x1d, 0x4c, 0xe4, 0x4f, 0x74, + 0xc8, 0x27, 0x92, 0x95, 0x33, 0xe7, 0xa9, 0x8b, 0x5d, 0x52, 0xd2, 0x78, 0x7d, 0x22, 0x7f, 0x6a, + 0x28, 0x14, 0x7d, 0x09, 0x07, 0x0b, 0x65, 0x91, 0xf1, 0xa2, 0xbc, 0x7b, 0x9e, 0xba, 0xc8, 0x91, + 0xd2, 0x6c, 0xdd, 0xb7, 0x2f, 0xe1, 0x40, 0xfa, 0x53, 0x1e, 0xce, 0x25, 0x15, 0x7c, 0x10, 0x06, + 0x43, 0x51, 0xde, 0x33, 0x1a, 0x63, 0xb8, 0x6b, 0x50, 0x64, 0x43, 0xf1, 0x8e, 0x73, 0x3a, 0xf1, + 0xa7, 0xbe, 0xa4, 0x82, 0xc9, 0x72, 0x56, 0xbb, 0x9e, 0xbf, 0xe3, 0xbc, 0xad, 0xb0, 0x2e, 0x93, + 0xca, 0xbf, 0x70, 0x2e, 0x47, 0xa1, 0x1f, 0x8c, 0xe8, 0x60, 0xcc, 0x02, 0xea, 0x0f, 0xcb, 0xfb, + 0xe7, 0xa9, 0x8b, 0x0c, 0x29, 0x2d, 0xf0, 0xfa, 0x98, 0x05, 0xce, 0x10, 0x9d, 0x01, 0xe8, 0x3b, + 0x68, 0x75, 0xe5, 0x9c, 0xb6, 0x98, 0x53, 0x88, 0xd6, 0x65, 0x7f, 0x03, 0x47, 0xbd, 0x88, 0x0d, + 0xee, 0x37, 0x02, 0xb9, 0x19, 0xa2, 0xd4, 0xa3, 0x10, 0xd9, 0x7f, 0x83, 0x62, 0x7c, 0xa8, 0x2b, + 0x99, 0x9c, 0x0b, 0xf4, 0x6b, 0xd8, 0x15, 0x92, 0x49, 0xae, 0xc9, 0xa5, 0xea, 0xf3, 0xcb, 0x65, + 0xe6, 0x2e, 0x13, 0x44, 0x4e, 0x0c, 0x0b, 0x55, 0x60, 0x7f, 0x16, 0x71, 0x7f, 0xca, 0x46, 0x5c, + 0x27, 0xa7, 0x40, 0x96, 0xdf, 0xc8, 0x86, 0x5d, 0x7d, 0x58, 0xa7, 0x26, 0x5f, 0x2d, 0x5c, 0x4e, + 0x02, 0xa5, 0x86, 0x28, 0x8c, 0x18, 0x91, 0xfd, 0x7b, 0x38, 0xd0, 0xdf, 0x4d, 0xce, 0x3f, 0x95, + 0xfe, 0xe7, 0x90, 0x65, 0x53, 0x13, 0x47, 0x53, 0x02, 0x7b, 0x6c, 0xaa, 0x42, 0x68, 0x0f, 0xc1, + 0x5a, 0x9d, 0x17, 0xb3, 0x30, 0x10, 0x5c, 0x85, 0x55, 0x29, 0x57, 0x51, 0x55, 0x29, 0x98, 0xaa, + 0x53, 0x29, 0x7d, 0xaa, 0x14, 0xe3, 0x4d, 0xce, 0x3b, 0x82, 0x49, 0xf4, 0xda, 0x64, 0x93, 0x4e, + 0xc2, 0xc1, 0xbd, 0xaa, 0x0f, 0xf6, 0x10, 0xab, 0x2f, 0x2a, 0xb8, 0x1d, 0x0e, 0xee, 0x1b, 0x0a, + 0xb4, 0xff, 0x6c, 0xea, 0xb4, 0x17, 0x1a, 0xdf, 0x7f, 0x76, 0x78, 0x57, 0x21, 0xd8, 0x79, 0x3a, + 0x04, 0x14, 0x8e, 0xd6, 0x94, 0xc7, 0xb7, 0x48, 0x46, 0x36, 0xb5, 0x11, 0xd9, 0xaf, 0x20, 0x7b, + 0xc7, 0xfc, 0xc9, 0x3c, 0x5a, 0x28, 0x46, 0x89, 0x34, 0x35, 0x8d, 0x84, 0x2c, 0x28, 0xf6, 0x3f, + 0xb2, 0x90, 0x8d, 0x41, 0x54, 0x85, 0xcc, 0x20, 0x1c, 0x2e, 0xb2, 0xfb, 0xea, 0xf1, 0xb1, 0xc5, + 0xdf, 0x7a, 0x38, 0xe4, 0x44, 0x73, 0x51, 0x15, 0x9e, 0xc5, 0xaa, 0xa8, 0x08, 0xe7, 0xd1, 0x80, + 0xd3, 0xd9, 0xfc, 0xf6, 0x9e, 0x3f, 0xc4, 0x09, 0x3f, 0x8a, 0x85, 0x5d, 0x2d, 0xbb, 0xd6, 0x22, + 0xf4, 0x07, 0x28, 0xa9, 0x8a, 0x0e, 0xf8, 0x84, 0xce, 0x67, 0x43, 0xb6, 0x2c, 0x82, 0x72, 0xc2, + 0x62, 0xdd, 0x10, 0xfa, 0x5a, 0x4e, 0x8a, 0x83, 0xe4, 0x27, 0x3a, 0x85, 0xdc, 0x58, 0x4e, 0x06, + 0x26, 0x7b, 0x19, 0xdd, 0x14, 0xfb, 0x0a, 0xd0, 0x79, 0xb3, 0xa1, 0x18, 0x06, 0x7e, 0x18, 0x50, + 0x31, 0x66, 0xb4, 0xfa, 0xf6, 0x6b, 0xdd, 0xac, 0x05, 0x92, 0xd7, 0x60, 0x77, 0xcc, 0xaa, 0x6f, + 0xbf, 0x46, 0x9f, 0x43, 0x5e, 0xb7, 0x0c, 0xff, 0x38, 0xf3, 0xa3, 0x07, 0xdd, 0xa5, 0x45, 0xa2, + 0xbb, 0x08, 0x6b, 0x04, 0x1d, 0xc3, 0xee, 0xdd, 0x84, 0x8d, 0x84, 0xee, 0xcc, 0x22, 0x31, 0x1f, + 0xf6, 0x7f, 0x33, 0x90, 0x4f, 0x84, 0x00, 0x15, 0x60, 0x9f, 0xe0, 0x2e, 0x26, 0x1f, 0x70, 0xc3, + 0xfa, 0x0c, 0x95, 0xe1, 0xb8, 0xef, 0xbe, 0x77, 0xbd, 0xef, 0x5d, 0x7a, 0x5d, 0xbb, 0xe9, 0x60, + 0xb7, 0x47, 0x5b, 0xb5, 0x6e, 0xcb, 0x4a, 0xa1, 0x97, 0x50, 0x76, 0xdc, 0xba, 0x47, 0x08, 0xae, + 0xf7, 0x96, 0xb2, 0x5a, 0xc7, 0xeb, 0xbb, 0x3d, 0x6b, 0x07, 0x7d, 0x0e, 0xa7, 0x4d, 0xc7, 0xad, + 0xb5, 0xe9, 0x8a, 0x53, 0x6f, 0xf7, 0x3e, 0x50, 0xfc, 0xc3, 0xb5, 0x43, 0x6e, 0xac, 0xf4, 0x36, + 0x42, 0xab, 0xd7, 0xae, 0x2f, 0x34, 0x64, 0xd0, 0x0b, 0x78, 0x66, 0x08, 0xe6, 0x08, 0xed, 0x79, + 0x1e, 0xed, 0x7a, 0x9e, 0x6b, 0xed, 0xa2, 0x43, 0x28, 0x3a, 0xee, 0x87, 0x5a, 0xdb, 0x69, 0x50, + 0x82, 0x6b, 0xed, 0x8e, 0xb5, 0x87, 0x8e, 0xe0, 0x60, 0x93, 0x97, 0x55, 0x2a, 0x16, 0x3c, 0xcf, + 0x75, 0x3c, 0x97, 0x7e, 0xc0, 0xa4, 0xeb, 0x78, 0xae, 0xb5, 0x8f, 0x4e, 0x00, 0xad, 0x8b, 0x5a, + 0x9d, 0x5a, 0xdd, 0xca, 0xa1, 0x67, 0x70, 0xb8, 0x8e, 0xbf, 0xc7, 0x37, 0x16, 0xa8, 0x30, 0x18, + 0xc7, 0xe8, 0x3b, 0xdc, 0xf6, 0xbe, 0xa7, 0x1d, 0xc7, 0x75, 0x3a, 0xfd, 0x8e, 0x95, 0x47, 0xc7, + 0x60, 0x35, 0x31, 0xa6, 0x8e, 0xdb, 0xed, 0x37, 0x9b, 0x4e, 0xdd, 0xc1, 0x6e, 0xcf, 0x2a, 0x18, + 0xcb, 0xdb, 0x2e, 0x5e, 0x54, 0x07, 0xea, 0xad, 0x9a, 0xeb, 0xe2, 0x36, 0x6d, 0x38, 0xdd, 0xda, + 0xbb, 0x36, 0x6e, 0x58, 0x25, 0x74, 0x06, 0x2f, 0x7a, 0xb8, 0x73, 0xed, 0x91, 0x1a, 0xb9, 0xa1, + 0x0b, 0x79, 0xb3, 0xe6, 0xb4, 0xfb, 0x04, 0x5b, 0x07, 0xe8, 0x0b, 0x38, 0x23, 0xf8, 0xbb, 0xbe, + 0x43, 0x70, 0x83, 0xba, 0x5e, 0x03, 0xd3, 0x26, 0xae, 0xf5, 0xfa, 0x04, 0xd3, 0x8e, 0xd3, 0xed, + 0x3a, 0xee, 0xb7, 0x96, 0x85, 0x7e, 0x09, 0xe7, 0x4b, 0xca, 0x52, 0xc1, 0x06, 0xeb, 0x50, 0xdd, + 0x6f, 0x91, 0x4f, 0x17, 0xff, 0xd0, 0xa3, 0xd7, 0x18, 0x13, 0x0b, 0xa1, 0x0a, 0x9c, 0xac, 0xcc, + 0x1b, 0x03, 0xb1, 0xed, 0x23, 0x25, 0xbb, 0xc6, 0xa4, 0x53, 0x73, 0x55, 0x82, 0xd7, 0x64, 0xc7, + 0xca, 0xed, 0x95, 0x6c, 0xd3, 0xed, 0x67, 0xf6, 0x3f, 0xd3, 0x50, 0x5c, 0x2b, 0x7a, 0xf4, 0x12, + 0x72, 0xc2, 0x1f, 0x05, 0x4c, 0xaa, 0x56, 0x36, 0x5d, 0xbe, 0x02, 0xf4, 0xd4, 0x1f, 0x33, 0x3f, + 0x30, 0xe3, 0xc5, 0x74, 0x5b, 0x4e, 0x23, 0x7a, 0xb8, 0x3c, 0x87, 0xec, 0xe2, 0xd5, 0x48, 0xeb, + 0x06, 0xd9, 0x1b, 0x98, 0xd7, 0xe2, 0x25, 0xe4, 0xd4, 0xfc, 0x12, 0x92, 0x4d, 0x67, 0xba, 0x77, + 0x8a, 0x64, 0x05, 0xa0, 0x5f, 0x40, 0x71, 0xca, 0x85, 0x60, 0x23, 0x4e, 0x4d, 0xfd, 0x83, 0x66, + 0x14, 0x62, 0xb0, 0xa9, 0x30, 0x45, 0x5a, 0xf4, 0xaf, 0x21, 0xed, 0x1a, 0x52, 0x0c, 0x1a, 0xd2, + 0xe6, 0xf8, 0x94, 0x2c, 0x6e, 0xb3, 0xe4, 0xf8, 0x94, 0x0c, 0xbd, 0x81, 0x43, 0xd3, 0xcb, 0x7e, + 0xe0, 0x4f, 0xe7, 0x53, 0xd3, 0xd3, 0x59, 0xed, 0xf2, 0x81, 0xee, 0x69, 0x83, 0xeb, 0xd6, 0x7e, + 0x01, 0xfb, 0xb7, 0x4c, 0x70, 0x35, 0xb9, 0xf5, 0x5b, 0x58, 0x24, 0x59, 0xf5, 0xdd, 0xe4, 0x5c, + 0x89, 0xd4, 0x3c, 0x8f, 0xd4, 0x34, 0xc9, 0x19, 0xd1, 0x1d, 0xe7, 0x44, 0xc5, 0x71, 0x69, 0x81, + 0x7d, 0x5c, 0x59, 0xc8, 0x27, 0x2c, 0x18, 0x5c, 0x5b, 0x78, 0x03, 0x87, 0xfc, 0xa3, 0x8c, 0x18, + 0x0d, 0x67, 0xec, 0xc7, 0x39, 0xa7, 0x43, 0x26, 0x59, 0xb9, 0xa0, 0x83, 0x7b, 0xa0, 0x05, 0x9e, + 0xc6, 0x1b, 0x4c, 0x32, 0xfb, 0x25, 0x54, 0x08, 0x17, 0x5c, 0x76, 0x7c, 0x21, 0xfc, 0x30, 0xa8, + 0x87, 0x81, 0x8c, 0xc2, 0x49, 0xfc, 0x00, 0xd8, 0x67, 0x70, 0xba, 0x55, 0x6a, 0x26, 0xb8, 0x3a, + 0xfc, 0xdd, 0x9c, 0x47, 0x0f, 0xdb, 0x0f, 0xbf, 0x87, 0xd3, 0xad, 0xd2, 0x78, 0xfc, 0x7f, 0x05, + 0xbb, 0x41, 0x38, 0xe4, 0xa2, 0x9c, 0x3a, 0x4f, 0x5f, 0xe4, 0xab, 0x27, 0x89, 0xb9, 0xe9, 0x86, + 0x43, 0xde, 0xf2, 0x85, 0x0c, 0xa3, 0x07, 0x62, 0x48, 0xf6, 0xbf, 0x53, 0x90, 0x4f, 0xc0, 0xe8, + 0x04, 0xf6, 0xe2, 0x19, 0x6d, 0x8a, 0x2a, 0xfe, 0x42, 0xaf, 0xa1, 0x34, 0x61, 0x42, 0x52, 0x35, + 0xb2, 0xa9, 0x4a, 0x52, 0xfc, 0xde, 0x6d, 0xa0, 0xe8, 0x1b, 0x78, 0x1e, 0xca, 0x31, 0x8f, 0xcc, + 0x5a, 0x22, 0xe6, 0x83, 0x01, 0x17, 0x82, 0xce, 0xa2, 0xf0, 0x56, 0x97, 0xda, 0x0e, 0x79, 0x4a, + 0x8c, 0xde, 0xc2, 0x7e, 0x5c, 0x23, 0xa2, 0x9c, 0xd1, 0xae, 0xbf, 0x78, 0x3c, 0xf2, 0x17, 0xde, + 0x2f, 0xa9, 0xf6, 0xbf, 0x52, 0x50, 0x5a, 0x17, 0xa2, 0x57, 0xba, 0xfa, 0x75, 0x09, 0xfa, 0x43, + 0x7d, 0x8f, 0x0c, 0x49, 0x20, 0x3f, 0xfb, 0x2e, 0x55, 0x38, 0x9e, 0xfa, 0x01, 0x9d, 0xf1, 0x80, + 0x4d, 0xfc, 0xbf, 0x72, 0xba, 0x58, 0x24, 0xd2, 0x9a, 0xbd, 0x55, 0x86, 0x6c, 0x28, 0xac, 0x5d, + 0x3a, 0xa3, 0x2f, 0xbd, 0x86, 0xbd, 0xe9, 0x43, 0x21, 0xb9, 0x11, 0xa1, 0x22, 0xe4, 0x1c, 0x97, + 0x36, 0xdb, 0xce, 0xb7, 0xad, 0x9e, 0xf5, 0x99, 0xfa, 0xec, 0xf6, 0xeb, 0x75, 0x8c, 0x1b, 0xb8, + 0x61, 0xa5, 0x10, 0x82, 0x92, 0x1a, 0x04, 0xb8, 0x41, 0x7b, 0x4e, 0x07, 0x7b, 0x7d, 0xf5, 0x2a, + 0x1c, 0xc1, 0x41, 0x8c, 0xb9, 0x1e, 0x25, 0x5e, 0xbf, 0x87, 0xad, 0x74, 0xf5, 0xef, 0x19, 0xd8, + 0xd3, 0x9b, 0x40, 0x84, 0x5a, 0x90, 0x4f, 0xac, 0xc7, 0xe8, 0x2c, 0x11, 0xc8, 0xc7, 0x6b, 0x73, + 0xa5, 0xbc, 0x7d, 0x55, 0x9b, 0x8b, 0xdf, 0xa4, 0xd0, 0x1f, 0xa1, 0x90, 0x5c, 0x10, 0x51, 0xf2, + 0xe1, 0xdf, 0xb2, 0x39, 0x7e, 0x52, 0xd7, 0x7b, 0xb0, 0xb0, 0x90, 0xfe, 0x54, 0x3d, 0xda, 0xf1, + 0xea, 0x85, 0x2a, 0x09, 0xfe, 0xc6, 0x3e, 0x57, 0x39, 0xdd, 0x2a, 0x8b, 0xcb, 0xbc, 0x6d, 0xae, + 0x18, 0x2f, 0x3f, 0x8f, 0xae, 0xb8, 0xbe, 0x71, 0x55, 0x5e, 0x3d, 0x25, 0x8e, 0xb5, 0x0d, 0xe1, + 0x68, 0x4b, 0x43, 0xa2, 0x5f, 0x25, 0x3d, 0x78, 0xb2, 0x9d, 0x2b, 0xaf, 0xff, 0x1f, 0x6d, 0x65, + 0x65, 0x4b, 0xe7, 0xae, 0x59, 0x79, 0xba, 0xef, 0xd7, 0xac, 0x7c, 0x62, 0x00, 0xbc, 0xfb, 0xed, + 0x9f, 0xae, 0x46, 0xbe, 0x1c, 0xcf, 0x6f, 0x2f, 0x07, 0xe1, 0xf4, 0x6a, 0xe2, 0x8f, 0xc6, 0x32, + 0xf0, 0x83, 0x51, 0xc0, 0xe5, 0x5f, 0xc2, 0xe8, 0xfe, 0x6a, 0x12, 0x0c, 0xaf, 0xf4, 0x32, 0x79, + 0xb5, 0x54, 0x77, 0xbb, 0xa7, 0xff, 0xad, 0xfa, 0xdd, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0x9a, + 0xf3, 0x15, 0x70, 0x86, 0x0d, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1177,11 +1262,13 @@ const _ = grpc.SupportPackageIsVersion4 type RouterClient interface { //* //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. - SendPayment(ctx context.Context, in *PaymentRequest, opts ...grpc.CallOption) (*PaymentResponse, error) + //PaymentRequest to the final destination. The call returns a stream of + //payment status updates. + SendPayment(ctx context.Context, in *SendPaymentRequest, opts ...grpc.CallOption) (Router_SendPaymentClient, error) + //* + //TrackPayment returns an update stream for the payment identified by the + //payment hash. + TrackPayment(ctx context.Context, in *TrackPaymentRequest, opts ...grpc.CallOption) (Router_TrackPaymentClient, error) //* //EstimateRouteFee allows callers to obtain a lower bound w.r.t how much it //may cost to send an HTLC to the target end destination. @@ -1209,13 +1296,68 @@ func NewRouterClient(cc *grpc.ClientConn) RouterClient { return &routerClient{cc} } -func (c *routerClient) SendPayment(ctx context.Context, in *PaymentRequest, opts ...grpc.CallOption) (*PaymentResponse, error) { - out := new(PaymentResponse) - err := c.cc.Invoke(ctx, "/routerrpc.Router/SendPayment", in, out, opts...) +func (c *routerClient) SendPayment(ctx context.Context, in *SendPaymentRequest, opts ...grpc.CallOption) (Router_SendPaymentClient, error) { + stream, err := c.cc.NewStream(ctx, &_Router_serviceDesc.Streams[0], "/routerrpc.Router/SendPayment", opts...) if err != nil { return nil, err } - return out, nil + x := &routerSendPaymentClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type Router_SendPaymentClient interface { + Recv() (*PaymentStatus, error) + grpc.ClientStream +} + +type routerSendPaymentClient struct { + grpc.ClientStream +} + +func (x *routerSendPaymentClient) Recv() (*PaymentStatus, error) { + m := new(PaymentStatus) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *routerClient) TrackPayment(ctx context.Context, in *TrackPaymentRequest, opts ...grpc.CallOption) (Router_TrackPaymentClient, error) { + stream, err := c.cc.NewStream(ctx, &_Router_serviceDesc.Streams[1], "/routerrpc.Router/TrackPayment", opts...) + if err != nil { + return nil, err + } + x := &routerTrackPaymentClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type Router_TrackPaymentClient interface { + Recv() (*PaymentStatus, error) + grpc.ClientStream +} + +type routerTrackPaymentClient struct { + grpc.ClientStream +} + +func (x *routerTrackPaymentClient) Recv() (*PaymentStatus, error) { + m := new(PaymentStatus) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil } func (c *routerClient) EstimateRouteFee(ctx context.Context, in *RouteFeeRequest, opts ...grpc.CallOption) (*RouteFeeResponse, error) { @@ -1258,11 +1400,13 @@ func (c *routerClient) QueryMissionControl(ctx context.Context, in *QueryMission type RouterServer interface { //* //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. - SendPayment(context.Context, *PaymentRequest) (*PaymentResponse, error) + //PaymentRequest to the final destination. The call returns a stream of + //payment status updates. + SendPayment(*SendPaymentRequest, Router_SendPaymentServer) error + //* + //TrackPayment returns an update stream for the payment identified by the + //payment hash. + TrackPayment(*TrackPaymentRequest, Router_TrackPaymentServer) error //* //EstimateRouteFee allows callers to obtain a lower bound w.r.t how much it //may cost to send an HTLC to the target end destination. @@ -1286,22 +1430,46 @@ func RegisterRouterServer(s *grpc.Server, srv RouterServer) { s.RegisterService(&_Router_serviceDesc, srv) } -func _Router_SendPayment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(PaymentRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RouterServer).SendPayment(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/routerrpc.Router/SendPayment", +func _Router_SendPayment_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(SendPaymentRequest) + if err := stream.RecvMsg(m); err != nil { + return err } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RouterServer).SendPayment(ctx, req.(*PaymentRequest)) + return srv.(RouterServer).SendPayment(m, &routerSendPaymentServer{stream}) +} + +type Router_SendPaymentServer interface { + Send(*PaymentStatus) error + grpc.ServerStream +} + +type routerSendPaymentServer struct { + grpc.ServerStream +} + +func (x *routerSendPaymentServer) Send(m *PaymentStatus) error { + return x.ServerStream.SendMsg(m) +} + +func _Router_TrackPayment_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(TrackPaymentRequest) + if err := stream.RecvMsg(m); err != nil { + return err } - return interceptor(ctx, in, info, handler) + return srv.(RouterServer).TrackPayment(m, &routerTrackPaymentServer{stream}) +} + +type Router_TrackPaymentServer interface { + Send(*PaymentStatus) error + grpc.ServerStream +} + +type routerTrackPaymentServer struct { + grpc.ServerStream +} + +func (x *routerTrackPaymentServer) Send(m *PaymentStatus) error { + return x.ServerStream.SendMsg(m) } func _Router_EstimateRouteFee_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { @@ -1380,10 +1548,6 @@ var _Router_serviceDesc = grpc.ServiceDesc{ ServiceName: "routerrpc.Router", HandlerType: (*RouterServer)(nil), Methods: []grpc.MethodDesc{ - { - MethodName: "SendPayment", - Handler: _Router_SendPayment_Handler, - }, { MethodName: "EstimateRouteFee", Handler: _Router_EstimateRouteFee_Handler, @@ -1401,6 +1565,17 @@ var _Router_serviceDesc = grpc.ServiceDesc{ Handler: _Router_QueryMissionControl_Handler, }, }, - Streams: []grpc.StreamDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "SendPayment", + Handler: _Router_SendPayment_Handler, + ServerStreams: true, + }, + { + StreamName: "TrackPayment", + Handler: _Router_TrackPayment_Handler, + ServerStreams: true, + }, + }, Metadata: "routerrpc/router.proto", } diff --git a/lnrpc/routerrpc/router.proto b/lnrpc/routerrpc/router.proto index f80f54c39fa..8646bd033a3 100644 --- a/lnrpc/routerrpc/router.proto +++ b/lnrpc/routerrpc/router.proto @@ -6,7 +6,7 @@ package routerrpc; option go_package = "github.com/lightningnetwork/lnd/lnrpc/routerrpc"; -message PaymentRequest { +message SendPaymentRequest { /// The identity pubkey of the payment recipient bytes dest = 1; @@ -60,24 +60,51 @@ message PaymentRequest { int32 cltv_limit = 9; } -message PaymentResponse { +message TrackPaymentRequest { + /// The hash of the payment to look up. + bytes payment_hash = 1; +} + +enum PaymentState { + /** + Payment is still in flight. + */ + IN_FLIGHT = 0; + + /** + Payment completed successfully. + */ + SUCCEEDED = 1; + + /** + There are more routes to try, but the payment timeout was exceeded. + */ + FAILED_TIMEOUT = 2; + /** - The payment hash that we paid to. Provided so callers are able to map - responses (which may be streaming) back to their original requests. + All possible routes were tried and failed permanently. Or were no + routes to the destination at all. */ - bytes pay_hash = 1; + FAILED_NO_ROUTE = 3; +} + + +message PaymentStatus { + /// Current state the payment is in. + PaymentState state = 1; /** - The pre-image of the payment successfully completed. + The pre-image of the payment when state is SUCCEEDED. */ - bytes pre_image = 2; + bytes preimage = 2; /** - If not an empty string, then a string representation of the payment error. + The taken route when state is SUCCEEDED. */ - string payment_err = 3; + lnrpc.Route route = 3; } + message RouteFeeRequest { /** The destination once wishes to obtain a routing fee quote to. @@ -305,12 +332,17 @@ message ChannelHistory { service Router { /** 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. + PaymentRequest to the final destination. The call returns a stream of + payment status updates. */ - rpc SendPayment(PaymentRequest) returns (PaymentResponse); + rpc SendPayment(SendPaymentRequest) returns (stream PaymentStatus); + + /** + TrackPayment returns an update stream for the payment identified by the + payment hash. + */ + rpc TrackPayment(TrackPaymentRequest) returns (stream PaymentStatus); + /** EstimateRouteFee allows callers to obtain a lower bound w.r.t how much it diff --git a/lnrpc/routerrpc/router_backend.go b/lnrpc/routerrpc/router_backend.go index f1eb61f4217..af1020063d3 100644 --- a/lnrpc/routerrpc/router_backend.go +++ b/lnrpc/routerrpc/router_backend.go @@ -47,6 +47,10 @@ type RouterBackend struct { // that we receive payment requests that send to destinations on our // network. ActiveNetParams *chaincfg.Params + + // Tower is the ControlTower instance that is used to track pending + // payments. + Tower routing.ControlTower } // QueryRoutes attempts to query the daemons' Channel Router for a possible @@ -349,8 +353,8 @@ func (r *RouterBackend) UnmarshallRoute(rpcroute *lnrpc.Route) ( // extractIntentFromSendRequest attempts to parse the SendRequest details // required to dispatch a client from the information presented by an RPC // client. -func (r *RouterBackend) extractIntentFromSendRequest(rpcPayReq *PaymentRequest) ( - *routing.LightningPayment, error) { +func (r *RouterBackend) extractIntentFromSendRequest( + rpcPayReq *SendPaymentRequest) (*routing.LightningPayment, error) { payIntent := &routing.LightningPayment{} diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go index 5047fc4e6e2..58e9d2eee3c 100644 --- a/lnrpc/routerrpc/router_server.go +++ b/lnrpc/routerrpc/router_server.go @@ -10,15 +10,18 @@ import ( "os" "path/filepath" - "github.com/lightningnetwork/lnd/htlcswitch" - "github.com/lightningnetwork/lnd/lntypes" - "github.com/btcsuite/btcutil" + "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing/route" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "gopkg.in/macaroon-bakery.v2/bakery" ) @@ -53,6 +56,10 @@ var ( Entity: "offchain", Action: "write", }}, + "/routerrpc.Router/TrackPayment": {{ + Entity: "offchain", + Action: "read", + }}, "/routerrpc.Router/EstimateRouteFee": {{ Entity: "offchain", Action: "read", @@ -185,23 +192,35 @@ func (s *Server) RegisterWithRootServer(grpcServer *grpc.Server) error { // 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(ctx context.Context, - req *PaymentRequest) (*PaymentResponse, error) { +func (s *Server) SendPayment(req *SendPaymentRequest, + stream Router_SendPaymentServer) error { payment, err := s.cfg.RouterBackend.extractIntentFromSendRequest(req) if err != nil { - return nil, err + return err } - preImage, _, err := s.cfg.Router.SendPayment(payment) + err = s.cfg.Router.SendPaymentAsync(payment) if err != nil { - return nil, err + // Transform user errors to grpc code. + if err == channeldb.ErrPaymentInFlight || + err == channeldb.ErrAlreadyPaid { + + log.Debugf("SendPayment async result for hash %x: %v", + payment.PaymentHash, err) + + return status.Error( + codes.AlreadyExists, err.Error(), + ) + } + + log.Errorf("SendPayment async error for hash %x: %v", + payment.PaymentHash, err) + + return err } - return &PaymentResponse{ - PayHash: payment.PaymentHash[:], - PreImage: preImage[:], - }, nil + return s.trackPayment(payment.PaymentHash, stream) } // EstimateRouteFee allows callers to obtain a lower bound w.r.t how much it @@ -460,3 +479,89 @@ func (s *Server) QueryMissionControl(ctx context.Context, return &response, nil } + +// 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 { + + paymentHash, err := lntypes.MakeHash(request.PaymentHash) + if err != nil { + return err + } + + log.Debugf("TrackPayment called for payment %v", paymentHash) + + return s.trackPayment(paymentHash, stream) +} + +// trackPayment writes payment status updates to the provided stream. +func (s *Server) trackPayment(paymentHash lntypes.Hash, + stream Router_TrackPaymentServer) error { + + // Subscribe to the outcome of this payment. + inFlight, resultChan, err := s.cfg.RouterBackend.Tower.SubscribePayment( + paymentHash, + ) + switch { + case err == channeldb.ErrPaymentNotInitiated: + return status.Error(codes.NotFound, err.Error()) + case err != nil: + return err + } + + // If it is in flight, send a state update to the client. Payment status + // update streams are expected to always send the current payment state + // immediately. + if inFlight { + err = stream.Send(&PaymentStatus{ + State: PaymentState_IN_FLIGHT, + }) + if err != nil { + return err + } + } + + // Wait for the outcome of the payment. For payments that have + // completed, the result should already be waiting on the channel. + select { + case result := <-resultChan: + // Marshall result to rpc type. + var status PaymentStatus + + if result.Success { + log.Debugf("Payment %v successfully completed", + paymentHash) + + status.State = PaymentState_SUCCEEDED + status.Preimage = result.Preimage[:] + status.Route = s.cfg.RouterBackend.MarshallRoute( + result.Route, + ) + } else { + switch result.FailureReason { + + case channeldb.FailureReasonTimeout: + status.State = PaymentState_FAILED_TIMEOUT + + case channeldb.FailureReasonNoRoute: + status.State = PaymentState_FAILED_NO_ROUTE + + default: + return errors.New("unknown failure reason") + } + } + + // Send event to the client. + err = stream.Send(&status) + if err != nil { + return err + } + + case <-stream.Context().Done(): + log.Debugf("Payment status stream %v canceled", paymentHash) + return stream.Context().Err() + } + + return nil +} diff --git a/rpcserver.go b/rpcserver.go index d5fb47d620e..b827b65b19c 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -477,6 +477,7 @@ func newRPCServer(s *server, macService *macaroons.Service, FindRoute: s.chanRouter.FindRoute, MissionControl: s.missionControl, ActiveNetParams: activeNetParams.Params, + Tower: s.controlTower, } var ( diff --git a/server.go b/server.go index 6e85dc3da6b..7887a8f2e67 100644 --- a/server.go +++ b/server.go @@ -192,6 +192,8 @@ type server struct { chanRouter *routing.ChannelRouter + controlTower routing.ControlTower + authGossiper *discovery.AuthenticatedGossiper utxoNursery *utxoNursery @@ -651,12 +653,14 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, cc *chainControl, paymentControl := channeldb.NewPaymentControl(chanDB) + s.controlTower = routing.NewControlTower(paymentControl) + s.chanRouter, err = routing.New(routing.Config{ Graph: chanGraph, Chain: cc.chainIO, ChainView: cc.chainView, Payer: s.htlcSwitch, - Control: routing.NewControlTower(paymentControl), + Control: s.controlTower, MissionControl: s.missionControl, ChannelPruneExpiry: routing.DefaultChannelPruneExpiry, GraphPruneInterval: time.Duration(time.Hour), From 2644759924e2ee28333d50416b8f0c37ddcf5ba2 Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Wed, 1 May 2019 14:31:24 +0200 Subject: [PATCH 8/9] channeldb: add todo for more detailed failure reasons --- channeldb/payments.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/channeldb/payments.go b/channeldb/payments.go index 934a3946e5b..7081e14b4da 100644 --- a/channeldb/payments.go +++ b/channeldb/payments.go @@ -95,6 +95,10 @@ const ( FailureReasonNoRoute FailureReason = 1 // TODO(halseth): cancel state. + + // TODO(joostjager): Add failure reasons for: + // UnknownPaymentHash, FinalInvalidAmt, FinalInvalidCltv + // LocalLiquidityInsufficient, RemoteCapacityInsufficient. ) // String returns a human readable FailureReason From 220c2becb175c839ee9b5e883f5f53d23fd18e27 Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Thu, 18 Apr 2019 15:27:54 +0200 Subject: [PATCH 9/9] lncli: unify payinvoice and sendpayment code This commit prepares lncli for moving over to routerrpc payment calls. --- cmd/lncli/commands.go | 198 +++++++++++++++++------------------------- 1 file changed, 81 insertions(+), 117 deletions(-) diff --git a/cmd/lncli/commands.go b/cmd/lncli/commands.go index 6a152950f61..d6f05befadd 100644 --- a/cmd/lncli/commands.go +++ b/cmd/lncli/commands.go @@ -2031,6 +2031,38 @@ var cltvLimitFlag = cli.UintFlag{ "this payment", } +// paymentFlags returns common flags for sendpayment and payinvoice. +func paymentFlags() []cli.Flag { + return []cli.Flag{ + cli.StringFlag{ + Name: "pay_req", + Usage: "a zpay32 encoded payment request to fulfill", + }, + cli.Int64Flag{ + Name: "fee_limit", + Usage: "maximum fee allowed in satoshis when " + + "sending the payment", + }, + cli.Int64Flag{ + Name: "fee_limit_percent", + Usage: "percentage of the payment's amount used as " + + "the maximum fee allowed when sending the " + + "payment", + }, + cltvLimitFlag, + cli.Uint64Flag{ + Name: "outgoing_chan_id", + Usage: "short channel id of the outgoing channel to " + + "use for the first hop of the payment", + Value: 0, + }, + cli.BoolFlag{ + Name: "force, f", + Usage: "will skip payment request confirmation", + }, + } +} + var sendPaymentCommand = cli.Command{ Name: "sendpayment", Category: "Payments", @@ -2057,7 +2089,7 @@ var sendPaymentCommand = cli.Command{ destination. `, ArgsUsage: "dest amt payment_hash final_cltv_delta | --pay_req=[payment request]", - Flags: []cli.Flag{ + Flags: append(paymentFlags(), cli.StringFlag{ Name: "dest, d", Usage: "the compressed identity pubkey of the " + @@ -2067,17 +2099,6 @@ var sendPaymentCommand = cli.Command{ Name: "amt, a", Usage: "number of satoshis to send", }, - cli.Int64Flag{ - Name: "fee_limit", - Usage: "maximum fee allowed in satoshis when sending" + - "the payment", - }, - cli.Int64Flag{ - Name: "fee_limit_percent", - Usage: "percentage of the payment's amount used as the" + - "maximum fee allowed when sending the payment", - }, - cltvLimitFlag, cli.StringFlag{ Name: "payment_hash, r", Usage: "the hash to use within the payment's HTLC", @@ -2086,25 +2107,11 @@ var sendPaymentCommand = cli.Command{ Name: "debug_send", Usage: "use the debug rHash when sending the HTLC", }, - cli.StringFlag{ - Name: "pay_req", - Usage: "a zpay32 encoded payment request to fulfill", - }, cli.Int64Flag{ Name: "final_cltv_delta", Usage: "the number of blocks the last hop has to reveal the preimage", }, - cli.Uint64Flag{ - Name: "outgoing_chan_id", - Usage: "short channel id of the outgoing channel to " + - "use for the first hop of the payment", - Value: 0, - }, - cli.BoolFlag{ - Name: "force, f", - Usage: "will skip payment request confirmation", - }, - }, + ), Action: sendPayment, } @@ -2134,26 +2141,7 @@ func retrieveFeeLimit(ctx *cli.Context) (*lnrpc.FeeLimit, error) { return nil, nil } -func confirmPayReq(ctx *cli.Context, client lnrpc.LightningClient, payReq string) error { - ctxb := context.Background() - - req := &lnrpc.PayReqString{PayReq: payReq} - resp, err := client.DecodePayReq(ctxb, req) - if err != nil { - return err - } - - // If the amount was not included in the invoice, then we let - // the payee specify the amount of satoshis they wish to send. - amt := resp.GetNumSatoshis() - if amt == 0 { - amt = ctx.Int64("amt") - if amt == 0 { - return fmt.Errorf("amount must be specified when " + - "paying a zero amount invoice") - } - } - +func confirmPayReq(resp *lnrpc.PayReq, amt int64) error { fmt.Printf("Description: %v\n", resp.GetDescription()) fmt.Printf("Amount (in satoshis): %v\n", amt) fmt.Printf("Destination: %v\n", resp.GetDestination()) @@ -2167,45 +2155,27 @@ func confirmPayReq(ctx *cli.Context, client lnrpc.LightningClient, payReq string } func sendPayment(ctx *cli.Context) error { - client, cleanUp := getClient(ctx) - defer cleanUp() // Show command help if no arguments provided if ctx.NArg() == 0 && ctx.NumFlags() == 0 { cli.ShowCommandHelp(ctx, "sendpayment") return nil } - // First, we'll retrieve the fee limit value passed since it can apply - // to both ways of sending payments (with the payment request or - // providing the details manually). - feeLimit, err := retrieveFeeLimit(ctx) - if err != nil { - return err - } - // If a payment request was provided, we can exit early since all of the // details of the payment are encoded within the request. if ctx.IsSet("pay_req") { - if !ctx.Bool("force") { - err = confirmPayReq(ctx, client, ctx.String("pay_req")) - if err != nil { - return err - } - } req := &lnrpc.SendRequest{ PaymentRequest: ctx.String("pay_req"), Amt: ctx.Int64("amt"), - FeeLimit: feeLimit, - OutgoingChanId: ctx.Uint64("outgoing_chan_id"), - CltvLimit: uint32(ctx.Int(cltvLimitFlag.Name)), } - return sendPaymentRequest(client, req) + return sendPaymentRequest(ctx, req) } var ( destNode []byte amount int64 + err error ) args := ctx.Args() @@ -2239,9 +2209,8 @@ func sendPayment(ctx *cli.Context) error { } req := &lnrpc.SendRequest{ - Dest: destNode, - Amt: amount, - FeeLimit: feeLimit, + Dest: destNode, + Amt: amount, } if ctx.Bool("debug_send") && (ctx.IsSet("payment_hash") || args.Present()) { @@ -2280,10 +2249,47 @@ func sendPayment(ctx *cli.Context) error { } } - return sendPaymentRequest(client, req) + return sendPaymentRequest(ctx, req) } -func sendPaymentRequest(client lnrpc.LightningClient, req *lnrpc.SendRequest) error { +func sendPaymentRequest(ctx *cli.Context, req *lnrpc.SendRequest) error { + client, cleanUp := getClient(ctx) + defer cleanUp() + + // First, we'll retrieve the fee limit value passed since it can apply + // to both ways of sending payments (with the payment request or + // providing the details manually). + feeLimit, err := retrieveFeeLimit(ctx) + if err != nil { + return err + } + req.FeeLimit = feeLimit + + req.OutgoingChanId = ctx.Uint64("outgoing_chan_id") + req.CltvLimit = uint32(ctx.Int(cltvLimitFlag.Name)) + + amt := req.Amt + + if req.PaymentRequest != "" { + req := &lnrpc.PayReqString{PayReq: req.PaymentRequest} + resp, err := client.DecodePayReq(context.Background(), req) + if err != nil { + return err + } + + invoiceAmt := resp.GetNumSatoshis() + if invoiceAmt != 0 { + amt = invoiceAmt + } + + if !ctx.Bool("force") { + err := confirmPayReq(resp, amt) + if err != nil { + return err + } + } + } + paymentStream, err := client.SendPayment(context.Background()) if err != nil { return err @@ -2325,45 +2331,18 @@ var payInvoiceCommand = cli.Command{ Category: "Payments", Usage: "Pay an invoice over lightning.", ArgsUsage: "pay_req", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "pay_req", - Usage: "a zpay32 encoded payment request to fulfill", - }, + Flags: append(paymentFlags(), cli.Int64Flag{ Name: "amt", Usage: "(optional) number of satoshis to fulfill the " + "invoice", }, - cli.Int64Flag{ - Name: "fee_limit", - Usage: "maximum fee allowed in satoshis when sending " + - "the payment", - }, - cli.Int64Flag{ - Name: "fee_limit_percent", - Usage: "percentage of the payment's amount used as the" + - "maximum fee allowed when sending the payment", - }, - cltvLimitFlag, - cli.Uint64Flag{ - Name: "outgoing_chan_id", - Usage: "short channel id of the outgoing channel to " + - "use for the first hop of the payment", - Value: 0, - }, - cli.BoolFlag{ - Name: "force, f", - Usage: "will skip payment request confirmation", - }, - }, + ), Action: actionDecorator(payInvoice), } func payInvoice(ctx *cli.Context) error { args := ctx.Args() - client, cleanUp := getClient(ctx) - defer cleanUp() var payReq string switch { @@ -2375,27 +2354,12 @@ func payInvoice(ctx *cli.Context) error { return fmt.Errorf("pay_req argument missing") } - feeLimit, err := retrieveFeeLimit(ctx) - if err != nil { - return err - } - - if !ctx.Bool("force") { - err = confirmPayReq(ctx, client, payReq) - if err != nil { - return err - } - } - req := &lnrpc.SendRequest{ PaymentRequest: payReq, Amt: ctx.Int64("amt"), - FeeLimit: feeLimit, - OutgoingChanId: ctx.Uint64("outgoing_chan_id"), - CltvLimit: uint32(ctx.Int(cltvLimitFlag.Name)), } - return sendPaymentRequest(client, req) + return sendPaymentRequest(ctx, req) } var sendToRouteCommand = cli.Command{