Skip to content

Commit

Permalink
Implement TryFrom<Bytes> for b256 (#6958)
Browse files Browse the repository at this point in the history
## Description

`impl From<Bytes> for b256` has been removed and replaced with `impl
TryFrom<Bytes> for b256` increasing saftey when converting between the
two types. `impl Into<Bytes> for b256` and `impl TryInto<b256> for
Bytes` have also been added.

This PR breaks the following implementation:

```sway
let mut my_bytes = Bytes::new();
my_bytes.push(1u8);

let result: b256 = b256::from(my_bytes);
```

The implementation should now be:

```sway
let mut my_bytes = Bytes::new();
my_bytes.push(1u8);

// Option 1
let result_1: Option<b256> = b256::try_from(my_bytes);
// Option 2
let result_2 = my_bytes.try_into();
```

Closes #6994.

## Checklist

- [x] I have linked to any relevant issues.
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [x] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [x] If my change requires substantial documentation changes, I have
[requested support from the DevRel
team](https://github.com/FuelLabs/devrel-requests/issues/new/choose)
- [x] I have added tests that prove my fix is effective or that my
feature works.
- [x] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [x] I have requested a review from the relevant team or maintainers.

---------

Co-authored-by: Igor Rončević <[email protected]>
  • Loading branch information
bitzoic and ironcev authored Mar 5, 2025
1 parent ba46576 commit be5ecbd
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 1 deletion.
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,10 @@ jobs:
run: cargo run --locked --release -p forc -- build --experimental partial_eq --release --locked --path ./test/src/sdk-harness
- name: Cargo Test sway-lib-std - Experimental Feature 'partial_eq'
run: cargo test --locked --release --manifest-path ./test/src/sdk-harness/Cargo.toml -- --nocapture
- name: Build All Tests - Experimental Feature 'try_from_bytes_for_b256'
run: cargo run --locked --release -p forc -- build --experimental try_from_bytes_for_b256 --release --locked --path ./test/src/sdk-harness
- name: Cargo Test sway-lib-std - Experimental Feature 'try_from_bytes_for_b256'
run: cargo test --locked --release --manifest-path ./test/src/sdk-harness/Cargo.toml -- --nocapture

forc-run-benchmarks:
runs-on: buildjet-4vcpu-ubuntu-2204
Expand Down Expand Up @@ -555,6 +559,8 @@ jobs:
run: forc build --experimental error_type --path sway-lib-core && forc test --experimental error_type --path sway-lib-core
- name: Run Core Unit Tests - Experimental feature 'partial_eq'
run: forc build --experimental partial_eq --path sway-lib-core && forc test --experimental partial_eq --path sway-lib-core
- name: Run Core Unit Tests - Experimental feature 'try_from_bytes_for_b256'
run: forc build --experimental try_from_bytes_for_b256 --path sway-lib-core && forc test --experimental try_from_bytes_for_b256 --path sway-lib-core
- name: Run Std Unit Tests
run: forc build --path sway-lib-std && forc test --path sway-lib-std
- name: Run Std Unit Tests - Experimental feature 'storage_domains'
Expand All @@ -563,6 +569,8 @@ jobs:
run: forc build --experimental error_type --path sway-lib-std && forc test --experimental error_type --path sway-lib-std
- name: Run Std Unit Tests - Experimental feature 'partial_eq'
run: forc build --experimental partial_eq --path sway-lib-std && forc test --experimental partial_eq --path sway-lib-std
- name: Run Std Unit Tests - Experimental feature 'try_from_bytes_for_b256'
run: forc build --experimental try_from_bytes_for_b256 --path sway-lib-std && forc test --experimental try_from_bytes_for_b256 --path sway-lib-std
- name: Run In Language Unit Tests
run: forc build --path test/src/in_language_tests && forc test --path test/src/in_language_tests
- name: Run In Language Unit Tests - Experimental feature 'storage_domains'
Expand All @@ -571,6 +579,8 @@ jobs:
run: forc build --experimental error_type --path test/src/in_language_tests && forc test --experimental error_type --path test/src/in_language_tests
- name: Run In Language Unit Tests - Experimental feature 'partial_eq'
run: forc build --experimental partial_eq --path test/src/in_language_tests && forc test --experimental partial_eq --path test/src/in_language_tests
- name: Run In Language Unit Tests - Experimental feature 'try_from_bytes_for_b256'
run: forc build --experimental try_from_bytes_for_b256 --path test/src/in_language_tests && forc test --experimental try_from_bytes_for_b256 --path test/src/in_language_tests

forc-pkg-fuels-deps-check:
runs-on: buildjet-4vcpu-ubuntu-2204
Expand Down
2 changes: 2 additions & 0 deletions sway-features/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ features! {
"https://github.com/FuelLabs/sway/issues/6883",
const_generics = false,
"https://github.com/FuelLabs/sway/issues/6860",
try_from_bytes_for_b256 = false,
"https://github.com/FuelLabs/sway/issues/6994",
}

#[derive(Clone, Debug, Default, Parser)]
Expand Down
40 changes: 40 additions & 0 deletions sway-lib-std/src/bytes.sw
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,7 @@ impl From<b256> for Bytes {
}
}

#[cfg(experimental_try_from_bytes_for_b256 = false)]
impl From<Bytes> for b256 {
// NOTE: this cas be lossy! Added here as the From trait currently requires it,
// but the conversion from `Bytes` ->`b256` should be implemented as
Expand All @@ -1019,6 +1020,45 @@ impl From<Bytes> for b256 {
}
}

#[cfg(experimental_try_from_bytes_for_b256 = true)]
impl TryFrom<Bytes> for b256 {
fn try_from(bytes: Bytes) -> Option<Self> {
if bytes.len() != 32 {
return None;
}
let mut value = 0x0000000000000000000000000000000000000000000000000000000000000000;
let ptr = __addr_of(value);
bytes.buf.ptr().copy_to::<b256>(ptr, 1);

Some(value)
}
}

impl Into<Bytes> for b256 {
fn into(self) -> Bytes {
// Artificially create bytes with capacity and len
let mut bytes = Bytes::with_capacity(32);
bytes.len = 32;
// Copy bytes from contract_id into the buffer of the target bytes
__addr_of(self).copy_bytes_to(bytes.buf.ptr, 32);

bytes
}
}

impl TryInto<b256> for Bytes {
fn try_into(self) -> Option<b256> {
if self.len != 32 {
return None;
}
let mut value = 0x0000000000000000000000000000000000000000000000000000000000000000;
let ptr = __addr_of(value);
self.buf.ptr().copy_to::<b256>(ptr, 1);

Some(value)
}
}

impl From<raw_slice> for Bytes {
/// Creates a `Bytes` from a `raw_slice`.
///
Expand Down
33 changes: 33 additions & 0 deletions sway-lib-std/src/bytes_conversions/b256.sw
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,41 @@ impl b256 {
/// assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20);
/// }
/// ```
#[cfg(experimental_try_from_bytes_for_b256 = false)]
pub fn from_be_bytes(bytes: Bytes) -> Self {
assert(bytes.len() == 32);
bytes.into()
}

/// Converts a sequence of big-endian bytes to a `b256`.
///
/// # Arguments
///
/// * `bytes`: [Bytes] - The 32 bytes that compose the `b256`.
///
/// # Returns
///
/// * [b256] - The resulting `b256` value.
///
/// # Examples
///
/// ```sway
/// fn foo() {
/// let mut bytes = Bytes::with_capacity(32);
/// let mut i: u8 = 0;
/// while i < 32_u8 {
/// bytes.push(i + 1);
/// i += 1_u8;
/// }
///
/// let x = b256::from_be_bytes(bytes);
///
/// assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20);
/// }
/// ```
#[cfg(experimental_try_from_bytes_for_b256 = true)]
pub fn from_be_bytes(bytes: Bytes) -> Self {
assert(bytes.len() == 32);
bytes.try_into().unwrap()
}
}
36 changes: 36 additions & 0 deletions sway-lib-std/src/bytes_conversions/u256.sw
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,47 @@ impl u256 {
/// assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20_u256);
/// }
/// ```
#[cfg(experimental_try_from_bytes_for_b256 = false)]
pub fn from_be_bytes(bytes: Bytes) -> Self {
assert(bytes.len() == 32);
let b: b256 = bytes.into();
asm(r1: b) {
r1: u256
}
}

/// Converts a sequence of big-endian bytes to a `u256`.
///
/// # Arguments
///
/// * `bytes`: [Bytes] - The 32 bytes that compose the `u256`.
///
/// # Returns
///
/// * [u256] - The resulting `u256` value.
///
/// # Examples
///
/// ```sway
/// fn foo() {
/// let mut bytes = Bytes::with_capacity(32);
/// let mut i: u8 = 0;
/// while i < 32_u8 {
/// bytes.push(i + 1);
/// i += 1_u8;
/// }
///
/// let x = u256::from_be_bytes(bytes);
///
/// assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20_u256);
/// }
/// ```
#[cfg(experimental_try_from_bytes_for_b256 = true)]
pub fn from_be_bytes(bytes: Bytes) -> Self {
assert(bytes.len() == 32);
let b: b256 = bytes.try_into().unwrap();
asm(r1: b) {
r1: u256
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ warning
|
1 | predicate;
2 | #[cfg(c)] a
| --- Unexpected attribute value: "c" for attribute: "cfg" expected value "target" or "program_type" or "experimental_new_encoding" or "experimental_storage_domains" or "experimental_references" or "experimental_error_type" or "experimental_partial_eq" or "experimental_const_generics"
| --- Unexpected attribute value: "c" for attribute: "cfg" expected value "target" or "program_type" or "experimental_new_encoding" or "experimental_storage_domains" or "experimental_references" or "experimental_error_type" or "experimental_partial_eq" or "experimental_const_generics" or "experimental_try_from_bytes_for_b256"
|
____

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,7 @@ fn bytes_from_b256() {
}

#[test]
#[cfg(experimental_try_from_bytes_for_b256 = false)]
fn bytes_into_b256() {
let mut initial_bytes = Bytes::with_capacity(32);

Expand All @@ -881,6 +882,44 @@ fn bytes_into_b256() {
}

#[test]
#[cfg(experimental_try_from_bytes_for_b256 = true)]
fn bytes_try_into_b256() {
let mut initial_bytes = Bytes::with_capacity(32);

let mut i = 0;
while i < 32 {
// 0x33 is 51 in decimal
initial_bytes.push(51u8);
i += 1;
}

let value: b256 = initial_bytes.try_into().unwrap();
let expected: b256 = 0x3333333333333333333333333333333333333333333333333333333333333333;

assert(value == expected);

let empty_bytes = Bytes::new();
let empty_result: Option<b256> = empty_bytes.try_into();
assert(empty_result.is_none());

let mut small_bytes = Bytes::new();
small_bytes.push(1u8);
let small_result: Option<b256> = small_bytes.try_into();
assert(small_result.is_none());

let mut large_bytes = Bytes::new();
let mut i = 0;
while i < 33 {
// 0x33 is 51 in decimal
large_bytes.push(51u8);
i += 1;
}
let large_result: Option<b256> = large_bytes.try_into();
assert(large_result.is_none());
}

#[test]
#[cfg(experimental_try_from_bytes_for_b256 = false)]
fn bytes_b256_from() {
let control = 0x3333333333333333333333333333333333333333333333333333333333333333;
let mut bytes = Bytes::with_capacity(32);
Expand All @@ -897,6 +936,42 @@ fn bytes_b256_from() {
assert(result_b256 == control);
}

#[test]
#[cfg(experimental_try_from_bytes_for_b256 = true)]
fn bytes_b256_try_from() {
let control = 0x3333333333333333333333333333333333333333333333333333333333333333;
let mut bytes = Bytes::with_capacity(32);

let mut i = 0;
while i < 32 {
// 0x33 is 51 in decimal
bytes.push(51u8);
i += 1;
}

let result_b256: b256 = b256::try_from(bytes).unwrap();
assert(result_b256 == control);

let empty_bytes = Bytes::new();
let empty_result = b256::try_from(empty_bytes);
assert(empty_result.is_none());

let mut small_bytes = Bytes::new();
small_bytes.push(1u8);
let small_result = b256::try_from(small_bytes);
assert(small_result.is_none());

let mut large_bytes = Bytes::new();
let mut i = 0;
while i < 33 {
// 0x33 is 51 in decimal
large_bytes.push(51u8);
i += 1;
}
let large_result = b256::try_from(large_bytes);
assert(large_result.is_none());
}

#[test]
fn bytes_b256_into() {
let initial = 0x3333333333333333333333333333333333333333333333333333333333333333;
Expand Down

0 comments on commit be5ecbd

Please sign in to comment.