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

refactor: updated NFT flows #9150

Merged
merged 37 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
d56bf49
feat: bytes to fields and back
benesjan Sep 17, 2024
b26fce3
WIP
benesjan Sep 17, 2024
916c069
WIP
benesjan Sep 17, 2024
cb6bf5e
WIP
benesjan Sep 17, 2024
d7c59d3
WIP
benesjan Sep 17, 2024
c70c8ea
asserting max value
benesjan Sep 18, 2024
57acc4a
linking issue
benesjan Sep 18, 2024
79b5d14
random input fuzz test
benesjan Sep 18, 2024
a2979c2
test_large_random_input_to_bytes_and_back
benesjan Sep 18, 2024
ffcb5e3
test fields partially used
benesjan Sep 18, 2024
e0b574f
all non-zero bytes used check
benesjan Sep 18, 2024
30fd9ff
enough fields check
benesjan Sep 18, 2024
c9025e0
fixes after update to new noir
benesjan Oct 11, 2024
8d23c1b
static_assert
benesjan Oct 17, 2024
72155ed
Update noir-projects/aztec-nr/aztec/src/utils/bytes.nr
benesjan Oct 17, 2024
47bb1cb
better index names
benesjan Oct 17, 2024
3271b22
updated error message in test
benesjan Oct 18, 2024
32d7f3c
refactor: updating NFT flows
benesjan Oct 10, 2024
84e2ef0
WIP
benesjan Oct 10, 2024
9fc8a5e
x coord as hiding point slot
benesjan Oct 10, 2024
643c53f
WIP
benesjan Oct 10, 2024
4e97c30
WIP
benesjan Oct 10, 2024
0ce52a6
last touches
benesjan Oct 10, 2024
a3d6337
comment fix
benesjan Oct 10, 2024
7b2aa8e
WIP
benesjan Oct 11, 2024
12332f6
WIP
benesjan Oct 11, 2024
e5f01b8
internal func opt.
benesjan Oct 11, 2024
982a4ee
removing lies
benesjan Oct 11, 2024
7b83c80
clarifying test names
benesjan Oct 11, 2024
d8471bd
WIP
benesjan Oct 11, 2024
28312f6
making test slower so that Nico is happy ;)
benesjan Oct 11, 2024
63f9e9b
fmt
benesjan Oct 21, 2024
121a753
fix
benesjan Oct 23, 2024
4cab3fa
Merge branch 'master' into 10-10-refactor_updating_nft_flows
benesjan Oct 24, 2024
8e90fb2
fixes
benesjan Oct 24, 2024
e0a1ee9
fmt
benesjan Oct 24, 2024
93ae409
fmt 2
benesjan Oct 24, 2024
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
2 changes: 1 addition & 1 deletion noir-projects/aztec-nr/aztec/src/prelude.nr
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub use crate::{
public_immutable::PublicImmutable, public_mutable::PublicMutable, private_set::PrivateSet,
shared_immutable::SharedImmutable, shared_mutable::SharedMutable, storage::Storable
},
context::{PrivateContext, PackedReturns, FunctionReturns},
context::{PrivateContext, PackedReturns, FunctionReturns, PublicContext},
note::{
note_header::NoteHeader, note_interface::{NoteInterface, NullifiableNote},
note_getter_options::NoteGetterOptions, note_viewer_options::NoteViewerOptions,
Expand Down
212 changes: 212 additions & 0 deletions noir-projects/aztec-nr/aztec/src/utils/bytes.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
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];

for dst_index in 0..M {
let mut field_value = 0;

for i in 0..31 {
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[dst_index] = field_value;
}

dst
}

// 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<let N: u32, let M: u32>(input: [Field; M]) -> [u8; N] {
let mut dst = [0; N];

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>();

// 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.
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
} else {
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 = src_index * 31 + i;
if byte_index < N {
dst[byte_index] = src[src_start_index + i];
}
}
}

dst
}

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_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 = [
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(input: [u8; 128]) {
let output = bytes_to_fields::<128, 5>(input);
let input_back = fields_to_bytes::<128, 5>(output);

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 = "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);
}

#[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);
}
}
}
2 changes: 2 additions & 0 deletions noir-projects/aztec-nr/aztec/src/utils/mod.nr
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod bytes;
mod collapse_array;
mod comparison;
mod point;
mod test;
mod to_bytes;

pub use crate::utils::collapse_array::collapse_array;
pub use crate::utils::bytes::{bytes_to_fields, fields_to_bytes};
Loading
Loading