Skip to content
Open
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
4 changes: 2 additions & 2 deletions crates/fuel-core/src/schema/blob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ impl Blob {
}

#[graphql(complexity = "query_costs().bytecode_read")]
async fn bytecode(&self, ctx: &Context<'_>) -> async_graphql::Result<HexString> {
async fn bytecode(&self, ctx: &Context<'_>) -> async_graphql::Result<HexString<'_>> {
let query = ctx.read_view()?;
query
.blob_bytecode(self.0)
.map(HexString)
.map(|bytes| HexString(bytes.into()))
.map_err(async_graphql::Error::from)
}
}
Expand Down
10 changes: 4 additions & 6 deletions crates/fuel-core/src/schema/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,13 +424,13 @@ impl BlockMutation {
pub struct BlockSubscription;

#[Subscription]
impl BlockSubscription {
impl <'a>BlockSubscription {
#[graphql(name = "alpha__new_blocks")]
async fn new_blocks<'a>(
async fn new_blocks(
&self,
ctx: &Context<'a>,
) -> async_graphql::Result<
impl Stream<Item = async_graphql::Result<HexString>> + 'a + use<'a>,
impl Stream<Item = async_graphql::Result<HexString<'a>>> + 'a + use<'a>,
> {
require_expensive_subscriptions(ctx)?;
let worker_state: &graphql_api::worker_service::SharedState =
Expand All @@ -440,9 +440,7 @@ impl BlockSubscription {
let stream = BroadcastStream::new(receiver).map(|result| {
result
.map(|bytes| {
// TODO: Avoid cloning the bytes here.
// https://github.com/FuelLabs/fuel-core/issues/2657
HexString(bytes.as_ref().clone())
HexString::from(bytes.as_ref().clone())
Copy link
Author

@grandima grandima Nov 2, 2025

Choose a reason for hiding this comment

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

Cow::Owned is still needed here and as a result - cloning. Because Cow::Borrowed cannot be returned since it references only to a data that is owned by this map. Here's a compilation failing example if we use Cow::Borrowed:

error[E0515]: cannot return value referencing function parameter `bytes`
   --> crates/fuel-core/src/schema/block.rs:443:21
    |
443 |                     HexString::from(bytes.as_ref().as_slice())
    |                     ^^^^^^^^^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^
    |                     |               |
    |                     |               `bytes` is borrowed here
    |                     returns a value referencing data owned by the current function

})
.map_err(Into::into)
});
Expand Down
4 changes: 2 additions & 2 deletions crates/fuel-core/src/schema/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ impl Contract {
}

#[graphql(complexity = "query_costs().bytecode_read")]
async fn bytecode(&self, ctx: &Context<'_>) -> async_graphql::Result<HexString> {
async fn bytecode(&self, ctx: &Context<'_>) -> async_graphql::Result<HexString<'_>> {
let query = ctx.read_view()?;
query
.contract_bytecode(self.0)
.map(HexString)
.map(|bytes|HexString(bytes.into()))
.map_err(Into::into)
}

Expand Down
4 changes: 2 additions & 2 deletions crates/fuel-core/src/schema/da_compressed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ impl From<Vec<u8>> for DaCompressedBlock {

#[Object]
impl DaCompressedBlock {
async fn bytes(&self) -> HexString {
HexString(self.bytes.clone())
async fn bytes(&self) -> HexString<'_> {
HexString(self.bytes.as_slice().into())
}
}

Expand Down
6 changes: 3 additions & 3 deletions crates/fuel-core/src/schema/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ impl Message {
(*self.0.nonce()).into()
}

async fn data(&self) -> HexString {
async fn data(&self) -> HexString<'_> {
self.0.data().clone().into()
}

Expand Down Expand Up @@ -90,7 +90,7 @@ impl MessageQuery {
after: Option<String>,
last: Option<i32>,
before: Option<String>,
) -> async_graphql::Result<Connection<HexString, Message, EmptyFields, EmptyFields>>
) -> async_graphql::Result<Connection<HexString<'_>, Message, EmptyFields, EmptyFields>>
{
let query = ctx.read_view()?;
let owner = owner.map(|owner| owner.0);
Expand Down Expand Up @@ -221,7 +221,7 @@ impl MessageProof {
self.0.amount.into()
}

async fn data(&self) -> HexString {
async fn data(&self) -> HexString<'_> {
self.0.data.clone().into()
}
}
Expand Down
39 changes: 28 additions & 11 deletions crates/fuel-core/src/schema/scalars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use std::{
},
str::FromStr,
};
use std::borrow::Cow;
pub use tx_pointer::TxPointer;
pub use utxo_id::UtxoId;

Expand Down Expand Up @@ -161,10 +162,26 @@ impl CursorType for SortedTxCursor {
}

#[derive(Clone, Debug, derive_more::Into, derive_more::From, PartialEq, Eq)]
pub struct HexString(pub(crate) Vec<u8>);
pub struct HexString<'a>(pub(crate) Cow<'a, [u8]>);

impl From<Vec<u8>> for HexString<'_> {
fn from(vec: Vec<u8>) -> Self {
HexString(Cow::Owned(vec))
}
}

impl <'a>From<&'a [u8]> for HexString<'a> {
fn from(value: &'a [u8]) -> Self { HexString(Cow::Borrowed(value)) }
}

impl From<HexString<'_>> for Vec<u8> {
fn from(hex: HexString) -> Self {
hex.0.into_owned()
}
}

#[Scalar(name = "HexString")]
impl ScalarType for HexString {
impl ScalarType for HexString<'_> {
fn parse(value: Value) -> InputValueResult<Self> {
match &value {
Value::String(value) => {
Expand All @@ -179,14 +196,14 @@ impl ScalarType for HexString {
}
}

impl Display for HexString {
impl Display for HexString<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let s = format!("0x{}", hex::encode(&self.0));
let s = format!("0x{}", hex::encode(&self.0.as_ref()));
s.fmt(f)
}
}

impl CursorType for HexString {
impl CursorType for HexString<'_> {
type Error = String;

fn decode_cursor(s: &str) -> Result<Self, Self::Error> {
Expand All @@ -198,28 +215,28 @@ impl CursorType for HexString {
}
}

impl FromStr for HexString {
impl FromStr for HexString<'_> {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let value = s.strip_prefix("0x").unwrap_or(s);
// decode into bytes
let bytes = hex::decode(value).map_err(|e| e.to_string())?;
Ok(HexString(bytes))
Ok(HexString(Cow::Owned(bytes)))
}
}

impl From<fuel_types::Nonce> for HexString {
impl From<fuel_types::Nonce> for HexString<'_> {
fn from(n: fuel_types::Nonce) -> Self {
HexString(n.to_vec())
HexString(Cow::Owned(n.to_vec()))
}
}

impl TryInto<fuel_types::Nonce> for HexString {
impl TryInto<fuel_types::Nonce> for HexString<'_> {
type Error = TryFromSliceError;

fn try_into(self) -> Result<fuel_types::Nonce, Self::Error> {
let bytes: [u8; 32] = self.0.as_slice().try_into()?;
let bytes: [u8; 32] = self.0.as_ref().try_into()?;
Ok(fuel_types::Nonce::from(bytes))
}
}
Expand Down
4 changes: 2 additions & 2 deletions crates/fuel-core/src/schema/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ impl StorageSlot {
self.key.into()
}

async fn value(&self) -> HexString {
HexString(self.value.clone())
async fn value(&self) -> HexString<'_> {
HexString::from(self.value.as_ref())
}
}
34 changes: 17 additions & 17 deletions crates/fuel-core/src/schema/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ impl TxQuery {
async fn dry_run_inner(
&self,
ctx: &Context<'_>,
txs: Vec<HexString>,
txs: Vec<HexString<'_>>,
// If set to false, disable input utxo validation, overriding the configuration of the node.
// This allows for non-existent inputs to be used without signature validation
// for read-only calls.
Expand Down Expand Up @@ -375,7 +375,7 @@ impl TxQuery {
#[graphql(
desc = "The original transaction that contains application level logic only"
)]
tx: HexString,
tx: HexString<'_>,
#[graphql(
desc = "Number of blocks into the future to estimate the gas price for"
)]
Expand Down Expand Up @@ -519,7 +519,7 @@ impl TxQuery {
async fn estimate_predicates(
&self,
ctx: &Context<'_>,
tx: HexString,
tx: HexString<'_>,
) -> async_graphql::Result<Transaction> {
let query = ctx.read_view()?.into_owned();

Expand Down Expand Up @@ -550,7 +550,7 @@ impl TxQuery {
async fn dry_run(
&self,
ctx: &Context<'_>,
txs: Vec<HexString>,
txs: Vec<HexString<'_>>,
// If set to false, disable input utxo validation, overriding the configuration of the node.
// This allows for non-existent inputs to be used without signature validation
// for read-only calls.
Expand All @@ -574,7 +574,7 @@ impl TxQuery {
async fn dry_run_record_storage_reads(
&self,
ctx: &Context<'_>,
txs: Vec<HexString>,
txs: Vec<HexString<'_>>,
// If set to false, disable input utxo validation, overriding the configuration of the node.
// This allows for non-existent inputs to be used without signature validation
// for read-only calls.
Expand Down Expand Up @@ -627,7 +627,7 @@ impl TxMutation {
async fn dry_run(
&self,
ctx: &Context<'_>,
txs: Vec<HexString>,
txs: Vec<HexString<'_>>,
// If set to false, disable input utxo validation, overriding the configuration of the node.
// This allows for non-existent inputs to be used without signature validation
// for read-only calls.
Expand All @@ -648,7 +648,7 @@ impl TxMutation {
async fn submit(
&self,
ctx: &Context<'_>,
tx: HexString,
tx: HexString<'_>,
estimate_predicates: Option<bool>,
) -> async_graphql::Result<Transaction> {
let txpool = ctx.data_unchecked::<TxPool>();
Expand Down Expand Up @@ -679,7 +679,7 @@ impl TxMutation {
pub struct TxStatusSubscription;

#[Subscription]
impl TxStatusSubscription {
impl<'a> TxStatusSubscription {
/// Returns a stream of status updates for the given transaction id.
/// If the current status is [`TransactionStatus::Success`], [`TransactionStatus::Failed`],
/// or [`TransactionStatus::SqueezedOut`] the stream will return that and end immediately.
Expand All @@ -693,7 +693,7 @@ impl TxStatusSubscription {
/// a status. If this occurs the stream can simply be restarted to return
/// the latest status.
#[graphql(complexity = "query_costs().status_change + child_complexity")]
async fn status_change<'a>(
async fn status_change(
&self,
ctx: &'a Context<'a>,
#[graphql(desc = "The ID of the transaction")] id: TransactionId,
Expand Down Expand Up @@ -721,7 +721,7 @@ impl TxStatusSubscription {
}

#[graphql(name = "alpha__preconfirmations")]
async fn preconfirmations<'a>(
async fn preconfirmations(
&self,
ctx: &'a Context<'a>,
) -> async_graphql::Result<
Expand All @@ -745,10 +745,10 @@ impl TxStatusSubscription {

/// Submits transaction to the `TxPool` and await either success or failure.
#[graphql(complexity = "query_costs().submit_and_await + child_complexity")]
async fn submit_and_await<'a>(
async fn submit_and_await(
&self,
ctx: &'a Context<'a>,
tx: HexString,
tx: HexString<'a>,
estimate_predicates: Option<bool>,
) -> async_graphql::Result<
impl Stream<Item = async_graphql::Result<TransactionStatus>> + 'a + use<'a>,
Expand All @@ -767,10 +767,10 @@ impl TxStatusSubscription {
/// Compared to the `submitAndAwait`, the stream also contains
/// `SubmittedStatus` and potentially preconfirmation as an intermediate state.
#[graphql(complexity = "query_costs().submit_and_await + child_complexity")]
async fn submit_and_await_status<'a>(
async fn submit_and_await_status(
&self,
ctx: &'a Context<'a>,
tx: HexString,
tx: HexString<'a>,
estimate_predicates: Option<bool>,
include_preconfirmation: Option<bool>,
) -> async_graphql::Result<
Expand All @@ -788,7 +788,7 @@ impl TxStatusSubscription {

async fn submit_and_await_status<'a>(
ctx: &'a Context<'a>,
tx: HexString,
tx: HexString<'a>,
estimate_predicates: bool,
include_preconfirmation: bool,
) -> async_graphql::Result<
Expand Down Expand Up @@ -884,8 +884,8 @@ pub mod schema_types {
// signature verification. They provide a mocked version of the predicate that
// returns `true` even if the signature doesn't match.
pub predicate_address: Address,
pub predicate: HexString,
pub predicate_data: HexString,
pub predicate: HexString<'static>,
pub predicate_data: HexString<'static>,
}

#[derive(async_graphql::InputObject)]
Expand Down
Loading