routing/payment_lifecycle+channeldb: collect existing outcome first

To move towards how we will handle existing attempt in case of MPP
(collecting their outcome will be done in separate goroutines separate
from the payment loop), we move to collect their outcome first.

To easily fetch HTLCs that are still not resolved, we add the utility
method InFlightHTLCs to channeldb.MPPayment.
This commit is contained in:
Johan T. Halseth 2020-04-01 00:13:25 +02:00
parent 49efbefb43
commit 7b5c10814b
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26
2 changed files with 52 additions and 18 deletions

@ -131,6 +131,21 @@ type MPPayment struct {
Status PaymentStatus Status PaymentStatus
} }
// InFlightHTLCs returns the HTLCs that are still in-flight, meaning they have
// not been settled or failed.
func (m *MPPayment) InFlightHTLCs() []HTLCAttempt {
var inflights []HTLCAttempt
for _, h := range m.HTLCs {
if h.Settle != nil || h.Failure != nil {
continue
}
inflights = append(inflights, h)
}
return inflights
}
// serializeHTLCSettleInfo serializes the details of a settled htlc. // serializeHTLCSettleInfo serializes the details of a settled htlc.
func serializeHTLCSettleInfo(w io.Writer, s *HTLCSettleInfo) error { func serializeHTLCSettleInfo(w io.Writer, s *HTLCSettleInfo) error {
if _, err := w.Write(s.Preimage[:]); err != nil { if _, err := w.Write(s.Preimage[:]); err != nil {

@ -32,6 +32,23 @@ func (p *paymentLifecycle) resumePayment() ([32]byte, *route.Route, error) {
paymentHash: p.paymentHash, paymentHash: p.paymentHash,
} }
// If we have an existing attempt, we'll start by collecting its result.
payment, err := p.router.cfg.Control.FetchPayment(
p.paymentHash,
)
if err != nil {
return [32]byte{}, nil, err
}
for _, a := range payment.InFlightHTLCs() {
a := a
_, err := shardHandler.collectResult(&a.HTLCAttemptInfo)
if err != nil {
return [32]byte{}, nil, err
}
}
// We'll continue until either our payment succeeds, or we encounter a // We'll continue until either our payment succeeds, or we encounter a
// critical error during path finding. // critical error during path finding.
for { for {
@ -160,31 +177,33 @@ func (p *paymentLifecycle) resumePayment() ([32]byte, *route.Route, error) {
// make a new attempt. // make a new attempt.
continue continue
} }
}
// Whether this was an existing attempt or one we just sent, // We'll collect the result of the shard just sent. We
// we'll now collect its result. We ignore the result for now // ignore the result for now if it is a success, as we
// if it is a success, as we will look it up in the control // will look it up in the control tower on the next
// tower on the next loop iteration. // loop iteration.
result, err := shardHandler.collectResult(attempt) result, err := shardHandler.collectResult(attempt)
if err != nil { if err != nil {
return [32]byte{}, nil, err return [32]byte{}, nil, err
} }
if result.err != nil { if result.err != nil {
// We must inspect the error to know whether it was // We must inspect the error to know whether it
// critical or not, to decide whether we should // was critical or not, to decide whether we
// continue trying. // should continue trying.
err = shardHandler.handleSendError(attempt, result.err) err := shardHandler.handleSendError(
attempt, result.err,
)
if err != nil { if err != nil {
return [32]byte{}, nil, err return [32]byte{}, nil, err
} }
// Error was handled successfully, continue to make a // Error was handled successfully, continue to
// new attempt. // make a new attempt.
continue continue
} }
} }
}
} }
// shardHandler holds what is necessary to send and collect the result of // shardHandler holds what is necessary to send and collect the result of