2
0
mirror of https://github.com/lightninglabs/loop synced 2024-11-17 21:25:56 +00:00

Merge pull request #698 from sputn1ck/io_reservation_expiry

This commit is contained in:
Konstantin Nick 2024-02-12 12:33:05 +01:00 committed by GitHub
commit 6a62cd02d7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 74 additions and 17 deletions

View File

@ -13,7 +13,10 @@ var (
ErrWaitForStateTimedOut = errors.New(
"timed out while waiting for event",
)
ErrInvalidContextType = errors.New("invalid context")
ErrInvalidContextType = errors.New("invalid context")
ErrWaitingForStateEarlyAbortError = errors.New(
"waiting for state early abort",
)
)
const (
@ -73,6 +76,8 @@ type Notification struct {
NextState StateType
// Event is the event that was processed.
Event EventType
// LastActionError is the error returned by the last action executed.
LastActionError error
}
// Observer is an interface that can be implemented by types that want to
@ -214,9 +219,10 @@ func (s *StateMachine) SendEvent(event EventType, eventCtx EventContext) error {
// Notify the state machine's observers.
s.observerMutex.Lock()
notification := Notification{
PreviousState: s.previous,
NextState: s.current,
Event: event,
PreviousState: s.previous,
NextState: s.current,
Event: event,
LastActionError: s.LastActionError,
}
for _, observer := range s.observers {

View File

@ -55,7 +55,8 @@ type WaitForStateOption interface {
// fsmOptions is a struct that holds all options that can be passed to the
// WaitForState function.
type fsmOptions struct {
initialWait time.Duration
initialWait time.Duration
abortEarlyOnError bool
}
// InitialWaitOption is an option that can be passed to the WaitForState
@ -76,6 +77,24 @@ func (w *InitialWaitOption) apply(o *fsmOptions) {
o.initialWait = w.initialWait
}
// AbortEarlyOnErrorOption is an option that can be passed to the WaitForState
// function to abort early if an error occurs.
type AbortEarlyOnErrorOption struct {
abortEarlyOnError bool
}
// apply implements the WaitForStateOption interface.
func (a *AbortEarlyOnErrorOption) apply(o *fsmOptions) {
o.abortEarlyOnError = a.abortEarlyOnError
}
// WithAbortEarlyOnErrorOption creates a new AbortEarlyOnErrorOption.
func WithAbortEarlyOnErrorOption() WaitForStateOption {
return &AbortEarlyOnErrorOption{
abortEarlyOnError: true,
}
}
// WaitForState waits for the state machine to reach the given state.
// If the optional initialWait parameter is set, the function will wait for
// the given duration before checking the state. This is useful if the
@ -105,7 +124,8 @@ func (s *CachedObserver) WaitForState(ctx context.Context,
defer cancel()
// Channel to notify when the desired state is reached
ch := make(chan struct{})
// or an error occurred.
ch := make(chan error)
// Goroutine to wait on condition variable
go func() {
@ -115,8 +135,26 @@ func (s *CachedObserver) WaitForState(ctx context.Context,
for {
// Check if the last state is the desired state
if s.lastNotification.NextState == state {
ch <- struct{}{}
return
select {
case <-timeoutCtx.Done():
return
case ch <- nil:
return
}
}
// Check if an error occurred
if s.lastNotification.Event == OnError {
if options.abortEarlyOnError {
select {
case <-timeoutCtx.Done():
return
case ch <- s.lastNotification.LastActionError:
return
}
}
}
// Otherwise, wait for the next notification
@ -130,7 +168,11 @@ func (s *CachedObserver) WaitForState(ctx context.Context,
return NewErrWaitingForStateTimeout(
state, s.lastNotification.NextState,
)
case <-ch:
case lastActionErr := <-ch:
if lastActionErr != nil {
return lastActionErr
}
return nil
}
}

View File

@ -21,7 +21,7 @@ import (
"github.com/lightningnetwork/lnd/lntypes"
)
var (
const (
// Define route independent max routing fees. We have currently no way
// to get a reliable estimate of the routing fees. Best we can do is
// the minimum routing fees, which is not very indicative.
@ -46,6 +46,10 @@ var (
// defaultPollPaymentTime is the default time to poll the server for the
// payment status.
defaultPollPaymentTime = time.Second * 15
// htlcExpiryDelta is the delta in blocks we require between the htlc
// expiry and reservation expiry.
htlcExpiryDelta = int32(40)
)
// InitInstantOutCtx contains the context for the InitInstantOutAction.
@ -96,6 +100,15 @@ func (f *FSM) InitInstantOutAction(eventCtx fsm.EventContext) fsm.EventType {
reservationAmt += uint64(res.Value)
reservationIds = append(reservationIds, resId[:])
reservations = append(reservations, res)
// Check that the reservation expiry is larger than the cltv
// expiry of the swap, with an additional delta to allow for
// preimage reveal.
if int32(res.Expiry) < initCtx.cltvExpiry+htlcExpiryDelta {
return f.HandleError(fmt.Errorf("reservation %x has "+
"expiry %v which is less than the swap expiry %v",
resId, res.Expiry, initCtx.cltvExpiry))
}
}
// Create the preimage for the swap.

View File

@ -8,6 +8,7 @@ import (
"time"
"github.com/btcsuite/btcd/btcutil"
"github.com/lightninglabs/loop/fsm"
"github.com/lightninglabs/loop/instantout/reservation"
looprpc "github.com/lightninglabs/loop/swapserverrpc"
"github.com/lightningnetwork/lnd/lntypes"
@ -169,15 +170,10 @@ func (m *Manager) NewInstantOut(ctx context.Context,
// waiting for sweepless sweep to be confirmed.
err = instantOut.DefaultObserver.WaitForState(
ctx, defaultStateWaitTime, WaitForSweeplessSweepConfirmed,
fsm.WithAbortEarlyOnErrorOption(),
)
if err != nil {
if instantOut.LastActionError != nil {
return instantOut, fmt.Errorf(
"error waiting for sweepless sweep "+
"confirmed: %w", instantOut.LastActionError,
)
}
return instantOut, nil
return nil, err
}
return instantOut, nil