Skip to content

Commit

Permalink
feat: add single value extractor
Browse files Browse the repository at this point in the history
  • Loading branch information
lonerapier committed Oct 8, 2024
1 parent d524cce commit 9dbc27a
Show file tree
Hide file tree
Showing 4 changed files with 395 additions and 10 deletions.
161 changes: 161 additions & 0 deletions circuits/json/extractor.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
pragma circom 2.1.9;

include "interpreter.circom";

template ObjectExtractor(DATA_BYTES, MAX_STACK_HEIGHT, keyLen, maxValueLen) {
// Declaration of signals.
signal input data[DATA_BYTES];
signal input key[keyLen];

signal output value[maxValueLen];

// Constraints.
signal value_starting_index[DATA_BYTES];
// flag determining whether this byte is matched value
signal is_value_match[DATA_BYTES];
// final mask
signal mask[DATA_BYTES];

component State[DATA_BYTES];
State[0] = StateUpdate(MAX_STACK_HEIGHT);
State[0].byte <== data[0];
for(var i = 0; i < MAX_STACK_HEIGHT; i++) {
State[0].stack[i] <== [0,0];
}
State[0].parsing_string <== 0;
State[0].parsing_number <== 0;

signal parsing_key[DATA_BYTES];
signal parsing_value[DATA_BYTES];
signal parsing_object_value[DATA_BYTES];
signal is_key_match[DATA_BYTES];
signal is_key_match_for_value[DATA_BYTES+1];
is_key_match_for_value[0] <== 0;
signal is_next_pair_at_depth[DATA_BYTES];
signal or[DATA_BYTES];

// initialise first iteration

// check inside key or value
parsing_key[0] <== InsideKey(MAX_STACK_HEIGHT)(State[0].next_stack, State[0].next_parsing_string, State[0].next_parsing_number);
parsing_value[0] <== InsideValueObject()(State[0].next_stack[0], State[0].next_stack[1], State[0].next_parsing_string, State[0].next_parsing_number);

is_key_match[0] <== KeyMatchAtDepth(DATA_BYTES, MAX_STACK_HEIGHT, keyLen, 0)(data, key, 0, parsing_key[0], State[0].next_stack);
is_next_pair_at_depth[0] <== NextKVPairAtDepth(MAX_STACK_HEIGHT, 0)(State[0].next_stack, data[0]);
is_key_match_for_value[1] <== Mux1()([is_key_match_for_value[0] * (1-is_next_pair_at_depth[0]), is_key_match[0] * (1-is_next_pair_at_depth[0])], is_key_match[0]);
is_value_match[0] <== parsing_value[0] * is_key_match_for_value[1];

mask[0] <== data[0] * is_value_match[0];

for(var data_idx = 1; data_idx < DATA_BYTES; data_idx++) {
State[data_idx] = StateUpdate(MAX_STACK_HEIGHT);
State[data_idx].byte <== data[data_idx];
State[data_idx].stack <== State[data_idx - 1].next_stack;
State[data_idx].parsing_string <== State[data_idx - 1].next_parsing_string;
State[data_idx].parsing_number <== State[data_idx - 1].next_parsing_number;

// - parsing key
// - parsing value (different for string/numbers and array)
// - key match (key 1, key 2)
// - is next pair
// - is key match for value
// - value_mask
// - mask

// check if inside key or not
parsing_key[data_idx] <== InsideKey(MAX_STACK_HEIGHT)(State[data_idx].next_stack, State[data_idx].next_parsing_string, State[data_idx].next_parsing_number);
// check if inside value
parsing_value[data_idx] <== InsideValueObject()(State[data_idx].next_stack[0], State[data_idx].next_stack[1], State[data_idx].next_parsing_string, State[data_idx].next_parsing_number);

// to get correct value, check:
// - key matches at current index and depth of key is as specified
// - whether next KV pair starts
// - whether key matched for a value (propogate key match until new KV pair of lower depth starts)
is_key_match[data_idx] <== KeyMatchAtDepth(DATA_BYTES, MAX_STACK_HEIGHT, keyLen, 0)(data, key, data_idx, parsing_key[data_idx], State[data_idx].next_stack);
is_next_pair_at_depth[data_idx] <== NextKVPairAtDepth(MAX_STACK_HEIGHT, 0)(State[data_idx].next_stack, data[data_idx]);
is_key_match_for_value[data_idx+1] <== Mux1()([is_key_match_for_value[data_idx] * (1-is_next_pair_at_depth[data_idx]), is_key_match[data_idx] * (1-is_next_pair_at_depth[data_idx])], is_key_match[data_idx]);
is_value_match[data_idx] <== is_key_match_for_value[data_idx+1] * parsing_value[data_idx];

or[data_idx] <== OR()(is_value_match[data_idx], is_value_match[data_idx - 1]);

// mask = currently parsing value and all subsequent keys matched
mask[data_idx] <== data[data_idx] * or[data_idx];
}

// find starting index of value in data by matching mask
signal is_zero_mask[DATA_BYTES];
signal is_prev_starting_index[DATA_BYTES];
value_starting_index[0] <== 0;
is_prev_starting_index[0] <== 0;
is_zero_mask[0] <== IsZero()(mask[0]);
for (var i=1 ; i<DATA_BYTES ; i++) {
is_zero_mask[i] <== IsZero()(mask[i]);
is_prev_starting_index[i] <== IsZero()(value_starting_index[i-1]);
value_starting_index[i] <== value_starting_index[i-1] + i * (1-is_zero_mask[i]) * is_prev_starting_index[i];
}

log("value starting index", value_starting_index[DATA_BYTES-1]);
value <== SelectSubArray(DATA_BYTES, maxValueLen)(mask, value_starting_index[DATA_BYTES-1], maxValueLen);
for (var i = 0 ; i < maxValueLen ; i++) {
log(i, value[i]);
}
}

template ArrayIndexExtractor(DATA_BYTES, MAX_STACK_HEIGHT, maxValueLen) {
assert(MAX_STACK_HEIGHT >= 2);

signal input data[DATA_BYTES];
signal input index;

signal output value[maxValueLen];

// value starting index in `data`
signal value_starting_index[DATA_BYTES];
// final mask
signal mask[DATA_BYTES];

component State[DATA_BYTES];
State[0] = StateUpdate(MAX_STACK_HEIGHT);
State[0].byte <== data[0];
for(var i = 0; i < MAX_STACK_HEIGHT; i++) {
State[0].stack[i] <== [0,0];
}
State[0].parsing_string <== 0;
State[0].parsing_number <== 0;

signal parsing_array[DATA_BYTES];
signal or[DATA_BYTES];

parsing_array[0] <== InsideArrayIndexObject()(State[0].next_stack[0], State[0].next_stack[1], State[0].next_parsing_string, State[0].next_parsing_number, index);
mask[0] <== data[0] * parsing_array[0];

for(var data_idx = 1; data_idx < DATA_BYTES; data_idx++) {
State[data_idx] = StateUpdate(MAX_STACK_HEIGHT);
State[data_idx].byte <== data[data_idx];
State[data_idx].stack <== State[data_idx - 1].next_stack;
State[data_idx].parsing_string <== State[data_idx - 1].next_parsing_string;
State[data_idx].parsing_number <== State[data_idx - 1].next_parsing_number;

parsing_array[data_idx] <== InsideArrayIndexObject()(State[data_idx].next_stack[0], State[data_idx].next_stack[1], State[data_idx].next_parsing_string, State[data_idx].next_parsing_number, index);

or[data_idx] <== OR()(parsing_array[data_idx], parsing_array[data_idx - 1]);
mask[data_idx] <== data[data_idx] * or[data_idx];
}

signal is_zero_mask[DATA_BYTES];
signal is_prev_starting_index[DATA_BYTES];
value_starting_index[0] <== 0;
is_prev_starting_index[0] <== 0;
is_zero_mask[0] <== IsZero()(mask[0]);
for (var i=1 ; i<DATA_BYTES ; i++) {
is_zero_mask[i] <== IsZero()(mask[i]);
is_prev_starting_index[i] <== IsZero()(value_starting_index[i-1]);
value_starting_index[i] <== value_starting_index[i-1] + i * (1-is_zero_mask[i]) * is_prev_starting_index[i];
}

log("value starting index", value_starting_index[DATA_BYTES-1]);
value <== SelectSubArray(DATA_BYTES, maxValueLen)(mask, value_starting_index[DATA_BYTES-1], maxValueLen);
for (var i = 0 ; i < maxValueLen ; i++) {
log(i, value[i]);
}
}
67 changes: 61 additions & 6 deletions circuits/json/interpreter.circom
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,6 @@ template InsideValueAtTop(n) {

/// Checks if current byte is inside a JSON value at specified depth
///
/// # Arguments
/// - `n`: maximum stack depth
/// - `depth`: stack height of parsed byte
///
/// # Inputs
/// - `stack`: current stack state
/// - `parsing_string`: whether current byte is inside a string or not
Expand All @@ -99,6 +95,34 @@ template InsideValue() {
out <== ifParsingValue * parsingStringXORNumber;
}

/// Checks if current byte is inside a JSON value at specified depth
///
/// # Inputs
/// - `stack`: current stack state
/// - `parsing_string`: whether current byte is inside a string or not
/// - `parsing_number`: wheter current byte is inside a number or not
///
/// # Output
/// - `out`: Returns `1` if current byte is inside a value
template InsideValueObject() {
signal input prev_stack[2];
signal input curr_stack[2];
signal input parsing_string;
signal input parsing_number;

signal output out;

signal insideObject <== IsEqual()([curr_stack[0], 1]);
signal insideArrayArray <== IsEqual()([curr_stack[0], 2]);

signal ifParsingValue <== prev_stack[0] * prev_stack[1];
signal parsingStringXORNumber <== XOR()(parsing_string, parsing_number);
signal insideObjectXORArray <== XOR()(insideObject, insideArrayArray);
signal isInsideObjectOrStringValue <== Mux1()([parsingStringXORNumber, insideObjectXORArray], insideObjectXORArray);

out <== ifParsingValue * isInsideObjectOrStringValue;
}

/// Checks if current byte is inside an array at specified index
///
/// # Arguments
Expand Down Expand Up @@ -134,9 +158,7 @@ template InsideArrayIndexAtTop(n, index) {
/// Checks if current byte is inside an array index at specified depth
///
/// # Arguments
/// - `n`: maximum stack depth
/// - `index`: array element index
/// - `depth`: stack height of parsed byte
///
/// # Inputs
/// - `stack`: current stack state
Expand All @@ -158,6 +180,39 @@ template InsideArrayIndex(index) {
out <== insideArrayIndex * (parsing_string + parsing_number);
}

/// Checks if current byte is inside an array index at specified depth
///
/// # Arguments
/// - `index`: array element index
///
/// # Inputs
/// - `stack`: current stack state
/// - `parsing_string`: whether current byte is inside a string or not
/// - `parsing_number`: wheter current byte is inside a number or not
///
/// # Output
/// - `out`: Returns `1` if current byte is inside an array index
template InsideArrayIndexObject() {
signal input prev_stack[2];
signal input curr_stack[2];
signal input parsing_string;
signal input parsing_number;
signal input index;

signal output out;

signal insideArray <== IsEqual()([prev_stack[0], 2]);
signal insideIndex <== IsEqual()([prev_stack[1], index]);
signal insideObject <== IsEqual()([curr_stack[0], 1]);
signal insideArrayArray <== IsEqual()([curr_stack[0], 2]);

signal parsingStringXORNumber <== XOR()(parsing_string, parsing_number);
signal insideObjectXORArray <== XOR()(insideObject, insideArrayArray);
signal isInsideObjectOrStringValue <== Mux1()([parsingStringXORNumber, insideObjectXORArray], insideObjectXORArray);
signal insideArrayIndex <== insideArray * insideIndex;
out <== insideArrayIndex * isInsideObjectOrStringValue;
}

/// Returns whether next key-value pair starts.
///
/// # Arguments
Expand Down
Loading

0 comments on commit 9dbc27a

Please sign in to comment.