Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: bytes_to_fields requiring only 1 generic param #9417

Merged
Merged
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
41 changes: 16 additions & 25 deletions noir-projects/aztec-nr/aztec/src/utils/bytes.nr
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
use std::static_assert;

// Converts the input bytes into an array of fields. A Field is ~254 bits meaning that each field can store 31 bytes.
// This implies that M = ceil(N / 31).
//
// Each 31 byte chunk is converted into a Field as if the chunk was the Field's big endian representation. If the last chunk
// is less than 31 bytes long, then only the relevant bytes are conisdered.
// For example, [1, 10, 3] is encoded as [1 * 256^2 + 10 * 256 + 3]
pub fn bytes_to_fields<let N: u32, let M: u32>(input: [u8; N]) -> [Field; M] {
static_assert(N <= 31 * M, "Bytes do not fit into fields");
let mut dst = [0; M];
pub fn bytes_to_fields<let N: u32>(input: [u8; N]) -> [Field; (N + 30) / 31] {
let mut dst = [0; (N + 30) / 31];

for dst_index in 0..M {
for dst_index in 0..((N + 30) / 31) {
let mut field_value = 0;

for i in 0..31 {
Expand Down Expand Up @@ -78,15 +76,15 @@ mod test {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31,
];
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I removed turbofish syntax in all the tests below as it makes it more explicit what info a dev needs to provide manually and what the compiler is able to derive on its own.

(A dev needs to provide only the number of bytes when converting from fields to bytes as the fields might contain less than num_fields*31 bytes of info).

let output = bytes_to_fields::<31, 1>(input);
let output = bytes_to_fields(input);

assert_eq(output[0], 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f);
}

#[test]
fn test_1_field_to_bytes() {
let input = [0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f];
let output = fields_to_bytes::<31, 1>(input);
let output: [u8; 31] = fields_to_bytes(input);

assert_eq(
output,
Expand All @@ -100,7 +98,7 @@ mod test {
#[test]
fn test_3_small_fields_to_bytes() {
let input = [1, 2, 3];
let output = fields_to_bytes::<93, 3>(input);
let output: [u8; 93] = fields_to_bytes(input);

// Each field should occupy 31 bytes with the non-zero value being placed in the last one.
assert_eq(
Expand All @@ -117,7 +115,7 @@ mod test {
#[test]
fn test_3_small_fields_to_less_bytes() {
let input = [1, 2, 3];
let output = fields_to_bytes::<63, 3>(input);
let output: [u8; 63] = fields_to_bytes(input);

// First 2 fields should occupy 31 bytes with the non-zero value being placed in the last one while the last
// field should occupy 1 byte. There is not information destruction here because the last field fits into
Expand All @@ -139,7 +137,7 @@ mod test {
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
];
let output = bytes_to_fields::<59, 2>(input);
let output = bytes_to_fields(input);

assert_eq(output[0], 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f);
assert_eq(output[1], 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b);
Expand All @@ -151,7 +149,7 @@ mod test {
0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f,
0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b,
];
let output = fields_to_bytes::<62, 2>(input);
let output: [u8; 62] = fields_to_bytes(input);

assert_eq(
output,
Expand All @@ -165,8 +163,8 @@ mod test {

#[test]
fn test_large_random_input_to_fields_and_back(input: [u8; 128]) {
let output = bytes_to_fields::<128, 5>(input);
let input_back = fields_to_bytes::<128, 5>(output);
let output = bytes_to_fields(input);
let input_back: [u8; 128] = fields_to_bytes(output);

assert_eq(input, input_back);
}
Expand All @@ -193,37 +191,30 @@ mod test {
+ input6[i] as Field;
}

let output = fields_to_bytes::<155, 5>(input);
let input_back = bytes_to_fields::<155, 5>(output);
let output: [u8; 155] = fields_to_bytes(input);
let input_back = bytes_to_fields(output);

assert_eq(input, input_back);
}

#[test(should_fail_with = "Argument is false")]
fn test_too_few_destination_fields() {
// This should fail because we need 2 fields to store 32 bytes but we only provide 1.
let input = [0 as u8; 32];
let _ignored_result = bytes_to_fields::<32, 1>(input);
}

#[test(should_fail_with = "Field does not fit into remaining bytes")]
fn test_too_few_destination_bytes() {
// We should get an error here because first field gets converted to 31 bytes and the second field needs
// at least 2 bytes but we provide it with 1.
let input = [1, 256];
let _ignored_result = fields_to_bytes::<32, 2>(input);
let _ignored_result: [u8; 32] = fields_to_bytes(input);
}

#[test(should_fail_with = "call to assert_max_bit_size")]
fn test_fields_to_bytes_value_too_large() {
let input = [2.pow_32(248)];
let _ignored_result = fields_to_bytes::<31, 1>(input);
let _ignored_result: [u8; 31] = fields_to_bytes(input);
}

#[test]
fn test_fields_to_bytes_max_value() {
let input = [2.pow_32(248) - 1];
let result = fields_to_bytes::<31, 1>(input);
let result: [u8; 31] = fields_to_bytes(input);

// We check that all the bytes were set to max value (255)
for i in 0..31 {
Expand Down
Loading