Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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
1 change: 1 addition & 0 deletions .changes/fixed/3187.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Use `String` for block type in GraphQL response extensions
29 changes: 29 additions & 0 deletions crates/client/src/reqwest_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ use fuel_core_types::{
},
fuel_types::BlockHeight,
};
use serde::{
Deserialize,
de::{
self,
Deserializer,
},
};
use std::{
future::Future,
marker::PhantomData,
Expand All @@ -24,6 +31,7 @@ pub struct ExtensionsRequest {
#[derive(Debug, Clone, serde::Deserialize)]
pub struct ExtensionsResponse {
pub required_fuel_block_height: Option<BlockHeight>,
#[serde(deserialize_with = "deserialize_block_height_option")]
pub current_fuel_block_height: Option<BlockHeight>,
pub fuel_block_height_precondition_failed: Option<bool>,
pub current_stf_version: Option<StateTransitionBytecodeVersion>,
Expand Down Expand Up @@ -58,6 +66,27 @@ impl<Operation> FuelOperation<Operation> {
}
}

fn deserialize_block_height_option<'de, D>(
deserializer: D,
) -> Result<Option<BlockHeight>, D::Error>
where
D: Deserializer<'de>,
{
let value = Option::<String>::deserialize(deserializer)?;
match value {
None => Ok(None),
Some(value) => {
let parsed = value.parse::<u32>().map_err(|_| {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't we parse directly into BlockHeight? It implemented FromStr trait

de::Error::invalid_value(
de::Unexpected::Str(&value),
&"a numeric block height string",
)
})?;
Ok(Some(BlockHeight::new(parsed)))
}
}
}

#[cfg(not(target_arch = "wasm32"))]
type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;

Expand Down
22 changes: 17 additions & 5 deletions crates/fuel-core/src/graphql_api/extensions/chain_state_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,8 @@ fn set_current_state<E>(
Value::Number(current_stf_version.into()),
);

let current_block_height: u32 = *current_block_height;
extensions.set(
CURRENT_FUEL_BLOCK_HEIGHT,
Value::Number(current_block_height.into()),
);
let block_height_str = block_to_trimmed_string(current_block_height);
extensions.set(CURRENT_FUEL_BLOCK_HEIGHT, Value::String(block_height_str));
}

trait SetExtensionsResponse {
Expand All @@ -141,6 +138,21 @@ impl SetExtensionsResponse for BTreeMap<String, Value> {
}
}

fn block_to_trimmed_string(block_height: BlockHeight) -> String {
let mut block_height_str = u32::from(block_height).to_string();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we don't want to use to_string implementation of the BlockHeight?


// trim leading zeros
let cut = block_height_str
.as_bytes()
.iter()
.position(|&b| b != b'0')
// if it's all zeros, keep a single "0"
.unwrap_or(block_height_str.len().saturating_sub(1));

block_height_str.drain(..cut);
block_height_str
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary leading zero trimming is dead code

Low Severity

The block_to_trimmed_string function contains logic to trim leading zeros from a string (lines 144-152), but this code path can never execute meaningfully. In Rust, u32::to_string() never produces leading zeros - it always outputs the minimal decimal representation. The entire trimming logic is dead code that adds unnecessary complexity. The function could be simplified to just u32::from(block_height).to_string().

Fix in Cursor Fix in Web

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... that's not what I was getting. I was getting leading zeros and that's why I added it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trimmed hex string breaks BlockHeight deserialization roundtrip

High Severity

block_to_trimmed_string strips leading zeros from BlockHeight::to_string()'s hex output (e.g., "00000064""64"), but the client-side deserialize_block_height_option calls BlockHeight::from_str on this trimmed value. Since BlockHeight is a fixed-width 4-byte hex type, from_str expects exactly 8 hex characters matching the Display format. Trimmed strings like "64" (wrong byte count) or "0" (odd-length hex) will fail to parse, breaking the response deserialization for current_fuel_block_height.

Additional Locations (1)
Fix in Cursor Fix in Web


#[cfg(test)]
mod tests {
use super::*;
Expand Down
Loading