@@ -3,12 +3,14 @@ use mtma_types::movement_aptos::aptos_config::config::NodeConfig;
3
3
use std:: path:: PathBuf ;
4
4
pub mod rest_api;
5
5
use kestrel:: process:: { command:: Command , ProcessOperations } ;
6
+ pub mod faucet_api;
6
7
pub mod runtime;
7
8
use anyhow:: Context ;
8
9
pub use rest_api:: RestApi ;
10
+ pub use faucet_api:: FaucetApi ;
9
11
use runtime:: Runtime ;
10
12
use std:: marker:: PhantomData ;
11
- use tracing:: { info, warn} ;
13
+ use tracing:: { info, debug , warn} ;
12
14
13
15
/// Errors thrown when running [MovementAptos].
14
16
#[ derive( Debug , thiserror:: Error ) ]
24
26
{
25
27
/// The [NodeConfig]
26
28
pub node_config : NodeConfig ,
29
+ /// The faucet port
30
+ pub faucet_port : u16 ,
27
31
/// Whether or not to multiprocess
28
32
pub multiprocess : bool ,
29
33
/// The workspace for the multiprocessing should it occur
32
36
pub workspace : PathBuf ,
33
37
/// The rest api state
34
38
pub rest_api : State < RestApi > ,
39
+ /// The faucet api state
40
+ pub faucet_api : State < FaucetApi > ,
35
41
/// The marker for the runtime
36
42
pub runtime : PhantomData < R > ,
37
43
}
@@ -41,26 +47,37 @@ where
41
47
R : Runtime ,
42
48
{
43
49
/// If you have something that marks your ability to get a runtime, you can use this.
44
- pub fn new ( node_config : NodeConfig , multiprocess : bool , workspace : PathBuf ) -> Self {
45
- Self { node_config, multiprocess, workspace, rest_api : State :: new ( ) , runtime : PhantomData }
50
+ pub fn new ( node_config : NodeConfig , faucet_port : u16 , multiprocess : bool , workspace : PathBuf ) -> Self {
51
+ Self { node_config, faucet_port , multiprocess, workspace, rest_api : State :: new ( ) , faucet_api : State :: new ( ) , runtime : PhantomData }
46
52
}
47
53
48
54
/// Constructs a new [MovementAptos] from a [NodeConfig].
49
- pub fn from_config ( config_path : NodeConfig ) -> Result < Self , MovementAptosError > {
55
+ pub fn try_from_config ( config_path : NodeConfig , faucet_port : Option < u16 > ) -> Result < Self , MovementAptosError > {
56
+
57
+ let faucet_port = match faucet_port {
58
+ Some ( port) => port,
59
+ None => portpicker:: pick_unused_port ( ) . context ( "Failed to pick unused port" ) . map_err ( |e| MovementAptosError :: Internal ( e. into ( ) ) ) ?
60
+ } ;
61
+
50
62
let workspace = config_path
51
63
. base
52
64
. working_dir
53
65
. clone ( )
54
66
. context ( "Working directory not set" )
55
67
. map_err ( |e| MovementAptosError :: Internal ( e. into ( ) ) ) ?;
56
- Ok ( Self :: new ( config_path, true , workspace) )
68
+ Ok ( Self :: new ( config_path, faucet_port , true , workspace) )
57
69
}
58
70
59
71
/// Borrow sthe rest api state
60
72
pub fn rest_api ( & self ) -> & State < RestApi > {
61
73
& self . rest_api
62
74
}
63
75
76
+ /// Borrow the faucet api state
77
+ pub fn faucet_api ( & self ) -> & State < FaucetApi > {
78
+ & self . faucet_api
79
+ }
80
+
64
81
/// Borrows the [NodeConfig]
65
82
pub fn node_config ( & self ) -> & NodeConfig {
66
83
& self . node_config
@@ -109,6 +126,8 @@ where
109
126
. await
110
127
. map_err ( |e| MovementAptosError :: Internal ( e. into ( ) ) ) ?;
111
128
129
+ // note: we could grab the entirety of the run-localnet [Args] struct and expose it, replacing the test dir and config path as is reasonable.
130
+ // But, we will do that ad hoc.
112
131
let command = Command :: line (
113
132
"aptos" ,
114
133
vec ! [
@@ -118,6 +137,8 @@ where
118
137
& self . workspace. to_string_lossy( ) ,
119
138
"--config-path" ,
120
139
& config_path. to_string_lossy( ) ,
140
+ "--faucet-port" ,
141
+ & self . faucet_port. to_string( ) ,
121
142
] ,
122
143
Some ( & self . workspace ) ,
123
144
false ,
@@ -143,6 +164,7 @@ where
143
164
/// Runs the node and fills state.
144
165
pub async fn run ( & self ) -> Result < ( ) , MovementAptosError > {
145
166
let rest_api = RestApi { rest_api_url : format ! ( "http://{}" , self . node_config. api. address) } ;
167
+ let faucet_api = FaucetApi { faucet_api_url : format ! ( "http://127.0.0.1:{}" , self . faucet_port) } ;
146
168
147
169
let runner = self . clone ( ) ;
148
170
let runner_task = kestrel:: task ( async move {
@@ -157,23 +179,52 @@ where
157
179
tokio:: time:: sleep ( std:: time:: Duration :: from_secs ( 5 ) ) . await ;
158
180
loop {
159
181
tokio:: time:: sleep ( std:: time:: Duration :: from_secs ( 1 ) ) . await ;
160
- info ! ( "POLLING REST API : {:?}" , rest_api) ;
182
+ debug ! ( "Polling rest api : {:?}" , rest_api) ;
161
183
// wait for the rest api to be ready
162
184
match reqwest:: get ( rest_api. rest_api_url . clone ( ) )
163
185
. await
164
186
. map_err ( |e| MovementAptosError :: Internal ( e. into ( ) ) )
165
187
{
166
188
Ok ( response) => {
167
- info ! ( "REST API RESPONSE : {:?}" , response) ;
189
+ debug ! ( "Received response from rest api : {:?}" , response) ;
168
190
if response. status ( ) . is_success ( ) {
169
191
rest_api_state. write ( ) . set ( rest_api) . await ;
170
192
break ;
171
193
} else {
172
- warn ! ( "REST API RESPONSE: {:?}" , response) ;
194
+ warn ! ( "Failed to poll rest api: {:?}" , response) ;
195
+ }
196
+ }
197
+ Err ( e) => {
198
+ warn ! ( "Encountered error while polling rest api: {:?}" , e) ;
199
+ }
200
+ }
201
+ }
202
+
203
+ Ok :: < _ , MovementAptosError > ( ( ) )
204
+ } ) ;
205
+
206
+ // faucet api state
207
+ let faucet_api_state = self . faucet_api . clone ( ) ;
208
+ let faucet_api_polling = kestrel:: task ( async move {
209
+ loop {
210
+ tokio:: time:: sleep ( std:: time:: Duration :: from_secs ( 1 ) ) . await ;
211
+ debug ! ( "Polling faucet api: {:?}" , faucet_api) ;
212
+ // wait for the faucet api to be ready
213
+ match reqwest:: get ( faucet_api. faucet_api_url . clone ( ) )
214
+ . await
215
+ . map_err ( |e| MovementAptosError :: Internal ( e. into ( ) ) )
216
+ {
217
+ Ok ( response) => {
218
+ debug ! ( "Received response from faucet api: {:?}" , response) ;
219
+ if response. status ( ) . is_success ( ) {
220
+ faucet_api_state. write ( ) . set ( faucet_api) . await ;
221
+ break ;
222
+ } else {
223
+ warn ! ( "Failed to poll faucet api: {:?}" , response) ;
173
224
}
174
225
}
175
226
Err ( e) => {
176
- warn ! ( "REST API ERROR : {:?}" , e) ;
227
+ warn ! ( "Encountered error while polling faucet api : {:?}" , e) ;
177
228
}
178
229
}
179
230
}
@@ -184,6 +235,7 @@ where
184
235
// await the runner
185
236
runner_task. await . map_err ( |e| MovementAptosError :: Internal ( e. into ( ) ) ) ??;
186
237
rest_api_polling. await . map_err ( |e| MovementAptosError :: Internal ( e. into ( ) ) ) ??;
238
+ faucet_api_polling. await . map_err ( |e| MovementAptosError :: Internal ( e. into ( ) ) ) ??;
187
239
188
240
Ok ( ( ) )
189
241
}
@@ -208,6 +260,7 @@ mod tests {
208
260
unique_id. to_string( ) . split( '-' ) . next( ) . unwrap( )
209
261
) ) ;
210
262
let db_dir = working_dir. join ( "0" ) ;
263
+ let faucet_port = portpicker:: pick_unused_port ( ) . context ( "Failed to pick unused port" ) . map_err ( |e| MovementAptosError :: Internal ( e. into ( ) ) ) ?;
211
264
212
265
// create parent dirs
213
266
{
@@ -229,15 +282,19 @@ mod tests {
229
282
node_config. storage . dir = db_dir. clone ( ) ;
230
283
231
284
let movement_aptos =
232
- MovementAptos :: < runtime:: Delegated > :: new ( node_config, true , working_dir) ;
285
+ MovementAptos :: < runtime:: Delegated > :: new ( node_config, faucet_port, true , working_dir) ;
286
+
287
+ // extract the states for which we will wait
233
288
let rest_api_state = movement_aptos. rest_api ( ) . read ( ) . clone ( ) ;
289
+ let faucet_api_state = movement_aptos. faucet_api ( ) . read ( ) . clone ( ) ;
234
290
235
291
let movement_aptos_task = kestrel:: task ( async move {
236
292
movement_aptos. run ( ) . await ?;
237
293
Ok :: < _ , MovementAptosError > ( ( ) )
238
294
} ) ;
239
295
240
296
rest_api_state. wait_for ( tokio:: time:: Duration :: from_secs ( 120 ) ) . await ?;
297
+ faucet_api_state. wait_for ( tokio:: time:: Duration :: from_secs ( 120 ) ) . await ?;
241
298
242
299
kestrel:: end!( movement_aptos_task) ?;
243
300
0 commit comments