Skip to content

Commit b6bbbd1

Browse files
authored
fix: add safety blocks and safety comments (#31)
1 parent 682df8b commit b6bbbd1

13 files changed

+125
-44
lines changed

Nargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ authors = [""]
55
compiler_version = ">=0.37.0"
66

77
[dependencies]
8-
noir_sort = {tag = "v0.2.1", git = "https://github.com/noir-lang/noir_sort"}
8+
noir_sort = {tag = "v0.2.2", git = "https://github.com/noir-lang/noir_sort"}

src/_comparison_tools/bounds_checker.nr

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ in this case, i == M
2626
* cost = 3 gates + 2 gates per iteration
2727
**/
2828
pub fn get_validity_flags<let N: u32>(boundary: u32) -> [Field; N] {
29-
let flags: [Field; N] = __get_validity_flags(boundary);
29+
//@Safety: The constraining is happening inside get_validity_flags_inner
30+
let flags: [Field; N] = unsafe { __get_validity_flags(boundary) };
3031
get_validity_flags_inner(boundary, flags)
3132
}
3233

src/_comparison_tools/lt.nr

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ pub unconstrained fn get_lte_predicate_large(x: Field, y: Field) -> bool {
1414
}
1515

1616
pub fn lte_field_240_bit(x: Field, y: Field) -> bool {
17-
let predicate = get_lte_predicate_large(x, y);
17+
//@Safety: check the comments below
18+
let predicate = unsafe { get_lte_predicate_large(x, y) };
1819
let delta = y as Field - x as Field;
1920

2021
// (x - y) * predicate
@@ -23,6 +24,8 @@ pub fn lte_field_240_bit(x: Field, y: Field) -> bool {
2324
// (y - x) * p + (1 - p) * (x - y + 1)
2425
// (y - x) * p + x - y + 1 + p * (y - x)
2526
let lt_parameter = 2 * (predicate as Field) * delta - predicate as Field - delta + 1;
27+
// checks that the bit length of lt_parameter is 240
28+
// i.e. checks the sign of lt_parameter
2629
lt_parameter.assert_max_bit_size::<240>();
2730

2831
predicate
@@ -40,18 +43,24 @@ pub fn assert_lte_240_bit(x: Field, y: Field) {
4043
}
4144

4245
pub fn lt_field_16_bit(x: Field, y: Field) -> bool {
43-
let predicate = get_lt_predicate_f(x, y);
46+
//@Safety: check the comments below
47+
let predicate = unsafe { get_lt_predicate_f(x, y) };
4448
let delta = y as Field - x as Field;
4549
let lt_parameter = 2 * (predicate as Field) * delta - predicate as Field - delta;
50+
// checks that the bit length of lt_parameter is 16
51+
// i.e. checks the sign of lt_parameter
4652
lt_parameter.assert_max_bit_size::<16>();
4753

4854
predicate
4955
}
5056

5157
pub fn lt_field_8_bit(x: Field, y: Field) -> bool {
52-
let predicate = get_lt_predicate_f(x, y);
58+
//@Safety: check the comments below
59+
let predicate = unsafe { get_lt_predicate_f(x, y) };
5360
let delta = y as Field - x as Field;
5461
let lt_parameter = 2 * (predicate as Field) * delta - predicate as Field - delta;
62+
// checks that the bit length of lt_parameter is 8
63+
// i.e. checks the sign of lt_parameter
5564
lt_parameter.assert_max_bit_size::<8>();
5665

5766
predicate

src/_string_tools/slice_field.nr

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,11 @@ unconstrained fn __slice_200_bits_from_field(f: Field) -> (Field, Field, bool) {
6464
}
6565

6666
pub fn slice_200_bits_from_field(f: Field) -> Field {
67-
let (lo, hi, borrow) = __slice_200_bits_from_field(f);
67+
//@Safety: check the comments below
68+
let (lo, hi, borrow) = unsafe { __slice_200_bits_from_field(f) };
69+
// checks that lo and hi are the correct slices of f
6870
assert(hi * TWO_POW_200 + lo == f);
71+
// checks that lo and hi are the correct bit sizes
6972
lo.assert_max_bit_size::<200>();
7073
hi.assert_max_bit_size::<56>();
7174
let lo_diff = PLO_200_felt - lo + (borrow as Field * TWO_POW_200);

src/_string_tools/slice_packed_field.nr

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,8 @@ unconstrained fn __divmod(numerator: Field, denominator: Field) -> (Field, Field
607607
* we know the quotient will fit into a 14 bit range check which will save us some fractional gates
608608
**/
609609
fn divmod_31(numerator: Field) -> (Field, Field) {
610-
let (quotient, remainder) = __divmod(numerator, 31);
610+
//@Safety: we check the bit lengths of qf and rf and their relation to the numerator with assertions later
611+
let (quotient, remainder) = unsafe { __divmod(numerator, 31) };
611612

612613
let qf = quotient as Field;
613614
let rf = remainder as Field;
@@ -641,14 +642,17 @@ unconstrained fn decompose(val: Field) -> [Field; 16] {
641642
pub fn get_last_limb_path<let OutputFields: u32>(last_limb_index: Field) -> [Field; OutputFields] {
642643
// TODO we offset by 1 explain why (0 byte length produces 0 - 1 which = invalid array index. we just add 1 and increase array length by 1 to compensate)
643644
let path = LAST_LIMB_PATH[last_limb_index + 1]; // 2
644-
let path_valid_bits = decompose(path);
645+
//@Safety: check the comments below
646+
let path_valid_bits = unsafe { decompose(path) };
645647
let mut path_valid_sum: Field = 0;
646648
let mut path_valid_output: [Field; OutputFields] = [0; OutputFields];
647649
for i in 0..OutputFields {
650+
// we check that the path valid bits are binary
648651
assert(path_valid_bits[i] * path_valid_bits[i] - path_valid_bits[i] == 0);
649652
path_valid_sum += (path_valid_bits[i] * (1 << i as u8) as Field);
650653
path_valid_output[i] = path_valid_bits[i];
651654
}
655+
// we check that the path valid bits sum to the path
652656
assert(path_valid_sum == path);
653657
path_valid_output
654658
}
@@ -659,7 +663,8 @@ pub fn get_last_limb_path<let OutputFields: u32>(last_limb_index: Field) -> [Fie
659663
* @details cost 46 gates
660664
**/
661665
pub fn slice_field(f: Field, num_bytes: Field) -> (Field, Field) {
662-
let chunks = __slice_field(f, num_bytes);
666+
//@Safety: we check the bit lengths of the chunks with assertions later
667+
let chunks = unsafe { __slice_field(f, num_bytes) };
663668
chunks[0].assert_max_bit_size::<8>(); // 1.25 gates
664669
chunks[1].assert_max_bit_size::<16>(); // 1.5 gates
665670
chunks[2].assert_max_bit_size::<32>(); // 1.75 gates
@@ -863,7 +868,9 @@ mod test {
863868
// let start_byte = 26;
864869
let num_bytes = 0;
865870
let start_byte: u32 = 0;
866-
let mut expected_slices: [Field; 3] = build_slices_for_test(text, start_byte, num_bytes);
871+
//@Safety: this is a test
872+
let mut expected_slices: [Field; 3] =
873+
unsafe { build_slices_for_test(text, start_byte, num_bytes) };
867874
let result_slices: [Field; 3] =
868875
slice_fields(slices, start_byte as Field, num_bytes as Field);
869876
assert(result_slices == expected_slices);
@@ -895,16 +902,19 @@ mod test {
895902
for j in 0..18 {
896903
let start_byte: u32 = byte_positions[j];
897904
let mut expected_slices: [Field; 3] =
898-
build_slices_for_test(text, start_byte, num_bytes);
905+
//@Safety: this is a test
906+
unsafe { build_slices_for_test(text, start_byte, num_bytes) };
899907
let result_slices: [Field; 3] =
900-
slice_fields(slices, start_byte as Field, num_bytes as Field);
908+
//@Safety: this is a test
909+
unsafe { slice_fields(slices, start_byte as Field, num_bytes as Field) };
901910
assert(result_slices == expected_slices);
902911
}
903912

904913
for j in 0..18 {
905914
let start_byte: u32 = text.len() - num_bytes - byte_positions[j];
906915
let mut expected_slices: [Field; 3] =
907-
build_slices_for_test(text, start_byte, num_bytes);
916+
//@Safety: this is a test
917+
unsafe { build_slices_for_test(text, start_byte, num_bytes) };
908918
let result_slices: [Field; 3] =
909919
slice_fields(slices, start_byte as Field, num_bytes as Field);
910920
assert(result_slices == expected_slices);

src/_string_tools/sum_bytes_into_field.nr

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,10 +185,12 @@ fn sum_var_bytes_into_field<let N: u32>(
185185
body_index: Field,
186186
num_bytes: Field,
187187
) -> Field {
188-
let path = get_path(num_bytes); // 5 gates
188+
//@Safety: check the comments below
189+
let path = unsafe { get_path(num_bytes) }; // 5 gates
189190
let path_f: [Field; 5] =
190191
[path[0] as Field, path[1] as Field, path[2] as Field, path[3] as Field, path[4] as Field];
191192

193+
// checkes that path_f is the binary representation of num_bytes
192194
assert(
193195
path_f[0] + path_f[1] * 2 + path_f[2] * 4 + path_f[3] * 8 + path_f[4] * 16
194196
== num_bytes as Field,

src/get_string.nr

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ fn process_escape_sequences<let N: u32>(input: BoundedVec<u8, N>) -> BoundedVec<
3030
+ (1 - is_escape_sequence) * character as Field;
3131

3232
written_byte = written_byte * (1 - skip) + cached_byte * skip;
33-
let written_byte_u8 = to_u8(written_byte);
33+
//@Safety: we assert that the casting is done correctly
34+
let written_byte_u8 = unsafe { to_u8(written_byte) };
3435
assert(written_byte_u8 as Field == written_byte);
3536

3637
result[result_ptr] = written_byte_u8;
@@ -41,7 +42,8 @@ fn process_escape_sequences<let N: u32>(input: BoundedVec<u8, N>) -> BoundedVec<
4142
}
4243

4344
let written_byte: Field = character as Field * (1 - skip) + cached_byte * skip;
44-
let written_byte_u8 = to_u8(written_byte);
45+
//@Safety: we assert that the casting is done correctly
46+
let written_byte_u8 = unsafe { to_u8(written_byte) };
4547
assert(written_byte_u8 as Field == written_byte);
4648
result[result_ptr] = written_byte_u8;
4749
result_ptr += 1;

src/getters.nr

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ impl<let NumBytes: u32, let NumPackedFields: u32, let MaxNumTokens: u32, let Max
5252

5353
let keyhash = keyhash + self.root_id * two_pow_216;
5454

55-
let key_index = self.find_key_in_map(keyhash);
55+
//@Safety: The assertion below checks that the keyhash is stored in the the index returned by the unconstrained function
56+
let key_index = unsafe { self.find_key_in_map(keyhash) };
5657

5758
assert(self.key_hashes[key_index] == keyhash, "get_json_entry_unchecked: key not found");
5859
let entry: JSONEntry = self.json_entries_packed[key_index].into();
@@ -91,7 +92,8 @@ impl<let NumBytes: u32, let NumPackedFields: u32, let MaxNumTokens: u32, let Max
9192

9293
let keyhash = keyhash + self.root_id * two_pow_216;
9394

94-
let key_index = self.find_key_in_map(keyhash);
95+
//@Safety: The assertion below checks that the keyhash is stored in the the index returned by the unconstrained function
96+
let key_index = unsafe { self.find_key_in_map(keyhash) };
9597

9698
assert(self.key_hashes[key_index] == keyhash, "get_json_entry_unchecked: key not found");
9799
let entry: JSONEntry = self.json_entries_packed[key_index].into();
@@ -114,7 +116,8 @@ impl<let NumBytes: u32, let NumPackedFields: u32, let MaxNumTokens: u32, let Max
114116

115117
let keyhash = keyhash + self.root_id * two_pow_216;
116118

117-
let key_index = self.find_key_in_map(keyhash);
119+
//@Safety: The assertion below checks that the keyhash is stored in the the index returned by the unconstrained function
120+
let key_index = unsafe { self.find_key_in_map(keyhash) };
118121

119122
assert(self.key_hashes[key_index] == keyhash, "get_json_entry_unchecked: key not found");
120123
let entry: JSONEntry = self.json_entries_packed[key_index].into();
@@ -136,7 +139,8 @@ impl<let NumBytes: u32, let NumPackedFields: u32, let MaxNumTokens: u32, let Max
136139

137140
let keyhash = keyhash + self.root_id * two_pow_216;
138141

139-
let key_index = self.find_key_in_map(keyhash);
142+
//@Safety: The assertion below checks that the keyhash is stored in the the index returned by the unconstrained function
143+
let key_index = unsafe { self.find_key_in_map(keyhash) };
140144

141145
assert(self.key_hashes[key_index] == keyhash, "get_json_entry_unchecked: key not found");
142146
let entry: JSONEntry = self.json_entries_packed[key_index].into();
@@ -319,7 +323,8 @@ impl<let NumBytes: u32, let NumPackedFields: u32, let MaxNumTokens: u32, let Max
319323

320324
let keyhash = keyhash + self.root_id * two_pow_216;
321325

322-
let search_result = self.search_for_key_in_map(keyhash);
326+
//@Safety: The assertion below checks that the keyhash is stored in the the index returned by the unconstrained function
327+
let search_result = unsafe { self.search_for_key_in_map(keyhash) };
323328
let found = search_result.found as Field;
324329

325330
let target_lt_smallest_entry = search_result.target_lt_smallest_entry as Field;
@@ -378,7 +383,8 @@ impl<let NumBytes: u32, let NumPackedFields: u32, let MaxNumTokens: u32, let Max
378383

379384
let keyhash = keyhash + self.root_id * two_pow_216;
380385

381-
let search_result = self.search_for_key_in_map(keyhash);
386+
//@Safety: the assertion (search_result.lhs_index - search_result.rhs_index) * found == 0 constraints this function
387+
let search_result = unsafe { self.search_for_key_in_map(keyhash) };
382388
let found = search_result.found as Field;
383389

384390
let target_lt_smallest_entry = search_result.target_lt_smallest_entry as Field;
@@ -437,7 +443,8 @@ impl<let NumBytes: u32, let NumPackedFields: u32, let MaxNumTokens: u32, let Max
437443
) -> BoundedVec<BoundedVec<u8, MaxKeyBytes>, MaxNumKeys> {
438444
let root_object: JSONEntry =
439445
JSONEntry::from(self.json_entries_packed[self.root_index_in_transcript]);
440-
let key_indices: BoundedVec<Field, MaxNumKeys> = self.__get_keys_at_root();
446+
//@Safety: the length of the index is constrained later.
447+
let key_indices: BoundedVec<Field, MaxNumKeys> = unsafe { self.__get_keys_at_root() };
441448

442449
assert(key_indices.len as Field == root_object.num_children);
443450

src/json.nr

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,8 @@ impl<let NumBytes: u32, let NumPackedFields: u32, let MaxNumTokens: u32, let Max
251251
let mut tokens: [Field; MaxNumTokens] = [0; MaxNumTokens];
252252
// maybe 71.75 gates per iteration
253253
for i in 0..MaxNumTokens {
254-
__check_entry_ptr_bounds(entry_ptr, MaxNumValues);
254+
//@Safety: check the comments below
255+
unsafe { __check_entry_ptr_bounds(entry_ptr, MaxNumValues) };
255256
// 5.25 gates
256257
let TranscriptEntry { token, index, length } =
257258
TranscriptEntry::from_field(self.transcript[i]);
@@ -462,7 +463,8 @@ impl<let NumBytes: u32, let NumPackedFields: u32, let MaxNumTokens: u32, let Max
462463
let mut scan_mode = GRAMMAR_SCAN;
463464
let mut length: Field = 0;
464465

465-
let raw_transcript = self.__build_transcript();
466+
//@Safety: check the comments below
467+
let raw_transcript = unsafe { self.__build_transcript() };
466468

467469
// 14 gates per iteration, plus fixed cost for initing 2,048 size lookup table (4,096 gates)
468470
let mut previous_was_potential_escape_sequence = 0;
@@ -570,7 +572,8 @@ impl<let NumBytes: u32, let NumPackedFields: u32, let MaxNumTokens: u32, let Max
570572
fn capture_missing_tokens(&mut self) {
571573
let mut transcript_ptr: Field = 0;
572574
// hmm probably need a null transcript value?!?!
573-
let updated_transcript = self.__capture_missing_tokens();
575+
//@Safety: check the comments below
576+
let updated_transcript = unsafe { self.__capture_missing_tokens() };
574577
// 26? gates per iteration
575578
let range_valid: [Field; MaxNumTokens] = get_validity_flags(self.transcript_length);
576579
for i in 0..MaxNumTokens {

src/json_entry.nr

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ impl JSONContextStackEntry {
3333
* @note method cost = 9.5 gates
3434
**/
3535
fn from_field(f: Field) -> Self {
36-
let result = JSONContextStackEntry::__from_field(f);
36+
//@Safety: the set of assertions done below will ensure the result is in the correct domain, and the computed result matches the input f
37+
let result = unsafe { JSONContextStackEntry::__from_field(f) };
3738

3839
result.context.assert_max_bit_size::<8>(); // 1.25
3940
result.num_entries.assert_max_bit_size::<16>(); // 1.5
@@ -119,8 +120,9 @@ impl JSONEntry {
119120

120121
// 11.75 gates
121122
fn extract_entry_type_id_and_parent_index_from_field(f: Field) -> (Field, Field, Field) {
123+
//@Safety: we check all the outputs are in the correct range and we can construct the input from the outputs of the decomposition
122124
let (id, parent_index, mid, entry_type) =
123-
JSONEntry::__extract_entry_type_id_and_parent_index_from_field(f);
125+
unsafe { JSONEntry::__extract_entry_type_id_and_parent_index_from_field(f) };
124126
id.assert_max_bit_size::<8>(); // 1.25
125127
parent_index.assert_max_bit_size::<16>(); // 1.5
126128
entry_type.assert_max_bit_size::<16>(); // 1.5
@@ -136,15 +138,17 @@ impl JSONEntry {
136138
(id, parent_index, entry_type)
137139
}
138140
fn extract_entry_type_and_id_from_field(f: Field) -> (Field, Field) {
139-
let (id, mid, entry_type) = JSONEntry::__extract_entry_type_and_id_from_field(f);
141+
let (id, mid, entry_type) = unsafe { JSONEntry::__extract_entry_type_and_id_from_field(f) };
140142
id.assert_max_bit_size::<8>(); // 1.25
141143
entry_type.assert_max_bit_size::<16>(); // 1.5
142144
mid.assert_max_bit_size::<136>(); // 5.5
143145
assert(id + mid * 0x10000 + entry_type * 0x100000000000000000000000000000000000000 == f);
144146
(id, entry_type)
145147
}
148+
146149
fn extract_parent_index_from_field(f: Field) -> Field {
147-
let (low, parent_index, hi) = JSONEntry::__extract_parent_index_from_field(f);
150+
//@Safety: we check all the outputs are in the correct range and we can construct the input from the outputs of the decomposition
151+
let (low, parent_index, hi) = unsafe { JSONEntry::__extract_parent_index_from_field(f) };
148152

149153
low.assert_max_bit_size::<16>(); // 1.75
150154
hi.assert_max_bit_size::<128>(); // 5.5
@@ -188,7 +192,8 @@ impl JSONEntry {
188192
// 4 gates. oof
189193
}
190194
fn from_field(f: Field) -> Self {
191-
let result = JSONEntry::__from_field(f);
195+
//@Safety: we check all the outputs are in the correct range and match the input field value
196+
let result = unsafe { JSONEntry::__from_field(f) };
192197
result.entry_type.assert_max_bit_size::<8>();
193198
result.json_length.assert_max_bit_size::<16>();
194199
result.json_pointer.assert_max_bit_size::<16>();
@@ -204,7 +209,8 @@ impl JSONEntry {
204209

205210
impl std::convert::From<JSONEntryPacked> for JSONEntry {
206211
fn from(JSONEntryPacked { value: f }: JSONEntryPacked) -> Self {
207-
let result = JSONEntry::__from_field(f);
212+
//@Safety: we check all the outputs are in the correct range and match the input field value
213+
let result = unsafe { JSONEntry::__from_field(f) };
208214
result.entry_type.assert_max_bit_size::<8>();
209215
result.json_length.assert_max_bit_size::<16>();
210216
result.json_pointer.assert_max_bit_size::<16>();

src/keymap.nr

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,17 @@ impl KeyIndexData {
3535
}
3636

3737
fn from_field(packed: Field) -> Self {
38-
let result = KeyIndexData::__from_field(packed);
38+
//@Safety: check the comments below
39+
let result = unsafe { KeyIndexData::__from_field(packed) };
40+
// checks that array_index is in range
3941
result.array_index.assert_max_bit_size::<16>();
42+
// checks that json_length is in range
4043
result.json_length.assert_max_bit_size::<16>();
44+
// checks that json_index is in range
4145
result.json_index.assert_max_bit_size::<16>();
46+
// checks that parent_id is in range
4247
result.parent_id.assert_max_bit_size::<16>();
48+
// checks that the input is a valid combination of the outputs of the decomposition
4349
assert(result.to_field() == packed);
4450
result
4551
}
@@ -146,12 +152,15 @@ impl<let NumBytes: u32, let NumPackedFields: u32, let MaxNumTokens: u32, let Max
146152
}
147153

148154
fn set_root_entry(&mut self) {
149-
let root_index = self.__find_root_entry();
155+
//@Safety: check the comments below
156+
let root_index = unsafe { self.__find_root_entry() };
150157

151158
let packed_entry = self.json_entries_packed[root_index];
152159
let entry: JSONEntry = packed_entry.into();
153160

161+
// checks that the entry is not empty
154162
assert(packed_entry.value != 0);
163+
// checks that the parent index is 0
155164
assert(entry.parent_index == 0);
156165
self.root_index_in_transcript = root_index;
157166
}

src/token_flags.nr

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ impl TokenFlags {
3333

3434
fn from_field(f: Field) -> Self {
3535
// 10 gates
36-
let r = TokenFlags::__from_field(f);
36+
//@Safety: check the comments below
37+
let r = unsafe { TokenFlags::__from_field(f) };
3738

39+
// checks that the flags are binary
3840
assert(r.create_json_entry * r.create_json_entry == r.create_json_entry);
3941
assert(
4042
r.is_end_of_object_or_array * r.is_end_of_object_or_array
@@ -49,6 +51,7 @@ impl TokenFlags {
4951
assert(r.is_value_token * r.is_value_token == r.is_value_token);
5052
assert(r.preserve_num_entries * r.preserve_num_entries == r.preserve_num_entries);
5153

54+
// asserts the relation of r and f
5255
assert(r.to_field() == f);
5356
r
5457
}

0 commit comments

Comments
 (0)