From d56bf49434e5cc5bbaff9e7d86b8161640bf55c8 Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 17 Sep 2024 09:10:09 +0000 Subject: [PATCH 01/17] feat: bytes to fields and back --- .../aztec-nr/aztec/src/utils/byte_array.nr | 21 +++++++++++++++++++ noir-projects/aztec-nr/aztec/src/utils/mod.nr | 1 + 2 files changed, 22 insertions(+) create mode 100644 noir-projects/aztec-nr/aztec/src/utils/byte_array.nr diff --git a/noir-projects/aztec-nr/aztec/src/utils/byte_array.nr b/noir-projects/aztec-nr/aztec/src/utils/byte_array.nr new file mode 100644 index 00000000000..7f32314124b --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/utils/byte_array.nr @@ -0,0 +1,21 @@ +// 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). +pub fn bytes_to_fields(input: [u8; N]) -> [Field; M] { + let mut output = [0; M]; + + for field_index in 0..M { + let mut field_value = 0; + + for i in 0..31 { + let byte_index = field_index * 31 + i; + if byte_index < N { + // Shift the existing value left by 8 bits and add the new byte + field_value = field_value * 256 + input[byte_index] as Field; + } + } + + output[field_index] = field_value; + } + + output +} diff --git a/noir-projects/aztec-nr/aztec/src/utils/mod.nr b/noir-projects/aztec-nr/aztec/src/utils/mod.nr index 2da0e5b639e..dba99592fe5 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/mod.nr @@ -1,3 +1,4 @@ +mod byte_array; mod collapse_array; mod comparison; mod point; From b26fce3007b8f34e86fd1db120b719d7acc8c5a7 Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 17 Sep 2024 09:43:45 +0000 Subject: [PATCH 02/17] WIP --- .../aztec-nr/aztec/src/utils/byte_array.nr | 21 ---- .../aztec-nr/aztec/src/utils/bytes.nr | 108 ++++++++++++++++++ noir-projects/aztec-nr/aztec/src/utils/mod.nr | 3 +- 3 files changed, 110 insertions(+), 22 deletions(-) delete mode 100644 noir-projects/aztec-nr/aztec/src/utils/byte_array.nr create mode 100644 noir-projects/aztec-nr/aztec/src/utils/bytes.nr diff --git a/noir-projects/aztec-nr/aztec/src/utils/byte_array.nr b/noir-projects/aztec-nr/aztec/src/utils/byte_array.nr deleted file mode 100644 index 7f32314124b..00000000000 --- a/noir-projects/aztec-nr/aztec/src/utils/byte_array.nr +++ /dev/null @@ -1,21 +0,0 @@ -// 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). -pub fn bytes_to_fields(input: [u8; N]) -> [Field; M] { - let mut output = [0; M]; - - for field_index in 0..M { - let mut field_value = 0; - - for i in 0..31 { - let byte_index = field_index * 31 + i; - if byte_index < N { - // Shift the existing value left by 8 bits and add the new byte - field_value = field_value * 256 + input[byte_index] as Field; - } - } - - output[field_index] = field_value; - } - - output -} diff --git a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr new file mode 100644 index 00000000000..7ac0570c2c1 --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr @@ -0,0 +1,108 @@ +// 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). +pub fn bytes_to_fields(input: [u8; N]) -> [Field; M] { + let mut output = [0; M]; + + for field_index in 0..M { + let mut field_value = 0; + + for i in 0..31 { + let byte_index = field_index * 31 + i; + if byte_index < N { + // Shift the existing value left by 8 bits and add the new byte + field_value = field_value * 256 + input[byte_index] as Field; + } + } + + output[field_index] = field_value; + } + + output +} + +// Converts the input array of fields into bytes. Each field of input has to contain only 31 bytes. +pub fn fields_to_bytes(input: [Field; M]) -> [u8; N] { + let mut output = [0; N]; + + for field_index in 0..M { + // We convert the field to 31 bytes. Normally a field is 254 bits, but we only used 248 bits + // in `bytes_to_fields` so we can safely convert it to 31 bytes. + let field_value_bytes: [u8; 31] = input[field_index].to_be_bytes(); + + for i in 0..31 { + let byte_index = field_index * 31 + i; + output[byte_index] = field_value_bytes[i]; + } + } + + output +} + +mod test { + use crate::utils::bytes::{bytes_to_fields, fields_to_bytes}; + + #[test] + fn test_bytes_to_1_field() { + let input = [ + 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 + ]; + let output = bytes_to_fields::<31, 1>(input); + + assert_eq(output[0], 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f); + } + + #[test] + fn test_1_field_to_bytes() { + let input = [0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f]; + let output = fields_to_bytes::<31, 1>(input); + + assert_eq( + output, [ + 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 + ] + ); + } + + #[test] + fn test_bytes_to_2_fields() { + let input = [ + 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, 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); + + assert_eq(output[0], 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f); + assert_eq(output[1], 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b); + } + + #[test] + fn test_2_fields_to_bytes() { + let input = [ + 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f, 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b + ]; + let output = fields_to_bytes::<62, 2>(input); + + assert_eq( + output, [ + 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, 0, 0, 0, 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 + ] + ); + } + + #[test] + fn test_large_random_input_to_fields_and_back() { + let input = [ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, + 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, + 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, + 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80 + ]; + let output = bytes_to_fields::<128, 5>(input); + let input_back = fields_to_bytes::<155, 5>(output); + + std::println(input_back); + } +} diff --git a/noir-projects/aztec-nr/aztec/src/utils/mod.nr b/noir-projects/aztec-nr/aztec/src/utils/mod.nr index dba99592fe5..ae469c45465 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/mod.nr @@ -1,4 +1,4 @@ -mod byte_array; +mod bytes; mod collapse_array; mod comparison; mod point; @@ -6,3 +6,4 @@ mod test; mod to_bytes; pub use crate::utils::collapse_array::collapse_array; +pub use crate::utils::bytes::{bytes_to_fields, fields_to_bytes}; From 916c0696c0b012b09daa11d1120793bc68a82c91 Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 17 Sep 2024 09:57:03 +0000 Subject: [PATCH 03/17] WIP --- noir-projects/aztec-nr/aztec/src/utils/bytes.nr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr index 7ac0570c2c1..818ab664a7b 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr @@ -101,8 +101,8 @@ mod test { 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80 ]; let output = bytes_to_fields::<128, 5>(input); - let input_back = fields_to_bytes::<155, 5>(output); + let input_back = fields_to_bytes::<128, 5>(output); - std::println(input_back); + assert_eq(input, input_back); } } From cb6bf5ec29b9aa029fdcbc76068b70194abf22e1 Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 17 Sep 2024 10:32:09 +0000 Subject: [PATCH 04/17] WIP --- noir-projects/aztec-nr/aztec/src/utils/bytes.nr | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr index 818ab664a7b..7e5f0d8257b 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr @@ -29,9 +29,18 @@ pub fn fields_to_bytes(input: [Field; M]) -> [u8; N] { // in `bytes_to_fields` so we can safely convert it to 31 bytes. let field_value_bytes: [u8; 31] = input[field_index].to_be_bytes(); + let remaining_bytes = N - field_index * 31; + let mut field_value_bytes_start_index = 0; + if remaining_bytes < 31 { + // If the remaining bytes are less than 31, we only copy the remaining bytes + field_value_bytes_start_index = 31 - remaining_bytes; + } + for i in 0..31 { let byte_index = field_index * 31 + i; - output[byte_index] = field_value_bytes[i]; + if byte_index < N { + output[byte_index] = field_value_bytes[field_value_bytes_start_index + i]; + } } } From d7c59d320da0a80d60e5b60de065ba6ef5bcbcd4 Mon Sep 17 00:00:00 2001 From: benesjan Date: Tue, 17 Sep 2024 10:42:58 +0000 Subject: [PATCH 05/17] WIP --- .../aztec-nr/aztec/src/utils/bytes.nr | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr index 7e5f0d8257b..b65a4efbb8c 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr @@ -1,7 +1,7 @@ // 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). pub fn bytes_to_fields(input: [u8; N]) -> [Field; M] { - let mut output = [0; M]; + let mut dst = [0; M]; for field_index in 0..M { let mut field_value = 0; @@ -14,37 +14,40 @@ pub fn bytes_to_fields(input: [u8; N]) -> [Field; M] { } } - output[field_index] = field_value; + dst[field_index] = field_value; } - output + dst } -// Converts the input array of fields into bytes. Each field of input has to contain only 31 bytes. +// Converts an input array of fields into bytes. Each field of input has to contain only 31 bytes. pub fn fields_to_bytes(input: [Field; M]) -> [u8; N] { - let mut output = [0; N]; + let mut dst = [0; N]; for field_index in 0..M { // We convert the field to 31 bytes. Normally a field is 254 bits, but we only used 248 bits // in `bytes_to_fields` so we can safely convert it to 31 bytes. - let field_value_bytes: [u8; 31] = input[field_index].to_be_bytes(); + let src: [u8; 31] = input[field_index].to_be_bytes(); + // Since some of the bytes might not be occupied (if the source value requiring less than 31 bytes), + // we have to compute the start index from which to copy. let remaining_bytes = N - field_index * 31; - let mut field_value_bytes_start_index = 0; - if remaining_bytes < 31 { + let src_start_index = if remaining_bytes < 31 { // If the remaining bytes are less than 31, we only copy the remaining bytes - field_value_bytes_start_index = 31 - remaining_bytes; - } + 31 - remaining_bytes + } else { + 0 + }; for i in 0..31 { let byte_index = field_index * 31 + i; if byte_index < N { - output[byte_index] = field_value_bytes[field_value_bytes_start_index + i]; + dst[byte_index] = src[src_start_index + i]; } } } - output + dst } mod test { From c70c8ea8eb8b1705cafc1aa9b478b5a175d86123 Mon Sep 17 00:00:00 2001 From: benesjan Date: Wed, 18 Sep 2024 07:54:28 +0000 Subject: [PATCH 06/17] asserting max value --- .../aztec-nr/aztec/src/utils/bytes.nr | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr index b65a4efbb8c..c77e8ee8b6a 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr @@ -25,9 +25,13 @@ pub fn fields_to_bytes(input: [Field; M]) -> [u8; N] { let mut dst = [0; N]; for field_index in 0..M { - // We convert the field to 31 bytes. Normally a field is 254 bits, but we only used 248 bits - // in `bytes_to_fields` so we can safely convert it to 31 bytes. - let src: [u8; 31] = input[field_index].to_be_bytes(); + let field = input[field_index]; + + // We expect that the field contains at most 31 bytes of information. + field.assert_max_bit_size(248); + + // Now we can safely convert the field to 31 bytes. + let src: [u8; 31] = field.to_be_bytes(); // Since some of the bytes might not be occupied (if the source value requiring less than 31 bytes), // we have to compute the start index from which to copy. @@ -117,4 +121,21 @@ mod test { assert_eq(input, input_back); } + + #[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); + } + + #[test] + fn test_fields_to_bytes_max_value() { + let input = [2.pow_32(248) - 1]; + let result = fields_to_bytes::<31, 1>(input); + + // We check that all the bytes were set to max value (255) + for i in 0..31 { + assert_eq(result[i], 255); + } + } } From 57acc4aac053da800cca3269ca17bc7ee69c7882 Mon Sep 17 00:00:00 2001 From: benesjan Date: Wed, 18 Sep 2024 08:02:54 +0000 Subject: [PATCH 07/17] linking issue --- noir-projects/aztec-nr/aztec/src/utils/bytes.nr | 1 + 1 file changed, 1 insertion(+) diff --git a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr index c77e8ee8b6a..dca220c7a60 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr @@ -21,6 +21,7 @@ pub fn bytes_to_fields(input: [u8; N]) -> [Field; M] { } // Converts an input array of fields into bytes. Each field of input has to contain only 31 bytes. +// TODO(#8618): Optimize for public use. pub fn fields_to_bytes(input: [Field; M]) -> [u8; N] { let mut dst = [0; N]; From 79b5d14231368052ed274c2637e550eeb6deb24a Mon Sep 17 00:00:00 2001 From: benesjan Date: Wed, 18 Sep 2024 08:28:07 +0000 Subject: [PATCH 08/17] random input fuzz test --- noir-projects/aztec-nr/aztec/src/utils/bytes.nr | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr index dca220c7a60..5d67abe3333 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr @@ -106,17 +106,7 @@ mod test { } #[test] - fn test_large_random_input_to_fields_and_back() { - let input = [ - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, - 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, - 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, - 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, - 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, - 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, - 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, - 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80 - ]; + 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); From a2979c267ad51b78c55ec8bda0483fb4c62f680b Mon Sep 17 00:00:00 2001 From: benesjan Date: Wed, 18 Sep 2024 09:57:35 +0000 Subject: [PATCH 09/17] test_large_random_input_to_bytes_and_back --- .../aztec-nr/aztec/src/utils/bytes.nr | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr index 5d67abe3333..757b630db94 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr @@ -113,6 +113,29 @@ mod test { assert_eq(input, input_back); } + // I need to get an array of random values lower than 2^248 on input and since there is no u248 type and modulo + // operation is not supported on a Field (to do field % 2^248), I will take multiple smaller values and combine + // them to get a value lower than 2^248. + #[test] + fn test_large_random_input_to_bytes_and_back( + input1: [u64; 5], + input2: [u64; 5], + input3: [u64; 5], + input4: [u32; 5], + input5: [u16; 5], + input6: [u8; 5] + ) { + let mut input = [0; 5]; + for i in 0..5 { + input[i] = (input1[i] as Field * 2.pow_32(184)) + (input2[i] as Field * 2.pow_32(120)) + (input3[i] as Field * 2.pow_32(56)) + (input4[i] as Field * 2.pow_32(24)) + (input5[i] as Field * 2.pow_32(8)) + input6[i] as Field; + } + + let output = fields_to_bytes::<155, 5>(input); + let input_back = bytes_to_fields::<155, 5>(output); + + assert_eq(input, input_back); + } + #[test(should_fail_with="call to assert_max_bit_size")] fn test_fields_to_bytes_value_too_large() { let input = [2.pow_32(248)]; From ffcb5e3b13c2a34cd13a09510c05d9f97bfc71d3 Mon Sep 17 00:00:00 2001 From: benesjan Date: Wed, 18 Sep 2024 10:31:10 +0000 Subject: [PATCH 10/17] test fields partially used --- .../aztec-nr/aztec/src/utils/bytes.nr | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr index 757b630db94..d43ae3634c4 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr @@ -80,6 +80,34 @@ mod test { ); } + #[test] + fn test_3_small_fields_to_bytes() { + let input = [1, 2, 3]; + let output = fields_to_bytes::<93, 3>(input); + + // Each field should occupy 31 bytes with the non-zero value being placed in the last one. + assert_eq( + output, [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3 + ] + ); + } + + #[test] + fn test_3_small_fields_to_less_bytes() { + let input = [1, 2, 3]; + let output = fields_to_bytes::<63, 3>(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 + // 1 byte. + assert_eq( + output, [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3 + ] + ); + } + #[test] fn test_bytes_to_2_fields() { let input = [ From e0b574fa9ecceff0c8b4a26b521a7aec92d3d253 Mon Sep 17 00:00:00 2001 From: benesjan Date: Wed, 18 Sep 2024 11:03:02 +0000 Subject: [PATCH 11/17] all non-zero bytes used check --- noir-projects/aztec-nr/aztec/src/utils/bytes.nr | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr index d43ae3634c4..3ae11ce82e5 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr @@ -44,6 +44,13 @@ pub fn fields_to_bytes(input: [Field; M]) -> [u8; N] { 0 }; + // Note: I tried combining this check with `assert_max_bit_size` above but `assert_max_bit_size` expects + // the argument to be a constant. Using comptime block to derive the number of bits also does not work + // because comptime is evaluated before generics. + for i in 0..src_start_index { + assert(src[i] == 0, "Field does not fit into remaining bytes"); + } + for i in 0..31 { let byte_index = field_index * 31 + i; if byte_index < N { @@ -164,6 +171,14 @@ mod test { assert_eq(input, input_back); } + #[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); + } + #[test(should_fail_with="call to assert_max_bit_size")] fn test_fields_to_bytes_value_too_large() { let input = [2.pow_32(248)]; From 30fd9ffc618c18699616bcbb1e781f7b75137ee9 Mon Sep 17 00:00:00 2001 From: benesjan Date: Wed, 18 Sep 2024 11:14:15 +0000 Subject: [PATCH 12/17] enough fields check --- noir-projects/aztec-nr/aztec/src/utils/bytes.nr | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr index 3ae11ce82e5..1fe15a67410 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr @@ -1,6 +1,7 @@ // 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). pub fn bytes_to_fields(input: [u8; N]) -> [Field; M] { + assert(N <= 31 * M, "Bytes do not fit into fields"); let mut dst = [0; M]; for field_index in 0..M { @@ -171,6 +172,13 @@ mod test { assert_eq(input, input_back); } + #[test(should_fail_with="Bytes do not fit into fields")] + 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 From c9025e0bcb01df24a379b10c52672cfbbbb76015 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 11 Oct 2024 17:00:51 +0000 Subject: [PATCH 13/17] fixes after update to new noir --- noir-projects/aztec-nr/aztec/src/utils/bytes.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr index 1fe15a67410..4d2ca15afd8 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr @@ -30,7 +30,7 @@ pub fn fields_to_bytes(input: [Field; M]) -> [u8; N] { let field = input[field_index]; // We expect that the field contains at most 31 bytes of information. - field.assert_max_bit_size(248); + field.assert_max_bit_size::<248>(); // Now we can safely convert the field to 31 bytes. let src: [u8; 31] = field.to_be_bytes(); From 8d23c1b94720a0c857f4875b6af88dd5395335a1 Mon Sep 17 00:00:00 2001 From: benesjan Date: Thu, 17 Oct 2024 10:33:40 +0000 Subject: [PATCH 14/17] static_assert --- noir-projects/aztec-nr/aztec/src/utils/bytes.nr | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr index 4d2ca15afd8..f10df2ab4f9 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr @@ -1,7 +1,9 @@ +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). pub fn bytes_to_fields(input: [u8; N]) -> [Field; M] { - assert(N <= 31 * M, "Bytes do not fit into fields"); + static_assert(N <= 31 * M, "Bytes do not fit into fields"); let mut dst = [0; M]; for field_index in 0..M { @@ -172,14 +174,14 @@ mod test { assert_eq(input, input_back); } - #[test(should_fail_with="Bytes do not fit into fields")] + #[test(should_fail_with = "Bytes do not fit into fields")] 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")] + #[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. @@ -187,7 +189,7 @@ mod test { let _ignored_result = fields_to_bytes::<32, 2>(input); } - #[test(should_fail_with="call to assert_max_bit_size")] + #[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); From 72155ed13f212a03697d8829162ed4b86eaf7f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Bene=C5=A1?= Date: Thu, 17 Oct 2024 12:36:58 +0200 Subject: [PATCH 15/17] Update noir-projects/aztec-nr/aztec/src/utils/bytes.nr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nicolás Venturo --- noir-projects/aztec-nr/aztec/src/utils/bytes.nr | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr index f10df2ab4f9..4f98dcf2392 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr @@ -2,6 +2,10 @@ 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(input: [u8; N]) -> [Field; M] { static_assert(N <= 31 * M, "Bytes do not fit into fields"); let mut dst = [0; M]; From 47bb1cbbfa76bd1a9c540a5b66f3fe4d6a650af5 Mon Sep 17 00:00:00 2001 From: benesjan Date: Thu, 17 Oct 2024 10:45:55 +0000 Subject: [PATCH 16/17] better index names --- .../aztec-nr/aztec/src/utils/bytes.nr | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr index 4f98dcf2392..9e7faa0159c 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr @@ -2,26 +2,26 @@ 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] +// For example, [1, 10, 3] is encoded as [1 * 256^2 + 10 * 256 + 3] pub fn bytes_to_fields(input: [u8; N]) -> [Field; M] { static_assert(N <= 31 * M, "Bytes do not fit into fields"); let mut dst = [0; M]; - for field_index in 0..M { + for dst_index in 0..M { let mut field_value = 0; for i in 0..31 { - let byte_index = field_index * 31 + i; + let byte_index = dst_index * 31 + i; if byte_index < N { // Shift the existing value left by 8 bits and add the new byte field_value = field_value * 256 + input[byte_index] as Field; } } - dst[field_index] = field_value; + dst[dst_index] = field_value; } dst @@ -32,8 +32,8 @@ pub fn bytes_to_fields(input: [u8; N]) -> [Field; M] { pub fn fields_to_bytes(input: [Field; M]) -> [u8; N] { let mut dst = [0; N]; - for field_index in 0..M { - let field = input[field_index]; + for src_index in 0..M { + let field = input[src_index]; // We expect that the field contains at most 31 bytes of information. field.assert_max_bit_size::<248>(); @@ -43,7 +43,7 @@ pub fn fields_to_bytes(input: [Field; M]) -> [u8; N] { // Since some of the bytes might not be occupied (if the source value requiring less than 31 bytes), // we have to compute the start index from which to copy. - let remaining_bytes = N - field_index * 31; + let remaining_bytes = N - src_index * 31; let src_start_index = if remaining_bytes < 31 { // If the remaining bytes are less than 31, we only copy the remaining bytes 31 - remaining_bytes @@ -59,7 +59,7 @@ pub fn fields_to_bytes(input: [Field; M]) -> [u8; N] { } for i in 0..31 { - let byte_index = field_index * 31 + i; + let byte_index = src_index * 31 + i; if byte_index < N { dst[byte_index] = src[src_start_index + i]; } From 3271b22625f7a980eb4c1d6822692210de09c5d1 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 18 Oct 2024 08:18:14 +0000 Subject: [PATCH 17/17] updated error message in test --- noir-projects/aztec-nr/aztec/src/utils/bytes.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr index 9e7faa0159c..58bbd233065 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/bytes.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/bytes.nr @@ -178,7 +178,7 @@ mod test { assert_eq(input, input_back); } - #[test(should_fail_with = "Bytes do not fit into fields")] + #[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];