routing: add FetchPayment method to ControlTower
This method is used to fetch a payment and all HTLC attempt that have been made for that payment. It will both be used to resume inflight attempts, and to fetch the final outcome of previous attempts. We also update the the mock control tower to mimic the real control tower, by letting it track multiple HTLC attempts for a given payment hash, laying the groundwork for later enabling it for MPP.
This commit is contained in:
parent
6d9f9c31f4
commit
e1f4d89ad9
@ -34,6 +34,10 @@ type ControlTower interface {
|
|||||||
// 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) error
|
||||||
|
|
||||||
|
// FetchPayment fetches the payment corresponding to the given payment
|
||||||
|
// hash.
|
||||||
|
FetchPayment(paymentHash lntypes.Hash) (*channeldb.MPPayment, error)
|
||||||
|
|
||||||
// Fail transitions a payment into the Failed state, and records the
|
// Fail transitions a payment into the Failed state, and records the
|
||||||
// ultimate reason the payment failed. Note that this should only be
|
// ultimate reason the payment failed. Note that this should only be
|
||||||
// called when all active active attempts are already failed. After
|
// called when all active active attempts are already failed. After
|
||||||
@ -132,6 +136,13 @@ func (p *controlTower) FailAttempt(paymentHash lntypes.Hash,
|
|||||||
return p.db.FailAttempt(paymentHash, attemptID, failInfo)
|
return p.db.FailAttempt(paymentHash, attemptID, failInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FetchPayment fetches the payment corresponding to the given payment hash.
|
||||||
|
func (p *controlTower) FetchPayment(paymentHash lntypes.Hash) (
|
||||||
|
*channeldb.MPPayment, error) {
|
||||||
|
|
||||||
|
return p.db.FetchPayment(paymentHash)
|
||||||
|
}
|
||||||
|
|
||||||
// createSuccessResult creates a success result to send to subscribers.
|
// createSuccessResult creates a success result to send to subscribers.
|
||||||
func createSuccessResult(htlcs []channeldb.HTLCAttempt) *PaymentResult {
|
func createSuccessResult(htlcs []channeldb.HTLCAttempt) *PaymentResult {
|
||||||
// Extract any preimage from the list of HTLCs.
|
// Extract any preimage from the list of HTLCs.
|
||||||
|
@ -188,9 +188,15 @@ type failArgs struct {
|
|||||||
reason channeldb.FailureReason
|
reason channeldb.FailureReason
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type testPayment struct {
|
||||||
|
info channeldb.PaymentCreationInfo
|
||||||
|
attempts []channeldb.HTLCAttempt
|
||||||
|
}
|
||||||
|
|
||||||
type mockControlTower struct {
|
type mockControlTower struct {
|
||||||
inflights map[lntypes.Hash]channeldb.InFlightPayment
|
payments map[lntypes.Hash]*testPayment
|
||||||
successful map[lntypes.Hash]struct{}
|
successful map[lntypes.Hash]struct{}
|
||||||
|
failed map[lntypes.Hash]channeldb.FailureReason
|
||||||
|
|
||||||
init chan initArgs
|
init chan initArgs
|
||||||
register chan registerArgs
|
register chan registerArgs
|
||||||
@ -205,8 +211,9 @@ var _ ControlTower = (*mockControlTower)(nil)
|
|||||||
|
|
||||||
func makeMockControlTower() *mockControlTower {
|
func makeMockControlTower() *mockControlTower {
|
||||||
return &mockControlTower{
|
return &mockControlTower{
|
||||||
inflights: make(map[lntypes.Hash]channeldb.InFlightPayment),
|
payments: make(map[lntypes.Hash]*testPayment),
|
||||||
successful: make(map[lntypes.Hash]struct{}),
|
successful: make(map[lntypes.Hash]struct{}),
|
||||||
|
failed: make(map[lntypes.Hash]channeldb.FailureReason),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,18 +227,22 @@ func (m *mockControlTower) InitPayment(phash lntypes.Hash,
|
|||||||
m.init <- initArgs{c}
|
m.init <- initArgs{c}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't allow re-init a successful payment.
|
||||||
if _, ok := m.successful[phash]; ok {
|
if _, ok := m.successful[phash]; ok {
|
||||||
return fmt.Errorf("already successful")
|
return channeldb.ErrAlreadyPaid
|
||||||
}
|
}
|
||||||
|
|
||||||
_, ok := m.inflights[phash]
|
_, failed := m.failed[phash]
|
||||||
if ok {
|
_, ok := m.payments[phash]
|
||||||
return fmt.Errorf("in flight")
|
|
||||||
|
// If the payment is known, only allow re-init if failed.
|
||||||
|
if ok && !failed {
|
||||||
|
return channeldb.ErrPaymentInFlight
|
||||||
}
|
}
|
||||||
|
|
||||||
m.inflights[phash] = channeldb.InFlightPayment{
|
delete(m.failed, phash)
|
||||||
Info: c,
|
m.payments[phash] = &testPayment{
|
||||||
Attempts: make([]channeldb.HTLCAttemptInfo, 0),
|
info: *c,
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -247,13 +258,24 @@ func (m *mockControlTower) RegisterAttempt(phash lntypes.Hash,
|
|||||||
m.register <- registerArgs{a}
|
m.register <- registerArgs{a}
|
||||||
}
|
}
|
||||||
|
|
||||||
p, ok := m.inflights[phash]
|
// Cannot register attempts for successful or failed payments.
|
||||||
if !ok {
|
if _, ok := m.successful[phash]; ok {
|
||||||
return fmt.Errorf("not in flight")
|
return channeldb.ErrPaymentAlreadySucceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
p.Attempts = append(p.Attempts, *a)
|
if _, ok := m.failed[phash]; ok {
|
||||||
m.inflights[phash] = p
|
return channeldb.ErrPaymentAlreadyFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
p, ok := m.payments[phash]
|
||||||
|
if !ok {
|
||||||
|
return channeldb.ErrPaymentNotInitiated
|
||||||
|
}
|
||||||
|
|
||||||
|
p.attempts = append(p.attempts, channeldb.HTLCAttempt{
|
||||||
|
HTLCAttemptInfo: *a,
|
||||||
|
})
|
||||||
|
m.payments[phash] = p
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -268,9 +290,69 @@ func (m *mockControlTower) SettleAttempt(phash lntypes.Hash,
|
|||||||
m.success <- successArgs{settleInfo.Preimage}
|
m.success <- successArgs{settleInfo.Preimage}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(m.inflights, phash)
|
// Only allow setting attempts for payments not yet succeeded or
|
||||||
m.successful[phash] = struct{}{}
|
// failed.
|
||||||
return nil
|
if _, ok := m.successful[phash]; ok {
|
||||||
|
return channeldb.ErrPaymentAlreadySucceeded
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := m.failed[phash]; ok {
|
||||||
|
return channeldb.ErrPaymentAlreadyFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
p, ok := m.payments[phash]
|
||||||
|
if !ok {
|
||||||
|
return channeldb.ErrPaymentNotInitiated
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the attempt with this pid, and set the settle info.
|
||||||
|
for i, a := range p.attempts {
|
||||||
|
if a.AttemptID != pid {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
p.attempts[i].Settle = settleInfo
|
||||||
|
|
||||||
|
// Mark the payment successful on first settled attempt.
|
||||||
|
m.successful[phash] = struct{}{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("pid not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockControlTower) FailAttempt(phash lntypes.Hash, pid uint64,
|
||||||
|
failInfo *channeldb.HTLCFailInfo) error {
|
||||||
|
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
|
||||||
|
// Only allow failing attempts for payments not yet succeeded or
|
||||||
|
// failed.
|
||||||
|
if _, ok := m.successful[phash]; ok {
|
||||||
|
return channeldb.ErrPaymentAlreadySucceeded
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := m.failed[phash]; ok {
|
||||||
|
return channeldb.ErrPaymentAlreadyFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
p, ok := m.payments[phash]
|
||||||
|
if !ok {
|
||||||
|
return channeldb.ErrPaymentNotInitiated
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the attempt with this pid, and set the failure info.
|
||||||
|
for i, a := range p.attempts {
|
||||||
|
if a.AttemptID != pid {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
p.attempts[i].Failure = failInfo
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("pid not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockControlTower) Fail(phash lntypes.Hash,
|
func (m *mockControlTower) Fail(phash lntypes.Hash,
|
||||||
@ -283,10 +365,50 @@ func (m *mockControlTower) Fail(phash lntypes.Hash,
|
|||||||
m.fail <- failArgs{reason}
|
m.fail <- failArgs{reason}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(m.inflights, phash)
|
// Cannot fail already successful or failed payments.
|
||||||
|
if _, ok := m.successful[phash]; ok {
|
||||||
|
return channeldb.ErrPaymentAlreadySucceeded
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := m.failed[phash]; ok {
|
||||||
|
return channeldb.ErrPaymentAlreadyFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := m.payments[phash]; !ok {
|
||||||
|
return channeldb.ErrPaymentNotInitiated
|
||||||
|
}
|
||||||
|
|
||||||
|
m.failed[phash] = reason
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *mockControlTower) FetchPayment(phash lntypes.Hash) (
|
||||||
|
*channeldb.MPPayment, error) {
|
||||||
|
|
||||||
|
m.Lock()
|
||||||
|
defer m.Unlock()
|
||||||
|
|
||||||
|
p, ok := m.payments[phash]
|
||||||
|
if !ok {
|
||||||
|
return nil, channeldb.ErrPaymentNotInitiated
|
||||||
|
}
|
||||||
|
|
||||||
|
mp := &channeldb.MPPayment{
|
||||||
|
Info: &p.info,
|
||||||
|
}
|
||||||
|
|
||||||
|
reason, ok := m.failed[phash]
|
||||||
|
if ok {
|
||||||
|
mp.FailureReason = &reason
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a copy of the current attempts.
|
||||||
|
mp.HTLCs = append(mp.HTLCs, p.attempts...)
|
||||||
|
|
||||||
|
return mp, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *mockControlTower) FetchInFlightPayments() (
|
func (m *mockControlTower) FetchInFlightPayments() (
|
||||||
[]*channeldb.InFlightPayment, error) {
|
[]*channeldb.InFlightPayment, error) {
|
||||||
|
|
||||||
@ -297,8 +419,25 @@ func (m *mockControlTower) FetchInFlightPayments() (
|
|||||||
m.fetchInFlight <- struct{}{}
|
m.fetchInFlight <- struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In flight are all payments not successful or failed.
|
||||||
var fl []*channeldb.InFlightPayment
|
var fl []*channeldb.InFlightPayment
|
||||||
for _, ifl := range m.inflights {
|
for hash, p := range m.payments {
|
||||||
|
if _, ok := m.successful[hash]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := m.failed[hash]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var attempts []channeldb.HTLCAttemptInfo
|
||||||
|
for _, a := range p.attempts {
|
||||||
|
attempts = append(attempts, a.HTLCAttemptInfo)
|
||||||
|
}
|
||||||
|
ifl := channeldb.InFlightPayment{
|
||||||
|
Info: &p.info,
|
||||||
|
Attempts: attempts,
|
||||||
|
}
|
||||||
|
|
||||||
fl = append(fl, &ifl)
|
fl = append(fl, &ifl)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,9 +449,3 @@ func (m *mockControlTower) SubscribePayment(paymentHash lntypes.Hash) (
|
|||||||
|
|
||||||
return false, nil, errors.New("not implemented")
|
return false, nil, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockControlTower) FailAttempt(hash lntypes.Hash, pid uint64,
|
|
||||||
failInfo *channeldb.HTLCFailInfo) error {
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user