routing+lnd: provide payment premiere as response to SendPayment

This commit is contained in:
Olaoluwa Osuntokun 2017-02-20 23:57:43 -08:00
parent ef6ddcf788
commit 2dfab8c6d7
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2
5 changed files with 54 additions and 32 deletions

@ -26,6 +26,10 @@ const (
htlcQueueSize = 50 htlcQueueSize = 50
) )
var (
zeroBytes [32]byte
)
// link represents an active channel capable of forwarding HTLCs. Each // link represents an active channel capable of forwarding HTLCs. Each
// active channel registered with the htlc switch creates a new link which will // active channel registered with the htlc switch creates a new link which will
// be used for forwarding outgoing HTLCs. The link also has additional // be used for forwarding outgoing HTLCs. The link also has additional
@ -60,6 +64,8 @@ type htlcPacket struct {
payHash [32]byte payHash [32]byte
amt btcutil.Amount amt btcutil.Amount
preImage chan [32]byte
err chan error err chan error
} }
@ -125,9 +131,9 @@ type htlcSwitch struct {
interfaceMtx sync.RWMutex interfaceMtx sync.RWMutex
interfaces map[chainhash.Hash][]*link interfaces map[chainhash.Hash][]*link
// onionIndex is an index used to properly forward a message // onionIndex is an index used to properly forward a message to the
// to the next hop within a Sphinx circuit. Within the sphinx packets, // next hop within a Sphinx circuit. Within the sphinx packets, the
// the "next-hop" destination is encoded as the hash160 of the node's // "next-hop" destination is encoded as the hash160 of the node's
// public key serialized in compressed format. // public key serialized in compressed format.
onionMtx sync.RWMutex onionMtx sync.RWMutex
onionIndex map[[ripemd160.Size]byte][]*link onionIndex map[[ripemd160.Size]byte][]*link
@ -145,11 +151,11 @@ type htlcSwitch struct {
// the RPC system. // the RPC system.
outgoingPayments chan *htlcPacket outgoingPayments chan *htlcPacket
// htlcPlex is the channel which all connected links use to // htlcPlex is the channel which all connected links use to coordinate
// coordinate the setup/teardown of Sphinx (onion routing) payment // the setup/teardown of Sphinx (onion routing) payment circuits.
// circuits. Active links forward any add/settle messages over this // Active links forward any add/settle messages over this channel each
// channel each state transition, sending new adds/settles which are // state transition, sending new adds/settles which are fully locked
// fully locked in. // in.
htlcPlex chan *htlcPacket htlcPlex chan *htlcPacket
// TODO(roasbeef): sampler to log sat/sec and tx/sec // 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, // In the event that the interface has insufficient capacity for the payment,
// an error is returned. Additionally, if the interface cannot be found, an // an error is returned. Additionally, if the interface cannot be found, an
// alternative error is returned. // 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.err = make(chan error, 1)
htlcPkt.preImage = make(chan [32]byte, 1)
h.outgoingPayments <- htlcPkt h.outgoingPayments <- htlcPkt
return <-htlcPkt.err return <-htlcPkt.preImage, <-htlcPkt.err
} }
// htlcForwarder is responsible for optimally forwarding (and possibly // htlcForwarder is responsible for optimally forwarding (and possibly
@ -243,6 +250,7 @@ out:
err := fmt.Errorf("Unable to locate link %x", err := fmt.Errorf("Unable to locate link %x",
dest[:]) dest[:])
hswcLog.Errorf(err.Error()) hswcLog.Errorf(err.Error())
htlcPkt.preImage <- zeroBytes
htlcPkt.err <- err htlcPkt.err <- err
continue continue
} }
@ -277,6 +285,7 @@ out:
} }
hswcLog.Errorf("Unable to send payment, insufficient capacity") hswcLog.Errorf("Unable to send payment, insufficient capacity")
htlcPkt.preImage <- zeroBytes
htlcPkt.err <- fmt.Errorf("Insufficient capacity") htlcPkt.err <- fmt.Errorf("Insufficient capacity")
case pkt := <-h.htlcPlex: case pkt := <-h.htlcPlex:
// TODO(roasbeef): properly account with cleared vs settled // TODO(roasbeef): properly account with cleared vs settled
@ -374,6 +383,7 @@ out:
// network. // network.
circuit.clear.linkChan <- &htlcPacket{ circuit.clear.linkChan <- &htlcPacket{
msg: wireMsg, msg: wireMsg,
preImage: make(chan [32]byte, 1),
err: make(chan error, 1), err: make(chan error, 1),
} }

@ -1055,6 +1055,7 @@ type pendingPayment struct {
htlc *lnwire.UpdateAddHTLC htlc *lnwire.UpdateAddHTLC
index uint64 index uint64
preImage chan [32]byte
err chan error err chan error
} }
@ -1316,6 +1317,7 @@ func (p *peer) handleDownStreamPkt(state *commitmentState, pkt *htlcPacket) {
state.pendingBatch = append(state.pendingBatch, &pendingPayment{ state.pendingBatch = append(state.pendingBatch, &pendingPayment{
htlc: htlc, htlc: htlc,
index: index, index: index,
preImage: pkt.preImage,
err: pkt.err, err: pkt.err,
}) })
@ -1565,15 +1567,17 @@ func (p *peer) handleUpstreamMsg(state *commitmentState, msg lnwire.Message) {
if p, ok := state.clearedHTCLs[parentIndex]; ok { if p, ok := state.clearedHTCLs[parentIndex]; ok {
switch htlc.EntryType { switch htlc.EntryType {
// If the HTLC was settled successfully, then // If the HTLC was settled successfully, then
// we return a nil error back to the possible // we return a nil error as well as the payment
// caller. // preimage back to the possible caller.
case lnwallet.Settle: case lnwallet.Settle:
p.preImage <- htlc.RPreimage
p.err <- nil p.err <- nil
// Otherwise, the HTLC failed, so we propagate // Otherwise, the HTLC failed, so we propagate
// the error back to the potential caller. // the error back to the potential caller.
case lnwallet.Fail: case lnwallet.Fail:
errMsg := state.cancelReasons[parentIndex] errMsg := state.cancelReasons[parentIndex]
p.preImage <- [32]byte{}
p.err <- errors.New(errMsg.String()) p.err <- errors.New(errMsg.String())
} }

@ -82,7 +82,7 @@ type Config struct {
// denoted by its public key. A non-nil error is to be returned if the // denoted by its public key. A non-nil error is to be returned if the
// payment was unsuccessful. // payment was unsuccessful.
SendToSwitch func(firstHop *btcec.PublicKey, 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 // 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 // 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 // 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 // will be returned which describes the path the successful payment traversed
// within the network to reach the destination. // within the network to reach the destination. Additionally, the payment
func (r *ChannelRouter) SendPayment(payment *LightningPayment) (*Route, error) { // 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 // Query the graph for a potential path to the destination node that
// can support our payment amount. If a path is ultimately unavailable, // can support our payment amount. If a path is ultimately unavailable,
// then an error will be returned. // then an error will be returned.
route, err := r.FindRoute(payment.Target, payment.Amount) route, err := r.FindRoute(payment.Target, payment.Amount)
if err != nil { if err != nil {
return nil, err return preImage, nil, err
} }
log.Tracef("Selected route for payment: %#v", route) 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. // htlcAdd message that we send directly to the switch.
sphinxPacket, err := generateSphinxPacket(route, payment.PaymentHash[:]) sphinxPacket, err := generateSphinxPacket(route, payment.PaymentHash[:])
if err != nil { if err != nil {
return nil, err return preImage, nil, err
} }
// Craft an HTLC packet to send to the layer 2 switch. The metadata // 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 // Attempt to send this payment through the network to complete the
// payment. If this attempt fails, then we'll bail our early. // payment. If this attempt fails, then we'll bail our early.
firstHop := route.Hops[0].Channel.Node.PubKey firstHop := route.Hops[0].Channel.Node.PubKey
if err := r.cfg.SendToSwitch(firstHop, htlcAdd); err != nil { preImage, err = r.cfg.SendToSwitch(firstHop, htlcAdd)
return nil, err if err != nil {
return preImage, nil, err
} }
return route, nil return preImage, route, nil
} }
// TopologyClient... // TopologyClient...

@ -965,7 +965,7 @@ func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer)
Amount: amt, Amount: amt,
PaymentHash: rHash, PaymentHash: rHash,
} }
route, err := r.server.chanRouter.SendPayment(payment) preImage, route, err := r.server.chanRouter.SendPayment(payment)
if err != nil { if err != nil {
errChan <- err errChan <- err
return return
@ -978,8 +978,8 @@ func (r *rpcServer) SendPayment(paymentStream lnrpc.Lightning_SendPaymentServer)
return return
} }
// TODO(roasbeef): tack on payment hash also?
err = paymentStream.Send(&lnrpc.SendResponse{ err = paymentStream.Send(&lnrpc.SendResponse{
PaymentPreimage: preImage[:],
PaymentRoute: marshalRoute(route), PaymentRoute: marshalRoute(route),
}) })
if err != nil { if err != nil {
@ -1047,7 +1047,7 @@ func (r *rpcServer) SendPaymentSync(ctx context.Context,
// Finally, send a payment request to the channel router. If the // Finally, send a payment request to the channel router. If the
// payment succeeds, then the returned route will be that was used // payment succeeds, then the returned route will be that was used
// successfully within the payment. // successfully within the payment.
route, err := r.server.chanRouter.SendPayment(&routing.LightningPayment{ preImage, route, err := r.server.chanRouter.SendPayment(&routing.LightningPayment{
Target: destPub, Target: destPub,
Amount: amt, Amount: amt,
PaymentHash: rHash, PaymentHash: rHash,
@ -1063,6 +1063,7 @@ func (r *rpcServer) SendPaymentSync(ctx context.Context,
} }
return &lnrpc.SendResponse{ return &lnrpc.SendResponse{
PaymentPreimage: preImage[:],
PaymentRoute: marshalRoute(route), PaymentRoute: marshalRoute(route),
}, nil }, nil
} }

@ -179,7 +179,7 @@ func newServer(listenAddrs []string, notifier chainntnfs.ChainNotifier,
Broadcast: s.broadcastMessage, Broadcast: s.broadcastMessage,
SendMessages: s.sendToPeer, SendMessages: s.sendToPeer,
SendToSwitch: func(firstHop *btcec.PublicKey, SendToSwitch: func(firstHop *btcec.PublicKey,
htlcAdd *lnwire.UpdateAddHTLC) error { htlcAdd *lnwire.UpdateAddHTLC) ([32]byte, error) {
firstHopPub := firstHop.SerializeCompressed() firstHopPub := firstHop.SerializeCompressed()
destInterface := chainhash.Hash(fastsha256.Sum256(firstHopPub)) destInterface := chainhash.Hash(fastsha256.Sum256(firstHopPub))