From b2b8a9523a5d465192efccfc2a00d8601e9760c7 Mon Sep 17 00:00:00 2001 From: sputn1ck Date: Fri, 25 Aug 2023 01:54:18 +0200 Subject: [PATCH] reservation: update reservation state machine This commit updates the reservation statemachine to allow for locking and spending of the initial reservation. --- instantout/reservation/actions.go | 41 +++++++++++++++++------ instantout/reservation/fsm.go | 18 ++++++++-- instantout/reservation/manager.go | 19 +++++++++++ instantout/reservation/reservation_fsm.md | 4 +++ 4 files changed, 69 insertions(+), 13 deletions(-) diff --git a/instantout/reservation/actions.go b/instantout/reservation/actions.go index 80ad375..c6942a2 100644 --- a/instantout/reservation/actions.go +++ b/instantout/reservation/actions.go @@ -86,8 +86,8 @@ func (r *FSM) SubscribeToConfirmationAction(_ fsm.EventContext) fsm.EventType { r.reservation.InitiationHeight) confChan, errConfChan, err := r.cfg.ChainNotifier.RegisterConfirmationsNtfn( - r.ctx, nil, pkscript, DefaultConfTarget, - r.reservation.InitiationHeight, + r.ctx, nil, pkscript, 1, + r.reservation.InitiationHeight-1, ) if err != nil { r.Errorf("unable to subscribe to conf notification: %v", err) @@ -143,28 +143,47 @@ func (r *FSM) SubscribeToConfirmationAction(_ fsm.EventContext) fsm.EventType { // ReservationConfirmedAction waits for the reservation to be either expired or // waits for other actions to happen. -func (r *FSM) ReservationConfirmedAction(_ fsm.EventContext) fsm.EventType { - blockHeightChan, errEpochChan, err := r.cfg.ChainNotifier. - RegisterBlockEpochNtfn(r.ctx) +func (f *FSM) ReservationConfirmedAction(_ fsm.EventContext) fsm.EventType { + blockHeightChan, errEpochChan, err := f.cfg.ChainNotifier. + RegisterBlockEpochNtfn(f.ctx) if err != nil { - return r.HandleError(err) + return f.HandleError(err) + } + + pkSCript, err := f.reservation.GetPkScript() + if err != nil { + return f.HandleError(err) + } + + spendChan, errSpendChan, err := f.cfg.ChainNotifier.RegisterSpendNtfn( + f.ctx, f.reservation.Outpoint, pkSCript, + f.reservation.InitiationHeight, + ) + if err != nil { + return f.HandleError(err) } for { select { case err := <-errEpochChan: - return r.HandleError(err) + return f.HandleError(err) + + case err := <-errSpendChan: + return f.HandleError(err) case blockHeight := <-blockHeightChan: - expired := blockHeight >= int32(r.reservation.Expiry) + expired := blockHeight >= int32(f.reservation.Expiry) if expired { - r.Debugf("Reservation %v expired", - r.reservation.ID) + f.Debugf("Reservation %v expired", + f.reservation.ID) return OnTimedOut } - case <-r.ctx.Done(): + case <-spendChan: + return OnSpent + + case <-f.ctx.Done(): return fsm.NoOp } } diff --git a/instantout/reservation/fsm.go b/instantout/reservation/fsm.go index 078012a..f3e595b 100644 --- a/instantout/reservation/fsm.go +++ b/instantout/reservation/fsm.go @@ -90,7 +90,11 @@ var ( // Failed is the state where the reservation has failed. Failed = fsm.StateType("Failed") - // Swept is the state where the reservation has been swept by the server. + // Spent is the state where a spend tx has been confirmed. + Spent = fsm.StateType("Spent") + + // Swept is the state where the reservation has been swept by the + // server. Swept = fsm.StateType("Swept") ) @@ -119,6 +123,10 @@ var ( // OnRecover is the event that is triggered when the reservation FSM // recovers from a restart. OnRecover = fsm.EventType("OnRecover") + + // OnSpent is the event that is triggered when the reservation has been + // spent. + OnSpent = fsm.EventType("OnSpent") ) // GetReservationStates returns the statemap that defines the reservation @@ -149,6 +157,7 @@ func (f *FSM) GetReservationStates() fsm.States { }, Confirmed: fsm.State{ Transitions: fsm.Transitions{ + OnSpent: Spent, OnTimedOut: TimedOut, OnRecover: Confirmed, }, @@ -157,6 +166,11 @@ func (f *FSM) GetReservationStates() fsm.States { TimedOut: fsm.State{ Action: fsm.NoOpAction, }, + + Spent: fsm.State{ + Action: fsm.NoOpAction, + }, + Failed: fsm.State{ Action: fsm.NoOpAction, }, @@ -208,7 +222,7 @@ func (r *FSM) Errorf(format string, args ...interface{}) { // isFinalState returns true if the state is a final state. func isFinalState(state fsm.StateType) bool { switch state { - case Failed, Swept, TimedOut: + case Failed, Swept, TimedOut, Spent: return true } return false diff --git a/instantout/reservation/manager.go b/instantout/reservation/manager.go index eedc8c9..38ad636 100644 --- a/instantout/reservation/manager.go +++ b/instantout/reservation/manager.go @@ -251,3 +251,22 @@ func (m *Manager) RecoverReservations(ctx context.Context) error { func (m *Manager) GetReservations(ctx context.Context) ([]*Reservation, error) { return m.cfg.Store.ListReservations(ctx) } + +// GetReservation returns the reservation for the given id. +func (m *Manager) GetReservation(ctx context.Context, id ID) (*Reservation, + error) { + + return m.cfg.Store.GetReservation(ctx, id) +} + +// LockReservation locks the reservation for the given id. +func (m *Manager) LockReservation(ctx context.Context, id ID) error { + // TODO(sputn1ck): implement + return nil +} + +// UnlockReservation unlocks the reservation for the given id. +func (m *Manager) UnlockReservation(ctx context.Context, id ID) error { + // TODO(sputn1ck): implement + return nil +} diff --git a/instantout/reservation/reservation_fsm.md b/instantout/reservation/reservation_fsm.md index 712a1ea..d15c3b0 100644 --- a/instantout/reservation/reservation_fsm.md +++ b/instantout/reservation/reservation_fsm.md @@ -2,6 +2,7 @@ stateDiagram-v2 [*] --> Init: OnServerRequest Confirmed +Confirmed --> SpendBroadcasted: OnSpendBroadcasted Confirmed --> TimedOut: OnTimedOut Confirmed --> Confirmed: OnRecover Failed @@ -9,6 +10,9 @@ Init Init --> Failed: OnError Init --> WaitForConfirmation: OnBroadcast Init --> Failed: OnRecover +SpendBroadcasted +SpendBroadcasted --> SpendConfirmed: OnSpendConfirmed +SpendConfirmed TimedOut WaitForConfirmation WaitForConfirmation --> WaitForConfirmation: OnRecover