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