Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 9 additions & 17 deletions bin/network-monitor/src/faucet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,16 @@ pub struct FaucetTestDetails {

/// Response from the faucet's `/pow` endpoint.
///
/// `deny_unknown_fields` makes the monitor flag schema drift loudly — a new field on the faucet
/// response will fail deserialization and surface in the error message, instead of being
/// silently dropped.
/// Only the fields the monitor uses are declared. Other fields, including ones added by newer
/// faucet releases, are ignored.
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
struct PowChallengeResponse {
challenge: String,
target: u64,
#[expect(
dead_code,
reason = "Part of the API response, unused but required for `deny_unknown_fields`"
)]
timestamp: u64,
}

/// Response from the faucet's `/get_tokens` endpoint.
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct GetTokensResponse {
/// The ID of the note that holds the minted tokens.
#[serde(deserialize_with = "deserialize_note_id")]
Expand All @@ -75,18 +67,19 @@ fn deserialize_note_id<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Not

/// Response from the faucet's `/get_metadata` endpoint.
///
/// Field set mirrors the faucet's `GetMetadataResponse` in
/// `bin/faucet/src/api/get_metadata.rs` on the `next` branch. Keep these in sync; the
/// `deny_unknown_fields` attribute will surface any drift loudly.
/// Mirrors the faucet's `GetMetadataResponse`. Unknown fields are ignored so that a faucet release
/// adding a field does not break the faucet check.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct GetMetadataResponse {
pub version: String,
pub id: String,
pub decimals: u8,
pub explorer_url: Option<String>,
pub pow_load_difficulty: u64,
pub base_amount: u64,
/// The token amounts, in base units, that the faucet offers.
#[serde(default)]
pub token_amounts: Vec<u64>,
/// The remaining balance of the funding account in base units. It is `None` when the funding
/// service did not answer.
pub balance: Option<u64>,
Expand Down Expand Up @@ -317,9 +310,8 @@ async fn read_success_body(response: reqwest::Response) -> anyhow::Result<String
}

/// Deserialize a faucet response using [`serde_path_to_error`] so that the failing JSON path (e.g.
/// `balance`, `explorer_url`) is included in the error message. Combined with
/// `#[serde(deny_unknown_fields)]` on each response type, this means renamed, removed, or newly
/// added fields all surface a precise field name rather than a generic "unexpected response".
/// `balance`, `explorer_url`) is included in the error message. A missing or mistyped field
/// surfaces its name rather than a generic "unexpected response".
fn parse_faucet_response<T>(body: &str) -> anyhow::Result<T>
where
T: for<'de> Deserialize<'de>,
Expand Down
2 changes: 2 additions & 0 deletions bin/network-monitor/src/view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ mod tests {
explorer_url: Some("https://explorer.example".to_string()),
pow_load_difficulty: 4,
base_amount: 100,
token_amounts: vec![100, 500],
balance: Some(1_000_000),
}),
};
Expand Down Expand Up @@ -435,6 +436,7 @@ mod tests {
explorer_url: None,
pow_load_difficulty: 4,
base_amount: 100,
token_amounts: vec![100, 500],
balance: None,
}),
};
Expand Down
Loading