From 2dfab8c6d758ccec2effc380bcdb9d7a173f93be Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 20 Feb 2017 23:57:43 -0800 Subject: [PATCH] routing+lnd: provide payment premiere as response to SendPayment --- htlcswitch.go | 34 ++++++++++++++++++++++------------ peer.go | 16 ++++++++++------ routing/router.go | 23 +++++++++++++++-------- rpcserver.go | 11 ++++++----- server.go | 2 +- 5 files changed, 54 insertions(+), 32 deletions(-) diff --git a/htlcswitch.go b/htlcswitch.go index 5af74485..5520a120 100644 --- a/htlcswitch.go +++ b/htlcswitch.go @@ -26,6 +26,10 @@ const ( htlcQueueSize = 50 ) +var ( + zeroBytes [32]byte +) + // link represents an active channel capable of forwarding HTLCs. Each // active channel registered with the htlc switch creates a new link which will // be used for forwarding outgoing HTLCs. The link also has additional @@ -60,6 +64,8 @@ type htlcPacket struct { payHash [32]byte amt btcutil.Amount + preImage chan [32]byte + err chan error } @@ -125,9 +131,9 @@ type htlcSwitch struct { interfaceMtx sync.RWMutex interfaces map[chainhash.Hash][]*link - // onionIndex is an index used to properly forward a message - // to the next hop within a Sphinx circuit. Within the sphinx packets, - // the "next-hop" destination is encoded as the hash160 of the node's + // onionIndex is an index used to properly forward a message to the + // next hop within a Sphinx circuit. Within the sphinx packets, the + // "next-hop" destination is encoded as the hash160 of the node's // public key serialized in compressed format. onionMtx sync.RWMutex onionIndex map[[ripemd160.Size]byte][]*link @@ -145,11 +151,11 @@ type htlcSwitch struct { // the RPC system. outgoingPayments chan *htlcPacket - // htlcPlex is the channel which all connected links use to - // coordinate the setup/teardown of Sphinx (onion routing) payment - // circuits. Active links forward any add/settle messages over this - // channel each state transition, sending new adds/settles which are - // fully locked in. + // htlcPlex is the channel which all connected links use to coordinate + // the setup/teardown of Sphinx (onion routing) payment circuits. + // Active links forward any add/settle messages over this channel each + // state transition, sending new adds/settles which are fully locked + // in. htlcPlex chan *htlcPacket // TODO(roasbeef): sampler to log sat/sec and tx/sec @@ -206,12 +212,13 @@ func (h *htlcSwitch) Stop() error { // In the event that the interface has insufficient capacity for the payment, // an error is returned. Additionally, if the interface cannot be found, an // alternative error is returned. -func (h *htlcSwitch) SendHTLC(htlcPkt *htlcPacket) error { +func (h *htlcSwitch) SendHTLC(htlcPkt *htlcPacket) ([32]byte, error) { htlcPkt.err = make(chan error, 1) + htlcPkt.preImage = make(chan [32]byte, 1) h.outgoingPayments <- htlcPkt - return <-htlcPkt.err + return <-htlcPkt.preImage, <-htlcPkt.err } // htlcForwarder is responsible for optimally forwarding (and possibly @@ -243,6 +250,7 @@ out: err := fmt.Errorf("Unable to locate link %x", dest[:]) hswcLog.Errorf(err.Error()) + htlcPkt.preImage <- zeroBytes htlcPkt.err <- err continue } @@ -277,6 +285,7 @@ out: } hswcLog.Errorf("Unable to send payment, insufficient capacity") + htlcPkt.preImage <- zeroBytes htlcPkt.err <- fmt.Errorf("Insufficient capacity") case pkt := <-h.htlcPlex: // TODO(roasbeef): properly account with cleared vs settled @@ -373,8 +382,9 @@ out: // continue propagating the HTLC across the // network. circuit.clear.linkChan <- &htlcPacket{ - msg: wireMsg, - err: make(chan error, 1), + msg: wireMsg, + preImage: make(chan [32]byte, 1), + err: make(chan error, 1), } // Reduce the available bandwidth for the link diff --git a/peer.go b/peer.go index 33d39181..516ad089 100644 --- a/peer.go +++ b/peer.go @@ -1055,7 +1055,8 @@ type pendingPayment struct { htlc *lnwire.UpdateAddHTLC index uint64 - err chan error + preImage chan [32]byte + err chan error } // commitmentState is the volatile+persistent state of an active channel's @@ -1314,9 +1315,10 @@ func (p *peer) handleDownStreamPkt(state *commitmentState, pkt *htlcPacket) { p.queueMsg(htlc, nil) state.pendingBatch = append(state.pendingBatch, &pendingPayment{ - htlc: htlc, - index: index, - err: pkt.err, + htlc: htlc, + index: index, + preImage: pkt.preImage, + err: pkt.err, }) case *lnwire.UpdateFufillHTLC: @@ -1565,15 +1567,17 @@ func (p *peer) handleUpstreamMsg(state *commitmentState, msg lnwire.Message) { if p, ok := state.clearedHTCLs[parentIndex]; ok { switch htlc.EntryType { // If the HTLC was settled successfully, then - // we return a nil error back to the possible - // caller. + // we return a nil error as well as the payment + // preimage back to the possible caller. case lnwallet.Settle: + p.preImage <- htlc.RPreimage p.err <- nil // Otherwise, the HTLC failed, so we propagate // the error back to the potential caller. case lnwallet.Fail: errMsg := state.cancelReasons[parentIndex] + p.preImage <- [32]byte{} p.err <- errors.New(errMsg.String()) } diff --git a/routing/router.go b/routing/router.go index ad9ecfbe..43257141 100644 --- a/routing/router.go +++ b/routing/router.go @@ -82,7 +82,7 @@ type Config struct { // denoted by its public key. A non-nil error is to be returned if the // payment was unsuccessful. SendToSwitch func(firstHop *btcec.PublicKey, - htlcAdd *lnwire.UpdateAddHTLC) error + htlcAdd *lnwire.UpdateAddHTLC) ([32]byte, error) } // ChannelRouter is the layer 3 router within the Lightning stack. Below the @@ -1105,14 +1105,20 @@ type LightningPayment struct { // payment is successful, or all candidates routes have been attempted and // resulted in a failed payment. If the payment succeeds, then a non-nil Route // will be returned which describes the path the successful payment traversed -// within the network to reach the destination. -func (r *ChannelRouter) SendPayment(payment *LightningPayment) (*Route, error) { +// within the network to reach the destination. Additionally, the payment +// preimage will also be returned. +func (r *ChannelRouter) SendPayment(payment *LightningPayment) ([32]byte, *Route, error) { + var ( + err error + preImage [32]byte + ) + // Query the graph for a potential path to the destination node that // can support our payment amount. If a path is ultimately unavailable, // then an error will be returned. route, err := r.FindRoute(payment.Target, payment.Amount) if err != nil { - return nil, err + return preImage, nil, err } log.Tracef("Selected route for payment: %#v", route) @@ -1120,7 +1126,7 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) (*Route, error) { // htlcAdd message that we send directly to the switch. sphinxPacket, err := generateSphinxPacket(route, payment.PaymentHash[:]) if err != nil { - return nil, err + return preImage, nil, err } // Craft an HTLC packet to send to the layer 2 switch. The metadata @@ -1135,11 +1141,12 @@ func (r *ChannelRouter) SendPayment(payment *LightningPayment) (*Route, error) { // Attempt to send this payment through the network to complete the // payment. If this attempt fails, then we'll bail our early. firstHop := route.Hops[0].Channel.Node.PubKey - if err := r.cfg.SendToSwitch(firstHop, htlcAdd); err != nil { - return nil, err + preImage, err = r.cfg.SendToSwitch(firstHop, htlcAdd) + if err != nil { + return preImage, nil, err } - return route, nil + return preImage, route, nil } // TopologyClient... diff --git a/rpcserver.go b/rpcserver.go index 54446edf..a37287f2 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -965,7 +965,7 @@ func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer) Amount: amt, PaymentHash: rHash, } - route, err := r.server.chanRouter.SendPayment(payment) + preImage, route, err := r.server.chanRouter.SendPayment(payment) if err != nil { errChan <- err return @@ -978,9 +978,9 @@ func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer) return } - // TODO(roasbeef): tack on payment hash also? err = paymentStream.Send(&lnrpc.SendResponse{ - PaymentRoute: marshalRoute(route), + PaymentPreimage: preImage[:], + PaymentRoute: marshalRoute(route), }) if err != nil { errChan <- err @@ -1047,7 +1047,7 @@ func (r *rpcServer) SendPaymentSync(ctx context.Context, // Finally, send a payment request to the channel router. If the // payment succeeds, then the returned route will be that was used // successfully within the payment. - route, err := r.server.chanRouter.SendPayment(&routing.LightningPayment{ + preImage, route, err := r.server.chanRouter.SendPayment(&routing.LightningPayment{ Target: destPub, Amount: amt, PaymentHash: rHash, @@ -1063,7 +1063,8 @@ func (r *rpcServer) SendPaymentSync(ctx context.Context, } return &lnrpc.SendResponse{ - PaymentRoute: marshalRoute(route), + PaymentPreimage: preImage[:], + PaymentRoute: marshalRoute(route), }, nil } diff --git a/server.go b/server.go index f8ccc821..a0a7a7d7 100644 --- a/server.go +++ b/server.go @@ -179,7 +179,7 @@ func newServer(listenAddrs []string, notifier chainntnfs.ChainNotifier, Broadcast: s.broadcastMessage, SendMessages: s.sendToPeer, SendToSwitch: func(firstHop *btcec.PublicKey, - htlcAdd *lnwire.UpdateAddHTLC) error { + htlcAdd *lnwire.UpdateAddHTLC) ([32]byte, error) { firstHopPub := firstHop.SerializeCompressed() destInterface := chainhash.Hash(fastsha256.Sum256(firstHopPub))