From d2be62436ff390e0ee6635030172119a0887dcde Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Wed, 7 Aug 2024 16:11:43 -0600 Subject: [PATCH 01/24] add: `reddit_response.json` --- json_examples/reddit_response.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 json_examples/reddit_response.json diff --git a/json_examples/reddit_response.json b/json_examples/reddit_response.json new file mode 100644 index 0000000..163e6b4 --- /dev/null +++ b/json_examples/reddit_response.json @@ -0,0 +1,14 @@ +{ + "data": { + "redditorInfoByName": { + "id": "t2_bepsb", + "karma": { + "fromAwardsGiven": 0, + "fromAwardsReceived": 470, + "fromComments": 9583, + "fromPosts": 13228, + "total": 23281 + } + } + } +} \ No newline at end of file From 6d4b7c3e76de22e34fdff5c3325b1f327f98b131 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 8 Aug 2024 07:13:16 -0600 Subject: [PATCH 02/24] refactor tests + add failing case --- circuits/parser.circom | 6 +++ circuits/test/parser.test.ts | 73 +++++++++++++++++++++++++++++------- 2 files changed, 65 insertions(+), 14 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 3eb446d..93b570f 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -129,6 +129,12 @@ template StateUpdate() { next_inside_value * (1 - next_inside_value) === 0; // - constrain that `next_inside_value` remain a bit flag next_end_of_kv * (1 - next_end_of_kv) === 0; // - constrain that `next_end_of_kv` remain a bit flag + component depthIsZero = IsZero(); + depthIsZero.in <== tree_depth; // Determine if `tree_depth` was `0` + component isOneLess = IsEqual(); + isOneLess.in[0] <== -1; + isOneLess.in[1] <== matcher.out[0]; // Determine if instruction was to `decrease_depth` + depthIsZero.out * isOneLess.out === 0; // IF ( `decrease_depth` AND `tree_depth == 0`) THEN FAIL // TODO: Can hit comma and then be sent to next KV, so comma will engage `parsing_to_key` //--------------------------------------------------------------------------------------------// } diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index 5f3488d..9460550 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -56,29 +56,69 @@ describe("parser", () => { }); + //--------------------------------------------------------------------------------------------// + //-Delimeters---------------------------------------------------------------------------------// + // - ASCII char: `{` + const start_brace = 123; + // - ASCII char: `}` + const end_brace = 125; + // - ASCII char `[` + const start_bracket = 91; + // - ASCII char `]` + const end_bracket = 93; + // - ASCII char `"` + const quote = 34; + // - ASCII char `:` + const colon = 58; + // - ASCII char `,` + const comma = 44; + //--------------------------------------------------------------------------------------------// + // White space + // - ASCII char: `\n` + const newline = 10; + // - ASCII char: ` ` + const space = 32; + //--------------------------------------------------------------------------------------------// + // Escape + // - ASCII char: `\` + const escape = 92; + //--------------------------------------------------------------------------------------------// + describe("StateUpdate", () => { let circuit: WitnessTester< ["byte", "tree_depth", "parsing_to_key", "inside_key", "parsing_to_value", "inside_value", "escaping", "end_of_kv"], ["next_tree_depth", "next_parsing_to_key", "next_inside_key", "next_parsing_to_value", "next_inside_value", "next_end_of_kv"] >; - function generateTestCase(input: any, expected: any) { + function generatePassCase(input: any, expected: any) { const description = Object.entries(input) .map(([key, value]) => `${key} = ${value}`) .join(", "); - it(`witness: ${description}`, async () => { + it(`(valid) witness: ${description}`, async () => { + circuit = await circomkit.WitnessTester(`StateUpdate`, { + file: "circuits/parser", + template: "StateUpdate", + }); + console.log("#constraints:", await circuit.getConstraintCount()); await circuit.expectPass(input, expected); }); } - before(async () => { - circuit = await circomkit.WitnessTester(`StateUpdate`, { - file: "circuits/parser", - template: "StateUpdate", + function generateFailCase(input: any) { + const description = Object.entries(input) + .map(([key, value]) => `${key} = ${value}`) + .join(", "); + + it(`(invalid) witness: ${description}`, async () => { + circuit = await circomkit.WitnessTester(`StateUpdate`, { + file: "circuits/parser", + template: "StateUpdate", + }); + console.log("#constraints:", await circuit.getConstraintCount()); + await circuit.expectFail(input); }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); + } let init = { byte: 0, @@ -98,17 +138,22 @@ describe("parser", () => { next_inside_key: init.inside_key, next_parsing_to_value: init.parsing_to_value, next_inside_value: init.inside_value, - next_end_of_kv: init.end_of_kv + next_end_of_kv: init.end_of_kv, }; - generateTestCase(init, out); + + generatePassCase(init, out); // Test 2: init setup -> `{` is read - let read_start_brace = init; - read_start_brace.byte = 123; - let read_start_brace_out = out; + let read_start_brace = { ...init }; + read_start_brace.byte = start_brace; + let read_start_brace_out = { ...out }; read_start_brace_out.next_tree_depth = 1; - generateTestCase(read_start_brace, read_start_brace_out); + generatePassCase(read_start_brace, read_start_brace_out); + // Test 3: init setup -> `}` is read (should be INVALID) + let read_end_brace = { ...init }; + read_end_brace.byte = end_brace; + generateFailCase(read_end_brace); }); }); From afcacdaa543186c57f781b972a5068f871d0fbb5 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 8 Aug 2024 07:21:47 -0600 Subject: [PATCH 03/24] easier fix --- circuits/test/parser.test.ts | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index 9460550..c7f172f 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -96,11 +96,6 @@ describe("parser", () => { .join(", "); it(`(valid) witness: ${description}`, async () => { - circuit = await circomkit.WitnessTester(`StateUpdate`, { - file: "circuits/parser", - template: "StateUpdate", - }); - console.log("#constraints:", await circuit.getConstraintCount()); await circuit.expectPass(input, expected); }); } @@ -111,15 +106,19 @@ describe("parser", () => { .join(", "); it(`(invalid) witness: ${description}`, async () => { - circuit = await circomkit.WitnessTester(`StateUpdate`, { - file: "circuits/parser", - template: "StateUpdate", - }); - console.log("#constraints:", await circuit.getConstraintCount()); await circuit.expectFail(input); }); } + before(async () => { + circuit = await circomkit.WitnessTester(`StateUpdate`, { + file: "circuits/parser", + template: "StateUpdate", + }); + console.log("#constraints:", await circuit.getConstraintCount()); + + }); + let init = { byte: 0, tree_depth: 0, @@ -138,7 +137,7 @@ describe("parser", () => { next_inside_key: init.inside_key, next_parsing_to_value: init.parsing_to_value, next_inside_value: init.inside_value, - next_end_of_kv: init.end_of_kv, + next_end_of_kv: init.end_of_kv }; generatePassCase(init, out); @@ -154,6 +153,8 @@ describe("parser", () => { let read_end_brace = { ...init }; read_end_brace.byte = end_brace; generateFailCase(read_end_brace); + + }); }); From 99e0e5cb597f98b6b990a6d634b3328912f0b372 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 8 Aug 2024 07:54:26 -0600 Subject: [PATCH 04/24] test: parse to key --- circuits/test/parser.test.ts | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index c7f172f..765eca9 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -90,22 +90,22 @@ describe("parser", () => { ["next_tree_depth", "next_parsing_to_key", "next_inside_key", "next_parsing_to_value", "next_inside_value", "next_end_of_kv"] >; - function generatePassCase(input: any, expected: any) { + function generatePassCase(input: any, expected: any, desc: string) { const description = Object.entries(input) .map(([key, value]) => `${key} = ${value}`) .join(", "); - it(`(valid) witness: ${description}`, async () => { + it(`(valid) witness: ${description}\n${desc}`, async () => { await circuit.expectPass(input, expected); }); } - function generateFailCase(input: any) { + function generateFailCase(input: any, desc: string) { const description = Object.entries(input) .map(([key, value]) => `${key} = ${value}`) .join(", "); - it(`(invalid) witness: ${description}`, async () => { + it(`(invalid) witness: ${description}\n${desc}`, async () => { await circuit.expectFail(input); }); } @@ -140,21 +140,29 @@ describe("parser", () => { next_end_of_kv: init.end_of_kv }; - generatePassCase(init, out); + generatePassCase(init, out, "init setup -> `do_nothing` byte"); // Test 2: init setup -> `{` is read let read_start_brace = { ...init }; read_start_brace.byte = start_brace; let read_start_brace_out = { ...out }; read_start_brace_out.next_tree_depth = 1; - generatePassCase(read_start_brace, read_start_brace_out); + generatePassCase(read_start_brace, read_start_brace_out, "init setup -> `{` is read"); // Test 3: init setup -> `}` is read (should be INVALID) let read_end_brace = { ...init }; read_end_brace.byte = end_brace; - generateFailCase(read_end_brace); - - + generateFailCase(read_end_brace, "init setup -> `}` is read (NEGATIVE TREE DEPTH!)"); + + // Test 4: `tree_depth == 1` setup -> `"` is read + let in_tree_find_key = { ...init }; + in_tree_find_key.tree_depth = 1; + in_tree_find_key.byte = quote; + let in_tree_find_key_out = { ...out }; + in_tree_find_key_out.next_inside_key = 1; + in_tree_find_key_out.next_parsing_to_key = 0; + in_tree_find_key_out.next_tree_depth = 1; + generatePassCase(in_tree_find_key, in_tree_find_key_out, "`tree_depth == 1` setup -> `\"` is read"); }); }); From 3a20a47f56839c9a69e261c7a75743ad61c0a7c8 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 8 Aug 2024 07:59:17 -0600 Subject: [PATCH 05/24] tests: key parsing --- circuits/test/parser.test.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index 765eca9..6d9f023 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -163,6 +163,30 @@ describe("parser", () => { in_tree_find_key_out.next_parsing_to_key = 0; in_tree_find_key_out.next_tree_depth = 1; generatePassCase(in_tree_find_key, in_tree_find_key_out, "`tree_depth == 1` setup -> `\"` is read"); + + // Test 5: `tree_depth == 1` AND `inside_key ==1` setup -> ` ` is read + let in_key = { ...init }; + in_key.tree_depth = 1; + in_key.parsing_to_key = 0; + in_key.inside_key = 1; + in_key.byte = space; + let in_key_out = { ...out }; + in_key_out.next_inside_key = 1; + in_key_out.next_parsing_to_key = 0; + 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" + let in_key_to_exit = { ...init }; + in_key_to_exit.tree_depth = 1; + in_key_to_exit.parsing_to_key = 0; + in_key_to_exit.inside_key = 1; + in_key_to_exit.byte = quote; + let in_key_to_exit_out = { ...out }; + in_key_to_exit_out.next_inside_key = 0; + in_key_to_exit_out.next_parsing_to_key = 0; + in_key_to_exit_out.next_tree_depth = 1; + generatePassCase(in_key_to_exit, in_key_to_exit_out, "`tree_depth == 1` AND `inside_key == 1 AND `parsing_to_key == 0` setup -> `\"` is read"); }); }); From 98a3b51004c639a74aece0c90ad7931dbd8b531e Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 8 Aug 2024 08:08:26 -0600 Subject: [PATCH 06/24] bug: `next_end_of_kv` on read `:` --- circuits/test/parser.test.ts | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index 6d9f023..484f8a2 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -176,7 +176,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; @@ -187,6 +187,24 @@ describe("parser", () => { in_key_to_exit_out.next_parsing_to_key = 0; in_key_to_exit_out.next_tree_depth = 1; generatePassCase(in_key_to_exit, in_key_to_exit_out, "`tree_depth == 1` AND `inside_key == 1 AND `parsing_to_key == 0` setup -> `\"` is read"); + + // Test 7: `tree_depth == 1` AND parsed through key` setup -> `:` is read + let parsed_key_wait_to_parse_value = { ...init }; + parsed_key_wait_to_parse_value.tree_depth = 1; + parsed_key_wait_to_parse_value.parsing_to_key = 0; + parsed_key_wait_to_parse_value.byte = colon; + let parsed_key_wait_to_parse_value_out = { ...out }; + parsed_key_wait_to_parse_value_out.next_tree_depth = 1; + parsed_key_wait_to_parse_value_out.next_parsing_to_key = 0; + parsed_key_wait_to_parse_value_out.next_parsing_to_value = 1; + generatePassCase(parsed_key_wait_to_parse_value, parsed_key_wait_to_parse_value_out, "`tree_depth == 1` AND parsed through key` setup -> `:` is read"); + + // State[ 13 ].tree_depth = 1 + // State[ 13 ].parsing_to_key = 0 + // State[ 13 ].inside_key = 0 + // State[ 13 ].parsing_to_value = 1 + // State[ 13 ].inside_value = 0 + // State[ 13 ].end_of_kv = 1 <<<< TODO: THIS IS NOT RIGHT }); }); From 44bcc18ac3a12b8d19aa2350185b7e190b4f8cc1 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 8 Aug 2024 09:09:30 -0600 Subject: [PATCH 07/24] fix: `end_of_kv` bug --- circuits/parser.circom | 7 ++++--- circuits/test/parser.test.ts | 7 ------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 93b570f..1751bc4 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -115,12 +115,13 @@ template StateUpdate() { signal PARSING_TO_VALUE_AND_NOT_HIT_QUOTE <== parsing_to_value * (1 - matcher.out[1]); // `parsing_to_value` AND (NOT `hit_quote`) next_parsing_to_value <== PARSING_TO_VALUE_AND_NOT_HIT_QUOTE + 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` - signal NOT_PARSING_TO_VALUE_AND_NOT_INSIDE_VALUE <== (1 - parsing_to_value) * (1 - inside_value); // (NOT `parsing_to_value`) AND (NOT `inside_value`) - next_end_of_kv <== NOT_PARSING_TO_KEY_AND_NOT_INSIDE_KEY * NOT_PARSING_TO_VALUE_AND_NOT_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` + signal NOT_PARSING_TO_VALUE_AND_PREV_INSIDE_VALUE <== (1 - parsing_to_value) * inside_value; // (NOT `parsing_to_value`) AND (NOT `inside_value`) + 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); + next_end_of_kv <== end_of_kv + NOT_PARSING_TO_KEY_AND_NOT_INSIDE_KEY * 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` // 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` + 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` // Constrain bit flags next_parsing_to_key * (1 - next_parsing_to_key) === 0; // - constrain that `next_parsing_to_key` remain a bit flag diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index 484f8a2..a01e71c 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -198,13 +198,6 @@ describe("parser", () => { parsed_key_wait_to_parse_value_out.next_parsing_to_key = 0; parsed_key_wait_to_parse_value_out.next_parsing_to_value = 1; generatePassCase(parsed_key_wait_to_parse_value, parsed_key_wait_to_parse_value_out, "`tree_depth == 1` AND parsed through key` setup -> `:` is read"); - - // State[ 13 ].tree_depth = 1 - // State[ 13 ].parsing_to_key = 0 - // State[ 13 ].inside_key = 0 - // State[ 13 ].parsing_to_value = 1 - // State[ 13 ].inside_value = 0 - // State[ 13 ].end_of_kv = 1 <<<< TODO: THIS IS NOT RIGHT }); }); From fb0fb34ec5e4d30f3250321f062124eaf10bbb13 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 8 Aug 2024 09:14:36 -0600 Subject: [PATCH 08/24] test: find value --- circuits/test/parser.test.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index a01e71c..45a72b1 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -198,6 +198,19 @@ describe("parser", () => { parsed_key_wait_to_parse_value_out.next_parsing_to_key = 0; parsed_key_wait_to_parse_value_out.next_parsing_to_value = 1; generatePassCase(parsed_key_wait_to_parse_value, parsed_key_wait_to_parse_value_out, "`tree_depth == 1` AND parsed through key` setup -> `:` is read"); + + // Test 8: `tree_depth == 1` AND parsing_to_value` setup -> `"` is read + let in_tree_find_value = { ...init }; + in_tree_find_value.tree_depth = 1; + in_tree_find_value.parsing_to_key = 0; + in_tree_find_value.parsing_to_value = 1; + in_tree_find_value.byte = quote; + let in_tree_find_value_out = { ...out }; + in_tree_find_value_out.next_tree_depth = 1; + in_tree_find_value_out.next_parsing_to_key = 0; + in_tree_find_value_out.next_parsing_to_value = 0; + in_tree_find_value_out.next_inside_value = 1; + generatePassCase(in_tree_find_value, in_tree_find_value_out, "`tree_depth == 1` AND parsing_to_value` setup -> `\"` is read"); }); }); From 0f82ab059a28d94a41c858101ab3ef3a1180cd6c Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 8 Aug 2024 09:21:32 -0600 Subject: [PATCH 09/24] tests: `inside_value` and `inside_value_to_exit` --- circuits/test/parser.test.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index 45a72b1..9719643 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -211,6 +211,33 @@ describe("parser", () => { in_tree_find_value_out.next_parsing_to_value = 0; in_tree_find_value_out.next_inside_value = 1; generatePassCase(in_tree_find_value, in_tree_find_value_out, "`tree_depth == 1` AND parsing_to_value` setup -> `\"` is read"); + + // Test 9: `tree_depth == 1` AND inside_value` setup -> ` ` is read + let in_value = { ...init }; + in_value.tree_depth = 1; + in_value.parsing_to_key = 0; + in_value.inside_value = 1; + in_value.byte = space; + let in_value_out = { ...out }; + in_value_out.next_tree_depth = 1; + in_value_out.next_parsing_to_key = 0; + in_value_out.next_parsing_to_value = 0; + in_value_out.next_inside_value = 1; + generatePassCase(in_value, in_value_out, "`tree_depth == 1` AND inside_value` setup -> ` ` is read"); + + // Test 10: `tree_depth == 1` AND inside_value` setup -> `"` is read + let in_value_to_exit = { ...init }; + in_value_to_exit.tree_depth = 1; + in_value_to_exit.parsing_to_key = 0; + in_value_to_exit.inside_value = 1; + in_value_to_exit.byte = quote; + let in_value_to_exit_out = { ...out }; + in_value_to_exit_out.next_tree_depth = 1; + in_value_to_exit_out.next_parsing_to_key = 0; + in_value_to_exit_out.next_parsing_to_value = 0; + in_value_to_exit_out.next_inside_value = 0; + in_value_to_exit_out.next_end_of_kv = 1; + generatePassCase(in_value_to_exit, in_value_to_exit_out, "`tree_depth == 1` AND inside_value` setup -> `\"` is read"); }); }); From 0b166ea31269ad66c760dd02170257c85ee41e4b Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 8 Aug 2024 09:49:14 -0600 Subject: [PATCH 10/24] test: parse to NEXT key --- circuits/parser.circom | 45 +++++++++++++++++++++--------------- circuits/test/parser.test.ts | 24 +++++++++++++++++++ 2 files changed, 50 insertions(+), 19 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 1751bc4..8d9c73e 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -89,36 +89,43 @@ 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, 3); - var do_nothing[3] = [ 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[3] = [ 1, 0, 0]; // Command returned by switch if we hit a start brace `{` - var decrease_depth[3] = [-1, 0, 0]; // Command returned by switch if we hit a end brace `}` - var hit_quote[3] = [ 0, 1, 0]; // Command returned by switch if we hit a quote `"` - var hit_colon[3] = [ 0, 0, 1]; // Command returned by switch if we hit a colon `:` - - matcher.branches <== [start_brace, end_brace, quote, colon, start_bracket, end_bracket, comma, escape ]; - matcher.vals <== [increase_depth, decrease_depth, hit_quote, hit_colon, do_nothing, do_nothing, do_nothing, do_nothing]; + 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]; matcher.case <== 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! - 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) - next_parsing_to_key <== parsing_to_key * (1 - matcher.out[1]); // IF (`parsing_to_key` AND `hit_quote`) THEN `parsing_to_key <== 0` + 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` + + + 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` + - 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_KEY_AND_NOT_INSIDE_KEY <== (1 - parsing_to_key) * (1 - inside_key); // (NOT `parsing_to_key`) AND (NOT `inside_key`) + // (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`) next_parsing_to_value <== PARSING_TO_VALUE_AND_NOT_HIT_QUOTE + 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` - signal NOT_PARSING_TO_VALUE_AND_PREV_INSIDE_VALUE <== (1 - parsing_to_value) * inside_value; // (NOT `parsing_to_value`) AND (NOT `inside_value`) - 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); - next_end_of_kv <== end_of_kv + NOT_PARSING_TO_KEY_AND_NOT_INSIDE_KEY * 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` - // 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` diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index 9719643..319ba6b 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -238,6 +238,30 @@ describe("parser", () => { in_value_to_exit_out.next_inside_value = 0; in_value_to_exit_out.next_end_of_kv = 1; generatePassCase(in_value_to_exit, in_value_to_exit_out, "`tree_depth == 1` AND inside_value` setup -> `\"` is read"); + + // Test 11: `tree_depth == 1` AND end_of_kv` setup -> ` ` is read + let in_end_of_kv = { ...init }; + in_end_of_kv.tree_depth = 1; + in_end_of_kv.parsing_to_key = 0; + in_end_of_kv.end_of_kv = 1; + in_end_of_kv.byte = space; + let in_end_of_kv_out = { ...out }; + in_end_of_kv_out.next_tree_depth = 1; + in_end_of_kv_out.next_parsing_to_key = 0; + in_end_of_kv_out.next_end_of_kv = 1; + generatePassCase(in_end_of_kv, in_end_of_kv_out, "`tree_depth == 1` AND end_of_kv` setup -> ` ` is read"); + + // Test 12: `tree_depth == 1` AND end_of_kv` setup -> `,` is read + let end_of_kv_to_parse_to_key = { ...init }; + end_of_kv_to_parse_to_key.tree_depth = 1; + end_of_kv_to_parse_to_key.parsing_to_key = 0; + end_of_kv_to_parse_to_key.end_of_kv = 1; + end_of_kv_to_parse_to_key.byte = comma; + let end_of_kv_to_parse_to_key_out = { ...out }; + end_of_kv_to_parse_to_key_out.next_tree_depth = 1; + end_of_kv_to_parse_to_key_out.next_parsing_to_key = 1; + end_of_kv_to_parse_to_key_out.next_end_of_kv = 0; + generatePassCase(end_of_kv_to_parse_to_key, end_of_kv_to_parse_to_key_out, "`tree_depth == 1` AND end_of_kv` setup -> ` ` is read"); }); }); From fc81c787349403ddd1e10eb6c8fe153a1f4b2992 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 8 Aug 2024 09:55:01 -0600 Subject: [PATCH 11/24] parses JSON with two string keys --- circuits.json | 8 +++++ create_witness/src/main.rs | 4 +-- inputs/test_extract_two_key/input.json | 50 ++++++++++++++++++++++++++ json_examples/test_two_key.json | 4 +++ 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 inputs/test_extract_two_key/input.json create mode 100644 json_examples/test_two_key.json diff --git a/circuits.json b/circuits.json index 3308dae..517b2d7 100644 --- a/circuits.json +++ b/circuits.json @@ -7,6 +7,14 @@ 21 ] }, + "test_extract_two_key": { + "file": "extract", + "template": "Extract", + "params": [ + 4, + 40 + ] + }, "test_extract_hard": { "file": "extract", "template": "Extract", diff --git a/create_witness/src/main.rs b/create_witness/src/main.rs index 469478a..233e6b9 100644 --- a/create_witness/src/main.rs +++ b/create_witness/src/main.rs @@ -9,7 +9,7 @@ pub const KEYS: &[&[u8]] = &[ b"\"title\"".as_slice(), ]; // pub const DATA: &[u8] = include_bytes!("../../json_examples/example.json"); -pub const DATA: &[u8] = include_bytes!("../../json_examples/test.json"); +pub const DATA: &[u8] = include_bytes!("../../json_examples/test_two_key.json"); #[derive(serde::Serialize)] pub struct Witness { @@ -59,7 +59,7 @@ pub fn main() { // num_data_bytes: DATA.len(), // For now we can set this to be the same data: DATA.to_vec(), }; - let mut file = std::fs::File::create("inputs/test_extract/input.json").unwrap(); + let mut file = std::fs::File::create("inputs/test_extract/input_two_key.json").unwrap(); file.write_all(serde_json::to_string_pretty(&witness).unwrap().as_bytes()) .unwrap(); } diff --git a/inputs/test_extract_two_key/input.json b/inputs/test_extract_two_key/input.json new file mode 100644 index 0000000..619812a --- /dev/null +++ b/inputs/test_extract_two_key/input.json @@ -0,0 +1,50 @@ +{ + "key": [ + 107, + 101, + 121, + 49 + ], + "data": [ + 123, + 10, + 32, + 32, + 32, + 32, + 34, + 107, + 101, + 121, + 49, + 34, + 58, + 32, + 34, + 97, + 98, + 99, + 34, + 44, + 10, + 32, + 32, + 32, + 32, + 34, + 107, + 101, + 121, + 50, + 34, + 58, + 32, + 34, + 100, + 101, + 102, + 34, + 10, + 125 + ] +} \ No newline at end of file diff --git a/json_examples/test_two_key.json b/json_examples/test_two_key.json new file mode 100644 index 0000000..437a6c5 --- /dev/null +++ b/json_examples/test_two_key.json @@ -0,0 +1,4 @@ +{ + "key1": "abc", + "key2": "def" +} \ No newline at end of file From f1ebb51f866fbe61136b1fa593b2afb56e6a3934 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 8 Aug 2024 10:10:21 -0600 Subject: [PATCH 12/24] WIP: value inside value --- circuits/parser.circom | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 8d9c73e..2d25751 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -25,8 +25,9 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -Notes: -- If there is no comma after leaving a value, then we should not be parsing to key. If anything breaks here, JSON was bad. +TODOs: +- Handle case where the value is an another JSON. Shouldn't be too bad as we should just reset to init state with different tree depth +- In fact, we might not even need tree depth if we replace it with `inside_value` that is a counter as it represents the same thing! */ /* @@ -124,7 +125,8 @@ template StateUpdate() { // (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`) - next_parsing_to_value <== PARSING_TO_VALUE_AND_NOT_HIT_QUOTE + 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` + 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` // TODO: Assert this never goes below zero (mod p) From 07fd671c1f7291d184845e35849890aa85b0a395 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 8 Aug 2024 10:26:16 -0600 Subject: [PATCH 13/24] comment --- circuits/parser.circom | 1 + 1 file changed, 1 insertion(+) diff --git a/circuits/parser.circom b/circuits/parser.circom index 2d25751..5965b3e 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -28,6 +28,7 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx TODOs: - Handle case where the value is an another JSON. Shouldn't be too bad as we should just reset to init state with different tree depth - In fact, we might not even need tree depth if we replace it with `inside_value` that is a counter as it represents the same thing! + - Actually, this may not work since multiple values exist at same height. Let's not change this yet. */ /* From a62dd351dc5678f85aae1727073a42435faa6180 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 8 Aug 2024 14:09:00 -0600 Subject: [PATCH 14/24] refactor (#10) * wip: start with bitmask * WIP: time to start testing * tests: `ArrayAdd` and `ArrayMul` * tests passing * update comments --- circuits/extract.circom | 4 -- circuits/operators.circom | 20 ++++++ circuits/parser.circom | 105 +++++++++++++++++++++----------- circuits/test/operators.test.ts | 40 ++++++++++++ circuits/test/parser.test.ts | 5 +- 5 files changed, 132 insertions(+), 42 deletions(-) diff --git a/circuits/extract.circom b/circuits/extract.circom index 2997cc7..0150cf8 100644 --- a/circuits/extract.circom +++ b/circuits/extract.circom @@ -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++) { @@ -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); diff --git a/circuits/operators.circom b/circuits/operators.circom index 27d6616..d62d5f0 100644 --- a/circuits/operators.circom +++ b/circuits/operators.circom @@ -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]; + } +} \ No newline at end of file diff --git a/circuits/parser.circom b/circuits/parser.circom index 5965b3e..36b61d3 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -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`. @@ -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 @@ -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; } \ No newline at end of file diff --git a/circuits/test/operators.test.ts b/circuits/test/operators.test.ts index 378708b..50baec2 100644 --- a/circuits/test/operators.test.ts +++ b/circuits/test/operators.test.ts @@ -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] } + ); + }); + + }); }); diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index 319ba6b..b6e1128 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -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"] >; @@ -126,7 +126,6 @@ describe("parser", () => { inside_key: 0, parsing_to_value: 0, inside_value: 0, - escaping: 0, end_of_kv: 0, }; @@ -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; From 63a00d542ce1404f190be07456054f468e9e2814 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 8 Aug 2024 14:18:38 -0600 Subject: [PATCH 15/24] feat: 2 key depth 1 json --- circuits/parser.circom | 14 +++++++------- circuits/test/parser.test.ts | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 36b61d3..a10471c 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -116,11 +116,11 @@ template StateUpdate() { 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]); - // } + 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]); + } next_tree_depth <== addToState.out[0]; next_parsing_to_key <== addToState.out[1]; @@ -210,8 +210,8 @@ template StateToMask() { 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! + // `tree_depth` can change: `IF (parsing_to_key XOR parsing_to_value XOR end_of_kv)` + mask[0] <== parsing_to_key + parsing_to_value + end_of_kv; // 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); diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index b6e1128..9e86994 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -261,6 +261,20 @@ describe("parser", () => { end_of_kv_to_parse_to_key_out.next_parsing_to_key = 1; end_of_kv_to_parse_to_key_out.next_end_of_kv = 0; generatePassCase(end_of_kv_to_parse_to_key, end_of_kv_to_parse_to_key_out, "`tree_depth == 1` AND end_of_kv` setup -> ` ` is read"); + + // Test 13: `tree_depth == 1` AND end_of_kv` setup -> `}` is read + let end_of_kv_to_exit_json = { ...init }; + end_of_kv_to_exit_json.tree_depth = 1; + end_of_kv_to_exit_json.parsing_to_key = 0; + end_of_kv_to_exit_json.end_of_kv = 1; + end_of_kv_to_exit_json.byte = end_brace; + let end_of_kv_to_exit_json_out = { ...out }; + end_of_kv_to_exit_json_out.next_tree_depth = 0; + end_of_kv_to_exit_json_out.next_parsing_to_key = 0; + end_of_kv_to_exit_json_out.next_end_of_kv = 1; + generatePassCase(end_of_kv_to_exit_json, end_of_kv_to_exit_json_out, "`tree_depth == 1` AND end_of_kv` setup -> `}` is read"); + + // NOTE: At this point, we can parse JSON that has 2 keys at depth 1! }); }); From 952824804437cee90c5e985c4bc6178b0de486ed Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 8 Aug 2024 18:57:58 -0600 Subject: [PATCH 16/24] 2 kv json and all tests passing --- circuits/extract.circom | 2 +- circuits/parser.circom | 35 +++++++++------ circuits/test/parser.test.ts | 85 +++++++++++++++++------------------- 3 files changed, 61 insertions(+), 61 deletions(-) diff --git a/circuits/extract.circom b/circuits/extract.circom index 0150cf8..f8fde0d 100644 --- a/circuits/extract.circom +++ b/circuits/extract.circom @@ -26,7 +26,7 @@ template Extract(KEY_BYTES, DATA_BYTES) { State[0] = StateUpdate(); State[0].byte <== data[0]; State[0].tree_depth <== 0; - State[0].parsing_to_key <== 1; // Initialize by saying we are parsing to the first key + State[0].parsing_to_key <== 0; State[0].inside_key <== 0; State[0].parsing_to_value <== 0; State[0].inside_value <== 0; diff --git a/circuits/parser.circom b/circuits/parser.circom index a10471c..3005bb0 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -93,14 +93,15 @@ template StateUpdate() { // Output management component matcher = Switch(5, 6); component mask = StateToMask(); + // Right now thinking more like "parsing_through_key" and "parsing_through_value" 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_start_brace[6] = [ 1, 1, 0, -1, 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 ]; + var hit_quote[6] = [ 0, 0, 1, 0, 1, 1 ]; // Command returned by switch if we hit a quote `"` + var hit_colon[6] = [ 0, -1, 0, 1, 0, 0 ]; // Command returned by switch if we hit a colon `:` + var hit_comma[6] = [ 0, 1, 0, -1, 0, 0 ]; matcher.branches <== [start_brace, end_brace, quote, colon, comma ]; matcher.vals <== [hit_start_brace, hit_end_brace, hit_quote, hit_colon, hit_comma]; @@ -116,11 +117,13 @@ template StateUpdate() { 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]); - } + // for(var i = 0; i<6; i++) { + // log("-----------------------"); + // log("mask[",i,"]: ", mask.mask[i]); + // log("mulMaskAndOut[",i,"]:", mulMaskAndOut.out[i]); + // log("state[",i,"]: ", state[i]); + // log("next_state[",i,"]: ", addToState.out[i]); + // } next_tree_depth <== addToState.out[0]; next_parsing_to_key <== addToState.out[1]; @@ -210,22 +213,26 @@ template StateToMask() { 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); + component init_tree = IsZero(); + init_tree.in <== tree_depth; + // `tree_depth` can change: `IF (parsing_to_key XOR parsing_to_value XOR end_of_kv)` - mask[0] <== parsing_to_key + parsing_to_value + end_of_kv; // TODO: Make sure these are never both 1! + mask[0] <== init_tree.out + (parsing_to_key + parsing_to_value + end_of_kv); // 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); + mask[1] <== NOT_INSIDE_KEY_AND_NOT_INSIDE_VALUE; // TODO: Changed and removed `NOT 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; + // `parsing_to_value` can change: `IF ((NOT parsing_to_key) AND (NOT inside_key) AND (NOT inside_value) AND (tree_depth != 0))` + signal INIT <== (1 - init_tree.out); + mask[3] <== INIT * 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); + mask[4] <== parsing_to_value - 2*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; diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index 9e86994..71f7748 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -1,3 +1,4 @@ +import { start } from "repl"; import { circomkit, WitnessTester } from "./common"; describe("parser", () => { @@ -122,159 +123,151 @@ describe("parser", () => { let init = { byte: 0, tree_depth: 0, - parsing_to_key: 1, + parsing_to_key: 0, inside_key: 0, parsing_to_value: 0, inside_value: 0, end_of_kv: 0, }; - - // Test 1: init setup -> `do_nothing` byte let out = { next_tree_depth: init.tree_depth, next_parsing_to_key: init.parsing_to_key, next_inside_key: init.inside_key, next_parsing_to_value: init.parsing_to_value, next_inside_value: init.inside_value, - next_end_of_kv: init.end_of_kv + next_end_of_kv: init.end_of_kv, }; - generatePassCase(init, out, "init setup -> `do_nothing` byte"); + // Test 1: init setup -> `do_nothing` byte + generatePassCase(init, out, ">>>> `NUL` read"); // Test 2: init setup -> `{` is read let read_start_brace = { ...init }; read_start_brace.byte = start_brace; let read_start_brace_out = { ...out }; read_start_brace_out.next_tree_depth = 1; - generatePassCase(read_start_brace, read_start_brace_out, "init setup -> `{` is read"); + read_start_brace_out.next_parsing_to_key = 1; + generatePassCase(read_start_brace, read_start_brace_out, ">>>> `{` read"); // Test 3: init setup -> `}` is read (should be INVALID) let read_end_brace = { ...init }; read_end_brace.byte = end_brace; - generateFailCase(read_end_brace, "init setup -> `}` is read (NEGATIVE TREE DEPTH!)"); + generateFailCase(read_end_brace, ">>>> `}` read --> (FAIL! NEGATIVE TREE DEPTH!)"); - // Test 4: `tree_depth == 1` setup -> `"` is read + // Test 4: `tree_depth == 1` and `parsing_key == 1` setup -> `"` is read let in_tree_find_key = { ...init }; in_tree_find_key.tree_depth = 1; + in_tree_find_key.parsing_to_key = 1; in_tree_find_key.byte = quote; let in_tree_find_key_out = { ...out }; + in_tree_find_key_out.next_parsing_to_key = 1; in_tree_find_key_out.next_inside_key = 1; - in_tree_find_key_out.next_parsing_to_key = 0; in_tree_find_key_out.next_tree_depth = 1; - generatePassCase(in_tree_find_key, in_tree_find_key_out, "`tree_depth == 1` setup -> `\"` is read"); + generatePassCase(in_tree_find_key, in_tree_find_key_out, ">>>> `\"` read"); // Test 5: `tree_depth == 1` AND `inside_key ==1` setup -> ` ` is read let in_key = { ...init }; in_key.tree_depth = 1; - in_key.parsing_to_key = 0; in_key.inside_key = 1; in_key.byte = space; let in_key_out = { ...out }; in_key_out.next_inside_key = 1; - in_key_out.next_parsing_to_key = 0; 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"); + generatePassCase(in_key, in_key_out, ">>>> ` ` 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; in_key_to_exit.inside_key = 1; in_key_to_exit.byte = quote; let in_key_to_exit_out = { ...out }; - in_key_to_exit_out.next_inside_key = 0; - in_key_to_exit_out.next_parsing_to_key = 0; in_key_to_exit_out.next_tree_depth = 1; - generatePassCase(in_key_to_exit, in_key_to_exit_out, "`tree_depth == 1` AND `inside_key == 1 AND `parsing_to_key == 0` setup -> `\"` is read"); + generatePassCase(in_key_to_exit, in_key_to_exit_out, "`\"` read"); // Test 7: `tree_depth == 1` AND parsed through key` setup -> `:` is read let parsed_key_wait_to_parse_value = { ...init }; parsed_key_wait_to_parse_value.tree_depth = 1; - parsed_key_wait_to_parse_value.parsing_to_key = 0; + parsed_key_wait_to_parse_value.parsing_to_key = 1; parsed_key_wait_to_parse_value.byte = colon; let parsed_key_wait_to_parse_value_out = { ...out }; parsed_key_wait_to_parse_value_out.next_tree_depth = 1; - parsed_key_wait_to_parse_value_out.next_parsing_to_key = 0; parsed_key_wait_to_parse_value_out.next_parsing_to_value = 1; - generatePassCase(parsed_key_wait_to_parse_value, parsed_key_wait_to_parse_value_out, "`tree_depth == 1` AND parsed through key` setup -> `:` is read"); + generatePassCase(parsed_key_wait_to_parse_value, parsed_key_wait_to_parse_value_out, ">>>> `:` read"); - // Test 8: `tree_depth == 1` AND parsing_to_value` setup -> `"` is read + // Test 8: `tree_depth == 1` AND parsing_to_value == 1` setup -> `"` is read let in_tree_find_value = { ...init }; in_tree_find_value.tree_depth = 1; - in_tree_find_value.parsing_to_key = 0; in_tree_find_value.parsing_to_value = 1; in_tree_find_value.byte = quote; let in_tree_find_value_out = { ...out }; in_tree_find_value_out.next_tree_depth = 1; - in_tree_find_value_out.next_parsing_to_key = 0; - in_tree_find_value_out.next_parsing_to_value = 0; in_tree_find_value_out.next_inside_value = 1; - generatePassCase(in_tree_find_value, in_tree_find_value_out, "`tree_depth == 1` AND parsing_to_value` setup -> `\"` is read"); + in_tree_find_value_out.next_parsing_to_value = 1; + generatePassCase(in_tree_find_value, in_tree_find_value_out, ">>>> `\"` read"); // Test 9: `tree_depth == 1` AND inside_value` setup -> ` ` is read let in_value = { ...init }; in_value.tree_depth = 1; - in_value.parsing_to_key = 0; in_value.inside_value = 1; in_value.byte = space; let in_value_out = { ...out }; in_value_out.next_tree_depth = 1; - in_value_out.next_parsing_to_key = 0; - in_value_out.next_parsing_to_value = 0; in_value_out.next_inside_value = 1; - generatePassCase(in_value, in_value_out, "`tree_depth == 1` AND inside_value` setup -> ` ` is read"); + generatePassCase(in_value, in_value_out, ">>>> ` ` is read"); // Test 10: `tree_depth == 1` AND inside_value` setup -> `"` is read let in_value_to_exit = { ...init }; in_value_to_exit.tree_depth = 1; - in_value_to_exit.parsing_to_key = 0; + in_value_to_exit.parsing_to_value = 1; in_value_to_exit.inside_value = 1; in_value_to_exit.byte = quote; let in_value_to_exit_out = { ...out }; in_value_to_exit_out.next_tree_depth = 1; - in_value_to_exit_out.next_parsing_to_key = 0; - in_value_to_exit_out.next_parsing_to_value = 0; - in_value_to_exit_out.next_inside_value = 0; - in_value_to_exit_out.next_end_of_kv = 1; - generatePassCase(in_value_to_exit, in_value_to_exit_out, "`tree_depth == 1` AND inside_value` setup -> `\"` is read"); + // in_value_to_exit_out.next_end_of_kv = 1; + in_value_to_exit_out.next_parsing_to_value = 1; + generatePassCase(in_value_to_exit, in_value_to_exit_out, ">>>> `\"` is read"); // Test 11: `tree_depth == 1` AND end_of_kv` setup -> ` ` is read let in_end_of_kv = { ...init }; in_end_of_kv.tree_depth = 1; - in_end_of_kv.parsing_to_key = 0; in_end_of_kv.end_of_kv = 1; in_end_of_kv.byte = space; let in_end_of_kv_out = { ...out }; in_end_of_kv_out.next_tree_depth = 1; - in_end_of_kv_out.next_parsing_to_key = 0; in_end_of_kv_out.next_end_of_kv = 1; - generatePassCase(in_end_of_kv, in_end_of_kv_out, "`tree_depth == 1` AND end_of_kv` setup -> ` ` is read"); + generatePassCase(in_end_of_kv, in_end_of_kv_out, ">>>> ` ` is read"); // Test 12: `tree_depth == 1` AND end_of_kv` setup -> `,` is read let end_of_kv_to_parse_to_key = { ...init }; end_of_kv_to_parse_to_key.tree_depth = 1; - end_of_kv_to_parse_to_key.parsing_to_key = 0; - end_of_kv_to_parse_to_key.end_of_kv = 1; + end_of_kv_to_parse_to_key.parsing_to_value = 1; + // end_of_kv_to_parse_to_key.end_of_kv = 1; end_of_kv_to_parse_to_key.byte = comma; let end_of_kv_to_parse_to_key_out = { ...out }; end_of_kv_to_parse_to_key_out.next_tree_depth = 1; end_of_kv_to_parse_to_key_out.next_parsing_to_key = 1; - end_of_kv_to_parse_to_key_out.next_end_of_kv = 0; - generatePassCase(end_of_kv_to_parse_to_key, end_of_kv_to_parse_to_key_out, "`tree_depth == 1` AND end_of_kv` setup -> ` ` is read"); + generatePassCase(end_of_kv_to_parse_to_key, end_of_kv_to_parse_to_key_out, ">>>> ` ` is read"); // Test 13: `tree_depth == 1` AND end_of_kv` setup -> `}` is read let end_of_kv_to_exit_json = { ...init }; end_of_kv_to_exit_json.tree_depth = 1; - end_of_kv_to_exit_json.parsing_to_key = 0; end_of_kv_to_exit_json.end_of_kv = 1; end_of_kv_to_exit_json.byte = end_brace; let end_of_kv_to_exit_json_out = { ...out }; - end_of_kv_to_exit_json_out.next_tree_depth = 0; - end_of_kv_to_exit_json_out.next_parsing_to_key = 0; end_of_kv_to_exit_json_out.next_end_of_kv = 1; - generatePassCase(end_of_kv_to_exit_json, end_of_kv_to_exit_json_out, "`tree_depth == 1` AND end_of_kv` setup -> `}` is read"); + generatePassCase(end_of_kv_to_exit_json, end_of_kv_to_exit_json_out, ">>>> `}` is read"); // NOTE: At this point, we can parse JSON that has 2 keys at depth 1! + + // Test 14: `tree_depth == 1` AND parsing_to_value` setup -> `{` is read + let end_of_key_to_inner_object = { ...init }; + end_of_key_to_inner_object.tree_depth = 1; + end_of_key_to_inner_object.parsing_to_value = 1; + end_of_key_to_inner_object.byte = start_brace; + let end_of_key_to_inner_object_out = { ...out }; + end_of_key_to_inner_object_out.next_tree_depth = 2; + end_of_key_to_inner_object_out.next_parsing_to_key = 1; + generatePassCase(end_of_key_to_inner_object, end_of_key_to_inner_object_out, ">>>> `{` is read"); }); }); From 86bb82d46469747c27df3ad4d4a754a0b34e7f91 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 8 Aug 2024 19:03:58 -0600 Subject: [PATCH 17/24] nested json works!!! --- circuits.json | 8 +++ create_witness/src/main.rs | 4 +- inputs/test_extract_depth/input.json | 74 ++++++++++++++++++++++++++++ json_examples/test_depth.json | 6 +++ 4 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 inputs/test_extract_depth/input.json create mode 100644 json_examples/test_depth.json diff --git a/circuits.json b/circuits.json index 517b2d7..18a7b0a 100644 --- a/circuits.json +++ b/circuits.json @@ -15,6 +15,14 @@ 40 ] }, + "test_extract_depth": { + "file": "extract", + "template": "Extract", + "params": [ + 4, + 64 + ] + }, "test_extract_hard": { "file": "extract", "template": "Extract", diff --git a/create_witness/src/main.rs b/create_witness/src/main.rs index 233e6b9..d790fef 100644 --- a/create_witness/src/main.rs +++ b/create_witness/src/main.rs @@ -9,7 +9,7 @@ pub const KEYS: &[&[u8]] = &[ b"\"title\"".as_slice(), ]; // pub const DATA: &[u8] = include_bytes!("../../json_examples/example.json"); -pub const DATA: &[u8] = include_bytes!("../../json_examples/test_two_key.json"); +pub const DATA: &[u8] = include_bytes!("../../json_examples/test_depth.json"); #[derive(serde::Serialize)] pub struct Witness { @@ -59,7 +59,7 @@ pub fn main() { // num_data_bytes: DATA.len(), // For now we can set this to be the same data: DATA.to_vec(), }; - let mut file = std::fs::File::create("inputs/test_extract/input_two_key.json").unwrap(); + let mut file = std::fs::File::create("inputs/test_extract_depth/input.json").unwrap(); file.write_all(serde_json::to_string_pretty(&witness).unwrap().as_bytes()) .unwrap(); } diff --git a/inputs/test_extract_depth/input.json b/inputs/test_extract_depth/input.json new file mode 100644 index 0000000..f4608b1 --- /dev/null +++ b/inputs/test_extract_depth/input.json @@ -0,0 +1,74 @@ +{ + "key": [ + 107, + 101, + 121, + 49 + ], + "data": [ + 123, + 10, + 32, + 32, + 32, + 32, + 34, + 107, + 101, + 121, + 49, + 34, + 58, + 32, + 34, + 97, + 98, + 99, + 34, + 44, + 10, + 32, + 32, + 32, + 32, + 34, + 107, + 101, + 121, + 50, + 34, + 58, + 32, + 123, + 10, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 34, + 107, + 101, + 121, + 51, + 34, + 58, + 32, + 34, + 100, + 101, + 102, + 34, + 10, + 32, + 32, + 32, + 32, + 125, + 10, + 125 + ] +} \ No newline at end of file diff --git a/json_examples/test_depth.json b/json_examples/test_depth.json new file mode 100644 index 0000000..ad2f816 --- /dev/null +++ b/json_examples/test_depth.json @@ -0,0 +1,6 @@ +{ + "key1": "abc", + "key2": { + "key3": "def" + } +} \ No newline at end of file From 4142ab0b672ba5e14733be733f1e9519bcb18ed0 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 8 Aug 2024 19:13:52 -0600 Subject: [PATCH 18/24] reduce constraints --- circuits/extract.circom | 3 --- circuits/parser.circom | 35 +++++++++++++---------------------- circuits/test/parser.test.ts | 8 ++------ 3 files changed, 15 insertions(+), 31 deletions(-) diff --git a/circuits/extract.circom b/circuits/extract.circom index f8fde0d..26a5717 100644 --- a/circuits/extract.circom +++ b/circuits/extract.circom @@ -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].end_of_kv <== 0; for(var data_pointer = 1; data_pointer < DATA_BYTES; data_pointer++) { State[data_pointer] = StateUpdate(); @@ -40,7 +39,6 @@ template Extract(KEY_BYTES, DATA_BYTES) { State[data_pointer].inside_key <== State[data_pointer - 1].next_inside_key; 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; // Debugging log("State[", data_pointer, "].tree_depth", "= ", State[data_pointer].tree_depth); @@ -48,7 +46,6 @@ template Extract(KEY_BYTES, DATA_BYTES) { log("State[", data_pointer, "].inside_key", "= ", State[data_pointer].inside_key); log("State[", data_pointer, "].parsing_to_value", "= ", State[data_pointer].parsing_to_value); log("State[", data_pointer, "].inside_value", "= ", State[data_pointer].inside_value); - log("State[", data_pointer, "].end_of_kv", "= ", State[data_pointer].end_of_kv); log("---"); } diff --git a/circuits/parser.circom b/circuits/parser.circom index 3005bb0..364ace8 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -48,14 +48,11 @@ template StateUpdate() { // 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`. - signal output next_tree_depth; // BIT_FLAG -- next state for `tree_depth`. signal output next_parsing_to_key; // BIT_FLAG -- next state for `parsing_to_key`. signal output next_inside_key; // BIT_FLAG -- next state for `inside_key`. signal output next_parsing_to_value; // BIT_FLAG -- next state for `parsing_to_value`. signal output next_inside_value; // BIT_FLAG -- next state for `inside_value`. - signal output next_end_of_kv; // BIT_FLAG -- next state for `end_of_kv`. // signal output escaping; // TODO: Add this in! @@ -91,17 +88,17 @@ 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(5, 6); + component matcher = Switch(5, 5); component mask = StateToMask(); // Right now thinking more like "parsing_through_key" and "parsing_through_value" - var state[6] = [tree_depth, parsing_to_key, inside_key, parsing_to_value, inside_value, end_of_kv]; + var state[5] = [tree_depth, parsing_to_key, inside_key, parsing_to_value, inside_value]; 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, 1, 0, -1, 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, 0, 1, 0, 1, 1 ]; // Command returned by switch if we hit a quote `"` - var hit_colon[6] = [ 0, -1, 0, 1, 0, 0 ]; // Command returned by switch if we hit a colon `:` - var hit_comma[6] = [ 0, 1, 0, -1, 0, 0 ]; + var do_nothing[5] = [ 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[5] = [ 1, 1, 0, -1, 0 ]; // Command returned by switch if we hit a start brace `{` + var hit_end_brace[5] = [-1, 0, 0, 0, 0 ]; // Command returned by switch if we hit a end brace `}` + var hit_quote[5] = [ 0, 0, 1, 0, 1 ]; // Command returned by switch if we hit a quote `"` + var hit_colon[5] = [ 0, -1, 0, 1, 0 ]; // Command returned by switch if we hit a colon `:` + var hit_comma[5] = [ 0, 1, 0, -1, 0 ]; matcher.branches <== [start_brace, end_brace, quote, colon, comma ]; matcher.vals <== [hit_start_brace, hit_end_brace, hit_quote, hit_colon, hit_comma]; @@ -109,11 +106,11 @@ template StateUpdate() { // log("byte: ", byte); - component mulMaskAndOut = ArrayMul(6); + component mulMaskAndOut = ArrayMul(5); mulMaskAndOut.lhs <== mask.mask; mulMaskAndOut.rhs <== matcher.out; - component addToState = ArrayAdd(6); + component addToState = ArrayAdd(5); addToState.lhs <== state; addToState.rhs <== mulMaskAndOut.out; @@ -130,7 +127,6 @@ template StateUpdate() { 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]; // log("next_inside_key: ", next_inside_key); @@ -139,7 +135,6 @@ template StateUpdate() { next_inside_key * (1 - next_inside_key) === 0; // - constrain that `next_inside_key` remain a bit flag next_parsing_to_value * (1 - next_parsing_to_value) === 0; // - constrain that `next_parsing_to_value` remain a bit flag next_inside_value * (1 - next_inside_value) === 0; // - constrain that `next_inside_value` remain a bit flag - next_end_of_kv * (1 - next_end_of_kv) === 0; // - constrain that `next_end_of_kv` remain a bit flag component depthIsZero = IsZero(); depthIsZero.in <== tree_depth; // Determine if `tree_depth` was `0` @@ -199,15 +194,14 @@ template Switch(m, n) { // 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]; + signal input state[5]; + signal output mask[5]; 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); @@ -217,7 +211,7 @@ template StateToMask() { init_tree.in <== tree_depth; // `tree_depth` can change: `IF (parsing_to_key XOR parsing_to_value XOR end_of_kv)` - mask[0] <== init_tree.out + (parsing_to_key + parsing_to_value + end_of_kv); // TODO: Make sure these are never both 1! + mask[0] <== init_tree.out + 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; // TODO: Changed and removed `NOT parsing_to_value) @@ -233,7 +227,4 @@ template StateToMask() { // `inside_value` can change: `IF ((NOT parsing_to_key) AND (C_NOR (inside_value, parsing_to value)))` // control----------^ mask[4] <== parsing_to_value - 2*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; } \ No newline at end of file diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index 71f7748..12eff3a 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -127,7 +127,6 @@ describe("parser", () => { inside_key: 0, parsing_to_value: 0, inside_value: 0, - end_of_kv: 0, }; let out = { next_tree_depth: init.tree_depth, @@ -135,7 +134,6 @@ describe("parser", () => { next_inside_key: init.inside_key, next_parsing_to_value: init.parsing_to_value, next_inside_value: init.inside_value, - next_end_of_kv: init.end_of_kv, }; // Test 1: init setup -> `do_nothing` byte @@ -230,11 +228,9 @@ describe("parser", () => { // Test 11: `tree_depth == 1` AND end_of_kv` setup -> ` ` is read let in_end_of_kv = { ...init }; in_end_of_kv.tree_depth = 1; - in_end_of_kv.end_of_kv = 1; in_end_of_kv.byte = space; let in_end_of_kv_out = { ...out }; in_end_of_kv_out.next_tree_depth = 1; - in_end_of_kv_out.next_end_of_kv = 1; generatePassCase(in_end_of_kv, in_end_of_kv_out, ">>>> ` ` is read"); // Test 12: `tree_depth == 1` AND end_of_kv` setup -> `,` is read @@ -251,10 +247,10 @@ describe("parser", () => { // Test 13: `tree_depth == 1` AND end_of_kv` setup -> `}` is read let end_of_kv_to_exit_json = { ...init }; end_of_kv_to_exit_json.tree_depth = 1; - end_of_kv_to_exit_json.end_of_kv = 1; + end_of_kv_to_exit_json.parsing_to_value = 1; end_of_kv_to_exit_json.byte = end_brace; let end_of_kv_to_exit_json_out = { ...out }; - end_of_kv_to_exit_json_out.next_end_of_kv = 1; + end_of_kv_to_exit_json_out.next_parsing_to_value = 1; generatePassCase(end_of_kv_to_exit_json, end_of_kv_to_exit_json_out, ">>>> `}` is read"); // NOTE: At this point, we can parse JSON that has 2 keys at depth 1! From 1d425e55f5f724ef915c0199bdb6a9bfa362509d Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 9 Aug 2024 06:50:13 -0600 Subject: [PATCH 19/24] cleanup --- circuits/parser.circom | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 364ace8..a8cb5ad 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -104,8 +104,6 @@ template StateUpdate() { matcher.vals <== [hit_start_brace, hit_end_brace, hit_quote, hit_colon, hit_comma]; matcher.case <== byte; - // log("byte: ", byte); - component mulMaskAndOut = ArrayMul(5); mulMaskAndOut.lhs <== mask.mask; mulMaskAndOut.rhs <== matcher.out; @@ -203,12 +201,11 @@ template StateToMask() { var parsing_to_value = state[3]; var inside_value = state[4]; - 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); + signal NOT_INSIDE_KEY_AND_NOT_INSIDE_VALUE <-- (1 - inside_key) * (1 - inside_value); + signal NOT_PARSING_TO_VALUE_NOT_INSIDE_VALUE <-- (1 - parsing_to_value) * (1 - inside_value); component init_tree = IsZero(); - init_tree.in <== tree_depth; + init_tree.in <-- tree_depth; // `tree_depth` can change: `IF (parsing_to_key XOR parsing_to_value XOR end_of_kv)` mask[0] <== init_tree.out + parsing_to_key + parsing_to_value; // TODO: Make sure these are never both 1! @@ -217,8 +214,7 @@ template StateToMask() { mask[1] <== NOT_INSIDE_KEY_AND_NOT_INSIDE_VALUE; // TODO: Changed and removed `NOT 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; + mask[2] <== NOT_PARSING_TO_VALUE_NOT_INSIDE_VALUE - 2 * inside_key; // `parsing_to_value` can change: `IF ((NOT parsing_to_key) AND (NOT inside_key) AND (NOT inside_value) AND (tree_depth != 0))` signal INIT <== (1 - init_tree.out); @@ -226,5 +222,5 @@ template StateToMask() { // `inside_value` can change: `IF ((NOT parsing_to_key) AND (C_NOR (inside_value, parsing_to value)))` // control----------^ - mask[4] <== parsing_to_value - 2*inside_value; + mask[4] <== parsing_to_value - 2 * inside_value; } \ No newline at end of file From ec6ec0ec66b3805245c44f412976e3f3c47b3945 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 9 Aug 2024 06:52:20 -0600 Subject: [PATCH 20/24] rename variables --- circuits/parser.circom | 48 ++++++++++++++++++------------------ circuits/test/parser.test.ts | 48 ++++++++++++++++++------------------ 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index a8cb5ad..bafbfcb 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -40,18 +40,18 @@ template StateUpdate() { signal input tree_depth; // STATUS_INDICATOR -- how deep in a JSON branch we are, e.g., `user.balance.value` key should be at depth `3`. // Should always be greater than or equal to `0` (TODO: implement this constraint). - signal input parsing_to_key; // BIT_FLAG -- whether we are currently parsing bytes until we find the next key (mutally exclusive with `inside_key` and both `*_value flags). - signal input inside_key; // BIT_FLAG -- whether we are currently inside a key (mutually exclusive with `parsing_to_key` and both `*_value` flags). + signal input parsing_key; // BIT_FLAG -- whether we are currently parsing bytes until we find the next key (mutally exclusive with `inside_key` and both `*_value flags). + signal input inside_key; // BIT_FLAG -- whether we are currently inside a key (mutually exclusive with `parsing_key` and both `*_value` flags). - 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 parsing_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_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 output next_tree_depth; // BIT_FLAG -- next state for `tree_depth`. - signal output next_parsing_to_key; // BIT_FLAG -- next state for `parsing_to_key`. + signal output next_parsing_key; // BIT_FLAG -- next state for `parsing_key`. signal output next_inside_key; // BIT_FLAG -- next state for `inside_key`. - signal output next_parsing_to_value; // BIT_FLAG -- next state for `parsing_to_value`. + signal output next_parsing_value; // BIT_FLAG -- next state for `parsing_value`. signal output next_inside_value; // BIT_FLAG -- next state for `inside_value`. // signal output escaping; // TODO: Add this in! @@ -91,7 +91,7 @@ template StateUpdate() { component matcher = Switch(5, 5); component mask = StateToMask(); // Right now thinking more like "parsing_through_key" and "parsing_through_value" - var state[5] = [tree_depth, parsing_to_key, inside_key, parsing_to_value, inside_value]; + var state[5] = [tree_depth, parsing_key, inside_key, parsing_value, inside_value]; mask.state <== state; var do_nothing[5] = [ 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[5] = [ 1, 1, 0, -1, 0 ]; // Command returned by switch if we hit a start brace `{` @@ -121,17 +121,17 @@ template StateUpdate() { // } next_tree_depth <== addToState.out[0]; - next_parsing_to_key <== addToState.out[1]; + next_parsing_key <== addToState.out[1]; next_inside_key <== addToState.out[2]; - next_parsing_to_value <== addToState.out[3]; + next_parsing_value <== addToState.out[3]; next_inside_value <== addToState.out[4]; // 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 + next_parsing_key * (1 - next_parsing_key) === 0; // - constrain that `next_parsing_key` remain a bit flag next_inside_key * (1 - next_inside_key) === 0; // - constrain that `next_inside_key` remain a bit flag - next_parsing_to_value * (1 - next_parsing_to_value) === 0; // - constrain that `next_parsing_to_value` remain a bit flag + next_parsing_value * (1 - next_parsing_value) === 0; // - constrain that `next_parsing_value` remain a bit flag next_inside_value * (1 - next_inside_value) === 0; // - constrain that `next_inside_value` remain a bit flag component depthIsZero = IsZero(); @@ -140,7 +140,7 @@ template StateUpdate() { isOneLess.in[0] <== -1; isOneLess.in[1] <== matcher.out[0]; // Determine if instruction was to `decrease_depth` depthIsZero.out * isOneLess.out === 0; // IF ( `decrease_depth` AND `tree_depth == 0`) THEN FAIL - // TODO: Can hit comma and then be sent to next KV, so comma will engage `parsing_to_key` + // TODO: Can hit comma and then be sent to next KV, so comma will engage `parsing_key` //--------------------------------------------------------------------------------------------// } @@ -196,31 +196,31 @@ template StateToMask() { signal output mask[5]; var tree_depth = state[0]; - var parsing_to_key = state[1]; + var parsing_key = state[1]; var inside_key = state[2]; - var parsing_to_value = state[3]; + var parsing_value = state[3]; var inside_value = state[4]; signal NOT_INSIDE_KEY_AND_NOT_INSIDE_VALUE <-- (1 - inside_key) * (1 - inside_value); - signal NOT_PARSING_TO_VALUE_NOT_INSIDE_VALUE <-- (1 - parsing_to_value) * (1 - inside_value); + signal NOT_PARSING_VALUE_NOT_INSIDE_VALUE <-- (1 - parsing_value) * (1 - inside_value); component init_tree = IsZero(); init_tree.in <-- tree_depth; - // `tree_depth` can change: `IF (parsing_to_key XOR parsing_to_value XOR end_of_kv)` - mask[0] <== init_tree.out + parsing_to_key + parsing_to_value; // TODO: Make sure these are never both 1! + // `tree_depth` can change: `IF (parsing_key XOR parsing_value XOR end_of_kv)` + mask[0] <== init_tree.out + parsing_key + parsing_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; // TODO: Changed and removed `NOT parsing_to_value) + // `parsing_key` can change: `IF ((NOT inside_key) AND (NOT inside_value) AND (NOT parsing_value))` + mask[1] <== NOT_INSIDE_KEY_AND_NOT_INSIDE_VALUE; // TODO: Changed and removed `NOT parsing_value) - // `inside_key` can change: `IF ((NOT parsing_to_value) AND (NOT inside_value)) THEN TOGGLE WITH inside_key` - mask[2] <== NOT_PARSING_TO_VALUE_NOT_INSIDE_VALUE - 2 * inside_key; + // `inside_key` can change: `IF ((NOT parsing_value) AND (NOT inside_value)) THEN TOGGLE WITH inside_key` + mask[2] <== NOT_PARSING_VALUE_NOT_INSIDE_VALUE - 2 * inside_key; - // `parsing_to_value` can change: `IF ((NOT parsing_to_key) AND (NOT inside_key) AND (NOT inside_value) AND (tree_depth != 0))` + // `parsing_value` can change: `IF ((NOT parsing_key) AND (NOT inside_key) AND (NOT inside_value) AND (tree_depth != 0))` signal INIT <== (1 - init_tree.out); mask[3] <== INIT * NOT_INSIDE_KEY_AND_NOT_INSIDE_VALUE; - // `inside_value` can change: `IF ((NOT parsing_to_key) AND (C_NOR (inside_value, parsing_to value)))` + // `inside_value` can change: `IF ((NOT parsing_key) AND (C_NOR (inside_value, parsing_to value)))` // control----------^ - mask[4] <== parsing_to_value - 2 * inside_value; + mask[4] <== parsing_value - 2 * inside_value; } \ No newline at end of file diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index 12eff3a..3e6fbf2 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -87,8 +87,8 @@ describe("parser", () => { describe("StateUpdate", () => { let circuit: WitnessTester< - ["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"] + ["byte", "tree_depth", "parsing_key", "inside_key", "parsing_value", "inside_value", "end_of_kv"], + ["next_tree_depth", "next_parsing_key", "next_inside_key", "next_parsing_value", "next_inside_value", "next_end_of_kv"] >; function generatePassCase(input: any, expected: any, desc: string) { @@ -123,16 +123,16 @@ describe("parser", () => { let init = { byte: 0, tree_depth: 0, - parsing_to_key: 0, + parsing_key: 0, inside_key: 0, - parsing_to_value: 0, + parsing_value: 0, inside_value: 0, }; let out = { next_tree_depth: init.tree_depth, - next_parsing_to_key: init.parsing_to_key, + next_parsing_key: init.parsing_key, next_inside_key: init.inside_key, - next_parsing_to_value: init.parsing_to_value, + next_parsing_value: init.parsing_value, next_inside_value: init.inside_value, }; @@ -144,7 +144,7 @@ describe("parser", () => { read_start_brace.byte = start_brace; let read_start_brace_out = { ...out }; read_start_brace_out.next_tree_depth = 1; - read_start_brace_out.next_parsing_to_key = 1; + read_start_brace_out.next_parsing_key = 1; generatePassCase(read_start_brace, read_start_brace_out, ">>>> `{` read"); // Test 3: init setup -> `}` is read (should be INVALID) @@ -155,10 +155,10 @@ describe("parser", () => { // Test 4: `tree_depth == 1` and `parsing_key == 1` setup -> `"` is read let in_tree_find_key = { ...init }; in_tree_find_key.tree_depth = 1; - in_tree_find_key.parsing_to_key = 1; + in_tree_find_key.parsing_key = 1; in_tree_find_key.byte = quote; let in_tree_find_key_out = { ...out }; - in_tree_find_key_out.next_parsing_to_key = 1; + in_tree_find_key_out.next_parsing_key = 1; in_tree_find_key_out.next_inside_key = 1; in_tree_find_key_out.next_tree_depth = 1; generatePassCase(in_tree_find_key, in_tree_find_key_out, ">>>> `\"` read"); @@ -173,7 +173,7 @@ describe("parser", () => { in_key_out.next_tree_depth = 1; generatePassCase(in_key, in_key_out, ">>>> ` ` 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_key == 0` setup -> `"` is read let in_key_to_exit = { ...init }; in_key_to_exit.tree_depth = 1; in_key_to_exit.inside_key = 1; @@ -185,22 +185,22 @@ describe("parser", () => { // Test 7: `tree_depth == 1` AND parsed through key` setup -> `:` is read let parsed_key_wait_to_parse_value = { ...init }; parsed_key_wait_to_parse_value.tree_depth = 1; - parsed_key_wait_to_parse_value.parsing_to_key = 1; + parsed_key_wait_to_parse_value.parsing_key = 1; parsed_key_wait_to_parse_value.byte = colon; let parsed_key_wait_to_parse_value_out = { ...out }; parsed_key_wait_to_parse_value_out.next_tree_depth = 1; - parsed_key_wait_to_parse_value_out.next_parsing_to_value = 1; + parsed_key_wait_to_parse_value_out.next_parsing_value = 1; generatePassCase(parsed_key_wait_to_parse_value, parsed_key_wait_to_parse_value_out, ">>>> `:` read"); - // Test 8: `tree_depth == 1` AND parsing_to_value == 1` setup -> `"` is read + // Test 8: `tree_depth == 1` AND parsing_value == 1` setup -> `"` is read let in_tree_find_value = { ...init }; in_tree_find_value.tree_depth = 1; - in_tree_find_value.parsing_to_value = 1; + in_tree_find_value.parsing_value = 1; in_tree_find_value.byte = quote; let in_tree_find_value_out = { ...out }; in_tree_find_value_out.next_tree_depth = 1; in_tree_find_value_out.next_inside_value = 1; - in_tree_find_value_out.next_parsing_to_value = 1; + in_tree_find_value_out.next_parsing_value = 1; generatePassCase(in_tree_find_value, in_tree_find_value_out, ">>>> `\"` read"); // Test 9: `tree_depth == 1` AND inside_value` setup -> ` ` is read @@ -216,13 +216,13 @@ describe("parser", () => { // Test 10: `tree_depth == 1` AND inside_value` setup -> `"` is read let in_value_to_exit = { ...init }; in_value_to_exit.tree_depth = 1; - in_value_to_exit.parsing_to_value = 1; + in_value_to_exit.parsing_value = 1; in_value_to_exit.inside_value = 1; in_value_to_exit.byte = quote; let in_value_to_exit_out = { ...out }; in_value_to_exit_out.next_tree_depth = 1; // in_value_to_exit_out.next_end_of_kv = 1; - in_value_to_exit_out.next_parsing_to_value = 1; + in_value_to_exit_out.next_parsing_value = 1; generatePassCase(in_value_to_exit, in_value_to_exit_out, ">>>> `\"` is read"); // Test 11: `tree_depth == 1` AND end_of_kv` setup -> ` ` is read @@ -236,33 +236,33 @@ describe("parser", () => { // Test 12: `tree_depth == 1` AND end_of_kv` setup -> `,` is read let end_of_kv_to_parse_to_key = { ...init }; end_of_kv_to_parse_to_key.tree_depth = 1; - end_of_kv_to_parse_to_key.parsing_to_value = 1; + end_of_kv_to_parse_to_key.parsing_value = 1; // end_of_kv_to_parse_to_key.end_of_kv = 1; end_of_kv_to_parse_to_key.byte = comma; let end_of_kv_to_parse_to_key_out = { ...out }; end_of_kv_to_parse_to_key_out.next_tree_depth = 1; - end_of_kv_to_parse_to_key_out.next_parsing_to_key = 1; + end_of_kv_to_parse_to_key_out.next_parsing_key = 1; generatePassCase(end_of_kv_to_parse_to_key, end_of_kv_to_parse_to_key_out, ">>>> ` ` is read"); // Test 13: `tree_depth == 1` AND end_of_kv` setup -> `}` is read let end_of_kv_to_exit_json = { ...init }; end_of_kv_to_exit_json.tree_depth = 1; - end_of_kv_to_exit_json.parsing_to_value = 1; + end_of_kv_to_exit_json.parsing_value = 1; end_of_kv_to_exit_json.byte = end_brace; let end_of_kv_to_exit_json_out = { ...out }; - end_of_kv_to_exit_json_out.next_parsing_to_value = 1; + end_of_kv_to_exit_json_out.next_parsing_value = 1; generatePassCase(end_of_kv_to_exit_json, end_of_kv_to_exit_json_out, ">>>> `}` is read"); // NOTE: At this point, we can parse JSON that has 2 keys at depth 1! - // Test 14: `tree_depth == 1` AND parsing_to_value` setup -> `{` is read + // Test 14: `tree_depth == 1` AND parsing_value` setup -> `{` is read let end_of_key_to_inner_object = { ...init }; end_of_key_to_inner_object.tree_depth = 1; - end_of_key_to_inner_object.parsing_to_value = 1; + end_of_key_to_inner_object.parsing_value = 1; end_of_key_to_inner_object.byte = start_brace; let end_of_key_to_inner_object_out = { ...out }; end_of_key_to_inner_object_out.next_tree_depth = 2; - end_of_key_to_inner_object_out.next_parsing_to_key = 1; + end_of_key_to_inner_object_out.next_parsing_key = 1; generatePassCase(end_of_key_to_inner_object, end_of_key_to_inner_object_out, ">>>> `{` is read"); }); From ba8888f0814ee349a843319c23888a7986c71895 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 9 Aug 2024 06:56:37 -0600 Subject: [PATCH 21/24] more cleaning --- circuits/parser.circom | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index bafbfcb..7a3ddd1 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -22,39 +22,30 @@ State[20]| " | COMPLETE WITH KV PARSING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ State[20].next_tree_depth == 0 | VALID JSON xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - - - -TODOs: -- Handle case where the value is an another JSON. Shouldn't be too bad as we should just reset to init state with different tree depth -- In fact, we might not even need tree depth if we replace it with `inside_value` that is a counter as it represents the same thing! - - Actually, this may not work since multiple values exist at same height. Let's not change this yet. */ /* TODO */ template StateUpdate() { - signal input byte; - - signal input tree_depth; // STATUS_INDICATOR -- how deep in a JSON branch we are, e.g., `user.balance.value` key should be at depth `3`. - // Should always be greater than or equal to `0` (TODO: implement this constraint). + signal input byte; + signal input tree_depth; // STATUS_INDICATOR -- how deep in a JSON branch we are, e.g., `user.balance.value` key should be at depth `3`. + // constrainted to be greater than or equal to `0`. signal input parsing_key; // BIT_FLAG -- whether we are currently parsing bytes until we find the next key (mutally exclusive with `inside_key` and both `*_value flags). - signal input inside_key; // BIT_FLAG -- whether we are currently inside a key (mutually exclusive with `parsing_key` and both `*_value` flags). - + signal input inside_key; // BIT_FLAG -- whether we are currently inside a key (mutually exclusive with `parsing_key` and both `*_value` flags). signal input parsing_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_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 inside_value; // BIT_FLAG -- whether we are currently inside a value (mutually exclusive with `parsing_value` and both `*_key` flags). - signal output next_tree_depth; // BIT_FLAG -- next state for `tree_depth`. + signal output next_tree_depth; // STATUS_INDICATOR -- next state for `tree_depth`. signal output next_parsing_key; // BIT_FLAG -- next state for `parsing_key`. - signal output next_inside_key; // BIT_FLAG -- next state for `inside_key`. + signal output next_inside_key; // BIT_FLAG -- next state for `inside_key`. signal output next_parsing_value; // BIT_FLAG -- next state for `parsing_value`. - signal output next_inside_value; // BIT_FLAG -- next state for `inside_value`. + signal output next_inside_value; // BIT_FLAG -- next state for `inside_value`. - // signal output escaping; // TODO: Add this in! + // TODO: Add this in! + // signal input escaping; // BIT_FLAG -- whether we have hit an escape ASCII symbol inside of a key or value. + // signal output escaping; //--------------------------------------------------------------------------------------------// //-Delimeters---------------------------------------------------------------------------------// From 887786a73a31bdbd20e4eaee75dabb6bcca9ce02 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 9 Aug 2024 07:13:41 -0600 Subject: [PATCH 22/24] more cleanup --- circuits.json | 8 ++ circuits/extract.circom | 12 +-- circuits/parser.circom | 23 ++--- create_witness/src/main.rs | 4 +- inputs/test_extract_sambhav/input.json | 115 +++++++++++++++++++++++++ json_examples/sambhav_example.json | 7 ++ 6 files changed, 147 insertions(+), 22 deletions(-) create mode 100644 inputs/test_extract_sambhav/input.json create mode 100644 json_examples/sambhav_example.json diff --git a/circuits.json b/circuits.json index 18a7b0a..43017d8 100644 --- a/circuits.json +++ b/circuits.json @@ -23,6 +23,14 @@ 64 ] }, + "test_extract_sambhav": { + "file": "extract", + "template": "Extract", + "params": [ + 4, + 105 + ] + }, "test_extract_hard": { "file": "extract", "template": "Extract", diff --git a/circuits/extract.circom b/circuits/extract.circom index 26a5717..07b649f 100644 --- a/circuits/extract.circom +++ b/circuits/extract.circom @@ -26,25 +26,25 @@ template Extract(KEY_BYTES, DATA_BYTES) { State[0] = StateUpdate(); State[0].byte <== data[0]; State[0].tree_depth <== 0; - State[0].parsing_to_key <== 0; + State[0].parsing_key <== 0; State[0].inside_key <== 0; - State[0].parsing_to_value <== 0; + State[0].parsing_value <== 0; State[0].inside_value <== 0; for(var data_pointer = 1; data_pointer < DATA_BYTES; data_pointer++) { State[data_pointer] = StateUpdate(); State[data_pointer].byte <== data[data_pointer]; State[data_pointer].tree_depth <== State[data_pointer - 1].next_tree_depth; - State[data_pointer].parsing_to_key <== State[data_pointer - 1].next_parsing_to_key; + State[data_pointer].parsing_key <== State[data_pointer - 1].next_parsing_key; State[data_pointer].inside_key <== State[data_pointer - 1].next_inside_key; - State[data_pointer].parsing_to_value <== State[data_pointer - 1].next_parsing_to_value; + State[data_pointer].parsing_value <== State[data_pointer - 1].next_parsing_value; State[data_pointer].inside_value <== State[data_pointer - 1].next_inside_value; // Debugging log("State[", data_pointer, "].tree_depth", "= ", State[data_pointer].tree_depth); - log("State[", data_pointer, "].parsing_to_key", "= ", State[data_pointer].parsing_to_key); + log("State[", data_pointer, "].parsing_key", "= ", State[data_pointer].parsing_key); log("State[", data_pointer, "].inside_key", "= ", State[data_pointer].inside_key); - log("State[", data_pointer, "].parsing_to_value", "= ", State[data_pointer].parsing_to_value); + log("State[", data_pointer, "].parsing_value", "= ", State[data_pointer].parsing_value); log("State[", data_pointer, "].inside_value", "= ", State[data_pointer].inside_value); log("---"); } diff --git a/circuits/parser.circom b/circuits/parser.circom index 7a3ddd1..7c4ef5a 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -117,21 +117,17 @@ template StateUpdate() { next_parsing_value <== addToState.out[3]; next_inside_value <== addToState.out[4]; - // log("next_inside_key: ", next_inside_key); - // Constrain bit flags next_parsing_key * (1 - next_parsing_key) === 0; // - constrain that `next_parsing_key` remain a bit flag next_inside_key * (1 - next_inside_key) === 0; // - constrain that `next_inside_key` remain a bit flag next_parsing_value * (1 - next_parsing_value) === 0; // - constrain that `next_parsing_value` remain a bit flag next_inside_value * (1 - next_inside_value) === 0; // - constrain that `next_inside_value` remain a bit flag - - component depthIsZero = IsZero(); - depthIsZero.in <== tree_depth; // Determine if `tree_depth` was `0` - component isOneLess = IsEqual(); - isOneLess.in[0] <== -1; - isOneLess.in[1] <== matcher.out[0]; // Determine if instruction was to `decrease_depth` - depthIsZero.out * isOneLess.out === 0; // IF ( `decrease_depth` AND `tree_depth == 0`) THEN FAIL - // TODO: Can hit comma and then be sent to next KV, so comma will engage `parsing_key` + + // Constrain `tree_depth` to never hit -1 (TODO: should always moves in 1 bit increments?) + component isMinusOne = IsEqual(); + isMinusOne.in[0] <== -1; + isMinusOne.in[1] <== next_tree_depth; + isMinusOne.out === 0; //--------------------------------------------------------------------------------------------// } @@ -202,16 +198,15 @@ template StateToMask() { mask[0] <== init_tree.out + parsing_key + parsing_value; // TODO: Make sure these are never both 1! // `parsing_key` can change: `IF ((NOT inside_key) AND (NOT inside_value) AND (NOT parsing_value))` - mask[1] <== NOT_INSIDE_KEY_AND_NOT_INSIDE_VALUE; // TODO: Changed and removed `NOT parsing_value) + mask[1] <== NOT_INSIDE_KEY_AND_NOT_INSIDE_VALUE; - // `inside_key` can change: `IF ((NOT parsing_value) AND (NOT inside_value)) THEN TOGGLE WITH inside_key` + // `inside_key` can change: `IF ((NOT parsing_value) AND (NOT inside_value) AND inside_key) THEN mask <== -1 ELSEIF (NOT parsing_value) AND (NOT inside_value) THEN mask <== 1` mask[2] <== NOT_PARSING_VALUE_NOT_INSIDE_VALUE - 2 * inside_key; // `parsing_value` can change: `IF ((NOT parsing_key) AND (NOT inside_key) AND (NOT inside_value) AND (tree_depth != 0))` signal INIT <== (1 - init_tree.out); mask[3] <== INIT * NOT_INSIDE_KEY_AND_NOT_INSIDE_VALUE; - // `inside_value` can change: `IF ((NOT parsing_key) AND (C_NOR (inside_value, parsing_to value)))` - // control----------^ + // `inside_value` can change: `IF (parsing_value AND (NOT inside_value)) THEN mask <== 1 ELSEIF (inside_value) mask <== -1` mask[4] <== parsing_value - 2 * inside_value; } \ No newline at end of file diff --git a/create_witness/src/main.rs b/create_witness/src/main.rs index d790fef..dbe494b 100644 --- a/create_witness/src/main.rs +++ b/create_witness/src/main.rs @@ -9,7 +9,7 @@ pub const KEYS: &[&[u8]] = &[ b"\"title\"".as_slice(), ]; // pub const DATA: &[u8] = include_bytes!("../../json_examples/example.json"); -pub const DATA: &[u8] = include_bytes!("../../json_examples/test_depth.json"); +pub const DATA: &[u8] = include_bytes!("../../json_examples/sambhav_example.json"); #[derive(serde::Serialize)] pub struct Witness { @@ -59,7 +59,7 @@ pub fn main() { // num_data_bytes: DATA.len(), // For now we can set this to be the same data: DATA.to_vec(), }; - let mut file = std::fs::File::create("inputs/test_extract_depth/input.json").unwrap(); + let mut file = std::fs::File::create("inputs/test_extract_sambhav/input.json").unwrap(); file.write_all(serde_json::to_string_pretty(&witness).unwrap().as_bytes()) .unwrap(); } diff --git a/inputs/test_extract_sambhav/input.json b/inputs/test_extract_sambhav/input.json new file mode 100644 index 0000000..9e6cbba --- /dev/null +++ b/inputs/test_extract_sambhav/input.json @@ -0,0 +1,115 @@ +{ + "key": [ + 107, + 101, + 121, + 49 + ], + "data": [ + 123, + 10, + 32, + 32, + 32, + 32, + 34, + 101, + 120, + 116, + 114, + 97, + 99, + 116, + 34, + 58, + 32, + 123, + 10, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 34, + 102, + 105, + 108, + 101, + 34, + 58, + 32, + 34, + 101, + 120, + 116, + 114, + 97, + 99, + 116, + 34, + 44, + 10, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 34, + 116, + 101, + 109, + 112, + 108, + 97, + 116, + 101, + 34, + 58, + 32, + 34, + 69, + 120, + 116, + 114, + 97, + 99, + 116, + 34, + 44, + 10, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 32, + 34, + 112, + 97, + 114, + 97, + 109, + 115, + 34, + 58, + 32, + 34, + 34, + 10, + 32, + 32, + 32, + 32, + 125, + 10, + 125 + ] +} \ No newline at end of file diff --git a/json_examples/sambhav_example.json b/json_examples/sambhav_example.json new file mode 100644 index 0000000..ab9c57e --- /dev/null +++ b/json_examples/sambhav_example.json @@ -0,0 +1,7 @@ +{ + "extract": { + "file": "extract", + "template": "Extract", + "params": "" + } +} \ No newline at end of file From 621e130390fdb9f708dc12bb0e9d5a8072fa9a15 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 9 Aug 2024 11:05:32 -0600 Subject: [PATCH 23/24] make comments clean --- circuits/parser.circom | 86 +++++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 39 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 7c4ef5a..8d4f405 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -75,55 +75,63 @@ template StateUpdate() { var escape = 92; //--------------------------------------------------------------------------------------------// - //--------------------------------------------------------------------------------------------// - //-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(5, 5); - component mask = StateToMask(); - // Right now thinking more like "parsing_through_key" and "parsing_through_value" - var state[5] = [tree_depth, parsing_key, inside_key, parsing_value, inside_value]; - mask.state <== state; - var do_nothing[5] = [ 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[5] = [ 1, 1, 0, -1, 0 ]; // Command returned by switch if we hit a start brace `{` - var hit_end_brace[5] = [-1, 0, 0, 0, 0 ]; // Command returned by switch if we hit a end brace `}` - var hit_quote[5] = [ 0, 0, 1, 0, 1 ]; // Command returned by switch if we hit a quote `"` - var hit_colon[5] = [ 0, -1, 0, 1, 0 ]; // Command returned by switch if we hit a colon `:` - var hit_comma[5] = [ 0, 1, 0, -1, 0 ]; + //--------------------------------------------------------------------------------------------// + //-Instructions for ASCII---------------------------------------------------------------------// + var state[5] = [tree_depth, parsing_key, inside_key, parsing_value, inside_value]; + var do_nothing[5] = [ 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[5] = [ 1, 1, 0, -1, 0 ]; // Command returned by switch if we hit a start brace `{` + var hit_end_brace[5] = [-1, 0, 0, 0, 0 ]; // Command returned by switch if we hit a end brace `}` + var hit_quote[5] = [ 0, 0, 1, 0, 1 ]; // Command returned by switch if we hit a quote `"` + var hit_colon[5] = [ 0, -1, 0, 1, 0 ]; // Command returned by switch if we hit a colon `:` + var hit_comma[5] = [ 0, 1, 0, -1, 0 ]; // Command returned by switch if we hit a comma `,` + //--------------------------------------------------------------------------------------------// - 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; - + //--------------------------------------------------------------------------------------------// + //-State machine updating---------------------------------------------------------------------// + // * yield instruction based on what byte we read * + component matcher = Switch(5, 5); + 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; + // * get the instruction mask based on current state * + component mask = StateToMask(); + mask.state <== state; + // * multiply the mask array elementwise with the instruction array * component mulMaskAndOut = ArrayMul(5); - mulMaskAndOut.lhs <== mask.mask; - mulMaskAndOut.rhs <== matcher.out; - - component addToState = ArrayAdd(5); - addToState.lhs <== state; - addToState.rhs <== mulMaskAndOut.out; + mulMaskAndOut.lhs <== mask.mask; + mulMaskAndOut.rhs <== matcher.out; + // * add the masked instruction to the state to get new state * + component addToState = ArrayAdd(5); + addToState.lhs <== state; + addToState.rhs <== mulMaskAndOut.out; + // * set the new state * + next_tree_depth <== addToState.out[0]; + next_parsing_key <== addToState.out[1]; + next_inside_key <== addToState.out[2]; + next_parsing_value <== addToState.out[3]; + next_inside_value <== addToState.out[4]; + //--------------------------------------------------------------------------------------------// - // for(var i = 0; i<6; i++) { + //--------------------------------------------------------------------------------------------// + // // DEBUGGING: internal state + // for(var i = 0; i<5; i++) { // log("-----------------------"); // log("mask[",i,"]: ", mask.mask[i]); // log("mulMaskAndOut[",i,"]:", mulMaskAndOut.out[i]); // log("state[",i,"]: ", state[i]); // log("next_state[",i,"]: ", addToState.out[i]); // } + //--------------------------------------------------------------------------------------------// - next_tree_depth <== addToState.out[0]; - next_parsing_key <== addToState.out[1]; - next_inside_key <== addToState.out[2]; - next_parsing_value <== addToState.out[3]; - next_inside_value <== addToState.out[4]; - - // Constrain bit flags + //--------------------------------------------------------------------------------------------// + //-Constraints--------------------------------------------------------------------------------// + // * constrain bit flags * next_parsing_key * (1 - next_parsing_key) === 0; // - constrain that `next_parsing_key` remain a bit flag - next_inside_key * (1 - next_inside_key) === 0; // - constrain that `next_inside_key` remain a bit flag + next_inside_key * (1 - next_inside_key) === 0; // - constrain that `next_inside_key` remain a bit flag next_parsing_value * (1 - next_parsing_value) === 0; // - constrain that `next_parsing_value` remain a bit flag - next_inside_value * (1 - next_inside_value) === 0; // - constrain that `next_inside_value` remain a bit flag - - // Constrain `tree_depth` to never hit -1 (TODO: should always moves in 1 bit increments?) + next_inside_value * (1 - next_inside_value) === 0; // - constrain that `next_inside_value` remain a bit flag + // * constrain `tree_depth` to never hit -1 (TODO: should always moves in 1 bit increments?) component isMinusOne = IsEqual(); isMinusOne.in[0] <== -1; isMinusOne.in[1] <== next_tree_depth; @@ -188,11 +196,11 @@ template StateToMask() { var parsing_value = state[3]; var inside_value = state[4]; - signal NOT_INSIDE_KEY_AND_NOT_INSIDE_VALUE <-- (1 - inside_key) * (1 - inside_value); - signal NOT_PARSING_VALUE_NOT_INSIDE_VALUE <-- (1 - parsing_value) * (1 - inside_value); + signal NOT_INSIDE_KEY_AND_NOT_INSIDE_VALUE <== (1 - inside_key) * (1 - inside_value); + signal NOT_PARSING_VALUE_NOT_INSIDE_VALUE <== (1 - parsing_value) * (1 - inside_value); component init_tree = IsZero(); - init_tree.in <-- tree_depth; + init_tree.in <== tree_depth; // `tree_depth` can change: `IF (parsing_key XOR parsing_value XOR end_of_kv)` mask[0] <== init_tree.out + parsing_key + parsing_value; // TODO: Make sure these are never both 1! From 18c68d8d7ffc0c4a91dbeeb4872cd6dd834b5b72 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 9 Aug 2024 14:08:08 -0400 Subject: [PATCH 24/24] WAYLON NITPICKING ME LOL --- circuits/extract.circom | 16 ++++++++-------- circuits/parser.circom | 33 ++++++++++++++++----------------- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/circuits/extract.circom b/circuits/extract.circom index 07b649f..76ebcae 100644 --- a/circuits/extract.circom +++ b/circuits/extract.circom @@ -24,21 +24,21 @@ template Extract(KEY_BYTES, DATA_BYTES) { // Initialze the parser component State[DATA_BYTES]; State[0] = StateUpdate(); - State[0].byte <== data[0]; - State[0].tree_depth <== 0; + State[0].byte <== data[0]; + State[0].tree_depth <== 0; State[0].parsing_key <== 0; - State[0].inside_key <== 0; + State[0].inside_key <== 0; State[0].parsing_value <== 0; - State[0].inside_value <== 0; + State[0].inside_value <== 0; for(var data_pointer = 1; data_pointer < DATA_BYTES; data_pointer++) { State[data_pointer] = StateUpdate(); - State[data_pointer].byte <== data[data_pointer]; - State[data_pointer].tree_depth <== State[data_pointer - 1].next_tree_depth; + State[data_pointer].byte <== data[data_pointer]; + State[data_pointer].tree_depth <== State[data_pointer - 1].next_tree_depth; State[data_pointer].parsing_key <== State[data_pointer - 1].next_parsing_key; - State[data_pointer].inside_key <== State[data_pointer - 1].next_inside_key; + State[data_pointer].inside_key <== State[data_pointer - 1].next_inside_key; State[data_pointer].parsing_value <== State[data_pointer - 1].next_parsing_value; - State[data_pointer].inside_value <== State[data_pointer - 1].next_inside_value; + State[data_pointer].inside_value <== State[data_pointer - 1].next_inside_value; // Debugging log("State[", data_pointer, "].tree_depth", "= ", State[data_pointer].tree_depth); diff --git a/circuits/parser.circom b/circuits/parser.circom index 8d4f405..e4ec021 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -90,10 +90,10 @@ template StateUpdate() { //--------------------------------------------------------------------------------------------// //-State machine updating---------------------------------------------------------------------// // * yield instruction based on what byte we read * - component matcher = Switch(5, 5); - 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; + component matcher = Switch(5, 5); + 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; // * get the instruction mask based on current state * component mask = StateToMask(); mask.state <== state; @@ -107,9 +107,9 @@ template StateUpdate() { addToState.rhs <== mulMaskAndOut.out; // * set the new state * next_tree_depth <== addToState.out[0]; - next_parsing_key <== addToState.out[1]; + next_parsing_key <== addToState.out[1]; next_inside_key <== addToState.out[2]; - next_parsing_value <== addToState.out[3]; + next_parsing_value <== addToState.out[3]; next_inside_value <== addToState.out[4]; //--------------------------------------------------------------------------------------------// @@ -190,17 +190,17 @@ template StateToMask() { signal input state[5]; signal output mask[5]; - var tree_depth = state[0]; - var parsing_key = state[1]; - var inside_key = state[2]; + var tree_depth = state[0]; + var parsing_key = state[1]; + var inside_key = state[2]; var parsing_value = state[3]; - var inside_value = state[4]; + var inside_value = state[4]; - signal NOT_INSIDE_KEY_AND_NOT_INSIDE_VALUE <== (1 - inside_key) * (1 - inside_value); - signal NOT_PARSING_VALUE_NOT_INSIDE_VALUE <== (1 - parsing_value) * (1 - inside_value); + signal NOT_INSIDE_KEY_AND_NOT_INSIDE_VALUE <== (1 - inside_key) * (1 - inside_value); + signal NOT_PARSING_VALUE_NOT_INSIDE_VALUE <== (1 - parsing_value) * (1 - inside_value); component init_tree = IsZero(); - init_tree.in <== tree_depth; + init_tree.in <== tree_depth; // `tree_depth` can change: `IF (parsing_key XOR parsing_value XOR end_of_kv)` mask[0] <== init_tree.out + parsing_key + parsing_value; // TODO: Make sure these are never both 1! @@ -211,10 +211,9 @@ template StateToMask() { // `inside_key` can change: `IF ((NOT parsing_value) AND (NOT inside_value) AND inside_key) THEN mask <== -1 ELSEIF (NOT parsing_value) AND (NOT inside_value) THEN mask <== 1` mask[2] <== NOT_PARSING_VALUE_NOT_INSIDE_VALUE - 2 * inside_key; - // `parsing_value` can change: `IF ((NOT parsing_key) AND (NOT inside_key) AND (NOT inside_value) AND (tree_depth != 0))` - signal INIT <== (1 - init_tree.out); - mask[3] <== INIT * NOT_INSIDE_KEY_AND_NOT_INSIDE_VALUE; + // `parsing_value` can change: `IF ((NOT inside_key) AND (NOT inside_value) AND (tree_depth != 0))` + mask[3] <== NOT_INSIDE_KEY_AND_NOT_INSIDE_VALUE * (1 - init_tree.out); // `inside_value` can change: `IF (parsing_value AND (NOT inside_value)) THEN mask <== 1 ELSEIF (inside_value) mask <== -1` - mask[4] <== parsing_value - 2 * inside_value; + mask[4] <== parsing_value - 2 * inside_value; } \ No newline at end of file