Skip to content

Commit

Permalink
added more comments + method descriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
zac-williamson committed Sep 2, 2024
1 parent 0d1442c commit b7c0f8f
Show file tree
Hide file tree
Showing 20 changed files with 1,119 additions and 867 deletions.
6 changes: 3 additions & 3 deletions out.txt

Large diffs are not rendered by default.

143 changes: 143 additions & 0 deletions src/bounds_checker.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@


/*
when iterating from 0 to N, validate i < M efficiently
we have an array of flags that describe whether entry is valid
flags start at 1 and at 0 more or less
we check:
1. flag starts at 0 or 1
2. flag transition cannot be 0 -> 1 i.e. new_flag * (1 - old_flag) == 0
3. flag ends at 0 or 1
the above validates that only one transition point occurs
we still need to test the transition point
transition happens when we get 1 -> 0 i.e. tx = i * (old_flag * (1 - new_flag))
in this case, i == M
// o * (1 - n) = o - on
// n * (1 - o) = n - on
// i*(o - on) * (1/i) - o + n
*/

/**
* @brief helper method that provides an array of Field elements `flags`, where `flags[i] = i < boundary`
* @description this method is cheaper than querying `i < boundary` for `u16` and `u32` types
* cost = 3 gates + 2 gates per iteration
**/
pub fn get_validity_flags<let N: u16>(boundary: u16) -> [Field; N] {
let flags: [Field; N] = __get_validity_flags(boundary);
get_validity_flags_inner(boundary, flags)
}

unconstrained fn __get_validity_flags<let N: u16>(boundary: u16) -> [Field; N] {
let mut result: [Field; N] = [0; N];
for i in 0..N as u16 {
if i < boundary {
result[i] = 1;
}
}
result
}

/**
* @brief implementation of `get_validity_flags`
* @description Given an array of `flags`, we apply the following checks to build an inductive proof about the validity of the flags array:
* 1. the first element `flags[0]` is in the range [0,1]
* 2. the last element `flags[N-1]` is in the range [0,1]
* 3. for any two flags `old, new` where `old = flags[i-1], new = flags[i]` and `i>0, i <N`, we validate the following:
* a. if `old` is 0, `new` *cannot* equal 1
* b. if `old` is 1 and `new` is 0, set `transition_index = i`
* The value of `transition_index` will equal the value `i` where `i = boundary` (or `N` if `boundary > N`)
* 4. we finally validate `transition_index == boundary` to prove the location where `flags[i-1] = 1` and `flags[i] = 0`
* aligns with what is expected from testing `i < boundary`
* N.B. this method will revert if `boundary > N`
**/
fn get_validity_flags_inner<let N: u16>(boundary: u16, flags: [Field; N]) -> [Field; N] {
let initial_flag = flags[0];
let final_flag = flags[N - 1];

// check first and last flags are in the range [0, 1]
assert(initial_flag * initial_flag == initial_flag);
assert(final_flag * final_flag == final_flag);

let mut transition_index = 0;

for i in 1..N {
let old_flag = flags[i - 1];
let new_flag = flags[i];
assert(new_flag == old_flag * new_flag);

// old = a, new = b
let idx = (old_flag * (1 - new_flag)) * (i as Field);
transition_index = transition_index + idx;
std::as_witness(transition_index);
}

assert(transition_index == boundary as Field);
flags
}

#[test]
fn test_get_validity_flags() {
for i in 0..32 {
let flags: [Field; 32] = get_validity_flags(i);
for j in 0..32 {
assert(flags[j] == (j < i) as Field);
}
}
}

#[test(should_fail)]
fn test_get_validity_flags_fail() {
let _ = get_validity_flags(33);
}

#[test(should_fail)]
fn test_get_validity_flags_bad_index_fail_a() {
let bad_flags: [Field; 10] = [1, 1, 1, 0, 0, 0, 1, 0, 0, 0];
let _ = get_validity_flags_inner(3, bad_flags);
}
#[test(should_fail)]
fn test_get_validity_flags_bad_index_fail_b() {
let bad_flags: [Field; 10] = [1, 1, 1, 1, 0, 0, 0, 0, 0, 0];
let _ = get_validity_flags_inner(3, bad_flags);
}

#[test(should_fail)]
fn test_get_validity_flags_bad_index_fail_c() {
let bad_flags: [Field; 10] = [1, 1, 0, 0, 0, 0, 0, 0, 0, 0];
let _ = get_validity_flags_inner(3, bad_flags);
}

#[test]
fn test_get_validity_flags_good_index_d() {
let bad_flags: [Field; 10] = [1, 1, 1, 0, 0, 0, 0, 0, 0, 0];
let _ = get_validity_flags_inner(3, bad_flags);
}

#[test(should_fail)]
fn test_get_validity_flags_bad_index_fail_e() {
let bad_flags: [Field; 10] = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
let _ = get_validity_flags_inner(11, bad_flags);
}

// this test uses bad flags but manipulates transition_index to be satisfiable
// nevertheless test will fail because our transition test (old * new = new) will fail
#[test(should_fail)]
fn test_get_validity_flags_bad_index_fail_f() {
let mut bad_flags: [Field; 10] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];

let fake_index_a = 2;
let fake_value_a = 100;

let fake_index_b = 4;
// 4 * Y = -2 * X
let fake_value_b = (-fake_value_a * fake_index_a) / fake_index_b;

bad_flags[fake_index_a] = fake_value_a;
bad_flags[fake_index_b] = fake_value_b;
let _ = get_validity_flags_inner(0, bad_flags);
}
110 changes: 59 additions & 51 deletions src/get_array.nr
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
use crate::getters;
use dep::noir_sort;
use crate::json_entry::JSONEntry;
use crate::redux::JSON;
use crate::keymap;
use crate::lt::{lt_field_8_bit, lt_field_16_bit, assert_lt_240_bit, assert_gt_240_bit};
use crate::redux_tables::{
OBJECT_LAYER, ARRAY_LAYER, NUMERIC_TOKEN, LITERAL_TOKEN, STRING_TOKEN, BEGIN_OBJECT_TOKEN,
BEGIN_ARRAY_TOKEN, ASCII_TO_NUMBER, ESCAPE_SEQUENCE_END_CHARS, ESCAPE_SEQUENCE_START_CHARS,
ESCAPE_SEQUENCE_REPLACEMENT
};
use crate::keyhash::Hasher;
use crate::json::JSON;
use crate::lt::lt_field_16_bit;
use crate::json_tables::{OBJECT_LAYER, ARRAY_LAYER, BEGIN_ARRAY_TOKEN};
use crate::keyhash::get_keyhash;
use crate::slice_field::slice_fields;
use crate::getters::JSONValue;

/**
* @brief getter methods for extracting array types out of a JSON struct
**/
impl<let NumBytes: u32, let NumPackedFields: u16, let MaxNumTokens: u16, let MaxNumValues: u16> JSON<NumBytes,NumPackedFields, MaxNumTokens, MaxNumValues> {

/**
* @brief if the root JSON is an array, return its length
**/
fn get_length(self) -> u32 {
assert(self.layer_context == ARRAY_LAYER, "can only get length of an array type");
let parent_entry = JSONEntry::from_field(self.packed_json_entries[self.layer_index_in_transcript]);
let parent_entry: JSONEntry = self.packed_json_entries[self.layer_index_in_transcript].into();
parent_entry.num_children as u32
}

/**
* @brief if the root JSON is an object, extract an array given by `key`
* @description returns an Option<JSON> where, if the array exists, the JSON object will have the requested array as its root value
**/
fn get_array<let KeyBytes: u16>(self, key: [u8; KeyBytes]) -> Option<Self> {
assert(self.layer_context != ARRAY_LAYER, "cannot extract array elements via a key");
let (exists, key_index) = self.key_exists_impl(key, KeyBytes);
let entry: JSONEntry = JSONEntry::from_field(self.packed_json_entries[key_index]);

let entry: JSONEntry = self.packed_json_entries[key_index].into();
assert(
(entry.entry_type - BEGIN_ARRAY_TOKEN) * exists as Field == 0, "key does not describe an object"
);
Expand All @@ -40,12 +40,14 @@ impl<let NumBytes: u32, let NumPackedFields: u16, let MaxNumTokens: u16, let Max
Option { _is_some: exists, _value: r }
}

/**
* @brief if the root JSON is an object, extract an array given by `key`
* @description will revert if the array does not exist
**/
fn get_array_unchecked<let KeyBytes: u16>(self, key: [u8; KeyBytes]) -> Self {
assert(self.layer_context != ARRAY_LAYER, "cannot extract array elements via a key");

let key_index = self.key_exists_impl_unchecked(key, KeyBytes);
let entry: JSONEntry = JSONEntry::from_field(self.packed_json_entries[key_index]);

let (entry, key_index) = self.get_json_entry_unchecked_with_key_index_var(key, KeyBytes);
assert(entry.entry_type == BEGIN_ARRAY_TOKEN, "key does not describe an object");

let mut r = self;
Expand All @@ -57,51 +59,57 @@ impl<let NumBytes: u32, let NumPackedFields: u16, let MaxNumTokens: u16, let Max
r
}

fn get_array_unchecked_var<let KeyBytes: u16>(self, key: [u8; KeyBytes], key_length: u16) -> Self {
/**
* @brief same as `get_array` for where the key length may be less than KeyBytes
**/
fn get_array_var<let KeyBytes: u16>(self, key: [u8; KeyBytes], key_length: u16) -> Option<Self> {
assert(self.layer_context != ARRAY_LAYER, "cannot extract array elements via a key");

let key_index = self.key_exists_impl_unchecked(key, key_length);
let entry: JSONEntry = JSONEntry::from_field(self.packed_json_entries[key_index]);

assert(entry.entry_type == BEGIN_ARRAY_TOKEN, "key does not describe an object");
let (exists, key_index) = self.key_exists_impl(key, key_length);
let entry: JSONEntry = self.packed_json_entries[key_index].into();
// TODO: ADD A layer_context VARIABLE INTO JSON WHICH DESCRIBES WHETHER WE ARE AN OBJECT, ARRAY OR SINGLE VALUE
assert(
(entry.entry_type - BEGIN_ARRAY_TOKEN) * exists as Field == 0, "key does not describe an object"
);

let mut r = self;
r.layer_id = entry.parent_index;
r.root_id = entry.id;
r.layer_context = ARRAY_LAYER;
r.layer_index_in_transcript = key_index;

r
Option { _is_some: exists, _value: r }
}
fn get_array_var<let KeyBytes: u16>(self, key: [u8; KeyBytes], key_length: u16) -> Option<Self> {

/**
* @brief same as `get_array_unchecked` for where the key length may be less than KeyBytes
**/
fn get_array_unchecked_var<let KeyBytes: u16>(self, key: [u8; KeyBytes], key_length: u16) -> Self {
assert(self.layer_context != ARRAY_LAYER, "cannot extract array elements via a key");
let (exists, key_index) = self.key_exists_impl(key, key_length);
let entry: JSONEntry = JSONEntry::from_field(self.packed_json_entries[key_index]);

// TODO: ADD A layer_context VARIABLE INTO JSON WHICH DESCRIBES WHETHER WE ARE AN OBJECT, ARRAY OR SINGLE VALUE
assert(
(entry.entry_type - BEGIN_ARRAY_TOKEN) * exists as Field == 0, "key does not describe an object"
);
let (entry, key_index) = self.get_json_entry_unchecked_with_key_index_var(key, key_length);
assert(entry.entry_type == BEGIN_ARRAY_TOKEN, "key does not describe an object");

let mut r = self;
r.layer_id = entry.parent_index;
r.root_id = entry.id;
r.layer_context = ARRAY_LAYER;
r.layer_index_in_transcript = key_index;

Option { _is_some: exists, _value: r }
r
}

/**
* @brief if the root JSON is an array, extract an array given by the position of the target in the source array
* @description returns an Option<JSON> where, if the array exists, the JSON object will have the requested array as its root value
**/
fn get_array_from_array(self, array_index: Field) -> Option<Self> {
assert(self.layer_context == ARRAY_LAYER, "can only acceess array elements from array");

let parent_entry = JSONEntry::from_field(self.packed_json_entries[self.layer_index_in_transcript]);

let parent_entry: JSONEntry = self.packed_json_entries[self.layer_index_in_transcript].into();
let valid = lt_field_16_bit(array_index, parent_entry.num_children);
let entry_index = (parent_entry.child_pointer + array_index) * valid as Field;

let entry = JSONEntry::from_field(self.packed_json_entries[entry_index]);

let entry: JSONEntry = self.packed_json_entries[entry_index].into();
assert(
(entry.entry_type - BEGIN_ARRAY_TOKEN) * valid as Field == 0, "get_object_from_array: entry exists but is not an object!"
);
Expand All @@ -115,17 +123,19 @@ impl<let NumBytes: u32, let NumPackedFields: u16, let MaxNumTokens: u16, let Max
Option { _is_some: valid, _value: r }
}

/**
* @brief if the root JSON is an array, extract an array given by the position of the target in the source array
* @description will revert if the array does not exist
**/
fn get_array_from_array_unchecked(self, array_index: Field) -> Self {
assert(self.layer_context == ARRAY_LAYER, "can only acceess array elements from array");

let parent_entry = JSONEntry::from_field(self.packed_json_entries[self.layer_index_in_transcript]);

let parent_entry: JSONEntry = self.packed_json_entries[self.layer_index_in_transcript].into();
let valid = lt_field_16_bit(array_index, parent_entry.num_children);
assert(valid, "array overflow");
let entry_index = (parent_entry.child_pointer + array_index);

let entry = JSONEntry::from_field(self.packed_json_entries[entry_index]);

let entry: JSONEntry = self.packed_json_entries[entry_index].into();
assert(
entry.entry_type == BEGIN_ARRAY_TOKEN, "get_array_from_array_unchecked: entry exists but is not an array!"
);
Expand All @@ -138,22 +148,20 @@ impl<let NumBytes: u32, let NumPackedFields: u16, let MaxNumTokens: u16, let Max
r
}

fn map<U, let MaxElements: u32, let MaxElementBytes: u32>(
self,
f: fn(JSONValue<MaxElementBytes>) -> U
) -> [U; MaxElements] where U: std::default::Default {
/**
* @brief if the root is an array, map over the array values, applying `fn f` to each value
**/
fn map<U, let MaxElements: u32, let MaxElementBytes: u32>(self, f: fn(JSONValue<MaxElementBytes>) -> U) -> [U; MaxElements] where U: std::default::Default {
assert(self.layer_context == ARRAY_LAYER, "can only call map on an array");

let entry = JSONEntry::from_field(self.packed_json_entries[self.layer_index_in_transcript]);

let entry: JSONEntry = self.packed_json_entries[self.layer_index_in_transcript].into();
let num_children = entry.num_children;
let mut r: [U; MaxElements] = [U::default(); MaxElements];

for i in 0..MaxElements {
let valid = lt_field_16_bit(i as Field, num_children);
let entry_index = (entry.child_pointer + i as Field) * valid as Field;
let child_entry = JSONEntry::from_field(self.packed_json_entries[entry_index]);

let child_entry: JSONEntry = self.packed_json_entries[entry_index].into();
let mut parsed_string: [u8; MaxElementBytes] = [0; MaxElementBytes];
for j in 0..MaxElementBytes {
let byte_valid = lt_field_16_bit(j as Field, child_entry.json_length);
Expand Down Expand Up @@ -209,9 +217,9 @@ fn test_array() {

let E = first.get_object_from_array_unchecked(4);

let entry_maybe = JSONEntry::from_field(E.packed_json_entries[E.layer_index_in_transcript]);
let entry_maybe: JSONEntry = E.packed_json_entries[E.layer_index_in_transcript].into();
println(f"entry = {entry_maybe}");
let child = JSONEntry::from_field(E.packed_json_entries[entry_maybe.child_pointer]);
let child: JSONEntry = E.packed_json_entries[entry_maybe.child_pointer].into();
println(f"target? = {child}");
println(f"{E}");
let E_A = E.get_array_unchecked("bar".as_bytes());
Expand Down
Loading

0 comments on commit b7c0f8f

Please sign in to comment.