@@ -10,9 +10,21 @@ import (
10
10
"github.com/btcsuite/btcd/btcec/v2"
11
11
"github.com/btcsuite/btcd/btcutil"
12
12
"github.com/lightninglabs/loop/fsm"
13
+ "github.com/lightninglabs/loop/swapserverrpc"
13
14
reservationrpc "github.com/lightninglabs/loop/swapserverrpc"
14
15
)
15
16
17
+ var (
18
+ defaultWaitForStateTime = time .Second * 15
19
+ )
20
+
21
+ // FSMSendEventReq contains the information needed to send an event to the FSM.
22
+ type FSMSendEventReq struct {
23
+ fsm * FSM
24
+ event fsm.EventType
25
+ eventCtx fsm.EventContext
26
+ }
27
+
16
28
// Manager manages the reservation state machines.
17
29
type Manager struct {
18
30
// cfg contains all the services that the reservation manager needs to
@@ -22,6 +34,10 @@ type Manager struct {
22
34
// activeReservations contains all the active reservationsFSMs.
23
35
activeReservations map [ID ]* FSM
24
36
37
+ currentHeight int32
38
+
39
+ reqChan chan * FSMSendEventReq
40
+
25
41
sync.Mutex
26
42
}
27
43
@@ -30,6 +46,7 @@ func NewManager(cfg *Config) *Manager {
30
46
return & Manager {
31
47
cfg : cfg ,
32
48
activeReservations : make (map [ID ]* FSM ),
49
+ reqChan : make (chan * FSMSendEventReq ),
33
50
}
34
51
}
35
52
@@ -42,7 +59,7 @@ func (m *Manager) Run(ctx context.Context, height int32,
42
59
runCtx , cancel := context .WithCancel (ctx )
43
60
defer cancel ()
44
61
45
- currentHeight : = height
62
+ m . currentHeight = height
46
63
47
64
err := m .RecoverReservations (runCtx )
48
65
if err != nil {
@@ -64,7 +81,9 @@ func (m *Manager) Run(ctx context.Context, height int32,
64
81
select {
65
82
case height := <- newBlockChan :
66
83
log .Debugf ("Received block %v" , height )
67
- currentHeight = height
84
+ m .Lock ()
85
+ m .currentHeight = height
86
+ m .Unlock ()
68
87
69
88
case reservationRes , ok := <- ntfnChan :
70
89
if ! ok {
@@ -76,13 +95,26 @@ func (m *Manager) Run(ctx context.Context, height int32,
76
95
77
96
log .Debugf ("Received reservation %x" ,
78
97
reservationRes .ReservationId )
79
- _ , err := m .newReservation (
80
- runCtx , uint32 (currentHeight ), reservationRes ,
98
+ _ , err := m .newReservationFromNtfn (
99
+ runCtx , uint32 (m . currentHeight ), reservationRes ,
81
100
)
82
101
if err != nil {
83
102
return err
84
103
}
85
104
105
+ case req := <- m .reqChan :
106
+ // We'll send the event in a goroutine to avoid blocking
107
+ // the main loop.
108
+ go func () {
109
+ err := req .fsm .SendEvent (
110
+ runCtx , req .event , req .eventCtx ,
111
+ )
112
+ if err != nil {
113
+ log .Errorf ("Error sending event: %v" ,
114
+ err )
115
+ }
116
+ }()
117
+
86
118
case err := <- newBlockErrChan :
87
119
return err
88
120
@@ -93,9 +125,11 @@ func (m *Manager) Run(ctx context.Context, height int32,
93
125
}
94
126
}
95
127
96
- // newReservation creates a new reservation from the reservation request.
97
- func (m * Manager ) newReservation (ctx context.Context , currentHeight uint32 ,
98
- req * reservationrpc.ServerReservationNotification ) (* FSM , error ) {
128
+ // newReservationFromNtfn creates a new reservation from the reservation
129
+ // notification.
130
+ func (m * Manager ) newReservationFromNtfn (ctx context.Context ,
131
+ currentHeight uint32 , req * reservationrpc.ServerReservationNotification ,
132
+ ) (* FSM , error ) {
99
133
100
134
var reservationID ID
101
135
err := reservationID .FromByteSlice (
@@ -120,7 +154,7 @@ func (m *Manager) newReservation(ctx context.Context, currentHeight uint32,
120
154
m .activeReservations [reservationID ] = reservationFSM
121
155
m .Unlock ()
122
156
123
- initContext := & InitReservationContext {
157
+ initContext := & ServerRequestedInitContext {
124
158
reservationID : reservationID ,
125
159
serverPubkey : serverKey ,
126
160
value : btcutil .Amount (req .Value ),
@@ -154,6 +188,66 @@ func (m *Manager) newReservation(ctx context.Context, currentHeight uint32,
154
188
return reservationFSM , nil
155
189
}
156
190
191
+ // RequestReservationFromServer sends a request to the server to create a new
192
+ // reservation.
193
+ func (m * Manager ) RequestReservationFromServer (ctx context.Context ,
194
+ value btcutil.Amount , expiry uint32 , maxPrepaymentAmt btcutil.Amount ) (
195
+ * Reservation , error ) {
196
+
197
+ m .Lock ()
198
+ currentHeight := m .currentHeight
199
+ m .Unlock ()
200
+ // Create a new reservation req.
201
+ req := & ClientRequestedInitContext {
202
+ value : value ,
203
+ relativeExpiry : expiry ,
204
+ heightHint : uint32 (currentHeight ),
205
+ maxPrepaymentAmt : maxPrepaymentAmt ,
206
+ }
207
+
208
+ reservationFSM := NewFSM (m .cfg , ProtocolVersionClientInitiated )
209
+ // Send the event to the main loop.
210
+ m .reqChan <- & FSMSendEventReq {
211
+ fsm : reservationFSM ,
212
+ event : OnClientInitialized ,
213
+ eventCtx : req ,
214
+ }
215
+
216
+ // We'll now wait for the reservation to be in the state where we are
217
+ // sending the prepayment.
218
+ err := reservationFSM .DefaultObserver .WaitForState (
219
+ ctx , defaultWaitForStateTime , SendPrepaymentPayment ,
220
+ fsm .WithAbortEarlyOnErrorOption (),
221
+ )
222
+ if err != nil {
223
+ return nil , err
224
+ }
225
+
226
+ // Now we can add the reservation to our active fsm.
227
+ m .Lock ()
228
+ m .activeReservations [reservationFSM .reservation .ID ] = reservationFSM
229
+ m .Unlock ()
230
+
231
+ return reservationFSM .reservation , nil
232
+ }
233
+
234
+ // QuoteReservation quotes the server for a new reservation.
235
+ func (m * Manager ) QuoteReservation (ctx context.Context , value btcutil.Amount ,
236
+ expiry uint32 ) (btcutil.Amount , error ) {
237
+
238
+ quoteReq := & swapserverrpc.QuoteReservationRequest {
239
+ Value : uint64 (value ),
240
+ Expiry : expiry ,
241
+ }
242
+
243
+ req , err := m .cfg .ReservationClient .QuoteReservation (ctx , quoteReq )
244
+ if err != nil {
245
+ return 0 , err
246
+ }
247
+
248
+ return btcutil .Amount (req .PrepayCost ), nil
249
+ }
250
+
157
251
// RecoverReservations tries to recover all reservations that are still active
158
252
// from the database.
159
253
func (m * Manager ) RecoverReservations (ctx context.Context ) error {
0 commit comments