Skip to content

Commit 49eca5d

Browse files
authored
Merge pull request #106 from movementlabsxyz/l-monninger/expose-faucet-api
feat: expose `movement-aptos` faucet API
2 parents b17d607 + ada50bf commit 49eca5d

File tree

7 files changed

+133
-23
lines changed

7 files changed

+133
-23
lines changed

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

migration/util/migrator-types/src/migrator/movement_aptos_migrator.rs

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ impl MovementAptosMigrator {
3737
}
3838

3939
/// Builds a [MovementAptosMigrator] from a [NodeConfig].
40-
pub fn from_config(config: NodeConfig) -> Result<Self, anyhow::Error> {
41-
let movement_aptos = MovementAptos::from_config(config)?;
40+
pub fn try_from_config(config: NodeConfig) -> Result<Self, anyhow::Error> {
41+
let movement_aptos = MovementAptos::try_from_config(config, None)?;
4242
let runner = Runner::MovementAptos(movement_aptos);
4343
Ok(Self::new(runner))
4444
}
@@ -47,7 +47,7 @@ impl MovementAptosMigrator {
4747
pub fn from_movement_aptos_node(node: MovementAptosNode) -> Result<Self, anyhow::Error> {
4848
// get the config from the node
4949
let config = node.node_config()?;
50-
Ok(Self::from_config(config)?)
50+
Ok(Self::try_from_config(config)?)
5151
}
5252

5353
/// Rest Api url for the runner.
@@ -68,6 +68,24 @@ impl MovementAptosMigrator {
6868
}
6969
}
7070

71+
/// Faucet Api url for the runner.
72+
pub async fn wait_for_faucet_api_url(
73+
&self,
74+
condition: impl Into<WaitCondition>,
75+
) -> Result<String, anyhow::Error> {
76+
match &self.runner {
77+
Runner::MovementAptos(movement_aptos) => {
78+
let faucet_api = movement_aptos
79+
.faucet_api()
80+
.read()
81+
.wait_for(condition)
82+
.await
83+
.context("failed to wait for Movement Aptos faucet api")?;
84+
Ok(faucet_api.listen_url().to_string())
85+
}
86+
}
87+
}
88+
7189
/// Waits for the rest client to be ready.
7290
pub async fn wait_for_rest_client_ready(
7391
&self,
@@ -83,15 +101,17 @@ impl MovementAptosMigrator {
83101
/// Waits for the rest faucet client to be ready.
84102
pub async fn wait_for_faucet_client_ready(
85103
&self,
86-
condition: impl Into<WaitCondition>,
104+
condition: impl Into<WaitCondition> + Clone,
87105
) -> Result<MovementAptosFaucetClient, anyhow::Error> {
88-
// let rest_api_url = self.wait_for_rest_api_url(condition).await?;
89-
// let faucet_client =
90-
// MovementAptosFaucetClient::new(rest_api_url.parse().map_err(|e| {
91-
// anyhow::anyhow!("failed to parse Movement Aptos rest api url: {}", e)
92-
// })?);
93-
// Ok(faucet_client)
94-
todo!()
106+
let rest_api_url = self.wait_for_rest_api_url(condition.clone()).await?.parse().map_err(|e| {
107+
anyhow::anyhow!("failed to parse Movement Aptos rest api url: {}", e)
108+
})?;
109+
let faucet_api_url = self.wait_for_faucet_api_url(condition).await?.parse().map_err(|e| {
110+
anyhow::anyhow!("failed to parse Movement Aptos faucet api url: {}", e)
111+
})?;
112+
let faucet_client =
113+
MovementAptosFaucetClient::new(rest_api_url, faucet_api_url);
114+
Ok(faucet_client)
95115
}
96116

97117
/// Gets a [MovementAptosNode] from the runner.
@@ -105,7 +125,7 @@ impl TryFrom<NodeConfig> for MovementAptosMigrator {
105125
type Error = anyhow::Error;
106126

107127
fn try_from(config: NodeConfig) -> Result<Self, Self::Error> {
108-
Ok(Self::from_config(config)?)
128+
Ok(Self::try_from_config(config)?)
109129
}
110130
}
111131

util/movement-aptos/core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ uuid = { workspace = true }
3030
serde_yaml = { workspace = true }
3131
tracing = { workspace = true }
3232
mtma-types = { workspace = true }
33+
portpicker = { workspace = true }
3334

3435
[dev-dependencies]
3536
uuid = { workspace = true }

util/movement-aptos/core/src/config.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ pub struct Config {
6363
#[clap(long)]
6464
pub node_config: NodeConfigWrapper,
6565

66+
/// The faucet port to use.
67+
#[clap(long)]
68+
pub faucet_port: Option<u16>,
69+
6670
/// The log file to use.
6771
#[orfile(config)]
6872
#[clap(long)]
@@ -88,7 +92,7 @@ impl Config {
8892

8993
node_config.base.working_dir = Some(db_dir.clone());
9094

91-
Ok(Config { node_config: NodeConfigWrapper(node_config), log_file: None })
95+
Ok(Config { node_config: NodeConfigWrapper(node_config), log_file: None, faucet_port: None })
9296
}
9397

9498
/// Builds the config into a [MovementAptos] runner.
@@ -107,6 +111,7 @@ impl Config {
107111

108112
Ok(MovementAptos::<runtime::TokioTest>::new(
109113
self.node_config.node_config().clone(),
114+
self.faucet_port.unwrap_or_default(),
110115
false,
111116
workspace_dir,
112117
))

util/movement-aptos/core/src/movement_aptos.rs

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ use mtma_types::movement_aptos::aptos_config::config::NodeConfig;
33
use std::path::PathBuf;
44
pub mod rest_api;
55
use kestrel::process::{command::Command, ProcessOperations};
6+
pub mod faucet_api;
67
pub mod runtime;
78
use anyhow::Context;
89
pub use rest_api::RestApi;
10+
pub use faucet_api::FaucetApi;
911
use runtime::Runtime;
1012
use std::marker::PhantomData;
11-
use tracing::{info, warn};
13+
use tracing::{info, debug, warn};
1214

1315
/// Errors thrown when running [MovementAptos].
1416
#[derive(Debug, thiserror::Error)]
@@ -24,6 +26,8 @@ where
2426
{
2527
/// The [NodeConfig]
2628
pub node_config: NodeConfig,
29+
/// The faucet port
30+
pub faucet_port: u16,
2731
/// Whether or not to multiprocess
2832
pub multiprocess: bool,
2933
/// The workspace for the multiprocessing should it occur
@@ -32,6 +36,8 @@ where
3236
pub workspace: PathBuf,
3337
/// The rest api state
3438
pub rest_api: State<RestApi>,
39+
/// The faucet api state
40+
pub faucet_api: State<FaucetApi>,
3541
/// The marker for the runtime
3642
pub runtime: PhantomData<R>,
3743
}
@@ -41,26 +47,37 @@ where
4147
R: Runtime,
4248
{
4349
/// 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 }
4652
}
4753

4854
/// 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+
5062
let workspace = config_path
5163
.base
5264
.working_dir
5365
.clone()
5466
.context("Working directory not set")
5567
.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))
5769
}
5870

5971
/// Borrow sthe rest api state
6072
pub fn rest_api(&self) -> &State<RestApi> {
6173
&self.rest_api
6274
}
6375

76+
/// Borrow the faucet api state
77+
pub fn faucet_api(&self) -> &State<FaucetApi> {
78+
&self.faucet_api
79+
}
80+
6481
/// Borrows the [NodeConfig]
6582
pub fn node_config(&self) -> &NodeConfig {
6683
&self.node_config
@@ -109,6 +126,8 @@ where
109126
.await
110127
.map_err(|e| MovementAptosError::Internal(e.into()))?;
111128

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.
112131
let command = Command::line(
113132
"aptos",
114133
vec![
@@ -118,6 +137,8 @@ where
118137
&self.workspace.to_string_lossy(),
119138
"--config-path",
120139
&config_path.to_string_lossy(),
140+
"--faucet-port",
141+
&self.faucet_port.to_string(),
121142
],
122143
Some(&self.workspace),
123144
false,
@@ -143,6 +164,7 @@ where
143164
/// Runs the node and fills state.
144165
pub async fn run(&self) -> Result<(), MovementAptosError> {
145166
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) };
146168

147169
let runner = self.clone();
148170
let runner_task = kestrel::task(async move {
@@ -157,23 +179,52 @@ where
157179
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
158180
loop {
159181
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
160-
info!("POLLING REST API: {:?}", rest_api);
182+
debug!("Polling rest api: {:?}", rest_api);
161183
// wait for the rest api to be ready
162184
match reqwest::get(rest_api.rest_api_url.clone())
163185
.await
164186
.map_err(|e| MovementAptosError::Internal(e.into()))
165187
{
166188
Ok(response) => {
167-
info!("REST API RESPONSE: {:?}", response);
189+
debug!("Received response from rest api: {:?}", response);
168190
if response.status().is_success() {
169191
rest_api_state.write().set(rest_api).await;
170192
break;
171193
} 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);
173224
}
174225
}
175226
Err(e) => {
176-
warn!("REST API ERROR: {:?}", e);
227+
warn!("Encountered error while polling faucet api: {:?}", e);
177228
}
178229
}
179230
}
@@ -184,6 +235,7 @@ where
184235
// await the runner
185236
runner_task.await.map_err(|e| MovementAptosError::Internal(e.into()))??;
186237
rest_api_polling.await.map_err(|e| MovementAptosError::Internal(e.into()))??;
238+
faucet_api_polling.await.map_err(|e| MovementAptosError::Internal(e.into()))??;
187239

188240
Ok(())
189241
}
@@ -208,6 +260,7 @@ mod tests {
208260
unique_id.to_string().split('-').next().unwrap()
209261
));
210262
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()))?;
211264

212265
// create parent dirs
213266
{
@@ -229,15 +282,19 @@ mod tests {
229282
node_config.storage.dir = db_dir.clone();
230283

231284
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
233288
let rest_api_state = movement_aptos.rest_api().read().clone();
289+
let faucet_api_state = movement_aptos.faucet_api().read().clone();
234290

235291
let movement_aptos_task = kestrel::task(async move {
236292
movement_aptos.run().await?;
237293
Ok::<_, MovementAptosError>(())
238294
});
239295

240296
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?;
241298

242299
kestrel::end!(movement_aptos_task)?;
243300

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#[derive(Debug, Clone)]
2+
pub struct FaucetApi {
3+
/// The Rest Api url.
4+
pub faucet_api_url: String,
5+
}
6+
7+
impl FaucetApi {
8+
pub fn new(faucet_api_url: String) -> Self {
9+
Self { faucet_api_url }
10+
}
11+
12+
/// Borrow the rest api listen url.
13+
pub fn listen_url(&self) -> &str {
14+
&self.faucet_api_url
15+
}
16+
}

util/movement-aptos/movement-aptos/docs/cli/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ Run run with all parameters passed explicitly as CLI flags. See Orfile documenta
108108
###### **Options:**
109109

110110
* `--node-config <NODE_CONFIG>` — The node config to use
111+
* `--faucet-port <FAUCET_PORT>` — The faucet port to use
111112
* `--log-file <LOG_FILE>` — The log file to use
112113

113114

0 commit comments

Comments
 (0)