Skip to content

Commit

Permalink
feat!: streaming decoder (#1564)
Browse files Browse the repository at this point in the history
- Related to #1432

# Release notes

In this release, we:

- Changed `ABIDecoder` methods to take `std::io::Read` instead of
`&[u8]`, allowing it to be used in a streaming manner.

# Summary

`ABIDecoder` methods take `bytes: impl std::io::Read` instead of `bytes:
&[u8]`. This allows decoding abi types without having to know the size
in advance. This is particularly useful when reading them directly from
VM memory, which will be used by the indexer after
FuelLabs/fuel-core#2491 is done.

# Breaking Changes

`ABIDecoder` methods take `bytes: impl std::io::Read` instead of `bytes:
&[u8]`. Callers using arrays or `Vec` must change the argument from
`&value` to `value.as_slice()`.

# Checklist

- [x] All **changes** are **covered** by **tests** (or not applicable)
- [x] All **changes** are **documented** (or not applicable)
- [x] I **reviewed** the **entire PR** myself (preferably, on GH UI)
- [x] I **described** all **Breaking Changes** (or there's none)

---------

Co-authored-by: hal3e <git@hal3e.io>
Co-authored-by: Ahmed Sagdati <37515857+segfault-magnet@users.noreply.github.com>
Co-authored-by: segfault-magnet <ahmed.sagdati.ets@gmail.com>
  • Loading branch information
4 people authored Jan 17, 2025
1 parent 7432468 commit 1570dc0
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 353 deletions.
16 changes: 8 additions & 8 deletions e2e/tests/debug_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ async fn can_debug_single_call_tx() -> Result<()> {
assert_eq!(
decoder.decode_fn_args(
&call_description.decode_fn_selector().unwrap(),
&call_description.encoded_args
call_description.encoded_args.as_slice()
)?,
vec!["AllStruct { some_struct: SomeStruct { field: 2, field_2: true } }"]
);
Expand Down Expand Up @@ -115,7 +115,7 @@ async fn can_debug_single_call_tx() -> Result<()> {
assert_eq!(
decoder.decode_fn_args(
&call_description.decode_fn_selector().unwrap(),
&call_description.encoded_args
call_description.encoded_args.as_slice()
)?,
vec!["AllStruct { some_struct: SomeStruct { field: 2, field_2: true } }"]
);
Expand Down Expand Up @@ -214,7 +214,7 @@ async fn can_debug_multi_call_tx() -> Result<()> {
assert_eq!(
decoder.decode_fn_args(
&call_description.decode_fn_selector().unwrap(),
&call_description.encoded_args
call_description.encoded_args.as_slice()
)?,
vec!["AllStruct { some_struct: SomeStruct { field: 2, field_2: true } }"]
);
Expand All @@ -229,7 +229,7 @@ async fn can_debug_multi_call_tx() -> Result<()> {
assert!(call_description.gas_forwarded.is_none());

assert_eq!(
decoder.decode_fn_args(&fn_selector, &call_description.encoded_args)?,
decoder.decode_fn_args(&fn_selector, call_description.encoded_args.as_slice())?,
vec!["AllStruct { some_struct: SomeStruct { field: 2, field_2: true } }", "MemoryAddress { contract_id: std::contract_id::ContractId { bits: Bits256([77, 127, 224, 17, 182, 42, 211, 241, 46, 156, 74, 204, 31, 156, 188, 77, 183, 63, 55, 80, 119, 142, 192, 75, 130, 205, 208, 253, 25, 104, 22, 171]) }, function_selector: 123, function_data: 456 }"]
);
}
Expand Down Expand Up @@ -286,7 +286,7 @@ async fn can_debug_multi_call_tx() -> Result<()> {
assert_eq!(
decoder.decode_fn_args(
&call_description.decode_fn_selector().unwrap(),
&call_description.encoded_args
call_description.encoded_args.as_slice()
)?,
vec!["AllStruct { some_struct: SomeStruct { field: 2, field_2: true } }"]
);
Expand All @@ -303,7 +303,7 @@ async fn can_debug_multi_call_tx() -> Result<()> {
assert_eq!(call_description.gas_forwarded, Some(25));

assert_eq!(
decoder.decode_fn_args(&call_description.decode_fn_selector().unwrap(), &call_description.encoded_args)?,
decoder.decode_fn_args(&call_description.decode_fn_selector().unwrap(), call_description.encoded_args.as_slice())?,
vec!["AllStruct { some_struct: SomeStruct { field: 2, field_2: true } }", "MemoryAddress { contract_id: std::contract_id::ContractId { bits: Bits256([77, 127, 224, 17, 182, 42, 211, 241, 46, 156, 74, 204, 31, 156, 188, 77, 183, 63, 55, 80, 119, 142, 192, 75, 130, 205, 208, 253, 25, 104, 22, 171]) }, function_selector: 123, function_data: 456 }"]
);
}
Expand Down Expand Up @@ -345,7 +345,7 @@ async fn can_debug_sway_script() -> Result<()> {
};

assert_eq!(
decoder.decode_fn_args("main", &desc.data)?,
decoder.decode_fn_args("main", desc.data.as_slice())?,
vec!["MyStruct { number: 10, boolean: false }"]
);

Expand Down Expand Up @@ -455,7 +455,7 @@ async fn can_detect_a_loader_script_w_data_section() -> Result<()> {
)?)?;

assert_eq!(
decoder.decode_fn_args("main", &script.data)?,
decoder.decode_fn_args("main", script.data.as_slice())?,
vec!["MyStruct { number: 10, boolean: false }"]
);

Expand Down
3 changes: 2 additions & 1 deletion examples/contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1198,7 +1198,8 @@ mod tests {

let call = &calls[0];
let fn_selector = call.decode_fn_selector()?;
let decoded_args = abi_formatter.decode_fn_args(&fn_selector, &call.encoded_args)?;
let decoded_args =
abi_formatter.decode_fn_args(&fn_selector, call.encoded_args.as_slice())?;

eprintln!(
"The script called: {fn_selector}({})",
Expand Down
8 changes: 4 additions & 4 deletions examples/debugging/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ mod tests {

assert_eq!(
format!("{expected_struct:?}"),
decoder.decode_as_debug_str(&param_type, &[0, 0, 0, 0, 0, 0, 0, 123])?
decoder.decode_as_debug_str(&param_type, [0, 0, 0, 0, 0, 0, 0, 123].as_slice())?
);
}
{
Expand All @@ -83,7 +83,7 @@ mod tests {

assert_eq!(
format!("{expected_struct:?}"),
decoder.decode_as_debug_str(&param_type, &[97, 98, 99])?
decoder.decode_as_debug_str(&param_type, [97, 98, 99].as_slice())?
);
}
{
Expand All @@ -97,7 +97,7 @@ mod tests {
format!("{expected_enum:?}"),
decoder.decode_as_debug_str(
&param_type,
&[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 10]
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 10].as_slice()
)?
);
}
Expand All @@ -117,7 +117,7 @@ mod tests {

assert_eq!(
format!("{expected_u8}"),
decoder.decode_as_debug_str(&param_type, &[1])?
decoder.decode_as_debug_str(&param_type, [1].as_slice())?
);
}

Expand Down
18 changes: 12 additions & 6 deletions packages/fuels-core/src/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ mod function_selector;
mod logs;
mod utils;

use std::io::Read;

pub use abi_decoder::*;
pub use abi_encoder::*;
pub use abi_formatter::*;
Expand All @@ -17,7 +19,7 @@ use crate::{
};

/// Decodes `bytes` into type `T` following the schema defined by T's `Parameterize` impl
pub fn try_from_bytes<T>(bytes: &[u8], decoder_config: DecoderConfig) -> Result<T>
pub fn try_from_bytes<T>(bytes: impl Read, decoder_config: DecoderConfig) -> Result<T>
where
T: Parameterize + Tokenizable,
{
Expand All @@ -41,13 +43,16 @@ mod tests {
macro_rules! test_decode {
($($for_type: ident),*) => {
$(assert_eq!(
try_from_bytes::<$for_type>(&bytes, DecoderConfig::default())?,
try_from_bytes::<$for_type>(bytes.as_slice(), DecoderConfig::default())?,
$for_type::MAX
);)*
};
}

assert!(try_from_bytes::<bool>(&bytes, DecoderConfig::default())?);
assert!(try_from_bytes::<bool>(
bytes.as_slice(),
DecoderConfig::default()
)?);

test_decode!(u8, u16, u32, u64);

Expand All @@ -58,7 +63,8 @@ mod tests {
fn convert_bytes_into_tuple() -> Result<()> {
let tuple_in_bytes = [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2];

let the_tuple: (u64, u32) = try_from_bytes(&tuple_in_bytes, DecoderConfig::default())?;
let the_tuple: (u64, u32) =
try_from_bytes(tuple_in_bytes.as_slice(), DecoderConfig::default())?;

assert_eq!(the_tuple, (1, 2));

Expand All @@ -72,7 +78,7 @@ mod tests {
macro_rules! test_decode {
($($for_type: ident),*) => {
$(assert_eq!(
try_from_bytes::<$for_type>(&bytes, DecoderConfig::default())?,
try_from_bytes::<$for_type>(bytes.as_slice(), DecoderConfig::default())?,
$for_type::new(bytes.as_slice().try_into()?)
);)*
};
Expand Down Expand Up @@ -109,7 +115,7 @@ mod tests {
.unwrap();

// when
let decoded = try_from_bytes::<Test>(&encoded, DecoderConfig::default()).unwrap();
let decoded = try_from_bytes::<Test>(encoded.as_slice(), DecoderConfig::default()).unwrap();

// then
assert_eq!(decoded, input);
Expand Down
Loading

0 comments on commit 1570dc0

Please sign in to comment.