routing: return full htlc attempt from shard handler
This commit is contained in:
parent
8e7c0757ec
commit
cc37485432
@ -2,6 +2,7 @@ package channeldb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
@ -177,6 +178,18 @@ func (m *MPPayment) InFlightHTLCs() []HTLCAttempt {
|
||||
return inflights
|
||||
}
|
||||
|
||||
// GetAttempt returns the specified htlc attempt on the payment.
|
||||
func (m *MPPayment) GetAttempt(id uint64) (*HTLCAttempt, error) {
|
||||
for _, htlc := range m.HTLCs {
|
||||
htlc := htlc
|
||||
if htlc.AttemptID == id {
|
||||
return &htlc, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("htlc attempt not found on payment")
|
||||
}
|
||||
|
||||
// serializeHTLCSettleInfo serializes the details of a settled htlc.
|
||||
func serializeHTLCSettleInfo(w io.Writer, s *HTLCSettleInfo) error {
|
||||
if _, err := w.Write(s.Preimage[:]); err != nil {
|
||||
|
@ -30,10 +30,12 @@ type ControlTower interface {
|
||||
// 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.
|
||||
SettleAttempt(lntypes.Hash, uint64, *channeldb.HTLCSettleInfo) error
|
||||
SettleAttempt(lntypes.Hash, uint64, *channeldb.HTLCSettleInfo) (
|
||||
*channeldb.HTLCAttempt, error)
|
||||
|
||||
// FailAttempt marks the given payment attempt failed.
|
||||
FailAttempt(lntypes.Hash, uint64, *channeldb.HTLCFailInfo) error
|
||||
FailAttempt(lntypes.Hash, uint64, *channeldb.HTLCFailInfo) (
|
||||
*channeldb.HTLCAttempt, error)
|
||||
|
||||
// FetchPayment fetches the payment corresponding to the given payment
|
||||
// hash.
|
||||
@ -146,38 +148,40 @@ func (p *controlTower) RegisterAttempt(paymentHash lntypes.Hash,
|
||||
// this is a multi shard payment, this might implicitly mean the the
|
||||
// full payment succeeded.
|
||||
func (p *controlTower) SettleAttempt(paymentHash lntypes.Hash,
|
||||
attemptID uint64, settleInfo *channeldb.HTLCSettleInfo) error {
|
||||
attemptID uint64, settleInfo *channeldb.HTLCSettleInfo) (
|
||||
*channeldb.HTLCAttempt, error) {
|
||||
|
||||
p.paymentsMtx.Lock(paymentHash)
|
||||
defer p.paymentsMtx.Unlock(paymentHash)
|
||||
|
||||
payment, err := p.db.SettleAttempt(paymentHash, attemptID, settleInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Notify subscribers of success event.
|
||||
p.notifySubscribers(paymentHash, payment)
|
||||
|
||||
return nil
|
||||
return payment.GetAttempt(attemptID)
|
||||
}
|
||||
|
||||
// FailAttempt marks the given payment attempt failed.
|
||||
func (p *controlTower) FailAttempt(paymentHash lntypes.Hash,
|
||||
attemptID uint64, failInfo *channeldb.HTLCFailInfo) error {
|
||||
attemptID uint64, failInfo *channeldb.HTLCFailInfo) (
|
||||
*channeldb.HTLCAttempt, error) {
|
||||
|
||||
p.paymentsMtx.Lock(paymentHash)
|
||||
defer p.paymentsMtx.Unlock(paymentHash)
|
||||
|
||||
payment, err := p.db.FailAttempt(paymentHash, attemptID, failInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Notify subscribers of failed attempt.
|
||||
p.notifySubscribers(paymentHash, payment)
|
||||
|
||||
return nil
|
||||
return payment.GetAttempt(attemptID)
|
||||
}
|
||||
|
||||
// FetchPayment fetches the payment corresponding to the given payment hash.
|
||||
|
@ -104,15 +104,18 @@ func TestControlTowerSubscribeSuccess(t *testing.T) {
|
||||
}
|
||||
|
||||
// Mark the payment as successful.
|
||||
err = pControl.SettleAttempt(
|
||||
info.PaymentHash, attempt.AttemptID,
|
||||
&channeldb.HTLCSettleInfo{
|
||||
Preimage: preimg,
|
||||
},
|
||||
settleInfo := channeldb.HTLCSettleInfo{
|
||||
Preimage: preimg,
|
||||
}
|
||||
htlcAttempt, err := pControl.SettleAttempt(
|
||||
info.PaymentHash, attempt.AttemptID, &settleInfo,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if *htlcAttempt.Settle != settleInfo {
|
||||
t.Fatalf("unexpected settle info returned")
|
||||
}
|
||||
|
||||
// Register a third subscriber after the payment succeeded.
|
||||
subscriber3, err := pControl.SubscribePayment(info.PaymentHash)
|
||||
@ -215,13 +218,18 @@ func testPaymentControlSubscribeFail(t *testing.T, registerAttempt bool) {
|
||||
}
|
||||
|
||||
// Fail the payment attempt.
|
||||
err := pControl.FailAttempt(
|
||||
info.PaymentHash, attempt.AttemptID,
|
||||
&channeldb.HTLCFailInfo{},
|
||||
failInfo := channeldb.HTLCFailInfo{
|
||||
Reason: channeldb.HTLCFailInternal,
|
||||
}
|
||||
htlcAttempt, err := pControl.FailAttempt(
|
||||
info.PaymentHash, attempt.AttemptID, &failInfo,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to fail htlc: %v", err)
|
||||
}
|
||||
if *htlcAttempt.Failure != failInfo {
|
||||
t.Fatalf("unexpected fail info returned")
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the payment as failed.
|
||||
|
@ -301,7 +301,8 @@ func (m *mockControlTower) RegisterAttempt(phash lntypes.Hash,
|
||||
}
|
||||
|
||||
func (m *mockControlTower) SettleAttempt(phash lntypes.Hash,
|
||||
pid uint64, settleInfo *channeldb.HTLCSettleInfo) error {
|
||||
pid uint64, settleInfo *channeldb.HTLCSettleInfo) (
|
||||
*channeldb.HTLCAttempt, error) {
|
||||
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
@ -313,7 +314,7 @@ func (m *mockControlTower) SettleAttempt(phash lntypes.Hash,
|
||||
// Only allow setting attempts if the payment is known.
|
||||
p, ok := m.payments[phash]
|
||||
if !ok {
|
||||
return channeldb.ErrPaymentNotInitiated
|
||||
return nil, channeldb.ErrPaymentNotInitiated
|
||||
}
|
||||
|
||||
// Find the attempt with this pid, and set the settle info.
|
||||
@ -323,24 +324,26 @@ func (m *mockControlTower) SettleAttempt(phash lntypes.Hash,
|
||||
}
|
||||
|
||||
if a.Settle != nil {
|
||||
return channeldb.ErrAttemptAlreadySettled
|
||||
return nil, channeldb.ErrAttemptAlreadySettled
|
||||
}
|
||||
if a.Failure != nil {
|
||||
return channeldb.ErrAttemptAlreadyFailed
|
||||
return nil, channeldb.ErrAttemptAlreadyFailed
|
||||
}
|
||||
|
||||
p.attempts[i].Settle = settleInfo
|
||||
|
||||
// Mark the payment successful on first settled attempt.
|
||||
m.successful[phash] = struct{}{}
|
||||
return nil
|
||||
return &channeldb.HTLCAttempt{
|
||||
Settle: settleInfo,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("pid not found")
|
||||
return nil, fmt.Errorf("pid not found")
|
||||
}
|
||||
|
||||
func (m *mockControlTower) FailAttempt(phash lntypes.Hash, pid uint64,
|
||||
failInfo *channeldb.HTLCFailInfo) error {
|
||||
failInfo *channeldb.HTLCFailInfo) (*channeldb.HTLCAttempt, error) {
|
||||
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
@ -352,7 +355,7 @@ func (m *mockControlTower) FailAttempt(phash lntypes.Hash, pid uint64,
|
||||
// Only allow failing attempts if the payment is known.
|
||||
p, ok := m.payments[phash]
|
||||
if !ok {
|
||||
return channeldb.ErrPaymentNotInitiated
|
||||
return nil, channeldb.ErrPaymentNotInitiated
|
||||
}
|
||||
|
||||
// Find the attempt with this pid, and set the failure info.
|
||||
@ -362,17 +365,19 @@ func (m *mockControlTower) FailAttempt(phash lntypes.Hash, pid uint64,
|
||||
}
|
||||
|
||||
if a.Settle != nil {
|
||||
return channeldb.ErrAttemptAlreadySettled
|
||||
return nil, channeldb.ErrAttemptAlreadySettled
|
||||
}
|
||||
if a.Failure != nil {
|
||||
return channeldb.ErrAttemptAlreadyFailed
|
||||
return nil, channeldb.ErrAttemptAlreadyFailed
|
||||
}
|
||||
|
||||
p.attempts[i].Failure = failInfo
|
||||
return nil
|
||||
return &channeldb.HTLCAttempt{
|
||||
Failure: failInfo,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("pid not found")
|
||||
return nil, fmt.Errorf("pid not found")
|
||||
}
|
||||
|
||||
func (m *mockControlTower) Fail(phash lntypes.Hash,
|
||||
|
@ -345,6 +345,9 @@ type launchOutcome struct {
|
||||
// reflect this error. This can be errors like not enough local
|
||||
// balance for the given route etc.
|
||||
err error
|
||||
|
||||
// attempt is the attempt structure as recorded in the database.
|
||||
attempt *channeldb.HTLCAttempt
|
||||
}
|
||||
|
||||
// launchShard creates and sends an HTLC attempt along the given route,
|
||||
@ -382,14 +385,15 @@ func (p *shardHandler) launchShard(rt *route.Route) (*channeldb.HTLCAttemptInfo,
|
||||
if sendErr != nil {
|
||||
// TODO(joostjager): Distinguish unexpected internal errors
|
||||
// from real send errors.
|
||||
err := p.failAttempt(attempt, sendErr)
|
||||
htlcAttempt, err := p.failAttempt(attempt, sendErr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Return a launchOutcome indicating the shard failed.
|
||||
return attempt, &launchOutcome{
|
||||
err: sendErr,
|
||||
attempt: htlcAttempt,
|
||||
err: sendErr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -398,9 +402,8 @@ func (p *shardHandler) launchShard(rt *route.Route) (*channeldb.HTLCAttemptInfo,
|
||||
|
||||
// shardResult holds the resulting outcome of a shard sent.
|
||||
type shardResult struct {
|
||||
// preimage is the payment preimage in case of a settled HTLC. Only set
|
||||
// if err is non-nil.
|
||||
preimage lntypes.Preimage
|
||||
// attempt is the attempt structure as recorded in the database.
|
||||
attempt *channeldb.HTLCAttempt
|
||||
|
||||
// err indicates that the shard failed.
|
||||
err error
|
||||
@ -493,13 +496,14 @@ func (p *shardHandler) collectResult(attempt *channeldb.HTLCAttemptInfo) (
|
||||
"the Switch, retrying.", attempt.AttemptID,
|
||||
p.paymentHash)
|
||||
|
||||
cErr := p.failAttempt(attempt, err)
|
||||
attempt, cErr := p.failAttempt(attempt, err)
|
||||
if cErr != nil {
|
||||
return nil, cErr
|
||||
}
|
||||
|
||||
return &shardResult{
|
||||
err: err,
|
||||
attempt: attempt,
|
||||
err: err,
|
||||
}, nil
|
||||
|
||||
// A critical, unexpected error was encountered.
|
||||
@ -533,13 +537,14 @@ func (p *shardHandler) collectResult(attempt *channeldb.HTLCAttemptInfo) (
|
||||
// In case of a payment failure, fail the attempt with the control
|
||||
// tower and return.
|
||||
if result.Error != nil {
|
||||
err := p.failAttempt(attempt, result.Error)
|
||||
attempt, err := p.failAttempt(attempt, result.Error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &shardResult{
|
||||
err: result.Error,
|
||||
attempt: attempt,
|
||||
err: result.Error,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -558,7 +563,7 @@ func (p *shardHandler) collectResult(attempt *channeldb.HTLCAttemptInfo) (
|
||||
|
||||
// In case of success we atomically store settle result to the DB move
|
||||
// the shard to the settled state.
|
||||
err = p.router.cfg.Control.SettleAttempt(
|
||||
htlcAttempt, err := p.router.cfg.Control.SettleAttempt(
|
||||
p.paymentHash, attempt.AttemptID,
|
||||
&channeldb.HTLCSettleInfo{
|
||||
Preimage: result.Preimage,
|
||||
@ -571,7 +576,7 @@ func (p *shardHandler) collectResult(attempt *channeldb.HTLCAttemptInfo) (
|
||||
}
|
||||
|
||||
return &shardResult{
|
||||
preimage: result.Preimage,
|
||||
attempt: htlcAttempt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -691,7 +696,7 @@ func (p *shardHandler) handleSendError(attempt *channeldb.HTLCAttemptInfo,
|
||||
|
||||
// failAttempt calls control tower to fail the current payment attempt.
|
||||
func (p *shardHandler) failAttempt(attempt *channeldb.HTLCAttemptInfo,
|
||||
sendError error) error {
|
||||
sendError error) (*channeldb.HTLCAttempt, error) {
|
||||
|
||||
log.Warnf("Attempt %v for payment %v failed: %v", attempt.AttemptID,
|
||||
p.paymentHash, sendError)
|
||||
|
@ -1827,7 +1827,7 @@ func (r *ChannelRouter) SendToRoute(hash lntypes.Hash, rt *route.Route) (
|
||||
|
||||
// We got a successful result.
|
||||
if result.err == nil {
|
||||
return result.preimage, nil
|
||||
return result.attempt.Settle.Preimage, nil
|
||||
}
|
||||
|
||||
// The shard failed, break switch to handle it.
|
||||
|
Loading…
Reference in New Issue
Block a user