Skip to content

Commit

Permalink
refactor (#10)
Browse files Browse the repository at this point in the history
* wip: start with bitmask

* WIP: time to start testing

* tests: `ArrayAdd` and `ArrayMul`

* tests passing

* update comments
  • Loading branch information
Autoparallel authored Aug 8, 2024
1 parent 07fd671 commit a62dd35
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 42 deletions.
4 changes: 0 additions & 4 deletions circuits/extract.circom
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ template Extract(KEY_BYTES, DATA_BYTES) {
State[0].inside_key <== 0;
State[0].parsing_to_value <== 0;
State[0].inside_value <== 0;
State[0].escaping <== 0;
State[0].end_of_kv <== 0;

for(var data_pointer = 1; data_pointer < DATA_BYTES; data_pointer++) {
Expand All @@ -42,9 +41,6 @@ template Extract(KEY_BYTES, DATA_BYTES) {
State[data_pointer].parsing_to_value <== State[data_pointer - 1].next_parsing_to_value;
State[data_pointer].inside_value <== State[data_pointer - 1].next_inside_value;
State[data_pointer].end_of_kv <== State[data_pointer - 1].next_end_of_kv;
// TODO: For the next state, we should use `next_`, this is only to make this compile for now.
State[data_pointer].escaping <== State[data_pointer - 1].escaping;


// Debugging
log("State[", data_pointer, "].tree_depth", "= ", State[data_pointer].tree_depth);
Expand Down
20 changes: 20 additions & 0 deletions circuits/operators.circom
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,23 @@ template Contains(n) {
// Apply `not` to this by 1-x
out <== 1 - someEqual.out;
}

template ArrayAdd(n) {
signal input lhs[n];
signal input rhs[n];
signal output out[n];

for(var i = 0; i < n; i++) {
out[i] <== lhs[i] + rhs[i];
}
}

template ArrayMul(n) {
signal input lhs[n];
signal input rhs[n];
signal output out[n];

for(var i = 0; i < n; i++) {
out[i] <== lhs[i] * rhs[i];
}
}
105 changes: 70 additions & 35 deletions circuits/parser.circom
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ template StateUpdate() {
signal input parsing_to_value; // BIT_FLAG -- whether we are currently parsing bytes until we find the next value (mutually exclusive with `inside_value` and both `*_key` flags).
signal input inside_value; // BIT_FLAG -- whether we are currently inside a value (mutually exclusive with `parsing_to_value` and both `*_key` flags).

signal input escaping; // BIT_FLAG -- whether we have hit an escape ASCII symbol inside of a key or value.
// signal input escaping; // BIT_FLAG -- whether we have hit an escape ASCII symbol inside of a key or value.

signal input end_of_kv; // BIT_FLAG -- reached end of key-value sequence, looking for comma delimiter or end of file signified by `tree_depth == 0`.

Expand Down Expand Up @@ -91,47 +91,45 @@ template StateUpdate() {
//-MACHINE INSTRUCTIONS-----------------------------------------------------------------------//
// TODO: ADD CASE FOR `is_number` for in range 48-57 https://www.ascii-code.com since a value may just be a number
// Output management
component matcher = Switch(8, 4);
var do_nothing[4] = [ 0, 0, 0, 0]; // Command returned by switch if we want to do nothing, e.g. read a whitespace char while looking for a key
var increase_depth[4] = [ 1, 0, 0, 0]; // Command returned by switch if we hit a start brace `{`
var decrease_depth[4] = [-1, 0, 0, 0]; // Command returned by switch if we hit a end brace `}`
var hit_quote[4] = [ 0, 1, 0, 0]; // Command returned by switch if we hit a quote `"`
var hit_colon[4] = [ 0, 0, 1, 0]; // Command returned by switch if we hit a colon `:`
var hit_comma[4] = [ 0, 0, 0, 1];

matcher.branches <== [start_brace, end_brace, quote, colon, comma, start_bracket, end_bracket, escape ];
matcher.vals <== [increase_depth, decrease_depth, hit_quote, hit_colon, hit_comma, do_nothing, do_nothing, do_nothing];
component matcher = Switch(5, 6);
component mask = StateToMask();
var state[6] = [tree_depth, parsing_to_key, inside_key, parsing_to_value, inside_value, end_of_kv];
mask.state <== state;
var do_nothing[6] = [ 0, 0, 0, 0, 0, 0 ]; // Command returned by switch if we want to do nothing, e.g. read a whitespace char while looking for a key
var hit_start_brace[6] = [ 1, 0, 0, 0, 0, 0 ]; // Command returned by switch if we hit a start brace `{`
var hit_end_brace[6] = [-1, 0, 0, 0, 0, 0 ]; // Command returned by switch if we hit a end brace `}`
var hit_quote[6] = [ 0, -1, 1, -1, 1, 1 ]; // Command returned by switch if we hit a quote `"`
var hit_colon[6] = [ 0, 0, 0, 1, 0, 0 ]; // Command returned by switch if we hit a colon `:`
var hit_comma[6] = [ 0, 1, 0, 0, 0, -1 ];

matcher.branches <== [start_brace, end_brace, quote, colon, comma ];
matcher.vals <== [hit_start_brace, hit_end_brace, hit_quote, hit_colon, hit_comma];
matcher.case <== byte;

// log("byte: ", byte);

// TODO: These could likely go into a switch statement with the output of the `Switch` above.
// TODO: Also could probably clean up things with de Morgan's laws or whatever.
// TODO: Could also clean this up and reduce constraints using PREV/CURR states like with `end_of_kv`
// An `IF ELSE` template would also be handy!
signal NOT_PARSING_TO_KEY_AND_NOT_INSIDE_KEY <== (1 - parsing_to_key) * (1 - inside_key);
signal NOT_PARSING_TO_VALUE_AND_PREV_INSIDE_VALUE <== (1 - parsing_to_value) * inside_value; // (NOT `parsing_to_value`) AND (NOT `inside_value`)
next_inside_value <== inside_value + (parsing_to_value - inside_value) * matcher.out[1]; // IF (`parsing_to_value` AND `hit_quote`) THEN `next_inside_value <== 1` ELSEIF (`inside_value` AND `hit_quote`) THEN `next_inside_value <==0`
// -note: can rewrite as -> `(1 - inside_value) * matcher_out[1] + parsing_to_value * matcher.out[1]
signal NOT_PARSING_TO_VALUE_AND_PREV_INSIDE_VALUE_AND_NOT_CURR_INSIDE_VALUE <== NOT_PARSING_TO_VALUE_AND_PREV_INSIDE_VALUE * (1 - next_inside_value);
signal NOT_PARSING_TO_KEY_AND_NOT_INSIDE_KEY_AND_NOT_PARSING_TO_VALUE_AND_PREV_INSIDE_VALUE_AND_NOT_CURR_INSIDE_VALUE <== NOT_PARSING_TO_KEY_AND_NOT_INSIDE_KEY * NOT_PARSING_TO_VALUE_AND_PREV_INSIDE_VALUE_AND_NOT_CURR_INSIDE_VALUE;
next_end_of_kv <== (end_of_kv - matcher.out[3]) + NOT_PARSING_TO_KEY_AND_NOT_INSIDE_KEY_AND_NOT_PARSING_TO_VALUE_AND_PREV_INSIDE_VALUE_AND_NOT_CURR_INSIDE_VALUE; // IF ((NOT `parsing_to_key`) AND (NOT `inside_key`)) AND (NOT(`parsing_to_value`) AND NOT( `inside_value)) THEN `next_end_of_kv <== 1`

component mulMaskAndOut = ArrayMul(6);
mulMaskAndOut.lhs <== mask.mask;
mulMaskAndOut.rhs <== matcher.out;

next_inside_key <== inside_key + (parsing_to_key - inside_key) * matcher.out[1]; // IF (`parsing_to_key` AND `hit_quote`) THEN `next_inside_key <== 1` ELSEIF (`inside_key` AND `hit_quote`) THEN `next_inside_key <== 0`
// - note: can rewrite as -> `inside_key * (1-matcher.out[1]) + parsing_to_key * matcher.out[1]`, but this will not be quadratic (according to circom)
signal END_OF_KV_AND_HIT_COMMA <== end_of_kv * (matcher.out[3]);
next_parsing_to_key <== parsing_to_key * (1 - matcher.out[1]) + END_OF_KV_AND_HIT_COMMA; // IF (`parsing_to_key` AND `hit_quote`) THEN `parsing_to_key <== 0`
component addToState = ArrayAdd(6);
addToState.lhs <== state;
addToState.rhs <== mulMaskAndOut.out;

// for(var i = 0; i<6; i++) {
// log("mask[", i,"]: ", mask.mask[i]);
// log("mulMaskAndOut[ ", i,"]: ", mulMaskAndOut.out[i]);
// log("addToState[ ", i,"]: ", addToState.out[i]);
// }


// (NOT `parsing_to_key`) AND (NOT `inside_key`)
signal PARSING_TO_VALUE_AND_NOT_HIT_QUOTE <== parsing_to_value * (1 - matcher.out[1]); // `parsing_to_value` AND (NOT `hit_quote`)
signal PARSING_TO_VALUE_AND_NOT_HIT_QUOTE_AND_NOT_HIT_BRACE <== PARSING_TO_VALUE_AND_NOT_HIT_QUOTE * (1 - matcher.out[0]);
next_parsing_to_value <== PARSING_TO_VALUE_AND_NOT_HIT_QUOTE_AND_NOT_HIT_BRACE + NOT_PARSING_TO_KEY_AND_NOT_INSIDE_KEY * matcher.out[2]; // IF (`parsing_to_value` AND (NOT `hit_quote`)) THEN `next_parsing_to_value <== 1 ELSEIF ((NOT `parsing_to_value` AND (NOT `inside_value)) AND `hit_colon`) THEN `next_parsing_to_value <== 1`
next_tree_depth <== addToState.out[0];
next_parsing_to_key <== addToState.out[1];
next_inside_key <== addToState.out[2];
next_parsing_to_value <== addToState.out[3];
next_inside_value <== addToState.out[4];
next_end_of_kv <== addToState.out[5];


// TODO: Assert this never goes below zero (mod p)
next_tree_depth <== tree_depth + (parsing_to_key + next_end_of_kv) * matcher.out[0]; // IF ((`parsing_to_key` OR `next_end_of_kv`) AND `read_brace` THEN `increase/decrease_depth`
// log("next_inside_key: ", next_inside_key);

// Constrain bit flags
next_parsing_to_key * (1 - next_parsing_to_key) === 0; // - constrain that `next_parsing_to_key` remain a bit flag
Expand Down Expand Up @@ -194,4 +192,41 @@ template Switch(m, n) {
match <== matchChecker.out;

out <== sum;
}

// TODO: Note at the moment mask 2 and 4 are the same, so this can be removed if it maintains.
template StateToMask() {
signal input state[6];
signal output mask[6];

var tree_depth = state[0];
var parsing_to_key = state[1];
var inside_key = state[2];
var parsing_to_value = state[3];
var inside_value = state[4];
var end_of_kv = state[5];

signal NOT_INSIDE_KEY_AND_NOT_INSIDE_VALUE <-- (1 - inside_key) * (1 - inside_value);
signal NOT_PARSING_TO_KEY_AND_NOT_PARSING_TO_VALUE <-- (1 - parsing_to_key) * (1 - parsing_to_value);
signal NOT_PARSING_TO_VALUE_NOT_INSIDE_VALUE <-- (1 - parsing_to_value) * (1 - inside_value);

// `tree_depth` can change: `IF (parsing_to_key XOR parsing_to_value)`
mask[0] <== parsing_to_key + parsing_to_value; // TODO: Make sure these are never both 1!

// `parsing_to_key` can change: `IF ((NOT inside_key) AND (NOT inside_value) AND (NOT parsing_to_value))`
mask[1] <== NOT_INSIDE_KEY_AND_NOT_INSIDE_VALUE * (1 - parsing_to_value);

// `inside_key` can change: `IF ((NOT parsing_to_value) AND (NOT inside_value)) THEN TOGGLE WITH inside_key`
signal inside_key_toggle <-- (-1)**inside_key;
mask[2] <== NOT_PARSING_TO_VALUE_NOT_INSIDE_VALUE * inside_key_toggle;

// `parsing_to_value` can change: `IF ((NOT parsing_to_key) AND (NOT inside_key) AND (NOT inside_value))`
mask[3] <== (1 - parsing_to_key) * NOT_INSIDE_KEY_AND_NOT_INSIDE_VALUE;

// `inside_value` can change: `IF ((NOT parsing_to_key) AND (C_NOR (inside_value, parsing_to value)))`
// control----------^
mask[4] <== (1 - parsing_to_key) * (parsing_to_value - inside_value);

// `end_of_kv` can change: `IF ((NOT inside_key) AND (NOT parsing_to_key) AND (NOT parsing_to_value))
mask[5] <== (1 - inside_key) * NOT_PARSING_TO_KEY_AND_NOT_PARSING_TO_VALUE;
}
40 changes: 40 additions & 0 deletions circuits/test/operators.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,44 @@ describe("operators", () => {
});

});

describe("ArrayAdd", () => {
let circuit: WitnessTester<["lhs", "rhs"], ["out"]>;
before(async () => {
circuit = await circomkit.WitnessTester(`ArrayAdd`, {
file: "circuits/operators",
template: "ArrayAdd",
params: [3],
});
console.log("#constraints:", await circuit.getConstraintCount());
});

it("witness: lhs = [0,1,2], rhs = [3,5,7]", async () => {
await circuit.expectPass(
{ lhs: [0, 1, 2], rhs: [3, 5, 7] },
{ out: [3, 6, 9] }
);
});

});

describe("ArrayMul", () => {
let circuit: WitnessTester<["lhs", "rhs"], ["out"]>;
before(async () => {
circuit = await circomkit.WitnessTester(`ArrayMul`, {
file: "circuits/operators",
template: "ArrayMul",
params: [3],
});
console.log("#constraints:", await circuit.getConstraintCount());
});

it("witness: lhs = [0,1,2], rhs = [3,5,7]", async () => {
await circuit.expectPass(
{ lhs: [0, 1, 2], rhs: [3, 5, 7] },
{ out: [0, 5, 14] }
);
});

});
});
5 changes: 2 additions & 3 deletions circuits/test/parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ describe("parser", () => {

describe("StateUpdate", () => {
let circuit: WitnessTester<
["byte", "tree_depth", "parsing_to_key", "inside_key", "parsing_to_value", "inside_value", "escaping", "end_of_kv"],
["byte", "tree_depth", "parsing_to_key", "inside_key", "parsing_to_value", "inside_value", "end_of_kv"],
["next_tree_depth", "next_parsing_to_key", "next_inside_key", "next_parsing_to_value", "next_inside_value", "next_end_of_kv"]
>;

Expand Down Expand Up @@ -126,7 +126,6 @@ describe("parser", () => {
inside_key: 0,
parsing_to_value: 0,
inside_value: 0,
escaping: 0,
end_of_kv: 0,
};

Expand Down Expand Up @@ -176,7 +175,7 @@ describe("parser", () => {
in_key_out.next_tree_depth = 1;
generatePassCase(in_key, in_key_out, "`tree_depth == 1` AND `inside_key == 1 AND `parsing_to_key == 0` setup -> ` ` is read");

// Test 6: `tree_depth == 1` AND `inside_key ==1 AND `parsing_to_key == 0` setup -> `"` is read
// Test 6: `tree_depth == 1` AND `inside_key == 1 AND `parsing_to_key == 0` setup -> `"` is read
let in_key_to_exit = { ...init };
in_key_to_exit.tree_depth = 1;
in_key_to_exit.parsing_to_key = 0;
Expand Down

0 comments on commit a62dd35

Please sign in to comment.