From d2be62436ff390e0ee6635030172119a0887dcde Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Wed, 7 Aug 2024 16:11:43 -0600 Subject: [PATCH 01/99] 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/99] 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/99] 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/99] 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/99] 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/99] 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/99] 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/99] 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/99] 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/99] 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/99] 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/99] 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/99] 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/99] 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/99] 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/99] 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/99] 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/99] 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/99] 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/99] 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/99] 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/99] 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/99] 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/99] 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 From 52fa82685b0891de4de3486cd8f1b38c4e129664 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 9 Aug 2024 16:01:31 -0400 Subject: [PATCH 25/99] feat: improved CLI for witness --- Cargo.lock | 220 +++++++++++++++++++++++++++++++++++-- Cargo.toml | 11 +- create_witness/Cargo.toml | 7 -- create_witness/src/main.rs | 96 ---------------- src/item.rs | 37 ------- src/lib.rs | 122 -------------------- src/main.rs | 57 ++++++++++ 7 files changed, 271 insertions(+), 279 deletions(-) delete mode 100644 create_witness/Cargo.toml delete mode 100644 create_witness/src/main.rs delete mode 100644 src/item.rs delete mode 100644 src/lib.rs create mode 100644 src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 256c9d6..2479f30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,16 +3,111 @@ version = 3 [[package]] -name = "create-witness" -version = "0.0.0" +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ - "serde", - "serde_json", + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "clap" +version = "4.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c937d4061031a6d0c8da4b9a4f98a172fc2976dfb1c19213a9cf7d0d3c837e36" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85379ba512b21a328adf887e85f7742d12e96eb31f3ef077df4ffc26b506ffed" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", ] [[package]] -name = "extractor" -version = "0.1.0" +name = "clap_derive" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itoa" @@ -20,6 +115,12 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + [[package]] name = "proc-macro2" version = "1.0.86" @@ -46,18 +147,18 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "serde" -version = "1.0.204" +version = "1.0.205" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.205" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1" dependencies = [ "proc-macro2", "quote", @@ -66,15 +167,22 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "2.0.72" @@ -91,3 +199,91 @@ name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "witness" +version = "0.0.0" +dependencies = [ + "clap", + "serde", + "serde_json", +] diff --git a/Cargo.toml b/Cargo.toml index 5a51ecc..25d2171 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,8 @@ -[workspace] -members = ["create_witness"] - [package] -name = "extractor" -version = "0.1.0" +name = "witness" edition = "2021" + +[dependencies] +serde = { version = "1.0.204", features = ["derive"] } +serde_json = "1.0.120" +clap = { version = "4.5.14", features = ["derive"] } diff --git a/create_witness/Cargo.toml b/create_witness/Cargo.toml deleted file mode 100644 index ffa0959..0000000 --- a/create_witness/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "create-witness" -edition = "2021" - -[dependencies] -serde = { version = "1.0.204", features = ["derive"] } -serde_json = "1.0.120" diff --git a/create_witness/src/main.rs b/create_witness/src/main.rs deleted file mode 100644 index dbe494b..0000000 --- a/create_witness/src/main.rs +++ /dev/null @@ -1,96 +0,0 @@ -use std::io::Write; - -// pub const KEY: &[u8] = b"\"glossary\"".as_slice(); -pub const KEY: &[u8] = b"key1".as_slice(); - -pub const KEYS: &[&[u8]] = &[ - b"\"glossary\"".as_slice(), - b"\"GlossDiv\"".as_slice(), - b"\"title\"".as_slice(), -]; -// pub const DATA: &[u8] = include_bytes!("../../json_examples/example.json"); -pub const DATA: &[u8] = include_bytes!("../../json_examples/sambhav_example.json"); - -#[derive(serde::Serialize)] -pub struct Witness { - // num_keys: usize, - // key_sizes: Vec, - // keys: Vec>, - // num_key_bytes: usize, - key: Vec, - // num_data_bytes: usize, - data: Vec, -} - -pub fn main() { - // Properly serialize information about the keys we want to extract - // let mut max_num_keys = 0; - // let mut max_num_key_bytes = 0; - // let mut key_sizes = vec![]; - // let mut keys = vec![]; - // for &key in KEYS { - // let key_len = key.len(); - // key_sizes.push(key_len); - // if key_len > max_num_key_bytes { - // max_num_key_bytes = key_len; - // } - // keys.push(key.to_vec()); - // max_num_keys += 1; - // } - // println!("MAX_NUM_KEYS: {max_num_keys}"); - // println!("MAX_NUM_KEY_BYTES: {max_num_key_bytes}"); - - // Enforce that each key comes in as af fixed length (TODO: we need to make sure we encode this somehow, perhaps we pass in a vector of key lengths) - // for key in &mut keys { - // key.extend(vec![0; max_num_key_bytes - key.len()]); - // } - - // Properly serialize information about the data we extract from - println!("NUM_KEY_BYTES: {}", KEY.len()); - println!("NUM_DATA_BYTES: {}", DATA.len()); - - // Create a witness file as `input.json` - let witness = Witness { - // num_keys: max_num_keys, // For now we can set this to be the same - // key_sizes, - // keys, - // num_key_bytes: KEY.len(), - key: KEY.to_vec(), - // 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_sambhav/input.json").unwrap(); - file.write_all(serde_json::to_string_pretty(&witness).unwrap().as_bytes()) - .unwrap(); -} - -// fn get_bits(bytes: &[u8]) -> Vec { -// bytes -// .iter() -// .flat_map(|&byte| { -// (0..8) -// .rev() -// .map(move |i| ((byte.to_be_bytes()[0] >> i) & 1) == 1) // ensure this is all big-endian -// }) -// .collect() -// } - -// #[cfg(test)] -// mod tests { -// use super::*; -// // Use example.json which has first two ASCII chars: `{` and `\n` -// // ASCII code for `{` 01111011 -// // ASCII code for `\n` 00001010 -// #[test] -// fn test_get_bits() { -// let bits = get_bits(DATA); -// #[allow(clippy::inconsistent_digit_grouping)] -// let compare_bits: Vec = vec![0, 1, 1, 1, 1, 0, 1, 1_, 0, 0, 0, 0, 1, 0, 1, 0] -// .into_iter() -// .map(|x| x == 1) -// .collect(); -// bits.iter() -// .zip(compare_bits.iter()) -// .for_each(|(x, y)| assert_eq!(x, y)); -// } -// } diff --git a/src/item.rs b/src/item.rs deleted file mode 100644 index 8823da8..0000000 --- a/src/item.rs +++ /dev/null @@ -1,37 +0,0 @@ -#[derive(Clone, Copy, Debug)] -pub struct Item<'a, T> { - pub label: &'a str, - inner: T, -} - -impl<'a> Item<'a, ()> { - pub fn create(label: &'a str) -> Self { - Item { label, inner: () } - } -} - -impl<'a, T: Copy> Item<'a, T> { - pub fn inner(&self) -> T { - self.inner - } - - pub fn append(self, label: &'a str) -> Item<'a, Item> { - Item { label, inner: self } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn create_nested_item() { - let item = Item::create("data") - .append("profile") - .append("identity") - .append("balance") - .append("userBalance") - .append("value"); - println!("{item:?}"); - } -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 71db018..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,122 +0,0 @@ -pub const EXAMPLE_JSON: &[u8] = include_bytes!("../json_examples/example.json"); -pub const VENMO_JSON: &[u8] = include_bytes!("../json_examples/venmo_response.json"); - -pub mod item; - -// TODO: Mark when we're in a key versus in a value, versus whitespace, etc.? - -pub struct Machine<'a> { - pub keys: Vec<&'a [u8]>, - depth: usize, - pointer: usize, -} - -#[derive(Debug)] -pub enum Instruction { - IncreaseDepth(usize), - DecreaseDepth(usize), - EOF, -} - -impl<'a> Machine<'a> { - pub fn new(keys: Vec<&'a [u8]>) -> Self { - Machine { - keys, - depth: 0, - pointer: 0, - } - } - - pub fn extract(&mut self, data_bytes: &'a [u8]) -> Option<&'a [u8]> { - // Make sure that there is more data in the JSON than what we have expressed in all of our keys else this makes no sense at all. - assert!(data_bytes.len() > self.keys.iter().map(|k| k.len()).sum()); - // Make sure the JSON begins with an opening bracket - assert_eq!(data_bytes[0] ^ b"{"[0], 0); - - while self.depth < self.keys.len() { - match get_key(self.keys[self.depth], &data_bytes[self.pointer..]) { - Instruction::EOF => return None, - _inst @ Instruction::DecreaseDepth(offset) => { - // dbg!(inst); - self.depth -= 1; - self.pointer += offset; - // dbg!(String::from_utf8_lossy(&[data_bytes[self.pointer]])); - } - _inst @ Instruction::IncreaseDepth(offset) => { - // dbg!(inst); - self.depth += 1; - self.pointer += offset; - // dbg!(String::from_utf8_lossy(&[data_bytes[self.pointer]])); - } - } - } - - // Get the value as a raw str at this location in the JSON and offset by one to bypass a `:` - let value_start = self.pointer + 1; - let mut value_length = 0; - // Grab the value up to the next delimiter doken (TODO: if a `,` or `}` is present in a string, we are doomed, so we need to track these objects better!) - while (data_bytes[value_start + value_length] != b"}"[0]) - & (data_bytes[value_start + value_length] != b","[0]) - { - value_length += 1; - } - Some(&data_bytes[value_start..value_start + value_length]) - } -} - -fn get_key(key: &[u8], data_bytes: &[u8]) -> Instruction { - let key_length = key.len(); - - // dbg!(String::from_utf8_lossy(key)); - - 'outer: for i in 0..(data_bytes.len() - key_length) { - #[allow(clippy::needless_range_loop)] - for j in 0..key_length { - // dbg!(String::from_utf8_lossy(&[data_bytes[i..i + key_length][j]])); - if data_bytes[i..i + key_length][j] == b"}"[0] { - // Hit an end brace "}" so we need to return the current pointer as an offset and decrease depth - return Instruction::DecreaseDepth(i + j); - } - if key[j] ^ data_bytes[i..i + key_length][j] != 0 { - continue 'outer; - } - } - // If we hit here then we must have fully matched a key so we return the current pointer as an offset - return Instruction::IncreaseDepth(i + key_length); - } - // If we hit here, we must have hit EOF (which is actually an error?) - Instruction::EOF -} - -#[cfg(test)] -mod tests { - - use super::*; - - #[test] - fn get_value_venmo() { - let keys = vec![ - b"\"data\"".as_slice(), - b"\"profile\"".as_slice(), - b"\"identity\"".as_slice(), - b"\"balance\"".as_slice(), - b"\"userBalance\"".as_slice(), - b"\"value\"".as_slice(), - ]; - let mut machine = Machine::new(keys); - let value = String::from_utf8_lossy(machine.extract(VENMO_JSON).unwrap()); - assert_eq!(value, " 523.69\n ") - } - - #[test] - fn get_value_example() { - let keys = vec![ - b"\"glossary\"".as_slice(), - b"\"GlossDiv\"".as_slice(), - b"\"title\"".as_slice(), - ]; - let mut machine = Machine::new(keys); - let value = String::from_utf8_lossy(machine.extract(EXAMPLE_JSON).unwrap()); - assert_eq!(value, " \"S\"a") - } -} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..b91a900 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,57 @@ +use clap::Parser; +use serde_json::Value; +use std::io::Write; +use std::path::PathBuf; + +#[derive(Parser, Debug)] +#[command(name = "witness")] +struct Args { + /// Path to the JSON file + #[arg(short, long)] + json_file: PathBuf, + + /// Keys to extract (can be specified multiple times) + #[arg(short, long)] + keys: Vec, +} + +#[derive(serde::Serialize)] +pub struct Witness { + #[serde(flatten)] + keys: serde_json::Map, + data: Vec, +} + +pub fn main() -> Result<(), Box> { + let args = Args::parse(); + + // Read the JSON file + let data = std::fs::read(&args.json_file)?; + + // Create a map to store keys + let mut keys_map = serde_json::Map::new(); + for (index, key) in args.keys.iter().enumerate() { + keys_map.insert( + format!("key{}", index + 1), + Value::Array( + key.as_bytes() + .iter() + .map(|x| serde_json::json!(x)) + .collect(), + ), + ); + } + + // Create a witness file as `input.json` + let witness = Witness { + keys: keys_map, + data, + }; + + let mut file = std::fs::File::create("input.json")?; + file.write_all(serde_json::to_string_pretty(&witness)?.as_bytes())?; + + println!("Input file created successfully."); + + Ok(()) +} From 331f21814b255cab44858cf07f684be67eecaee5 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 9 Aug 2024 16:05:54 -0400 Subject: [PATCH 26/99] gitignore input.json --- .gitignore | 3 +- inputs/extract/input.json | 803 ------------------------- inputs/test_extract/input.json | 31 - inputs/test_extract_depth/input.json | 74 --- inputs/test_extract_hard/input.json | 58 -- inputs/test_extract_sambhav/input.json | 115 ---- inputs/test_extract_two_key/input.json | 50 -- 7 files changed, 2 insertions(+), 1132 deletions(-) delete mode 100644 inputs/extract/input.json delete mode 100644 inputs/test_extract/input.json delete mode 100644 inputs/test_extract_depth/input.json delete mode 100644 inputs/test_extract_hard/input.json delete mode 100644 inputs/test_extract_sambhav/input.json delete mode 100644 inputs/test_extract_two_key/input.json diff --git a/.gitignore b/.gitignore index 5b586b4..40d4c9c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ node_modules/* build/* ptau/* circuits/test/*.circom -circuits/main/* \ No newline at end of file +circuits/main/* +inputs/**/*.json \ No newline at end of file diff --git a/inputs/extract/input.json b/inputs/extract/input.json deleted file mode 100644 index 556be54..0000000 --- a/inputs/extract/input.json +++ /dev/null @@ -1,803 +0,0 @@ -{ - "key": [ - 34, - 103, - 108, - 111, - 115, - 115, - 97, - 114, - 121, - 34 - ], - "data": [ - 123, - 10, - 32, - 32, - 32, - 32, - 34, - 103, - 108, - 111, - 115, - 115, - 97, - 114, - 121, - 34, - 58, - 32, - 123, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 116, - 105, - 116, - 108, - 101, - 34, - 58, - 32, - 34, - 101, - 120, - 97, - 109, - 112, - 108, - 101, - 32, - 103, - 108, - 111, - 115, - 115, - 97, - 114, - 121, - 34, - 44, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 71, - 108, - 111, - 115, - 115, - 68, - 105, - 118, - 34, - 58, - 32, - 123, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 116, - 105, - 116, - 108, - 101, - 34, - 58, - 32, - 34, - 83, - 34, - 44, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 71, - 108, - 111, - 115, - 115, - 76, - 105, - 115, - 116, - 34, - 58, - 32, - 123, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 71, - 108, - 111, - 115, - 115, - 69, - 110, - 116, - 114, - 121, - 34, - 58, - 32, - 123, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 73, - 68, - 34, - 58, - 32, - 34, - 83, - 71, - 77, - 76, - 34, - 44, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 83, - 111, - 114, - 116, - 65, - 115, - 34, - 58, - 32, - 34, - 83, - 71, - 77, - 76, - 34, - 44, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 71, - 108, - 111, - 115, - 115, - 84, - 101, - 114, - 109, - 34, - 58, - 32, - 34, - 83, - 116, - 97, - 110, - 100, - 97, - 114, - 100, - 32, - 71, - 101, - 110, - 101, - 114, - 97, - 108, - 105, - 122, - 101, - 100, - 32, - 77, - 97, - 114, - 107, - 117, - 112, - 32, - 76, - 97, - 110, - 103, - 117, - 97, - 103, - 101, - 34, - 44, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 65, - 99, - 114, - 111, - 110, - 121, - 109, - 34, - 58, - 32, - 34, - 83, - 71, - 77, - 76, - 34, - 44, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 65, - 98, - 98, - 114, - 101, - 118, - 34, - 58, - 32, - 34, - 73, - 83, - 79, - 32, - 56, - 56, - 55, - 57, - 58, - 49, - 57, - 56, - 54, - 34, - 44, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 71, - 108, - 111, - 115, - 115, - 68, - 101, - 102, - 34, - 58, - 32, - 123, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 112, - 97, - 114, - 97, - 34, - 58, - 32, - 34, - 65, - 32, - 109, - 101, - 116, - 97, - 45, - 109, - 97, - 114, - 107, - 117, - 112, - 32, - 108, - 97, - 110, - 103, - 117, - 97, - 103, - 101, - 44, - 32, - 117, - 115, - 101, - 100, - 32, - 116, - 111, - 32, - 99, - 114, - 101, - 97, - 116, - 101, - 32, - 109, - 97, - 114, - 107, - 117, - 112, - 32, - 108, - 97, - 110, - 103, - 117, - 97, - 103, - 101, - 115, - 32, - 115, - 117, - 99, - 104, - 32, - 97, - 115, - 32, - 68, - 111, - 99, - 66, - 111, - 111, - 107, - 46, - 34, - 44, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 71, - 108, - 111, - 115, - 115, - 83, - 101, - 101, - 65, - 108, - 115, - 111, - 34, - 58, - 32, - 91, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 71, - 77, - 76, - 34, - 44, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 88, - 77, - 76, - 34, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 93, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 125, - 44, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 71, - 108, - 111, - 115, - 115, - 83, - 101, - 101, - 34, - 58, - 32, - 34, - 109, - 97, - 114, - 107, - 117, - 112, - 34, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 125, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 125, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 125, - 10, - 32, - 32, - 32, - 32, - 125, - 10, - 125 - ] -} \ No newline at end of file diff --git a/inputs/test_extract/input.json b/inputs/test_extract/input.json deleted file mode 100644 index d99f3f3..0000000 --- a/inputs/test_extract/input.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "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, - 10, - 125 - ] -} \ No newline at end of file diff --git a/inputs/test_extract_depth/input.json b/inputs/test_extract_depth/input.json deleted file mode 100644 index f4608b1..0000000 --- a/inputs/test_extract_depth/input.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "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/inputs/test_extract_hard/input.json b/inputs/test_extract_hard/input.json deleted file mode 100644 index a80958f..0000000 --- a/inputs/test_extract_hard/input.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "key": [ - 107, - 101, - 121, - 49 - ], - "data": [ - 123, - 10, - 32, - 32, - 32, - 32, - 34, - 107, - 101, - 121, - 49, - 34, - 58, - 32, - 34, - 123, - 125, - 97, - 44, - 98, - 99, - 125, - 34, - 44, - 10, - 32, - 32, - 32, - 32, - 34, - 107, - 101, - 121, - 50, - 34, - 58, - 32, - 34, - 92, - 34, - 97, - 98, - 99, - 92, - 34, - 34, - 10, - 125 - ] -} \ No newline at end of file diff --git a/inputs/test_extract_sambhav/input.json b/inputs/test_extract_sambhav/input.json deleted file mode 100644 index 9e6cbba..0000000 --- a/inputs/test_extract_sambhav/input.json +++ /dev/null @@ -1,115 +0,0 @@ -{ - "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/inputs/test_extract_two_key/input.json b/inputs/test_extract_two_key/input.json deleted file mode 100644 index 619812a..0000000 --- a/inputs/test_extract_two_key/input.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "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 From 7eef73ebdada5bf978557a3a08d7dfe2b9eb417f Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 9 Aug 2024 16:10:51 -0400 Subject: [PATCH 27/99] Update main.rs --- src/main.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index b91a900..cf94f9d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,14 @@ struct Args { /// Keys to extract (can be specified multiple times) #[arg(short, long)] keys: Vec, + + /// Output directory (will be created if it doesn't exist) + #[arg(short, long, default_value = ".")] + output_dir: PathBuf, + + /// Output filename (will be created if it doesn't exist) + #[arg(short, long, default_value = "output.json")] + filename: String, } #[derive(serde::Serialize)] @@ -48,7 +56,8 @@ pub fn main() -> Result<(), Box> { data, }; - let mut file = std::fs::File::create("input.json")?; + let output_file = args.output_dir.join(args.filename); + let mut file = std::fs::File::create(output_file)?; file.write_all(serde_json::to_string_pretty(&witness)?.as_bytes())?; println!("Input file created successfully."); From ca148b56eb435681be49ae9060b5f872caee9b00 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 9 Aug 2024 17:44:24 -0400 Subject: [PATCH 28/99] feat: update rust --- src/main.rs | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index cf94f9d..8f16d6c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -53,14 +53,43 @@ pub fn main() -> Result<(), Box> { // Create a witness file as `input.json` let witness = Witness { keys: keys_map, - data, + data: data.clone(), }; + if !args.output_dir.exists() { + std::fs::create_dir_all(&args.output_dir)?; + } + let output_file = args.output_dir.join(args.filename); let mut file = std::fs::File::create(output_file)?; file.write_all(serde_json::to_string_pretty(&witness)?.as_bytes())?; - println!("Input file created successfully."); + // Prepare lines to print + let mut lines = Vec::new(); + lines.push(String::from("Key lengths:")); + for (index, key) in args.keys.iter().enumerate() { + lines.push(format!("key{} length: {}", index + 1, key.len())); + } + lines.push(format!("Data length: {}", data.len())); + + // Print the output inside a nicely formatted box + print_boxed_output(lines); Ok(()) } + +fn print_boxed_output(lines: Vec) { + // Determine the maximum length of the lines + let max_length = lines.iter().map(|line| line.len()).max().unwrap_or(0); + + // Characters for the box + let top_border = format!("┌{}┐", "─".repeat(max_length + 2)); + let bottom_border = format!("└{}┘", "─".repeat(max_length + 2)); + + // Print the box with content + println!("{}", top_border); + for line in lines { + println!("│ {: Date: Fri, 9 Aug 2024 17:44:34 -0400 Subject: [PATCH 29/99] feat: parse with array as value --- circuits.json | 7 +++++++ circuits/extract.circom | 10 ++-------- circuits/parser.circom | 24 ++++++++++++++---------- json_examples/test_array.json | 1 + 4 files changed, 24 insertions(+), 18 deletions(-) create mode 100644 json_examples/test_array.json diff --git a/circuits.json b/circuits.json index 43017d8..e4ccfd6 100644 --- a/circuits.json +++ b/circuits.json @@ -31,6 +31,13 @@ 105 ] }, + "extract_array": { + "file": "extract", + "template": "Extract", + "params": [ + 15 + ] + }, "test_extract_hard": { "file": "extract", "template": "Extract", diff --git a/circuits/extract.circom b/circuits/extract.circom index 76ebcae..78c69f4 100644 --- a/circuits/extract.circom +++ b/circuits/extract.circom @@ -4,20 +4,14 @@ include "bytes.circom"; include "operators.circom"; include "parser.circom"; -template Extract(KEY_BYTES, DATA_BYTES) { - signal input key[KEY_BYTES]; +template Extract(DATA_BYTES) { signal input data[DATA_BYTES]; - signal output KeyMatches[DATA_BYTES - KEY_BYTES]; // TODO: Add assertions on the inputs here! //--------------------------------------------------------------------------------------------// //-CONSTRAINTS--------------------------------------------------------------------------------// - //--------------------------------------------------------------------------------------------// - // Working with a single key for now to do substring matching - component keyASCII = ASCII(KEY_BYTES); - keyASCII.in <== key; - + //--------------------------------------------------------------------------------------------// component dataASCII = ASCII(DATA_BYTES); dataASCII.in <== data; //--------------------------------------------------------------------------------------------// diff --git a/circuits/parser.circom b/circuits/parser.circom index e4ec021..aeb73bc 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -78,21 +78,25 @@ template StateUpdate() { // TODO: ADD CASE FOR `is_number` for in range 48-57 https://www.ascii-code.com since a value may just be a number //--------------------------------------------------------------------------------------------// //-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 `,` + 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 `,` + var hit_start_bracket[5] = [ 0, 0, 0, 0, 1 ]; // Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) + var hit_end_bracket[5] = [ 0, 0, 0, 0, 1 ]; // Command returned by switch if we hit a start bracket `]` + // TODO + var hit_number[5] = [ 0, 0, 0, 0, 1 ]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// //-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]; + component matcher = Switch(7, 5); + matcher.branches <== [start_brace, end_brace, quote, colon, comma, start_bracket, end_bracket ]; + matcher.vals <== [hit_start_brace, hit_end_brace, hit_quote, hit_colon, hit_comma, hit_start_bracket, hit_end_bracket]; matcher.case <== byte; // * get the instruction mask based on current state * component mask = StateToMask(); diff --git a/json_examples/test_array.json b/json_examples/test_array.json new file mode 100644 index 0000000..6ba7a05 --- /dev/null +++ b/json_examples/test_array.json @@ -0,0 +1 @@ +{"key":[0,1,2]} \ No newline at end of file From 59987572596d1df4eb2119309ba06cc83609dfb5 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 9 Aug 2024 18:16:09 -0400 Subject: [PATCH 30/99] feat; `InRange` template --- circuits/bytes.circom | 18 ++++++++- circuits/operators.circom | 71 +++++++++++++++++++++++++++++++++ circuits/parser.circom | 7 ++-- circuits/test/operators.test.ts | 33 +++++++++++++++ json_examples/test_number.json | 1 + 5 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 json_examples/test_number.json diff --git a/circuits/bytes.circom b/circuits/bytes.circom index 6b5c38b..87a8b25 100644 --- a/circuits/bytes.circom +++ b/circuits/bytes.circom @@ -55,4 +55,20 @@ template ASCII(n) { Byte[i] = U8ToBits(); Byte[i].in <== in[i]; } -} \ No newline at end of file +} + +template Num2Bits(n) { + signal input in; + signal output out[n]; + var lc1=0; + + var e2=1; + for (var i = 0; i> i) & 1; + out[i] * (out[i] -1 ) === 0; + lc1 += out[i] * e2; + e2 = e2+e2; + } + + lc1 === in; +} diff --git a/circuits/operators.circom b/circuits/operators.circom index d62d5f0..ba2d6e2 100644 --- a/circuits/operators.circom +++ b/circuits/operators.circom @@ -1,5 +1,7 @@ pragma circom 2.1.9; +include "bytes.circom"; + /* All tests for this file are located in: `./test/operators.test.ts` @@ -142,4 +144,73 @@ template ArrayMul(n) { for(var i = 0; i < n; i++) { out[i] <== lhs[i] * rhs[i]; } +} + +template LessThan(n) { + assert(n <= 252); + signal input in[2]; + signal output out; + + component n2b = Num2Bits(n+1); + + n2b.in <== in[0]+ (1< out; +} + +// N is the number of bits the input have. +// The MSF is the sign bit. +template GreaterThan(n) { + signal input in[2]; + signal output out; + + component lt = LessThan(n); + + lt.in[0] <== in[1]; + lt.in[1] <== in[0]; + lt.out ==> out; +} + +// N is the number of bits the input have. +// The MSF is the sign bit. +template GreaterEqThan(n) { + signal input in[2]; + signal output out; + + component lt = LessThan(n); + + lt.in[0] <== in[1]; + lt.in[1] <== in[0]+1; + lt.out ==> out; +} + +template InRange(n) { + signal input in; + signal input range[2]; + signal output out; + + component gte = GreaterEqThan(n); + gte.in <== [in, range[0]]; + log("gte.out: ", gte.out); + + component lte = LessEqThan(n); + lte.in <== [in, range[1]]; + log("lte.out: ", lte.out); + + out <== gte.out * lte.out; } \ No newline at end of file diff --git a/circuits/parser.circom b/circuits/parser.circom index aeb73bc..6c84465 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -94,9 +94,10 @@ template StateUpdate() { //--------------------------------------------------------------------------------------------// //-State machine updating---------------------------------------------------------------------// // * yield instruction based on what byte we read * - component matcher = Switch(7, 5); - matcher.branches <== [start_brace, end_brace, quote, colon, comma, start_bracket, end_bracket ]; - matcher.vals <== [hit_start_brace, hit_end_brace, hit_quote, hit_colon, hit_comma, hit_start_bracket, hit_end_bracket]; + component matcher = Switch(8, 5); + matcher.branches <== [start_brace, end_brace, quote, colon, comma, start_bracket, end_bracket , number ]; + matcher.vals <== [hit_start_brace, hit_end_brace, hit_quote, hit_colon, hit_comma, hit_start_bracket, hit_end_bracket, hit_number]; + component LEQ = LessEqThan(8); matcher.case <== byte; // * get the instruction mask based on current state * component mask = StateToMask(); diff --git a/circuits/test/operators.test.ts b/circuits/test/operators.test.ts index 50baec2..4ba9891 100644 --- a/circuits/test/operators.test.ts +++ b/circuits/test/operators.test.ts @@ -198,4 +198,37 @@ describe("operators", () => { }); }); + + describe("InRange", () => { + let circuit: WitnessTester<["in", "range"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`InRange`, { + file: "circuits/operators", + template: "InRange", + params: [8], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("witness: in = 1, range = [0,2]", async () => { + await circuit.expectPass( + { in: 1, range: [0, 2] }, + { out: 1 } + ); + }); + + it("witness: in = 69, range = [128,255]", async () => { + await circuit.expectPass( + { in: 69, range: [128, 255] }, + { out: 0 } + ); + }); + + it("witness: in = 200, range = [128,255]", async () => { + await circuit.expectPass( + { in: 1, range: [0, 2] }, + { out: 1 } + ); + }); + }); }); diff --git a/json_examples/test_number.json b/json_examples/test_number.json new file mode 100644 index 0000000..5d472e5 --- /dev/null +++ b/json_examples/test_number.json @@ -0,0 +1 @@ +{"key":123} \ No newline at end of file From e9d8b1736a40549cf2d33a039233c9e47470d423 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 9 Aug 2024 18:23:42 -0400 Subject: [PATCH 31/99] WIP: number parsing --- circuits.json | 7 +++++++ circuits/parser.circom | 7 +++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/circuits.json b/circuits.json index e4ccfd6..289a1c8 100644 --- a/circuits.json +++ b/circuits.json @@ -38,6 +38,13 @@ 15 ] }, + "extract_number": { + "file": "extract", + "template": "Extract", + "params": [ + 11 + ] + }, "test_extract_hard": { "file": "extract", "template": "Extract", diff --git a/circuits/parser.circom b/circuits/parser.circom index 6c84465..18bf7eb 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -95,10 +95,13 @@ template StateUpdate() { //-State machine updating---------------------------------------------------------------------// // * yield instruction based on what byte we read * component matcher = Switch(8, 5); + var number = 256; // Number beyond a byte to represent an ASCII numeral matcher.branches <== [start_brace, end_brace, quote, colon, comma, start_bracket, end_bracket , number ]; matcher.vals <== [hit_start_brace, hit_end_brace, hit_quote, hit_colon, hit_comma, hit_start_bracket, hit_end_bracket, hit_number]; - component LEQ = LessEqThan(8); - matcher.case <== byte; + component numeral_range_check = InRange(8); + numeral_range_check.in <== byte; + numeral_range_check.range <== [48, 57]; // ASCII NUMERALS + matcher.case <== (1 - numeral_range_check.out) * byte + numeral_range_check.out * 256; // * get the instruction mask based on current state * component mask = StateToMask(); mask.state <== state; From b8923e749294064517d3740faeb389e750d5ac2c Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Sat, 10 Aug 2024 07:53:21 -0400 Subject: [PATCH 32/99] good stopping point --- circuits/extract.circom | 7 +++++++ circuits/parser.circom | 7 ++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/circuits/extract.circom b/circuits/extract.circom index 78c69f4..4ccba22 100644 --- a/circuits/extract.circom +++ b/circuits/extract.circom @@ -45,4 +45,11 @@ template Extract(DATA_BYTES) { // Constrain to have valid JSON (TODO: more is needed) State[DATA_BYTES - 1].next_tree_depth === 0; + + log("State[", DATA_BYTES, "].tree_depth", "= ", State[DATA_BYTES-1].tree_depth); + log("State[", DATA_BYTES, "].parsing_key", "= ", State[DATA_BYTES-1].parsing_key); + log("State[", DATA_BYTES, "].inside_key", "= ", State[DATA_BYTES-1].inside_key); + log("State[", DATA_BYTES, "].parsing_value", "= ", State[DATA_BYTES-1].parsing_value); + log("State[", DATA_BYTES, "].inside_value", "= ", State[DATA_BYTES-1].inside_value); + log("---"); } \ No newline at end of file diff --git a/circuits/parser.circom b/circuits/parser.circom index 18bf7eb..4dda5cb 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -25,17 +25,18 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ /* -TODO +TODO: Probably need a "reading_number" and "reading_array" and possibly "reading_string" state so that we can make rules based on these. E.g., a number is done when you hit white space. */ 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`. + signal input tree_depth; // STACK -- 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 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 in_number_value; // signal output next_tree_depth; // STATUS_INDICATOR -- next state for `tree_depth`. signal output next_parsing_key; // BIT_FLAG -- next state for `parsing_key`. @@ -101,7 +102,7 @@ template StateUpdate() { component numeral_range_check = InRange(8); numeral_range_check.in <== byte; numeral_range_check.range <== [48, 57]; // ASCII NUMERALS - matcher.case <== (1 - numeral_range_check.out) * byte + numeral_range_check.out * 256; + matcher.case <== (1 - numeral_range_check.out) * byte + numeral_range_check.out * 256; // IF (NOT is_number) THEN byte ELSEIF 256 // * get the instruction mask based on current state * component mask = StateToMask(); mask.state <== state; From c673c1b35513b0e7900af2502c57a793b59659bf Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Sat, 10 Aug 2024 09:29:41 -0400 Subject: [PATCH 33/99] compiling again --- circuits.json | 3 +- circuits/extract.circom | 62 +++++++------ circuits/operators.circom | 2 - circuits/parser.circom | 182 ++++++++++++++++++++------------------ 4 files changed, 133 insertions(+), 116 deletions(-) diff --git a/circuits.json b/circuits.json index 289a1c8..5e0c923 100644 --- a/circuits.json +++ b/circuits.json @@ -1,9 +1,8 @@ { - "test_extract": { + "extract_test": { "file": "extract", "template": "Extract", "params": [ - 4, 21 ] }, diff --git a/circuits/extract.circom b/circuits/extract.circom index 4ccba22..712e607 100644 --- a/circuits/extract.circom +++ b/circuits/extract.circom @@ -18,38 +18,46 @@ template Extract(DATA_BYTES) { // Initialze the parser component State[DATA_BYTES]; State[0] = StateUpdate(); - State[0].byte <== data[0]; - State[0].tree_depth <== 0; - State[0].parsing_key <== 0; - State[0].inside_key <== 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_key <== State[data_pointer - 1].next_parsing_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[0].byte <== data[0]; + State[0].pointer <== 0; + State[0].depth <== [0,0,0,0]; + State[0].parsing_string <== 0; + State[0].parsing_array <== 0; + State[0].parsing_object <== 0; + State[0].parsing_number <== 0; + State[0].key_or_value <== 0; + + for(var data_idx = 1; data_idx < DATA_BYTES; data_idx++) { + State[data_idx] = StateUpdate(); + State[data_idx].byte <== data[data_idx]; + State[data_idx].pointer <== State[data_idx - 1].pointer; + State[data_idx].depth <== State[data_idx - 1].depth; + State[data_idx].parsing_string <== State[data_idx - 1].parsing_string; + State[data_idx].parsing_array <== State[data_idx - 1].parsing_array; + State[data_idx].parsing_object <== State[data_idx - 1].parsing_object; + State[data_idx].parsing_number <== State[data_idx - 1].parsing_number; + State[data_idx].key_or_value <== State[data_idx - 1].key_or_value; // Debugging - log("State[", data_pointer, "].tree_depth", "= ", State[data_pointer].tree_depth); - 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_value", "= ", State[data_pointer].parsing_value); - log("State[", data_pointer, "].inside_value", "= ", State[data_pointer].inside_value); - log("---"); + log("State[", data_idx, "].pointer ", "= ", State[data_idx].pointer); + for(var i = 0; i<4; i++) { + log("State[", data_idx, "].depth[", i,"] ", "= ", State[data_idx].depth[i]); + } + log("State[", data_idx, "].parsing_string", "= ", State[data_idx].parsing_string); + log("State[", data_idx, "].parsing_array ", "= ", State[data_idx].parsing_array ); + log("State[", data_idx, "].parsing_object", "= ", State[data_idx].parsing_object); + log("State[", data_idx, "].parsing_number", "= ", State[data_idx].parsing_number); + log("State[", data_idx, "].key_or_value ", "= ", State[data_idx].key_or_value ); + log("-----------------------------------------"); } // Constrain to have valid JSON (TODO: more is needed) - State[DATA_BYTES - 1].next_tree_depth === 0; + // State[DATA_BYTES - 1].next_tree_depth === 0; - log("State[", DATA_BYTES, "].tree_depth", "= ", State[DATA_BYTES-1].tree_depth); - log("State[", DATA_BYTES, "].parsing_key", "= ", State[DATA_BYTES-1].parsing_key); - log("State[", DATA_BYTES, "].inside_key", "= ", State[DATA_BYTES-1].inside_key); - log("State[", DATA_BYTES, "].parsing_value", "= ", State[DATA_BYTES-1].parsing_value); - log("State[", DATA_BYTES, "].inside_value", "= ", State[DATA_BYTES-1].inside_value); + // log("State[", DATA_BYTES, "].tree_depth", "= ", State[DATA_BYTES-1].tree_depth); + // log("State[", DATA_BYTES, "].parsing_key", "= ", State[DATA_BYTES-1].parsing_key); + // log("State[", DATA_BYTES, "].inside_key", "= ", State[DATA_BYTES-1].inside_key); + // log("State[", DATA_BYTES, "].parsing_value", "= ", State[DATA_BYTES-1].parsing_value); + // log("State[", DATA_BYTES, "].inside_value", "= ", State[DATA_BYTES-1].inside_value); log("---"); } \ No newline at end of file diff --git a/circuits/operators.circom b/circuits/operators.circom index ba2d6e2..11de6e3 100644 --- a/circuits/operators.circom +++ b/circuits/operators.circom @@ -206,11 +206,9 @@ template InRange(n) { component gte = GreaterEqThan(n); gte.in <== [in, range[0]]; - log("gte.out: ", gte.out); component lte = LessEqThan(n); lte.in <== [in, range[1]]; - log("lte.out: ", lte.out); out <== gte.out * lte.out; } \ No newline at end of file diff --git a/circuits/parser.circom b/circuits/parser.circom index 4dda5cb..c2b22d8 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -25,29 +25,16 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ /* -TODO: Probably need a "reading_number" and "reading_array" and possibly "reading_string" state so that we can make rules based on these. E.g., a number is done when you hit white space. +JSON TYPES: +Number. +String. +Boolean. +Array. +Object. +Whitespace. +Null. */ template StateUpdate() { - signal input byte; - - signal input tree_depth; // STACK -- 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 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 in_number_value; // - - 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_parsing_value; // BIT_FLAG -- next state for `parsing_value`. - signal output next_inside_value; // BIT_FLAG -- next state for `inside_value`. - - // 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---------------------------------------------------------------------------------// // - ASCII char: `{` @@ -76,50 +63,74 @@ template StateUpdate() { var escape = 92; //--------------------------------------------------------------------------------------------// - // TODO: ADD CASE FOR `is_number` for in range 48-57 https://www.ascii-code.com since a value may just be a number + signal input byte; + + signal input pointer; // POINTER -- points to the stack to mark where we currently are inside the JSON. + signal input depth[4]; // STACK -- how deep in a JSON nest we are and what type we are currently inside (e.g., `1` for object, `-1` for array). + signal input parsing_string; + signal input parsing_array; + signal input parsing_object; + signal input parsing_number; + signal input key_or_value; // BIT_FLAG-- whether we are in a key or a value + // signal parsing_boolean; + // signal parsing_null; // TODO + + signal output next_pointer; + signal output next_depth[4]; + signal output next_parsing_string; + signal output next_parsing_object; + signal output next_parsing_array; + signal output next_parsing_number; + signal output next_key_or_value; //--------------------------------------------------------------------------------------------// //-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 `,` - var hit_start_bracket[5] = [ 0, 0, 0, 0, 1 ]; // Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) - var hit_end_bracket[5] = [ 0, 0, 0, 0, 1 ]; // Command returned by switch if we hit a start bracket `]` - // TODO - var hit_number[5] = [ 0, 0, 0, 0, 1 ]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) + var pushpop = 0; + var obj_or_arr = 0; + var parsing_state[7] = [pushpop, obj_or_arr, parsing_string, parsing_array, parsing_object, parsing_number, key_or_value]; + var do_nothing[7] = [0, 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[7] = [1, 1, 0, -1, 1, 0, 0]; // Command returned by switch if we hit a start brace `{` + var hit_end_brace[7] = [-1, 1, 0, 0, -1, 0, 0]; // Command returned by switch if we hit a end brace `}` + var hit_start_bracket[7] = [1, -1, 0, 1, -1, 0, 0]; // TODO: Might want `key_or_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) + var hit_end_bracket[7] = [-1, -1, 0, -1, 0, 0, 0]; // Command returned by switch if we hit a start bracket `]` + var hit_quote[7] = [0, 0, 1, 1, 1, 0, 0]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` + var hit_colon[7] = [0, 0, 0, 0, 0, 0, 1]; // Command returned by switch if we hit a colon `:` + var hit_comma[7] = [0, 0, 0, 0, 0, -1, 0]; // Command returned by switch if we hit a comma `,` + var hit_number[7] = [0, 0, 0, 0, 0, 1, 0]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// //-State machine updating---------------------------------------------------------------------// // * yield instruction based on what byte we read * - component matcher = Switch(8, 5); + component matcher = Switch(8, 7); var number = 256; // Number beyond a byte to represent an ASCII numeral - matcher.branches <== [start_brace, end_brace, quote, colon, comma, start_bracket, end_bracket , number ]; - matcher.vals <== [hit_start_brace, hit_end_brace, hit_quote, hit_colon, hit_comma, hit_start_bracket, hit_end_bracket, hit_number]; + matcher.branches <== [start_brace, end_brace, quote, colon, comma, start_bracket, end_bracket, number ]; + matcher.vals <== [hit_start_brace, hit_end_brace, hit_quote, hit_colon, hit_comma, hit_start_bracket, hit_end_bracket, hit_number]; component numeral_range_check = InRange(8); - numeral_range_check.in <== byte; + numeral_range_check.in <== byte; numeral_range_check.range <== [48, 57]; // ASCII NUMERALS - matcher.case <== (1 - numeral_range_check.out) * byte + numeral_range_check.out * 256; // IF (NOT is_number) THEN byte ELSEIF 256 + matcher.case <== (1 - numeral_range_check.out) * byte + numeral_range_check.out * 256; // IF (NOT is_number) THEN byte ELSE 256 // * get the instruction mask based on current state * - component mask = StateToMask(); - mask.state <== state; + component mask = StateToMask(); + mask.in <== parsing_state; // * multiply the mask array elementwise with the instruction array * - component mulMaskAndOut = ArrayMul(5); - mulMaskAndOut.lhs <== mask.mask; - mulMaskAndOut.rhs <== matcher.out; + component mulMaskAndOut = ArrayMul(7); + mulMaskAndOut.lhs <== mask.out; + 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; + component addToState = ArrayAdd(7); + addToState.lhs <== parsing_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]; + next_pointer <== addToState.out[0]; + for(var i = 0; i<4; i++) { + next_depth[i] <== depth[i] + key_or_value * (i - next_pointer); // todo this is definitely wrong + } + // next_depth[next_pointer] <== addToState.out[1]; + next_parsing_string <== addToState.out[2]; + next_parsing_array <== addToState.out[3]; + next_parsing_object <== addToState.out[4]; + next_parsing_number <== addToState.out[5]; + next_key_or_value <== addToState.out[6]; //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// @@ -136,15 +147,15 @@ template StateUpdate() { //--------------------------------------------------------------------------------------------// //-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_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?) - component isMinusOne = IsEqual(); - isMinusOne.in[0] <== -1; - isMinusOne.in[1] <== next_tree_depth; - isMinusOne.out === 0; + // 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 + // // * 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; //--------------------------------------------------------------------------------------------// } @@ -194,35 +205,36 @@ template Switch(m, n) { 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[5]; - signal output mask[5]; + signal input in[7]; + signal output out[7]; - 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]; - - 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; - - // `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! + signal pushpop <== in[0]; + signal val_or_array <== in[1]; + signal parsing_string <== in[2]; + signal parsing_array <== in[3]; + signal parsing_object <== in[4]; + signal parsing_number <== in[5]; + signal key_or_value <== in[6]; + + // can push or pop the depth stack if we're not parsing a string + out[0] <== (1 - parsing_string); + + // + out[1] <== (1 - parsing_string); + + // `parsing_string` can change: + out[2] <== 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; + // `parsing_array` can change: + out[3] <== (1 - parsing_string); - // `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_object` can change: + out[4] <== (1 - parsing_string); - // `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); + // `parsing_number` can change: + out[5] <== (1 - parsing_string); - // `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; + // `key_or_value` can change: + out[6] <== (1 - parsing_string); } \ No newline at end of file From 5432b1796ff301b354b762c1f8b2385d7a891621 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Sat, 10 Aug 2024 11:09:02 -0400 Subject: [PATCH 34/99] stack hard --- circuits.json | 2 +- circuits/extract.circom | 24 +++++++------- circuits/parser.circom | 70 ++++++++++++++++++++++++++++++----------- json_examples/test.json | 4 +-- 4 files changed, 65 insertions(+), 35 deletions(-) diff --git a/circuits.json b/circuits.json index 5e0c923..e9f5d76 100644 --- a/circuits.json +++ b/circuits.json @@ -3,7 +3,7 @@ "file": "extract", "template": "Extract", "params": [ - 21 + 12 ] }, "test_extract_two_key": { diff --git a/circuits/extract.circom b/circuits/extract.circom index 712e607..dce4b52 100644 --- a/circuits/extract.circom +++ b/circuits/extract.circom @@ -20,7 +20,7 @@ template Extract(DATA_BYTES) { State[0] = StateUpdate(); State[0].byte <== data[0]; State[0].pointer <== 0; - State[0].depth <== [0,0,0,0]; + State[0].stack <== [0,0,0,0]; State[0].parsing_string <== 0; State[0].parsing_array <== 0; State[0].parsing_object <== 0; @@ -28,27 +28,27 @@ template Extract(DATA_BYTES) { State[0].key_or_value <== 0; for(var data_idx = 1; data_idx < DATA_BYTES; data_idx++) { - State[data_idx] = StateUpdate(); + State[data_idx] = StateUpdate(); State[data_idx].byte <== data[data_idx]; - State[data_idx].pointer <== State[data_idx - 1].pointer; - State[data_idx].depth <== State[data_idx - 1].depth; - State[data_idx].parsing_string <== State[data_idx - 1].parsing_string; - State[data_idx].parsing_array <== State[data_idx - 1].parsing_array; - State[data_idx].parsing_object <== State[data_idx - 1].parsing_object; - State[data_idx].parsing_number <== State[data_idx - 1].parsing_number; - State[data_idx].key_or_value <== State[data_idx - 1].key_or_value; + State[data_idx].pointer <== State[data_idx - 1].next_pointer; + State[data_idx].stack <== State[data_idx - 1].next_stack; + State[data_idx].parsing_string <== State[data_idx - 1].next_parsing_string; + State[data_idx].parsing_array <== State[data_idx - 1].next_parsing_array; + State[data_idx].parsing_object <== State[data_idx - 1].next_parsing_object; + State[data_idx].parsing_number <== State[data_idx - 1].next_parsing_number; + State[data_idx].key_or_value <== State[data_idx - 1].next_key_or_value; // Debugging log("State[", data_idx, "].pointer ", "= ", State[data_idx].pointer); for(var i = 0; i<4; i++) { - log("State[", data_idx, "].depth[", i,"] ", "= ", State[data_idx].depth[i]); + log("State[", data_idx, "].stack[", i,"] ", "= ", State[data_idx].stack[i]); } log("State[", data_idx, "].parsing_string", "= ", State[data_idx].parsing_string); log("State[", data_idx, "].parsing_array ", "= ", State[data_idx].parsing_array ); log("State[", data_idx, "].parsing_object", "= ", State[data_idx].parsing_object); log("State[", data_idx, "].parsing_number", "= ", State[data_idx].parsing_number); log("State[", data_idx, "].key_or_value ", "= ", State[data_idx].key_or_value ); - log("-----------------------------------------"); + log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); } // Constrain to have valid JSON (TODO: more is needed) @@ -59,5 +59,5 @@ template Extract(DATA_BYTES) { // log("State[", DATA_BYTES, "].inside_key", "= ", State[DATA_BYTES-1].inside_key); // log("State[", DATA_BYTES, "].parsing_value", "= ", State[DATA_BYTES-1].parsing_value); // log("State[", DATA_BYTES, "].inside_value", "= ", State[DATA_BYTES-1].inside_value); - log("---"); + // log("---"); } \ No newline at end of file diff --git a/circuits/parser.circom b/circuits/parser.circom index c2b22d8..06c47c8 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -66,7 +66,7 @@ template StateUpdate() { signal input byte; signal input pointer; // POINTER -- points to the stack to mark where we currently are inside the JSON. - signal input depth[4]; // STACK -- how deep in a JSON nest we are and what type we are currently inside (e.g., `1` for object, `-1` for array). + signal input stack[4]; // STACK -- how deep in a JSON nest we are and what type we are currently inside (e.g., `1` for object, `-1` for array). signal input parsing_string; signal input parsing_array; signal input parsing_object; @@ -76,7 +76,7 @@ template StateUpdate() { // signal parsing_null; // TODO signal output next_pointer; - signal output next_depth[4]; + signal output next_stack[4]; signal output next_parsing_string; signal output next_parsing_object; signal output next_parsing_array; @@ -121,26 +121,28 @@ template StateUpdate() { addToState.lhs <== parsing_state; addToState.rhs <== mulMaskAndOut.out; // * set the new state * - next_pointer <== addToState.out[0]; - for(var i = 0; i<4; i++) { - next_depth[i] <== depth[i] + key_or_value * (i - next_pointer); // todo this is definitely wrong - } - // next_depth[next_pointer] <== addToState.out[1]; - next_parsing_string <== addToState.out[2]; - next_parsing_array <== addToState.out[3]; - next_parsing_object <== addToState.out[4]; - next_parsing_number <== addToState.out[5]; - next_key_or_value <== addToState.out[6]; + component newStack = RewriteStack(4); + newStack.pointer <== pointer; + newStack.stack <== stack; + newStack.pushpop <== addToState.out[0]; + newStack.obj_or_arr <== addToState.out[1]; + next_pointer <== newStack.next_pointer; + next_stack <== newStack.next_stack; + next_parsing_string <== addToState.out[2]; + next_parsing_array <== addToState.out[3]; + next_parsing_object <== addToState.out[4]; + next_parsing_number <== addToState.out[5]; + next_key_or_value <== addToState.out[6]; //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// - // // 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]); + // DEBUGGING: internal state + // for(var i = 0; i<7; i++) { + // log("------------------------------------------"); + // log(">>>> parsing_state[",i,"]: ", parsing_state[i]); + // log(">>>> mask[",i,"] : ", mask.out[i]); + // log(">>>> command[",i,"] : ", matcher.out[i]); + // log(">>>> addToState[",i,"] : ", addToState.out[i]); // } //--------------------------------------------------------------------------------------------// @@ -237,4 +239,34 @@ template StateToMask() { // `key_or_value` can change: out[6] <== (1 - parsing_string); +} + +template RewriteStack(n) { + signal input pointer; + signal input stack[n]; + signal input pushpop; + signal input obj_or_arr; + + signal output next_pointer; + signal output next_stack[n]; + + next_pointer <== pointer + pushpop; // If pushpop is 0, pointer doesn't change, if -1, decrement, +1 increment + + /* + IDEA: + + We want to look at the old data + - if pushpop is 0, we are going to just return the old stack + - if pushpop is 1, we are going to increment the pointer and write a new value + - if pushpop is -1, we are going to decrement the pointer and delete an old value if it was the same value + */ + + // Indicate which position in the stack should change (if any) + component indicator[n]; + for(var i = 0; i < n; i++) { + indicator[i] = IsZero(); + indicator[i].in <== pointer - i; // Change at pointer or TODO: change at incremented pointer + next_stack[i] <== indicator[i].out * obj_or_arr; + } + } \ No newline at end of file diff --git a/json_examples/test.json b/json_examples/test.json index 31a17d9..7f1fc65 100644 --- a/json_examples/test.json +++ b/json_examples/test.json @@ -1,3 +1 @@ -{ - "key1": "abc" -} \ No newline at end of file +{ "k": "v" } \ No newline at end of file From 0e04f427df699ec60cd6a88cffd4766fc1668cb5 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Sat, 10 Aug 2024 14:00:45 -0400 Subject: [PATCH 35/99] save progress --- circuits/extract.circom | 17 +++++++++++------ circuits/parser.circom | 12 +++++++----- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/circuits/extract.circom b/circuits/extract.circom index dce4b52..3e6900d 100644 --- a/circuits/extract.circom +++ b/circuits/extract.circom @@ -54,10 +54,15 @@ template Extract(DATA_BYTES) { // Constrain to have valid JSON (TODO: more is needed) // State[DATA_BYTES - 1].next_tree_depth === 0; - // log("State[", DATA_BYTES, "].tree_depth", "= ", State[DATA_BYTES-1].tree_depth); - // log("State[", DATA_BYTES, "].parsing_key", "= ", State[DATA_BYTES-1].parsing_key); - // log("State[", DATA_BYTES, "].inside_key", "= ", State[DATA_BYTES-1].inside_key); - // log("State[", DATA_BYTES, "].parsing_value", "= ", State[DATA_BYTES-1].parsing_value); - // log("State[", DATA_BYTES, "].inside_value", "= ", State[DATA_BYTES-1].inside_value); - // log("---"); + // Debugging + log("State[", DATA_BYTES, "].pointer ", "= ", State[DATA_BYTES -1].next_pointer); + for(var i = 0; i<4; i++) { + log("State[", DATA_BYTES, "].stack[", i,"] ", "= ", State[DATA_BYTES -1 ].next_stack[i]); + } + log("State[", DATA_BYTES, "].parsing_string", "= ", State[DATA_BYTES-1].next_parsing_string); + log("State[", DATA_BYTES, "].parsing_array ", "= ", State[DATA_BYTES-1].next_parsing_array ); + log("State[", DATA_BYTES, "].parsing_object", "= ", State[DATA_BYTES-1].next_parsing_object); + log("State[", DATA_BYTES, "].parsing_number", "= ", State[DATA_BYTES-1].next_parsing_number); + log("State[", DATA_BYTES, "].key_or_value ", "= ", State[DATA_BYTES-1].next_key_or_value ); + log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); } \ No newline at end of file diff --git a/circuits/parser.circom b/circuits/parser.circom index 06c47c8..9fca347 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -212,24 +212,24 @@ template StateToMask() { signal output out[7]; signal pushpop <== in[0]; - signal val_or_array <== in[1]; + signal obj_or_array <== in[1]; signal parsing_string <== in[2]; signal parsing_array <== in[3]; signal parsing_object <== in[4]; signal parsing_number <== in[5]; signal key_or_value <== in[6]; - // can push or pop the depth stack if we're not parsing a string + // `pushpop` can change: IF NOT `parsing_string` out[0] <== (1 - parsing_string); - // + // `val_or_array`: IF NOT `parsing_string` out[1] <== (1 - parsing_string); // `parsing_string` can change: out[2] <== 1; // `parsing_array` can change: - out[3] <== (1 - parsing_string); + out[3] <== (1 - parsing_string) * parsing_array; // `parsing_object` can change: out[4] <== (1 - parsing_string); @@ -263,10 +263,12 @@ template RewriteStack(n) { // Indicate which position in the stack should change (if any) component indicator[n]; + signal unchanged_stack[n]; for(var i = 0; i < n; i++) { indicator[i] = IsZero(); indicator[i].in <== pointer - i; // Change at pointer or TODO: change at incremented pointer - next_stack[i] <== indicator[i].out * obj_or_arr; + unchanged_stack[i] <== (1-indicator[i].out) * stack[i]; + next_stack[i] <== unchanged_stack[i] + indicator[i].out * obj_or_arr; } } \ No newline at end of file From ded3c36a96e870e002f14a0bc7028215583746cc Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Sat, 10 Aug 2024 18:29:07 -0400 Subject: [PATCH 36/99] save wip --- .gitignore | 3 + circuits/parser.circom | 41 ++++-- circuits/test/parser.test.ts | 251 +++++++++++++++++++---------------- 3 files changed, 165 insertions(+), 130 deletions(-) diff --git a/.gitignore b/.gitignore index 40d4c9c..bbed9c8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,11 @@ node_modules/* +# Circomkit generated build/* ptau/* circuits/test/*.circom circuits/main/* + +# Rust generated inputs/**/*.json \ No newline at end of file diff --git a/circuits/parser.circom b/circuits/parser.circom index 9fca347..b1739f9 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -87,15 +87,15 @@ template StateUpdate() { var pushpop = 0; var obj_or_arr = 0; var parsing_state[7] = [pushpop, obj_or_arr, parsing_string, parsing_array, parsing_object, parsing_number, key_or_value]; - var do_nothing[7] = [0, 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[7] = [1, 1, 0, -1, 1, 0, 0]; // Command returned by switch if we hit a start brace `{` - var hit_end_brace[7] = [-1, 1, 0, 0, -1, 0, 0]; // Command returned by switch if we hit a end brace `}` - var hit_start_bracket[7] = [1, -1, 0, 1, -1, 0, 0]; // TODO: Might want `key_or_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) - var hit_end_bracket[7] = [-1, -1, 0, -1, 0, 0, 0]; // Command returned by switch if we hit a start bracket `]` - var hit_quote[7] = [0, 0, 1, 1, 1, 0, 0]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` - var hit_colon[7] = [0, 0, 0, 0, 0, 0, 1]; // Command returned by switch if we hit a colon `:` - var hit_comma[7] = [0, 0, 0, 0, 0, -1, 0]; // Command returned by switch if we hit a comma `,` - var hit_number[7] = [0, 0, 0, 0, 0, 1, 0]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) + var do_nothing[7] = [0, 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[7] = [1, 1, 0, -1, 1, 0, 0]; // Command returned by switch if we hit a start brace `{` + var hit_end_brace[7] = [-1, 1, 0, 0, -1, 0, 0]; // Command returned by switch if we hit a end brace `}` + var hit_start_bracket[7] = [1, -1, 0, 1, -1, 0, 0]; // TODO: Might want `key_or_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) + var hit_end_bracket[7] = [-1, -1, 0, -1, 0, 0, 0]; // Command returned by switch if we hit a start bracket `]` + var hit_quote[7] = [0, 0, 1, 0, 0, 0, 1]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` + var hit_colon[7] = [0, 0, 0, 0, 0, 0, 1]; // Command returned by switch if we hit a colon `:` + var hit_comma[7] = [0, 0, 0, 0, 0, -1, 0]; // Command returned by switch if we hit a comma `,` + var hit_number[7] = [0, 0, 0, 0, 0, 1, 0]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// @@ -241,7 +241,10 @@ template StateToMask() { out[6] <== (1 - parsing_string); } +// TODO: IMPORTANT NOTE, THE STACK IS CONSTRAINED TO 2**8 so the LessThan and GreaterThan work (could be changed) +// TODO: Might be good to change value before increment pointer AND decrement pointer before changing value template RewriteStack(n) { + assert(n < 2**8); signal input pointer; signal input stack[n]; signal input pushpop; @@ -250,8 +253,6 @@ template RewriteStack(n) { signal output next_pointer; signal output next_stack[n]; - next_pointer <== pointer + pushpop; // If pushpop is 0, pointer doesn't change, if -1, decrement, +1 increment - /* IDEA: @@ -264,11 +265,23 @@ template RewriteStack(n) { // Indicate which position in the stack should change (if any) component indicator[n]; signal unchanged_stack[n]; + var accrue_pointer; for(var i = 0; i < n; i++) { - indicator[i] = IsZero(); - indicator[i].in <== pointer - i; // Change at pointer or TODO: change at incremented pointer + indicator[i] = IsZero(); + indicator[i].in <== pointer - i; // Change at pointer or TODO: change at incremented pointer unchanged_stack[i] <== (1-indicator[i].out) * stack[i]; - next_stack[i] <== unchanged_stack[i] + indicator[i].out * obj_or_arr; + next_stack[i] <== unchanged_stack[i] + indicator[i].out * obj_or_arr; } + + next_pointer <== pointer + pushpop; // If pushpop is 0, pointer doesn't change, if -1, decrement, +1 increment + + component isOverflow = GreaterThan(8); + isOverflow.in[0] <== next_pointer; + isOverflow.in[1] <== n; + isOverflow.out === 0; + component isUnderflow = LessThan(8); + isUnderflow.in[0] <== next_pointer; + isUnderflow.in[1] <== 0; + isUnderflow.out === 0; } \ No newline at end of file diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index 3e6fbf2..321a182 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_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"] + ["byte", "pointer", "stack", "parsing_string", "parsing_array", "parsing_object", "parsing_number", "key_or_value"], + ["next_pointer", "next_stack", "next_parsing_string", "next_parsing_array", "next_parsing_object", "next_parsing_number", "next_key_or_value"] >; function generatePassCase(input: any, expected: any, desc: string) { @@ -122,18 +122,22 @@ describe("parser", () => { let init = { byte: 0, - tree_depth: 0, - parsing_key: 0, - inside_key: 0, - parsing_value: 0, - inside_value: 0, + pointer: 0, + stack: [0, 0, 0, 0], + parsing_string: 0, + parsing_array: 0, + parsing_object: 0, + parsing_number: 0, + key_or_value: 0, }; let out = { - next_tree_depth: init.tree_depth, - next_parsing_key: init.parsing_key, - next_inside_key: init.inside_key, - next_parsing_value: init.parsing_value, - next_inside_value: init.inside_value, + next_pointer: init.pointer, + next_stack: init.stack, + next_parsing_string: init.parsing_string, + next_parsing_array: init.parsing_array, + next_parsing_object: init.parsing_object, + next_parsing_number: init.parsing_number, + next_key_or_value: init.key_or_value, }; // Test 1: init setup -> `do_nothing` byte @@ -143,127 +147,142 @@ describe("parser", () => { 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; - read_start_brace_out.next_parsing_key = 1; + read_start_brace_out.next_pointer = 1; + read_start_brace_out.next_stack = [1, 0, 0, 0]; + read_start_brace_out.next_parsing_object = 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, ">>>> `}` read --> (FAIL! NEGATIVE TREE DEPTH!)"); - - // 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_key = 1; - in_tree_find_key.byte = quote; - let in_tree_find_key_out = { ...out }; - 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"); - + generateFailCase(read_end_brace, ">>>> `}` read --> (stack underflow)"); + + // Test 4: after just reading { then read a quote + let in_object_find_key = { ...init }; + in_object_find_key.pointer = read_start_brace_out.next_pointer; + in_object_find_key.stack = read_start_brace_out.next_stack; + in_object_find_key.parsing_object = read_start_brace_out.next_parsing_object; + in_object_find_key.byte = quote; + let in_object_find_key_out = { ...out }; + in_object_find_key_out.next_pointer = 1; + in_object_find_key_out.next_stack = [1, 0, 0, 0]; + in_object_find_key_out.next_parsing_string = 1; + in_object_find_key_out.next_key_or_value = 1; + in_object_find_key_out.next_parsing_object = 1; + generatePassCase(in_object_find_key, in_object_find_key_out, ">>>> `\"` read"); + + // TODO: THESE SHOULD ACTUALLY SAY WE ARE KEY OR VALUE // Test 5: `tree_depth == 1` AND `inside_key ==1` setup -> ` ` is read let in_key = { ...init }; - in_key.tree_depth = 1; - in_key.inside_key = 1; + in_key.pointer = read_start_brace_out.next_pointer; + in_key.stack = read_start_brace_out.next_stack; + in_key.parsing_object = read_start_brace_out.next_parsing_object; + in_key.parsing_string = 1 in_key.byte = space; let in_key_out = { ...out }; - in_key_out.next_inside_key = 1; - in_key_out.next_tree_depth = 1; + in_key_out.next_pointer = 1; + in_key_out.next_stack = [1, 0, 0, 0]; + in_key_out.next_parsing_string = 1; + in_key_out.next_parsing_object = 1; generatePassCase(in_key, in_key_out, ">>>> ` ` 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; + in_key_to_exit.pointer = read_start_brace_out.next_pointer; + in_key_to_exit.stack = read_start_brace_out.next_stack; + in_key_to_exit.parsing_object = read_start_brace_out.next_parsing_object; + in_key_to_exit.parsing_string = 1 in_key_to_exit.byte = quote; let in_key_to_exit_out = { ...out }; - in_key_to_exit_out.next_tree_depth = 1; + in_key_out.next_pointer = 1; + in_key_out.next_stack = [1, 0, 0, 0]; + in_key_out.next_parsing_object = 1; 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_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_value = 1; - generatePassCase(parsed_key_wait_to_parse_value, parsed_key_wait_to_parse_value_out, ">>>> `:` 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_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_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.inside_value = 1; - in_value.byte = space; - let in_value_out = { ...out }; - in_value_out.next_tree_depth = 1; - in_value_out.next_inside_value = 1; - 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_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_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.byte = space; - let in_end_of_kv_out = { ...out }; - in_end_of_kv_out.next_tree_depth = 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 - 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_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_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_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_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_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_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_key = 1; - generatePassCase(end_of_key_to_inner_object, end_of_key_to_inner_object_out, ">>>> `{` is read"); + + //// BREAK HERE AND RENAME AND ADJUST + // // 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_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_value = 1; + // generatePassCase(parsed_key_wait_to_parse_value, parsed_key_wait_to_parse_value_out, ">>>> `:` 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_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_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.inside_value = 1; + // in_value.byte = space; + // let in_value_out = { ...out }; + // in_value_out.next_tree_depth = 1; + // in_value_out.next_inside_value = 1; + // 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_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_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.byte = space; + // let in_end_of_kv_out = { ...out }; + // in_end_of_kv_out.next_tree_depth = 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 + // 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_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_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_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_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_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_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_key = 1; + // generatePassCase(end_of_key_to_inner_object, end_of_key_to_inner_object_out, ">>>> `{` is read"); }); }); From a11efb8289fc5639cde462ea81670dff1fd14a11 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Sat, 10 Aug 2024 18:59:22 -0400 Subject: [PATCH 37/99] getting through tests --- circuits/parser.circom | 18 +++++++++--------- circuits/test/parser.test.ts | 10 ++++++---- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index b1739f9..dfd154c 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -137,13 +137,13 @@ template StateUpdate() { //--------------------------------------------------------------------------------------------// // DEBUGGING: internal state - // for(var i = 0; i<7; i++) { - // log("------------------------------------------"); - // log(">>>> parsing_state[",i,"]: ", parsing_state[i]); - // log(">>>> mask[",i,"] : ", mask.out[i]); - // log(">>>> command[",i,"] : ", matcher.out[i]); - // log(">>>> addToState[",i,"] : ", addToState.out[i]); - // } + for(var i = 0; i<7; i++) { + log("------------------------------------------"); + log(">>>> parsing_state[",i,"]: ", parsing_state[i]); + log(">>>> mask[",i,"] : ", mask.out[i]); + log(">>>> command[",i,"] : ", matcher.out[i]); + log(">>>> addToState[",i,"] : ", addToState.out[i]); + } //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// @@ -226,7 +226,7 @@ template StateToMask() { out[1] <== (1 - parsing_string); // `parsing_string` can change: - out[2] <== 1; + out[2] <== 1 - 2 * parsing_string; // `parsing_array` can change: out[3] <== (1 - parsing_string) * parsing_array; @@ -238,7 +238,7 @@ template StateToMask() { out[5] <== (1 - parsing_string); // `key_or_value` can change: - out[6] <== (1 - parsing_string); + out[6] <== (1 - parsing_string) - 2 * key_or_value; } // TODO: IMPORTANT NOTE, THE STACK IS CONSTRAINED TO 2**8 so the LessThan and GreaterThan work (could be changed) diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index 321a182..9704293 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -177,13 +177,15 @@ describe("parser", () => { in_key.pointer = read_start_brace_out.next_pointer; in_key.stack = read_start_brace_out.next_stack; in_key.parsing_object = read_start_brace_out.next_parsing_object; - in_key.parsing_string = 1 + in_key.parsing_string = 1; + in_key.key_or_value = 1; in_key.byte = space; let in_key_out = { ...out }; in_key_out.next_pointer = 1; in_key_out.next_stack = [1, 0, 0, 0]; in_key_out.next_parsing_string = 1; in_key_out.next_parsing_object = 1; + in_key_out.next_key_or_value = 1; generatePassCase(in_key, in_key_out, ">>>> ` ` read"); // Test 6: `tree_depth == 1` AND `inside_key == 1 AND `parsing_key == 0` setup -> `"` is read @@ -194,9 +196,9 @@ describe("parser", () => { in_key_to_exit.parsing_string = 1 in_key_to_exit.byte = quote; let in_key_to_exit_out = { ...out }; - in_key_out.next_pointer = 1; - in_key_out.next_stack = [1, 0, 0, 0]; - in_key_out.next_parsing_object = 1; + in_key_to_exit_out.next_pointer = 1; + in_key_to_exit_out.next_stack = [1, 0, 0, 0]; + in_key_to_exit_out.next_parsing_object = 1; generatePassCase(in_key_to_exit, in_key_to_exit_out, "`\"` read"); From ce1bde959b6502812411404b343b4b9caeec3734 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Sun, 11 Aug 2024 14:59:36 -0400 Subject: [PATCH 38/99] wip: stack --- circuits/parser.circom | 74 +++++++++++++++----- circuits/test/parser.test.ts | 129 +++++++++++++++++++++++------------ 2 files changed, 144 insertions(+), 59 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index dfd154c..66b0dd4 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -33,6 +33,8 @@ Array. Object. Whitespace. Null. + +TODO: Might not need the "parsing object" and "parsing array" as these are kinda captured by the stack? */ template StateUpdate() { //--------------------------------------------------------------------------------------------// @@ -136,14 +138,25 @@ template StateUpdate() { //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// - // DEBUGGING: internal state - for(var i = 0; i<7; i++) { - log("------------------------------------------"); - log(">>>> parsing_state[",i,"]: ", parsing_state[i]); - log(">>>> mask[",i,"] : ", mask.out[i]); - log(">>>> command[",i,"] : ", matcher.out[i]); - log(">>>> addToState[",i,"] : ", addToState.out[i]); + // // DEBUGGING: internal state + // for(var i = 0; i<7; i++) { + // log("------------------------------------------"); + // log(">>>> parsing_state[",i,"]: ", parsing_state[i]); + // log(">>>> mask[",i,"] : ", mask.out[i]); + // log(">>>> command[",i,"] : ", matcher.out[i]); + // log(">>>> addToState[",i,"] : ", addToState.out[i]); + // } + // Debugging + log("next_pointer ", "= ", next_pointer); + for(var i = 0; i<4; i++) { + log("next_stack[", i,"] ", "= ", next_stack[i]); } + log("next_parsing_string", "= ", next_parsing_string); + log("next_parsing_array ", "= ", next_parsing_array ); + log("next_parsing_object", "= ", next_parsing_object); + log("next_parsing_number", "= ", next_parsing_number); + log("next_key_or_value ", "= ", next_key_or_value ); + log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// @@ -262,19 +275,48 @@ template RewriteStack(n) { - if pushpop is -1, we are going to decrement the pointer and delete an old value if it was the same value */ +next_pointer <== pointer + pushpop; // If pushpop is 0, pointer doesn't change, if -1, decrement, +1 increment + // Indicate which position in the stack should change (if any) - component indicator[n]; - signal unchanged_stack[n]; - var accrue_pointer; + component isPop[n]; + component isPush[n]; + component isNoOp[n]; + component indicatorPush[n]; + component indicatorPop[n]; + signal isPopAt[n]; + signal isPushAt[n]; + signal isNoOpAt[n]; + + // EXAMPLE: + // `pointer == 1`, `stack == [1, 0, 0, 0]` + // >>>> `pushpop == -1` + // This means we need to decrement pointer, then pop from the stack + // This means we take `next_pointer` then set this to zero + + //TODO: Note, we are not effectively using the stack, we could actually pop and read these values to save to inner state signals + // I.e., the `in_object` and `in_array` or whatever for(var i = 0; i < n; i++) { - indicator[i] = IsZero(); - indicator[i].in <== pointer - i; // Change at pointer or TODO: change at incremented pointer - unchanged_stack[i] <== (1-indicator[i].out) * stack[i]; - next_stack[i] <== unchanged_stack[i] + indicator[i].out * obj_or_arr; + isPop[i] = IsZero(); + isPop[i].in <== pushpop + 1; // TRUE if we are popping + + isPush[i] = IsZero(); + isPush[i].in <== pushpop - 1; // TRUE if we are pushing + + indicatorPush[i] = IsZero(); + indicatorPush[i].in <== pointer - i; // 1 in the position of the current pointer + + indicatorPop[i] = IsZero(); + indicatorPop[i].in <== next_pointer - i; // 1 in the position of the current pointer + + isPopAt[i] <== indicatorPop[i].out * isPop[i].out; // Index to pop from + isPushAt[i] <== indicatorPush[i].out * isPush[i].out; // Index to push to + + // Could use GreaterEqThan to set any position in the stack at next_pointer or above 0? + + // Leave the stack alone except for where we indicate change + next_stack[i] <== stack[i] + (isPushAt[i] - isPopAt[i]) * obj_or_arr; } - next_pointer <== pointer + pushpop; // If pushpop is 0, pointer doesn't change, if -1, decrement, +1 increment - component isOverflow = GreaterThan(8); isOverflow.in[0] <== next_pointer; isOverflow.in[1] <== n; diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index 9704293..20cb8ea 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -157,52 +157,95 @@ describe("parser", () => { read_end_brace.byte = end_brace; generateFailCase(read_end_brace, ">>>> `}` read --> (stack underflow)"); - // Test 4: after just reading { then read a quote - let in_object_find_key = { ...init }; - in_object_find_key.pointer = read_start_brace_out.next_pointer; - in_object_find_key.stack = read_start_brace_out.next_stack; - in_object_find_key.parsing_object = read_start_brace_out.next_parsing_object; - in_object_find_key.byte = quote; - let in_object_find_key_out = { ...out }; - in_object_find_key_out.next_pointer = 1; - in_object_find_key_out.next_stack = [1, 0, 0, 0]; - in_object_find_key_out.next_parsing_string = 1; - in_object_find_key_out.next_key_or_value = 1; - in_object_find_key_out.next_parsing_object = 1; - generatePassCase(in_object_find_key, in_object_find_key_out, ">>>> `\"` read"); - - // TODO: THESE SHOULD ACTUALLY SAY WE ARE KEY OR VALUE - // Test 5: `tree_depth == 1` AND `inside_key ==1` setup -> ` ` is read - let in_key = { ...init }; - in_key.pointer = read_start_brace_out.next_pointer; - in_key.stack = read_start_brace_out.next_stack; - in_key.parsing_object = read_start_brace_out.next_parsing_object; - in_key.parsing_string = 1; - in_key.key_or_value = 1; - in_key.byte = space; - let in_key_out = { ...out }; - in_key_out.next_pointer = 1; - in_key_out.next_stack = [1, 0, 0, 0]; - in_key_out.next_parsing_string = 1; - in_key_out.next_parsing_object = 1; - in_key_out.next_key_or_value = 1; - generatePassCase(in_key, in_key_out, ">>>> ` ` 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.pointer = read_start_brace_out.next_pointer; - in_key_to_exit.stack = read_start_brace_out.next_stack; - in_key_to_exit.parsing_object = read_start_brace_out.next_parsing_object; - in_key_to_exit.parsing_string = 1 - in_key_to_exit.byte = quote; - let in_key_to_exit_out = { ...out }; - in_key_to_exit_out.next_pointer = 1; - in_key_to_exit_out.next_stack = [1, 0, 0, 0]; - in_key_to_exit_out.next_parsing_object = 1; - generatePassCase(in_key_to_exit, in_key_to_exit_out, "`\"` read"); + // // Test 4: after just reading { then read a quote + // let in_object_find_key = { ...init }; + // in_object_find_key.pointer = read_start_brace_out.next_pointer; + // in_object_find_key.stack = read_start_brace_out.next_stack; + // in_object_find_key.parsing_object = read_start_brace_out.next_parsing_object; + // in_object_find_key.byte = quote; + // let in_object_find_key_out = { ...out }; + // in_object_find_key_out.next_pointer = 1; + // in_object_find_key_out.next_stack = [1, 0, 0, 0]; + // in_object_find_key_out.next_parsing_string = 1; + // in_object_find_key_out.next_key_or_value = 1; + // in_object_find_key_out.next_parsing_object = 1; + // generatePassCase(in_object_find_key, in_object_find_key_out, ">>>> `\"` read"); + + // // TODO: THESE SHOULD ACTUALLY SAY WE ARE KEY OR VALUE + // // Test 5: `tree_depth == 1` AND `inside_key ==1` setup -> ` ` is read + // let in_key = { ...init }; + // in_key.pointer = read_start_brace_out.next_pointer; + // in_key.stack = read_start_brace_out.next_stack; + // in_key.parsing_object = read_start_brace_out.next_parsing_object; + // in_key.parsing_string = 1; + // in_key.key_or_value = 1; + // in_key.byte = space; + // let in_key_out = { ...out }; + // in_key_out.next_pointer = 1; + // in_key_out.next_stack = [1, 0, 0, 0]; + // in_key_out.next_parsing_string = 1; + // in_key_out.next_parsing_object = 1; + // in_key_out.next_key_or_value = 1; + // generatePassCase(in_key, in_key_out, ">>>> ` ` 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.pointer = read_start_brace_out.next_pointer; + // in_key_to_exit.stack = read_start_brace_out.next_stack; + // in_key_to_exit.parsing_object = read_start_brace_out.next_parsing_object; + // in_key_to_exit.parsing_string = 1 + // in_key_to_exit.byte = quote; + // let in_key_to_exit_out = { ...out }; + // in_key_to_exit_out.next_pointer = 1; + // in_key_to_exit_out.next_stack = [1, 0, 0, 0]; + // in_key_to_exit_out.next_parsing_object = 1; + // generatePassCase(in_key_to_exit, in_key_to_exit_out, "`\"` read"); + + // Test 7: Stack Management + // init: read `{`, read another `{` + // expect: pointer --> 2 + // stack --> [1,1,0,0] + let in_object = { ...init }; + in_object.pointer = read_start_brace_out.next_pointer; + in_object.stack = read_start_brace_out.next_stack; + in_object.parsing_object = read_start_brace_out.next_parsing_object; + in_object.byte = start_brace; + let in_object_out = { ...out }; + in_object_out.next_pointer = 2; + in_object_out.next_stack = [1, 1, 0, 0]; + in_object_out.next_parsing_object = 2; + generatePassCase(in_object, in_object_out, ">>>> `\"` read"); + + // Test 8: Stack Management + // init: read `{` then read`}` + // expect: pointer-- > 0 + // stack-- > [0, 0, 0, 0] + let in_object_to_leave = { ...init }; + in_object_to_leave.pointer = read_start_brace_out.next_pointer; + in_object_to_leave.stack = read_start_brace_out.next_stack; + in_object_to_leave.parsing_object = read_start_brace_out.next_parsing_object; + in_object_to_leave.byte = end_brace; + let in_object_to_leave_out = { ...out }; + in_object_to_leave_out.next_pointer = 0; + in_object_to_leave_out.next_stack = [0, 0, 0, 0]; + in_object_to_leave_out.next_parsing_object = 0; + generatePassCase(in_object_to_leave, in_object_to_leave_out, ">>>> `\"` read"); + + // // Test 9: Stack Management + // // init: read `{`, then read `[` + // // expect: pointer --> 2 + // // stack --> [1,-1,0,0] + // in_object.byte = start_bracket; + // in_object_out.next_pointer = 2; + // in_object_out.next_stack = [1, -1, 0, 0]; + // in_object_out.next_parsing_object = 1; + // in_object_out.next_parsing_array = 1; + // generatePassCase(in_object, in_object_out, ">>>> `\"` read"); //// BREAK HERE AND RENAME AND ADJUST + /// USE CLEAR PREVIOUS SETUPS SO THIS IS EASIER TO PARSE VVVVVVVVVVVV + // // 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; From 1e4979d638fd0b18e4496f5d27d4a2c8212291d1 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Sun, 11 Aug 2024 15:12:31 -0400 Subject: [PATCH 39/99] big ints killing me --- circuits/parser.circom | 2 -- circuits/test/parser.test.ts | 24 ++++++++++++++---------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 66b0dd4..cbb9c33 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -280,12 +280,10 @@ next_pointer <== pointer + pushpop; // If pushpop is 0, pointer doesn't change, // Indicate which position in the stack should change (if any) component isPop[n]; component isPush[n]; - component isNoOp[n]; component indicatorPush[n]; component indicatorPop[n]; signal isPopAt[n]; signal isPushAt[n]; - signal isNoOpAt[n]; // EXAMPLE: // `pointer == 1`, `stack == [1, 0, 0, 0]` diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index 20cb8ea..701197d 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -231,16 +231,20 @@ describe("parser", () => { in_object_to_leave_out.next_parsing_object = 0; generatePassCase(in_object_to_leave, in_object_to_leave_out, ">>>> `\"` read"); - // // Test 9: Stack Management - // // init: read `{`, then read `[` - // // expect: pointer --> 2 - // // stack --> [1,-1,0,0] - // in_object.byte = start_bracket; - // in_object_out.next_pointer = 2; - // in_object_out.next_stack = [1, -1, 0, 0]; - // in_object_out.next_parsing_object = 1; - // in_object_out.next_parsing_array = 1; - // generatePassCase(in_object, in_object_out, ">>>> `\"` read"); + // Test 9: Stack Management + // init: read `{`, then read `[` + // expect: pointer --> 2 + // stack --> [1,-1,0,0] + in_object.byte = start_bracket; + in_object_out.next_pointer = 2; + in_object_out.next_stack = + [1, + 21888242871839275222246405745257275088548364400416034343698204186575808495616, + 0, + 0]; + in_object_out.next_parsing_object = 1; + in_object_out.next_parsing_array = 1; + generatePassCase(in_object, in_object_out, ">>>> `\"` read"); //// BREAK HERE AND RENAME AND ADJUST From 226d691a93da4565884684910d9d10f6a0c06069 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Sun, 11 Aug 2024 15:30:18 -0400 Subject: [PATCH 40/99] use strings for tests --- circuits/test/parser.test.ts | 67 +++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index 701197d..e5a3db4 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -60,11 +60,11 @@ describe("parser", () => { //--------------------------------------------------------------------------------------------// //-Delimeters---------------------------------------------------------------------------------// // - ASCII char: `{` - const start_brace = 123; + const start_brace = "123"; // - ASCII char: `}` - const end_brace = 125; + const end_brace = "125"; // - ASCII char `[` - const start_bracket = 91; + const start_bracket = "91"; // - ASCII char `]` const end_bracket = 93; // - ASCII char `"` @@ -121,14 +121,14 @@ describe("parser", () => { }); let init = { - byte: 0, - pointer: 0, - stack: [0, 0, 0, 0], - parsing_string: 0, - parsing_array: 0, - parsing_object: 0, - parsing_number: 0, - key_or_value: 0, + byte: "0", + pointer: "0", + stack: ["0", "0", "0", "0"], + parsing_string: "0", + parsing_array: "0", + parsing_object: "0", + parsing_number: "0", + key_or_value: "0", }; let out = { next_pointer: init.pointer, @@ -147,9 +147,9 @@ describe("parser", () => { let read_start_brace = { ...init }; read_start_brace.byte = start_brace; let read_start_brace_out = { ...out }; - read_start_brace_out.next_pointer = 1; - read_start_brace_out.next_stack = [1, 0, 0, 0]; - read_start_brace_out.next_parsing_object = 1; + read_start_brace_out.next_pointer = "1"; + read_start_brace_out.next_stack = ["1", "0", "0", "0"]; + read_start_brace_out.next_parsing_object = "1"; generatePassCase(read_start_brace, read_start_brace_out, ">>>> `{` read"); // Test 3: init setup -> `}` is read (should be INVALID) @@ -211,10 +211,10 @@ describe("parser", () => { in_object.parsing_object = read_start_brace_out.next_parsing_object; in_object.byte = start_brace; let in_object_out = { ...out }; - in_object_out.next_pointer = 2; - in_object_out.next_stack = [1, 1, 0, 0]; - in_object_out.next_parsing_object = 2; - generatePassCase(in_object, in_object_out, ">>>> `\"` read"); + in_object_out.next_pointer = "2"; + in_object_out.next_stack = ["1", "1", "0", "0"]; + in_object_out.next_parsing_object = "2"; + generatePassCase(in_object, in_object_out, ">>>> `{` read"); // Test 8: Stack Management // init: read `{` then read`}` @@ -226,25 +226,30 @@ describe("parser", () => { in_object_to_leave.parsing_object = read_start_brace_out.next_parsing_object; in_object_to_leave.byte = end_brace; let in_object_to_leave_out = { ...out }; - in_object_to_leave_out.next_pointer = 0; - in_object_to_leave_out.next_stack = [0, 0, 0, 0]; - in_object_to_leave_out.next_parsing_object = 0; + in_object_to_leave_out.next_pointer = "0"; + in_object_to_leave_out.next_stack = ["0", "0", "0", "0"]; + in_object_to_leave_out.next_parsing_object = "0"; generatePassCase(in_object_to_leave, in_object_to_leave_out, ">>>> `\"` read"); // Test 9: Stack Management // init: read `{`, then read `[` // expect: pointer --> 2 // stack --> [1,-1,0,0] - in_object.byte = start_bracket; - in_object_out.next_pointer = 2; - in_object_out.next_stack = - [1, - 21888242871839275222246405745257275088548364400416034343698204186575808495616, - 0, - 0]; - in_object_out.next_parsing_object = 1; - in_object_out.next_parsing_array = 1; - generatePassCase(in_object, in_object_out, ">>>> `\"` read"); + let in_object_to_read_start_bracket = { ...init }; + in_object_to_read_start_bracket.byte = start_bracket; + in_object_to_read_start_bracket.pointer = "1"; + in_object_to_read_start_bracket.stack = ["1", "0", "0", "0"]; + in_object_to_read_start_bracket.parsing_object = "1"; + let in_object_to_read_start_bracket_out = { ...out }; + in_object_to_read_start_bracket_out.next_pointer = "2"; + in_object_to_read_start_bracket_out.next_stack = + ["1", + "21888242871839275222246405745257275088548364400416034343698204186575808495616", + "0", + "0"]; + in_object_to_read_start_bracket_out.next_parsing_object = "1"; + in_object_to_read_start_bracket_out.next_parsing_array = "1"; + generatePassCase(in_object_to_read_start_bracket, in_object_to_read_start_bracket_out, ">>>> `\"` read"); //// BREAK HERE AND RENAME AND ADJUST From 8126615db1b1edab3b70f0b332d2e382481ddbe9 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Sun, 11 Aug 2024 15:33:54 -0400 Subject: [PATCH 41/99] reduce and fix some tests! --- circuits/parser.circom | 58 +++++++++++++----------------------- circuits/test/parser.test.ts | 16 ++-------- 2 files changed, 23 insertions(+), 51 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index cbb9c33..8c8c517 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -70,8 +70,6 @@ template StateUpdate() { signal input pointer; // POINTER -- points to the stack to mark where we currently are inside the JSON. signal input stack[4]; // STACK -- how deep in a JSON nest we are and what type we are currently inside (e.g., `1` for object, `-1` for array). signal input parsing_string; - signal input parsing_array; - signal input parsing_object; signal input parsing_number; signal input key_or_value; // BIT_FLAG-- whether we are in a key or a value // signal parsing_boolean; @@ -80,30 +78,28 @@ template StateUpdate() { signal output next_pointer; signal output next_stack[4]; signal output next_parsing_string; - signal output next_parsing_object; - signal output next_parsing_array; signal output next_parsing_number; signal output next_key_or_value; //--------------------------------------------------------------------------------------------// //-Instructions for ASCII---------------------------------------------------------------------// var pushpop = 0; var obj_or_arr = 0; - var parsing_state[7] = [pushpop, obj_or_arr, parsing_string, parsing_array, parsing_object, parsing_number, key_or_value]; - var do_nothing[7] = [0, 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[7] = [1, 1, 0, -1, 1, 0, 0]; // Command returned by switch if we hit a start brace `{` - var hit_end_brace[7] = [-1, 1, 0, 0, -1, 0, 0]; // Command returned by switch if we hit a end brace `}` - var hit_start_bracket[7] = [1, -1, 0, 1, -1, 0, 0]; // TODO: Might want `key_or_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) - var hit_end_bracket[7] = [-1, -1, 0, -1, 0, 0, 0]; // Command returned by switch if we hit a start bracket `]` - var hit_quote[7] = [0, 0, 1, 0, 0, 0, 1]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` - var hit_colon[7] = [0, 0, 0, 0, 0, 0, 1]; // Command returned by switch if we hit a colon `:` - var hit_comma[7] = [0, 0, 0, 0, 0, -1, 0]; // Command returned by switch if we hit a comma `,` - var hit_number[7] = [0, 0, 0, 0, 0, 1, 0]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) + var parsing_state[5] = [pushpop, obj_or_arr, parsing_string, parsing_number, key_or_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, 0, 0]; // Command returned by switch if we hit a start brace `{` + var hit_end_brace[5] = [-1, 1, 0, 0, 0]; // Command returned by switch if we hit a end brace `}` + var hit_start_bracket[5] = [1, -1, 0, 0, 0]; // TODO: Might want `key_or_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) + var hit_end_bracket[5] = [-1, -1, 0, 0, 0]; // Command returned by switch if we hit a start bracket `]` + var hit_quote[5] = [0, 0, 1, 0, 1]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` + var hit_colon[5] = [0, 0, 0, 0, 1]; // Command returned by switch if we hit a colon `:` + var hit_comma[5] = [0, 0, 0, -1, 0]; // Command returned by switch if we hit a comma `,` + var hit_number[5] = [0, 0, 0, 1, 0]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// //-State machine updating---------------------------------------------------------------------// // * yield instruction based on what byte we read * - component matcher = Switch(8, 7); + component matcher = Switch(8, 5); var number = 256; // Number beyond a byte to represent an ASCII numeral matcher.branches <== [start_brace, end_brace, quote, colon, comma, start_bracket, end_bracket, number ]; matcher.vals <== [hit_start_brace, hit_end_brace, hit_quote, hit_colon, hit_comma, hit_start_bracket, hit_end_bracket, hit_number]; @@ -115,11 +111,11 @@ template StateUpdate() { component mask = StateToMask(); mask.in <== parsing_state; // * multiply the mask array elementwise with the instruction array * - component mulMaskAndOut = ArrayMul(7); + component mulMaskAndOut = ArrayMul(5); mulMaskAndOut.lhs <== mask.out; mulMaskAndOut.rhs <== matcher.out; // * add the masked instruction to the state to get new state * - component addToState = ArrayAdd(7); + component addToState = ArrayAdd(5); addToState.lhs <== parsing_state; addToState.rhs <== mulMaskAndOut.out; // * set the new state * @@ -131,10 +127,8 @@ template StateUpdate() { next_pointer <== newStack.next_pointer; next_stack <== newStack.next_stack; next_parsing_string <== addToState.out[2]; - next_parsing_array <== addToState.out[3]; - next_parsing_object <== addToState.out[4]; - next_parsing_number <== addToState.out[5]; - next_key_or_value <== addToState.out[6]; + next_parsing_number <== addToState.out[3]; + next_key_or_value <== addToState.out[4]; //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// @@ -152,8 +146,6 @@ template StateUpdate() { log("next_stack[", i,"] ", "= ", next_stack[i]); } log("next_parsing_string", "= ", next_parsing_string); - log("next_parsing_array ", "= ", next_parsing_array ); - log("next_parsing_object", "= ", next_parsing_object); log("next_parsing_number", "= ", next_parsing_number); log("next_key_or_value ", "= ", next_key_or_value ); log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); @@ -221,16 +213,14 @@ template Switch(m, n) { } template StateToMask() { - signal input in[7]; - signal output out[7]; + signal input in[5]; + signal output out[5]; signal pushpop <== in[0]; signal obj_or_array <== in[1]; signal parsing_string <== in[2]; - signal parsing_array <== in[3]; - signal parsing_object <== in[4]; - signal parsing_number <== in[5]; - signal key_or_value <== in[6]; + signal parsing_number <== in[3]; + signal key_or_value <== in[4]; // `pushpop` can change: IF NOT `parsing_string` out[0] <== (1 - parsing_string); @@ -240,18 +230,12 @@ template StateToMask() { // `parsing_string` can change: out[2] <== 1 - 2 * parsing_string; - - // `parsing_array` can change: - out[3] <== (1 - parsing_string) * parsing_array; - - // `parsing_object` can change: - out[4] <== (1 - parsing_string); // `parsing_number` can change: - out[5] <== (1 - parsing_string); + out[3] <== (1 - parsing_string); // `key_or_value` can change: - out[6] <== (1 - parsing_string) - 2 * key_or_value; + out[4] <== (1 - parsing_string) - 2 * key_or_value; } // TODO: IMPORTANT NOTE, THE STACK IS CONSTRAINED TO 2**8 so the LessThan and GreaterThan work (could be changed) diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index e5a3db4..8823d28 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", "pointer", "stack", "parsing_string", "parsing_array", "parsing_object", "parsing_number", "key_or_value"], - ["next_pointer", "next_stack", "next_parsing_string", "next_parsing_array", "next_parsing_object", "next_parsing_number", "next_key_or_value"] + ["byte", "pointer", "stack", "parsing_string", "parsing_number", "key_or_value"], + ["next_pointer", "next_stack", "next_parsing_string", "next_parsing_number", "next_key_or_value"] >; function generatePassCase(input: any, expected: any, desc: string) { @@ -125,8 +125,6 @@ describe("parser", () => { pointer: "0", stack: ["0", "0", "0", "0"], parsing_string: "0", - parsing_array: "0", - parsing_object: "0", parsing_number: "0", key_or_value: "0", }; @@ -134,8 +132,6 @@ describe("parser", () => { next_pointer: init.pointer, next_stack: init.stack, next_parsing_string: init.parsing_string, - next_parsing_array: init.parsing_array, - next_parsing_object: init.parsing_object, next_parsing_number: init.parsing_number, next_key_or_value: init.key_or_value, }; @@ -149,7 +145,6 @@ describe("parser", () => { let read_start_brace_out = { ...out }; read_start_brace_out.next_pointer = "1"; read_start_brace_out.next_stack = ["1", "0", "0", "0"]; - read_start_brace_out.next_parsing_object = "1"; generatePassCase(read_start_brace, read_start_brace_out, ">>>> `{` read"); // Test 3: init setup -> `}` is read (should be INVALID) @@ -208,12 +203,10 @@ describe("parser", () => { let in_object = { ...init }; in_object.pointer = read_start_brace_out.next_pointer; in_object.stack = read_start_brace_out.next_stack; - in_object.parsing_object = read_start_brace_out.next_parsing_object; in_object.byte = start_brace; let in_object_out = { ...out }; in_object_out.next_pointer = "2"; in_object_out.next_stack = ["1", "1", "0", "0"]; - in_object_out.next_parsing_object = "2"; generatePassCase(in_object, in_object_out, ">>>> `{` read"); // Test 8: Stack Management @@ -223,12 +216,10 @@ describe("parser", () => { let in_object_to_leave = { ...init }; in_object_to_leave.pointer = read_start_brace_out.next_pointer; in_object_to_leave.stack = read_start_brace_out.next_stack; - in_object_to_leave.parsing_object = read_start_brace_out.next_parsing_object; in_object_to_leave.byte = end_brace; let in_object_to_leave_out = { ...out }; in_object_to_leave_out.next_pointer = "0"; in_object_to_leave_out.next_stack = ["0", "0", "0", "0"]; - in_object_to_leave_out.next_parsing_object = "0"; generatePassCase(in_object_to_leave, in_object_to_leave_out, ">>>> `\"` read"); // Test 9: Stack Management @@ -239,7 +230,6 @@ describe("parser", () => { in_object_to_read_start_bracket.byte = start_bracket; in_object_to_read_start_bracket.pointer = "1"; in_object_to_read_start_bracket.stack = ["1", "0", "0", "0"]; - in_object_to_read_start_bracket.parsing_object = "1"; let in_object_to_read_start_bracket_out = { ...out }; in_object_to_read_start_bracket_out.next_pointer = "2"; in_object_to_read_start_bracket_out.next_stack = @@ -247,8 +237,6 @@ describe("parser", () => { "21888242871839275222246405745257275088548364400416034343698204186575808495616", "0", "0"]; - in_object_to_read_start_bracket_out.next_parsing_object = "1"; - in_object_to_read_start_bracket_out.next_parsing_array = "1"; generatePassCase(in_object_to_read_start_bracket, in_object_to_read_start_bracket_out, ">>>> `\"` read"); From a2e0c9af48436b1f7ccea3618486c04b6c4840b5 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Sun, 11 Aug 2024 15:33:54 -0400 Subject: [PATCH 42/99] reduce and fix some tests! --- circuits/parser.circom | 58 +++++++++++++----------------------- circuits/test/parser.test.ts | 16 ++-------- 2 files changed, 23 insertions(+), 51 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index cbb9c33..8c8c517 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -70,8 +70,6 @@ template StateUpdate() { signal input pointer; // POINTER -- points to the stack to mark where we currently are inside the JSON. signal input stack[4]; // STACK -- how deep in a JSON nest we are and what type we are currently inside (e.g., `1` for object, `-1` for array). signal input parsing_string; - signal input parsing_array; - signal input parsing_object; signal input parsing_number; signal input key_or_value; // BIT_FLAG-- whether we are in a key or a value // signal parsing_boolean; @@ -80,30 +78,28 @@ template StateUpdate() { signal output next_pointer; signal output next_stack[4]; signal output next_parsing_string; - signal output next_parsing_object; - signal output next_parsing_array; signal output next_parsing_number; signal output next_key_or_value; //--------------------------------------------------------------------------------------------// //-Instructions for ASCII---------------------------------------------------------------------// var pushpop = 0; var obj_or_arr = 0; - var parsing_state[7] = [pushpop, obj_or_arr, parsing_string, parsing_array, parsing_object, parsing_number, key_or_value]; - var do_nothing[7] = [0, 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[7] = [1, 1, 0, -1, 1, 0, 0]; // Command returned by switch if we hit a start brace `{` - var hit_end_brace[7] = [-1, 1, 0, 0, -1, 0, 0]; // Command returned by switch if we hit a end brace `}` - var hit_start_bracket[7] = [1, -1, 0, 1, -1, 0, 0]; // TODO: Might want `key_or_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) - var hit_end_bracket[7] = [-1, -1, 0, -1, 0, 0, 0]; // Command returned by switch if we hit a start bracket `]` - var hit_quote[7] = [0, 0, 1, 0, 0, 0, 1]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` - var hit_colon[7] = [0, 0, 0, 0, 0, 0, 1]; // Command returned by switch if we hit a colon `:` - var hit_comma[7] = [0, 0, 0, 0, 0, -1, 0]; // Command returned by switch if we hit a comma `,` - var hit_number[7] = [0, 0, 0, 0, 0, 1, 0]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) + var parsing_state[5] = [pushpop, obj_or_arr, parsing_string, parsing_number, key_or_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, 0, 0]; // Command returned by switch if we hit a start brace `{` + var hit_end_brace[5] = [-1, 1, 0, 0, 0]; // Command returned by switch if we hit a end brace `}` + var hit_start_bracket[5] = [1, -1, 0, 0, 0]; // TODO: Might want `key_or_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) + var hit_end_bracket[5] = [-1, -1, 0, 0, 0]; // Command returned by switch if we hit a start bracket `]` + var hit_quote[5] = [0, 0, 1, 0, 1]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` + var hit_colon[5] = [0, 0, 0, 0, 1]; // Command returned by switch if we hit a colon `:` + var hit_comma[5] = [0, 0, 0, -1, 0]; // Command returned by switch if we hit a comma `,` + var hit_number[5] = [0, 0, 0, 1, 0]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// //-State machine updating---------------------------------------------------------------------// // * yield instruction based on what byte we read * - component matcher = Switch(8, 7); + component matcher = Switch(8, 5); var number = 256; // Number beyond a byte to represent an ASCII numeral matcher.branches <== [start_brace, end_brace, quote, colon, comma, start_bracket, end_bracket, number ]; matcher.vals <== [hit_start_brace, hit_end_brace, hit_quote, hit_colon, hit_comma, hit_start_bracket, hit_end_bracket, hit_number]; @@ -115,11 +111,11 @@ template StateUpdate() { component mask = StateToMask(); mask.in <== parsing_state; // * multiply the mask array elementwise with the instruction array * - component mulMaskAndOut = ArrayMul(7); + component mulMaskAndOut = ArrayMul(5); mulMaskAndOut.lhs <== mask.out; mulMaskAndOut.rhs <== matcher.out; // * add the masked instruction to the state to get new state * - component addToState = ArrayAdd(7); + component addToState = ArrayAdd(5); addToState.lhs <== parsing_state; addToState.rhs <== mulMaskAndOut.out; // * set the new state * @@ -131,10 +127,8 @@ template StateUpdate() { next_pointer <== newStack.next_pointer; next_stack <== newStack.next_stack; next_parsing_string <== addToState.out[2]; - next_parsing_array <== addToState.out[3]; - next_parsing_object <== addToState.out[4]; - next_parsing_number <== addToState.out[5]; - next_key_or_value <== addToState.out[6]; + next_parsing_number <== addToState.out[3]; + next_key_or_value <== addToState.out[4]; //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// @@ -152,8 +146,6 @@ template StateUpdate() { log("next_stack[", i,"] ", "= ", next_stack[i]); } log("next_parsing_string", "= ", next_parsing_string); - log("next_parsing_array ", "= ", next_parsing_array ); - log("next_parsing_object", "= ", next_parsing_object); log("next_parsing_number", "= ", next_parsing_number); log("next_key_or_value ", "= ", next_key_or_value ); log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); @@ -221,16 +213,14 @@ template Switch(m, n) { } template StateToMask() { - signal input in[7]; - signal output out[7]; + signal input in[5]; + signal output out[5]; signal pushpop <== in[0]; signal obj_or_array <== in[1]; signal parsing_string <== in[2]; - signal parsing_array <== in[3]; - signal parsing_object <== in[4]; - signal parsing_number <== in[5]; - signal key_or_value <== in[6]; + signal parsing_number <== in[3]; + signal key_or_value <== in[4]; // `pushpop` can change: IF NOT `parsing_string` out[0] <== (1 - parsing_string); @@ -240,18 +230,12 @@ template StateToMask() { // `parsing_string` can change: out[2] <== 1 - 2 * parsing_string; - - // `parsing_array` can change: - out[3] <== (1 - parsing_string) * parsing_array; - - // `parsing_object` can change: - out[4] <== (1 - parsing_string); // `parsing_number` can change: - out[5] <== (1 - parsing_string); + out[3] <== (1 - parsing_string); // `key_or_value` can change: - out[6] <== (1 - parsing_string) - 2 * key_or_value; + out[4] <== (1 - parsing_string) - 2 * key_or_value; } // TODO: IMPORTANT NOTE, THE STACK IS CONSTRAINED TO 2**8 so the LessThan and GreaterThan work (could be changed) diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index e5a3db4..8823d28 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", "pointer", "stack", "parsing_string", "parsing_array", "parsing_object", "parsing_number", "key_or_value"], - ["next_pointer", "next_stack", "next_parsing_string", "next_parsing_array", "next_parsing_object", "next_parsing_number", "next_key_or_value"] + ["byte", "pointer", "stack", "parsing_string", "parsing_number", "key_or_value"], + ["next_pointer", "next_stack", "next_parsing_string", "next_parsing_number", "next_key_or_value"] >; function generatePassCase(input: any, expected: any, desc: string) { @@ -125,8 +125,6 @@ describe("parser", () => { pointer: "0", stack: ["0", "0", "0", "0"], parsing_string: "0", - parsing_array: "0", - parsing_object: "0", parsing_number: "0", key_or_value: "0", }; @@ -134,8 +132,6 @@ describe("parser", () => { next_pointer: init.pointer, next_stack: init.stack, next_parsing_string: init.parsing_string, - next_parsing_array: init.parsing_array, - next_parsing_object: init.parsing_object, next_parsing_number: init.parsing_number, next_key_or_value: init.key_or_value, }; @@ -149,7 +145,6 @@ describe("parser", () => { let read_start_brace_out = { ...out }; read_start_brace_out.next_pointer = "1"; read_start_brace_out.next_stack = ["1", "0", "0", "0"]; - read_start_brace_out.next_parsing_object = "1"; generatePassCase(read_start_brace, read_start_brace_out, ">>>> `{` read"); // Test 3: init setup -> `}` is read (should be INVALID) @@ -208,12 +203,10 @@ describe("parser", () => { let in_object = { ...init }; in_object.pointer = read_start_brace_out.next_pointer; in_object.stack = read_start_brace_out.next_stack; - in_object.parsing_object = read_start_brace_out.next_parsing_object; in_object.byte = start_brace; let in_object_out = { ...out }; in_object_out.next_pointer = "2"; in_object_out.next_stack = ["1", "1", "0", "0"]; - in_object_out.next_parsing_object = "2"; generatePassCase(in_object, in_object_out, ">>>> `{` read"); // Test 8: Stack Management @@ -223,12 +216,10 @@ describe("parser", () => { let in_object_to_leave = { ...init }; in_object_to_leave.pointer = read_start_brace_out.next_pointer; in_object_to_leave.stack = read_start_brace_out.next_stack; - in_object_to_leave.parsing_object = read_start_brace_out.next_parsing_object; in_object_to_leave.byte = end_brace; let in_object_to_leave_out = { ...out }; in_object_to_leave_out.next_pointer = "0"; in_object_to_leave_out.next_stack = ["0", "0", "0", "0"]; - in_object_to_leave_out.next_parsing_object = "0"; generatePassCase(in_object_to_leave, in_object_to_leave_out, ">>>> `\"` read"); // Test 9: Stack Management @@ -239,7 +230,6 @@ describe("parser", () => { in_object_to_read_start_bracket.byte = start_bracket; in_object_to_read_start_bracket.pointer = "1"; in_object_to_read_start_bracket.stack = ["1", "0", "0", "0"]; - in_object_to_read_start_bracket.parsing_object = "1"; let in_object_to_read_start_bracket_out = { ...out }; in_object_to_read_start_bracket_out.next_pointer = "2"; in_object_to_read_start_bracket_out.next_stack = @@ -247,8 +237,6 @@ describe("parser", () => { "21888242871839275222246405745257275088548364400416034343698204186575808495616", "0", "0"]; - in_object_to_read_start_bracket_out.next_parsing_object = "1"; - in_object_to_read_start_bracket_out.next_parsing_array = "1"; generatePassCase(in_object_to_read_start_bracket, in_object_to_read_start_bracket_out, ">>>> `\"` read"); From 0652140ccebe790fe6bc9e49ba272730b9d917e1 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Mon, 12 Aug 2024 08:48:20 -0400 Subject: [PATCH 43/99] moar tests --- circuits/parser.circom | 26 +++++++++----------------- circuits/test/parser.test.ts | 24 ++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 8c8c517..72ec268 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -262,10 +262,11 @@ template RewriteStack(n) { next_pointer <== pointer + pushpop; // If pushpop is 0, pointer doesn't change, if -1, decrement, +1 increment // Indicate which position in the stack should change (if any) - component isPop[n]; - component isPush[n]; - component indicatorPush[n]; - component indicatorPop[n]; + component isPop = IsZero(); + isPop.in <== pushpop + 1; + component isPush = IsZero(); + isPush.in <== pushpop - 1; + component indicator[n]; signal isPopAt[n]; signal isPushAt[n]; @@ -278,20 +279,11 @@ next_pointer <== pointer + pushpop; // If pushpop is 0, pointer doesn't change, //TODO: Note, we are not effectively using the stack, we could actually pop and read these values to save to inner state signals // I.e., the `in_object` and `in_array` or whatever for(var i = 0; i < n; i++) { - isPop[i] = IsZero(); - isPop[i].in <== pushpop + 1; // TRUE if we are popping + indicator[i] = IsZero(); + indicator[i].in <== pointer - isPop.out - i; // 1 in the position of the current pointer - isPush[i] = IsZero(); - isPush[i].in <== pushpop - 1; // TRUE if we are pushing - - indicatorPush[i] = IsZero(); - indicatorPush[i].in <== pointer - i; // 1 in the position of the current pointer - - indicatorPop[i] = IsZero(); - indicatorPop[i].in <== next_pointer - i; // 1 in the position of the current pointer - - isPopAt[i] <== indicatorPop[i].out * isPop[i].out; // Index to pop from - isPushAt[i] <== indicatorPush[i].out * isPush[i].out; // Index to push to + isPopAt[i] <== indicator[i].out * isPop.out; // Index to pop from + isPushAt[i] <== indicator[i].out * isPush.out; // Index to push to // Could use GreaterEqThan to set any position in the stack at next_pointer or above 0? diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index 8823d28..5292fb1 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -220,7 +220,7 @@ describe("parser", () => { let in_object_to_leave_out = { ...out }; in_object_to_leave_out.next_pointer = "0"; in_object_to_leave_out.next_stack = ["0", "0", "0", "0"]; - generatePassCase(in_object_to_leave, in_object_to_leave_out, ">>>> `\"` read"); + generatePassCase(in_object_to_leave, in_object_to_leave_out, ">>>> `}` read"); // Test 9: Stack Management // init: read `{`, then read `[` @@ -237,7 +237,27 @@ describe("parser", () => { "21888242871839275222246405745257275088548364400416034343698204186575808495616", "0", "0"]; - generatePassCase(in_object_to_read_start_bracket, in_object_to_read_start_bracket_out, ">>>> `\"` read"); + generatePassCase(in_object_to_read_start_bracket, in_object_to_read_start_bracket_out, ">>>> `[` read"); + + // Test 10: Stack Management + // init: read 4x `{`, then read `{` + // expect: pointer --> 4 + // stack --> [1,1,1,1] + let in_max_stack = { ...init }; + in_max_stack.byte = start_brace; + in_max_stack.pointer = "4"; + in_max_stack.stack = ["1", "1", "1", "1"]; + generateFailCase(in_max_stack, ">>>> `{` read --> (stack overflow)"); + + // Test 11: Stack Management + // init: read 4x `{`, then read `[` + // expect: pointer --> 4 + // stack --> [1,1,1,1] + let in_max_stack_2 = { ...init }; + in_max_stack_2.byte = start_bracket; + in_max_stack_2.pointer = "4"; + in_max_stack_2.stack = ["1", "1", "1", "1"]; + generateFailCase(in_max_stack, ">>>> `[` read --> (stack overflow)"); //// BREAK HERE AND RENAME AND ADJUST From e2fb1923d92a455fdf71c5f20bc582f708283dc9 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Mon, 12 Aug 2024 08:56:06 -0400 Subject: [PATCH 44/99] cosmetics --- circuits/parser.circom | 56 +++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 72ec268..13d0e7c 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -85,15 +85,15 @@ template StateUpdate() { var pushpop = 0; var obj_or_arr = 0; var parsing_state[5] = [pushpop, obj_or_arr, parsing_string, parsing_number, key_or_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, 0, 0]; // Command returned by switch if we hit a start brace `{` - var hit_end_brace[5] = [-1, 1, 0, 0, 0]; // Command returned by switch if we hit a end brace `}` - var hit_start_bracket[5] = [1, -1, 0, 0, 0]; // TODO: Might want `key_or_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) - var hit_end_bracket[5] = [-1, -1, 0, 0, 0]; // Command returned by switch if we hit a start bracket `]` - var hit_quote[5] = [0, 0, 1, 0, 1]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` - var hit_colon[5] = [0, 0, 0, 0, 1]; // Command returned by switch if we hit a colon `:` - var hit_comma[5] = [0, 0, 0, -1, 0]; // Command returned by switch if we hit a comma `,` - var hit_number[5] = [0, 0, 0, 1, 0]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) + 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, 0, 0]; // Command returned by switch if we hit a start brace `{` + var hit_end_brace[5] = [-1, 1, 0, 0, 0]; // Command returned by switch if we hit a end brace `}` + var hit_start_bracket[5] = [1, -1, 0, 0, 0]; // TODO: Might want `key_or_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) + var hit_end_bracket[5] = [-1, -1, 0, 0, 0]; // Command returned by switch if we hit a start bracket `]` + var hit_quote[5] = [0, 0, 1, 0, 1]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` + var hit_colon[5] = [0, 0, 0, 0, 1]; // Command returned by switch if we hit a colon `:` + var hit_comma[5] = [0, 0, 0, -1, 0]; // Command returned by switch if we hit a comma `,` + var hit_number[5] = [0, 0, 0, 1, 0]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// @@ -119,16 +119,16 @@ template StateUpdate() { addToState.lhs <== parsing_state; addToState.rhs <== mulMaskAndOut.out; // * set the new state * - component newStack = RewriteStack(4); - newStack.pointer <== pointer; - newStack.stack <== stack; - newStack.pushpop <== addToState.out[0]; - newStack.obj_or_arr <== addToState.out[1]; - next_pointer <== newStack.next_pointer; - next_stack <== newStack.next_stack; - next_parsing_string <== addToState.out[2]; - next_parsing_number <== addToState.out[3]; - next_key_or_value <== addToState.out[4]; + component newStack = RewriteStack(4); + newStack.pointer <== pointer; + newStack.stack <== stack; + newStack.pushpop <== addToState.out[0]; + newStack.obj_or_arr <== addToState.out[1]; + next_pointer <== newStack.next_pointer; + next_stack <== newStack.next_stack; + next_parsing_string <== addToState.out[2]; + next_parsing_number <== addToState.out[3]; + next_key_or_value <== addToState.out[4]; //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// @@ -259,7 +259,7 @@ template RewriteStack(n) { - if pushpop is -1, we are going to decrement the pointer and delete an old value if it was the same value */ -next_pointer <== pointer + pushpop; // If pushpop is 0, pointer doesn't change, if -1, decrement, +1 increment + next_pointer <== pointer + pushpop; // If pushpop is 0, pointer doesn't change, if -1, decrement, +1 increment // Indicate which position in the stack should change (if any) component isPop = IsZero(); @@ -270,22 +270,12 @@ next_pointer <== pointer + pushpop; // If pushpop is 0, pointer doesn't change, signal isPopAt[n]; signal isPushAt[n]; - // EXAMPLE: - // `pointer == 1`, `stack == [1, 0, 0, 0]` - // >>>> `pushpop == -1` - // This means we need to decrement pointer, then pop from the stack - // This means we take `next_pointer` then set this to zero - - //TODO: Note, we are not effectively using the stack, we could actually pop and read these values to save to inner state signals - // I.e., the `in_object` and `in_array` or whatever for(var i = 0; i < n; i++) { indicator[i] = IsZero(); - indicator[i].in <== pointer - isPop.out - i; // 1 in the position of the current pointer - - isPopAt[i] <== indicator[i].out * isPop.out; // Index to pop from - isPushAt[i] <== indicator[i].out * isPush.out; // Index to push to + indicator[i].in <== pointer - isPop.out - i; - // Could use GreaterEqThan to set any position in the stack at next_pointer or above 0? + isPopAt[i] <== indicator[i].out * isPop.out; + isPushAt[i] <== indicator[i].out * isPush.out; // Leave the stack alone except for where we indicate change next_stack[i] <== stack[i] + (isPushAt[i] - isPopAt[i]) * obj_or_arr; From 40f88c58d4295cf6e01a38c5cffdeea76f3d07a5 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Mon, 12 Aug 2024 09:12:39 -0400 Subject: [PATCH 45/99] continuing with tests --- circuits/parser.circom | 2 +- circuits/test/parser.test.ts | 101 +++++++++++++++++++++-------------- 2 files changed, 61 insertions(+), 42 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 13d0e7c..e3cab23 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -71,7 +71,7 @@ template StateUpdate() { signal input stack[4]; // STACK -- how deep in a JSON nest we are and what type we are currently inside (e.g., `1` for object, `-1` for array). signal input parsing_string; signal input parsing_number; - signal input key_or_value; // BIT_FLAG-- whether we are in a key or a value + signal input key_or_value; // (conditional?) BIT_FLAG -- 1 if in KEY string 0 if in VALUE string? // signal parsing_boolean; // signal parsing_null; // TODO diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index 5292fb1..5e114a1 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -66,23 +66,23 @@ describe("parser", () => { // - ASCII char `[` const start_bracket = "91"; // - ASCII char `]` - const end_bracket = 93; + const end_bracket = "93"; // - ASCII char `"` - const quote = 34; + const quote = "34"; // - ASCII char `:` - const colon = 58; + const colon = "58"; // - ASCII char `,` - const comma = 44; + const comma = "44"; //--------------------------------------------------------------------------------------------// // White space // - ASCII char: `\n` - const newline = 10; + const newline = "10"; // - ASCII char: ` ` - const space = 32; + const space = "32"; //--------------------------------------------------------------------------------------------// // Escape // - ASCII char: `\` - const escape = 92; + const escape = "92"; //--------------------------------------------------------------------------------------------// describe("StateUpdate", () => { @@ -136,52 +136,70 @@ describe("parser", () => { next_key_or_value: init.key_or_value, }; - // Test 1: init setup -> `do_nothing` byte + //-----------------------------------------------------------------------------// + // Test 1: + // init: ZEROS then read `do_nothing` byte + // expect: ZEROS generatePassCase(init, out, ">>>> `NUL` read"); + //-----------------------------------------------------------------------------// - // Test 2: init setup -> `{` is read + //-----------------------------------------------------------------------------// + // Test 2: + // init: ZEROS -> `{` is read + // expect: pointer --> 1 + // stack --> [1,0,0,0] let read_start_brace = { ...init }; read_start_brace.byte = start_brace; let read_start_brace_out = { ...out }; read_start_brace_out.next_pointer = "1"; read_start_brace_out.next_stack = ["1", "0", "0", "0"]; generatePassCase(read_start_brace, read_start_brace_out, ">>>> `{` read"); + //-----------------------------------------------------------------------------// - // Test 3: init setup -> `}` is read (should be INVALID) + //-----------------------------------------------------------------------------// + // Test 3: + // init: ZEROS -> `}` is read + // expect: FAIL stack underflow let read_end_brace = { ...init }; read_end_brace.byte = end_brace; generateFailCase(read_end_brace, ">>>> `}` read --> (stack underflow)"); - - // // Test 4: after just reading { then read a quote - // let in_object_find_key = { ...init }; - // in_object_find_key.pointer = read_start_brace_out.next_pointer; - // in_object_find_key.stack = read_start_brace_out.next_stack; - // in_object_find_key.parsing_object = read_start_brace_out.next_parsing_object; - // in_object_find_key.byte = quote; - // let in_object_find_key_out = { ...out }; - // in_object_find_key_out.next_pointer = 1; - // in_object_find_key_out.next_stack = [1, 0, 0, 0]; - // in_object_find_key_out.next_parsing_string = 1; - // in_object_find_key_out.next_key_or_value = 1; - // in_object_find_key_out.next_parsing_object = 1; - // generatePassCase(in_object_find_key, in_object_find_key_out, ">>>> `\"` read"); - - // // TODO: THESE SHOULD ACTUALLY SAY WE ARE KEY OR VALUE - // // Test 5: `tree_depth == 1` AND `inside_key ==1` setup -> ` ` is read - // let in_key = { ...init }; - // in_key.pointer = read_start_brace_out.next_pointer; - // in_key.stack = read_start_brace_out.next_stack; - // in_key.parsing_object = read_start_brace_out.next_parsing_object; - // in_key.parsing_string = 1; - // in_key.key_or_value = 1; - // in_key.byte = space; - // let in_key_out = { ...out }; - // in_key_out.next_pointer = 1; - // in_key_out.next_stack = [1, 0, 0, 0]; - // in_key_out.next_parsing_string = 1; - // in_key_out.next_parsing_object = 1; - // in_key_out.next_key_or_value = 1; - // generatePassCase(in_key, in_key_out, ">>>> ` ` read"); + //-----------------------------------------------------------------------------// + + //-----------------------------------------------------------------------------// + // Test 4: + // init: pointer == 1, stack = [1,0,0,0] -> `"` is read + // expect: parsing_string --> 1 + // key_or_value --> 1 + let in_object_find_key = { ...init }; + in_object_find_key.pointer = read_start_brace_out.next_pointer; + in_object_find_key.stack = read_start_brace_out.next_stack; + in_object_find_key.byte = quote; + let in_object_find_key_out = { ...out }; + in_object_find_key_out.next_pointer = in_object_find_key.pointer; + in_object_find_key_out.next_stack = in_object_find_key.stack; + in_object_find_key_out.next_parsing_string = "1"; + in_object_find_key_out.next_key_or_value = "1"; + generatePassCase(in_object_find_key, in_object_find_key_out, ">>>> `\"` read"); + //-----------------------------------------------------------------------------// + + //-----------------------------------------------------------------------------// + // Test 5: + // init: pointer == 1, stack = [1,0,0,0], parsing_string = 1, key_or_value = 1 -> ` ` is read + // expect: parsing_string --> 1 + // key_or_value --> 1 + let in_key = { ...init }; + in_key.pointer = read_start_brace_out.next_pointer; + in_key.stack = read_start_brace_out.next_stack; + in_key.parsing_string = "1"; + in_key.key_or_value = "1"; + in_key.byte = space; + let in_key_out = { ...out }; + in_key_out.next_pointer = in_key.pointer; + in_key_out.next_stack = in_key.stack; + in_key_out.next_parsing_string = "1"; + in_key_out.next_key_or_value = "1"; + generatePassCase(in_key, in_key_out, ">>>> ` ` read"); + //-----------------------------------------------------------------------------// // // Test 6: `tree_depth == 1` AND `inside_key == 1 AND `parsing_key == 0` setup -> `"` is read // let in_key_to_exit = { ...init }; @@ -196,6 +214,7 @@ describe("parser", () => { // in_key_to_exit_out.next_parsing_object = 1; // generatePassCase(in_key_to_exit, in_key_to_exit_out, "`\"` read"); + // TODO: Test stack fully works with brackets too // Test 7: Stack Management // init: read `{`, read another `{` // expect: pointer --> 2 From d08ff4cfb15d8b9921f76c42d07f4789094a9807 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Mon, 12 Aug 2024 09:31:32 -0400 Subject: [PATCH 46/99] save state --- circuits/parser.circom | 22 ++-- circuits/test/parser.test.ts | 209 +++++++++++++++++++---------------- 2 files changed, 123 insertions(+), 108 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index e3cab23..726c995 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -71,7 +71,7 @@ template StateUpdate() { signal input stack[4]; // STACK -- how deep in a JSON nest we are and what type we are currently inside (e.g., `1` for object, `-1` for array). signal input parsing_string; signal input parsing_number; - signal input key_or_value; // (conditional?) BIT_FLAG -- 1 if in KEY string 0 if in VALUE string? + signal input in_value; // BIT_FLAG -- 1 if in KEY string 0 otherwise (TODO: disable with a colon? Might need to track that with stack) // signal parsing_boolean; // signal parsing_null; // TODO @@ -79,20 +79,20 @@ template StateUpdate() { signal output next_stack[4]; signal output next_parsing_string; signal output next_parsing_number; - signal output next_key_or_value; + signal output next_in_value; //--------------------------------------------------------------------------------------------// //-Instructions for ASCII---------------------------------------------------------------------// var pushpop = 0; var obj_or_arr = 0; - var parsing_state[5] = [pushpop, obj_or_arr, parsing_string, parsing_number, key_or_value]; + var parsing_state[5] = [pushpop, obj_or_arr, parsing_string, parsing_number, in_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, 0, 0]; // Command returned by switch if we hit a start brace `{` var hit_end_brace[5] = [-1, 1, 0, 0, 0]; // Command returned by switch if we hit a end brace `}` - var hit_start_bracket[5] = [1, -1, 0, 0, 0]; // TODO: Might want `key_or_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) + var hit_start_bracket[5] = [1, -1, 0, 0, 0]; // TODO: Might want `in_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) var hit_end_bracket[5] = [-1, -1, 0, 0, 0]; // Command returned by switch if we hit a start bracket `]` - var hit_quote[5] = [0, 0, 1, 0, 1]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` + var hit_quote[5] = [0, 0, 1, 0, 0]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` var hit_colon[5] = [0, 0, 0, 0, 1]; // Command returned by switch if we hit a colon `:` - var hit_comma[5] = [0, 0, 0, -1, 0]; // Command returned by switch if we hit a comma `,` + var hit_comma[5] = [0, 0, 0, -1, -1]; // Command returned by switch if we hit a comma `,` var hit_number[5] = [0, 0, 0, 1, 0]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) //--------------------------------------------------------------------------------------------// @@ -128,7 +128,7 @@ template StateUpdate() { next_stack <== newStack.next_stack; next_parsing_string <== addToState.out[2]; next_parsing_number <== addToState.out[3]; - next_key_or_value <== addToState.out[4]; + next_in_value <== addToState.out[4]; //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// @@ -147,7 +147,7 @@ template StateUpdate() { } log("next_parsing_string", "= ", next_parsing_string); log("next_parsing_number", "= ", next_parsing_number); - log("next_key_or_value ", "= ", next_key_or_value ); + log("next_in_value ", " = ", next_in_value ); log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); //--------------------------------------------------------------------------------------------// @@ -220,7 +220,7 @@ template StateToMask() { signal obj_or_array <== in[1]; signal parsing_string <== in[2]; signal parsing_number <== in[3]; - signal key_or_value <== in[4]; + signal in_value <== in[4]; // `pushpop` can change: IF NOT `parsing_string` out[0] <== (1 - parsing_string); @@ -234,8 +234,8 @@ template StateToMask() { // `parsing_number` can change: out[3] <== (1 - parsing_string); - // `key_or_value` can change: - out[4] <== (1 - parsing_string) - 2 * key_or_value; + // `in_value` can change: + out[4] <== (1 - parsing_string) - 2 * in_value; } // TODO: IMPORTANT NOTE, THE STACK IS CONSTRAINED TO 2**8 so the LessThan and GreaterThan work (could be changed) diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index 5e114a1..a46ef5a 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -126,16 +126,17 @@ describe("parser", () => { stack: ["0", "0", "0", "0"], parsing_string: "0", parsing_number: "0", - key_or_value: "0", + in_value: "0", }; let out = { next_pointer: init.pointer, next_stack: init.stack, next_parsing_string: init.parsing_string, next_parsing_number: init.parsing_number, - next_key_or_value: init.key_or_value, + next_in_value: init.in_value, }; + //-----------------------------------------------------------------------------// // Test 1: // init: ZEROS then read `do_nothing` byte @@ -169,7 +170,6 @@ describe("parser", () => { // Test 4: // init: pointer == 1, stack = [1,0,0,0] -> `"` is read // expect: parsing_string --> 1 - // key_or_value --> 1 let in_object_find_key = { ...init }; in_object_find_key.pointer = read_start_brace_out.next_pointer; in_object_find_key.stack = read_start_brace_out.next_stack; @@ -178,119 +178,55 @@ describe("parser", () => { in_object_find_key_out.next_pointer = in_object_find_key.pointer; in_object_find_key_out.next_stack = in_object_find_key.stack; in_object_find_key_out.next_parsing_string = "1"; - in_object_find_key_out.next_key_or_value = "1"; generatePassCase(in_object_find_key, in_object_find_key_out, ">>>> `\"` read"); //-----------------------------------------------------------------------------// //-----------------------------------------------------------------------------// // Test 5: - // init: pointer == 1, stack = [1,0,0,0], parsing_string = 1, key_or_value = 1 -> ` ` is read + // init: pointer == 1, stack = [1,0,0,0], parsing_string = 1 -> ` ` is read // expect: parsing_string --> 1 - // key_or_value --> 1 + // in_key --> 1 let in_key = { ...init }; in_key.pointer = read_start_brace_out.next_pointer; in_key.stack = read_start_brace_out.next_stack; in_key.parsing_string = "1"; - in_key.key_or_value = "1"; in_key.byte = space; let in_key_out = { ...out }; in_key_out.next_pointer = in_key.pointer; in_key_out.next_stack = in_key.stack; in_key_out.next_parsing_string = "1"; - in_key_out.next_key_or_value = "1"; generatePassCase(in_key, in_key_out, ">>>> ` ` 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.pointer = read_start_brace_out.next_pointer; - // in_key_to_exit.stack = read_start_brace_out.next_stack; - // in_key_to_exit.parsing_object = read_start_brace_out.next_parsing_object; - // in_key_to_exit.parsing_string = 1 - // in_key_to_exit.byte = quote; - // let in_key_to_exit_out = { ...out }; - // in_key_to_exit_out.next_pointer = 1; - // in_key_to_exit_out.next_stack = [1, 0, 0, 0]; - // in_key_to_exit_out.next_parsing_object = 1; - // generatePassCase(in_key_to_exit, in_key_to_exit_out, "`\"` read"); - - // TODO: Test stack fully works with brackets too - // Test 7: Stack Management - // init: read `{`, read another `{` - // expect: pointer --> 2 - // stack --> [1,1,0,0] - let in_object = { ...init }; - in_object.pointer = read_start_brace_out.next_pointer; - in_object.stack = read_start_brace_out.next_stack; - in_object.byte = start_brace; - let in_object_out = { ...out }; - in_object_out.next_pointer = "2"; - in_object_out.next_stack = ["1", "1", "0", "0"]; - generatePassCase(in_object, in_object_out, ">>>> `{` read"); - - // Test 8: Stack Management - // init: read `{` then read`}` - // expect: pointer-- > 0 - // stack-- > [0, 0, 0, 0] - let in_object_to_leave = { ...init }; - in_object_to_leave.pointer = read_start_brace_out.next_pointer; - in_object_to_leave.stack = read_start_brace_out.next_stack; - in_object_to_leave.byte = end_brace; - let in_object_to_leave_out = { ...out }; - in_object_to_leave_out.next_pointer = "0"; - in_object_to_leave_out.next_stack = ["0", "0", "0", "0"]; - generatePassCase(in_object_to_leave, in_object_to_leave_out, ">>>> `}` read"); - - // Test 9: Stack Management - // init: read `{`, then read `[` - // expect: pointer --> 2 - // stack --> [1,-1,0,0] - let in_object_to_read_start_bracket = { ...init }; - in_object_to_read_start_bracket.byte = start_bracket; - in_object_to_read_start_bracket.pointer = "1"; - in_object_to_read_start_bracket.stack = ["1", "0", "0", "0"]; - let in_object_to_read_start_bracket_out = { ...out }; - in_object_to_read_start_bracket_out.next_pointer = "2"; - in_object_to_read_start_bracket_out.next_stack = - ["1", - "21888242871839275222246405745257275088548364400416034343698204186575808495616", - "0", - "0"]; - generatePassCase(in_object_to_read_start_bracket, in_object_to_read_start_bracket_out, ">>>> `[` read"); - - // Test 10: Stack Management - // init: read 4x `{`, then read `{` - // expect: pointer --> 4 - // stack --> [1,1,1,1] - let in_max_stack = { ...init }; - in_max_stack.byte = start_brace; - in_max_stack.pointer = "4"; - in_max_stack.stack = ["1", "1", "1", "1"]; - generateFailCase(in_max_stack, ">>>> `{` read --> (stack overflow)"); - - // Test 11: Stack Management - // init: read 4x `{`, then read `[` - // expect: pointer --> 4 - // stack --> [1,1,1,1] - let in_max_stack_2 = { ...init }; - in_max_stack_2.byte = start_bracket; - in_max_stack_2.pointer = "4"; - in_max_stack_2.stack = ["1", "1", "1", "1"]; - generateFailCase(in_max_stack, ">>>> `[` read --> (stack overflow)"); - - - //// BREAK HERE AND RENAME AND ADJUST - /// USE CLEAR PREVIOUS SETUPS SO THIS IS EASIER TO PARSE VVVVVVVVVVVV + //-----------------------------------------------------------------------------// + // Test 6: + // init: pointer = 1, stack = [1,0,0,0], parsing_string = 1 setup -> `"` is read + // expect: parsing_string --> 0 + // + let in_key_to_exit = { ...init }; + in_key_to_exit.pointer = read_start_brace_out.next_pointer; + in_key_to_exit.stack = read_start_brace_out.next_stack; + in_key_to_exit.parsing_string = "1" + in_key_to_exit.byte = quote; + let in_key_to_exit_out = { ...out }; + in_key_to_exit_out.next_pointer = in_key_to_exit.pointer; + in_key_to_exit_out.next_stack = in_key_to_exit.stack; + 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_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_value = 1; - // generatePassCase(parsed_key_wait_to_parse_value, parsed_key_wait_to_parse_value_out, ">>>> `:` read"); + //-----------------------------------------------------------------------------// + // Test 7: + // init: pointer = 1, stack = [1,0,0,0] -> `:` is read + let parsed_key_wait_to_parse_value = { ...init }; + parsed_key_wait_to_parse_value.pointer = read_start_brace_out.next_pointer; + parsed_key_wait_to_parse_value.stack = read_start_brace_out.next_stack; + 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_pointer = parsed_key_wait_to_parse_value.pointer; + parsed_key_wait_to_parse_value_out.next_stack = parsed_key_wait_to_parse_value.stack; + parsed_key_wait_to_parse_value_out.next_in_value = "1"; + generatePassCase(parsed_key_wait_to_parse_value, parsed_key_wait_to_parse_value_out, ">>>> `: ` read"); + //-----------------------------------------------------------------------------// // // Test 8: `tree_depth == 1` AND parsing_value == 1` setup -> `"` is read // let in_tree_find_value = { ...init }; @@ -364,6 +300,85 @@ describe("parser", () => { // end_of_key_to_inner_object_out.next_tree_depth = 2; // 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"); + + + + + + + + + + + + + + + + + // TODO: Test stack fully works with brackets too + // Test 7: Stack Management + // init: read `{`, read another `{` + // expect: pointer --> 2 + // stack --> [1,1,0,0] + let in_object = { ...init }; + in_object.pointer = read_start_brace_out.next_pointer; + in_object.stack = read_start_brace_out.next_stack; + in_object.byte = start_brace; + let in_object_out = { ...out }; + in_object_out.next_pointer = "2"; + in_object_out.next_stack = ["1", "1", "0", "0"]; + generatePassCase(in_object, in_object_out, ">>>> `{` read"); + + // Test 8: Stack Management + // init: read `{` then read`}` + // expect: pointer --> 0 + // stack --> [0, 0, 0, 0] + let in_object_to_leave = { ...init }; + in_object_to_leave.pointer = read_start_brace_out.next_pointer; + in_object_to_leave.stack = read_start_brace_out.next_stack; + in_object_to_leave.byte = end_brace; + let in_object_to_leave_out = { ...out }; + in_object_to_leave_out.next_pointer = "0"; + in_object_to_leave_out.next_stack = ["0", "0", "0", "0"]; + generatePassCase(in_object_to_leave, in_object_to_leave_out, ">>>> `}` read"); + + // Test 9: Stack Management + // init: read `{`, then read `[` + // expect: pointer --> 2 + // stack --> [1,-1,0,0] + let in_object_to_read_start_bracket = { ...init }; + in_object_to_read_start_bracket.byte = start_bracket; + in_object_to_read_start_bracket.pointer = "1"; + in_object_to_read_start_bracket.stack = ["1", "0", "0", "0"]; + let in_object_to_read_start_bracket_out = { ...out }; + in_object_to_read_start_bracket_out.next_pointer = "2"; + in_object_to_read_start_bracket_out.next_stack = + ["1", + "21888242871839275222246405745257275088548364400416034343698204186575808495616", + "0", + "0"]; + generatePassCase(in_object_to_read_start_bracket, in_object_to_read_start_bracket_out, ">>>> `[` read"); + + // Test 10: Stack Management + // init: read 4x `{`, then read `{` + // expect: pointer --> 4 + // stack --> [1,1,1,1] + let in_max_stack = { ...init }; + in_max_stack.byte = start_brace; + in_max_stack.pointer = "4"; + in_max_stack.stack = ["1", "1", "1", "1"]; + generateFailCase(in_max_stack, ">>>> `{` read --> (stack overflow)"); + + // Test 11: Stack Management + // init: read 4x `{`, then read `[` + // expect: pointer --> 4 + // stack --> [1,1,1,1] + let in_max_stack_2 = { ...init }; + in_max_stack_2.byte = start_bracket; + in_max_stack_2.pointer = "4"; + in_max_stack_2.stack = ["1", "1", "1", "1"]; + generateFailCase(in_max_stack, ">>>> `[` read --> (stack overflow)"); }); }); From ad2e643c7ebfa5cb532774f064cf932b2e03c183 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Mon, 12 Aug 2024 10:07:13 -0500 Subject: [PATCH 47/99] renumbered stack entries --- circuits/parser.circom | 53 +++++++--------- circuits/test/parser.test.ts | 113 ++++++++++++++++++++--------------- 2 files changed, 88 insertions(+), 78 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 726c995..ccff4b3 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -71,7 +71,6 @@ template StateUpdate() { signal input stack[4]; // STACK -- how deep in a JSON nest we are and what type we are currently inside (e.g., `1` for object, `-1` for array). signal input parsing_string; signal input parsing_number; - signal input in_value; // BIT_FLAG -- 1 if in KEY string 0 otherwise (TODO: disable with a colon? Might need to track that with stack) // signal parsing_boolean; // signal parsing_null; // TODO @@ -79,27 +78,26 @@ template StateUpdate() { signal output next_stack[4]; signal output next_parsing_string; signal output next_parsing_number; - signal output next_in_value; //--------------------------------------------------------------------------------------------// //-Instructions for ASCII---------------------------------------------------------------------// var pushpop = 0; - var obj_or_arr = 0; - var parsing_state[5] = [pushpop, obj_or_arr, parsing_string, parsing_number, in_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, 0, 0]; // Command returned by switch if we hit a start brace `{` - var hit_end_brace[5] = [-1, 1, 0, 0, 0]; // Command returned by switch if we hit a end brace `}` - var hit_start_bracket[5] = [1, -1, 0, 0, 0]; // TODO: Might want `in_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) - var hit_end_bracket[5] = [-1, -1, 0, 0, 0]; // Command returned by switch if we hit a start bracket `]` - var hit_quote[5] = [0, 0, 1, 0, 0]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` - var hit_colon[5] = [0, 0, 0, 0, 1]; // Command returned by switch if we hit a colon `:` - var hit_comma[5] = [0, 0, 0, -1, -1]; // Command returned by switch if we hit a comma `,` - var hit_number[5] = [0, 0, 0, 1, 0]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) + var stack_val = 0; + var parsing_state[4] = [pushpop, stack_val, parsing_string, parsing_number]; + 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 hit_start_brace[4] = [1, 1, 0, 0 ]; // Command returned by switch if we hit a start brace `{` + var hit_end_brace[4] = [-1, 1, 0, 0 ]; // Command returned by switch if we hit a end brace `}` + var hit_start_bracket[4] = [1, 2, 0, 0 ]; // TODO: Might want `in_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) + var hit_end_bracket[4] = [-1, 2, 0, 0 ]; // Command returned by switch if we hit a start bracket `]` + var hit_quote[4] = [0, 0, 1, 0 ]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` + var hit_colon[4] = [1, 3, 0, 0 ]; // Command returned by switch if we hit a colon `:` + var hit_comma[4] = [-1, 3, 0, -1 ]; // Command returned by switch if we hit a comma `,` + var hit_number[4] = [0, 0, 0, 1 ]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// //-State machine updating---------------------------------------------------------------------// // * yield instruction based on what byte we read * - component matcher = Switch(8, 5); + component matcher = Switch(8, 4); var number = 256; // Number beyond a byte to represent an ASCII numeral matcher.branches <== [start_brace, end_brace, quote, colon, comma, start_bracket, end_bracket, number ]; matcher.vals <== [hit_start_brace, hit_end_brace, hit_quote, hit_colon, hit_comma, hit_start_bracket, hit_end_bracket, hit_number]; @@ -111,11 +109,11 @@ template StateUpdate() { component mask = StateToMask(); mask.in <== parsing_state; // * multiply the mask array elementwise with the instruction array * - component mulMaskAndOut = ArrayMul(5); + component mulMaskAndOut = ArrayMul(4); mulMaskAndOut.lhs <== mask.out; mulMaskAndOut.rhs <== matcher.out; // * add the masked instruction to the state to get new state * - component addToState = ArrayAdd(5); + component addToState = ArrayAdd(4); addToState.lhs <== parsing_state; addToState.rhs <== mulMaskAndOut.out; // * set the new state * @@ -123,12 +121,11 @@ template StateUpdate() { newStack.pointer <== pointer; newStack.stack <== stack; newStack.pushpop <== addToState.out[0]; - newStack.obj_or_arr <== addToState.out[1]; + newStack.stack_val <== addToState.out[1]; next_pointer <== newStack.next_pointer; next_stack <== newStack.next_stack; next_parsing_string <== addToState.out[2]; next_parsing_number <== addToState.out[3]; - next_in_value <== addToState.out[4]; //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// @@ -147,7 +144,6 @@ template StateUpdate() { } log("next_parsing_string", "= ", next_parsing_string); log("next_parsing_number", "= ", next_parsing_number); - log("next_in_value ", " = ", next_in_value ); log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); //--------------------------------------------------------------------------------------------// @@ -213,14 +209,13 @@ template Switch(m, n) { } template StateToMask() { - signal input in[5]; - signal output out[5]; + signal input in[4]; + signal output out[4]; signal pushpop <== in[0]; - signal obj_or_array <== in[1]; + signal stack_val <== in[1]; signal parsing_string <== in[2]; signal parsing_number <== in[3]; - signal in_value <== in[4]; // `pushpop` can change: IF NOT `parsing_string` out[0] <== (1 - parsing_string); @@ -232,10 +227,7 @@ template StateToMask() { out[2] <== 1 - 2 * parsing_string; // `parsing_number` can change: - out[3] <== (1 - parsing_string); - - // `in_value` can change: - out[4] <== (1 - parsing_string) - 2 * in_value; + out[3] <== (1 - parsing_string) * (- 2 * parsing_number); } // TODO: IMPORTANT NOTE, THE STACK IS CONSTRAINED TO 2**8 so the LessThan and GreaterThan work (could be changed) @@ -245,8 +237,7 @@ template RewriteStack(n) { signal input pointer; signal input stack[n]; signal input pushpop; - signal input obj_or_arr; - + signal input stack_val; signal output next_pointer; signal output next_stack[n]; @@ -278,9 +269,9 @@ template RewriteStack(n) { isPushAt[i] <== indicator[i].out * isPush.out; // Leave the stack alone except for where we indicate change - next_stack[i] <== stack[i] + (isPushAt[i] - isPopAt[i]) * obj_or_arr; + next_stack[i] <== stack[i] + (isPushAt[i] - isPopAt[i]) * stack_val; } - + component isOverflow = GreaterThan(8); isOverflow.in[0] <== next_pointer; isOverflow.in[1] <== n; diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index a46ef5a..359a38e 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -60,35 +60,35 @@ describe("parser", () => { //--------------------------------------------------------------------------------------------// //-Delimeters---------------------------------------------------------------------------------// // - ASCII char: `{` - const start_brace = "123"; + const start_brace = 123; // - ASCII char: `}` - const end_brace = "125"; + const end_brace = 125; // - ASCII char `[` - const start_bracket = "91"; + const start_bracket = 91; // - ASCII char `]` - const end_bracket = "93"; - // - ASCII char `"` - const quote = "34"; + const end_bracket = 93; + // - ASCII char `` + const quote = 34; // - ASCII char `:` - const colon = "58"; + const colon = 58; // - ASCII char `,` - const comma = "44"; + const comma = 44; //--------------------------------------------------------------------------------------------// // White space // - ASCII char: `\n` - const newline = "10"; + const newline = 10; // - ASCII char: ` ` - const space = "32"; + const space = 32; //--------------------------------------------------------------------------------------------// // Escape // - ASCII char: `\` - const escape = "92"; + const escape = 92; //--------------------------------------------------------------------------------------------// describe("StateUpdate", () => { let circuit: WitnessTester< - ["byte", "pointer", "stack", "parsing_string", "parsing_number", "key_or_value"], - ["next_pointer", "next_stack", "next_parsing_string", "next_parsing_number", "next_key_or_value"] + ["byte", "pointer", "stack", "parsing_string", "parsing_number"], + ["next_pointer", "next_stack", "next_parsing_string", "next_parsing_number"] >; function generatePassCase(input: any, expected: any, desc: string) { @@ -121,19 +121,17 @@ describe("parser", () => { }); let init = { - byte: "0", - pointer: "0", - stack: ["0", "0", "0", "0"], - parsing_string: "0", - parsing_number: "0", - in_value: "0", + byte: 0, + pointer: 0, + stack: [0, 0, 0, 0], + parsing_string: 0, + parsing_number: 0, }; let out = { next_pointer: init.pointer, next_stack: init.stack, next_parsing_string: init.parsing_string, next_parsing_number: init.parsing_number, - next_in_value: init.in_value, }; @@ -152,8 +150,8 @@ describe("parser", () => { let read_start_brace = { ...init }; read_start_brace.byte = start_brace; let read_start_brace_out = { ...out }; - read_start_brace_out.next_pointer = "1"; - read_start_brace_out.next_stack = ["1", "0", "0", "0"]; + read_start_brace_out.next_pointer = 1; + read_start_brace_out.next_stack = [1, 0, 0, 0]; generatePassCase(read_start_brace, read_start_brace_out, ">>>> `{` read"); //-----------------------------------------------------------------------------// @@ -177,7 +175,7 @@ describe("parser", () => { let in_object_find_key_out = { ...out }; in_object_find_key_out.next_pointer = in_object_find_key.pointer; in_object_find_key_out.next_stack = in_object_find_key.stack; - in_object_find_key_out.next_parsing_string = "1"; + in_object_find_key_out.next_parsing_string = 1; generatePassCase(in_object_find_key, in_object_find_key_out, ">>>> `\"` read"); //-----------------------------------------------------------------------------// @@ -189,12 +187,12 @@ describe("parser", () => { let in_key = { ...init }; in_key.pointer = read_start_brace_out.next_pointer; in_key.stack = read_start_brace_out.next_stack; - in_key.parsing_string = "1"; + in_key.parsing_string = 1; in_key.byte = space; let in_key_out = { ...out }; in_key_out.next_pointer = in_key.pointer; in_key_out.next_stack = in_key.stack; - in_key_out.next_parsing_string = "1"; + in_key_out.next_parsing_string = 1; generatePassCase(in_key, in_key_out, ">>>> ` ` read"); //-----------------------------------------------------------------------------// @@ -206,7 +204,7 @@ describe("parser", () => { let in_key_to_exit = { ...init }; in_key_to_exit.pointer = read_start_brace_out.next_pointer; in_key_to_exit.stack = read_start_brace_out.next_stack; - in_key_to_exit.parsing_string = "1" + in_key_to_exit.parsing_string = 1 in_key_to_exit.byte = quote; let in_key_to_exit_out = { ...out }; in_key_to_exit_out.next_pointer = in_key_to_exit.pointer; @@ -222,10 +220,9 @@ describe("parser", () => { parsed_key_wait_to_parse_value.stack = read_start_brace_out.next_stack; 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_pointer = parsed_key_wait_to_parse_value.pointer; - parsed_key_wait_to_parse_value_out.next_stack = parsed_key_wait_to_parse_value.stack; - parsed_key_wait_to_parse_value_out.next_in_value = "1"; - generatePassCase(parsed_key_wait_to_parse_value, parsed_key_wait_to_parse_value_out, ">>>> `: ` read"); + parsed_key_wait_to_parse_value_out.next_pointer = 2; + parsed_key_wait_to_parse_value_out.next_stack = [1, 3, 0, 0]; + generatePassCase(parsed_key_wait_to_parse_value, parsed_key_wait_to_parse_value_out, ">>>> `:` read"); //-----------------------------------------------------------------------------// // // Test 8: `tree_depth == 1` AND parsing_value == 1` setup -> `"` is read @@ -326,8 +323,8 @@ describe("parser", () => { in_object.stack = read_start_brace_out.next_stack; in_object.byte = start_brace; let in_object_out = { ...out }; - in_object_out.next_pointer = "2"; - in_object_out.next_stack = ["1", "1", "0", "0"]; + in_object_out.next_pointer = 2; + in_object_out.next_stack = [1, 1, 0, 0]; generatePassCase(in_object, in_object_out, ">>>> `{` read"); // Test 8: Stack Management @@ -339,25 +336,21 @@ describe("parser", () => { in_object_to_leave.stack = read_start_brace_out.next_stack; in_object_to_leave.byte = end_brace; let in_object_to_leave_out = { ...out }; - in_object_to_leave_out.next_pointer = "0"; - in_object_to_leave_out.next_stack = ["0", "0", "0", "0"]; + in_object_to_leave_out.next_pointer = 0; + in_object_to_leave_out.next_stack = [0, 0, 0, 0]; generatePassCase(in_object_to_leave, in_object_to_leave_out, ">>>> `}` read"); // Test 9: Stack Management // init: read `{`, then read `[` // expect: pointer --> 2 - // stack --> [1,-1,0,0] + // stack --> [1,2,0,0] let in_object_to_read_start_bracket = { ...init }; in_object_to_read_start_bracket.byte = start_bracket; - in_object_to_read_start_bracket.pointer = "1"; - in_object_to_read_start_bracket.stack = ["1", "0", "0", "0"]; + in_object_to_read_start_bracket.pointer = 1; + in_object_to_read_start_bracket.stack = [1, 0, 0, 0]; let in_object_to_read_start_bracket_out = { ...out }; - in_object_to_read_start_bracket_out.next_pointer = "2"; - in_object_to_read_start_bracket_out.next_stack = - ["1", - "21888242871839275222246405745257275088548364400416034343698204186575808495616", - "0", - "0"]; + in_object_to_read_start_bracket_out.next_pointer = 2; + in_object_to_read_start_bracket_out.next_stack = [1, 2, 0, 0]; generatePassCase(in_object_to_read_start_bracket, in_object_to_read_start_bracket_out, ">>>> `[` read"); // Test 10: Stack Management @@ -366,8 +359,8 @@ describe("parser", () => { // stack --> [1,1,1,1] let in_max_stack = { ...init }; in_max_stack.byte = start_brace; - in_max_stack.pointer = "4"; - in_max_stack.stack = ["1", "1", "1", "1"]; + in_max_stack.pointer = 4; + in_max_stack.stack = [1, 1, 1, 1]; generateFailCase(in_max_stack, ">>>> `{` read --> (stack overflow)"); // Test 11: Stack Management @@ -376,9 +369,35 @@ describe("parser", () => { // stack --> [1,1,1,1] let in_max_stack_2 = { ...init }; in_max_stack_2.byte = start_bracket; - in_max_stack_2.pointer = "4"; - in_max_stack_2.stack = ["1", "1", "1", "1"]; + in_max_stack_2.pointer = 4; + in_max_stack_2.stack = [1, 1, 1, 1]; generateFailCase(in_max_stack, ">>>> `[` read --> (stack overflow)"); + + // Test 12: Stack Management + // init: read `{` and `[`, then read `]` + // expect: pointer --> 2 + // stack --> [1,0,0,0] + let in_object_and_array = { ...init }; + in_object_and_array.byte = end_bracket; + in_object_and_array.pointer = 2; + in_object_and_array.stack = [1, 2, 0, 0]; + let in_object_and_array_out = { ...out }; + in_object_and_array_out.next_pointer = 1; + in_object_and_array_out.next_stack = [1, 0, 0, 0]; + generatePassCase(in_object_and_array, in_object_and_array_out, ">>>> `[` read"); + + // Test 12: Stack Management + // init: read `{` and `:`, then read `,` + // expect: pointer --> 2 + // stack --> [1,0,0,0] + let in_object_and_value = { ...init }; + in_object_and_value.byte = comma; + in_object_and_value.pointer = 2; + in_object_and_value.stack = [1, 3, 0, 0]; + let in_object_and_value_out = { ...out }; + in_object_and_value_out.next_pointer = 1; + in_object_and_value_out.next_stack = [1, 0, 0, 0]; + generatePassCase(in_object_and_value, in_object_and_value_out, ">>>> `[` read"); }); }); From 088490921db6b076e2a1de5a9743007d802ad673 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Mon, 12 Aug 2024 10:33:31 -0500 Subject: [PATCH 48/99] wip: handle end of arr/obj --- circuits/extract.circom | 12 --------- circuits/parser.circom | 59 ++++++++++++++++++++++++++++++++--------- 2 files changed, 46 insertions(+), 25 deletions(-) diff --git a/circuits/extract.circom b/circuits/extract.circom index 3e6900d..5767407 100644 --- a/circuits/extract.circom +++ b/circuits/extract.circom @@ -22,10 +22,7 @@ template Extract(DATA_BYTES) { State[0].pointer <== 0; State[0].stack <== [0,0,0,0]; State[0].parsing_string <== 0; - State[0].parsing_array <== 0; - State[0].parsing_object <== 0; State[0].parsing_number <== 0; - State[0].key_or_value <== 0; for(var data_idx = 1; data_idx < DATA_BYTES; data_idx++) { State[data_idx] = StateUpdate(); @@ -33,10 +30,7 @@ template Extract(DATA_BYTES) { State[data_idx].pointer <== State[data_idx - 1].next_pointer; State[data_idx].stack <== State[data_idx - 1].next_stack; State[data_idx].parsing_string <== State[data_idx - 1].next_parsing_string; - State[data_idx].parsing_array <== State[data_idx - 1].next_parsing_array; - State[data_idx].parsing_object <== State[data_idx - 1].next_parsing_object; State[data_idx].parsing_number <== State[data_idx - 1].next_parsing_number; - State[data_idx].key_or_value <== State[data_idx - 1].next_key_or_value; // Debugging log("State[", data_idx, "].pointer ", "= ", State[data_idx].pointer); @@ -44,10 +38,7 @@ template Extract(DATA_BYTES) { log("State[", data_idx, "].stack[", i,"] ", "= ", State[data_idx].stack[i]); } log("State[", data_idx, "].parsing_string", "= ", State[data_idx].parsing_string); - log("State[", data_idx, "].parsing_array ", "= ", State[data_idx].parsing_array ); - log("State[", data_idx, "].parsing_object", "= ", State[data_idx].parsing_object); log("State[", data_idx, "].parsing_number", "= ", State[data_idx].parsing_number); - log("State[", data_idx, "].key_or_value ", "= ", State[data_idx].key_or_value ); log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); } @@ -60,9 +51,6 @@ template Extract(DATA_BYTES) { log("State[", DATA_BYTES, "].stack[", i,"] ", "= ", State[DATA_BYTES -1 ].next_stack[i]); } log("State[", DATA_BYTES, "].parsing_string", "= ", State[DATA_BYTES-1].next_parsing_string); - log("State[", DATA_BYTES, "].parsing_array ", "= ", State[DATA_BYTES-1].next_parsing_array ); - log("State[", DATA_BYTES, "].parsing_object", "= ", State[DATA_BYTES-1].next_parsing_object); log("State[", DATA_BYTES, "].parsing_number", "= ", State[DATA_BYTES-1].next_parsing_number); - log("State[", DATA_BYTES, "].key_or_value ", "= ", State[DATA_BYTES-1].next_key_or_value ); log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); } \ No newline at end of file diff --git a/circuits/parser.circom b/circuits/parser.circom index ccff4b3..a417215 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -85,12 +85,12 @@ template StateUpdate() { var parsing_state[4] = [pushpop, stack_val, parsing_string, parsing_number]; 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 hit_start_brace[4] = [1, 1, 0, 0 ]; // Command returned by switch if we hit a start brace `{` - var hit_end_brace[4] = [-1, 1, 0, 0 ]; // Command returned by switch if we hit a end brace `}` + var hit_end_brace[4] = [-1, -1, 0, 0 ]; // Command returned by switch if we hit a end brace `}` var hit_start_bracket[4] = [1, 2, 0, 0 ]; // TODO: Might want `in_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) - var hit_end_bracket[4] = [-1, 2, 0, 0 ]; // Command returned by switch if we hit a start bracket `]` + var hit_end_bracket[4] = [-1, -2, 0, 0 ]; // Command returned by switch if we hit a start bracket `]` var hit_quote[4] = [0, 0, 1, 0 ]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` var hit_colon[4] = [1, 3, 0, 0 ]; // Command returned by switch if we hit a colon `:` - var hit_comma[4] = [-1, 3, 0, -1 ]; // Command returned by switch if we hit a comma `,` + var hit_comma[4] = [-1, -3, 0, -1 ]; // Command returned by switch if we hit a comma `,` var hit_number[4] = [0, 0, 0, 1 ]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) //--------------------------------------------------------------------------------------------// @@ -138,13 +138,13 @@ template StateUpdate() { // log(">>>> addToState[",i,"] : ", addToState.out[i]); // } // Debugging - log("next_pointer ", "= ", next_pointer); - for(var i = 0; i<4; i++) { - log("next_stack[", i,"] ", "= ", next_stack[i]); - } - log("next_parsing_string", "= ", next_parsing_string); - log("next_parsing_number", "= ", next_parsing_number); - log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + // log("next_pointer ", "= ", next_pointer); + // for(var i = 0; i<4; i++) { + // log("next_stack[", i,"] ", "= ", next_stack[i]); + // } + // log("next_parsing_string", "= ", next_parsing_string); + // log("next_parsing_number", "= ", next_parsing_number); + // log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// @@ -220,7 +220,8 @@ template StateToMask() { // `pushpop` can change: IF NOT `parsing_string` out[0] <== (1 - parsing_string); - // `val_or_array`: IF NOT `parsing_string` + // `stack_val`: IF NOT `parsing_string` OR + // TODO: `parsing_array` out[1] <== (1 - parsing_string); // `parsing_string` can change: @@ -248,6 +249,22 @@ template RewriteStack(n) { - if pushpop is 0, we are going to just return the old stack - if pushpop is 1, we are going to increment the pointer and write a new value - if pushpop is -1, we are going to decrement the pointer and delete an old value if it was the same value + + TODO: There's the weird case of "no trailing commas" for KVs in JSON. + This constitutes valid JSON, fortunately, and is NOT optional. Or, at least, + we should NOT consider it to be for this current impl. + Basically, JSON must be like: + ``` + { + "a": "valA", + "b": "valB" + } + ``` + so there is the one end here where we have to go from: + stack == [1,3,0,0,...] + to + next_stack == [0,0,0,0,...] + on the case we get a POP instruction reading an object OR an array (no trailing commas in arrays either) */ next_pointer <== pointer + pushpop; // If pushpop is 0, pointer doesn't change, if -1, decrement, +1 increment @@ -261,15 +278,31 @@ template RewriteStack(n) { signal isPopAt[n]; signal isPushAt[n]; + // TODO: Thinking of it this way + component isEndOfArrayOrObject[n]; + // top of stack is a 3, then we need to pop off 3, and check the value underneath + // is correct match (i.e., a brace or bracket (1 or 2)) + for(var i = 0; i < n; i++) { + // Points to top of stack if POP indicator[i] = IsZero(); - indicator[i].in <== pointer - isPop.out - i; + indicator[i].in <== pointer - isPop.out - i; + // Indicators for index to PUSH to or POP from isPopAt[i] <== indicator[i].out * isPop.out; isPushAt[i] <== indicator[i].out * isPush.out; + + // NEW STUFF -------------------------------------------// + // If this is TRUE, then we are POP and the top is a `:` marker (3) + isEndOfArrayOrObject[i] = IsZero(); + isEndOfArrayOrObject[i].in <== stack[i] * indicator[i].out - 3; + // Therefore, if we are hitting a stack value + //------------------------------------------------------// // Leave the stack alone except for where we indicate change - next_stack[i] <== stack[i] + (isPushAt[i] - isPopAt[i]) * stack_val; + next_stack[i] <== stack[i] + (isPushAt[i] + isPopAt[i]) * stack_val; + + // TODO: Constrain next_stack entries to be 0,1,2,3 } component isOverflow = GreaterThan(8); From 780e8c24e52efb67090cc4fd49c464236588f447 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Mon, 12 Aug 2024 12:10:11 -0500 Subject: [PATCH 49/99] maybe progress --- circuits/parser.circom | 36 ++++++++++++++++++++++++++++++------ circuits/test/parser.test.ts | 19 ++++++++++++++++--- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index a417215..f661928 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -267,8 +267,6 @@ template RewriteStack(n) { on the case we get a POP instruction reading an object OR an array (no trailing commas in arrays either) */ - next_pointer <== pointer + pushpop; // If pushpop is 0, pointer doesn't change, if -1, decrement, +1 increment - // Indicate which position in the stack should change (if any) component isPop = IsZero(); isPop.in <== pushpop + 1; @@ -280,31 +278,57 @@ template RewriteStack(n) { // TODO: Thinking of it this way component isEndOfArrayOrObject[n]; + component doublePopIndicator[n]; + signal doublePopAt[n]; + signal stackModifier[n]; // top of stack is a 3, then we need to pop off 3, and check the value underneath // is correct match (i.e., a brace or bracket (1 or 2)) for(var i = 0; i < n; i++) { - // Points to top of stack if POP + // Points to top of stack if POP else it points to unallocated position indicator[i] = IsZero(); indicator[i].in <== pointer - isPop.out - i; // Indicators for index to PUSH to or POP from isPopAt[i] <== indicator[i].out * isPop.out; + log("isPopAt[",i,"]: ", isPopAt[i]); isPushAt[i] <== indicator[i].out * isPush.out; // NEW STUFF -------------------------------------------// // If this is TRUE, then we are POP and the top is a `:` marker (3) isEndOfArrayOrObject[i] = IsZero(); - isEndOfArrayOrObject[i].in <== stack[i] * indicator[i].out - 3; - // Therefore, if we are hitting a stack value + isEndOfArrayOrObject[i].in <== stack[i] * isPopAt[i] - 3; + log("isEndOfArrayOrObject[",i,"]: ", isEndOfArrayOrObject[i].out); + // --> If this, then if stack_val = -1 or -2, we clear this + // --> Then we need to constrain the next lower loc in the stack is +1 or +2 resp. + + // Where this is TRUE is the second value to pop off, else it is just at the pointer + doublePopIndicator[i] = IsZero(); + doublePopIndicator[i].in <== pointer - 2 * isPop.out - i; + log("doublePopIndicator[",i,"]: ", doublePopIndicator[i].out); + + // Double pop at the correct loc IF we at end of array or object + doublePopAt[i] <== stack[i] * doublePopIndicator[i].out; + log("doublePopAt[",i,"]: ", doublePopAt[i]); + + log("---"); + // Therefore, if we get a comma, we don't want to do anything different + // But if it is a } or a ], we pop off the 3, and look at next stack value + // to see if it is correct (`{` or `]`), and clear that off too if it is + //------------------------------------------------------// // Leave the stack alone except for where we indicate change - next_stack[i] <== stack[i] + (isPushAt[i] + isPopAt[i]) * stack_val; + stackModifier[i] <== (stack[i] + (isPushAt[i] + isPopAt[i]) * stack_val) ; + next_stack[i] <== stackModifier[i] * (1 - isEndOfArrayOrObject[i].out); + // is 1 if we read a `:` ^^^^^^^^^^^^^^^^^^^^^ // TODO: Constrain next_stack entries to be 0,1,2,3 } + // TODO: This decrements by 2 if we hit a ] or } when the top of stack is 3 + next_pointer <== pointer + pushpop; // If pushpop is 0, pointer doesn't change, if -1, decrement, +1 increment + component isOverflow = GreaterThan(8); isOverflow.in[0] <== next_pointer; isOverflow.in[1] <== n; diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index 359a38e..a983a03 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -384,12 +384,12 @@ describe("parser", () => { let in_object_and_array_out = { ...out }; in_object_and_array_out.next_pointer = 1; in_object_and_array_out.next_stack = [1, 0, 0, 0]; - generatePassCase(in_object_and_array, in_object_and_array_out, ">>>> `[` read"); + generatePassCase(in_object_and_array, in_object_and_array_out, ">>>> `]` read"); // Test 12: Stack Management // init: read `{` and `:`, then read `,` // expect: pointer --> 2 - // stack --> [1,0,0,0] + // stack --> [1,3,0,0] let in_object_and_value = { ...init }; in_object_and_value.byte = comma; in_object_and_value.pointer = 2; @@ -397,7 +397,20 @@ describe("parser", () => { let in_object_and_value_out = { ...out }; in_object_and_value_out.next_pointer = 1; in_object_and_value_out.next_stack = [1, 0, 0, 0]; - generatePassCase(in_object_and_value, in_object_and_value_out, ">>>> `[` read"); + generatePassCase(in_object_and_value, in_object_and_value_out, ">>>> `,` read"); + + // Test 13: Stack Management + // init: read `{` and `:`, then read `,` + // expect: pointer --> 2 + // stack --> [1,3,0,0] + let in_object_and_value_to_leave_object = { ...init }; + in_object_and_value_to_leave_object.byte = end_brace; + in_object_and_value_to_leave_object.pointer = 2; + in_object_and_value_to_leave_object.stack = [1, 3, 0, 0]; + let in_object_and_value_to_leave_object_out = { ...out }; + in_object_and_value_to_leave_object_out.next_pointer = 0; + in_object_and_value_to_leave_object_out.next_stack = [0, 0, 0, 0]; + generatePassCase(in_object_and_value_to_leave_object, in_object_and_value_to_leave_object_out, ">>>> `,` read"); }); }); From f1c674cca7bfc3527c5afdf64d4f907f079ffbaa Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Mon, 12 Aug 2024 13:56:00 -0500 Subject: [PATCH 50/99] save --- circuits/parser.circom | 82 +++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 28 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index f661928..dafde6e 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -272,56 +272,82 @@ template RewriteStack(n) { isPop.in <== pushpop + 1; component isPush = IsZero(); isPush.in <== pushpop - 1; + component prev_indicator[n]; component indicator[n]; signal isPopAt[n]; signal isPushAt[n]; - // TODO: Thinking of it this way - component isEndOfArrayOrObject[n]; - component doublePopIndicator[n]; - signal doublePopAt[n]; - signal stackModifier[n]; + component readEndChar = IsZero(); + readEndChar.in <== (stack_val + 1) * (stack_val + 2); + // top of stack is a 3, then we need to pop off 3, and check the value underneath // is correct match (i.e., a brace or bracket (1 or 2)) + // component toDoublePop = IsZero(); + + signal accum[n]; + for(var i = 0; i < n; i++) { + // points to 1 value back + prev_indicator[i] = IsZero(); + prev_indicator[i].in <== pointer - 2 * isPop.out - i; + log("prev_indicator[",i,"]: ", prev_indicator[i].out); + // Points to top of stack if POP else it points to unallocated position indicator[i] = IsZero(); - indicator[i].in <== pointer - isPop.out - i; + indicator[i].in <== pointer - isPop.out - i; + log("indicator[",i,"]: ", indicator[i].out); + + // TODO: `isDoublePop` will be true IF (stack[i] * indicator[i] == 3) AND readEndChar + accum[i] <== stack[i] * indicator[i].out; + } + + var next_accum = 0; + for(var i = 0; i < n; i++) { + next_accum += accum[i]; + } + + component atColon = IsZero(); + atColon.in <== next_accum - 3; + + log("atColon = ", atColon.out); + + // TODO: Now we can say `IF at_colon AND readEndChar` THEN doublePop! + + for(var i = 0; i < n; i++) { // Indicators for index to PUSH to or POP from - isPopAt[i] <== indicator[i].out * isPop.out; + // TODO: make potentially two values here enabled if we need to pop twice + isPopAt[i] <== indicator[i].out * isPop.out; // want to add: `prev_indicator[i] * isDoublePop` log("isPopAt[",i,"]: ", isPopAt[i]); isPushAt[i] <== indicator[i].out * isPush.out; - // NEW STUFF -------------------------------------------// - // If this is TRUE, then we are POP and the top is a `:` marker (3) - isEndOfArrayOrObject[i] = IsZero(); - isEndOfArrayOrObject[i].in <== stack[i] * isPopAt[i] - 3; - log("isEndOfArrayOrObject[",i,"]: ", isEndOfArrayOrObject[i].out); - // --> If this, then if stack_val = -1 or -2, we clear this - // --> Then we need to constrain the next lower loc in the stack is +1 or +2 resp. + // // NEW STUFF -------------------------------------------// + // // If this is TRUE, then we are POP and the top is a `:` marker (3) + // isEndOfArrayOrObject[i] = IsZero(); + // isEndOfArrayOrObject[i].in <== stack[i] * isPopAt[i] - 3; + // log("isEndOfArrayOrObject[",i,"]: ", isEndOfArrayOrObject[i].out); + // // --> If this, then if stack_val = -1 or -2, we clear this + // // --> Then we need to constrain the next lower loc in the stack is +1 or +2 resp. - // Where this is TRUE is the second value to pop off, else it is just at the pointer - doublePopIndicator[i] = IsZero(); - doublePopIndicator[i].in <== pointer - 2 * isPop.out - i; - log("doublePopIndicator[",i,"]: ", doublePopIndicator[i].out); + // // Where this is TRUE is the second value to pop off, else it is just at the pointer + // doublePopIndicator[i] = IsZero(); + // doublePopIndicator[i].in <== (pointer - 2 * isPop.out - i); + // log("doublePopIndicator[",i,"]: ", doublePopIndicator[i].out); - // Double pop at the correct loc IF we at end of array or object - doublePopAt[i] <== stack[i] * doublePopIndicator[i].out; - log("doublePopAt[",i,"]: ", doublePopAt[i]); + // // Double pop at the correct loc IF we at end of array or object + // doublePopAt[i] <== (1 - readEndChar.out) * doublePopIndicator[i].out; + // log("doublePopAt[",i,"]: ", doublePopAt[i]); - log("---"); - // Therefore, if we get a comma, we don't want to do anything different - // But if it is a } or a ], we pop off the 3, and look at next stack value - // to see if it is correct (`{` or `]`), and clear that off too if it is + // log("---"); + // // Therefore, if we get a comma, we don't want to do anything different + // // But if it is a } or a ], we pop off the 3, and look at next stack value + // // to see if it is correct (`{` or `]`), and clear that off too if it is //------------------------------------------------------// // Leave the stack alone except for where we indicate change - stackModifier[i] <== (stack[i] + (isPushAt[i] + isPopAt[i]) * stack_val) ; - next_stack[i] <== stackModifier[i] * (1 - isEndOfArrayOrObject[i].out); - // is 1 if we read a `:` ^^^^^^^^^^^^^^^^^^^^^ + next_stack[i] <== (stack[i] + (isPushAt[i] + isPopAt[i]) * stack_val); // TODO: Constrain next_stack entries to be 0,1,2,3 } From cb7fa98eebdb7eb68101e92853cf7bb17c1d70c3 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Mon, 12 Aug 2024 15:34:41 -0600 Subject: [PATCH 51/99] almost! --- circuits/parser.circom | 50 ++++++++++++++---------------------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index dafde6e..75127c6 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -138,13 +138,13 @@ template StateUpdate() { // log(">>>> addToState[",i,"] : ", addToState.out[i]); // } // Debugging - // log("next_pointer ", "= ", next_pointer); - // for(var i = 0; i<4; i++) { - // log("next_stack[", i,"] ", "= ", next_stack[i]); - // } - // log("next_parsing_string", "= ", next_parsing_string); - // log("next_parsing_number", "= ", next_parsing_number); - // log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + log("next_pointer ", "= ", next_pointer); + for(var i = 0; i<4; i++) { + log("next_stack[", i,"] ", "= ", next_stack[i]); + } + log("next_parsing_string", "= ", next_parsing_string); + log("next_parsing_number", "= ", next_parsing_number); + log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// @@ -309,51 +309,33 @@ template RewriteStack(n) { component atColon = IsZero(); atColon.in <== next_accum - 3; + signal isDoublePop <== atColon.out * readEndChar.out; log("atColon = ", atColon.out); + log("isDoublePop = ", isDoublePop); // TODO: Now we can say `IF at_colon AND readEndChar` THEN doublePop! + signal isPopAtPrev[n]; + signal singleOrDoublePop[n]; for(var i = 0; i < n; i++) { // Indicators for index to PUSH to or POP from // TODO: make potentially two values here enabled if we need to pop twice - isPopAt[i] <== indicator[i].out * isPop.out; // want to add: `prev_indicator[i] * isDoublePop` + isPopAtPrev[i] <== prev_indicator[i].out * isDoublePop; // temp signal + isPopAt[i] <== isPopAtPrev[i] + indicator[i].out * isPop.out; // want to add: `prev_indicator[i] * isDoublePop` log("isPopAt[",i,"]: ", isPopAt[i]); + isPushAt[i] <== indicator[i].out * isPush.out; - // // NEW STUFF -------------------------------------------// - // // If this is TRUE, then we are POP and the top is a `:` marker (3) - // isEndOfArrayOrObject[i] = IsZero(); - // isEndOfArrayOrObject[i].in <== stack[i] * isPopAt[i] - 3; - // log("isEndOfArrayOrObject[",i,"]: ", isEndOfArrayOrObject[i].out); - // // --> If this, then if stack_val = -1 or -2, we clear this - // // --> Then we need to constrain the next lower loc in the stack is +1 or +2 resp. - - // // Where this is TRUE is the second value to pop off, else it is just at the pointer - // doublePopIndicator[i] = IsZero(); - // doublePopIndicator[i].in <== (pointer - 2 * isPop.out - i); - // log("doublePopIndicator[",i,"]: ", doublePopIndicator[i].out); - - // // Double pop at the correct loc IF we at end of array or object - // doublePopAt[i] <== (1 - readEndChar.out) * doublePopIndicator[i].out; - // log("doublePopAt[",i,"]: ", doublePopAt[i]); - - // log("---"); - // // Therefore, if we get a comma, we don't want to do anything different - // // But if it is a } or a ], we pop off the 3, and look at next stack value - // // to see if it is correct (`{` or `]`), and clear that off too if it is - - //------------------------------------------------------// - // Leave the stack alone except for where we indicate change - next_stack[i] <== (stack[i] + (isPushAt[i] + isPopAt[i]) * stack_val); + next_stack[i] <== stack[i] + (isPushAt[i] + isPopAt[i]) * stack_val; // TODO: Constrain next_stack entries to be 0,1,2,3 } // TODO: This decrements by 2 if we hit a ] or } when the top of stack is 3 - next_pointer <== pointer + pushpop; // If pushpop is 0, pointer doesn't change, if -1, decrement, +1 increment + next_pointer <== pointer + (1 + isDoublePop) * pushpop; // If pushpop is 0, pointer doesn't change, if -1, decrement, +1 increment component isOverflow = GreaterThan(8); isOverflow.in[0] <== next_pointer; From 59fdcf13c55ad6bdd9e7802daf3ce88aed1a24f0 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Mon, 12 Aug 2024 16:02:03 -0600 Subject: [PATCH 52/99] tests passing --- circuits/parser.circom | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 75127c6..4ddc7d2 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -291,12 +291,12 @@ template RewriteStack(n) { // points to 1 value back prev_indicator[i] = IsZero(); prev_indicator[i].in <== pointer - 2 * isPop.out - i; - log("prev_indicator[",i,"]: ", prev_indicator[i].out); + // log("prev_indicator[",i,"]: ", prev_indicator[i].out); // Points to top of stack if POP else it points to unallocated position indicator[i] = IsZero(); indicator[i].in <== pointer - isPop.out - i; - log("indicator[",i,"]: ", indicator[i].out); + // log("indicator[",i,"]: ", indicator[i].out); // TODO: `isDoublePop` will be true IF (stack[i] * indicator[i] == 3) AND readEndChar accum[i] <== stack[i] * indicator[i].out; @@ -311,25 +311,43 @@ template RewriteStack(n) { atColon.in <== next_accum - 3; signal isDoublePop <== atColon.out * readEndChar.out; - log("atColon = ", atColon.out); - log("isDoublePop = ", isDoublePop); + // log("atColon = ", atColon.out); + // log("isDoublePop = ", isDoublePop); // TODO: Now we can say `IF at_colon AND readEndChar` THEN doublePop! signal isPopAtPrev[n]; + signal isNotPopAtPrev[n]; signal singleOrDoublePop[n]; + signal push_val[n]; + signal second_pop_val[n]; + signal first_pop_val[n]; + signal temp_val[n]; + for(var i = 0; i < n; i++) { // Indicators for index to PUSH to or POP from // TODO: make potentially two values here enabled if we need to pop twice isPopAtPrev[i] <== prev_indicator[i].out * isDoublePop; // temp signal - isPopAt[i] <== isPopAtPrev[i] + indicator[i].out * isPop.out; // want to add: `prev_indicator[i] * isDoublePop` + log("isPopAtPrev[",i,"]: ", isPopAtPrev[i]); + isNotPopAtPrev[i] <== indicator[i].out * (1-isDoublePop); + log("isNotPopAtPrev[",i,"]: ", isNotPopAtPrev[i]); + isPopAt[i] <== indicator[i].out * isPop.out; // want to add: `prev_indicator[i] * isDoublePop` log("isPopAt[",i,"]: ", isPopAt[i]); isPushAt[i] <== indicator[i].out * isPush.out; // Leave the stack alone except for where we indicate change - next_stack[i] <== stack[i] + (isPushAt[i] + isPopAt[i]) * stack_val; + second_pop_val[i] <== isPopAtPrev[i] * stack_val; + temp_val[i] <== stack_val - (3 + stack_val) * isDoublePop; + first_pop_val[i] <== isPopAt[i] * temp_val[i]; // = isPopAt[i] * (stack_val * (1 - isDoublePop) - 3 * isDoublePop) + push_val[i] <== isPushAt[i] * stack_val; + + log("first_pop_val[",i,"]: ", first_pop_val[i]); + log("second_pop_val[",i,"]: ", second_pop_val[i]); + log("push_val[",i,"]: ", push_val[i]); + + next_stack[i] <== stack[i] + push_val[i] + first_pop_val[i] + second_pop_val[i]; // TODO: Constrain next_stack entries to be 0,1,2,3 } From c1ac3ea73e8b27889ae2a924c3b64b32f5b1e211 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Mon, 12 Aug 2024 16:05:28 -0600 Subject: [PATCH 53/99] cleaning --- circuits/parser.circom | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 4ddc7d2..00f0fd6 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -283,22 +283,18 @@ template RewriteStack(n) { // top of stack is a 3, then we need to pop off 3, and check the value underneath // is correct match (i.e., a brace or bracket (1 or 2)) - // component toDoublePop = IsZero(); signal accum[n]; for(var i = 0; i < n; i++) { - // points to 1 value back + // points to 1 value back from top prev_indicator[i] = IsZero(); prev_indicator[i].in <== pointer - 2 * isPop.out - i; - // log("prev_indicator[",i,"]: ", prev_indicator[i].out); // Points to top of stack if POP else it points to unallocated position indicator[i] = IsZero(); indicator[i].in <== pointer - isPop.out - i; - // log("indicator[",i,"]: ", indicator[i].out); - // TODO: `isDoublePop` will be true IF (stack[i] * indicator[i] == 3) AND readEndChar accum[i] <== stack[i] * indicator[i].out; } @@ -311,14 +307,7 @@ template RewriteStack(n) { atColon.in <== next_accum - 3; signal isDoublePop <== atColon.out * readEndChar.out; - // log("atColon = ", atColon.out); - // log("isDoublePop = ", isDoublePop); - - // TODO: Now we can say `IF at_colon AND readEndChar` THEN doublePop! signal isPopAtPrev[n]; - signal isNotPopAtPrev[n]; - signal singleOrDoublePop[n]; - signal push_val[n]; signal second_pop_val[n]; signal first_pop_val[n]; signal temp_val[n]; @@ -327,13 +316,8 @@ template RewriteStack(n) { for(var i = 0; i < n; i++) { // Indicators for index to PUSH to or POP from - // TODO: make potentially two values here enabled if we need to pop twice isPopAtPrev[i] <== prev_indicator[i].out * isDoublePop; // temp signal - log("isPopAtPrev[",i,"]: ", isPopAtPrev[i]); - isNotPopAtPrev[i] <== indicator[i].out * (1-isDoublePop); - log("isNotPopAtPrev[",i,"]: ", isNotPopAtPrev[i]); isPopAt[i] <== indicator[i].out * isPop.out; // want to add: `prev_indicator[i] * isDoublePop` - log("isPopAt[",i,"]: ", isPopAt[i]); isPushAt[i] <== indicator[i].out * isPush.out; @@ -341,13 +325,8 @@ template RewriteStack(n) { second_pop_val[i] <== isPopAtPrev[i] * stack_val; temp_val[i] <== stack_val - (3 + stack_val) * isDoublePop; first_pop_val[i] <== isPopAt[i] * temp_val[i]; // = isPopAt[i] * (stack_val * (1 - isDoublePop) - 3 * isDoublePop) - push_val[i] <== isPushAt[i] * stack_val; - - log("first_pop_val[",i,"]: ", first_pop_val[i]); - log("second_pop_val[",i,"]: ", second_pop_val[i]); - log("push_val[",i,"]: ", push_val[i]); - next_stack[i] <== stack[i] + push_val[i] + first_pop_val[i] + second_pop_val[i]; + next_stack[i] <== stack[i] + isPushAt[i] * stack_val + first_pop_val[i] + second_pop_val[i]; // TODO: Constrain next_stack entries to be 0,1,2,3 } From b86253565e3b25e1b5dac93b00a1e9b3e496b575 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Mon, 12 Aug 2024 16:15:34 -0600 Subject: [PATCH 54/99] wip: big example --- circuits.json | 3 +-- circuits/extract.circom | 12 ++++++++---- circuits/parser.circom | 22 +++++++++++----------- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/circuits.json b/circuits.json index e9f5d76..dff3ce2 100644 --- a/circuits.json +++ b/circuits.json @@ -52,11 +52,10 @@ 48 ] }, - "extract": { + "extract_example": { "file": "extract", "template": "Extract", "params": [ - 10, 787 ] } diff --git a/circuits/extract.circom b/circuits/extract.circom index 5767407..fd8b175 100644 --- a/circuits/extract.circom +++ b/circuits/extract.circom @@ -15,17 +15,21 @@ template Extract(DATA_BYTES) { component dataASCII = ASCII(DATA_BYTES); dataASCII.in <== data; //--------------------------------------------------------------------------------------------// + var MAX_STACK_HEIGHT = 16; // Initialze the parser component State[DATA_BYTES]; - State[0] = StateUpdate(); + State[0] = StateUpdate(MAX_STACK_HEIGHT); State[0].byte <== data[0]; State[0].pointer <== 0; - State[0].stack <== [0,0,0,0]; + for(var i = 0; i < MAX_STACK_HEIGHT; i++) { + State[0].stack[i] <== 0; + } + // State[0].stack <== [0,0,0,0,0,0,0,0,0,0,0,0]; State[0].parsing_string <== 0; State[0].parsing_number <== 0; for(var data_idx = 1; data_idx < DATA_BYTES; data_idx++) { - State[data_idx] = StateUpdate(); + State[data_idx] = StateUpdate(MAX_STACK_HEIGHT); State[data_idx].byte <== data[data_idx]; State[data_idx].pointer <== State[data_idx - 1].next_pointer; State[data_idx].stack <== State[data_idx - 1].next_stack; @@ -34,7 +38,7 @@ template Extract(DATA_BYTES) { // Debugging log("State[", data_idx, "].pointer ", "= ", State[data_idx].pointer); - for(var i = 0; i<4; i++) { + for(var i = 0; i>>> addToState[",i,"] : ", addToState.out[i]); // } // Debugging - log("next_pointer ", "= ", next_pointer); - for(var i = 0; i<4; i++) { - log("next_stack[", i,"] ", "= ", next_stack[i]); - } - log("next_parsing_string", "= ", next_parsing_string); - log("next_parsing_number", "= ", next_parsing_number); - log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + // log("next_pointer ", "= ", next_pointer); + // for(var i = 0; i<4; i++) { + // log("next_stack[", i,"] ", "= ", next_stack[i]); + // } + // log("next_parsing_string", "= ", next_parsing_string); + // log("next_parsing_number", "= ", next_parsing_number); + // log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// From 2b283a8207b14c97ea7c936c61eb1edb460449e9 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Mon, 12 Aug 2024 16:33:34 -0600 Subject: [PATCH 55/99] bug: fails to parse example.json --- circuits.json | 2 +- json_examples/example.json | 47 +++++++++++++++++------------------ json_examples/example_json.md | 24 ++++++++++++++++++ 3 files changed, 48 insertions(+), 25 deletions(-) create mode 100644 json_examples/example_json.md diff --git a/circuits.json b/circuits.json index dff3ce2..6d656bb 100644 --- a/circuits.json +++ b/circuits.json @@ -56,7 +56,7 @@ "file": "extract", "template": "Extract", "params": [ - 787 + 157 ] } } \ No newline at end of file diff --git a/json_examples/example.json b/json_examples/example.json index c8eef94..8664af9 100644 --- a/json_examples/example.json +++ b/json_examples/example.json @@ -1,25 +1,24 @@ -{ - "glossary": { - "title": "example glossary", - "GlossDiv": { - "title": "S", - "GlossList": { - "GlossEntry": { - "ID": "SGML", - "SortAs": "SGML", - "GlossTerm": "Standard Generalized Markup Language", - "Acronym": "SGML", - "Abbrev": "ISO 8879:1986", - "GlossDef": { - "para": "A meta-markup language, used to create markup languages such as DocBook.", - "GlossSeeAlso": [ - "GML", - "XML" - ] - }, - "GlossSee": "markup" - } - } - } - } +{ "a": +{ "b": "c", +"d": { +"e": "f", +"g": { +"h": { +"i": "j", +"k": "l", +"m": "n", +"o": "p", +"q": "r", +"s": { +"t": "u", +"v": [ +"w", +"x" +] +}, +"y": "z" +} +} +} +} } \ No newline at end of file diff --git a/json_examples/example_json.md b/json_examples/example_json.md new file mode 100644 index 0000000..90ee1c3 --- /dev/null +++ b/json_examples/example_json.md @@ -0,0 +1,24 @@ +{ "a": // 7 +{ "b": "c", // 19 +"d": { // 25 +"e": "f", // 35 +"g": { // 42 +"h": { // 48 +"i": "j", +"k": "l", +"m": "n", +"o": "p", +"q": "r", +"s": { +"t": "u", +"v": [ +"w", +"x" +] +}, // 138 +"y": "z" // 147 +} // 149 +} // 151 +} // 153 +} // 155 +} // 157 \ No newline at end of file From 762992c9d187dca9a5aec7d9dafc4d3af9ff6aae Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Mon, 12 Aug 2024 16:59:07 -0600 Subject: [PATCH 56/99] add basic switch template again --- circuits/parser.circom | 42 +++++++++++++++++++++--- circuits/test/parser.test.ts | 63 ++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 4 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 2912701..4638b46 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -97,7 +97,7 @@ template StateUpdate(MAX_STACK_HEIGHT) { //--------------------------------------------------------------------------------------------// //-State machine updating---------------------------------------------------------------------// // * yield instruction based on what byte we read * - component matcher = Switch(8, 4); + component matcher = SwitchArray(8, 4); var number = 256; // Number beyond a byte to represent an ASCII numeral matcher.branches <== [start_brace, end_brace, quote, colon, comma, start_bracket, end_bracket, number ]; matcher.vals <== [hit_start_brace, hit_end_brace, hit_quote, hit_colon, hit_comma, hit_start_bracket, hit_end_bracket, hit_number]; @@ -106,8 +106,10 @@ template StateUpdate(MAX_STACK_HEIGHT) { numeral_range_check.range <== [48, 57]; // ASCII NUMERALS matcher.case <== (1 - numeral_range_check.out) * byte + numeral_range_check.out * 256; // IF (NOT is_number) THEN byte ELSE 256 // * get the instruction mask based on current state * - component mask = StateToMask(); + component mask = StateToMask(MAX_STACK_HEIGHT); mask.in <== parsing_state; + mask.stack <== stack; + mask.pointer <== pointer; // * multiply the mask array elementwise with the instruction array * component mulMaskAndOut = ArrayMul(4); mulMaskAndOut.lhs <== mask.out; @@ -178,7 +180,7 @@ This function is creates an exhaustive switch statement from `0` up to `n`. - `match`: is set to `0` if `case` does not match on any of `branches` - `out[n]`: the selected output value if one of `branches` is selected (will be `[0,0,...]` otherwise) */ -template Switch(m, n) { +template SwitchArray(m, n) { assert(m > 0); assert(n > 0); signal input case; @@ -208,8 +210,37 @@ template Switch(m, n) { out <== sum; } -template StateToMask() { +template Switch(n) { + assert(n > 0); + signal input case; + signal input branches[n]; + signal input vals[n]; + signal output match; + signal output out; + + + // Verify that the `case` is in the possible set of branches + component indicator[n]; + component matchChecker = Contains(n); + signal temp_val[n]; + var sum; + for(var i = 0; i < n; i++) { + indicator[i] = IsZero(); + indicator[i].in <== case - branches[i]; + matchChecker.array[i] <== 1 - indicator[i].out; + temp_val[i] <== indicator[i].out * vals[i]; + sum += temp_val[i]; + } + matchChecker.in <== 0; + match <== matchChecker.out; + + out <== sum; +} + +template StateToMask(n) { signal input in[4]; + signal input stack[n]; + signal input pointer; signal output out[4]; signal pushpop <== in[0]; @@ -217,6 +248,9 @@ template StateToMask() { signal parsing_string <== in[2]; signal parsing_number <== in[3]; + // component stackTop = Switch(n); + + // `pushpop` can change: IF NOT `parsing_string` out[0] <== (1 - parsing_string); diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index a983a03..e05da13 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -8,6 +8,48 @@ describe("parser", () => { circuit = await circomkit.WitnessTester(`Switch`, { file: "circuits/parser", template: "Switch", + params: [3], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("witness: case = 0, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { + await circuit.expectPass( + { case: 0, branches: [0, 1, 2], vals: [69, 420, 1337] }, + { match: 1, out: 69 }, + ); + }); + + it("witness: case = 1, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { + await circuit.expectPass( + { case: 1, branches: [0, 1, 2], vals: [69, 420, 1337] }, + { match: 1, out: 420 }, + ); + }); + + it("witness: case = 2, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { + await circuit.expectPass( + { case: 2, branches: [0, 1, 2], vals: [69, 420, 1337] }, + { match: 1, out: 1337 }, + ); + }); + + it("witness: case = 3, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { + await circuit.expectPass( + { case: 3, branches: [0, 1, 2], vals: [69, 420, 1337] }, + { match: 0, out: 0 }, + ); + }); + + + }); + + describe("SwitchArray", () => { + let circuit: WitnessTester<["case", "branches", "vals"], ["match", "out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`SwitchArray`, { + file: "circuits/parser", + template: "SwitchArray", params: [3, 2], }); console.log("#constraints:", await circuit.getConstraintCount()); @@ -115,6 +157,7 @@ describe("parser", () => { circuit = await circomkit.WitnessTester(`StateUpdate`, { file: "circuits/parser", template: "StateUpdate", + params: [4], }); console.log("#constraints:", await circuit.getConstraintCount()); @@ -411,6 +454,26 @@ describe("parser", () => { in_object_and_value_to_leave_object_out.next_pointer = 0; in_object_and_value_to_leave_object_out.next_stack = [0, 0, 0, 0]; generatePassCase(in_object_and_value_to_leave_object, in_object_and_value_to_leave_object_out, ">>>> `,` read"); + + + + + + + + + //-----------------------------------------------------------------------------// + // Test SOMETHING: + // init: pointer = 1, stack = [1,2,0,0] -> `,` is read + let inside_array = { ...init }; + inside_array.pointer = read_start_brace_out.next_pointer; + inside_array.stack = read_start_brace_out.next_stack; + inside_array.byte = comma; + let inside_array_out = { ...out }; + inside_array_out.next_pointer = 2; + inside_array_out.next_stack = [1, 2, 0, 0]; + generatePassCase(inside_array, inside_array_out, ">>>> `,` read"); + //-----------------------------------------------------------------------------// }); }); From d544d5cd56cf7a840fc914d3a6ac40f35fa43958 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Mon, 12 Aug 2024 17:10:56 -0600 Subject: [PATCH 57/99] feat: `GetTopOfStack` Gotta work with this to make life easier. We can make easier conditionals for this function and perhaps not even need to store a pointer and just store the stack! Other notes: The issue is that parsing commas in arrays will return back a -3 value to overwrite a stack position, but this will fail when inside of an array. So, if we know that the top of stack is a 2 (for inside of an array) we can make conditionals on this. We could return even something like -4 for the comma so we have to transform it depending on what the top of stack is --- circuits/parser.circom | 20 ++++++++++++++++---- circuits/test/parser.test.ts | 21 +++++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 4638b46..cd63e85 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -108,8 +108,6 @@ template StateUpdate(MAX_STACK_HEIGHT) { // * get the instruction mask based on current state * component mask = StateToMask(MAX_STACK_HEIGHT); mask.in <== parsing_state; - mask.stack <== stack; - mask.pointer <== pointer; // * multiply the mask array elementwise with the instruction array * component mulMaskAndOut = ArrayMul(4); mulMaskAndOut.lhs <== mask.out; @@ -239,8 +237,6 @@ template Switch(n) { template StateToMask(n) { signal input in[4]; - signal input stack[n]; - signal input pointer; signal output out[4]; signal pushpop <== in[0]; @@ -265,6 +261,22 @@ template StateToMask(n) { out[3] <== (1 - parsing_string) * (- 2 * parsing_number); } +template GetTopOfStack(n) { + signal input stack[n]; + signal input pointer; + + signal output out; + + component atTop = Switch(n); + for(var i = 0; i < n; i++) { + atTop.branches[i] <== i + 1; + atTop.vals[i] <== stack[i]; + } + atTop.case <== pointer; + + out <== atTop.out; +} + // TODO: IMPORTANT NOTE, THE STACK IS CONSTRAINED TO 2**8 so the LessThan and GreaterThan work (could be changed) // TODO: Might be good to change value before increment pointer AND decrement pointer before changing value template RewriteStack(n) { diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index e05da13..dad35d7 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -99,6 +99,27 @@ describe("parser", () => { }); + describe("GetTopOfStack", () => { + let circuit: WitnessTester<["stack", "pointer"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`GetTopOfStack`, { + file: "circuits/parser", + template: "GetTopOfStack", + params: [4], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("witness: pointer = 4, stack = [0, 1, 2, 3, 4]", async () => { + await circuit.expectPass( + { pointer: 4, stack: [1, 2, 3, 4] }, + { out: 4 }, + ); + }); + + + }); + //--------------------------------------------------------------------------------------------// //-Delimeters---------------------------------------------------------------------------------// // - ASCII char: `{` From 3f38c79f8315169bf59de0bf1850626b3a61ed98 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Mon, 12 Aug 2024 18:06:15 -0600 Subject: [PATCH 58/99] IT"S LIVING --- circuits/parser.circom | 43 ++++++++++++++++++++++++++++++------ circuits/test/parser.test.ts | 4 ++-- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index cd63e85..4219a42 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -90,7 +90,7 @@ template StateUpdate(MAX_STACK_HEIGHT) { var hit_end_bracket[4] = [-1, -2, 0, 0 ]; // Command returned by switch if we hit a start bracket `]` var hit_quote[4] = [0, 0, 1, 0 ]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` var hit_colon[4] = [1, 3, 0, 0 ]; // Command returned by switch if we hit a colon `:` - var hit_comma[4] = [-1, -3, 0, -1 ]; // Command returned by switch if we hit a comma `,` + var hit_comma[4] = [-1, -4, 0, -1 ]; // Command returned by switch if we hit a comma `,` var hit_number[4] = [0, 0, 0, 1 ]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) //--------------------------------------------------------------------------------------------// @@ -314,8 +314,24 @@ template RewriteStack(n) { */ // Indicate which position in the stack should change (if any) + component readComma = IsEqual(); + readComma.in[0] <== -4; + readComma.in[1] <== stack_val; + + component topOfStack = GetTopOfStack(n); + topOfStack.pointer <== pointer; + topOfStack.stack <== stack; + + component isArray = IsEqual(); + isArray.in[0] <== topOfStack.out; + isArray.in[1] <== 2; + + signal READ_COMMA_AND_IN_ARRAY <== (1-readComma.out) + (1-isArray.out); + component isReadCommaAndInArray = IsZero(); + isReadCommaAndInArray.in <== READ_COMMA_AND_IN_ARRAY; + component isPop = IsZero(); - isPop.in <== pushpop + 1; + isPop.in <== (1 - isReadCommaAndInArray.out) * pushpop + 1; component isPush = IsZero(); isPush.in <== pushpop - 1; component prev_indicator[n]; @@ -326,6 +342,12 @@ template RewriteStack(n) { component readEndChar = IsZero(); readEndChar.in <== (stack_val + 1) * (stack_val + 2); + + + signal NOT_READ_COMMA <== (1-readComma.out) * stack_val; + signal READ_COMMA <== readComma.out * ((1-isArray.out) * (-3) + isArray.out * (-2)); + signal corrected_stack_val <== READ_COMMA + NOT_READ_COMMA; + // top of stack is a 3, then we need to pop off 3, and check the value underneath // is correct match (i.e., a brace or bracket (1 or 2)) @@ -368,17 +390,24 @@ template RewriteStack(n) { isPushAt[i] <== indicator[i].out * isPush.out; // Leave the stack alone except for where we indicate change - second_pop_val[i] <== isPopAtPrev[i] * stack_val; - temp_val[i] <== stack_val - (3 + stack_val) * isDoublePop; - first_pop_val[i] <== isPopAt[i] * temp_val[i]; // = isPopAt[i] * (stack_val * (1 - isDoublePop) - 3 * isDoublePop) + second_pop_val[i] <== isPopAtPrev[i] * corrected_stack_val; + temp_val[i] <== corrected_stack_val - (3 + corrected_stack_val) * isDoublePop; + first_pop_val[i] <== isPopAt[i] * temp_val[i]; // = isPopAt[i] * (corrected_stack_val * (1 - isDoublePop) - 3 * isDoublePop) - next_stack[i] <== stack[i] + isPushAt[i] * stack_val + first_pop_val[i] + second_pop_val[i]; + next_stack[i] <== stack[i] + isPushAt[i] * corrected_stack_val + first_pop_val[i] + second_pop_val[i]; // TODO: Constrain next_stack entries to be 0,1,2,3 } // TODO: This decrements by 2 if we hit a ] or } when the top of stack is 3 - next_pointer <== pointer + (1 + isDoublePop) * pushpop; // If pushpop is 0, pointer doesn't change, if -1, decrement, +1 increment + // signal READ_COMMA_AND_IN_ARRAY <== (1-readComma.out) + (1-isArray.out); + // log("topOfStack = ", topOfStack.out); + // log("readComma = ", readComma.out); + // log("isArray = ", isArray.out); + + signal IS_READ_COMMA_AND_IN_ARRAY_MODIFIER <== (1-isReadCommaAndInArray.out) * pushpop; + // log("IS_READ_COMMA_AND_IN_ARRAY_MODIFIER = ", IS_READ_COMMA_AND_IN_ARRAY_MODIFIER); + next_pointer <== pointer + (1 + isDoublePop) * IS_READ_COMMA_AND_IN_ARRAY_MODIFIER; // If pushpop is 0, pointer doesn't change, if -1, decrement, +1 increment component isOverflow = GreaterThan(8); isOverflow.in[0] <== next_pointer; diff --git a/circuits/test/parser.test.ts b/circuits/test/parser.test.ts index dad35d7..fe07b3c 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser.test.ts @@ -487,8 +487,8 @@ describe("parser", () => { // Test SOMETHING: // init: pointer = 1, stack = [1,2,0,0] -> `,` is read let inside_array = { ...init }; - inside_array.pointer = read_start_brace_out.next_pointer; - inside_array.stack = read_start_brace_out.next_stack; + inside_array.pointer = 2; + inside_array.stack = [1, 2, 0, 0]; inside_array.byte = comma; let inside_array_out = { ...out }; inside_array_out.next_pointer = 2; From 449ac72313f735e6d3b1bbbb25c953b869d8ab18 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Tue, 13 Aug 2024 14:28:28 -0600 Subject: [PATCH 59/99] refactor: use `circomlib` directly --- circuits/bytes.circom | 70 ++--- circuits/extract.circom | 3 +- circuits/operators.circom | 214 --------------- circuits/parser.circom | 85 +----- circuits/test/bytes.test.ts | 58 ---- circuits/test/{ => parser}/parser.test.ts | 100 +------ .../utils.test.ts} | 170 ++++++++---- circuits/utils.circom | 250 ++++++++++++++++++ 8 files changed, 402 insertions(+), 548 deletions(-) delete mode 100644 circuits/operators.circom delete mode 100644 circuits/test/bytes.test.ts rename circuits/test/{ => parser}/parser.test.ts (81%) rename circuits/test/{operators.test.ts => utils/utils.test.ts} (58%) create mode 100644 circuits/utils.circom diff --git a/circuits/bytes.circom b/circuits/bytes.circom index 87a8b25..7ac26bd 100644 --- a/circuits/bytes.circom +++ b/circuits/bytes.circom @@ -20,22 +20,22 @@ This function reads in a unsigned 8-bit integer and converts it to an array of b # Constraints: - `in`: must be between `0` and `2**8 - 1` */ -template U8ToBits() { - signal input in; - signal byte[8]; - var lc1 = 0; +// template U8ToBits() { +// signal input in; +// signal byte[8]; +// var lc1 = 0; - // log("input to u8ToByte: ", in); +// // log("input to u8ToByte: ", in); - var e2 = 1; - for (var i = 0; i < 8; i++) { - byte[i] <-- (in >> i) & 1; - byte[i] * (byte[i] - 1) === 0; - lc1 += byte[i] * e2; - e2 = e2 + e2; - } - lc1 === in; -} +// var e2 = 1; +// for (var i = 0; i < 8; i++) { +// byte[i] <-- (in >> i) & 1; +// byte[i] * (byte[i] - 1) === 0; +// lc1 += byte[i] * e2; +// e2 = e2 + e2; +// } +// lc1 === in; +// } /* This function reads in an array of unsigned numbers that will be constrained to be valid unsigned 8-bit integers. @@ -47,28 +47,28 @@ This function reads in an array of unsigned numbers that will be constrained to # Constraints: - `in[n]`: each element of this array must be between `0` and `2**8-1` */ -template ASCII(n) { - signal input in[n]; +// template ASCII(n) { +// signal input in[n]; - component Byte[n]; - for(var i = 0; i < n; i++) { - Byte[i] = U8ToBits(); - Byte[i].in <== in[i]; - } -} +// component Byte[n]; +// for(var i = 0; i < n; i++) { +// Byte[i] = U8ToBits(); +// Byte[i].in <== in[i]; +// } +// } -template Num2Bits(n) { - signal input in; - signal output out[n]; - var lc1=0; +// template Num2Bits(n) { +// signal input in; +// signal output out[n]; +// var lc1=0; - var e2=1; - for (var i = 0; i> i) & 1; - out[i] * (out[i] -1 ) === 0; - lc1 += out[i] * e2; - e2 = e2+e2; - } +// var e2=1; +// for (var i = 0; i> i) & 1; +// out[i] * (out[i] -1 ) === 0; +// lc1 += out[i] * e2; +// e2 = e2+e2; +// } - lc1 === in; -} +// lc1 === in; +// } diff --git a/circuits/extract.circom b/circuits/extract.circom index fd8b175..5e735e7 100644 --- a/circuits/extract.circom +++ b/circuits/extract.circom @@ -1,7 +1,6 @@ pragma circom 2.1.9; -include "bytes.circom"; -include "operators.circom"; +include "utils.circom"; include "parser.circom"; template Extract(DATA_BYTES) { diff --git a/circuits/operators.circom b/circuits/operators.circom deleted file mode 100644 index 11de6e3..0000000 --- a/circuits/operators.circom +++ /dev/null @@ -1,214 +0,0 @@ -pragma circom 2.1.9; - -include "bytes.circom"; - -/* -All tests for this file are located in: `./test/operators.test.ts` - -Some of the functions here were based off the circomlib: -https://github.com/iden3/circomlib/blob/cff5ab6288b55ef23602221694a6a38a0239dcc0/circuits/comparators.circom -*/ - - -/* -This function is an indicator for zero. - -# Inputs: -- `in`: some number -- `out`: either `0` or `1` - - `1` if `in` is equal to `0` - - `0` otherwise - -# Constraints -- `in`: must be either `0` or `1`. -*/ -template IsZero() { - signal input in; - signal output out; - - signal inv; - - inv <-- in!=0 ? 1/in : 0; - - out <== -in * inv + 1; - in * out === 0; -} - -/* -This function is an indicator for two equal inputs. - -# Inputs: -- `in[2]`: two numbers -- `out`: either `0` or `1` - - `1` if `in[0]` is equal to `in[1]` - - `0` otherwise -*/ -template IsEqual() { - signal input in[2]; - signal output out; - - component isz = IsZero(); - - in[1] - in[0] ==> isz.in; - - isz.out ==> out; -} - -/* -This function is an indicator for two equal array inputs. - -# Inputs: -- `n`: the length of arrays to compare -- `in[2][n]`: two arrays of `n` numbers -- `out`: either `0` or `1` - - `1` if `in[0]` is equal to `in[1]` as arrays (i.e., component by component) - - `0` otherwise -*/ -template IsEqualArray(n) { - signal input in[2][n]; - signal output out; - - var accum = 0; - component equalComponent[n]; - - for(var i = 0; i < n; i++) { - equalComponent[i] = IsEqual(); - equalComponent[i].in[0] <== in[0][i]; - equalComponent[i].in[1] <== in[1][i]; - accum += equalComponent[i].out; - } - - component totalEqual = IsEqual(); - totalEqual.in[0] <== n; - totalEqual.in[1] <== accum; - out <== totalEqual.out; -} - - -// TODO: There should be a way to have the below assertion come from the field itself. -/* -This function is an indicator for if an array contains an element. - -# Inputs: -- `n`: the size of the array to search through -- `in`: a number -- `array[n]`: the array we want to search through -- `out`: either `0` or `1` - - `1` if `in` is found inside `array` - - `0` otherwise -*/ -template Contains(n) { - assert(n > 0); - /* - If `n = p` for this large `p`, then it could be that this function - returns the wrong value if every element in `array` was equal to `in`. - This is EXTREMELY unlikely and iterating this high is impossible anyway. - But it is better to check than miss something, so we bound it by `2**254` for now. - */ - assert(n < 2**254); - signal input in; - signal input array[n]; - signal output out; - - var accum = 0; - component equalComponent[n]; - for(var i = 0; i < n; i++) { - equalComponent[i] = IsEqual(); - equalComponent[i].in[0] <== in; - equalComponent[i].in[1] <== array[i]; - accum = accum + equalComponent[i].out; - } - - component someEqual = IsZero(); - someEqual.in <== accum; - - // 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]; - } -} - -template LessThan(n) { - assert(n <= 252); - signal input in[2]; - signal output out; - - component n2b = Num2Bits(n+1); - - n2b.in <== in[0]+ (1< out; -} - -// N is the number of bits the input have. -// The MSF is the sign bit. -template GreaterThan(n) { - signal input in[2]; - signal output out; - - component lt = LessThan(n); - - lt.in[0] <== in[1]; - lt.in[1] <== in[0]; - lt.out ==> out; -} - -// N is the number of bits the input have. -// The MSF is the sign bit. -template GreaterEqThan(n) { - signal input in[2]; - signal output out; - - component lt = LessThan(n); - - lt.in[0] <== in[1]; - lt.in[1] <== in[0]+1; - lt.out ==> out; -} - -template InRange(n) { - signal input in; - signal input range[2]; - signal output out; - - component gte = GreaterEqThan(n); - gte.in <== [in, range[0]]; - - component lte = LessEqThan(n); - lte.in <== [in, range[1]]; - - out <== gte.out * lte.out; -} \ No newline at end of file diff --git a/circuits/parser.circom b/circuits/parser.circom index 4219a42..e33e530 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -1,6 +1,6 @@ pragma circom 2.1.9; -include "operators.circom"; +include "utils.circom"; /* Notes: for `test.json` xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx @@ -162,79 +162,6 @@ template StateUpdate(MAX_STACK_HEIGHT) { //--------------------------------------------------------------------------------------------// } -/* -This function is creates an exhaustive switch statement from `0` up to `n`. - -# Inputs: -- `m`: the number of switch cases -- `n`: the output array length -- `case`: which case of the switch to select -- `branches[m]`: the values that enable taking different branches in the switch - (e.g., if `branch[i] == 10` then if `case == 10` we set `out == `vals[i]`) -- `vals[m][n]`: the value that is emitted for a given switch case - (e.g., `val[i]` array is emitted on `case == `branch[i]`) - -# Outputs -- `match`: is set to `0` if `case` does not match on any of `branches` -- `out[n]`: the selected output value if one of `branches` is selected (will be `[0,0,...]` otherwise) -*/ -template SwitchArray(m, n) { - assert(m > 0); - assert(n > 0); - signal input case; - signal input branches[m]; - signal input vals[m][n]; - signal output match; - signal output out[n]; - - - // Verify that the `case` is in the possible set of branches - component indicator[m]; - component matchChecker = Contains(m); - signal component_out[m][n]; - var sum[n]; - for(var i = 0; i < m; i++) { - indicator[i] = IsZero(); - indicator[i].in <== case - branches[i]; - matchChecker.array[i] <== 1 - indicator[i].out; - for(var j = 0; j < n; j++) { - component_out[i][j] <== indicator[i].out * vals[i][j]; - sum[j] += component_out[i][j]; - } - } - matchChecker.in <== 0; - match <== matchChecker.out; - - out <== sum; -} - -template Switch(n) { - assert(n > 0); - signal input case; - signal input branches[n]; - signal input vals[n]; - signal output match; - signal output out; - - - // Verify that the `case` is in the possible set of branches - component indicator[n]; - component matchChecker = Contains(n); - signal temp_val[n]; - var sum; - for(var i = 0; i < n; i++) { - indicator[i] = IsZero(); - indicator[i].in <== case - branches[i]; - matchChecker.array[i] <== 1 - indicator[i].out; - temp_val[i] <== indicator[i].out * vals[i]; - sum += temp_val[i]; - } - matchChecker.in <== 0; - match <== matchChecker.out; - - out <== sum; -} - template StateToMask(n) { signal input in[4]; signal output out[4]; @@ -244,9 +171,6 @@ template StateToMask(n) { signal parsing_string <== in[2]; signal parsing_number <== in[3]; - // component stackTop = Switch(n); - - // `pushpop` can change: IF NOT `parsing_string` out[0] <== (1 - parsing_string); @@ -399,14 +323,7 @@ template RewriteStack(n) { // TODO: Constrain next_stack entries to be 0,1,2,3 } - // TODO: This decrements by 2 if we hit a ] or } when the top of stack is 3 - // signal READ_COMMA_AND_IN_ARRAY <== (1-readComma.out) + (1-isArray.out); - // log("topOfStack = ", topOfStack.out); - // log("readComma = ", readComma.out); - // log("isArray = ", isArray.out); - signal IS_READ_COMMA_AND_IN_ARRAY_MODIFIER <== (1-isReadCommaAndInArray.out) * pushpop; - // log("IS_READ_COMMA_AND_IN_ARRAY_MODIFIER = ", IS_READ_COMMA_AND_IN_ARRAY_MODIFIER); next_pointer <== pointer + (1 + isDoublePop) * IS_READ_COMMA_AND_IN_ARRAY_MODIFIER; // If pushpop is 0, pointer doesn't change, if -1, decrement, +1 increment component isOverflow = GreaterThan(8); diff --git a/circuits/test/bytes.test.ts b/circuits/test/bytes.test.ts deleted file mode 100644 index 2d26bde..0000000 --- a/circuits/test/bytes.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { circomkit, WitnessTester } from "./common"; - -describe("bytes", () => { - let circuit: WitnessTester<["in"], ["out"]>; - - describe("U8ToBits", () => { - before(async () => { - circuit = await circomkit.WitnessTester(`U8ToBits`, { - file: "circuits/bytes", - template: "U8ToBits", - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("(valid) witness: in = 0", async () => { - await circuit.expectPass({ in: 0 }); - }); - - it("(valid) witness: in = 15", async () => { - await circuit.expectPass({ in: 15 }); - }); - - it("(valid) witness: in = 255", async () => { - await circuit.expectPass({ in: 255 }); - }); - - it("(invalid) witness: in = 256", async () => { - await circuit.expectFail({ in: 256 }); - }); - - it("(invalid) witness: in = 42069", async () => { - await circuit.expectFail({ in: 42069 }); - }); - }); - - describe("ASCII", () => { - before(async () => { - circuit = await circomkit.WitnessTester(`ASCII`, { - file: "circuits/bytes", - template: "ASCII", - params: [13], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("(valid) witness: in = b\"Hello, world!\"", async () => { - await circuit.expectPass( - { in: [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] }, - ); - }); - - it("(invalid) witness: in = [256, ...]", async () => { - await circuit.expectFail( - { in: [256, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] } - ); - }); - }); -}); diff --git a/circuits/test/parser.test.ts b/circuits/test/parser/parser.test.ts similarity index 81% rename from circuits/test/parser.test.ts rename to circuits/test/parser/parser.test.ts index fe07b3c..4f6e7aa 100644 --- a/circuits/test/parser.test.ts +++ b/circuits/test/parser/parser.test.ts @@ -1,104 +1,6 @@ -import { start } from "repl"; -import { circomkit, WitnessTester } from "./common"; +import { circomkit, WitnessTester } from "../common"; describe("parser", () => { - describe("Switch", () => { - let circuit: WitnessTester<["case", "branches", "vals"], ["match", "out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`Switch`, { - file: "circuits/parser", - template: "Switch", - params: [3], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("witness: case = 0, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { - await circuit.expectPass( - { case: 0, branches: [0, 1, 2], vals: [69, 420, 1337] }, - { match: 1, out: 69 }, - ); - }); - - it("witness: case = 1, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { - await circuit.expectPass( - { case: 1, branches: [0, 1, 2], vals: [69, 420, 1337] }, - { match: 1, out: 420 }, - ); - }); - - it("witness: case = 2, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { - await circuit.expectPass( - { case: 2, branches: [0, 1, 2], vals: [69, 420, 1337] }, - { match: 1, out: 1337 }, - ); - }); - - it("witness: case = 3, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { - await circuit.expectPass( - { case: 3, branches: [0, 1, 2], vals: [69, 420, 1337] }, - { match: 0, out: 0 }, - ); - }); - - - }); - - describe("SwitchArray", () => { - let circuit: WitnessTester<["case", "branches", "vals"], ["match", "out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`SwitchArray`, { - file: "circuits/parser", - template: "SwitchArray", - params: [3, 2], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("witness: case = 0, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { - await circuit.expectPass( - { case: 0, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, - { match: 1, out: [69, 0] }, - ); - }); - - it("witness: case = 1, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { - await circuit.expectPass( - { case: 1, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, - { match: 1, out: [420, 1] }, - ); - }); - - it("witness: case = 2, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { - await circuit.expectPass( - { case: 2, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, - { match: 1, out: [1337, 2] }, - ); - }); - - it("witness: case = 3, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { - await circuit.expectPass( - { case: 3, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, - { match: 0, out: [0, 0] } - ); - }); - - it("witness: case = 420, branches = [69, 420, 1337], vals = [[10,3], [20,5], [30,7]]", async () => { - await circuit.expectPass( - { case: 420, branches: [69, 420, 1337], vals: [[10, 3], [20, 5], [30, 7]] }, - { match: 1, out: [20, 5] } - ); - }); - - it("witness: case = 0, branches = [69, 420, 1337], vals = [[10,3], [20,5], [30,7]]", async () => { - await circuit.expectPass( - { case: 0, branches: [69, 420, 1337], vals: [[10, 3], [20, 5], [30, 7]] }, - { match: 0, out: [0, 0] } - ); - }); - - }); - describe("GetTopOfStack", () => { let circuit: WitnessTester<["stack", "pointer"], ["out"]>; before(async () => { diff --git a/circuits/test/operators.test.ts b/circuits/test/utils/utils.test.ts similarity index 58% rename from circuits/test/operators.test.ts rename to circuits/test/utils/utils.test.ts index 4ba9891..f65493d 100644 --- a/circuits/test/operators.test.ts +++ b/circuits/test/utils/utils.test.ts @@ -1,65 +1,26 @@ -import { circomkit, WitnessTester } from "./common"; +import { circomkit, WitnessTester } from "../common"; -describe("operators", () => { - describe("IsZero", () => { +describe("utils", () => { + describe("ASCII", () => { let circuit: WitnessTester<["in"], ["out"]>; before(async () => { - circuit = await circomkit.WitnessTester(`IsZero`, { - file: "circuits/operators", - template: "IsZero", + circuit = await circomkit.WitnessTester(`ASCII`, { + file: "circuits/utils", + template: "ASCII", + params: [13], }); console.log("#constraints:", await circuit.getConstraintCount()); }); - it("witness: 0", async () => { + it("(valid) witness: in = b\"Hello, world!\"", async () => { await circuit.expectPass( - { in: 0 }, - { out: 1 }); - }); - - it("witness: 1", async () => { - await circuit.expectPass( - { in: 1 }, - { out: 0 } - ); - }); - - it("witness: 42069", async () => { - await circuit.expectPass( - { in: 42069 }, - { out: 0 } - ); - }); - }); - - describe("IsEqual", () => { - let circuit: WitnessTester<["in"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`IsEqual`, { - file: "circuits/operators", - template: "IsEqual", - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("witness: [0,0]", async () => { - await circuit.expectPass( - { in: [0, 0] }, - { out: 1 } + { in: [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] }, ); }); - it("witness: [42069, 42069]", async () => { - await circuit.expectPass( - { in: [42069, 42069] }, - { out: 1 }, - ); - }); - - it("witness: [42069, 0]", async () => { - await circuit.expectPass( - { in: [42069, 0] }, - { out: 0 }, + it("(invalid) witness: in = [256, ...]", async () => { + await circuit.expectFail( + { in: [256, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] } ); }); }); @@ -68,7 +29,7 @@ describe("operators", () => { let circuit: WitnessTester<["in"], ["out"]>; before(async () => { circuit = await circomkit.WitnessTester(`IsEqualArray`, { - file: "circuits/operators", + file: "circuits/utils", template: "IsEqualArray", params: [3], }); @@ -122,7 +83,7 @@ describe("operators", () => { let circuit: WitnessTester<["in", "array"], ["out"]>; before(async () => { circuit = await circomkit.WitnessTester(`Contains`, { - file: "circuits/operators", + file: "circuits/utils", template: "Contains", params: [3], }); @@ -163,7 +124,7 @@ describe("operators", () => { let circuit: WitnessTester<["lhs", "rhs"], ["out"]>; before(async () => { circuit = await circomkit.WitnessTester(`ArrayAdd`, { - file: "circuits/operators", + file: "circuits/utils", template: "ArrayAdd", params: [3], }); @@ -183,7 +144,7 @@ describe("operators", () => { let circuit: WitnessTester<["lhs", "rhs"], ["out"]>; before(async () => { circuit = await circomkit.WitnessTester(`ArrayMul`, { - file: "circuits/operators", + file: "circuits/utils", template: "ArrayMul", params: [3], }); @@ -203,7 +164,7 @@ describe("operators", () => { let circuit: WitnessTester<["in", "range"], ["out"]>; before(async () => { circuit = await circomkit.WitnessTester(`InRange`, { - file: "circuits/operators", + file: "circuits/utils", template: "InRange", params: [8], }); @@ -231,4 +192,101 @@ describe("operators", () => { ); }); }); + + describe("Switch", () => { + let circuit: WitnessTester<["case", "branches", "vals"], ["match", "out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`Switch`, { + file: "circuits/utils", + template: "Switch", + params: [3], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("witness: case = 0, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { + await circuit.expectPass( + { case: 0, branches: [0, 1, 2], vals: [69, 420, 1337] }, + { match: 1, out: 69 }, + ); + }); + + it("witness: case = 1, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { + await circuit.expectPass( + { case: 1, branches: [0, 1, 2], vals: [69, 420, 1337] }, + { match: 1, out: 420 }, + ); + }); + + it("witness: case = 2, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { + await circuit.expectPass( + { case: 2, branches: [0, 1, 2], vals: [69, 420, 1337] }, + { match: 1, out: 1337 }, + ); + }); + + it("witness: case = 3, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { + await circuit.expectPass( + { case: 3, branches: [0, 1, 2], vals: [69, 420, 1337] }, + { match: 0, out: 0 }, + ); + }); + + + }); + + describe("SwitchArray", () => { + let circuit: WitnessTester<["case", "branches", "vals"], ["match", "out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`SwitchArray`, { + file: "circuits/utils", + template: "SwitchArray", + params: [3, 2], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("witness: case = 0, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { + await circuit.expectPass( + { case: 0, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, + { match: 1, out: [69, 0] }, + ); + }); + + it("witness: case = 1, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { + await circuit.expectPass( + { case: 1, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, + { match: 1, out: [420, 1] }, + ); + }); + + it("witness: case = 2, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { + await circuit.expectPass( + { case: 2, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, + { match: 1, out: [1337, 2] }, + ); + }); + + it("witness: case = 3, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { + await circuit.expectPass( + { case: 3, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, + { match: 0, out: [0, 0] } + ); + }); + + it("witness: case = 420, branches = [69, 420, 1337], vals = [[10,3], [20,5], [30,7]]", async () => { + await circuit.expectPass( + { case: 420, branches: [69, 420, 1337], vals: [[10, 3], [20, 5], [30, 7]] }, + { match: 1, out: [20, 5] } + ); + }); + + it("witness: case = 0, branches = [69, 420, 1337], vals = [[10,3], [20,5], [30,7]]", async () => { + await circuit.expectPass( + { case: 0, branches: [69, 420, 1337], vals: [[10, 3], [20, 5], [30, 7]] }, + { match: 0, out: [0, 0] } + ); + }); + + }); }); diff --git a/circuits/utils.circom b/circuits/utils.circom new file mode 100644 index 0000000..41d4bb5 --- /dev/null +++ b/circuits/utils.circom @@ -0,0 +1,250 @@ +pragma circom 2.1.9; + +include "circomlib/circuits/bitify.circom"; +include "circomlib/circuits/comparators.circom"; + +/* +All tests for this file are located in: `./test/operators.test.ts` +*/ + +template ASCII(n) { + signal input in[n]; + + component Byte[n]; + for(var i = 0; i < n; i++) { + Byte[i] = Num2Bits(8); + Byte[i].in <== in[i]; + } +} + +/* +This function is an indicator for two equal array inputs. + +# Inputs: +- `n`: the length of arrays to compare +- `in[2][n]`: two arrays of `n` numbers +- `out`: either `0` or `1` + - `1` if `in[0]` is equal to `in[1]` as arrays (i.e., component by component) + - `0` otherwise +*/ +template IsEqualArray(n) { + signal input in[2][n]; + signal output out; + + var accum = 0; + component equalComponent[n]; + + for(var i = 0; i < n; i++) { + equalComponent[i] = IsEqual(); + equalComponent[i].in[0] <== in[0][i]; + equalComponent[i].in[1] <== in[1][i]; + accum += equalComponent[i].out; + } + + component totalEqual = IsEqual(); + totalEqual.in[0] <== n; + totalEqual.in[1] <== accum; + out <== totalEqual.out; +} + + +// TODO: There should be a way to have the below assertion come from the field itself. +/* +This function is an indicator for if an array contains an element. + +# Inputs: +- `n`: the size of the array to search through +- `in`: a number +- `array[n]`: the array we want to search through +- `out`: either `0` or `1` + - `1` if `in` is found inside `array` + - `0` otherwise +*/ +template Contains(n) { + assert(n > 0); + /* + If `n = p` for this large `p`, then it could be that this function + returns the wrong value if every element in `array` was equal to `in`. + This is EXTREMELY unlikely and iterating this high is impossible anyway. + But it is better to check than miss something, so we bound it by `2**254` for now. + */ + assert(n < 2**254); + signal input in; + signal input array[n]; + signal output out; + + var accum = 0; + component equalComponent[n]; + for(var i = 0; i < n; i++) { + equalComponent[i] = IsEqual(); + equalComponent[i].in[0] <== in; + equalComponent[i].in[1] <== array[i]; + accum = accum + equalComponent[i].out; + } + + component someEqual = IsZero(); + someEqual.in <== accum; + + // 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]; + } +} + +// template LessThan(n) { +// assert(n <= 252); +// signal input in[2]; +// signal output out; + +// component n2b = Num2Bits(n+1); + +// n2b.in <== in[0]+ (1< out; +// } + +// // N is the number of bits the input have. +// // The MSF is the sign bit. +// template GreaterThan(n) { +// signal input in[2]; +// signal output out; + +// component lt = LessThan(n); + +// lt.in[0] <== in[1]; +// lt.in[1] <== in[0]; +// lt.out ==> out; +// } + +// // N is the number of bits the input have. +// // The MSF is the sign bit. +// template GreaterEqThan(n) { +// signal input in[2]; +// signal output out; + +// component lt = LessThan(n); + +// lt.in[0] <== in[1]; +// lt.in[1] <== in[0]+1; +// lt.out ==> out; +// } + +template InRange(n) { + signal input in; + signal input range[2]; + signal output out; + + component gte = GreaterEqThan(n); + gte.in <== [in, range[0]]; + + component lte = LessEqThan(n); + lte.in <== [in, range[1]]; + + out <== gte.out * lte.out; +} + +/* +This function is creates an exhaustive switch statement from `0` up to `n`. + +# Inputs: +- `m`: the number of switch cases +- `n`: the output array length +- `case`: which case of the switch to select +- `branches[m]`: the values that enable taking different branches in the switch + (e.g., if `branch[i] == 10` then if `case == 10` we set `out == `vals[i]`) +- `vals[m][n]`: the value that is emitted for a given switch case + (e.g., `val[i]` array is emitted on `case == `branch[i]`) + +# Outputs +- `match`: is set to `0` if `case` does not match on any of `branches` +- `out[n]`: the selected output value if one of `branches` is selected (will be `[0,0,...]` otherwise) +*/ +template SwitchArray(m, n) { + assert(m > 0); + assert(n > 0); + signal input case; + signal input branches[m]; + signal input vals[m][n]; + signal output match; + signal output out[n]; + + + // Verify that the `case` is in the possible set of branches + component indicator[m]; + component matchChecker = Contains(m); + signal component_out[m][n]; + var sum[n]; + for(var i = 0; i < m; i++) { + indicator[i] = IsZero(); + indicator[i].in <== case - branches[i]; + matchChecker.array[i] <== 1 - indicator[i].out; + for(var j = 0; j < n; j++) { + component_out[i][j] <== indicator[i].out * vals[i][j]; + sum[j] += component_out[i][j]; + } + } + matchChecker.in <== 0; + match <== matchChecker.out; + + out <== sum; +} + +template Switch(n) { + assert(n > 0); + signal input case; + signal input branches[n]; + signal input vals[n]; + signal output match; + signal output out; + + + // Verify that the `case` is in the possible set of branches + component indicator[n]; + component matchChecker = Contains(n); + signal temp_val[n]; + var sum; + for(var i = 0; i < n; i++) { + indicator[i] = IsZero(); + indicator[i].in <== case - branches[i]; + matchChecker.array[i] <== 1 - indicator[i].out; + temp_val[i] <== indicator[i].out * vals[i]; + sum += temp_val[i]; + } + matchChecker.in <== 0; + match <== matchChecker.out; + + out <== sum; +} \ No newline at end of file From f4fc9447fc7a9e10beec13111ecfa9884dec4a10 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Tue, 13 Aug 2024 14:35:05 -0600 Subject: [PATCH 60/99] refactor: JSONs --- .../{reddit_response.json => response/reddit.json} | 0 .../{spotify_response.json => response/spotify.json} | 0 json_examples/{venmo_response.json => response/venmo.json} | 0 json_examples/sambhav_example.json | 7 ------- json_examples/{ => test}/example.json | 0 json_examples/{ => test}/example_json.md | 6 +++++- json_examples/test/string_escape.json | 1 + json_examples/{test_two_key.json => test/two_keys.json} | 0 json_examples/test/value_array.json | 1 + json_examples/test/value_number.json | 1 + json_examples/test/value_object.json | 1 + json_examples/{test.json => test/value_string.json} | 0 json_examples/test_array.json | 1 - json_examples/test_depth.json | 6 ------ json_examples/test_hard.json | 4 ---- json_examples/test_number.json | 1 - 16 files changed, 9 insertions(+), 20 deletions(-) rename json_examples/{reddit_response.json => response/reddit.json} (100%) rename json_examples/{spotify_response.json => response/spotify.json} (100%) rename json_examples/{venmo_response.json => response/venmo.json} (100%) delete mode 100644 json_examples/sambhav_example.json rename json_examples/{ => test}/example.json (100%) rename json_examples/{ => test}/example_json.md (84%) create mode 100644 json_examples/test/string_escape.json rename json_examples/{test_two_key.json => test/two_keys.json} (100%) create mode 100644 json_examples/test/value_array.json create mode 100644 json_examples/test/value_number.json create mode 100644 json_examples/test/value_object.json rename json_examples/{test.json => test/value_string.json} (100%) delete mode 100644 json_examples/test_array.json delete mode 100644 json_examples/test_depth.json delete mode 100644 json_examples/test_hard.json delete mode 100644 json_examples/test_number.json diff --git a/json_examples/reddit_response.json b/json_examples/response/reddit.json similarity index 100% rename from json_examples/reddit_response.json rename to json_examples/response/reddit.json diff --git a/json_examples/spotify_response.json b/json_examples/response/spotify.json similarity index 100% rename from json_examples/spotify_response.json rename to json_examples/response/spotify.json diff --git a/json_examples/venmo_response.json b/json_examples/response/venmo.json similarity index 100% rename from json_examples/venmo_response.json rename to json_examples/response/venmo.json diff --git a/json_examples/sambhav_example.json b/json_examples/sambhav_example.json deleted file mode 100644 index ab9c57e..0000000 --- a/json_examples/sambhav_example.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extract": { - "file": "extract", - "template": "Extract", - "params": "" - } -} \ No newline at end of file diff --git a/json_examples/example.json b/json_examples/test/example.json similarity index 100% rename from json_examples/example.json rename to json_examples/test/example.json diff --git a/json_examples/example_json.md b/json_examples/test/example_json.md similarity index 84% rename from json_examples/example_json.md rename to json_examples/test/example_json.md index 90ee1c3..bd11903 100644 --- a/json_examples/example_json.md +++ b/json_examples/test/example_json.md @@ -1,3 +1,6 @@ +# Notes on this JSON for reference + + { "a": // 7 { "b": "c", // 19 "d": { // 25 @@ -21,4 +24,5 @@ } // 151 } // 153 } // 155 -} // 157 \ No newline at end of file +} // 157 + diff --git a/json_examples/test/string_escape.json b/json_examples/test/string_escape.json new file mode 100644 index 0000000..8765251 --- /dev/null +++ b/json_examples/test/string_escape.json @@ -0,0 +1 @@ +{ "a": "\"b\"" } \ No newline at end of file diff --git a/json_examples/test_two_key.json b/json_examples/test/two_keys.json similarity index 100% rename from json_examples/test_two_key.json rename to json_examples/test/two_keys.json diff --git a/json_examples/test/value_array.json b/json_examples/test/value_array.json new file mode 100644 index 0000000..865f425 --- /dev/null +++ b/json_examples/test/value_array.json @@ -0,0 +1 @@ +{ "k" : [420,69] } \ No newline at end of file diff --git a/json_examples/test/value_number.json b/json_examples/test/value_number.json new file mode 100644 index 0000000..70ddfbb --- /dev/null +++ b/json_examples/test/value_number.json @@ -0,0 +1 @@ +{ "k" : 69 } \ No newline at end of file diff --git a/json_examples/test/value_object.json b/json_examples/test/value_object.json new file mode 100644 index 0000000..dfaaba1 --- /dev/null +++ b/json_examples/test/value_object.json @@ -0,0 +1 @@ +{ "a": { "b": "c" } } \ No newline at end of file diff --git a/json_examples/test.json b/json_examples/test/value_string.json similarity index 100% rename from json_examples/test.json rename to json_examples/test/value_string.json diff --git a/json_examples/test_array.json b/json_examples/test_array.json deleted file mode 100644 index 6ba7a05..0000000 --- a/json_examples/test_array.json +++ /dev/null @@ -1 +0,0 @@ -{"key":[0,1,2]} \ No newline at end of file diff --git a/json_examples/test_depth.json b/json_examples/test_depth.json deleted file mode 100644 index ad2f816..0000000 --- a/json_examples/test_depth.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "key1": "abc", - "key2": { - "key3": "def" - } -} \ No newline at end of file diff --git a/json_examples/test_hard.json b/json_examples/test_hard.json deleted file mode 100644 index 7904a51..0000000 --- a/json_examples/test_hard.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "key1": "{}a,bc}", - "key2": "\"abc\"" -} \ No newline at end of file diff --git a/json_examples/test_number.json b/json_examples/test_number.json deleted file mode 100644 index 5d472e5..0000000 --- a/json_examples/test_number.json +++ /dev/null @@ -1 +0,0 @@ -{"key":123} \ No newline at end of file From e4e256b7a03b3f43ef547a7521992c30b0ca896d Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Tue, 13 Aug 2024 14:40:15 -0600 Subject: [PATCH 61/99] redo all in `circuits.json` --- circuits.json | 45 ++++++++++----------------------------------- 1 file changed, 10 insertions(+), 35 deletions(-) diff --git a/circuits.json b/circuits.json index 6d656bb..f1c6e83 100644 --- a/circuits.json +++ b/circuits.json @@ -1,62 +1,37 @@ { - "extract_test": { + "extract": { "file": "extract", "template": "Extract", "params": [ - 12 - ] - }, - "test_extract_two_key": { - "file": "extract", - "template": "Extract", - "params": [ - 4, - 40 - ] - }, - "test_extract_depth": { - "file": "extract", - "template": "Extract", - "params": [ - 4, - 64 - ] - }, - "test_extract_sambhav": { - "file": "extract", - "template": "Extract", - "params": [ - 4, - 105 + 157 ] }, - "extract_array": { + "value_string": { "file": "extract", "template": "Extract", "params": [ - 15 + 12 ] }, - "extract_number": { + "value_number": { "file": "extract", "template": "Extract", "params": [ - 11 + 12 ] }, - "test_extract_hard": { + "value_array": { "file": "extract", "template": "Extract", "params": [ - 4, - 48 + 18 ] }, - "extract_example": { + "value_object": { "file": "extract", "template": "Extract", "params": [ - 157 + 21 ] } } \ No newline at end of file From ccb86bfb2990c9b02d07c904fe3d3a180b4d65d9 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Tue, 13 Aug 2024 14:47:12 -0600 Subject: [PATCH 62/99] changeable stack height --- circuits.json | 15 ++++++++++----- circuits/extract.circom | 5 ++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/circuits.json b/circuits.json index f1c6e83..9396cbc 100644 --- a/circuits.json +++ b/circuits.json @@ -3,35 +3,40 @@ "file": "extract", "template": "Extract", "params": [ - 157 + 157, + 13 ] }, "value_string": { "file": "extract", "template": "Extract", "params": [ - 12 + 12, + 2 ] }, "value_number": { "file": "extract", "template": "Extract", "params": [ - 12 + 12, + 2 ] }, "value_array": { "file": "extract", "template": "Extract", "params": [ - 18 + 18, + 3 ] }, "value_object": { "file": "extract", "template": "Extract", "params": [ - 21 + 21, + 4 ] } } \ No newline at end of file diff --git a/circuits/extract.circom b/circuits/extract.circom index 5e735e7..ce6983b 100644 --- a/circuits/extract.circom +++ b/circuits/extract.circom @@ -3,7 +3,7 @@ pragma circom 2.1.9; include "utils.circom"; include "parser.circom"; -template Extract(DATA_BYTES) { +template Extract(DATA_BYTES, MAX_STACK_HEIGHT) { signal input data[DATA_BYTES]; // TODO: Add assertions on the inputs here! @@ -14,7 +14,6 @@ template Extract(DATA_BYTES) { component dataASCII = ASCII(DATA_BYTES); dataASCII.in <== data; //--------------------------------------------------------------------------------------------// - var MAX_STACK_HEIGHT = 16; // Initialze the parser component State[DATA_BYTES]; State[0] = StateUpdate(MAX_STACK_HEIGHT); @@ -50,7 +49,7 @@ template Extract(DATA_BYTES) { // Debugging log("State[", DATA_BYTES, "].pointer ", "= ", State[DATA_BYTES -1].next_pointer); - for(var i = 0; i<4; i++) { + for(var i = 0; i < MAX_STACK_HEIGHT; i++) { log("State[", DATA_BYTES, "].stack[", i,"] ", "= ", State[DATA_BYTES -1 ].next_stack[i]); } log("State[", DATA_BYTES, "].parsing_string", "= ", State[DATA_BYTES-1].next_parsing_string); From 43d0a14af2c096226ef52d7d9894961ea8b669bd Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Tue, 13 Aug 2024 15:21:02 -0600 Subject: [PATCH 63/99] save state --- circuits/test/common/index.ts | 22 +- circuits/test/parser/index.ts | 43 ++ circuits/test/parser/mask.test.ts | 1 + circuits/test/parser/parser.test.ts | 585 +++++++++------------------- circuits/test/parser/stack.test.ts | 144 +++++++ circuits/test/utils/utils.test.ts | 471 +++++++++++----------- 6 files changed, 636 insertions(+), 630 deletions(-) create mode 100644 circuits/test/parser/index.ts create mode 100644 circuits/test/parser/mask.test.ts create mode 100644 circuits/test/parser/stack.test.ts diff --git a/circuits/test/common/index.ts b/circuits/test/common/index.ts index d187e36..8fea3d5 100644 --- a/circuits/test/common/index.ts +++ b/circuits/test/common/index.ts @@ -5,4 +5,24 @@ export const circomkit = new Circomkit({ verbose: false, }); -export { WitnessTester }; \ No newline at end of file +export { WitnessTester }; + +export function generatePassCase(input: any, expected: any, desc: string, circuit: any) { + const description = Object.entries(input) + .map(([key, value]) => `${key} = ${value}`) + .join(", "); + + it(`(valid) witness: ${description}\n${desc}`, async () => { + await circuit.expectPass(input, expected); + }); +} + +export function generateFailCase(input: any, desc: string, circuit: any) { + const description = Object.entries(input) + .map(([key, value]) => `${key} = ${value}`) + .join(", "); + + it(`(invalid) witness: ${description}\n${desc}`, async () => { + await circuit.expectFail(input); + }); +} \ No newline at end of file diff --git a/circuits/test/parser/index.ts b/circuits/test/parser/index.ts new file mode 100644 index 0000000..95e18b7 --- /dev/null +++ b/circuits/test/parser/index.ts @@ -0,0 +1,43 @@ +// constants.ts + +export const Delimiters = { + // ASCII char: `{` + START_BRACE: 123, + // ASCII char: `}` + END_BRACE: 125, + // ASCII char `[` + START_BRACKET: 91, + // ASCII char `]` + END_BRACKET: 93, + // ASCII char `"` + QUOTE: 34, + // ASCII char `:` + COLON: 58, + // ASCII char `,` + COMMA: 44, +}; + +export const WhiteSpace = { + // ASCII char: `\n` + NEWLINE: 10, + // ASCII char: ` ` + SPACE: 32, +}; + +export const Numbers = { + ZERO: 48, + ONE: 49, + TWO: 50, + THREE: 51, + FOUR: 52, + FIVE: 53, + SIX: 54, + SEVEN: 55, + EIGHT: 56, + NINE: 57 +} + +export const Escape = { + // ASCII char: `\` + BACKSLASH: 92, +}; \ No newline at end of file diff --git a/circuits/test/parser/mask.test.ts b/circuits/test/parser/mask.test.ts new file mode 100644 index 0000000..0ffdd02 --- /dev/null +++ b/circuits/test/parser/mask.test.ts @@ -0,0 +1 @@ +// TODO \ No newline at end of file diff --git a/circuits/test/parser/parser.test.ts b/circuits/test/parser/parser.test.ts index 4f6e7aa..3bc0e3d 100644 --- a/circuits/test/parser/parser.test.ts +++ b/circuits/test/parser/parser.test.ts @@ -1,404 +1,203 @@ -import { circomkit, WitnessTester } from "../common"; +import { circomkit, WitnessTester, generatePassCase, generateFailCase } from "../common"; +import { Delimiters, WhiteSpace, Numbers, Escape } from '.'; -describe("parser", () => { - describe("GetTopOfStack", () => { - let circuit: WitnessTester<["stack", "pointer"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`GetTopOfStack`, { - file: "circuits/parser", - template: "GetTopOfStack", - params: [4], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("witness: pointer = 4, stack = [0, 1, 2, 3, 4]", async () => { - await circuit.expectPass( - { pointer: 4, stack: [1, 2, 3, 4] }, - { out: 4 }, - ); - }); - - - }); - - //--------------------------------------------------------------------------------------------// - //-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", "pointer", "stack", "parsing_string", "parsing_number"], - ["next_pointer", "next_stack", "next_parsing_string", "next_parsing_number"] - >; - function generatePassCase(input: any, expected: any, desc: string) { - const description = Object.entries(input) - .map(([key, value]) => `${key} = ${value}`) - .join(", "); - it(`(valid) witness: ${description}\n${desc}`, async () => { - await circuit.expectPass(input, expected); - }); - } - - function generateFailCase(input: any, desc: string) { - const description = Object.entries(input) - .map(([key, value]) => `${key} = ${value}`) - .join(", "); - - it(`(invalid) witness: ${description}\n${desc}`, async () => { - await circuit.expectFail(input); - }); - } - - before(async () => { - circuit = await circomkit.WitnessTester(`StateUpdate`, { - file: "circuits/parser", - template: "StateUpdate", - params: [4], - }); - console.log("#constraints:", await circuit.getConstraintCount()); +describe("StateUpdate", () => { + let circuit: WitnessTester< + ["byte", "pointer", "stack", "parsing_string", "parsing_number"], + ["next_pointer", "next_stack", "next_parsing_string", "next_parsing_number"] + >; + before(async () => { + circuit = await circomkit.WitnessTester(`StateUpdate`, { + file: "circuits/parser", + template: "StateUpdate", + params: [4], }); + console.log("#constraints:", await circuit.getConstraintCount()); - let init = { - byte: 0, - pointer: 0, - stack: [0, 0, 0, 0], - parsing_string: 0, - parsing_number: 0, - }; - let out = { - next_pointer: init.pointer, - next_stack: init.stack, - next_parsing_string: init.parsing_string, - next_parsing_number: init.parsing_number, - }; - - - //-----------------------------------------------------------------------------// - // Test 1: - // init: ZEROS then read `do_nothing` byte - // expect: ZEROS - generatePassCase(init, out, ">>>> `NUL` read"); - //-----------------------------------------------------------------------------// - - //-----------------------------------------------------------------------------// - // Test 2: - // init: ZEROS -> `{` is read - // expect: pointer --> 1 - // stack --> [1,0,0,0] - let read_start_brace = { ...init }; - read_start_brace.byte = start_brace; - let read_start_brace_out = { ...out }; - read_start_brace_out.next_pointer = 1; - read_start_brace_out.next_stack = [1, 0, 0, 0]; - generatePassCase(read_start_brace, read_start_brace_out, ">>>> `{` read"); - //-----------------------------------------------------------------------------// - - //-----------------------------------------------------------------------------// - // Test 3: - // init: ZEROS -> `}` is read - // expect: FAIL stack underflow - let read_end_brace = { ...init }; - read_end_brace.byte = end_brace; - generateFailCase(read_end_brace, ">>>> `}` read --> (stack underflow)"); - //-----------------------------------------------------------------------------// - - //-----------------------------------------------------------------------------// - // Test 4: - // init: pointer == 1, stack = [1,0,0,0] -> `"` is read - // expect: parsing_string --> 1 - let in_object_find_key = { ...init }; - in_object_find_key.pointer = read_start_brace_out.next_pointer; - in_object_find_key.stack = read_start_brace_out.next_stack; - in_object_find_key.byte = quote; - let in_object_find_key_out = { ...out }; - in_object_find_key_out.next_pointer = in_object_find_key.pointer; - in_object_find_key_out.next_stack = in_object_find_key.stack; - in_object_find_key_out.next_parsing_string = 1; - generatePassCase(in_object_find_key, in_object_find_key_out, ">>>> `\"` read"); - //-----------------------------------------------------------------------------// - - //-----------------------------------------------------------------------------// - // Test 5: - // init: pointer == 1, stack = [1,0,0,0], parsing_string = 1 -> ` ` is read - // expect: parsing_string --> 1 - // in_key --> 1 - let in_key = { ...init }; - in_key.pointer = read_start_brace_out.next_pointer; - in_key.stack = read_start_brace_out.next_stack; - in_key.parsing_string = 1; - in_key.byte = space; - let in_key_out = { ...out }; - in_key_out.next_pointer = in_key.pointer; - in_key_out.next_stack = in_key.stack; - in_key_out.next_parsing_string = 1; - generatePassCase(in_key, in_key_out, ">>>> ` ` read"); - //-----------------------------------------------------------------------------// - - //-----------------------------------------------------------------------------// - // Test 6: - // init: pointer = 1, stack = [1,0,0,0], parsing_string = 1 setup -> `"` is read - // expect: parsing_string --> 0 - // - let in_key_to_exit = { ...init }; - in_key_to_exit.pointer = read_start_brace_out.next_pointer; - in_key_to_exit.stack = read_start_brace_out.next_stack; - in_key_to_exit.parsing_string = 1 - in_key_to_exit.byte = quote; - let in_key_to_exit_out = { ...out }; - in_key_to_exit_out.next_pointer = in_key_to_exit.pointer; - in_key_to_exit_out.next_stack = in_key_to_exit.stack; - generatePassCase(in_key_to_exit, in_key_to_exit_out, "`\"` read"); - //-----------------------------------------------------------------------------// - - //-----------------------------------------------------------------------------// - // Test 7: - // init: pointer = 1, stack = [1,0,0,0] -> `:` is read - let parsed_key_wait_to_parse_value = { ...init }; - parsed_key_wait_to_parse_value.pointer = read_start_brace_out.next_pointer; - parsed_key_wait_to_parse_value.stack = read_start_brace_out.next_stack; - 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_pointer = 2; - parsed_key_wait_to_parse_value_out.next_stack = [1, 3, 0, 0]; - generatePassCase(parsed_key_wait_to_parse_value, parsed_key_wait_to_parse_value_out, ">>>> `:` 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_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_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.inside_value = 1; - // in_value.byte = space; - // let in_value_out = { ...out }; - // in_value_out.next_tree_depth = 1; - // in_value_out.next_inside_value = 1; - // 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_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_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.byte = space; - // let in_end_of_kv_out = { ...out }; - // in_end_of_kv_out.next_tree_depth = 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 - // 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_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_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_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_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_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_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_key = 1; - // generatePassCase(end_of_key_to_inner_object, end_of_key_to_inner_object_out, ">>>> `{` is read"); - - - - - - - - - - - - - - - - - // TODO: Test stack fully works with brackets too - // Test 7: Stack Management - // init: read `{`, read another `{` - // expect: pointer --> 2 - // stack --> [1,1,0,0] - let in_object = { ...init }; - in_object.pointer = read_start_brace_out.next_pointer; - in_object.stack = read_start_brace_out.next_stack; - in_object.byte = start_brace; - let in_object_out = { ...out }; - in_object_out.next_pointer = 2; - in_object_out.next_stack = [1, 1, 0, 0]; - generatePassCase(in_object, in_object_out, ">>>> `{` read"); - - // Test 8: Stack Management - // init: read `{` then read`}` - // expect: pointer --> 0 - // stack --> [0, 0, 0, 0] - let in_object_to_leave = { ...init }; - in_object_to_leave.pointer = read_start_brace_out.next_pointer; - in_object_to_leave.stack = read_start_brace_out.next_stack; - in_object_to_leave.byte = end_brace; - let in_object_to_leave_out = { ...out }; - in_object_to_leave_out.next_pointer = 0; - in_object_to_leave_out.next_stack = [0, 0, 0, 0]; - generatePassCase(in_object_to_leave, in_object_to_leave_out, ">>>> `}` read"); - - // Test 9: Stack Management - // init: read `{`, then read `[` - // expect: pointer --> 2 - // stack --> [1,2,0,0] - let in_object_to_read_start_bracket = { ...init }; - in_object_to_read_start_bracket.byte = start_bracket; - in_object_to_read_start_bracket.pointer = 1; - in_object_to_read_start_bracket.stack = [1, 0, 0, 0]; - let in_object_to_read_start_bracket_out = { ...out }; - in_object_to_read_start_bracket_out.next_pointer = 2; - in_object_to_read_start_bracket_out.next_stack = [1, 2, 0, 0]; - generatePassCase(in_object_to_read_start_bracket, in_object_to_read_start_bracket_out, ">>>> `[` read"); - - // Test 10: Stack Management - // init: read 4x `{`, then read `{` - // expect: pointer --> 4 - // stack --> [1,1,1,1] - let in_max_stack = { ...init }; - in_max_stack.byte = start_brace; - in_max_stack.pointer = 4; - in_max_stack.stack = [1, 1, 1, 1]; - generateFailCase(in_max_stack, ">>>> `{` read --> (stack overflow)"); - - // Test 11: Stack Management - // init: read 4x `{`, then read `[` - // expect: pointer --> 4 - // stack --> [1,1,1,1] - let in_max_stack_2 = { ...init }; - in_max_stack_2.byte = start_bracket; - in_max_stack_2.pointer = 4; - in_max_stack_2.stack = [1, 1, 1, 1]; - generateFailCase(in_max_stack, ">>>> `[` read --> (stack overflow)"); - - // Test 12: Stack Management - // init: read `{` and `[`, then read `]` - // expect: pointer --> 2 - // stack --> [1,0,0,0] - let in_object_and_array = { ...init }; - in_object_and_array.byte = end_bracket; - in_object_and_array.pointer = 2; - in_object_and_array.stack = [1, 2, 0, 0]; - let in_object_and_array_out = { ...out }; - in_object_and_array_out.next_pointer = 1; - in_object_and_array_out.next_stack = [1, 0, 0, 0]; - generatePassCase(in_object_and_array, in_object_and_array_out, ">>>> `]` read"); - - // Test 12: Stack Management - // init: read `{` and `:`, then read `,` - // expect: pointer --> 2 - // stack --> [1,3,0,0] - let in_object_and_value = { ...init }; - in_object_and_value.byte = comma; - in_object_and_value.pointer = 2; - in_object_and_value.stack = [1, 3, 0, 0]; - let in_object_and_value_out = { ...out }; - in_object_and_value_out.next_pointer = 1; - in_object_and_value_out.next_stack = [1, 0, 0, 0]; - generatePassCase(in_object_and_value, in_object_and_value_out, ">>>> `,` read"); - - // Test 13: Stack Management - // init: read `{` and `:`, then read `,` - // expect: pointer --> 2 - // stack --> [1,3,0,0] - let in_object_and_value_to_leave_object = { ...init }; - in_object_and_value_to_leave_object.byte = end_brace; - in_object_and_value_to_leave_object.pointer = 2; - in_object_and_value_to_leave_object.stack = [1, 3, 0, 0]; - let in_object_and_value_to_leave_object_out = { ...out }; - in_object_and_value_to_leave_object_out.next_pointer = 0; - in_object_and_value_to_leave_object_out.next_stack = [0, 0, 0, 0]; - generatePassCase(in_object_and_value_to_leave_object, in_object_and_value_to_leave_object_out, ">>>> `,` read"); - - - - - - - - - //-----------------------------------------------------------------------------// - // Test SOMETHING: - // init: pointer = 1, stack = [1,2,0,0] -> `,` is read - let inside_array = { ...init }; - inside_array.pointer = 2; - inside_array.stack = [1, 2, 0, 0]; - inside_array.byte = comma; - let inside_array_out = { ...out }; - inside_array_out.next_pointer = 2; - inside_array_out.next_stack = [1, 2, 0, 0]; - generatePassCase(inside_array, inside_array_out, ">>>> `,` read"); - //-----------------------------------------------------------------------------// }); + let init = { + byte: 0, + pointer: 0, + stack: [0, 0, 0, 0], + parsing_string: 0, + parsing_number: 0, + }; + let out = { + next_pointer: init.pointer, + next_stack: init.stack, + next_parsing_string: init.parsing_string, + next_parsing_number: init.parsing_number, + }; + + + //-----------------------------------------------------------------------------// + // Test 1: + // init: ZEROS then read `do_nothing` byte + // expect: ZEROS + generatePassCase(init, out, ">>>> `NUL` read", circuit); + //-----------------------------------------------------------------------------// + + //-----------------------------------------------------------------------------// + // Test 2: + // init: ZEROS -> `{` is read + // expect: pointer --> 1 + // stack --> [1,0,0,0] + let read_start_brace = { ...init }; + read_start_brace.byte = Delimiters.START_BRACE; + let read_start_brace_out = { ...out }; + read_start_brace_out.next_pointer = 1; + read_start_brace_out.next_stack = [1, 0, 0, 0]; + generatePassCase(read_start_brace, read_start_brace_out, ">>>> `{` read"); + //-----------------------------------------------------------------------------// + + //-----------------------------------------------------------------------------// + // Test 3: + // init: ZEROS -> `}` is read + // expect: FAIL stack underflow + let read_end_brace = { ...init }; + read_end_brace.byte = Delimiters.END_BRACE; + generateFailCase(read_end_brace, ">>>> `}` read --> (stack underflow)"); + //-----------------------------------------------------------------------------// + + //-----------------------------------------------------------------------------// + // Test 4: + // init: pointer == 1, stack = [1,0,0,0] -> `"` is read + // expect: parsing_string --> 1 + let in_object_find_key = { ...init }; + in_object_find_key.pointer = read_start_brace_out.next_pointer; + in_object_find_key.stack = read_start_brace_out.next_stack; + in_object_find_key.byte = Delimiters.QUOTE; + let in_object_find_key_out = { ...out }; + in_object_find_key_out.next_pointer = in_object_find_key.pointer; + in_object_find_key_out.next_stack = in_object_find_key.stack; + in_object_find_key_out.next_parsing_string = 1; + generatePassCase(in_object_find_key, in_object_find_key_out, ">>>> `\"` read"); + //-----------------------------------------------------------------------------// + + //-----------------------------------------------------------------------------// + // Test 5: + // init: pointer == 1, stack = [1,0,0,0], parsing_string = 1 -> ` ` is read + // expect: parsing_string --> 1 + // in_key --> 1 + let in_key = { ...init }; + in_key.pointer = read_start_brace_out.next_pointer; + in_key.stack = read_start_brace_out.next_stack; + in_key.parsing_string = 1; + in_key.byte = WhiteSpace.SPACE; + let in_key_out = { ...out }; + in_key_out.next_pointer = in_key.pointer; + in_key_out.next_stack = in_key.stack; + in_key_out.next_parsing_string = 1; + generatePassCase(in_key, in_key_out, ">>>> ` ` read"); + //-----------------------------------------------------------------------------// + + //-----------------------------------------------------------------------------// + // Test 6: + // init: pointer = 1, stack = [1,0,0,0], parsing_string = 1 setup -> `"` is read + // expect: parsing_string --> 0 + // + let in_key_to_exit = { ...init }; + in_key_to_exit.pointer = read_start_brace_out.next_pointer; + in_key_to_exit.stack = read_start_brace_out.next_stack; + in_key_to_exit.parsing_string = 1 + in_key_to_exit.byte = Delimiters.QUOTE; + let in_key_to_exit_out = { ...out }; + in_key_to_exit_out.next_pointer = in_key_to_exit.pointer; + in_key_to_exit_out.next_stack = in_key_to_exit.stack; + generatePassCase(in_key_to_exit, in_key_to_exit_out, "`\"` read"); + //-----------------------------------------------------------------------------// + + //-----------------------------------------------------------------------------// + // Test 7: + // init: pointer = 1, stack = [1,0,0,0] -> `:` is read + let parsed_key_wait_to_parse_value = { ...init }; + parsed_key_wait_to_parse_value.pointer = read_start_brace_out.next_pointer; + parsed_key_wait_to_parse_value.stack = read_start_brace_out.next_stack; + parsed_key_wait_to_parse_value.byte = Delimiters.COLON; + let parsed_key_wait_to_parse_value_out = { ...out }; + parsed_key_wait_to_parse_value_out.next_pointer = 2; + parsed_key_wait_to_parse_value_out.next_stack = [1, 3, 0, 0]; + generatePassCase(parsed_key_wait_to_parse_value, parsed_key_wait_to_parse_value_out, ">>>> `:` 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_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_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.inside_value = 1; + // in_value.byte = space; + // let in_value_out = { ...out }; + // in_value_out.next_tree_depth = 1; + // in_value_out.next_inside_value = 1; + // 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_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_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.byte = space; + // let in_end_of_kv_out = { ...out }; + // in_end_of_kv_out.next_tree_depth = 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 + // 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_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_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_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_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_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_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_key = 1; + // generatePassCase(end_of_key_to_inner_object, end_of_key_to_inner_object_out, ">>>> `{` is read"); + }); + diff --git a/circuits/test/parser/stack.test.ts b/circuits/test/parser/stack.test.ts new file mode 100644 index 0000000..56f622e --- /dev/null +++ b/circuits/test/parser/stack.test.ts @@ -0,0 +1,144 @@ +import { circomkit, WitnessTester } from "../common"; +import { Delimiters, WhiteSpace, Numbers, Escape } from './constants'; + +describe("GetTopOfStack", () => { + let circuit: WitnessTester<["stack", "pointer"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`GetTopOfStack`, { + file: "circuits/parser", + template: "GetTopOfStack", + params: [4], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("witness: pointer = 4, stack = [0, 1, 2, 3, 4]", async () => { + await circuit.expectPass( + { pointer: 4, stack: [1, 2, 3, 4] }, + { out: 4 }, + ); + }); + + + + + // TODO: Test stack fully works with brackets too + // Test 7: Stack Management + // init: read `{`, read another `{` + // expect: pointer --> 2 + // stack --> [1,1,0,0] + let in_object = { ...init }; + in_object.pointer = read_start_brace_out.next_pointer; + in_object.stack = read_start_brace_out.next_stack; + in_object.byte = Delimiters.START_BRACE; + let in_object_out = { ...out }; + in_object_out.next_pointer = 2; + in_object_out.next_stack = [1, 1, 0, 0]; + generatePassCase(in_object, in_object_out, ">>>> `{` read"); + + // Test 8: Stack Management + // init: read `{` then read`}` + // expect: pointer --> 0 + // stack --> [0, 0, 0, 0] + let in_object_to_leave = { ...init }; + in_object_to_leave.pointer = read_start_brace_out.next_pointer; + in_object_to_leave.stack = read_start_brace_out.next_stack; + in_object_to_leave.byte = Delimiters.END_BRACE; + let in_object_to_leave_out = { ...out }; + in_object_to_leave_out.next_pointer = 0; + in_object_to_leave_out.next_stack = [0, 0, 0, 0]; + generatePassCase(in_object_to_leave, in_object_to_leave_out, ">>>> `}` read"); + + // Test 9: Stack Management + // init: read `{`, then read `[` + // expect: pointer --> 2 + // stack --> [1,2,0,0] + let in_object_to_read_start_bracket = { ...init }; + in_object_to_read_start_bracket.byte = Delimiters.START_BRACKET; + in_object_to_read_start_bracket.pointer = 1; + in_object_to_read_start_bracket.stack = [1, 0, 0, 0]; + let in_object_to_read_start_bracket_out = { ...out }; + in_object_to_read_start_bracket_out.next_pointer = 2; + in_object_to_read_start_bracket_out.next_stack = [1, 2, 0, 0]; + generatePassCase(in_object_to_read_start_bracket, in_object_to_read_start_bracket_out, ">>>> `[` read"); + + // Test 10: Stack Management + // init: read 4x `{`, then read `{` + // expect: pointer --> 4 + // stack --> [1,1,1,1] + let in_max_stack = { ...init }; + in_max_stack.byte = Delimiters.START_BRACE; + in_max_stack.pointer = 4; + in_max_stack.stack = [1, 1, 1, 1]; + generateFailCase(in_max_stack, ">>>> `{` read --> (stack overflow)"); + + // Test 11: Stack Management + // init: read 4x `{`, then read `[` + // expect: pointer --> 4 + // stack --> [1,1,1,1] + let in_max_stack_2 = { ...init }; + in_max_stack_2.byte = Delimiters.START_BRACKET; + in_max_stack_2.pointer = 4; + in_max_stack_2.stack = [1, 1, 1, 1]; + generateFailCase(in_max_stack, ">>>> `[` read --> (stack overflow)"); + + // Test 12: Stack Management + // init: read `{` and `[`, then read `]` + // expect: pointer --> 2 + // stack --> [1,0,0,0] + let in_object_and_array = { ...init }; + in_object_and_array.byte = Delimiters.END_BRACKET; + in_object_and_array.pointer = 2; + in_object_and_array.stack = [1, 2, 0, 0]; + let in_object_and_array_out = { ...out }; + in_object_and_array_out.next_pointer = 1; + in_object_and_array_out.next_stack = [1, 0, 0, 0]; + generatePassCase(in_object_and_array, in_object_and_array_out, ">>>> `]` read"); + + // Test 12: Stack Management + // init: read `{` and `:`, then read `,` + // expect: pointer --> 2 + // stack --> [1,3,0,0] + let in_object_and_value = { ...init }; + in_object_and_value.byte = Delimiters.COMMA; + in_object_and_value.pointer = 2; + in_object_and_value.stack = [1, 3, 0, 0]; + let in_object_and_value_out = { ...out }; + in_object_and_value_out.next_pointer = 1; + in_object_and_value_out.next_stack = [1, 0, 0, 0]; + generatePassCase(in_object_and_value, in_object_and_value_out, ">>>> `,` read"); + + // Test 13: Stack Management + // init: read `{` and `:`, then read `,` + // expect: pointer --> 2 + // stack --> [1,3,0,0] + let in_object_and_value_to_leave_object = { ...init }; + in_object_and_value_to_leave_object.byte = Delimiters.END_BRACE; + in_object_and_value_to_leave_object.pointer = 2; + in_object_and_value_to_leave_object.stack = [1, 3, 0, 0]; + let in_object_and_value_to_leave_object_out = { ...out }; + in_object_and_value_to_leave_object_out.next_pointer = 0; + in_object_and_value_to_leave_object_out.next_stack = [0, 0, 0, 0]; + generatePassCase(in_object_and_value_to_leave_object, in_object_and_value_to_leave_object_out, ">>>> `,` read"); + + + + + + + + + //-----------------------------------------------------------------------------// + // Test SOMETHING: + // init: pointer = 1, stack = [1,2,0,0] -> `,` is read + let inside_array = { ...init }; + inside_array.pointer = 2; + inside_array.stack = [1, 2, 0, 0]; + inside_array.byte = Delimiters.COMMA; + let inside_array_out = { ...out }; + inside_array_out.next_pointer = 2; + inside_array_out.next_stack = [1, 2, 0, 0]; + generatePassCase(inside_array, inside_array_out, ">>>> `,` read"); + //-----------------------------------------------------------------------------// + +}); \ No newline at end of file diff --git a/circuits/test/utils/utils.test.ts b/circuits/test/utils/utils.test.ts index f65493d..c92fd42 100644 --- a/circuits/test/utils/utils.test.ts +++ b/circuits/test/utils/utils.test.ts @@ -1,292 +1,291 @@ import { circomkit, WitnessTester } from "../common"; -describe("utils", () => { - describe("ASCII", () => { - let circuit: WitnessTester<["in"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`ASCII`, { - file: "circuits/utils", - template: "ASCII", - params: [13], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("(valid) witness: in = b\"Hello, world!\"", async () => { - await circuit.expectPass( - { in: [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] }, - ); - }); +describe("ASCII", () => { + let circuit: WitnessTester<["in"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`ASCII`, { + file: "circuits/utils", + template: "ASCII", + params: [13], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); - it("(invalid) witness: in = [256, ...]", async () => { - await circuit.expectFail( - { in: [256, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] } - ); - }); + it("(valid) witness: in = b\"Hello, world!\"", async () => { + await circuit.expectPass( + { in: [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] }, + ); }); - describe("IsEqualArray", () => { - let circuit: WitnessTester<["in"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`IsEqualArray`, { - file: "circuits/utils", - template: "IsEqualArray", - params: [3], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); + it("(invalid) witness: in = [256, ...]", async () => { + await circuit.expectFail( + { in: [256, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] } + ); + }); +}); - it("witness: [[0,0,0],[0,0,0]]", async () => { - await circuit.expectPass( - { in: [[0, 0, 0], [0, 0, 0]] }, - { out: 1 } - ); +describe("IsEqualArray", () => { + let circuit: WitnessTester<["in"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`IsEqualArray`, { + file: "circuits/utils", + template: "IsEqualArray", + params: [3], }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); - it("witness: [[1,420,69],[1,420,69]]", async () => { - await circuit.expectPass( - { in: [[1, 420, 69], [1, 420, 69]] }, - { out: 1 }, - ); - }); + it("witness: [[0,0,0],[0,0,0]]", async () => { + await circuit.expectPass( + { in: [[0, 0, 0], [0, 0, 0]] }, + { out: 1 } + ); + }); - it("witness: [[0,0,0],[1,420,69]]", async () => { - await circuit.expectPass( - { in: [[0, 0, 0], [1, 420, 69]] }, - { out: 0 }, - ); - }); + it("witness: [[1,420,69],[1,420,69]]", async () => { + await circuit.expectPass( + { in: [[1, 420, 69], [1, 420, 69]] }, + { out: 1 }, + ); + }); - it("witness: [[1,420,0],[1,420,69]]", async () => { - await circuit.expectPass( - { in: [[1, 420, 0], [1, 420, 69]] }, - { out: 0 }, - ); - }); + it("witness: [[0,0,0],[1,420,69]]", async () => { + await circuit.expectPass( + { in: [[0, 0, 0], [1, 420, 69]] }, + { out: 0 }, + ); + }); - it("witness: [[1,0,69],[1,420,69]]", async () => { - await circuit.expectPass( - { in: [[1, 0, 69], [1, 420, 69]] }, - { out: 0 }, - ); - }); + it("witness: [[1,420,0],[1,420,69]]", async () => { + await circuit.expectPass( + { in: [[1, 420, 0], [1, 420, 69]] }, + { out: 0 }, + ); + }); - it("witness: [[0,420,69],[1,420,69]]", async () => { - await circuit.expectPass( - { in: [[0, 420, 69], [1, 420, 69]] }, - { out: 0 }, - ); - }); + it("witness: [[1,0,69],[1,420,69]]", async () => { + await circuit.expectPass( + { in: [[1, 0, 69], [1, 420, 69]] }, + { out: 0 }, + ); }); - describe("Contains", () => { - let circuit: WitnessTester<["in", "array"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`Contains`, { - file: "circuits/utils", - template: "Contains", - params: [3], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); + it("witness: [[0,420,69],[1,420,69]]", async () => { + await circuit.expectPass( + { in: [[0, 420, 69], [1, 420, 69]] }, + { out: 0 }, + ); + }); +}); - it("witness: in = 0, array = [0,1,2]", async () => { - await circuit.expectPass( - { in: 0, array: [0, 1, 2] }, - { out: 1 } - ); +describe("Contains", () => { + let circuit: WitnessTester<["in", "array"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`Contains`, { + file: "circuits/utils", + template: "Contains", + params: [3], }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); - it("witness: in = 1, array = [0,1,2]", async () => { - await circuit.expectPass( - { in: 1, array: [0, 1, 2] }, - { out: 1 } - ); - }); + it("witness: in = 0, array = [0,1,2]", async () => { + await circuit.expectPass( + { in: 0, array: [0, 1, 2] }, + { out: 1 } + ); + }); - it("witness: in = 2, array = [0,1,2]", async () => { - await circuit.expectPass( - { in: 2, array: [0, 1, 2] }, - { out: 1 } - ); - }); + it("witness: in = 1, array = [0,1,2]", async () => { + await circuit.expectPass( + { in: 1, array: [0, 1, 2] }, + { out: 1 } + ); + }); - it("witness: in = 42069, array = [0,1,2]", async () => { - await circuit.expectPass( - { in: 42069, array: [0, 1, 2] }, - { out: 0 } - ); - }); + it("witness: in = 2, array = [0,1,2]", async () => { + await circuit.expectPass( + { in: 2, array: [0, 1, 2] }, + { out: 1 } + ); + }); + it("witness: in = 42069, array = [0,1,2]", async () => { + await circuit.expectPass( + { in: 42069, array: [0, 1, 2] }, + { out: 0 } + ); }); - describe("ArrayAdd", () => { - let circuit: WitnessTester<["lhs", "rhs"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`ArrayAdd`, { - file: "circuits/utils", - 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("ArrayAdd", () => { + let circuit: WitnessTester<["lhs", "rhs"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`ArrayAdd`, { + file: "circuits/utils", + 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/utils", - 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] } - ); +describe("ArrayMul", () => { + let circuit: WitnessTester<["lhs", "rhs"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`ArrayMul`, { + file: "circuits/utils", + template: "ArrayMul", + params: [3], }); - + console.log("#constraints:", await circuit.getConstraintCount()); }); - describe("InRange", () => { - let circuit: WitnessTester<["in", "range"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`InRange`, { - file: "circuits/utils", - template: "InRange", - params: [8], - }); - 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] } + ); + }); - it("witness: in = 1, range = [0,2]", async () => { - await circuit.expectPass( - { in: 1, range: [0, 2] }, - { out: 1 } - ); - }); +}); - it("witness: in = 69, range = [128,255]", async () => { - await circuit.expectPass( - { in: 69, range: [128, 255] }, - { out: 0 } - ); +describe("InRange", () => { + let circuit: WitnessTester<["in", "range"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`InRange`, { + file: "circuits/utils", + template: "InRange", + params: [8], }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); - it("witness: in = 200, range = [128,255]", async () => { - await circuit.expectPass( - { in: 1, range: [0, 2] }, - { out: 1 } - ); - }); + it("witness: in = 1, range = [0,2]", async () => { + await circuit.expectPass( + { in: 1, range: [0, 2] }, + { out: 1 } + ); }); - describe("Switch", () => { - let circuit: WitnessTester<["case", "branches", "vals"], ["match", "out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`Switch`, { - file: "circuits/utils", - template: "Switch", - params: [3], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); + it("witness: in = 69, range = [128,255]", async () => { + await circuit.expectPass( + { in: 69, range: [128, 255] }, + { out: 0 } + ); + }); - it("witness: case = 0, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { - await circuit.expectPass( - { case: 0, branches: [0, 1, 2], vals: [69, 420, 1337] }, - { match: 1, out: 69 }, - ); - }); + it("witness: in = 200, range = [128,255]", async () => { + await circuit.expectPass( + { in: 1, range: [0, 2] }, + { out: 1 } + ); + }); +}); - it("witness: case = 1, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { - await circuit.expectPass( - { case: 1, branches: [0, 1, 2], vals: [69, 420, 1337] }, - { match: 1, out: 420 }, - ); +describe("Switch", () => { + let circuit: WitnessTester<["case", "branches", "vals"], ["match", "out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`Switch`, { + file: "circuits/utils", + template: "Switch", + params: [3], }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); - it("witness: case = 2, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { - await circuit.expectPass( - { case: 2, branches: [0, 1, 2], vals: [69, 420, 1337] }, - { match: 1, out: 1337 }, - ); - }); + it("witness: case = 0, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { + await circuit.expectPass( + { case: 0, branches: [0, 1, 2], vals: [69, 420, 1337] }, + { match: 1, out: 69 }, + ); + }); - it("witness: case = 3, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { - await circuit.expectPass( - { case: 3, branches: [0, 1, 2], vals: [69, 420, 1337] }, - { match: 0, out: 0 }, - ); - }); + it("witness: case = 1, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { + await circuit.expectPass( + { case: 1, branches: [0, 1, 2], vals: [69, 420, 1337] }, + { match: 1, out: 420 }, + ); + }); + it("witness: case = 2, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { + await circuit.expectPass( + { case: 2, branches: [0, 1, 2], vals: [69, 420, 1337] }, + { match: 1, out: 1337 }, + ); + }); + it("witness: case = 3, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { + await circuit.expectPass( + { case: 3, branches: [0, 1, 2], vals: [69, 420, 1337] }, + { match: 0, out: 0 }, + ); }); - describe("SwitchArray", () => { - let circuit: WitnessTester<["case", "branches", "vals"], ["match", "out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`SwitchArray`, { - file: "circuits/utils", - template: "SwitchArray", - params: [3, 2], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - it("witness: case = 0, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { - await circuit.expectPass( - { case: 0, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, - { match: 1, out: [69, 0] }, - ); - }); +}); - it("witness: case = 1, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { - await circuit.expectPass( - { case: 1, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, - { match: 1, out: [420, 1] }, - ); +describe("SwitchArray", () => { + let circuit: WitnessTester<["case", "branches", "vals"], ["match", "out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`SwitchArray`, { + file: "circuits/utils", + template: "SwitchArray", + params: [3, 2], }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); - it("witness: case = 2, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { - await circuit.expectPass( - { case: 2, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, - { match: 1, out: [1337, 2] }, - ); - }); + it("witness: case = 0, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { + await circuit.expectPass( + { case: 0, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, + { match: 1, out: [69, 0] }, + ); + }); - it("witness: case = 3, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { - await circuit.expectPass( - { case: 3, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, - { match: 0, out: [0, 0] } - ); - }); + it("witness: case = 1, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { + await circuit.expectPass( + { case: 1, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, + { match: 1, out: [420, 1] }, + ); + }); - it("witness: case = 420, branches = [69, 420, 1337], vals = [[10,3], [20,5], [30,7]]", async () => { - await circuit.expectPass( - { case: 420, branches: [69, 420, 1337], vals: [[10, 3], [20, 5], [30, 7]] }, - { match: 1, out: [20, 5] } - ); - }); + it("witness: case = 2, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { + await circuit.expectPass( + { case: 2, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, + { match: 1, out: [1337, 2] }, + ); + }); - it("witness: case = 0, branches = [69, 420, 1337], vals = [[10,3], [20,5], [30,7]]", async () => { - await circuit.expectPass( - { case: 0, branches: [69, 420, 1337], vals: [[10, 3], [20, 5], [30, 7]] }, - { match: 0, out: [0, 0] } - ); - }); + it("witness: case = 3, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { + await circuit.expectPass( + { case: 3, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, + { match: 0, out: [0, 0] } + ); + }); + it("witness: case = 420, branches = [69, 420, 1337], vals = [[10,3], [20,5], [30,7]]", async () => { + await circuit.expectPass( + { case: 420, branches: [69, 420, 1337], vals: [[10, 3], [20, 5], [30, 7]] }, + { match: 1, out: [20, 5] } + ); }); + + it("witness: case = 0, branches = [69, 420, 1337], vals = [[10,3], [20,5], [30,7]]", async () => { + await circuit.expectPass( + { case: 0, branches: [69, 420, 1337], vals: [[10, 3], [20, 5], [30, 7]] }, + { match: 0, out: [0, 0] } + ); + }); + }); + From 304987fc3e5a86b57f349ab60951c5a67d932930 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Tue, 13 Aug 2024 15:38:02 -0600 Subject: [PATCH 64/99] reorganize tests --- circuits/test/common/index.ts | 19 ------- circuits/test/parser/index.ts | 15 ++++++ circuits/test/parser/parser.test.ts | 63 ++++++++++++---------- circuits/test/parser/stack.test.ts | 82 ++++++++++++++++++----------- 4 files changed, 101 insertions(+), 78 deletions(-) diff --git a/circuits/test/common/index.ts b/circuits/test/common/index.ts index 8fea3d5..a5ebae5 100644 --- a/circuits/test/common/index.ts +++ b/circuits/test/common/index.ts @@ -7,22 +7,3 @@ export const circomkit = new Circomkit({ export { WitnessTester }; -export function generatePassCase(input: any, expected: any, desc: string, circuit: any) { - const description = Object.entries(input) - .map(([key, value]) => `${key} = ${value}`) - .join(", "); - - it(`(valid) witness: ${description}\n${desc}`, async () => { - await circuit.expectPass(input, expected); - }); -} - -export function generateFailCase(input: any, desc: string, circuit: any) { - const description = Object.entries(input) - .map(([key, value]) => `${key} = ${value}`) - .join(", "); - - it(`(invalid) witness: ${description}\n${desc}`, async () => { - await circuit.expectFail(input); - }); -} \ No newline at end of file diff --git a/circuits/test/parser/index.ts b/circuits/test/parser/index.ts index 95e18b7..1c5bb69 100644 --- a/circuits/test/parser/index.ts +++ b/circuits/test/parser/index.ts @@ -40,4 +40,19 @@ export const Numbers = { export const Escape = { // ASCII char: `\` BACKSLASH: 92, +}; + +export const INITIAL_IN = { + byte: 0, + pointer: 0, + stack: [0, 0, 0, 0], + parsing_string: 0, + parsing_number: 0, +}; + +export const INITIAL_OUT = { + next_pointer: INITIAL_IN.pointer, + next_stack: INITIAL_IN.stack, + next_parsing_string: INITIAL_IN.parsing_string, + next_parsing_number: INITIAL_IN.parsing_number, }; \ No newline at end of file diff --git a/circuits/test/parser/parser.test.ts b/circuits/test/parser/parser.test.ts index 3bc0e3d..d7722a1 100644 --- a/circuits/test/parser/parser.test.ts +++ b/circuits/test/parser/parser.test.ts @@ -1,5 +1,5 @@ -import { circomkit, WitnessTester, generatePassCase, generateFailCase } from "../common"; -import { Delimiters, WhiteSpace, Numbers, Escape } from '.'; +import { circomkit, WitnessTester } from "../common"; +import { Delimiters, WhiteSpace, Numbers, Escape, INITIAL_IN, INITIAL_OUT } from '.'; @@ -9,6 +9,26 @@ describe("StateUpdate", () => { ["next_pointer", "next_stack", "next_parsing_string", "next_parsing_number"] >; + function generatePassCase(input: any, expected: any, desc: string) { + const description = Object.entries(input) + .map(([key, value]) => `${key} = ${value}`) + .join(", "); + + it(`(valid) witness: ${description}\n${desc}`, async () => { + await circuit.expectPass(input, expected); + }); + } + + function generateFailCase(input: any, desc: string) { + const description = Object.entries(input) + .map(([key, value]) => `${key} = ${value}`) + .join(", "); + + it(`(invalid) witness: ${description}\n${desc}`, async () => { + await circuit.expectFail(input); + }); + } + before(async () => { circuit = await circomkit.WitnessTester(`StateUpdate`, { file: "circuits/parser", @@ -19,26 +39,11 @@ describe("StateUpdate", () => { }); - let init = { - byte: 0, - pointer: 0, - stack: [0, 0, 0, 0], - parsing_string: 0, - parsing_number: 0, - }; - let out = { - next_pointer: init.pointer, - next_stack: init.stack, - next_parsing_string: init.parsing_string, - next_parsing_number: init.parsing_number, - }; - - //-----------------------------------------------------------------------------// // Test 1: // init: ZEROS then read `do_nothing` byte // expect: ZEROS - generatePassCase(init, out, ">>>> `NUL` read", circuit); + generatePassCase(INITIAL_IN, INITIAL_OUT, ">>>> `NUL` read"); //-----------------------------------------------------------------------------// //-----------------------------------------------------------------------------// @@ -46,9 +51,9 @@ describe("StateUpdate", () => { // init: ZEROS -> `{` is read // expect: pointer --> 1 // stack --> [1,0,0,0] - let read_start_brace = { ...init }; + let read_start_brace = { ...INITIAL_IN }; read_start_brace.byte = Delimiters.START_BRACE; - let read_start_brace_out = { ...out }; + let read_start_brace_out = { ...INITIAL_OUT }; read_start_brace_out.next_pointer = 1; read_start_brace_out.next_stack = [1, 0, 0, 0]; generatePassCase(read_start_brace, read_start_brace_out, ">>>> `{` read"); @@ -58,7 +63,7 @@ describe("StateUpdate", () => { // Test 3: // init: ZEROS -> `}` is read // expect: FAIL stack underflow - let read_end_brace = { ...init }; + let read_end_brace = { ...INITIAL_IN }; read_end_brace.byte = Delimiters.END_BRACE; generateFailCase(read_end_brace, ">>>> `}` read --> (stack underflow)"); //-----------------------------------------------------------------------------// @@ -67,11 +72,11 @@ describe("StateUpdate", () => { // Test 4: // init: pointer == 1, stack = [1,0,0,0] -> `"` is read // expect: parsing_string --> 1 - let in_object_find_key = { ...init }; + let in_object_find_key = { ...INITIAL_IN }; in_object_find_key.pointer = read_start_brace_out.next_pointer; in_object_find_key.stack = read_start_brace_out.next_stack; in_object_find_key.byte = Delimiters.QUOTE; - let in_object_find_key_out = { ...out }; + let in_object_find_key_out = { ...INITIAL_OUT }; in_object_find_key_out.next_pointer = in_object_find_key.pointer; in_object_find_key_out.next_stack = in_object_find_key.stack; in_object_find_key_out.next_parsing_string = 1; @@ -83,12 +88,12 @@ describe("StateUpdate", () => { // init: pointer == 1, stack = [1,0,0,0], parsing_string = 1 -> ` ` is read // expect: parsing_string --> 1 // in_key --> 1 - let in_key = { ...init }; + let in_key = { ...INITIAL_IN }; in_key.pointer = read_start_brace_out.next_pointer; in_key.stack = read_start_brace_out.next_stack; in_key.parsing_string = 1; in_key.byte = WhiteSpace.SPACE; - let in_key_out = { ...out }; + let in_key_out = { ...INITIAL_OUT }; in_key_out.next_pointer = in_key.pointer; in_key_out.next_stack = in_key.stack; in_key_out.next_parsing_string = 1; @@ -100,12 +105,12 @@ describe("StateUpdate", () => { // init: pointer = 1, stack = [1,0,0,0], parsing_string = 1 setup -> `"` is read // expect: parsing_string --> 0 // - let in_key_to_exit = { ...init }; + let in_key_to_exit = { ...INITIAL_IN }; in_key_to_exit.pointer = read_start_brace_out.next_pointer; in_key_to_exit.stack = read_start_brace_out.next_stack; in_key_to_exit.parsing_string = 1 in_key_to_exit.byte = Delimiters.QUOTE; - let in_key_to_exit_out = { ...out }; + let in_key_to_exit_out = { ...INITIAL_OUT }; in_key_to_exit_out.next_pointer = in_key_to_exit.pointer; in_key_to_exit_out.next_stack = in_key_to_exit.stack; generatePassCase(in_key_to_exit, in_key_to_exit_out, "`\"` read"); @@ -114,11 +119,11 @@ describe("StateUpdate", () => { //-----------------------------------------------------------------------------// // Test 7: // init: pointer = 1, stack = [1,0,0,0] -> `:` is read - let parsed_key_wait_to_parse_value = { ...init }; + let parsed_key_wait_to_parse_value = { ...INITIAL_IN }; parsed_key_wait_to_parse_value.pointer = read_start_brace_out.next_pointer; parsed_key_wait_to_parse_value.stack = read_start_brace_out.next_stack; parsed_key_wait_to_parse_value.byte = Delimiters.COLON; - let parsed_key_wait_to_parse_value_out = { ...out }; + let parsed_key_wait_to_parse_value_out = { ...INITIAL_OUT }; parsed_key_wait_to_parse_value_out.next_pointer = 2; parsed_key_wait_to_parse_value_out.next_stack = [1, 3, 0, 0]; generatePassCase(parsed_key_wait_to_parse_value, parsed_key_wait_to_parse_value_out, ">>>> `:` read"); diff --git a/circuits/test/parser/stack.test.ts b/circuits/test/parser/stack.test.ts index 56f622e..dca3232 100644 --- a/circuits/test/parser/stack.test.ts +++ b/circuits/test/parser/stack.test.ts @@ -1,5 +1,5 @@ import { circomkit, WitnessTester } from "../common"; -import { Delimiters, WhiteSpace, Numbers, Escape } from './constants'; +import { Delimiters, WhiteSpace, Numbers, Escape, INITIAL_IN, INITIAL_OUT } from '.'; describe("GetTopOfStack", () => { let circuit: WitnessTester<["stack", "pointer"], ["out"]>; @@ -18,20 +18,51 @@ describe("GetTopOfStack", () => { { out: 4 }, ); }); +}); +describe("StateUpdate :: RewriteStack", () => { + let circuit: WitnessTester< + ["byte", "pointer", "stack", "parsing_string", "parsing_number"], + ["next_pointer", "next_stack", "next_parsing_string", "next_parsing_number"] + >; + before(async () => { + circuit = await circomkit.WitnessTester(`GetTopOfStack`, { + file: "circuits/parser", + template: "StateUpdate", + params: [4], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + function generatePassCase(input: any, expected: any, desc: string) { + const description = Object.entries(input) + .map(([key, value]) => `${key} = ${value}`) + .join(", "); + it(`(valid) witness: ${description}\n${desc}`, async () => { + await circuit.expectPass(input, expected); + }); + } + function generateFailCase(input: any, desc: string) { + const description = Object.entries(input) + .map(([key, value]) => `${key} = ${value}`) + .join(", "); + + it(`(invalid) witness: ${description}\n${desc}`, async () => { + await circuit.expectFail(input); + }); + } // TODO: Test stack fully works with brackets too // Test 7: Stack Management // init: read `{`, read another `{` // expect: pointer --> 2 // stack --> [1,1,0,0] - let in_object = { ...init }; - in_object.pointer = read_start_brace_out.next_pointer; - in_object.stack = read_start_brace_out.next_stack; + let in_object = { ...INITIAL_IN }; + in_object.pointer = 1; + in_object.stack = [1, 0, 0, 0]; in_object.byte = Delimiters.START_BRACE; - let in_object_out = { ...out }; + let in_object_out = { ...INITIAL_OUT }; in_object_out.next_pointer = 2; in_object_out.next_stack = [1, 1, 0, 0]; generatePassCase(in_object, in_object_out, ">>>> `{` read"); @@ -40,24 +71,22 @@ describe("GetTopOfStack", () => { // init: read `{` then read`}` // expect: pointer --> 0 // stack --> [0, 0, 0, 0] - let in_object_to_leave = { ...init }; - in_object_to_leave.pointer = read_start_brace_out.next_pointer; - in_object_to_leave.stack = read_start_brace_out.next_stack; + let in_object_to_leave = { ...INITIAL_IN }; + in_object_to_leave.pointer = 1; + in_object_to_leave.stack = [1, 0, 0, 0]; in_object_to_leave.byte = Delimiters.END_BRACE; - let in_object_to_leave_out = { ...out }; - in_object_to_leave_out.next_pointer = 0; - in_object_to_leave_out.next_stack = [0, 0, 0, 0]; + let in_object_to_leave_out = { ...INITIAL_OUT }; generatePassCase(in_object_to_leave, in_object_to_leave_out, ">>>> `}` read"); // Test 9: Stack Management // init: read `{`, then read `[` // expect: pointer --> 2 // stack --> [1,2,0,0] - let in_object_to_read_start_bracket = { ...init }; + let in_object_to_read_start_bracket = { ...INITIAL_IN }; in_object_to_read_start_bracket.byte = Delimiters.START_BRACKET; in_object_to_read_start_bracket.pointer = 1; in_object_to_read_start_bracket.stack = [1, 0, 0, 0]; - let in_object_to_read_start_bracket_out = { ...out }; + let in_object_to_read_start_bracket_out = { ...INITIAL_OUT }; in_object_to_read_start_bracket_out.next_pointer = 2; in_object_to_read_start_bracket_out.next_stack = [1, 2, 0, 0]; generatePassCase(in_object_to_read_start_bracket, in_object_to_read_start_bracket_out, ">>>> `[` read"); @@ -66,7 +95,7 @@ describe("GetTopOfStack", () => { // init: read 4x `{`, then read `{` // expect: pointer --> 4 // stack --> [1,1,1,1] - let in_max_stack = { ...init }; + let in_max_stack = { ...INITIAL_IN }; in_max_stack.byte = Delimiters.START_BRACE; in_max_stack.pointer = 4; in_max_stack.stack = [1, 1, 1, 1]; @@ -76,7 +105,7 @@ describe("GetTopOfStack", () => { // init: read 4x `{`, then read `[` // expect: pointer --> 4 // stack --> [1,1,1,1] - let in_max_stack_2 = { ...init }; + let in_max_stack_2 = { ...INITIAL_IN }; in_max_stack_2.byte = Delimiters.START_BRACKET; in_max_stack_2.pointer = 4; in_max_stack_2.stack = [1, 1, 1, 1]; @@ -86,11 +115,11 @@ describe("GetTopOfStack", () => { // init: read `{` and `[`, then read `]` // expect: pointer --> 2 // stack --> [1,0,0,0] - let in_object_and_array = { ...init }; + let in_object_and_array = { ...INITIAL_IN }; in_object_and_array.byte = Delimiters.END_BRACKET; in_object_and_array.pointer = 2; in_object_and_array.stack = [1, 2, 0, 0]; - let in_object_and_array_out = { ...out }; + let in_object_and_array_out = { ...INITIAL_OUT }; in_object_and_array_out.next_pointer = 1; in_object_and_array_out.next_stack = [1, 0, 0, 0]; generatePassCase(in_object_and_array, in_object_and_array_out, ">>>> `]` read"); @@ -99,11 +128,11 @@ describe("GetTopOfStack", () => { // init: read `{` and `:`, then read `,` // expect: pointer --> 2 // stack --> [1,3,0,0] - let in_object_and_value = { ...init }; + let in_object_and_value = { ...INITIAL_IN }; in_object_and_value.byte = Delimiters.COMMA; in_object_and_value.pointer = 2; in_object_and_value.stack = [1, 3, 0, 0]; - let in_object_and_value_out = { ...out }; + let in_object_and_value_out = { ...INITIAL_OUT }; in_object_and_value_out.next_pointer = 1; in_object_and_value_out.next_stack = [1, 0, 0, 0]; generatePassCase(in_object_and_value, in_object_and_value_out, ">>>> `,` read"); @@ -112,30 +141,23 @@ describe("GetTopOfStack", () => { // init: read `{` and `:`, then read `,` // expect: pointer --> 2 // stack --> [1,3,0,0] - let in_object_and_value_to_leave_object = { ...init }; + let in_object_and_value_to_leave_object = { ...INITIAL_IN }; in_object_and_value_to_leave_object.byte = Delimiters.END_BRACE; in_object_and_value_to_leave_object.pointer = 2; in_object_and_value_to_leave_object.stack = [1, 3, 0, 0]; - let in_object_and_value_to_leave_object_out = { ...out }; + let in_object_and_value_to_leave_object_out = { ...INITIAL_OUT }; in_object_and_value_to_leave_object_out.next_pointer = 0; in_object_and_value_to_leave_object_out.next_stack = [0, 0, 0, 0]; generatePassCase(in_object_and_value_to_leave_object, in_object_and_value_to_leave_object_out, ">>>> `,` read"); - - - - - - - //-----------------------------------------------------------------------------// // Test SOMETHING: // init: pointer = 1, stack = [1,2,0,0] -> `,` is read - let inside_array = { ...init }; + let inside_array = { ...INITIAL_IN }; inside_array.pointer = 2; inside_array.stack = [1, 2, 0, 0]; inside_array.byte = Delimiters.COMMA; - let inside_array_out = { ...out }; + let inside_array_out = { ...INITIAL_OUT }; inside_array_out.next_pointer = 2; inside_array_out.next_stack = [1, 2, 0, 0]; generatePassCase(inside_array, inside_array_out, ">>>> `,` read"); From 9e4426cb41bd26f145f7510873e8ee11e0474050 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Tue, 13 Aug 2024 15:42:23 -0600 Subject: [PATCH 65/99] small cleanup --- circuits/test/parser/stack.test.ts | 47 +++++++++++++++++++----------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/circuits/test/parser/stack.test.ts b/circuits/test/parser/stack.test.ts index dca3232..b673483 100644 --- a/circuits/test/parser/stack.test.ts +++ b/circuits/test/parser/stack.test.ts @@ -53,8 +53,8 @@ describe("StateUpdate :: RewriteStack", () => { }); } - // TODO: Test stack fully works with brackets too - // Test 7: Stack Management + + //-TEST_1----------------------------------------------------------// // init: read `{`, read another `{` // expect: pointer --> 2 // stack --> [1,1,0,0] @@ -67,7 +67,7 @@ describe("StateUpdate :: RewriteStack", () => { in_object_out.next_stack = [1, 1, 0, 0]; generatePassCase(in_object, in_object_out, ">>>> `{` read"); - // Test 8: Stack Management + //-TEST_2----------------------------------------------------------// // init: read `{` then read`}` // expect: pointer --> 0 // stack --> [0, 0, 0, 0] @@ -76,9 +76,12 @@ describe("StateUpdate :: RewriteStack", () => { in_object_to_leave.stack = [1, 0, 0, 0]; in_object_to_leave.byte = Delimiters.END_BRACE; let in_object_to_leave_out = { ...INITIAL_OUT }; - generatePassCase(in_object_to_leave, in_object_to_leave_out, ">>>> `}` read"); + generatePassCase(in_object_to_leave, + in_object_to_leave_out, + ">>>> `}` read" + ); - // Test 9: Stack Management + //-TEST_3----------------------------------------------------------// // init: read `{`, then read `[` // expect: pointer --> 2 // stack --> [1,2,0,0] @@ -89,9 +92,12 @@ describe("StateUpdate :: RewriteStack", () => { let in_object_to_read_start_bracket_out = { ...INITIAL_OUT }; in_object_to_read_start_bracket_out.next_pointer = 2; in_object_to_read_start_bracket_out.next_stack = [1, 2, 0, 0]; - generatePassCase(in_object_to_read_start_bracket, in_object_to_read_start_bracket_out, ">>>> `[` read"); + generatePassCase(in_object_to_read_start_bracket, + in_object_to_read_start_bracket_out, + ">>>> `[` read" + ); - // Test 10: Stack Management + //-TEST_4----------------------------------------------------------// // init: read 4x `{`, then read `{` // expect: pointer --> 4 // stack --> [1,1,1,1] @@ -101,7 +107,7 @@ describe("StateUpdate :: RewriteStack", () => { in_max_stack.stack = [1, 1, 1, 1]; generateFailCase(in_max_stack, ">>>> `{` read --> (stack overflow)"); - // Test 11: Stack Management + //-TEST_5----------------------------------------------------------// // init: read 4x `{`, then read `[` // expect: pointer --> 4 // stack --> [1,1,1,1] @@ -111,7 +117,7 @@ describe("StateUpdate :: RewriteStack", () => { in_max_stack_2.stack = [1, 1, 1, 1]; generateFailCase(in_max_stack, ">>>> `[` read --> (stack overflow)"); - // Test 12: Stack Management + //-TEST_6----------------------------------------------------------// // init: read `{` and `[`, then read `]` // expect: pointer --> 2 // stack --> [1,0,0,0] @@ -122,9 +128,12 @@ describe("StateUpdate :: RewriteStack", () => { let in_object_and_array_out = { ...INITIAL_OUT }; in_object_and_array_out.next_pointer = 1; in_object_and_array_out.next_stack = [1, 0, 0, 0]; - generatePassCase(in_object_and_array, in_object_and_array_out, ">>>> `]` read"); + generatePassCase(in_object_and_array, + in_object_and_array_out, + ">>>> `]` read" + ); - // Test 12: Stack Management + //-TEST_7----------------------------------------------------------// // init: read `{` and `:`, then read `,` // expect: pointer --> 2 // stack --> [1,3,0,0] @@ -135,9 +144,12 @@ describe("StateUpdate :: RewriteStack", () => { let in_object_and_value_out = { ...INITIAL_OUT }; in_object_and_value_out.next_pointer = 1; in_object_and_value_out.next_stack = [1, 0, 0, 0]; - generatePassCase(in_object_and_value, in_object_and_value_out, ">>>> `,` read"); + generatePassCase(in_object_and_value, + in_object_and_value_out, + ">>>> `,` read" + ); - // Test 13: Stack Management + //-TEST_8----------------------------------------------------------// // init: read `{` and `:`, then read `,` // expect: pointer --> 2 // stack --> [1,3,0,0] @@ -148,10 +160,12 @@ describe("StateUpdate :: RewriteStack", () => { let in_object_and_value_to_leave_object_out = { ...INITIAL_OUT }; in_object_and_value_to_leave_object_out.next_pointer = 0; in_object_and_value_to_leave_object_out.next_stack = [0, 0, 0, 0]; - generatePassCase(in_object_and_value_to_leave_object, in_object_and_value_to_leave_object_out, ">>>> `,` read"); + generatePassCase(in_object_and_value_to_leave_object, + in_object_and_value_to_leave_object_out, + ">>>> `,` read" + ); - //-----------------------------------------------------------------------------// - // Test SOMETHING: + //-TEST_9----------------------------------------------------------// // init: pointer = 1, stack = [1,2,0,0] -> `,` is read let inside_array = { ...INITIAL_IN }; inside_array.pointer = 2; @@ -161,6 +175,5 @@ describe("StateUpdate :: RewriteStack", () => { inside_array_out.next_pointer = 2; inside_array_out.next_stack = [1, 2, 0, 0]; generatePassCase(inside_array, inside_array_out, ">>>> `,` read"); - //-----------------------------------------------------------------------------// }); \ No newline at end of file From 70688ba48417df4a8fa6e2ee093f5cb261fa9a9a Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Tue, 13 Aug 2024 16:06:38 -0600 Subject: [PATCH 66/99] continuing test cleanup --- circuits/test/parser/parser.test.ts | 143 ++++++++++++---------------- circuits/test/parser/stack.test.ts | 22 ++++- 2 files changed, 78 insertions(+), 87 deletions(-) diff --git a/circuits/test/parser/parser.test.ts b/circuits/test/parser/parser.test.ts index d7722a1..db8ee46 100644 --- a/circuits/test/parser/parser.test.ts +++ b/circuits/test/parser/parser.test.ts @@ -39,16 +39,15 @@ describe("StateUpdate", () => { }); - //-----------------------------------------------------------------------------// - // Test 1: + //-TEST_1----------------------------------------------------------// // init: ZEROS then read `do_nothing` byte // expect: ZEROS generatePassCase(INITIAL_IN, INITIAL_OUT, ">>>> `NUL` read"); - //-----------------------------------------------------------------------------// - //-----------------------------------------------------------------------------// - // Test 2: - // init: ZEROS -> `{` is read + // TODO: Consider moving to `stack.test.ts` + //-TEST_2----------------------------------------------------------// + // init: INIT + // read: `{` // expect: pointer --> 1 // stack --> [1,0,0,0] let read_start_brace = { ...INITIAL_IN }; @@ -57,110 +56,86 @@ describe("StateUpdate", () => { read_start_brace_out.next_pointer = 1; read_start_brace_out.next_stack = [1, 0, 0, 0]; generatePassCase(read_start_brace, read_start_brace_out, ">>>> `{` read"); - //-----------------------------------------------------------------------------// - //-----------------------------------------------------------------------------// - // Test 3: - // init: ZEROS -> `}` is read - // expect: FAIL stack underflow + //-TEST_3----------------------------------------------------------// + // state: INIT + // read: `}` + // expect: FAIL (stack underflow) let read_end_brace = { ...INITIAL_IN }; read_end_brace.byte = Delimiters.END_BRACE; generateFailCase(read_end_brace, ">>>> `}` read --> (stack underflow)"); - //-----------------------------------------------------------------------------// - //-----------------------------------------------------------------------------// - // Test 4: - // init: pointer == 1, stack = [1,0,0,0] -> `"` is read + //-TEST_4----------------------------------------------------------// + // state: pointer == 1, stack == [1,0,0,0] + // read: `"` // expect: parsing_string --> 1 let in_object_find_key = { ...INITIAL_IN }; - in_object_find_key.pointer = read_start_brace_out.next_pointer; - in_object_find_key.stack = read_start_brace_out.next_stack; + in_object_find_key.pointer = 1; + in_object_find_key.stack = [1, 0, 0, 0]; in_object_find_key.byte = Delimiters.QUOTE; let in_object_find_key_out = { ...INITIAL_OUT }; - in_object_find_key_out.next_pointer = in_object_find_key.pointer; - in_object_find_key_out.next_stack = in_object_find_key.stack; + in_object_find_key_out.next_pointer = 1; + in_object_find_key_out.next_stack = [1, 0, 0, 0]; in_object_find_key_out.next_parsing_string = 1; generatePassCase(in_object_find_key, in_object_find_key_out, ">>>> `\"` read"); - //-----------------------------------------------------------------------------// - //-----------------------------------------------------------------------------// - // Test 5: - // init: pointer == 1, stack = [1,0,0,0], parsing_string = 1 -> ` ` is read - // expect: parsing_string --> 1 - // in_key --> 1 + //-TEST_5----------------------------------------------------------// + // state: pointer == 1, stack = [1,0,0,0], parsing_string == 1 + // read: ` ` + // expect: NIL let in_key = { ...INITIAL_IN }; - in_key.pointer = read_start_brace_out.next_pointer; - in_key.stack = read_start_brace_out.next_stack; + in_key.pointer = 1; + in_key.stack = [1, 0, 0, 0]; in_key.parsing_string = 1; in_key.byte = WhiteSpace.SPACE; let in_key_out = { ...INITIAL_OUT }; - in_key_out.next_pointer = in_key.pointer; - in_key_out.next_stack = in_key.stack; + in_key_out.next_pointer = 1; + in_key_out.next_stack = [1, 0, 0, 0]; in_key_out.next_parsing_string = 1; generatePassCase(in_key, in_key_out, ">>>> ` ` read"); - //-----------------------------------------------------------------------------// - //-----------------------------------------------------------------------------// - // Test 6: - // init: pointer = 1, stack = [1,0,0,0], parsing_string = 1 setup -> `"` is read + //-TEST_6----------------------------------------------------------// + // init: pointer == 1, stack == [1,0,0,0] + // read: `"` // expect: parsing_string --> 0 // let in_key_to_exit = { ...INITIAL_IN }; - in_key_to_exit.pointer = read_start_brace_out.next_pointer; - in_key_to_exit.stack = read_start_brace_out.next_stack; + in_key_to_exit.pointer = 1; + in_key_to_exit.stack = [1, 0, 0, 0]; in_key_to_exit.parsing_string = 1 in_key_to_exit.byte = Delimiters.QUOTE; let in_key_to_exit_out = { ...INITIAL_OUT }; - in_key_to_exit_out.next_pointer = in_key_to_exit.pointer; - in_key_to_exit_out.next_stack = in_key_to_exit.stack; + in_key_to_exit_out.next_pointer = 1; + in_key_to_exit_out.next_stack = [1, 0, 0, 0]; generatePassCase(in_key_to_exit, in_key_to_exit_out, "`\"` read"); - //-----------------------------------------------------------------------------// - - //-----------------------------------------------------------------------------// - // Test 7: - // init: pointer = 1, stack = [1,0,0,0] -> `:` is read - let parsed_key_wait_to_parse_value = { ...INITIAL_IN }; - parsed_key_wait_to_parse_value.pointer = read_start_brace_out.next_pointer; - parsed_key_wait_to_parse_value.stack = read_start_brace_out.next_stack; - parsed_key_wait_to_parse_value.byte = Delimiters.COLON; - let parsed_key_wait_to_parse_value_out = { ...INITIAL_OUT }; - parsed_key_wait_to_parse_value_out.next_pointer = 2; - parsed_key_wait_to_parse_value_out.next_stack = [1, 3, 0, 0]; - generatePassCase(parsed_key_wait_to_parse_value, parsed_key_wait_to_parse_value_out, ">>>> `:` 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_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_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.inside_value = 1; - // in_value.byte = space; - // let in_value_out = { ...out }; - // in_value_out.next_tree_depth = 1; - // in_value_out.next_inside_value = 1; - // 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_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_value = 1; - // generatePassCase(in_value_to_exit, in_value_to_exit_out, ">>>> `\"` is read"); + + //-TEST_7----------------------------------------------------------// + // state: pointer == 2, stack == [1,3,0,0] + // read: `"` + // expect: parsing_string --> 1 + let in_tree_find_value = { ...INITIAL_IN }; + in_tree_find_value.pointer = 1; + in_tree_find_value.stack = [1, 3, 0, 0]; + in_tree_find_value.byte = Delimiters.QUOTE; + let in_tree_find_value_out = { ...INITIAL_OUT }; + in_tree_find_value_out.next_pointer = 1; + in_tree_find_value_out.next_stack = [1, 3, 0, 0]; + in_tree_find_value_out.next_parsing_string = 1; + generatePassCase(in_tree_find_value, in_tree_find_value_out, ">>>> `\"` read"); + + //-TEST_8----------------------------------------------------------// + // state: pointer == 2, stack == [1,3,0,0], parsing_string == 1 + // read: `"` + // expect: parsing_string == 0, + let in_value_to_exit = { ...INITIAL_IN }; + in_value_to_exit.pointer = 2; + in_value_to_exit.stack = [1, 3, 0, 0]; + in_value_to_exit.parsing_string = 1; + in_value_to_exit.byte = Delimiters.QUOTE; + let in_value_to_exit_out = { ...INITIAL_OUT }; + in_value_to_exit_out.next_pointer = 2; + in_value_to_exit_out.next_stack = [1, 3, 0, 0]; + 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 }; diff --git a/circuits/test/parser/stack.test.ts b/circuits/test/parser/stack.test.ts index b673483..93d99f4 100644 --- a/circuits/test/parser/stack.test.ts +++ b/circuits/test/parser/stack.test.ts @@ -55,7 +55,8 @@ describe("StateUpdate :: RewriteStack", () => { //-TEST_1----------------------------------------------------------// - // init: read `{`, read another `{` + // state: pointer == 1, stack == [1,0,0,0] + // read: `{` // expect: pointer --> 2 // stack --> [1,1,0,0] let in_object = { ...INITIAL_IN }; @@ -68,9 +69,10 @@ describe("StateUpdate :: RewriteStack", () => { generatePassCase(in_object, in_object_out, ">>>> `{` read"); //-TEST_2----------------------------------------------------------// - // init: read `{` then read`}` + // state: pointer == 1, stack == [1,0,0,0] + // read: `}` // expect: pointer --> 0 - // stack --> [0, 0, 0, 0] + // stack --> [0,0,0,0] let in_object_to_leave = { ...INITIAL_IN }; in_object_to_leave.pointer = 1; in_object_to_leave.stack = [1, 0, 0, 0]; @@ -176,4 +178,18 @@ describe("StateUpdate :: RewriteStack", () => { inside_array_out.next_stack = [1, 2, 0, 0]; generatePassCase(inside_array, inside_array_out, ">>>> `,` read"); + //-TEST_10----------------------------------------------------------// + // state: pointer == 1, stack == [1,0,0,0] + // read: `:` + // expect: pointer --> 2 + // stack --> [1,3,0,0] + let parsed_key_wait_to_parse_value = { ...INITIAL_IN }; + parsed_key_wait_to_parse_value.pointer = 1; + parsed_key_wait_to_parse_value.stack = [1, 0, 0, 0]; + parsed_key_wait_to_parse_value.byte = Delimiters.COLON; + let parsed_key_wait_to_parse_value_out = { ...INITIAL_OUT }; + parsed_key_wait_to_parse_value_out.next_pointer = 2; + parsed_key_wait_to_parse_value_out.next_stack = [1, 3, 0, 0]; + generatePassCase(parsed_key_wait_to_parse_value, parsed_key_wait_to_parse_value_out, ">>>> `:` read"); + }); \ No newline at end of file From dbf5c8f00bdbddc4c562a70498831d3401c06731 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Tue, 13 Aug 2024 16:08:42 -0600 Subject: [PATCH 67/99] remove antiquated tests --- circuits/test/parser/parser.test.ts | 64 +++++++++-------------------- circuits/test/parser/stack.test.ts | 5 ++- 2 files changed, 23 insertions(+), 46 deletions(-) diff --git a/circuits/test/parser/parser.test.ts b/circuits/test/parser/parser.test.ts index db8ee46..017a8f8 100644 --- a/circuits/test/parser/parser.test.ts +++ b/circuits/test/parser/parser.test.ts @@ -55,7 +55,10 @@ describe("StateUpdate", () => { let read_start_brace_out = { ...INITIAL_OUT }; read_start_brace_out.next_pointer = 1; read_start_brace_out.next_stack = [1, 0, 0, 0]; - generatePassCase(read_start_brace, read_start_brace_out, ">>>> `{` read"); + generatePassCase(read_start_brace, + read_start_brace_out, + ">>>> `{` read" + ); //-TEST_3----------------------------------------------------------// // state: INIT @@ -63,7 +66,9 @@ describe("StateUpdate", () => { // expect: FAIL (stack underflow) let read_end_brace = { ...INITIAL_IN }; read_end_brace.byte = Delimiters.END_BRACE; - generateFailCase(read_end_brace, ">>>> `}` read --> (stack underflow)"); + generateFailCase(read_end_brace, + ">>>> `}` read --> (stack underflow)" + ); //-TEST_4----------------------------------------------------------// // state: pointer == 1, stack == [1,0,0,0] @@ -77,7 +82,10 @@ describe("StateUpdate", () => { in_object_find_key_out.next_pointer = 1; in_object_find_key_out.next_stack = [1, 0, 0, 0]; in_object_find_key_out.next_parsing_string = 1; - generatePassCase(in_object_find_key, in_object_find_key_out, ">>>> `\"` read"); + generatePassCase(in_object_find_key, + in_object_find_key_out, + ">>>> `\"` read" + ); //-TEST_5----------------------------------------------------------// // state: pointer == 1, stack = [1,0,0,0], parsing_string == 1 @@ -121,7 +129,10 @@ describe("StateUpdate", () => { in_tree_find_value_out.next_pointer = 1; in_tree_find_value_out.next_stack = [1, 3, 0, 0]; in_tree_find_value_out.next_parsing_string = 1; - generatePassCase(in_tree_find_value, in_tree_find_value_out, ">>>> `\"` read"); + generatePassCase(in_tree_find_value, + in_tree_find_value_out, + ">>>> `\"` read" + ); //-TEST_8----------------------------------------------------------// // state: pointer == 2, stack == [1,3,0,0], parsing_string == 1 @@ -135,47 +146,10 @@ describe("StateUpdate", () => { let in_value_to_exit_out = { ...INITIAL_OUT }; in_value_to_exit_out.next_pointer = 2; in_value_to_exit_out.next_stack = [1, 3, 0, 0]; - 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.byte = space; - // let in_end_of_kv_out = { ...out }; - // in_end_of_kv_out.next_tree_depth = 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 - // 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_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_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_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_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_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_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_key = 1; - // generatePassCase(end_of_key_to_inner_object, end_of_key_to_inner_object_out, ">>>> `{` is read"); + generatePassCase(in_value_to_exit, + in_value_to_exit_out, + ">>>> `\"` is read" + ); }); diff --git a/circuits/test/parser/stack.test.ts b/circuits/test/parser/stack.test.ts index 93d99f4..0efe251 100644 --- a/circuits/test/parser/stack.test.ts +++ b/circuits/test/parser/stack.test.ts @@ -190,6 +190,9 @@ describe("StateUpdate :: RewriteStack", () => { let parsed_key_wait_to_parse_value_out = { ...INITIAL_OUT }; parsed_key_wait_to_parse_value_out.next_pointer = 2; parsed_key_wait_to_parse_value_out.next_stack = [1, 3, 0, 0]; - generatePassCase(parsed_key_wait_to_parse_value, parsed_key_wait_to_parse_value_out, ">>>> `:` read"); + generatePassCase(parsed_key_wait_to_parse_value, + parsed_key_wait_to_parse_value_out, + ">>>> `:` read" + ); }); \ No newline at end of file From 38683a384826de9b84c04c4efc45d02bcd5ccdf6 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Tue, 13 Aug 2024 16:33:03 -0600 Subject: [PATCH 68/99] cleanup --- circuits/bytes.circom | 74 ------------------------------ circuits/parser.circom | 43 ++++++++--------- circuits/test/parser/stack.test.ts | 5 +- 3 files changed, 22 insertions(+), 100 deletions(-) delete mode 100644 circuits/bytes.circom diff --git a/circuits/bytes.circom b/circuits/bytes.circom deleted file mode 100644 index 7ac26bd..0000000 --- a/circuits/bytes.circom +++ /dev/null @@ -1,74 +0,0 @@ -pragma circom 2.1.9; - -/* -All tests for this file are located in: `./test/bytes.test.ts` - -Some of the functions here were based off the circomlib: -https://github.com/iden3/circomlib/blob/cff5ab6288b55ef23602221694a6a38a0239dcc0/circuits/bitify.circom -*/ - -/* -This function reads in a unsigned 8-bit integer and converts it to an array of bits. - -# Inputs: -- `in`: a number -- `array[n]`: the array we want to search through -- `out`: either `0` or `1` - - `1` if `in` is found inside `array` - - `0` otherwise - -# Constraints: -- `in`: must be between `0` and `2**8 - 1` -*/ -// template U8ToBits() { -// signal input in; -// signal byte[8]; -// var lc1 = 0; - -// // log("input to u8ToByte: ", in); - -// var e2 = 1; -// for (var i = 0; i < 8; i++) { -// byte[i] <-- (in >> i) & 1; -// byte[i] * (byte[i] - 1) === 0; -// lc1 += byte[i] * e2; -// e2 = e2 + e2; -// } -// lc1 === in; -// } - -/* -This function reads in an array of unsigned numbers that will be constrained to be valid unsigned 8-bit integers. - -# Inputs: -- `n`: the length of the ASCII string (as integers) to verify -- `in[n]`: a list of numbers - -# Constraints: -- `in[n]`: each element of this array must be between `0` and `2**8-1` -*/ -// template ASCII(n) { -// signal input in[n]; - -// component Byte[n]; -// for(var i = 0; i < n; i++) { -// Byte[i] = U8ToBits(); -// Byte[i].in <== in[i]; -// } -// } - -// template Num2Bits(n) { -// signal input in; -// signal output out[n]; -// var lc1=0; - -// var e2=1; -// for (var i = 0; i> i) & 1; -// out[i] * (out[i] -1 ) === 0; -// lc1 += out[i] * e2; -// e2 = e2+e2; -// } - -// lc1 === in; -// } diff --git a/circuits/parser.circom b/circuits/parser.circom index e33e530..6fbadc1 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -36,6 +36,10 @@ Null. TODO: Might not need the "parsing object" and "parsing array" as these are kinda captured by the stack? */ +template Delimeters() { + signal output START_BRACE <== 123; +} + template StateUpdate(MAX_STACK_HEIGHT) { //--------------------------------------------------------------------------------------------// //-Delimeters---------------------------------------------------------------------------------// @@ -85,12 +89,12 @@ template StateUpdate(MAX_STACK_HEIGHT) { var parsing_state[4] = [pushpop, stack_val, parsing_string, parsing_number]; 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 hit_start_brace[4] = [1, 1, 0, 0 ]; // Command returned by switch if we hit a start brace `{` - var hit_end_brace[4] = [-1, -1, 0, 0 ]; // Command returned by switch if we hit a end brace `}` + var hit_end_brace[4] = [-1, 1, 0, 0 ]; // Command returned by switch if we hit a end brace `}` var hit_start_bracket[4] = [1, 2, 0, 0 ]; // TODO: Might want `in_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) - var hit_end_bracket[4] = [-1, -2, 0, 0 ]; // Command returned by switch if we hit a start bracket `]` + var hit_end_bracket[4] = [-1, 2, 0, 0 ]; // Command returned by switch if we hit a start bracket `]` var hit_quote[4] = [0, 0, 1, 0 ]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` var hit_colon[4] = [1, 3, 0, 0 ]; // Command returned by switch if we hit a colon `:` - var hit_comma[4] = [-1, -4, 0, -1 ]; // Command returned by switch if we hit a comma `,` + var hit_comma[4] = [-1, 4, 0, -1 ]; // Command returned by switch if we hit a comma `,` var hit_number[4] = [0, 0, 0, 1 ]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) //--------------------------------------------------------------------------------------------// @@ -212,6 +216,8 @@ template RewriteStack(n) { signal output next_pointer; signal output next_stack[n]; + component Delimeters = Delimeters(); + var brace = Delimeters.START_BRACE; /* IDEA: @@ -239,7 +245,7 @@ template RewriteStack(n) { // Indicate which position in the stack should change (if any) component readComma = IsEqual(); - readComma.in[0] <== -4; + readComma.in[0] <== 4; readComma.in[1] <== stack_val; component topOfStack = GetTopOfStack(n); @@ -250,7 +256,7 @@ template RewriteStack(n) { isArray.in[0] <== topOfStack.out; isArray.in[1] <== 2; - signal READ_COMMA_AND_IN_ARRAY <== (1-readComma.out) + (1-isArray.out); + signal READ_COMMA_AND_IN_ARRAY <== (1 - readComma.out) + (1 - isArray.out); component isReadCommaAndInArray = IsZero(); isReadCommaAndInArray.in <== READ_COMMA_AND_IN_ARRAY; @@ -264,20 +270,15 @@ template RewriteStack(n) { signal isPushAt[n]; component readEndChar = IsZero(); - readEndChar.in <== (stack_val + 1) * (stack_val + 2); - + readEndChar.in <== (stack_val - 1) * (stack_val - 2); - - signal NOT_READ_COMMA <== (1-readComma.out) * stack_val; - signal READ_COMMA <== readComma.out * ((1-isArray.out) * (-3) + isArray.out * (-2)); + signal NOT_READ_COMMA <== (1 - readComma.out) * stack_val; + signal READ_COMMA <== readComma.out * ((1-isArray.out) * (3) + isArray.out * (2)); signal corrected_stack_val <== READ_COMMA + NOT_READ_COMMA; // top of stack is a 3, then we need to pop off 3, and check the value underneath // is correct match (i.e., a brace or bracket (1 or 2)) - - signal accum[n]; - for(var i = 0; i < n; i++) { // points to 1 value back from top prev_indicator[i] = IsZero(); @@ -286,17 +287,11 @@ template RewriteStack(n) { // Points to top of stack if POP else it points to unallocated position indicator[i] = IsZero(); indicator[i].in <== pointer - isPop.out - i; - - accum[i] <== stack[i] * indicator[i].out; - } - - var next_accum = 0; - for(var i = 0; i < n; i++) { - next_accum += accum[i]; } - component atColon = IsZero(); - atColon.in <== next_accum - 3; + component atColon = IsEqual(); + atColon.in[0] <== topOfStack.out; + atColon.in[1] <== 3; signal isDoublePop <== atColon.out * readEndChar.out; signal isPopAtPrev[n]; @@ -315,10 +310,10 @@ template RewriteStack(n) { // Leave the stack alone except for where we indicate change second_pop_val[i] <== isPopAtPrev[i] * corrected_stack_val; - temp_val[i] <== corrected_stack_val - (3 + corrected_stack_val) * isDoublePop; + temp_val[i] <== corrected_stack_val + (1 + corrected_stack_val) * isDoublePop; first_pop_val[i] <== isPopAt[i] * temp_val[i]; // = isPopAt[i] * (corrected_stack_val * (1 - isDoublePop) - 3 * isDoublePop) - next_stack[i] <== stack[i] + isPushAt[i] * corrected_stack_val + first_pop_val[i] + second_pop_val[i]; + next_stack[i] <== stack[i] + isPushAt[i] * corrected_stack_val - first_pop_val[i] - second_pop_val[i]; // TODO: Constrain next_stack entries to be 0,1,2,3 } diff --git a/circuits/test/parser/stack.test.ts b/circuits/test/parser/stack.test.ts index 0efe251..0c7e61d 100644 --- a/circuits/test/parser/stack.test.ts +++ b/circuits/test/parser/stack.test.ts @@ -152,7 +152,8 @@ describe("StateUpdate :: RewriteStack", () => { ); //-TEST_8----------------------------------------------------------// - // init: read `{` and `:`, then read `,` + // init: pointer == 2, stack == [1,3,0,0] + // read: `}` // expect: pointer --> 2 // stack --> [1,3,0,0] let in_object_and_value_to_leave_object = { ...INITIAL_IN }; @@ -164,7 +165,7 @@ describe("StateUpdate :: RewriteStack", () => { in_object_and_value_to_leave_object_out.next_stack = [0, 0, 0, 0]; generatePassCase(in_object_and_value_to_leave_object, in_object_and_value_to_leave_object_out, - ">>>> `,` read" + ">>>> `}` read" ); //-TEST_9----------------------------------------------------------// From 049d45b2c1e77b7dc4b501fdd9463cbfbdf2bc98 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Tue, 13 Aug 2024 16:58:57 -0600 Subject: [PATCH 69/99] refactor: `language.circom` --- circuits/language.circom | 42 +++++++++++++++++ circuits/parser.circom | 97 ++++++---------------------------------- circuits/utils.circom | 55 +---------------------- notes.md | 34 ++++++++++++++ 4 files changed, 90 insertions(+), 138 deletions(-) create mode 100644 circuits/language.circom create mode 100644 notes.md diff --git a/circuits/language.circom b/circuits/language.circom new file mode 100644 index 0000000..ad86d83 --- /dev/null +++ b/circuits/language.circom @@ -0,0 +1,42 @@ +pragma circom 2.1.9; + +template Syntax() { + //-Delimeters---------------------------------------------------------------------------------// + // - ASCII char: `{` + signal output START_BRACE <== 123; + // - ASCII char: `}` + signal output END_BRACE <== 125; + // - ASCII char `[` + signal output START_BRACKET <== 91; + // - ASCII char `]` + signal output END_BRACKET <== 93; + // - ASCII char `"` + signal output QUOTE <== 34; + // - ASCII char `:` + signal output COLON <== 58; + // - ASCII char `,` + signal output COMMA <== 44; + //-White_space--------------------------------------------------------------------------------// + // - ASCII char: `\n` + signal output NEWLINE <== 10; + // - ASCII char: ` ` + signal output SPACE <== 32; + //-Escape-------------------------------------------------------------------------------------// + // - ASCII char: `\` + signal output ESCAPE <== 92; + //-Number_Remapping---------------------------------------------------------------------------// + signal output NUMBER <== 256; // past a u8 -- reserved for ANY numerical ASCII (48 - 57) +} + +template Command() { + // STATE = [pushpop, stack_val, parsing_string, parsing_number] + signal output 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 + signal output START_BRACE[4] <== [1, 1, 0, 0 ]; // Command returned by switch if we hit a start brace `{` + signal output END_BRACE[4] <== [-1, 1, 0, 0 ]; // Command returned by switch if we hit a end brace `}` + signal output START_BRACKET[4] <== [1, 2, 0, 0 ]; // TODO: Might want `in_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) + signal output END_BRACKET[4] <== [-1, 2, 0, 0 ]; // Command returned by switch if we hit a start bracket `]` + signal output QUOTE[4] <== [0, 0, 1, 0 ]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` + signal output COLON[4] <== [1, 3, 0, 0 ]; // Command returned by switch if we hit a colon `:` + signal output COMMA[4] <== [-1, 4, 0, -1 ]; // Command returned by switch if we hit a comma `,` + signal output NUMBER[4] <== [0, 0, 0, 1 ]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) +} \ No newline at end of file diff --git a/circuits/parser.circom b/circuits/parser.circom index 6fbadc1..01aa32c 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -1,110 +1,41 @@ pragma circom 2.1.9; include "utils.circom"; -/* -Notes: for `test.json` -xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - POINTER | Read In: | STATE -------------------------------------------------- -State[1] | { | PARSING TO KEY -------------------------------------------------- -State[7] | " | INSIDE KEY -------------------------------------------------- -State[12]| " | NOT INSIDE KEY -------------------------------------------------- -State[13]| : | PARSING TO VALUE -------------------------------------------------- -State[15]| " | INSIDE VALUE -------------------------------------------------- -State[19]| " | NOT INSIDE VALUE -------------------------------------------------- -State[20]| " | COMPLETE WITH KV PARSING -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -State[20].next_tree_depth == 0 | VALID JSON -xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -*/ +include "language.circom"; /* -JSON TYPES: -Number. -String. -Boolean. -Array. -Object. -Whitespace. -Null. - TODO: Might not need the "parsing object" and "parsing array" as these are kinda captured by the stack? */ -template Delimeters() { - signal output START_BRACE <== 123; -} template StateUpdate(MAX_STACK_HEIGHT) { - //--------------------------------------------------------------------------------------------// - //-Delimeters---------------------------------------------------------------------------------// - // - ASCII char: `{` - var start_brace = 123; - // - ASCII char: `}` - var end_brace = 125; - // - ASCII char `[` - var start_bracket = 91; - // - ASCII char `]` - var end_bracket = 93; - // - ASCII char `"` - var quote = 34; - // - ASCII char `:` - var colon = 58; - // - ASCII char `,` - var comma = 44; - //--------------------------------------------------------------------------------------------// - // White space - // - ASCII char: `\n` - var newline = 10; - // - ASCII char: ` ` - var space = 32; - //--------------------------------------------------------------------------------------------// - // Escape - // - ASCII char: `\` - var escape = 92; - //--------------------------------------------------------------------------------------------// - signal input byte; signal input pointer; // POINTER -- points to the stack to mark where we currently are inside the JSON. signal input stack[MAX_STACK_HEIGHT]; // STACK -- how deep in a JSON nest we are and what type we are currently inside (e.g., `1` for object, `-1` for array). signal input parsing_string; signal input parsing_number; + // TODO // signal parsing_boolean; - // signal parsing_null; // TODO + // signal parsing_null; signal output next_pointer; signal output next_stack[MAX_STACK_HEIGHT]; signal output next_parsing_string; signal output next_parsing_number; - //--------------------------------------------------------------------------------------------// - //-Instructions for ASCII---------------------------------------------------------------------// + + component Syntax = Syntax(); + component Command = Command(); + var pushpop = 0; var stack_val = 0; var parsing_state[4] = [pushpop, stack_val, parsing_string, parsing_number]; - 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 hit_start_brace[4] = [1, 1, 0, 0 ]; // Command returned by switch if we hit a start brace `{` - var hit_end_brace[4] = [-1, 1, 0, 0 ]; // Command returned by switch if we hit a end brace `}` - var hit_start_bracket[4] = [1, 2, 0, 0 ]; // TODO: Might want `in_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) - var hit_end_bracket[4] = [-1, 2, 0, 0 ]; // Command returned by switch if we hit a start bracket `]` - var hit_quote[4] = [0, 0, 1, 0 ]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` - var hit_colon[4] = [1, 3, 0, 0 ]; // Command returned by switch if we hit a colon `:` - var hit_comma[4] = [-1, 4, 0, -1 ]; // Command returned by switch if we hit a comma `,` - var hit_number[4] = [0, 0, 0, 1 ]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) - //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// //-State machine updating---------------------------------------------------------------------// // * yield instruction based on what byte we read * component matcher = SwitchArray(8, 4); - var number = 256; // Number beyond a byte to represent an ASCII numeral - matcher.branches <== [start_brace, end_brace, quote, colon, comma, start_bracket, end_bracket, number ]; - matcher.vals <== [hit_start_brace, hit_end_brace, hit_quote, hit_colon, hit_comma, hit_start_bracket, hit_end_bracket, hit_number]; + matcher.branches <== [Syntax.START_BRACE, Syntax.END_BRACE, Syntax.QUOTE, Syntax.COLON, Syntax.COMMA, Syntax.START_BRACKET, Syntax.END_BRACKET, Syntax.NUMBER ]; + matcher.vals <== [Command.START_BRACE, Command.END_BRACE, Command.QUOTE, Command.COLON, Command.COMMA, Command.START_BRACKET, Command.END_BRACKET, Command.NUMBER]; component numeral_range_check = InRange(8); numeral_range_check.in <== byte; numeral_range_check.range <== [48, 57]; // ASCII NUMERALS @@ -125,7 +56,7 @@ template StateUpdate(MAX_STACK_HEIGHT) { newStack.pointer <== pointer; newStack.stack <== stack; newStack.pushpop <== addToState.out[0]; - newStack.stack_val <== addToState.out[1]; + newStack.stack_val <== addToState.out[1]; next_pointer <== newStack.next_pointer; next_stack <== newStack.next_stack; next_parsing_string <== addToState.out[2]; @@ -216,8 +147,6 @@ template RewriteStack(n) { signal output next_pointer; signal output next_stack[n]; - component Delimeters = Delimeters(); - var brace = Delimeters.START_BRACE; /* IDEA: @@ -309,9 +238,9 @@ template RewriteStack(n) { isPushAt[i] <== indicator[i].out * isPush.out; // Leave the stack alone except for where we indicate change - second_pop_val[i] <== isPopAtPrev[i] * corrected_stack_val; - temp_val[i] <== corrected_stack_val + (1 + corrected_stack_val) * isDoublePop; - first_pop_val[i] <== isPopAt[i] * temp_val[i]; // = isPopAt[i] * (corrected_stack_val * (1 - isDoublePop) - 3 * isDoublePop) + second_pop_val[i] <== isPopAtPrev[i] * corrected_stack_val; + temp_val[i] <== corrected_stack_val + (1 + corrected_stack_val) * isDoublePop; + first_pop_val[i] <== isPopAt[i] * temp_val[i]; // = isPopAt[i] * (corrected_stack_val * (1 - isDoublePop) - 3 * isDoublePop) next_stack[i] <== stack[i] + isPushAt[i] * corrected_stack_val - first_pop_val[i] - second_pop_val[i]; diff --git a/circuits/utils.circom b/circuits/utils.circom index 41d4bb5..3a58c24 100644 --- a/circuits/utils.circom +++ b/circuits/utils.circom @@ -4,7 +4,7 @@ include "circomlib/circuits/bitify.circom"; include "circomlib/circuits/comparators.circom"; /* -All tests for this file are located in: `./test/operators.test.ts` +All tests for this file are located in: `./test/utils/utils.test.ts` */ template ASCII(n) { @@ -109,59 +109,6 @@ template ArrayMul(n) { } } -// template LessThan(n) { -// assert(n <= 252); -// signal input in[2]; -// signal output out; - -// component n2b = Num2Bits(n+1); - -// n2b.in <== in[0]+ (1< out; -// } - -// // N is the number of bits the input have. -// // The MSF is the sign bit. -// template GreaterThan(n) { -// signal input in[2]; -// signal output out; - -// component lt = LessThan(n); - -// lt.in[0] <== in[1]; -// lt.in[1] <== in[0]; -// lt.out ==> out; -// } - -// // N is the number of bits the input have. -// // The MSF is the sign bit. -// template GreaterEqThan(n) { -// signal input in[2]; -// signal output out; - -// component lt = LessThan(n); - -// lt.in[0] <== in[1]; -// lt.in[1] <== in[0]+1; -// lt.out ==> out; -// } - template InRange(n) { signal input in; signal input range[2]; diff --git a/notes.md b/notes.md new file mode 100644 index 0000000..aa97d7f --- /dev/null +++ b/notes.md @@ -0,0 +1,34 @@ +# Notes + +## JSON Types +- [x] Object +- [x] String +- [ ] Array (PARTIALLY COMPLETED, TODO: Need to parse internally) +- [ ] Number +- [ ] Boolean +- [ ] Null + +## Expected Output +> This is old at this point, but we should update it. +``` +Notes: for `test.json` +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + POINTER | Read In: | STATE +------------------------------------------------- +State[1] | { | PARSING TO KEY +------------------------------------------------- +State[7] | " | INSIDE KEY +------------------------------------------------- +State[12]| " | NOT INSIDE KEY +------------------------------------------------- +State[13]| : | PARSING TO VALUE +------------------------------------------------- +State[15]| " | INSIDE VALUE +------------------------------------------------- +State[19]| " | NOT INSIDE VALUE +------------------------------------------------- +State[20]| " | COMPLETE WITH KV PARSING +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +State[20].next_tree_depth == 0 | VALID JSON +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +``` \ No newline at end of file From e553e0481d3fafee6e1bbcaf3d774bb562f35f7e Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Tue, 13 Aug 2024 17:00:20 -0600 Subject: [PATCH 70/99] todo note --- circuits/parser.circom | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 01aa32c..dc10b4c 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -4,7 +4,7 @@ include "utils.circom"; include "language.circom"; /* -TODO: Might not need the "parsing object" and "parsing array" as these are kinda captured by the stack? +TODO: Change the values to push onto stack to be given by START_BRACE, COLON, etc. */ template StateUpdate(MAX_STACK_HEIGHT) { From a79fd4e265f08c1e9112bf0fbe461766cf67ecfe Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Wed, 14 Aug 2024 12:05:05 -0600 Subject: [PATCH 71/99] good save state --- circuits.json | 2 +- circuits/language.circom | 8 +-- circuits/parser.circom | 72 ++++++++++++++--------- circuits/test/parser/values.test.ts | 91 +++++++++++++++++++++++++++++ 4 files changed, 142 insertions(+), 31 deletions(-) create mode 100644 circuits/test/parser/values.test.ts diff --git a/circuits.json b/circuits.json index 9396cbc..b8a4a39 100644 --- a/circuits.json +++ b/circuits.json @@ -20,7 +20,7 @@ "template": "Extract", "params": [ 12, - 2 + 3 ] }, "value_array": { diff --git a/circuits/language.circom b/circuits/language.circom index ad86d83..dbaaad4 100644 --- a/circuits/language.circom +++ b/circuits/language.circom @@ -30,13 +30,13 @@ template Syntax() { template Command() { // STATE = [pushpop, stack_val, parsing_string, parsing_number] - signal output 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 + signal output NOTHING[4] <== [0, 0, 0, -1 ]; // Command returned by switch if we want to do nothing, e.g. read a whitespace char while looking for a key signal output START_BRACE[4] <== [1, 1, 0, 0 ]; // Command returned by switch if we hit a start brace `{` - signal output END_BRACE[4] <== [-1, 1, 0, 0 ]; // Command returned by switch if we hit a end brace `}` + signal output END_BRACE[4] <== [-1, 1, 0, -1 ]; // Command returned by switch if we hit a end brace `}` signal output START_BRACKET[4] <== [1, 2, 0, 0 ]; // TODO: Might want `in_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) - signal output END_BRACKET[4] <== [-1, 2, 0, 0 ]; // Command returned by switch if we hit a start bracket `]` + signal output END_BRACKET[4] <== [-1, 2, 0, -1 ]; // Command returned by switch if we hit a start bracket `]` signal output QUOTE[4] <== [0, 0, 1, 0 ]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` signal output COLON[4] <== [1, 3, 0, 0 ]; // Command returned by switch if we hit a colon `:` signal output COMMA[4] <== [-1, 4, 0, -1 ]; // Command returned by switch if we hit a comma `,` - signal output NUMBER[4] <== [0, 0, 0, 1 ]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) + signal output NUMBER[4] <== [0, 256, 0, 1 ]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) } \ No newline at end of file diff --git a/circuits/parser.circom b/circuits/parser.circom index dc10b4c..2dcd468 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -39,10 +39,16 @@ template StateUpdate(MAX_STACK_HEIGHT) { component numeral_range_check = InRange(8); numeral_range_check.in <== byte; numeral_range_check.range <== [48, 57]; // ASCII NUMERALS - matcher.case <== (1 - numeral_range_check.out) * byte + numeral_range_check.out * 256; // IF (NOT is_number) THEN byte ELSE 256 + log("isNumeral:", numeral_range_check.out); + signal IS_NUMBER <== numeral_range_check.out * Syntax.NUMBER; + matcher.case <== (1 - numeral_range_check.out) * byte + IS_NUMBER; // IF (NOT is_number) THEN byte ELSE 256 + // * get the instruction mask based on current state * component mask = StateToMask(MAX_STACK_HEIGHT); - mask.in <== parsing_state; + // mask.in <== parsing_state; + mask.in <== [matcher.out[0],matcher.out[1],parsing_string,parsing_number]; // TODO: This is awkward. Things need to be rewritten + + // * multiply the mask array elementwise with the instruction array * component mulMaskAndOut = ArrayMul(4); mulMaskAndOut.lhs <== mask.out; @@ -51,6 +57,7 @@ template StateUpdate(MAX_STACK_HEIGHT) { component addToState = ArrayAdd(4); addToState.lhs <== parsing_state; addToState.rhs <== mulMaskAndOut.out; + // * set the new state * component newStack = RewriteStack(MAX_STACK_HEIGHT); newStack.pointer <== pointer; @@ -61,26 +68,12 @@ template StateUpdate(MAX_STACK_HEIGHT) { next_stack <== newStack.next_stack; next_parsing_string <== addToState.out[2]; next_parsing_number <== addToState.out[3]; - //--------------------------------------------------------------------------------------------// - //--------------------------------------------------------------------------------------------// - // // DEBUGGING: internal state - // for(var i = 0; i<7; i++) { - // log("------------------------------------------"); - // log(">>>> parsing_state[",i,"]: ", parsing_state[i]); - // log(">>>> mask[",i,"] : ", mask.out[i]); - // log(">>>> command[",i,"] : ", matcher.out[i]); - // log(">>>> addToState[",i,"] : ", addToState.out[i]); + // for(var i = 0; i < 4; i++) { + // log("matcher.out[",i,"]: ", matcher.out[i]); + // log("mask.out[",i,"]: ", mask.out[i]); + // log("mulMaskAndOut[",i,"]: ", mulMaskAndOut.out[i]); // } - // Debugging - // log("next_pointer ", "= ", next_pointer); - // for(var i = 0; i<4; i++) { - // log("next_stack[", i,"] ", "= ", next_stack[i]); - // } - // log("next_parsing_string", "= ", next_parsing_string); - // log("next_parsing_number", "= ", next_parsing_number); - // log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// //-Constraints--------------------------------------------------------------------------------// @@ -98,6 +91,7 @@ template StateUpdate(MAX_STACK_HEIGHT) { } template StateToMask(n) { + // TODO: Probably need to assert things are bits where necessary. signal input in[4]; signal output out[4]; @@ -106,18 +100,44 @@ template StateToMask(n) { signal parsing_string <== in[2]; signal parsing_number <== in[3]; - // `pushpop` can change: IF NOT `parsing_string` - out[0] <== (1 - parsing_string); + // `pushpop` can change: IF NOT `parsing_string` + out[0] <== (1 - parsing_string) * (1 - parsing_number); - // `stack_val`: IF NOT `parsing_string` OR - // TODO: `parsing_array` - out[1] <== (1 - parsing_string); + // `stack_val`can change: IF NOT `parsing_string` + out[1] <== (1 - parsing_string) * (1- parsing_number); // `parsing_string` can change: out[2] <== 1 - 2 * parsing_string; // `parsing_number` can change: - out[3] <== (1 - parsing_string) * (- 2 * parsing_number); + component isDelimeter = InRange(8); + isDelimeter.in <== stack_val; + isDelimeter.range[0] <== 1; + isDelimeter.range[1] <== 4; + component isNumber = IsEqual(); + isNumber.in <== [stack_val, 256]; + component isParsingString = IsEqual(); + isParsingString.in[0] <== parsing_string; + isParsingString.in[1] <== 1; + component isParsingNumber = IsEqual(); + isParsingNumber.in[0] <== parsing_number; + isParsingNumber.in[1] <== 1; + component toParseNumber = Switch(16); + // TODO: Could combine this into something that returns arrays so that we can set the mask more easily. + toParseNumber.branches <== [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + toParseNumber.vals <== [1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]; + component stateToNum = Bits2Num(4); + stateToNum.in <== [isParsingString.out, isParsingNumber.out, isNumber.out, isDelimeter.out]; + // 1 2 4 8 + toParseNumber.case <== stateToNum.out; + log("isNumber: ", isNumber.out); + log("isParsingString: ", isParsingString.out); + log("isParsingNumber: ", isParsingNumber.out); + log("isDelimeter: ", isDelimeter.out); + log("stateToNum: ", stateToNum.out); + log("toParseNumber: ", toParseNumber.out); + + out[3] <== toParseNumber.out; } template GetTopOfStack(n) { diff --git a/circuits/test/parser/values.test.ts b/circuits/test/parser/values.test.ts new file mode 100644 index 0000000..c164ae2 --- /dev/null +++ b/circuits/test/parser/values.test.ts @@ -0,0 +1,91 @@ +import { circomkit, WitnessTester } from "../common"; +import { Delimiters, WhiteSpace, Numbers, Escape, INITIAL_IN, INITIAL_OUT } from '.'; + +describe("StateUpdate :: Values", () => { + let circuit: WitnessTester< + ["byte", "pointer", "stack", "parsing_string", "parsing_number"], + ["next_pointer", "next_stack", "next_parsing_string", "next_parsing_number"] + >; + before(async () => { + circuit = await circomkit.WitnessTester(`GetTopOfStack`, { + file: "circuits/parser", + template: "StateUpdate", + params: [4], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + function generatePassCase(input: any, expected: any, desc: string) { + const description = Object.entries(input) + .map(([key, value]) => `${key} = ${value}`) + .join(", "); + + it(`(valid) witness: ${description}\n${desc}`, async () => { + await circuit.expectPass(input, expected); + }); + } + + function generateFailCase(input: any, desc: string) { + const description = Object.entries(input) + .map(([key, value]) => `${key} = ${value}`) + .join(", "); + + it(`(invalid) witness: ${description}\n${desc}`, async () => { + await circuit.expectFail(input); + }); + } + + + //-TEST_1----------------------------------------------------------// + // idea: Read a number value after a key in an object. + // state: pointer == 2, stack == [1,3,0,0] + // read: `0` + // expect: pointer --> 2 + // stack --> [1,3,0,0] + // parsing_number --> 1 + let read_number = { ...INITIAL_IN }; + read_number.pointer = 2; + read_number.stack = [1, 3, 0, 0]; + read_number.byte = Numbers.ZERO; + let read_number_out = { ...INITIAL_OUT }; + read_number_out.next_pointer = 2; + read_number_out.next_stack = [1, 3, 0, 0]; + read_number_out.next_parsing_number = 1; + generatePassCase(read_number, read_number_out, ">>>> `0` read"); + + //-TEST_2----------------------------------------------------------// + // idea: Inside a number value after a key in an object. + // state: pointer == 2, stack == [1,3,0,0], parsing_number == 1 + // read: `,` + // expect: pointer --> 2 + // stack --> [1,3,0,0] + // parsing_number --> 0 + let inside_number = { ...INITIAL_IN }; + inside_number.pointer = 2; + inside_number.stack = [1, 3, 0, 0]; + inside_number.parsing_number = 1; + inside_number.byte = Delimiters.COMMA; + let inside_number_out = { ...INITIAL_OUT }; + inside_number_out.next_pointer = 2; + inside_number_out.next_stack = [1, 3, 0, 0]; + generatePassCase(inside_number, inside_number_out, ">>>> `,` read"); + + // TODO: Note that reading a space while reading a number will not throw an error! + + //-TEST_2----------------------------------------------------------// + // idea: Inside a number value after a key in an object. + // state: pointer == 2, stack == [1,3,0,0], parsing_number == 1 + // read: `1` + // expect: pointer --> 2 + // stack --> [1,3,0,0] + // parsing_number --> 0 + let inside_number_continue = { ...INITIAL_IN }; + inside_number_continue.pointer = 2; + inside_number_continue.stack = [1, 3, 0, 0]; + inside_number_continue.parsing_number = 1; + inside_number_continue.byte = Numbers.ONE; + let inside_number_continue_out = { ...INITIAL_OUT }; + inside_number_continue_out.next_pointer = 2; + inside_number_continue_out.next_stack = [1, 3, 0, 0]; + inside_number_continue_out.next_parsing_number = 1; + generatePassCase(inside_number_continue, inside_number_continue_out, ">>>> `,` read"); +}); \ No newline at end of file From 6f5a203d1abe160fa9129adfd108f813499ebae9 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Wed, 14 Aug 2024 13:20:05 -0600 Subject: [PATCH 72/99] good state! --- circuits/language.circom | 4 ++-- circuits/parser.circom | 24 ++++++++++++------------ json_examples/test/example_json.md | 3 --- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/circuits/language.circom b/circuits/language.circom index dbaaad4..f9ba54f 100644 --- a/circuits/language.circom +++ b/circuits/language.circom @@ -32,9 +32,9 @@ template Command() { // STATE = [pushpop, stack_val, parsing_string, parsing_number] signal output NOTHING[4] <== [0, 0, 0, -1 ]; // Command returned by switch if we want to do nothing, e.g. read a whitespace char while looking for a key signal output START_BRACE[4] <== [1, 1, 0, 0 ]; // Command returned by switch if we hit a start brace `{` - signal output END_BRACE[4] <== [-1, 1, 0, -1 ]; // Command returned by switch if we hit a end brace `}` + signal output END_BRACE[4] <== [-1, -1, 0, -1 ]; // Command returned by switch if we hit a end brace `}` signal output START_BRACKET[4] <== [1, 2, 0, 0 ]; // TODO: Might want `in_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) - signal output END_BRACKET[4] <== [-1, 2, 0, -1 ]; // Command returned by switch if we hit a start bracket `]` + signal output END_BRACKET[4] <== [-1, -2, 0, -1 ]; // Command returned by switch if we hit a start bracket `]` signal output QUOTE[4] <== [0, 0, 1, 0 ]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` signal output COLON[4] <== [1, 3, 0, 0 ]; // Command returned by switch if we hit a colon `:` signal output COMMA[4] <== [-1, 4, 0, -1 ]; // Command returned by switch if we hit a comma `,` diff --git a/circuits/parser.circom b/circuits/parser.circom index 2dcd468..c484968 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -39,7 +39,7 @@ template StateUpdate(MAX_STACK_HEIGHT) { component numeral_range_check = InRange(8); numeral_range_check.in <== byte; numeral_range_check.range <== [48, 57]; // ASCII NUMERALS - log("isNumeral:", numeral_range_check.out); + // log("isNumeral:", numeral_range_check.out); signal IS_NUMBER <== numeral_range_check.out * Syntax.NUMBER; matcher.case <== (1 - numeral_range_check.out) * byte + IS_NUMBER; // IF (NOT is_number) THEN byte ELSE 256 @@ -125,17 +125,17 @@ template StateToMask(n) { component toParseNumber = Switch(16); // TODO: Could combine this into something that returns arrays so that we can set the mask more easily. toParseNumber.branches <== [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; - toParseNumber.vals <== [1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]; + toParseNumber.vals <== [0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]; component stateToNum = Bits2Num(4); stateToNum.in <== [isParsingString.out, isParsingNumber.out, isNumber.out, isDelimeter.out]; // 1 2 4 8 toParseNumber.case <== stateToNum.out; - log("isNumber: ", isNumber.out); - log("isParsingString: ", isParsingString.out); - log("isParsingNumber: ", isParsingNumber.out); - log("isDelimeter: ", isDelimeter.out); - log("stateToNum: ", stateToNum.out); - log("toParseNumber: ", toParseNumber.out); + // log("isNumber: ", isNumber.out); + // log("isParsingString: ", isParsingString.out); + // log("isParsingNumber: ", isParsingNumber.out); + // log("isDelimeter: ", isDelimeter.out); + // log("stateToNum: ", stateToNum.out); + // log("toParseNumber: ", toParseNumber.out); out[3] <== toParseNumber.out; } @@ -219,10 +219,10 @@ template RewriteStack(n) { signal isPushAt[n]; component readEndChar = IsZero(); - readEndChar.in <== (stack_val - 1) * (stack_val - 2); + readEndChar.in <== (stack_val + 1) * (stack_val + 2); signal NOT_READ_COMMA <== (1 - readComma.out) * stack_val; - signal READ_COMMA <== readComma.out * ((1-isArray.out) * (3) + isArray.out * (2)); + signal READ_COMMA <== readComma.out * ((1-isArray.out) * (-3) + isArray.out * (-2)); signal corrected_stack_val <== READ_COMMA + NOT_READ_COMMA; // top of stack is a 3, then we need to pop off 3, and check the value underneath @@ -259,10 +259,10 @@ template RewriteStack(n) { // Leave the stack alone except for where we indicate change second_pop_val[i] <== isPopAtPrev[i] * corrected_stack_val; - temp_val[i] <== corrected_stack_val + (1 + corrected_stack_val) * isDoublePop; + temp_val[i] <== corrected_stack_val - (3 + corrected_stack_val) * isDoublePop; first_pop_val[i] <== isPopAt[i] * temp_val[i]; // = isPopAt[i] * (corrected_stack_val * (1 - isDoublePop) - 3 * isDoublePop) - next_stack[i] <== stack[i] + isPushAt[i] * corrected_stack_val - first_pop_val[i] - second_pop_val[i]; + next_stack[i] <== stack[i] + isPushAt[i] * corrected_stack_val + first_pop_val[i] + second_pop_val[i]; // TODO: Constrain next_stack entries to be 0,1,2,3 } diff --git a/json_examples/test/example_json.md b/json_examples/test/example_json.md index bd11903..a398ff8 100644 --- a/json_examples/test/example_json.md +++ b/json_examples/test/example_json.md @@ -1,6 +1,3 @@ -# Notes on this JSON for reference - - { "a": // 7 { "b": "c", // 19 "d": { // 25 From 37c28bf57753a884759ca3dfaeb0fa25d405a5dc Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Wed, 14 Aug 2024 14:35:36 -0600 Subject: [PATCH 73/99] Update notes.md --- notes.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/notes.md b/notes.md index aa97d7f..28ea2fa 100644 --- a/notes.md +++ b/notes.md @@ -1,13 +1,17 @@ # Notes -## JSON Types +## TODOs +### JSON Types - [x] Object - [x] String - [ ] Array (PARTIALLY COMPLETED, TODO: Need to parse internally) -- [ ] Number +- [x] Number - [ ] Boolean - [ ] Null +### string escape +shouldn't be too hard, just add one more state variable `escaping` that is only enabled when parsing a string and can only be toggled -- next state will always have to set back to 0. + ## Expected Output > This is old at this point, but we should update it. ``` From 558ba963c3eb4908e2dd3f5f6bd2fa790c926eb6 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Wed, 14 Aug 2024 14:59:43 -0600 Subject: [PATCH 74/99] 2d stack --- circuits/parser.circom | 28 +++++++-------- circuits/test/common/index.ts | 15 ++++++++ circuits/test/parser/index.ts | 2 +- circuits/test/parser/parser.test.ts | 22 ++++++------ circuits/test/parser/stack.test.ts | 53 ++++++++++++++--------------- circuits/test/parser/values.test.ts | 12 +++---- 6 files changed, 72 insertions(+), 60 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index c484968..450a9bc 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -11,7 +11,7 @@ template StateUpdate(MAX_STACK_HEIGHT) { signal input byte; signal input pointer; // POINTER -- points to the stack to mark where we currently are inside the JSON. - signal input stack[MAX_STACK_HEIGHT]; // STACK -- how deep in a JSON nest we are and what type we are currently inside (e.g., `1` for object, `-1` for array). + signal input stack[MAX_STACK_HEIGHT][2]; // STACK -- how deep in a JSON nest we are and what type we are currently inside (e.g., `1` for object, `-1` for array). signal input parsing_string; signal input parsing_number; // TODO @@ -19,7 +19,7 @@ template StateUpdate(MAX_STACK_HEIGHT) { // signal parsing_null; signal output next_pointer; - signal output next_stack[MAX_STACK_HEIGHT]; + signal output next_stack[MAX_STACK_HEIGHT][2]; signal output next_parsing_string; signal output next_parsing_number; @@ -141,12 +141,12 @@ template StateToMask(n) { } template GetTopOfStack(n) { - signal input stack[n]; + signal input stack[n][2]; signal input pointer; - signal output out; + signal output out[2]; - component atTop = Switch(n); + component atTop = SwitchArray(n,2); for(var i = 0; i < n; i++) { atTop.branches[i] <== i + 1; atTop.vals[i] <== stack[i]; @@ -161,11 +161,11 @@ template GetTopOfStack(n) { template RewriteStack(n) { assert(n < 2**8); signal input pointer; - signal input stack[n]; + signal input stack[n][2]; signal input pushpop; signal input stack_val; signal output next_pointer; - signal output next_stack[n]; + signal output next_stack[n][2]; /* IDEA: @@ -193,18 +193,18 @@ template RewriteStack(n) { */ // Indicate which position in the stack should change (if any) - component readComma = IsEqual(); - readComma.in[0] <== 4; - readComma.in[1] <== stack_val; - component topOfStack = GetTopOfStack(n); topOfStack.pointer <== pointer; topOfStack.stack <== stack; component isArray = IsEqual(); - isArray.in[0] <== topOfStack.out; + isArray.in[0] <== topOfStack.out[0]; isArray.in[1] <== 2; + component readComma = IsEqual(); + readComma.in[0] <== 4; + readComma.in[1] <== stack_val; + signal READ_COMMA_AND_IN_ARRAY <== (1 - readComma.out) + (1 - isArray.out); component isReadCommaAndInArray = IsZero(); isReadCommaAndInArray.in <== READ_COMMA_AND_IN_ARRAY; @@ -239,7 +239,7 @@ template RewriteStack(n) { } component atColon = IsEqual(); - atColon.in[0] <== topOfStack.out; + atColon.in[0] <== topOfStack.out[0]; atColon.in[1] <== 3; signal isDoublePop <== atColon.out * readEndChar.out; @@ -262,7 +262,7 @@ template RewriteStack(n) { temp_val[i] <== corrected_stack_val - (3 + corrected_stack_val) * isDoublePop; first_pop_val[i] <== isPopAt[i] * temp_val[i]; // = isPopAt[i] * (corrected_stack_val * (1 - isDoublePop) - 3 * isDoublePop) - next_stack[i] <== stack[i] + isPushAt[i] * corrected_stack_val + first_pop_val[i] + second_pop_val[i]; + next_stack[i][0] <== stack[i][0] + isPushAt[i] * corrected_stack_val + first_pop_val[i] + second_pop_val[i]; // TODO: Constrain next_stack entries to be 0,1,2,3 } diff --git a/circuits/test/common/index.ts b/circuits/test/common/index.ts index a5ebae5..f78729b 100644 --- a/circuits/test/common/index.ts +++ b/circuits/test/common/index.ts @@ -7,3 +7,18 @@ export const circomkit = new Circomkit({ export { WitnessTester }; +function stringifyValue(value: any): string { + if (Array.isArray(value)) { + return `[${value.map(stringifyValue).join(', ')}]`; + } + if (typeof value === 'object' && value !== null) { + return `{${Object.entries(value).map(([k, v]) => `${k}: ${stringifyValue(v)}`).join(', ')}}`; + } + return String(value); +} + +export function generateDescription(input: any): string { + return Object.entries(input) + .map(([key, value]) => `${key} = ${stringifyValue(value)}`) + .join(", "); +} \ No newline at end of file diff --git a/circuits/test/parser/index.ts b/circuits/test/parser/index.ts index 1c5bb69..a658951 100644 --- a/circuits/test/parser/index.ts +++ b/circuits/test/parser/index.ts @@ -45,7 +45,7 @@ export const Escape = { export const INITIAL_IN = { byte: 0, pointer: 0, - stack: [0, 0, 0, 0], + stack: [[0, 0], [0, 0], [0, 0], [0, 0]], parsing_string: 0, parsing_number: 0, }; diff --git a/circuits/test/parser/parser.test.ts b/circuits/test/parser/parser.test.ts index 017a8f8..e220966 100644 --- a/circuits/test/parser/parser.test.ts +++ b/circuits/test/parser/parser.test.ts @@ -54,7 +54,7 @@ describe("StateUpdate", () => { read_start_brace.byte = Delimiters.START_BRACE; let read_start_brace_out = { ...INITIAL_OUT }; read_start_brace_out.next_pointer = 1; - read_start_brace_out.next_stack = [1, 0, 0, 0]; + read_start_brace_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; generatePassCase(read_start_brace, read_start_brace_out, ">>>> `{` read" @@ -76,11 +76,11 @@ describe("StateUpdate", () => { // expect: parsing_string --> 1 let in_object_find_key = { ...INITIAL_IN }; in_object_find_key.pointer = 1; - in_object_find_key.stack = [1, 0, 0, 0]; + in_object_find_key.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; in_object_find_key.byte = Delimiters.QUOTE; let in_object_find_key_out = { ...INITIAL_OUT }; in_object_find_key_out.next_pointer = 1; - in_object_find_key_out.next_stack = [1, 0, 0, 0]; + in_object_find_key_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; in_object_find_key_out.next_parsing_string = 1; generatePassCase(in_object_find_key, in_object_find_key_out, @@ -93,12 +93,12 @@ describe("StateUpdate", () => { // expect: NIL let in_key = { ...INITIAL_IN }; in_key.pointer = 1; - in_key.stack = [1, 0, 0, 0]; + in_key.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; in_key.parsing_string = 1; in_key.byte = WhiteSpace.SPACE; let in_key_out = { ...INITIAL_OUT }; in_key_out.next_pointer = 1; - in_key_out.next_stack = [1, 0, 0, 0]; + in_key_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; in_key_out.next_parsing_string = 1; generatePassCase(in_key, in_key_out, ">>>> ` ` read"); @@ -109,12 +109,12 @@ describe("StateUpdate", () => { // let in_key_to_exit = { ...INITIAL_IN }; in_key_to_exit.pointer = 1; - in_key_to_exit.stack = [1, 0, 0, 0]; + in_key_to_exit.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; in_key_to_exit.parsing_string = 1 in_key_to_exit.byte = Delimiters.QUOTE; let in_key_to_exit_out = { ...INITIAL_OUT }; in_key_to_exit_out.next_pointer = 1; - in_key_to_exit_out.next_stack = [1, 0, 0, 0]; + in_key_to_exit_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; generatePassCase(in_key_to_exit, in_key_to_exit_out, "`\"` read"); //-TEST_7----------------------------------------------------------// @@ -123,11 +123,11 @@ describe("StateUpdate", () => { // expect: parsing_string --> 1 let in_tree_find_value = { ...INITIAL_IN }; in_tree_find_value.pointer = 1; - in_tree_find_value.stack = [1, 3, 0, 0]; + in_tree_find_value.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; in_tree_find_value.byte = Delimiters.QUOTE; let in_tree_find_value_out = { ...INITIAL_OUT }; in_tree_find_value_out.next_pointer = 1; - in_tree_find_value_out.next_stack = [1, 3, 0, 0]; + in_tree_find_value_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; in_tree_find_value_out.next_parsing_string = 1; generatePassCase(in_tree_find_value, in_tree_find_value_out, @@ -140,12 +140,12 @@ describe("StateUpdate", () => { // expect: parsing_string == 0, let in_value_to_exit = { ...INITIAL_IN }; in_value_to_exit.pointer = 2; - in_value_to_exit.stack = [1, 3, 0, 0]; + in_value_to_exit.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; in_value_to_exit.parsing_string = 1; in_value_to_exit.byte = Delimiters.QUOTE; let in_value_to_exit_out = { ...INITIAL_OUT }; in_value_to_exit_out.next_pointer = 2; - in_value_to_exit_out.next_stack = [1, 3, 0, 0]; + in_value_to_exit_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; generatePassCase(in_value_to_exit, in_value_to_exit_out, ">>>> `\"` is read" diff --git a/circuits/test/parser/stack.test.ts b/circuits/test/parser/stack.test.ts index 0c7e61d..cad64ee 100644 --- a/circuits/test/parser/stack.test.ts +++ b/circuits/test/parser/stack.test.ts @@ -1,4 +1,4 @@ -import { circomkit, WitnessTester } from "../common"; +import { circomkit, WitnessTester, generateDescription } from "../common"; import { Delimiters, WhiteSpace, Numbers, Escape, INITIAL_IN, INITIAL_OUT } from '.'; describe("GetTopOfStack", () => { @@ -12,10 +12,10 @@ describe("GetTopOfStack", () => { console.log("#constraints:", await circuit.getConstraintCount()); }); - it("witness: pointer = 4, stack = [0, 1, 2, 3, 4]", async () => { + it("witness: pointer = 4, stack = [[1,0], [2,0], [3,1], [4,2]]", async () => { await circuit.expectPass( - { pointer: 4, stack: [1, 2, 3, 4] }, - { out: 4 }, + { pointer: 4, stack: [[1, 0], [2, 0], [3, 1], [4, 2]] }, + { out: [4, 2] }, ); }); }); @@ -33,10 +33,9 @@ describe("StateUpdate :: RewriteStack", () => { }); console.log("#constraints:", await circuit.getConstraintCount()); }); + function generatePassCase(input: any, expected: any, desc: string) { - const description = Object.entries(input) - .map(([key, value]) => `${key} = ${value}`) - .join(", "); + const description = generateDescription(input); it(`(valid) witness: ${description}\n${desc}`, async () => { await circuit.expectPass(input, expected); @@ -44,11 +43,9 @@ describe("StateUpdate :: RewriteStack", () => { } function generateFailCase(input: any, desc: string) { - const description = Object.entries(input) - .map(([key, value]) => `${key} = ${value}`) - .join(", "); + const description = generateDescription(input); - it(`(invalid) witness: ${description}\n${desc}`, async () => { + it(`(valid) witness: ${description}\n${desc}`, async () => { await circuit.expectFail(input); }); } @@ -61,11 +58,11 @@ describe("StateUpdate :: RewriteStack", () => { // stack --> [1,1,0,0] let in_object = { ...INITIAL_IN }; in_object.pointer = 1; - in_object.stack = [1, 0, 0, 0]; + in_object.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; in_object.byte = Delimiters.START_BRACE; let in_object_out = { ...INITIAL_OUT }; in_object_out.next_pointer = 2; - in_object_out.next_stack = [1, 1, 0, 0]; + in_object_out.next_stack = [[1, 0], [1, 0], [0, 0], [0, 0]]; generatePassCase(in_object, in_object_out, ">>>> `{` read"); //-TEST_2----------------------------------------------------------// @@ -75,7 +72,7 @@ describe("StateUpdate :: RewriteStack", () => { // stack --> [0,0,0,0] let in_object_to_leave = { ...INITIAL_IN }; in_object_to_leave.pointer = 1; - in_object_to_leave.stack = [1, 0, 0, 0]; + in_object_to_leave.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; in_object_to_leave.byte = Delimiters.END_BRACE; let in_object_to_leave_out = { ...INITIAL_OUT }; generatePassCase(in_object_to_leave, @@ -90,10 +87,10 @@ describe("StateUpdate :: RewriteStack", () => { let in_object_to_read_start_bracket = { ...INITIAL_IN }; in_object_to_read_start_bracket.byte = Delimiters.START_BRACKET; in_object_to_read_start_bracket.pointer = 1; - in_object_to_read_start_bracket.stack = [1, 0, 0, 0]; + in_object_to_read_start_bracket.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; let in_object_to_read_start_bracket_out = { ...INITIAL_OUT }; in_object_to_read_start_bracket_out.next_pointer = 2; - in_object_to_read_start_bracket_out.next_stack = [1, 2, 0, 0]; + in_object_to_read_start_bracket_out.next_stack = [[1, 0], [2, 0], [0, 0], [0, 0]]; generatePassCase(in_object_to_read_start_bracket, in_object_to_read_start_bracket_out, ">>>> `[` read" @@ -106,7 +103,7 @@ describe("StateUpdate :: RewriteStack", () => { let in_max_stack = { ...INITIAL_IN }; in_max_stack.byte = Delimiters.START_BRACE; in_max_stack.pointer = 4; - in_max_stack.stack = [1, 1, 1, 1]; + in_max_stack.stack = [[1, 0], [1, 0], [1, 0], [1, 0]]; generateFailCase(in_max_stack, ">>>> `{` read --> (stack overflow)"); //-TEST_5----------------------------------------------------------// @@ -116,7 +113,7 @@ describe("StateUpdate :: RewriteStack", () => { let in_max_stack_2 = { ...INITIAL_IN }; in_max_stack_2.byte = Delimiters.START_BRACKET; in_max_stack_2.pointer = 4; - in_max_stack_2.stack = [1, 1, 1, 1]; + in_max_stack_2.stack = [[1, 0], [1, 0], [1, 0], [1, 0]]; generateFailCase(in_max_stack, ">>>> `[` read --> (stack overflow)"); //-TEST_6----------------------------------------------------------// @@ -126,10 +123,10 @@ describe("StateUpdate :: RewriteStack", () => { let in_object_and_array = { ...INITIAL_IN }; in_object_and_array.byte = Delimiters.END_BRACKET; in_object_and_array.pointer = 2; - in_object_and_array.stack = [1, 2, 0, 0]; + in_object_and_array.stack = [[1, 0], [2, 0], [0, 0], [0, 0]]; let in_object_and_array_out = { ...INITIAL_OUT }; in_object_and_array_out.next_pointer = 1; - in_object_and_array_out.next_stack = [1, 0, 0, 0]; + in_object_and_array_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; generatePassCase(in_object_and_array, in_object_and_array_out, ">>>> `]` read" @@ -142,10 +139,10 @@ describe("StateUpdate :: RewriteStack", () => { let in_object_and_value = { ...INITIAL_IN }; in_object_and_value.byte = Delimiters.COMMA; in_object_and_value.pointer = 2; - in_object_and_value.stack = [1, 3, 0, 0]; + in_object_and_value.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; let in_object_and_value_out = { ...INITIAL_OUT }; in_object_and_value_out.next_pointer = 1; - in_object_and_value_out.next_stack = [1, 0, 0, 0]; + in_object_and_value_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; generatePassCase(in_object_and_value, in_object_and_value_out, ">>>> `,` read" @@ -159,10 +156,10 @@ describe("StateUpdate :: RewriteStack", () => { let in_object_and_value_to_leave_object = { ...INITIAL_IN }; in_object_and_value_to_leave_object.byte = Delimiters.END_BRACE; in_object_and_value_to_leave_object.pointer = 2; - in_object_and_value_to_leave_object.stack = [1, 3, 0, 0]; + in_object_and_value_to_leave_object.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; let in_object_and_value_to_leave_object_out = { ...INITIAL_OUT }; in_object_and_value_to_leave_object_out.next_pointer = 0; - in_object_and_value_to_leave_object_out.next_stack = [0, 0, 0, 0]; + in_object_and_value_to_leave_object_out.next_stack = [[0, 0], [0, 0], [0, 0], [0, 0]]; generatePassCase(in_object_and_value_to_leave_object, in_object_and_value_to_leave_object_out, ">>>> `}` read" @@ -172,11 +169,11 @@ describe("StateUpdate :: RewriteStack", () => { // init: pointer = 1, stack = [1,2,0,0] -> `,` is read let inside_array = { ...INITIAL_IN }; inside_array.pointer = 2; - inside_array.stack = [1, 2, 0, 0]; + inside_array.stack = [[1, 0], [2, 0], [0, 0], [0, 0]]; inside_array.byte = Delimiters.COMMA; let inside_array_out = { ...INITIAL_OUT }; inside_array_out.next_pointer = 2; - inside_array_out.next_stack = [1, 2, 0, 0]; + inside_array_out.next_stack = [[1, 0], [2, 0], [0, 0], [0, 0]]; generatePassCase(inside_array, inside_array_out, ">>>> `,` read"); //-TEST_10----------------------------------------------------------// @@ -186,11 +183,11 @@ describe("StateUpdate :: RewriteStack", () => { // stack --> [1,3,0,0] let parsed_key_wait_to_parse_value = { ...INITIAL_IN }; parsed_key_wait_to_parse_value.pointer = 1; - parsed_key_wait_to_parse_value.stack = [1, 0, 0, 0]; + parsed_key_wait_to_parse_value.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; parsed_key_wait_to_parse_value.byte = Delimiters.COLON; let parsed_key_wait_to_parse_value_out = { ...INITIAL_OUT }; parsed_key_wait_to_parse_value_out.next_pointer = 2; - parsed_key_wait_to_parse_value_out.next_stack = [1, 3, 0, 0]; + parsed_key_wait_to_parse_value_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; generatePassCase(parsed_key_wait_to_parse_value, parsed_key_wait_to_parse_value_out, ">>>> `:` read" diff --git a/circuits/test/parser/values.test.ts b/circuits/test/parser/values.test.ts index c164ae2..8878b47 100644 --- a/circuits/test/parser/values.test.ts +++ b/circuits/test/parser/values.test.ts @@ -44,11 +44,11 @@ describe("StateUpdate :: Values", () => { // parsing_number --> 1 let read_number = { ...INITIAL_IN }; read_number.pointer = 2; - read_number.stack = [1, 3, 0, 0]; + read_number.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; read_number.byte = Numbers.ZERO; let read_number_out = { ...INITIAL_OUT }; read_number_out.next_pointer = 2; - read_number_out.next_stack = [1, 3, 0, 0]; + read_number_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; read_number_out.next_parsing_number = 1; generatePassCase(read_number, read_number_out, ">>>> `0` read"); @@ -61,12 +61,12 @@ describe("StateUpdate :: Values", () => { // parsing_number --> 0 let inside_number = { ...INITIAL_IN }; inside_number.pointer = 2; - inside_number.stack = [1, 3, 0, 0]; + inside_number.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; inside_number.parsing_number = 1; inside_number.byte = Delimiters.COMMA; let inside_number_out = { ...INITIAL_OUT }; inside_number_out.next_pointer = 2; - inside_number_out.next_stack = [1, 3, 0, 0]; + inside_number_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; generatePassCase(inside_number, inside_number_out, ">>>> `,` read"); // TODO: Note that reading a space while reading a number will not throw an error! @@ -80,12 +80,12 @@ describe("StateUpdate :: Values", () => { // parsing_number --> 0 let inside_number_continue = { ...INITIAL_IN }; inside_number_continue.pointer = 2; - inside_number_continue.stack = [1, 3, 0, 0]; + inside_number_continue.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; inside_number_continue.parsing_number = 1; inside_number_continue.byte = Numbers.ONE; let inside_number_continue_out = { ...INITIAL_OUT }; inside_number_continue_out.next_pointer = 2; - inside_number_continue_out.next_stack = [1, 3, 0, 0]; + inside_number_continue_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; inside_number_continue_out.next_parsing_number = 1; generatePassCase(inside_number_continue, inside_number_continue_out, ">>>> `,` read"); }); \ No newline at end of file From e0f8893d137aacfadf991cd3c8f1420393584a40 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Wed, 14 Aug 2024 15:10:02 -0600 Subject: [PATCH 75/99] basic array tracking --- circuits/parser.circom | 5 +- circuits/test/parser/parser.test.ts | 234 ++++++++++++++-------------- circuits/test/parser/stack.test.ts | 26 ++-- circuits/test/parser/values.test.ts | 117 +++++++------- 4 files changed, 185 insertions(+), 197 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 450a9bc..2a19d05 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -231,7 +231,7 @@ template RewriteStack(n) { for(var i = 0; i < n; i++) { // points to 1 value back from top prev_indicator[i] = IsZero(); - prev_indicator[i].in <== pointer - 2 * isPop.out - i; + prev_indicator[i].in <== pointer - 1 - isPop.out - i; // Points to top of stack if POP else it points to unallocated position indicator[i] = IsZero(); @@ -263,7 +263,10 @@ template RewriteStack(n) { first_pop_val[i] <== isPopAt[i] * temp_val[i]; // = isPopAt[i] * (corrected_stack_val * (1 - isDoublePop) - 3 * isDoublePop) next_stack[i][0] <== stack[i][0] + isPushAt[i] * corrected_stack_val + first_pop_val[i] + second_pop_val[i]; + next_stack[i][1] <== prev_indicator[i].out * isReadCommaAndInArray.out; + log("next_stack[",i,"][0]: ", next_stack[i][0]); + log("next_stack[",i,"][1]: ", next_stack[i][1]); // TODO: Constrain next_stack entries to be 0,1,2,3 } diff --git a/circuits/test/parser/parser.test.ts b/circuits/test/parser/parser.test.ts index e220966..b5b4ec0 100644 --- a/circuits/test/parser/parser.test.ts +++ b/circuits/test/parser/parser.test.ts @@ -1,4 +1,4 @@ -import { circomkit, WitnessTester } from "../common"; +import { circomkit, WitnessTester, generateDescription } from "../common"; import { Delimiters, WhiteSpace, Numbers, Escape, INITIAL_IN, INITIAL_OUT } from '.'; @@ -10,9 +10,7 @@ describe("StateUpdate", () => { >; function generatePassCase(input: any, expected: any, desc: string) { - const description = Object.entries(input) - .map(([key, value]) => `${key} = ${value}`) - .join(", "); + const description = generateDescription(input); it(`(valid) witness: ${description}\n${desc}`, async () => { await circuit.expectPass(input, expected); @@ -20,11 +18,9 @@ describe("StateUpdate", () => { } function generateFailCase(input: any, desc: string) { - const description = Object.entries(input) - .map(([key, value]) => `${key} = ${value}`) - .join(", "); + const description = generateDescription(input); - it(`(invalid) witness: ${description}\n${desc}`, async () => { + it(`(valid) witness: ${description}\n${desc}`, async () => { await circuit.expectFail(input); }); } @@ -39,117 +35,117 @@ describe("StateUpdate", () => { }); - //-TEST_1----------------------------------------------------------// - // init: ZEROS then read `do_nothing` byte - // expect: ZEROS - generatePassCase(INITIAL_IN, INITIAL_OUT, ">>>> `NUL` read"); - - // TODO: Consider moving to `stack.test.ts` - //-TEST_2----------------------------------------------------------// - // init: INIT - // read: `{` - // expect: pointer --> 1 - // stack --> [1,0,0,0] - let read_start_brace = { ...INITIAL_IN }; - read_start_brace.byte = Delimiters.START_BRACE; - let read_start_brace_out = { ...INITIAL_OUT }; - read_start_brace_out.next_pointer = 1; - read_start_brace_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - generatePassCase(read_start_brace, - read_start_brace_out, - ">>>> `{` read" - ); - - //-TEST_3----------------------------------------------------------// - // state: INIT - // read: `}` - // expect: FAIL (stack underflow) - let read_end_brace = { ...INITIAL_IN }; - read_end_brace.byte = Delimiters.END_BRACE; - generateFailCase(read_end_brace, - ">>>> `}` read --> (stack underflow)" - ); - - //-TEST_4----------------------------------------------------------// - // state: pointer == 1, stack == [1,0,0,0] - // read: `"` - // expect: parsing_string --> 1 - let in_object_find_key = { ...INITIAL_IN }; - in_object_find_key.pointer = 1; - in_object_find_key.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - in_object_find_key.byte = Delimiters.QUOTE; - let in_object_find_key_out = { ...INITIAL_OUT }; - in_object_find_key_out.next_pointer = 1; - in_object_find_key_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - in_object_find_key_out.next_parsing_string = 1; - generatePassCase(in_object_find_key, - in_object_find_key_out, - ">>>> `\"` read" - ); - - //-TEST_5----------------------------------------------------------// - // state: pointer == 1, stack = [1,0,0,0], parsing_string == 1 - // read: ` ` - // expect: NIL - let in_key = { ...INITIAL_IN }; - in_key.pointer = 1; - in_key.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - in_key.parsing_string = 1; - in_key.byte = WhiteSpace.SPACE; - let in_key_out = { ...INITIAL_OUT }; - in_key_out.next_pointer = 1; - in_key_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - in_key_out.next_parsing_string = 1; - generatePassCase(in_key, in_key_out, ">>>> ` ` read"); - - //-TEST_6----------------------------------------------------------// - // init: pointer == 1, stack == [1,0,0,0] - // read: `"` - // expect: parsing_string --> 0 - // - let in_key_to_exit = { ...INITIAL_IN }; - in_key_to_exit.pointer = 1; - in_key_to_exit.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - in_key_to_exit.parsing_string = 1 - in_key_to_exit.byte = Delimiters.QUOTE; - let in_key_to_exit_out = { ...INITIAL_OUT }; - in_key_to_exit_out.next_pointer = 1; - in_key_to_exit_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - generatePassCase(in_key_to_exit, in_key_to_exit_out, "`\"` read"); - - //-TEST_7----------------------------------------------------------// - // state: pointer == 2, stack == [1,3,0,0] - // read: `"` - // expect: parsing_string --> 1 - let in_tree_find_value = { ...INITIAL_IN }; - in_tree_find_value.pointer = 1; - in_tree_find_value.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - in_tree_find_value.byte = Delimiters.QUOTE; - let in_tree_find_value_out = { ...INITIAL_OUT }; - in_tree_find_value_out.next_pointer = 1; - in_tree_find_value_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - in_tree_find_value_out.next_parsing_string = 1; - generatePassCase(in_tree_find_value, - in_tree_find_value_out, - ">>>> `\"` read" - ); - - //-TEST_8----------------------------------------------------------// - // state: pointer == 2, stack == [1,3,0,0], parsing_string == 1 - // read: `"` - // expect: parsing_string == 0, - let in_value_to_exit = { ...INITIAL_IN }; - in_value_to_exit.pointer = 2; - in_value_to_exit.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - in_value_to_exit.parsing_string = 1; - in_value_to_exit.byte = Delimiters.QUOTE; - let in_value_to_exit_out = { ...INITIAL_OUT }; - in_value_to_exit_out.next_pointer = 2; - in_value_to_exit_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - generatePassCase(in_value_to_exit, - in_value_to_exit_out, - ">>>> `\"` is read" - ); + // //-TEST_1----------------------------------------------------------// + // // init: ZEROS then read `do_nothing` byte + // // expect: ZEROS + // generatePassCase(INITIAL_IN, INITIAL_OUT, ">>>> `NUL` read"); + + // // TODO: Consider moving to `stack.test.ts` + // //-TEST_2----------------------------------------------------------// + // // init: INIT + // // read: `{` + // // expect: pointer --> 1 + // // stack --> [1,0,0,0] + // let read_start_brace = { ...INITIAL_IN }; + // read_start_brace.byte = Delimiters.START_BRACE; + // let read_start_brace_out = { ...INITIAL_OUT }; + // read_start_brace_out.next_pointer = 1; + // read_start_brace_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // generatePassCase(read_start_brace, + // read_start_brace_out, + // ">>>> `{` read" + // ); + + // //-TEST_3----------------------------------------------------------// + // // state: INIT + // // read: `}` + // // expect: FAIL (stack underflow) + // let read_end_brace = { ...INITIAL_IN }; + // read_end_brace.byte = Delimiters.END_BRACE; + // generateFailCase(read_end_brace, + // ">>>> `}` read --> (stack underflow)" + // ); + + // //-TEST_4----------------------------------------------------------// + // // state: pointer == 1, stack == [1,0,0,0] + // // read: `"` + // // expect: parsing_string --> 1 + // let in_object_find_key = { ...INITIAL_IN }; + // in_object_find_key.pointer = 1; + // in_object_find_key.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // in_object_find_key.byte = Delimiters.QUOTE; + // let in_object_find_key_out = { ...INITIAL_OUT }; + // in_object_find_key_out.next_pointer = 1; + // in_object_find_key_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // in_object_find_key_out.next_parsing_string = 1; + // generatePassCase(in_object_find_key, + // in_object_find_key_out, + // ">>>> `\"` read" + // ); + + // //-TEST_5----------------------------------------------------------// + // // state: pointer == 1, stack = [1,0,0,0], parsing_string == 1 + // // read: ` ` + // // expect: NIL + // let in_key = { ...INITIAL_IN }; + // in_key.pointer = 1; + // in_key.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // in_key.parsing_string = 1; + // in_key.byte = WhiteSpace.SPACE; + // let in_key_out = { ...INITIAL_OUT }; + // in_key_out.next_pointer = 1; + // in_key_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // in_key_out.next_parsing_string = 1; + // generatePassCase(in_key, in_key_out, ">>>> ` ` read"); + + // //-TEST_6----------------------------------------------------------// + // // init: pointer == 1, stack == [1,0,0,0] + // // read: `"` + // // expect: parsing_string --> 0 + // // + // let in_key_to_exit = { ...INITIAL_IN }; + // in_key_to_exit.pointer = 1; + // in_key_to_exit.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // in_key_to_exit.parsing_string = 1 + // in_key_to_exit.byte = Delimiters.QUOTE; + // let in_key_to_exit_out = { ...INITIAL_OUT }; + // in_key_to_exit_out.next_pointer = 1; + // in_key_to_exit_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // generatePassCase(in_key_to_exit, in_key_to_exit_out, "`\"` read"); + + // //-TEST_7----------------------------------------------------------// + // // state: pointer == 2, stack == [1,3,0,0] + // // read: `"` + // // expect: parsing_string --> 1 + // let in_tree_find_value = { ...INITIAL_IN }; + // in_tree_find_value.pointer = 1; + // in_tree_find_value.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + // in_tree_find_value.byte = Delimiters.QUOTE; + // let in_tree_find_value_out = { ...INITIAL_OUT }; + // in_tree_find_value_out.next_pointer = 1; + // in_tree_find_value_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + // in_tree_find_value_out.next_parsing_string = 1; + // generatePassCase(in_tree_find_value, + // in_tree_find_value_out, + // ">>>> `\"` read" + // ); + + // //-TEST_8----------------------------------------------------------// + // // state: pointer == 2, stack == [1,3,0,0], parsing_string == 1 + // // read: `"` + // // expect: parsing_string == 0, + // let in_value_to_exit = { ...INITIAL_IN }; + // in_value_to_exit.pointer = 2; + // in_value_to_exit.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + // in_value_to_exit.parsing_string = 1; + // in_value_to_exit.byte = Delimiters.QUOTE; + // let in_value_to_exit_out = { ...INITIAL_OUT }; + // in_value_to_exit_out.next_pointer = 2; + // in_value_to_exit_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + // generatePassCase(in_value_to_exit, + // in_value_to_exit_out, + // ">>>> `\"` is read" + // ); }); diff --git a/circuits/test/parser/stack.test.ts b/circuits/test/parser/stack.test.ts index cad64ee..81af1e7 100644 --- a/circuits/test/parser/stack.test.ts +++ b/circuits/test/parser/stack.test.ts @@ -165,18 +165,7 @@ describe("StateUpdate :: RewriteStack", () => { ">>>> `}` read" ); - //-TEST_9----------------------------------------------------------// - // init: pointer = 1, stack = [1,2,0,0] -> `,` is read - let inside_array = { ...INITIAL_IN }; - inside_array.pointer = 2; - inside_array.stack = [[1, 0], [2, 0], [0, 0], [0, 0]]; - inside_array.byte = Delimiters.COMMA; - let inside_array_out = { ...INITIAL_OUT }; - inside_array_out.next_pointer = 2; - inside_array_out.next_stack = [[1, 0], [2, 0], [0, 0], [0, 0]]; - generatePassCase(inside_array, inside_array_out, ">>>> `,` read"); - - //-TEST_10----------------------------------------------------------// + //-TEST_9-----------------------------------------------------------// // state: pointer == 1, stack == [1,0,0,0] // read: `:` // expect: pointer --> 2 @@ -193,4 +182,17 @@ describe("StateUpdate :: RewriteStack", () => { ">>>> `:` read" ); + // Internal array parsing -----------------------------------------// + + //-TEST_10----------------------------------------------------------// + // init: pointer = 1, stack = [1,2,0,0] -> `,` is read + let inside_array = { ...INITIAL_IN }; + inside_array.pointer = 2; + inside_array.stack = [[1, 0], [2, 0], [0, 0], [0, 0]]; + inside_array.byte = Delimiters.COMMA; + let inside_array_out = { ...INITIAL_OUT }; + inside_array_out.next_pointer = 2; + inside_array_out.next_stack = [[1, 0], [2, 1], [0, 0], [0, 0]]; + generatePassCase(inside_array, inside_array_out, ">>>> `,` read"); + }); \ No newline at end of file diff --git a/circuits/test/parser/values.test.ts b/circuits/test/parser/values.test.ts index 8878b47..55b1ea9 100644 --- a/circuits/test/parser/values.test.ts +++ b/circuits/test/parser/values.test.ts @@ -1,4 +1,4 @@ -import { circomkit, WitnessTester } from "../common"; +import { circomkit, WitnessTester, generateDescription } from "../common"; import { Delimiters, WhiteSpace, Numbers, Escape, INITIAL_IN, INITIAL_OUT } from '.'; describe("StateUpdate :: Values", () => { @@ -15,77 +15,64 @@ describe("StateUpdate :: Values", () => { console.log("#constraints:", await circuit.getConstraintCount()); }); function generatePassCase(input: any, expected: any, desc: string) { - const description = Object.entries(input) - .map(([key, value]) => `${key} = ${value}`) - .join(", "); + const description = generateDescription(input); it(`(valid) witness: ${description}\n${desc}`, async () => { await circuit.expectPass(input, expected); }); } - function generateFailCase(input: any, desc: string) { - const description = Object.entries(input) - .map(([key, value]) => `${key} = ${value}`) - .join(", "); + // //-TEST_1----------------------------------------------------------// + // // idea: Read a number value after a key in an object. + // // state: pointer == 2, stack == [1,3,0,0] + // // read: `0` + // // expect: pointer --> 2 + // // stack --> [1,3,0,0] + // // parsing_number --> 1 + // let read_number = { ...INITIAL_IN }; + // read_number.pointer = 2; + // read_number.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + // read_number.byte = Numbers.ZERO; + // let read_number_out = { ...INITIAL_OUT }; + // read_number_out.next_pointer = 2; + // read_number_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + // read_number_out.next_parsing_number = 1; + // generatePassCase(read_number, read_number_out, ">>>> `0` read"); - it(`(invalid) witness: ${description}\n${desc}`, async () => { - await circuit.expectFail(input); - }); - } - - - //-TEST_1----------------------------------------------------------// - // idea: Read a number value after a key in an object. - // state: pointer == 2, stack == [1,3,0,0] - // read: `0` - // expect: pointer --> 2 - // stack --> [1,3,0,0] - // parsing_number --> 1 - let read_number = { ...INITIAL_IN }; - read_number.pointer = 2; - read_number.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - read_number.byte = Numbers.ZERO; - let read_number_out = { ...INITIAL_OUT }; - read_number_out.next_pointer = 2; - read_number_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - read_number_out.next_parsing_number = 1; - generatePassCase(read_number, read_number_out, ">>>> `0` read"); - - //-TEST_2----------------------------------------------------------// - // idea: Inside a number value after a key in an object. - // state: pointer == 2, stack == [1,3,0,0], parsing_number == 1 - // read: `,` - // expect: pointer --> 2 - // stack --> [1,3,0,0] - // parsing_number --> 0 - let inside_number = { ...INITIAL_IN }; - inside_number.pointer = 2; - inside_number.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - inside_number.parsing_number = 1; - inside_number.byte = Delimiters.COMMA; - let inside_number_out = { ...INITIAL_OUT }; - inside_number_out.next_pointer = 2; - inside_number_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - generatePassCase(inside_number, inside_number_out, ">>>> `,` read"); + // //-TEST_2----------------------------------------------------------// + // // idea: Inside a number value after a key in an object. + // // state: pointer == 2, stack == [1,3,0,0], parsing_number == 1 + // // read: `,` + // // expect: pointer --> 2 + // // stack --> [1,3,0,0] + // // parsing_number --> 0 + // let inside_number = { ...INITIAL_IN }; + // inside_number.pointer = 2; + // inside_number.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + // inside_number.parsing_number = 1; + // inside_number.byte = Delimiters.COMMA; + // let inside_number_out = { ...INITIAL_OUT }; + // inside_number_out.next_pointer = 2; + // inside_number_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + // generatePassCase(inside_number, inside_number_out, ">>>> `,` read"); - // TODO: Note that reading a space while reading a number will not throw an error! + // // TODO: Note that reading a space while reading a number will not throw an error! - //-TEST_2----------------------------------------------------------// - // idea: Inside a number value after a key in an object. - // state: pointer == 2, stack == [1,3,0,0], parsing_number == 1 - // read: `1` - // expect: pointer --> 2 - // stack --> [1,3,0,0] - // parsing_number --> 0 - let inside_number_continue = { ...INITIAL_IN }; - inside_number_continue.pointer = 2; - inside_number_continue.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - inside_number_continue.parsing_number = 1; - inside_number_continue.byte = Numbers.ONE; - let inside_number_continue_out = { ...INITIAL_OUT }; - inside_number_continue_out.next_pointer = 2; - inside_number_continue_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - inside_number_continue_out.next_parsing_number = 1; - generatePassCase(inside_number_continue, inside_number_continue_out, ">>>> `,` read"); + // //-TEST_2----------------------------------------------------------// + // // idea: Inside a number value after a key in an object. + // // state: pointer == 2, stack == [1,3,0,0], parsing_number == 1 + // // read: `1` + // // expect: pointer --> 2 + // // stack --> [1,3,0,0] + // // parsing_number --> 0 + // let inside_number_continue = { ...INITIAL_IN }; + // inside_number_continue.pointer = 2; + // inside_number_continue.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + // inside_number_continue.parsing_number = 1; + // inside_number_continue.byte = Numbers.ONE; + // let inside_number_continue_out = { ...INITIAL_OUT }; + // inside_number_continue_out.next_pointer = 2; + // inside_number_continue_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + // inside_number_continue_out.next_parsing_number = 1; + // generatePassCase(inside_number_continue, inside_number_continue_out, ">>>> `,` read"); }); \ No newline at end of file From 33fbe23782807aa7db7803e9b3ccfff5d6eb8154 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Wed, 14 Aug 2024 15:24:40 -0600 Subject: [PATCH 76/99] tests passing but still not properly indexing in arrays --- circuits/extract.circom | 8 +- circuits/parser.circom | 16 +- circuits/test/parser/parser.test.ts | 222 ++++++++++++++-------------- circuits/test/parser/values.test.ts | 102 ++++++------- 4 files changed, 178 insertions(+), 170 deletions(-) diff --git a/circuits/extract.circom b/circuits/extract.circom index ce6983b..10eb0ab 100644 --- a/circuits/extract.circom +++ b/circuits/extract.circom @@ -20,9 +20,8 @@ template Extract(DATA_BYTES, MAX_STACK_HEIGHT) { State[0].byte <== data[0]; State[0].pointer <== 0; for(var i = 0; i < MAX_STACK_HEIGHT; i++) { - State[0].stack[i] <== 0; + State[0].stack[i] <== [0,0]; } - // State[0].stack <== [0,0,0,0,0,0,0,0,0,0,0,0]; State[0].parsing_string <== 0; State[0].parsing_number <== 0; @@ -37,7 +36,7 @@ template Extract(DATA_BYTES, MAX_STACK_HEIGHT) { // Debugging log("State[", data_idx, "].pointer ", "= ", State[data_idx].pointer); for(var i = 0; i { }); - // //-TEST_1----------------------------------------------------------// - // // init: ZEROS then read `do_nothing` byte - // // expect: ZEROS - // generatePassCase(INITIAL_IN, INITIAL_OUT, ">>>> `NUL` read"); - - // // TODO: Consider moving to `stack.test.ts` - // //-TEST_2----------------------------------------------------------// - // // init: INIT - // // read: `{` - // // expect: pointer --> 1 - // // stack --> [1,0,0,0] - // let read_start_brace = { ...INITIAL_IN }; - // read_start_brace.byte = Delimiters.START_BRACE; - // let read_start_brace_out = { ...INITIAL_OUT }; - // read_start_brace_out.next_pointer = 1; - // read_start_brace_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - // generatePassCase(read_start_brace, - // read_start_brace_out, - // ">>>> `{` read" - // ); - - // //-TEST_3----------------------------------------------------------// - // // state: INIT - // // read: `}` - // // expect: FAIL (stack underflow) - // let read_end_brace = { ...INITIAL_IN }; - // read_end_brace.byte = Delimiters.END_BRACE; - // generateFailCase(read_end_brace, - // ">>>> `}` read --> (stack underflow)" - // ); - - // //-TEST_4----------------------------------------------------------// - // // state: pointer == 1, stack == [1,0,0,0] - // // read: `"` - // // expect: parsing_string --> 1 - // let in_object_find_key = { ...INITIAL_IN }; - // in_object_find_key.pointer = 1; - // in_object_find_key.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - // in_object_find_key.byte = Delimiters.QUOTE; - // let in_object_find_key_out = { ...INITIAL_OUT }; - // in_object_find_key_out.next_pointer = 1; - // in_object_find_key_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - // in_object_find_key_out.next_parsing_string = 1; - // generatePassCase(in_object_find_key, - // in_object_find_key_out, - // ">>>> `\"` read" - // ); - - // //-TEST_5----------------------------------------------------------// - // // state: pointer == 1, stack = [1,0,0,0], parsing_string == 1 - // // read: ` ` - // // expect: NIL - // let in_key = { ...INITIAL_IN }; - // in_key.pointer = 1; - // in_key.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - // in_key.parsing_string = 1; - // in_key.byte = WhiteSpace.SPACE; - // let in_key_out = { ...INITIAL_OUT }; - // in_key_out.next_pointer = 1; - // in_key_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - // in_key_out.next_parsing_string = 1; - // generatePassCase(in_key, in_key_out, ">>>> ` ` read"); - - // //-TEST_6----------------------------------------------------------// - // // init: pointer == 1, stack == [1,0,0,0] - // // read: `"` - // // expect: parsing_string --> 0 - // // - // let in_key_to_exit = { ...INITIAL_IN }; - // in_key_to_exit.pointer = 1; - // in_key_to_exit.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - // in_key_to_exit.parsing_string = 1 - // in_key_to_exit.byte = Delimiters.QUOTE; - // let in_key_to_exit_out = { ...INITIAL_OUT }; - // in_key_to_exit_out.next_pointer = 1; - // in_key_to_exit_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - // generatePassCase(in_key_to_exit, in_key_to_exit_out, "`\"` read"); - - // //-TEST_7----------------------------------------------------------// - // // state: pointer == 2, stack == [1,3,0,0] - // // read: `"` - // // expect: parsing_string --> 1 - // let in_tree_find_value = { ...INITIAL_IN }; - // in_tree_find_value.pointer = 1; - // in_tree_find_value.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - // in_tree_find_value.byte = Delimiters.QUOTE; - // let in_tree_find_value_out = { ...INITIAL_OUT }; - // in_tree_find_value_out.next_pointer = 1; - // in_tree_find_value_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - // in_tree_find_value_out.next_parsing_string = 1; - // generatePassCase(in_tree_find_value, - // in_tree_find_value_out, - // ">>>> `\"` read" - // ); - - // //-TEST_8----------------------------------------------------------// - // // state: pointer == 2, stack == [1,3,0,0], parsing_string == 1 - // // read: `"` - // // expect: parsing_string == 0, - // let in_value_to_exit = { ...INITIAL_IN }; - // in_value_to_exit.pointer = 2; - // in_value_to_exit.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - // in_value_to_exit.parsing_string = 1; - // in_value_to_exit.byte = Delimiters.QUOTE; - // let in_value_to_exit_out = { ...INITIAL_OUT }; - // in_value_to_exit_out.next_pointer = 2; - // in_value_to_exit_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - // generatePassCase(in_value_to_exit, - // in_value_to_exit_out, - // ">>>> `\"` is read" - // ); + //-TEST_1----------------------------------------------------------// + // init: ZEROS then read `do_nothing` byte + // expect: ZEROS + generatePassCase(INITIAL_IN, INITIAL_OUT, ">>>> `NUL` read"); + + // TODO: Consider moving to `stack.test.ts` + //-TEST_2----------------------------------------------------------// + // init: INIT + // read: `{` + // expect: pointer --> 1 + // stack --> [1,0,0,0] + let read_start_brace = { ...INITIAL_IN }; + read_start_brace.byte = Delimiters.START_BRACE; + let read_start_brace_out = { ...INITIAL_OUT }; + read_start_brace_out.next_pointer = 1; + read_start_brace_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + generatePassCase(read_start_brace, + read_start_brace_out, + ">>>> `{` read" + ); + + //-TEST_3----------------------------------------------------------// + // state: INIT + // read: `}` + // expect: FAIL (stack underflow) + let read_end_brace = { ...INITIAL_IN }; + read_end_brace.byte = Delimiters.END_BRACE; + generateFailCase(read_end_brace, + ">>>> `}` read --> (stack underflow)" + ); + + //-TEST_4----------------------------------------------------------// + // state: pointer == 1, stack == [1,0,0,0] + // read: `"` + // expect: parsing_string --> 1 + let in_object_find_key = { ...INITIAL_IN }; + in_object_find_key.pointer = 1; + in_object_find_key.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + in_object_find_key.byte = Delimiters.QUOTE; + let in_object_find_key_out = { ...INITIAL_OUT }; + in_object_find_key_out.next_pointer = 1; + in_object_find_key_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + in_object_find_key_out.next_parsing_string = 1; + generatePassCase(in_object_find_key, + in_object_find_key_out, + ">>>> `\"` read" + ); + + //-TEST_5----------------------------------------------------------// + // state: pointer == 1, stack = [1,0,0,0], parsing_string == 1 + // read: ` ` + // expect: NIL + let in_key = { ...INITIAL_IN }; + in_key.pointer = 1; + in_key.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + in_key.parsing_string = 1; + in_key.byte = WhiteSpace.SPACE; + let in_key_out = { ...INITIAL_OUT }; + in_key_out.next_pointer = 1; + in_key_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + in_key_out.next_parsing_string = 1; + generatePassCase(in_key, in_key_out, ">>>> ` ` read"); + + //-TEST_6----------------------------------------------------------// + // init: pointer == 1, stack == [1,0,0,0] + // read: `"` + // expect: parsing_string --> 0 + // + let in_key_to_exit = { ...INITIAL_IN }; + in_key_to_exit.pointer = 1; + in_key_to_exit.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + in_key_to_exit.parsing_string = 1 + in_key_to_exit.byte = Delimiters.QUOTE; + let in_key_to_exit_out = { ...INITIAL_OUT }; + in_key_to_exit_out.next_pointer = 1; + in_key_to_exit_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + generatePassCase(in_key_to_exit, in_key_to_exit_out, "`\"` read"); + + //-TEST_7----------------------------------------------------------// + // state: pointer == 2, stack == [1,3,0,0] + // read: `"` + // expect: parsing_string --> 1 + let in_tree_find_value = { ...INITIAL_IN }; + in_tree_find_value.pointer = 1; + in_tree_find_value.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + in_tree_find_value.byte = Delimiters.QUOTE; + let in_tree_find_value_out = { ...INITIAL_OUT }; + in_tree_find_value_out.next_pointer = 1; + in_tree_find_value_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + in_tree_find_value_out.next_parsing_string = 1; + generatePassCase(in_tree_find_value, + in_tree_find_value_out, + ">>>> `\"` read" + ); + + //-TEST_8----------------------------------------------------------// + // state: pointer == 2, stack == [1,3,0,0], parsing_string == 1 + // read: `"` + // expect: parsing_string == 0, + let in_value_to_exit = { ...INITIAL_IN }; + in_value_to_exit.pointer = 2; + in_value_to_exit.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + in_value_to_exit.parsing_string = 1; + in_value_to_exit.byte = Delimiters.QUOTE; + let in_value_to_exit_out = { ...INITIAL_OUT }; + in_value_to_exit_out.next_pointer = 2; + in_value_to_exit_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + generatePassCase(in_value_to_exit, + in_value_to_exit_out, + ">>>> `\"` is read" + ); }); diff --git a/circuits/test/parser/values.test.ts b/circuits/test/parser/values.test.ts index 55b1ea9..c74fb8f 100644 --- a/circuits/test/parser/values.test.ts +++ b/circuits/test/parser/values.test.ts @@ -22,57 +22,59 @@ describe("StateUpdate :: Values", () => { }); } - // //-TEST_1----------------------------------------------------------// - // // idea: Read a number value after a key in an object. - // // state: pointer == 2, stack == [1,3,0,0] - // // read: `0` - // // expect: pointer --> 2 - // // stack --> [1,3,0,0] - // // parsing_number --> 1 - // let read_number = { ...INITIAL_IN }; - // read_number.pointer = 2; - // read_number.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - // read_number.byte = Numbers.ZERO; - // let read_number_out = { ...INITIAL_OUT }; - // read_number_out.next_pointer = 2; - // read_number_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - // read_number_out.next_parsing_number = 1; - // generatePassCase(read_number, read_number_out, ">>>> `0` read"); + //-TEST_1----------------------------------------------------------// + // idea: Read a number value after a key in an object. + // state: pointer == 2, stack == [1,3,0,0] + // read: `0` + // expect: pointer --> 2 + // stack --> [1,3,0,0] + // parsing_number --> 1 + let read_number = { ...INITIAL_IN }; + read_number.pointer = 2; + read_number.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + read_number.byte = Numbers.ZERO; + let read_number_out = { ...INITIAL_OUT }; + read_number_out.next_pointer = 2; + read_number_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + read_number_out.next_parsing_number = 1; + generatePassCase(read_number, read_number_out, ">>>> `0` read"); - // //-TEST_2----------------------------------------------------------// - // // idea: Inside a number value after a key in an object. - // // state: pointer == 2, stack == [1,3,0,0], parsing_number == 1 - // // read: `,` - // // expect: pointer --> 2 - // // stack --> [1,3,0,0] - // // parsing_number --> 0 - // let inside_number = { ...INITIAL_IN }; - // inside_number.pointer = 2; - // inside_number.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - // inside_number.parsing_number = 1; - // inside_number.byte = Delimiters.COMMA; - // let inside_number_out = { ...INITIAL_OUT }; - // inside_number_out.next_pointer = 2; - // inside_number_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - // generatePassCase(inside_number, inside_number_out, ">>>> `,` read"); + //-TEST_2----------------------------------------------------------// + // idea: Inside a number value after a key in an object. + // state: pointer == 2, stack == [1,3,0,0], parsing_number == 1 + // read: `,` + // expect: pointer --> 2 + // stack --> [1,3,0,0] + // parsing_number --> 0 + let inside_number = { ...INITIAL_IN }; + inside_number.pointer = 2; + inside_number.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + inside_number.parsing_number = 1; + inside_number.byte = Delimiters.COMMA; + let inside_number_out = { ...INITIAL_OUT }; + inside_number_out.next_pointer = 2; + inside_number_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + generatePassCase(inside_number, inside_number_out, ">>>> `,` read"); + + // TODO: Note that reading a space while reading a number will not throw an error! + + //-TEST_2----------------------------------------------------------// + // idea: Inside a number value after a key in an object. + // state: pointer == 2, stack == [1,3,0,0], parsing_number == 1 + // read: `1` + // expect: pointer --> 2 + // stack --> [1,3,0,0] + // parsing_number --> 0 + let inside_number_continue = { ...INITIAL_IN }; + inside_number_continue.pointer = 2; + inside_number_continue.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + inside_number_continue.parsing_number = 1; + inside_number_continue.byte = Numbers.ONE; + let inside_number_continue_out = { ...INITIAL_OUT }; + inside_number_continue_out.next_pointer = 2; + inside_number_continue_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + inside_number_continue_out.next_parsing_number = 1; + generatePassCase(inside_number_continue, inside_number_continue_out, ">>>> `,` read"); - // // TODO: Note that reading a space while reading a number will not throw an error! - // //-TEST_2----------------------------------------------------------// - // // idea: Inside a number value after a key in an object. - // // state: pointer == 2, stack == [1,3,0,0], parsing_number == 1 - // // read: `1` - // // expect: pointer --> 2 - // // stack --> [1,3,0,0] - // // parsing_number --> 0 - // let inside_number_continue = { ...INITIAL_IN }; - // inside_number_continue.pointer = 2; - // inside_number_continue.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - // inside_number_continue.parsing_number = 1; - // inside_number_continue.byte = Numbers.ONE; - // let inside_number_continue_out = { ...INITIAL_OUT }; - // inside_number_continue_out.next_pointer = 2; - // inside_number_continue_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - // inside_number_continue_out.next_parsing_number = 1; - // generatePassCase(inside_number_continue, inside_number_continue_out, ">>>> `,` read"); }); \ No newline at end of file From 7a30f8ad0c2bac4ca6abd07c5235ae55d73a36a8 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Wed, 14 Aug 2024 15:31:05 -0600 Subject: [PATCH 77/99] small cleanup --- circuits/parser.circom | 6 +++--- circuits/test/parser/values.test.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 9b721fe..bf68a9d 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -254,7 +254,7 @@ template RewriteStack(n) { signal first_pop_val[n]; signal temp_val[n]; - +log("read_comma_in_array: ", read_comma_in_array); for(var i = 0; i < n; i++) { // Indicators for index to PUSH to or POP from @@ -269,9 +269,9 @@ template RewriteStack(n) { first_pop_val[i] <== isPopAt[i] * temp_val[i]; // = isPopAt[i] * (corrected_stack_val * (1 - isDoublePop) - 3 * isDoublePop) next_stack[i][0] <== stack[i][0] + isPushAt[i] * corrected_stack_val + first_pop_val[i] + second_pop_val[i]; - next_stack[i][1] <== prev_indicator[i].out * read_comma_in_array; + next_stack[i][1] <== stack[i][1] + prev_indicator[i].out * read_comma_in_array; - log("read_comma_in_array: ", read_comma_in_array); + log("prev_indicator[i]: ", prev_indicator[i].out); log("next_stack[", i,"] ", "= [",next_stack[i][0], "][", next_stack[i][1],"]" ); // TODO: Constrain next_stack entries to be 0,1,2,3 } diff --git a/circuits/test/parser/values.test.ts b/circuits/test/parser/values.test.ts index c74fb8f..8f39c4a 100644 --- a/circuits/test/parser/values.test.ts +++ b/circuits/test/parser/values.test.ts @@ -74,7 +74,7 @@ describe("StateUpdate :: Values", () => { inside_number_continue_out.next_pointer = 2; inside_number_continue_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; inside_number_continue_out.next_parsing_number = 1; - generatePassCase(inside_number_continue, inside_number_continue_out, ">>>> `,` read"); + generatePassCase(inside_number_continue, inside_number_continue_out, ">>>> `1` read"); }); \ No newline at end of file From caa4941760c3d4f658445f52f849e133375cc2fe Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Wed, 14 Aug 2024 15:43:10 -0600 Subject: [PATCH 78/99] almost there! --- circuits/parser.circom | 20 ++++++++++++++------ circuits/test/parser/stack.test.ts | 12 ------------ circuits/test/parser/values.test.ts | 24 ++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index bf68a9d..a017e9a 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -101,7 +101,7 @@ template StateToMask(n) { signal parsing_number <== in[3]; // `pushpop` can change: IF NOT `parsing_string` - out[0] <== (1 - parsing_string) * (1 - parsing_number); + out[0] <== (1 - parsing_string); // `stack_val`can change: IF NOT `parsing_string` out[1] <== (1 - parsing_string); @@ -216,7 +216,8 @@ template RewriteStack(n) { signal read_comma_in_array <== readComma.out * isArray.out; component isPop = IsZero(); - isPop.in <== (1 - isReadCommaAndInArray.out) * pushpop + 1; + isPop.in <== (1 - isReadCommaAndInArray.out) * pushpop + 1; // TODO: can simplify? + component isPush = IsZero(); isPush.in <== pushpop - 1; component prev_indicator[n]; @@ -234,6 +235,10 @@ template RewriteStack(n) { // top of stack is a 3, then we need to pop off 3, and check the value underneath // is correct match (i.e., a brace or bracket (1 or 2)) + component readEndArr = IsZero(); + readEndArr.in <== stack_val + 2; + signal isPopArr <== isPop.out * readEndArr.out; + for(var i = 0; i < n; i++) { // points to 1 value back from top prev_indicator[i] = IsZero(); @@ -253,8 +258,9 @@ template RewriteStack(n) { signal second_pop_val[n]; signal first_pop_val[n]; signal temp_val[n]; + signal temp_val2[n]; -log("read_comma_in_array: ", read_comma_in_array); +// log("read_comma_in_array: ", read_comma_in_array); for(var i = 0; i < n; i++) { // Indicators for index to PUSH to or POP from @@ -269,10 +275,12 @@ log("read_comma_in_array: ", read_comma_in_array); first_pop_val[i] <== isPopAt[i] * temp_val[i]; // = isPopAt[i] * (corrected_stack_val * (1 - isDoublePop) - 3 * isDoublePop) next_stack[i][0] <== stack[i][0] + isPushAt[i] * corrected_stack_val + first_pop_val[i] + second_pop_val[i]; - next_stack[i][1] <== stack[i][1] + prev_indicator[i].out * read_comma_in_array; - log("prev_indicator[i]: ", prev_indicator[i].out); - log("next_stack[", i,"] ", "= [",next_stack[i][0], "][", next_stack[i][1],"]" ); + temp_val2[i] <== prev_indicator[i].out * read_comma_in_array; + next_stack[i][1] <== stack[i][1] + temp_val2[i] - stack[i][1] * isPopArr; + + // log("prev_indicator[i]: ", prev_indicator[i].out); + // log("next_stack[", i,"] ", "= [",next_stack[i][0], "][", next_stack[i][1],"]" ); // TODO: Constrain next_stack entries to be 0,1,2,3 } diff --git a/circuits/test/parser/stack.test.ts b/circuits/test/parser/stack.test.ts index 81af1e7..0add5af 100644 --- a/circuits/test/parser/stack.test.ts +++ b/circuits/test/parser/stack.test.ts @@ -182,17 +182,5 @@ describe("StateUpdate :: RewriteStack", () => { ">>>> `:` read" ); - // Internal array parsing -----------------------------------------// - - //-TEST_10----------------------------------------------------------// - // init: pointer = 1, stack = [1,2,0,0] -> `,` is read - let inside_array = { ...INITIAL_IN }; - inside_array.pointer = 2; - inside_array.stack = [[1, 0], [2, 0], [0, 0], [0, 0]]; - inside_array.byte = Delimiters.COMMA; - let inside_array_out = { ...INITIAL_OUT }; - inside_array_out.next_pointer = 2; - inside_array_out.next_stack = [[1, 0], [2, 1], [0, 0], [0, 0]]; - generatePassCase(inside_array, inside_array_out, ">>>> `,` read"); }); \ No newline at end of file diff --git a/circuits/test/parser/values.test.ts b/circuits/test/parser/values.test.ts index 8f39c4a..e82c661 100644 --- a/circuits/test/parser/values.test.ts +++ b/circuits/test/parser/values.test.ts @@ -76,5 +76,29 @@ describe("StateUpdate :: Values", () => { inside_number_continue_out.next_parsing_number = 1; generatePassCase(inside_number_continue, inside_number_continue_out, ">>>> `1` read"); + describe("StateUpdate :: Values :: Array", () => { + // Internal array parsing -----------------------------------------// + //-TEST_10----------------------------------------------------------// + // init: pointer = 1, stack = [1,2,0,0] -> `,` is read + let in_arr = { ...INITIAL_IN }; + in_arr.pointer = 2; + in_arr.stack = [[1, 0], [2, 0], [0, 0], [0, 0]]; + in_arr.byte = Delimiters.COMMA; + let in_arr_out = { ...INITIAL_OUT }; + in_arr_out.next_pointer = 2; + in_arr_out.next_stack = [[1, 0], [2, 1], [0, 0], [0, 0]]; + generatePassCase(in_arr, in_arr_out, ">>>> `,` read"); + + //-TEST_10----------------------------------------------------------// + // init: pointer = 1, stack = [1,2,0,0] -> `,` is read + let in_arr_idx_to_leave = { ...INITIAL_IN }; + in_arr_idx_to_leave.pointer = 2; + in_arr_idx_to_leave.stack = [[1, 0], [2, 1], [0, 0], [0, 0]]; + in_arr_idx_to_leave.byte = Delimiters.END_BRACKET; + let in_arr_idx_to_leave_out = { ...INITIAL_OUT }; + in_arr_idx_to_leave_out.next_pointer = 1; + in_arr_idx_to_leave_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + generatePassCase(in_arr_idx_to_leave, in_arr_idx_to_leave_out, ">>>> `]` read"); + }); }); \ No newline at end of file From 17f110525426c7188adeb368b457e68fbbeb32ce Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Wed, 14 Aug 2024 15:45:21 -0600 Subject: [PATCH 79/99] arrays! --- circuits/test/parser/values.test.ts | 4 ++-- notes.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/circuits/test/parser/values.test.ts b/circuits/test/parser/values.test.ts index e82c661..95cb8e0 100644 --- a/circuits/test/parser/values.test.ts +++ b/circuits/test/parser/values.test.ts @@ -52,8 +52,8 @@ describe("StateUpdate :: Values", () => { inside_number.parsing_number = 1; inside_number.byte = Delimiters.COMMA; let inside_number_out = { ...INITIAL_OUT }; - inside_number_out.next_pointer = 2; - inside_number_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + inside_number_out.next_pointer = 1; + inside_number_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; generatePassCase(inside_number, inside_number_out, ">>>> `,` read"); // TODO: Note that reading a space while reading a number will not throw an error! diff --git a/notes.md b/notes.md index 28ea2fa..7658cb3 100644 --- a/notes.md +++ b/notes.md @@ -4,7 +4,7 @@ ### JSON Types - [x] Object - [x] String -- [ ] Array (PARTIALLY COMPLETED, TODO: Need to parse internally) +- [x] Array - [x] Number - [ ] Boolean - [ ] Null From 7294dd53fb900c60797bf90c63483c1de5ea98cc Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Wed, 14 Aug 2024 15:53:22 -0600 Subject: [PATCH 80/99] satisfying! --- circuits.json | 16 ++++++++++++++++ circuits/parser.circom | 4 ++-- json_examples/test/value_array_nested.json | 1 + json_examples/test/value_array_object.json | 1 + 4 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 json_examples/test/value_array_nested.json create mode 100644 json_examples/test/value_array_object.json diff --git a/circuits.json b/circuits.json index b8a4a39..d3ce6be 100644 --- a/circuits.json +++ b/circuits.json @@ -31,6 +31,22 @@ 3 ] }, + "value_array_nested": { + "file": "extract", + "template": "Extract", + "params": [ + 24, + 5 + ] + }, + "value_array_object": { + "file": "extract", + "template": "Extract", + "params": [ + 25, + 5 + ] + }, "value_object": { "file": "extract", "template": "Extract", diff --git a/circuits/parser.circom b/circuits/parser.circom index a017e9a..1db6cae 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -201,13 +201,13 @@ template RewriteStack(n) { isArray.in[0] <== topOfStack.out[0]; isArray.in[1] <== 2; - log("isArray: ", isArray.out); + // log("isArray: ", isArray.out); component readComma = IsEqual(); readComma.in[0] <== 4; readComma.in[1] <== stack_val; - log("readComma: ", readComma.out); + // log("readComma: ", readComma.out); signal READ_COMMA_AND_IN_ARRAY <== (1 - readComma.out) + (1 - isArray.out); // POORLY NAMED. THIS IS MORE LIKE XNOR or something. component isReadCommaAndInArray = IsZero(); diff --git a/json_examples/test/value_array_nested.json b/json_examples/test/value_array_nested.json new file mode 100644 index 0000000..13907e1 --- /dev/null +++ b/json_examples/test/value_array_nested.json @@ -0,0 +1 @@ +{ "a": [[1,0],[0,1,3]] } \ No newline at end of file diff --git a/json_examples/test/value_array_object.json b/json_examples/test/value_array_object.json new file mode 100644 index 0000000..2bbaec9 --- /dev/null +++ b/json_examples/test/value_array_object.json @@ -0,0 +1 @@ +{"a":[{"b":5},{"c":"b"}]} \ No newline at end of file From 2270ac909fdae3e03177c38740ddf99c20f67b70 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Wed, 14 Aug 2024 16:03:34 -0600 Subject: [PATCH 81/99] another example --- circuits.json | 8 ++++++++ json_examples/test/value_array_object_array.json | 1 + notes.md | 6 ++++++ 3 files changed, 15 insertions(+) create mode 100644 json_examples/test/value_array_object_array.json diff --git a/circuits.json b/circuits.json index d3ce6be..b845556 100644 --- a/circuits.json +++ b/circuits.json @@ -47,6 +47,14 @@ 5 ] }, + "value_array_object_array": { + "file": "extract", + "template": "Extract", + "params": [ + 31, + 6 + ] + }, "value_object": { "file": "extract", "template": "Extract", diff --git a/json_examples/test/value_array_object_array.json b/json_examples/test/value_array_object_array.json new file mode 100644 index 0000000..885e4ed --- /dev/null +++ b/json_examples/test/value_array_object_array.json @@ -0,0 +1 @@ +{"a":[{"b":5},{"c":[0,1,"a"]}]} \ No newline at end of file diff --git a/notes.md b/notes.md index 7658cb3..f21dd32 100644 --- a/notes.md +++ b/notes.md @@ -9,6 +9,12 @@ - [ ] Boolean - [ ] Null +Parsing null and bool need to do some kind of look ahead parsing. To handle numbers properly we also probably need that actually since we need to look ahead to where we get white space. +Need to look ahead for `true` and `false` for example to ensure we get a full match, or we fail or something. Lookaehad might be overkill, but yeah. + +#### Numbers +Numbers can have `e` and decimal `.` in them. Riperoni. + ### string escape shouldn't be too hard, just add one more state variable `escaping` that is only enabled when parsing a string and can only be toggled -- next state will always have to set back to 0. From 267e91396868a633871f68d34818d47a5b27dee6 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Wed, 14 Aug 2024 16:12:04 -0600 Subject: [PATCH 82/99] Update notes.md --- notes.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/notes.md b/notes.md index f21dd32..32ae25d 100644 --- a/notes.md +++ b/notes.md @@ -18,6 +18,8 @@ Numbers can have `e` and decimal `.` in them. Riperoni. ### string escape shouldn't be too hard, just add one more state variable `escaping` that is only enabled when parsing a string and can only be toggled -- next state will always have to set back to 0. +This could also allow for parsing unicode + ## Expected Output > This is old at this point, but we should update it. ``` From ea18b67868fdec2008bf731594f1dbf5e66ac89a Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 15 Aug 2024 16:33:28 -0600 Subject: [PATCH 83/99] save notes --- notes.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/notes.md b/notes.md index 32ae25d..ba1c261 100644 --- a/notes.md +++ b/notes.md @@ -20,6 +20,11 @@ shouldn't be too hard, just add one more state variable `escaping` that is only This could also allow for parsing unicode +### Other thoughts + - Pointer may not actually be necessary, because it just points to the first unallocated position in the stac + - How do we know how tall to make the stack? Open braces `{`, open brackets `[` and colons `:` all push onto the stack. + - We might not actually need to push the stack higher with a colon, instead we could push state into the second slot of the stack like we do with commas inside of arrays. + ## Expected Output > This is old at this point, but we should update it. ``` From e84b1961ab4f513ba2ee37ab02eb7ba5c7d7deec Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 15 Aug 2024 16:41:46 -0600 Subject: [PATCH 84/99] Squashed commit of the following: commit ed2c440f149ed5afafd1a7a3f558072afde5da1f Author: Colin Roberts Date: Thu Aug 15 16:32:44 2024 -0600 tests/refactor: state update and improved JSON parsing (#11) * add: `reddit_response.json` * refactor tests + add failing case * easier fix * test: parse to key * tests: key parsing * bug: `next_end_of_kv` on read `:` * fix: `end_of_kv` bug * test: find value * tests: `inside_value` and `inside_value_to_exit` * test: parse to NEXT key * parses JSON with two string keys * WIP: value inside value * comment * refactor (#10) * wip: start with bitmask * WIP: time to start testing * tests: `ArrayAdd` and `ArrayMul` * tests passing * update comments * feat: 2 key depth 1 json * 2 kv json and all tests passing * nested json works!!! * reduce constraints * cleanup * rename variables * more cleaning * more cleanup * make comments clean * WAYLON NITPICKING ME LOL clean up merge commit From d5c06e13eee525f3d9ea672f418cf2273d09a555 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 15 Aug 2024 17:31:51 -0600 Subject: [PATCH 85/99] WIP: remove `pointer` state --- circuits/extract.circom | 3 - circuits/parser.circom | 129 ++++++------ circuits/test/parser/parser.test.ts | 222 ++++++++++----------- circuits/test/parser/stack.test.ts | 291 +++++++++++++++------------- circuits/test/parser/values.test.ts | 146 +++++++------- 5 files changed, 407 insertions(+), 384 deletions(-) diff --git a/circuits/extract.circom b/circuits/extract.circom index 10eb0ab..2311f7a 100644 --- a/circuits/extract.circom +++ b/circuits/extract.circom @@ -18,7 +18,6 @@ template Extract(DATA_BYTES, MAX_STACK_HEIGHT) { component State[DATA_BYTES]; State[0] = StateUpdate(MAX_STACK_HEIGHT); State[0].byte <== data[0]; - State[0].pointer <== 0; for(var i = 0; i < MAX_STACK_HEIGHT; i++) { State[0].stack[i] <== [0,0]; } @@ -28,13 +27,11 @@ template Extract(DATA_BYTES, MAX_STACK_HEIGHT) { for(var data_idx = 1; data_idx < DATA_BYTES; data_idx++) { State[data_idx] = StateUpdate(MAX_STACK_HEIGHT); State[data_idx].byte <== data[data_idx]; - State[data_idx].pointer <== State[data_idx - 1].next_pointer; State[data_idx].stack <== State[data_idx - 1].next_stack; State[data_idx].parsing_string <== State[data_idx - 1].next_parsing_string; State[data_idx].parsing_number <== State[data_idx - 1].next_parsing_number; // Debugging - log("State[", data_idx, "].pointer ", "= ", State[data_idx].pointer); for(var i = 0; i { }); - //-TEST_1----------------------------------------------------------// - // init: ZEROS then read `do_nothing` byte - // expect: ZEROS - generatePassCase(INITIAL_IN, INITIAL_OUT, ">>>> `NUL` read"); - - // TODO: Consider moving to `stack.test.ts` - //-TEST_2----------------------------------------------------------// - // init: INIT - // read: `{` - // expect: pointer --> 1 - // stack --> [1,0,0,0] - let read_start_brace = { ...INITIAL_IN }; - read_start_brace.byte = Delimiters.START_BRACE; - let read_start_brace_out = { ...INITIAL_OUT }; - read_start_brace_out.next_pointer = 1; - read_start_brace_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - generatePassCase(read_start_brace, - read_start_brace_out, - ">>>> `{` read" - ); - - //-TEST_3----------------------------------------------------------// - // state: INIT - // read: `}` - // expect: FAIL (stack underflow) - let read_end_brace = { ...INITIAL_IN }; - read_end_brace.byte = Delimiters.END_BRACE; - generateFailCase(read_end_brace, - ">>>> `}` read --> (stack underflow)" - ); - - //-TEST_4----------------------------------------------------------// - // state: pointer == 1, stack == [1,0,0,0] - // read: `"` - // expect: parsing_string --> 1 - let in_object_find_key = { ...INITIAL_IN }; - in_object_find_key.pointer = 1; - in_object_find_key.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - in_object_find_key.byte = Delimiters.QUOTE; - let in_object_find_key_out = { ...INITIAL_OUT }; - in_object_find_key_out.next_pointer = 1; - in_object_find_key_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - in_object_find_key_out.next_parsing_string = 1; - generatePassCase(in_object_find_key, - in_object_find_key_out, - ">>>> `\"` read" - ); - - //-TEST_5----------------------------------------------------------// - // state: pointer == 1, stack = [1,0,0,0], parsing_string == 1 - // read: ` ` - // expect: NIL - let in_key = { ...INITIAL_IN }; - in_key.pointer = 1; - in_key.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - in_key.parsing_string = 1; - in_key.byte = WhiteSpace.SPACE; - let in_key_out = { ...INITIAL_OUT }; - in_key_out.next_pointer = 1; - in_key_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - in_key_out.next_parsing_string = 1; - generatePassCase(in_key, in_key_out, ">>>> ` ` read"); - - //-TEST_6----------------------------------------------------------// - // init: pointer == 1, stack == [1,0,0,0] - // read: `"` - // expect: parsing_string --> 0 - // - let in_key_to_exit = { ...INITIAL_IN }; - in_key_to_exit.pointer = 1; - in_key_to_exit.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - in_key_to_exit.parsing_string = 1 - in_key_to_exit.byte = Delimiters.QUOTE; - let in_key_to_exit_out = { ...INITIAL_OUT }; - in_key_to_exit_out.next_pointer = 1; - in_key_to_exit_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - generatePassCase(in_key_to_exit, in_key_to_exit_out, "`\"` read"); - - //-TEST_7----------------------------------------------------------// - // state: pointer == 2, stack == [1,3,0,0] - // read: `"` - // expect: parsing_string --> 1 - let in_tree_find_value = { ...INITIAL_IN }; - in_tree_find_value.pointer = 1; - in_tree_find_value.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - in_tree_find_value.byte = Delimiters.QUOTE; - let in_tree_find_value_out = { ...INITIAL_OUT }; - in_tree_find_value_out.next_pointer = 1; - in_tree_find_value_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - in_tree_find_value_out.next_parsing_string = 1; - generatePassCase(in_tree_find_value, - in_tree_find_value_out, - ">>>> `\"` read" - ); - - //-TEST_8----------------------------------------------------------// - // state: pointer == 2, stack == [1,3,0,0], parsing_string == 1 - // read: `"` - // expect: parsing_string == 0, - let in_value_to_exit = { ...INITIAL_IN }; - in_value_to_exit.pointer = 2; - in_value_to_exit.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - in_value_to_exit.parsing_string = 1; - in_value_to_exit.byte = Delimiters.QUOTE; - let in_value_to_exit_out = { ...INITIAL_OUT }; - in_value_to_exit_out.next_pointer = 2; - in_value_to_exit_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - generatePassCase(in_value_to_exit, - in_value_to_exit_out, - ">>>> `\"` is read" - ); + // //-TEST_1----------------------------------------------------------// + // // init: ZEROS then read `do_nothing` byte + // // expect: ZEROS + // generatePassCase(INITIAL_IN, INITIAL_OUT, ">>>> `NUL` read"); + + // // TODO: Consider moving to `stack.test.ts` + // //-TEST_2----------------------------------------------------------// + // // init: INIT + // // read: `{` + // // expect: pointer --> 1 + // // stack --> [1,0,0,0] + // let read_start_brace = { ...INITIAL_IN }; + // read_start_brace.byte = Delimiters.START_BRACE; + // let read_start_brace_out = { ...INITIAL_OUT }; + // read_start_brace_out.next_pointer = 1; + // read_start_brace_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // generatePassCase(read_start_brace, + // read_start_brace_out, + // ">>>> `{` read" + // ); + + // //-TEST_3----------------------------------------------------------// + // // state: INIT + // // read: `}` + // // expect: FAIL (stack underflow) + // let read_end_brace = { ...INITIAL_IN }; + // read_end_brace.byte = Delimiters.END_BRACE; + // generateFailCase(read_end_brace, + // ">>>> `}` read --> (stack underflow)" + // ); + + // //-TEST_4----------------------------------------------------------// + // // state: pointer == 1, stack == [1,0,0,0] + // // read: `"` + // // expect: parsing_string --> 1 + // let in_object_find_key = { ...INITIAL_IN }; + // in_object_find_key.pointer = 1; + // in_object_find_key.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // in_object_find_key.byte = Delimiters.QUOTE; + // let in_object_find_key_out = { ...INITIAL_OUT }; + // in_object_find_key_out.next_pointer = 1; + // in_object_find_key_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // in_object_find_key_out.next_parsing_string = 1; + // generatePassCase(in_object_find_key, + // in_object_find_key_out, + // ">>>> `\"` read" + // ); + + // //-TEST_5----------------------------------------------------------// + // // state: pointer == 1, stack = [1,0,0,0], parsing_string == 1 + // // read: ` ` + // // expect: NIL + // let in_key = { ...INITIAL_IN }; + // in_key.pointer = 1; + // in_key.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // in_key.parsing_string = 1; + // in_key.byte = WhiteSpace.SPACE; + // let in_key_out = { ...INITIAL_OUT }; + // in_key_out.next_pointer = 1; + // in_key_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // in_key_out.next_parsing_string = 1; + // generatePassCase(in_key, in_key_out, ">>>> ` ` read"); + + // //-TEST_6----------------------------------------------------------// + // // init: pointer == 1, stack == [1,0,0,0] + // // read: `"` + // // expect: parsing_string --> 0 + // // + // let in_key_to_exit = { ...INITIAL_IN }; + // in_key_to_exit.pointer = 1; + // in_key_to_exit.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // in_key_to_exit.parsing_string = 1 + // in_key_to_exit.byte = Delimiters.QUOTE; + // let in_key_to_exit_out = { ...INITIAL_OUT }; + // in_key_to_exit_out.next_pointer = 1; + // in_key_to_exit_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // generatePassCase(in_key_to_exit, in_key_to_exit_out, "`\"` read"); + + // //-TEST_7----------------------------------------------------------// + // // state: pointer == 2, stack == [1,3,0,0] + // // read: `"` + // // expect: parsing_string --> 1 + // let in_tree_find_value = { ...INITIAL_IN }; + // in_tree_find_value.pointer = 1; + // in_tree_find_value.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + // in_tree_find_value.byte = Delimiters.QUOTE; + // let in_tree_find_value_out = { ...INITIAL_OUT }; + // in_tree_find_value_out.next_pointer = 1; + // in_tree_find_value_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + // in_tree_find_value_out.next_parsing_string = 1; + // generatePassCase(in_tree_find_value, + // in_tree_find_value_out, + // ">>>> `\"` read" + // ); + + // //-TEST_8----------------------------------------------------------// + // // state: pointer == 2, stack == [1,3,0,0], parsing_string == 1 + // // read: `"` + // // expect: parsing_string == 0, + // let in_value_to_exit = { ...INITIAL_IN }; + // in_value_to_exit.pointer = 2; + // in_value_to_exit.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + // in_value_to_exit.parsing_string = 1; + // in_value_to_exit.byte = Delimiters.QUOTE; + // let in_value_to_exit_out = { ...INITIAL_OUT }; + // in_value_to_exit_out.next_pointer = 2; + // in_value_to_exit_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + // generatePassCase(in_value_to_exit, + // in_value_to_exit_out, + // ">>>> `\"` is read" + // ); }); diff --git a/circuits/test/parser/stack.test.ts b/circuits/test/parser/stack.test.ts index 0add5af..685bf34 100644 --- a/circuits/test/parser/stack.test.ts +++ b/circuits/test/parser/stack.test.ts @@ -2,7 +2,7 @@ import { circomkit, WitnessTester, generateDescription } from "../common"; import { Delimiters, WhiteSpace, Numbers, Escape, INITIAL_IN, INITIAL_OUT } from '.'; describe("GetTopOfStack", () => { - let circuit: WitnessTester<["stack", "pointer"], ["out"]>; + let circuit: WitnessTester<["stack"], ["value", "pointer"]>; before(async () => { circuit = await circomkit.WitnessTester(`GetTopOfStack`, { file: "circuits/parser", @@ -12,12 +12,29 @@ describe("GetTopOfStack", () => { console.log("#constraints:", await circuit.getConstraintCount()); }); - it("witness: pointer = 4, stack = [[1,0], [2,0], [3,1], [4,2]]", async () => { - await circuit.expectPass( - { pointer: 4, stack: [[1, 0], [2, 0], [3, 1], [4, 2]] }, - { out: [4, 2] }, - ); - }); + function generatePassCase(input: any, expected: any) { + const description = generateDescription(input); + + it(`(valid) witness: ${description}`, async () => { + await circuit.expectPass(input, expected); + }); + } + + let input = { stack: [[1, 0], [2, 0], [3, 1], [4, 2]] }; + let output = { value: [4, 2], pointer: 3 }; + generatePassCase(input, output); + + input.stack[2] = [0, 0]; + input.stack[3] = [0, 0]; + output.value = [2, 0] + output.pointer = 1; + generatePassCase(input, output); + + input.stack[0] = [0, 0]; + input.stack[1] = [0, 0]; + output.value = [0, 0] + output.pointer = 0; + generatePassCase(input, output); }); describe("StateUpdate :: RewriteStack", () => { @@ -51,136 +68,136 @@ describe("StateUpdate :: RewriteStack", () => { } - //-TEST_1----------------------------------------------------------// - // state: pointer == 1, stack == [1,0,0,0] - // read: `{` - // expect: pointer --> 2 - // stack --> [1,1,0,0] - let in_object = { ...INITIAL_IN }; - in_object.pointer = 1; - in_object.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - in_object.byte = Delimiters.START_BRACE; - let in_object_out = { ...INITIAL_OUT }; - in_object_out.next_pointer = 2; - in_object_out.next_stack = [[1, 0], [1, 0], [0, 0], [0, 0]]; - generatePassCase(in_object, in_object_out, ">>>> `{` read"); - - //-TEST_2----------------------------------------------------------// - // state: pointer == 1, stack == [1,0,0,0] - // read: `}` - // expect: pointer --> 0 - // stack --> [0,0,0,0] - let in_object_to_leave = { ...INITIAL_IN }; - in_object_to_leave.pointer = 1; - in_object_to_leave.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - in_object_to_leave.byte = Delimiters.END_BRACE; - let in_object_to_leave_out = { ...INITIAL_OUT }; - generatePassCase(in_object_to_leave, - in_object_to_leave_out, - ">>>> `}` read" - ); - - //-TEST_3----------------------------------------------------------// - // init: read `{`, then read `[` - // expect: pointer --> 2 - // stack --> [1,2,0,0] - let in_object_to_read_start_bracket = { ...INITIAL_IN }; - in_object_to_read_start_bracket.byte = Delimiters.START_BRACKET; - in_object_to_read_start_bracket.pointer = 1; - in_object_to_read_start_bracket.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - let in_object_to_read_start_bracket_out = { ...INITIAL_OUT }; - in_object_to_read_start_bracket_out.next_pointer = 2; - in_object_to_read_start_bracket_out.next_stack = [[1, 0], [2, 0], [0, 0], [0, 0]]; - generatePassCase(in_object_to_read_start_bracket, - in_object_to_read_start_bracket_out, - ">>>> `[` read" - ); - - //-TEST_4----------------------------------------------------------// - // init: read 4x `{`, then read `{` - // expect: pointer --> 4 - // stack --> [1,1,1,1] - let in_max_stack = { ...INITIAL_IN }; - in_max_stack.byte = Delimiters.START_BRACE; - in_max_stack.pointer = 4; - in_max_stack.stack = [[1, 0], [1, 0], [1, 0], [1, 0]]; - generateFailCase(in_max_stack, ">>>> `{` read --> (stack overflow)"); - - //-TEST_5----------------------------------------------------------// - // init: read 4x `{`, then read `[` - // expect: pointer --> 4 - // stack --> [1,1,1,1] - let in_max_stack_2 = { ...INITIAL_IN }; - in_max_stack_2.byte = Delimiters.START_BRACKET; - in_max_stack_2.pointer = 4; - in_max_stack_2.stack = [[1, 0], [1, 0], [1, 0], [1, 0]]; - generateFailCase(in_max_stack, ">>>> `[` read --> (stack overflow)"); - - //-TEST_6----------------------------------------------------------// - // init: read `{` and `[`, then read `]` - // expect: pointer --> 2 - // stack --> [1,0,0,0] - let in_object_and_array = { ...INITIAL_IN }; - in_object_and_array.byte = Delimiters.END_BRACKET; - in_object_and_array.pointer = 2; - in_object_and_array.stack = [[1, 0], [2, 0], [0, 0], [0, 0]]; - let in_object_and_array_out = { ...INITIAL_OUT }; - in_object_and_array_out.next_pointer = 1; - in_object_and_array_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - generatePassCase(in_object_and_array, - in_object_and_array_out, - ">>>> `]` read" - ); - - //-TEST_7----------------------------------------------------------// - // init: read `{` and `:`, then read `,` - // expect: pointer --> 2 - // stack --> [1,3,0,0] - let in_object_and_value = { ...INITIAL_IN }; - in_object_and_value.byte = Delimiters.COMMA; - in_object_and_value.pointer = 2; - in_object_and_value.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - let in_object_and_value_out = { ...INITIAL_OUT }; - in_object_and_value_out.next_pointer = 1; - in_object_and_value_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - generatePassCase(in_object_and_value, - in_object_and_value_out, - ">>>> `,` read" - ); - - //-TEST_8----------------------------------------------------------// - // init: pointer == 2, stack == [1,3,0,0] - // read: `}` - // expect: pointer --> 2 - // stack --> [1,3,0,0] - let in_object_and_value_to_leave_object = { ...INITIAL_IN }; - in_object_and_value_to_leave_object.byte = Delimiters.END_BRACE; - in_object_and_value_to_leave_object.pointer = 2; - in_object_and_value_to_leave_object.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - let in_object_and_value_to_leave_object_out = { ...INITIAL_OUT }; - in_object_and_value_to_leave_object_out.next_pointer = 0; - in_object_and_value_to_leave_object_out.next_stack = [[0, 0], [0, 0], [0, 0], [0, 0]]; - generatePassCase(in_object_and_value_to_leave_object, - in_object_and_value_to_leave_object_out, - ">>>> `}` read" - ); - - //-TEST_9-----------------------------------------------------------// - // state: pointer == 1, stack == [1,0,0,0] - // read: `:` - // expect: pointer --> 2 - // stack --> [1,3,0,0] - let parsed_key_wait_to_parse_value = { ...INITIAL_IN }; - parsed_key_wait_to_parse_value.pointer = 1; - parsed_key_wait_to_parse_value.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - parsed_key_wait_to_parse_value.byte = Delimiters.COLON; - let parsed_key_wait_to_parse_value_out = { ...INITIAL_OUT }; - parsed_key_wait_to_parse_value_out.next_pointer = 2; - parsed_key_wait_to_parse_value_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - generatePassCase(parsed_key_wait_to_parse_value, - parsed_key_wait_to_parse_value_out, - ">>>> `:` read" - ); + // //-TEST_1----------------------------------------------------------// + // // state: pointer == 1, stack == [1,0,0,0] + // // read: `{` + // // expect: pointer --> 2 + // // stack --> [1,1,0,0] + // let in_object = { ...INITIAL_IN }; + // in_object.pointer = 1; + // in_object.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // in_object.byte = Delimiters.START_BRACE; + // let in_object_out = { ...INITIAL_OUT }; + // in_object_out.next_pointer = 2; + // in_object_out.next_stack = [[1, 0], [1, 0], [0, 0], [0, 0]]; + // generatePassCase(in_object, in_object_out, ">>>> `{` read"); + + // //-TEST_2----------------------------------------------------------// + // // state: pointer == 1, stack == [1,0,0,0] + // // read: `}` + // // expect: pointer --> 0 + // // stack --> [0,0,0,0] + // let in_object_to_leave = { ...INITIAL_IN }; + // in_object_to_leave.pointer = 1; + // in_object_to_leave.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // in_object_to_leave.byte = Delimiters.END_BRACE; + // let in_object_to_leave_out = { ...INITIAL_OUT }; + // generatePassCase(in_object_to_leave, + // in_object_to_leave_out, + // ">>>> `}` read" + // ); + + // //-TEST_3----------------------------------------------------------// + // // init: read `{`, then read `[` + // // expect: pointer --> 2 + // // stack --> [1,2,0,0] + // let in_object_to_read_start_bracket = { ...INITIAL_IN }; + // in_object_to_read_start_bracket.byte = Delimiters.START_BRACKET; + // in_object_to_read_start_bracket.pointer = 1; + // in_object_to_read_start_bracket.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // let in_object_to_read_start_bracket_out = { ...INITIAL_OUT }; + // in_object_to_read_start_bracket_out.next_pointer = 2; + // in_object_to_read_start_bracket_out.next_stack = [[1, 0], [2, 0], [0, 0], [0, 0]]; + // generatePassCase(in_object_to_read_start_bracket, + // in_object_to_read_start_bracket_out, + // ">>>> `[` read" + // ); + + // //-TEST_4----------------------------------------------------------// + // // init: read 4x `{`, then read `{` + // // expect: pointer --> 4 + // // stack --> [1,1,1,1] + // let in_max_stack = { ...INITIAL_IN }; + // in_max_stack.byte = Delimiters.START_BRACE; + // in_max_stack.pointer = 4; + // in_max_stack.stack = [[1, 0], [1, 0], [1, 0], [1, 0]]; + // generateFailCase(in_max_stack, ">>>> `{` read --> (stack overflow)"); + + // //-TEST_5----------------------------------------------------------// + // // init: read 4x `{`, then read `[` + // // expect: pointer --> 4 + // // stack --> [1,1,1,1] + // let in_max_stack_2 = { ...INITIAL_IN }; + // in_max_stack_2.byte = Delimiters.START_BRACKET; + // in_max_stack_2.pointer = 4; + // in_max_stack_2.stack = [[1, 0], [1, 0], [1, 0], [1, 0]]; + // generateFailCase(in_max_stack, ">>>> `[` read --> (stack overflow)"); + + // //-TEST_6----------------------------------------------------------// + // // init: read `{` and `[`, then read `]` + // // expect: pointer --> 2 + // // stack --> [1,0,0,0] + // let in_object_and_array = { ...INITIAL_IN }; + // in_object_and_array.byte = Delimiters.END_BRACKET; + // in_object_and_array.pointer = 2; + // in_object_and_array.stack = [[1, 0], [2, 0], [0, 0], [0, 0]]; + // let in_object_and_array_out = { ...INITIAL_OUT }; + // in_object_and_array_out.next_pointer = 1; + // in_object_and_array_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // generatePassCase(in_object_and_array, + // in_object_and_array_out, + // ">>>> `]` read" + // ); + + // //-TEST_7----------------------------------------------------------// + // // init: read `{` and `:`, then read `,` + // // expect: pointer --> 2 + // // stack --> [1,3,0,0] + // let in_object_and_value = { ...INITIAL_IN }; + // in_object_and_value.byte = Delimiters.COMMA; + // in_object_and_value.pointer = 2; + // in_object_and_value.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + // let in_object_and_value_out = { ...INITIAL_OUT }; + // in_object_and_value_out.next_pointer = 1; + // in_object_and_value_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // generatePassCase(in_object_and_value, + // in_object_and_value_out, + // ">>>> `,` read" + // ); + + // //-TEST_8----------------------------------------------------------// + // // init: pointer == 2, stack == [1,3,0,0] + // // read: `}` + // // expect: pointer --> 2 + // // stack --> [1,3,0,0] + // let in_object_and_value_to_leave_object = { ...INITIAL_IN }; + // in_object_and_value_to_leave_object.byte = Delimiters.END_BRACE; + // in_object_and_value_to_leave_object.pointer = 2; + // in_object_and_value_to_leave_object.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + // let in_object_and_value_to_leave_object_out = { ...INITIAL_OUT }; + // in_object_and_value_to_leave_object_out.next_pointer = 0; + // in_object_and_value_to_leave_object_out.next_stack = [[0, 0], [0, 0], [0, 0], [0, 0]]; + // generatePassCase(in_object_and_value_to_leave_object, + // in_object_and_value_to_leave_object_out, + // ">>>> `}` read" + // ); + + // //-TEST_9-----------------------------------------------------------// + // // state: pointer == 1, stack == [1,0,0,0] + // // read: `:` + // // expect: pointer --> 2 + // // stack --> [1,3,0,0] + // let parsed_key_wait_to_parse_value = { ...INITIAL_IN }; + // parsed_key_wait_to_parse_value.pointer = 1; + // parsed_key_wait_to_parse_value.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // parsed_key_wait_to_parse_value.byte = Delimiters.COLON; + // let parsed_key_wait_to_parse_value_out = { ...INITIAL_OUT }; + // parsed_key_wait_to_parse_value_out.next_pointer = 2; + // parsed_key_wait_to_parse_value_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + // generatePassCase(parsed_key_wait_to_parse_value, + // parsed_key_wait_to_parse_value_out, + // ">>>> `:` read" + // ); }); \ No newline at end of file diff --git a/circuits/test/parser/values.test.ts b/circuits/test/parser/values.test.ts index 95cb8e0..dd437c8 100644 --- a/circuits/test/parser/values.test.ts +++ b/circuits/test/parser/values.test.ts @@ -22,83 +22,83 @@ describe("StateUpdate :: Values", () => { }); } - //-TEST_1----------------------------------------------------------// - // idea: Read a number value after a key in an object. - // state: pointer == 2, stack == [1,3,0,0] - // read: `0` - // expect: pointer --> 2 - // stack --> [1,3,0,0] - // parsing_number --> 1 - let read_number = { ...INITIAL_IN }; - read_number.pointer = 2; - read_number.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - read_number.byte = Numbers.ZERO; - let read_number_out = { ...INITIAL_OUT }; - read_number_out.next_pointer = 2; - read_number_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - read_number_out.next_parsing_number = 1; - generatePassCase(read_number, read_number_out, ">>>> `0` read"); + // //-TEST_1----------------------------------------------------------// + // // idea: Read a number value after a key in an object. + // // state: pointer == 2, stack == [1,3,0,0] + // // read: `0` + // // expect: pointer --> 2 + // // stack --> [1,3,0,0] + // // parsing_number --> 1 + // let read_number = { ...INITIAL_IN }; + // read_number.pointer = 2; + // read_number.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + // read_number.byte = Numbers.ZERO; + // let read_number_out = { ...INITIAL_OUT }; + // read_number_out.next_pointer = 2; + // read_number_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + // read_number_out.next_parsing_number = 1; + // generatePassCase(read_number, read_number_out, ">>>> `0` read"); - //-TEST_2----------------------------------------------------------// - // idea: Inside a number value after a key in an object. - // state: pointer == 2, stack == [1,3,0,0], parsing_number == 1 - // read: `,` - // expect: pointer --> 2 - // stack --> [1,3,0,0] - // parsing_number --> 0 - let inside_number = { ...INITIAL_IN }; - inside_number.pointer = 2; - inside_number.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - inside_number.parsing_number = 1; - inside_number.byte = Delimiters.COMMA; - let inside_number_out = { ...INITIAL_OUT }; - inside_number_out.next_pointer = 1; - inside_number_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - generatePassCase(inside_number, inside_number_out, ">>>> `,` read"); + // //-TEST_2----------------------------------------------------------// + // // idea: Inside a number value after a key in an object. + // // state: pointer == 2, stack == [1,3,0,0], parsing_number == 1 + // // read: `,` + // // expect: pointer --> 2 + // // stack --> [1,3,0,0] + // // parsing_number --> 0 + // let inside_number = { ...INITIAL_IN }; + // inside_number.pointer = 2; + // inside_number.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + // inside_number.parsing_number = 1; + // inside_number.byte = Delimiters.COMMA; + // let inside_number_out = { ...INITIAL_OUT }; + // inside_number_out.next_pointer = 1; + // inside_number_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // generatePassCase(inside_number, inside_number_out, ">>>> `,` read"); - // TODO: Note that reading a space while reading a number will not throw an error! + // // TODO: Note that reading a space while reading a number will not throw an error! - //-TEST_2----------------------------------------------------------// - // idea: Inside a number value after a key in an object. - // state: pointer == 2, stack == [1,3,0,0], parsing_number == 1 - // read: `1` - // expect: pointer --> 2 - // stack --> [1,3,0,0] - // parsing_number --> 0 - let inside_number_continue = { ...INITIAL_IN }; - inside_number_continue.pointer = 2; - inside_number_continue.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - inside_number_continue.parsing_number = 1; - inside_number_continue.byte = Numbers.ONE; - let inside_number_continue_out = { ...INITIAL_OUT }; - inside_number_continue_out.next_pointer = 2; - inside_number_continue_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - inside_number_continue_out.next_parsing_number = 1; - generatePassCase(inside_number_continue, inside_number_continue_out, ">>>> `1` read"); + // //-TEST_2----------------------------------------------------------// + // // idea: Inside a number value after a key in an object. + // // state: pointer == 2, stack == [1,3,0,0], parsing_number == 1 + // // read: `1` + // // expect: pointer --> 2 + // // stack --> [1,3,0,0] + // // parsing_number --> 0 + // let inside_number_continue = { ...INITIAL_IN }; + // inside_number_continue.pointer = 2; + // inside_number_continue.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + // inside_number_continue.parsing_number = 1; + // inside_number_continue.byte = Numbers.ONE; + // let inside_number_continue_out = { ...INITIAL_OUT }; + // inside_number_continue_out.next_pointer = 2; + // inside_number_continue_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + // inside_number_continue_out.next_parsing_number = 1; + // generatePassCase(inside_number_continue, inside_number_continue_out, ">>>> `1` read"); - describe("StateUpdate :: Values :: Array", () => { - // Internal array parsing -----------------------------------------// + // describe("StateUpdate :: Values :: Array", () => { + // // Internal array parsing -----------------------------------------// - //-TEST_10----------------------------------------------------------// - // init: pointer = 1, stack = [1,2,0,0] -> `,` is read - let in_arr = { ...INITIAL_IN }; - in_arr.pointer = 2; - in_arr.stack = [[1, 0], [2, 0], [0, 0], [0, 0]]; - in_arr.byte = Delimiters.COMMA; - let in_arr_out = { ...INITIAL_OUT }; - in_arr_out.next_pointer = 2; - in_arr_out.next_stack = [[1, 0], [2, 1], [0, 0], [0, 0]]; - generatePassCase(in_arr, in_arr_out, ">>>> `,` read"); + // //-TEST_10----------------------------------------------------------// + // // init: pointer = 1, stack = [1,2,0,0] -> `,` is read + // let in_arr = { ...INITIAL_IN }; + // in_arr.pointer = 2; + // in_arr.stack = [[1, 0], [2, 0], [0, 0], [0, 0]]; + // in_arr.byte = Delimiters.COMMA; + // let in_arr_out = { ...INITIAL_OUT }; + // in_arr_out.next_pointer = 2; + // in_arr_out.next_stack = [[1, 0], [2, 1], [0, 0], [0, 0]]; + // generatePassCase(in_arr, in_arr_out, ">>>> `,` read"); - //-TEST_10----------------------------------------------------------// - // init: pointer = 1, stack = [1,2,0,0] -> `,` is read - let in_arr_idx_to_leave = { ...INITIAL_IN }; - in_arr_idx_to_leave.pointer = 2; - in_arr_idx_to_leave.stack = [[1, 0], [2, 1], [0, 0], [0, 0]]; - in_arr_idx_to_leave.byte = Delimiters.END_BRACKET; - let in_arr_idx_to_leave_out = { ...INITIAL_OUT }; - in_arr_idx_to_leave_out.next_pointer = 1; - in_arr_idx_to_leave_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - generatePassCase(in_arr_idx_to_leave, in_arr_idx_to_leave_out, ">>>> `]` read"); - }); + // //-TEST_10----------------------------------------------------------// + // // init: pointer = 1, stack = [1,2,0,0] -> `,` is read + // let in_arr_idx_to_leave = { ...INITIAL_IN }; + // in_arr_idx_to_leave.pointer = 2; + // in_arr_idx_to_leave.stack = [[1, 0], [2, 1], [0, 0], [0, 0]]; + // in_arr_idx_to_leave.byte = Delimiters.END_BRACKET; + // let in_arr_idx_to_leave_out = { ...INITIAL_OUT }; + // in_arr_idx_to_leave_out.next_pointer = 1; + // in_arr_idx_to_leave_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // generatePassCase(in_arr_idx_to_leave, in_arr_idx_to_leave_out, ">>>> `]` read"); + // }); }); \ No newline at end of file From 9b94cd9ccca9db52f3f78294888989844eebbffb Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 15 Aug 2024 18:04:57 -0600 Subject: [PATCH 86/99] WIP: cleaning further Many stack tests passing. I want to now not push to stack when we read a colon and instead enable use of the second stack position. This will break more things temporarily. --- circuits/language.circom | 20 +-- circuits/parser.circom | 103 ++++++++------- circuits/test/parser/index.ts | 2 - circuits/test/parser/stack.test.ts | 196 ++++++++++++++--------------- 4 files changed, 157 insertions(+), 164 deletions(-) diff --git a/circuits/language.circom b/circuits/language.circom index f9ba54f..3528c80 100644 --- a/circuits/language.circom +++ b/circuits/language.circom @@ -29,14 +29,14 @@ template Syntax() { } template Command() { - // STATE = [pushpop, stack_val, parsing_string, parsing_number] - signal output NOTHING[4] <== [0, 0, 0, -1 ]; // Command returned by switch if we want to do nothing, e.g. read a whitespace char while looking for a key - signal output START_BRACE[4] <== [1, 1, 0, 0 ]; // Command returned by switch if we hit a start brace `{` - signal output END_BRACE[4] <== [-1, -1, 0, -1 ]; // Command returned by switch if we hit a end brace `}` - signal output START_BRACKET[4] <== [1, 2, 0, 0 ]; // TODO: Might want `in_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) - signal output END_BRACKET[4] <== [-1, -2, 0, -1 ]; // Command returned by switch if we hit a start bracket `]` - signal output QUOTE[4] <== [0, 0, 1, 0 ]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` - signal output COLON[4] <== [1, 3, 0, 0 ]; // Command returned by switch if we hit a colon `:` - signal output COMMA[4] <== [-1, 4, 0, -1 ]; // Command returned by switch if we hit a comma `,` - signal output NUMBER[4] <== [0, 256, 0, 1 ]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) + // STATE = [read_write_value, parsing_string, parsing_number] + signal output NOTHING[3] <== [0, 0, -1 ]; // Command returned by switch if we want to do nothing, e.g. read a whitespace char while looking for a key + signal output START_BRACE[3] <== [1, 0, 0 ]; // Command returned by switch if we hit a start brace `{` + signal output END_BRACE[3] <== [-1, 0, -1 ]; // Command returned by switch if we hit a end brace `}` + signal output START_BRACKET[3] <== [2, 0, 0 ]; // TODO: Might want `in_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) + signal output END_BRACKET[3] <== [-2, 0, -1 ]; // Command returned by switch if we hit a start bracket `]` + signal output QUOTE[3] <== [0, 1, 0 ]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` + signal output COLON[3] <== [3, 0, 0 ]; // Command returned by switch if we hit a colon `:` + signal output COMMA[3] <== [4, 0, -1 ]; // Command returned by switch if we hit a comma `,` + signal output NUMBER[3] <== [256, 0, 1 ]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) } \ No newline at end of file diff --git a/circuits/parser.circom b/circuits/parser.circom index a631d52..c2ef9b6 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -24,14 +24,13 @@ template StateUpdate(MAX_STACK_HEIGHT) { component Syntax = Syntax(); component Command = Command(); - var pushpop = 0; var read_write_value = 0; - var parsing_state[4] = [pushpop, read_write_value, parsing_string, parsing_number]; + var parsing_state[3] = [read_write_value, parsing_string, parsing_number]; //--------------------------------------------------------------------------------------------// //-State machine updating---------------------------------------------------------------------// // * yield instruction based on what byte we read * - component matcher = SwitchArray(8, 4); + component matcher = SwitchArray(8, 3); matcher.branches <== [Syntax.START_BRACE, Syntax.END_BRACE, Syntax.QUOTE, Syntax.COLON, Syntax.COMMA, Syntax.START_BRACKET, Syntax.END_BRACKET, Syntax.NUMBER ]; matcher.vals <== [Command.START_BRACE, Command.END_BRACE, Command.QUOTE, Command.COLON, Command.COMMA, Command.START_BRACKET, Command.END_BRACKET, Command.NUMBER]; component numeral_range_check = InRange(8); @@ -44,26 +43,25 @@ template StateUpdate(MAX_STACK_HEIGHT) { // * get the instruction mask based on current state * component mask = StateToMask(MAX_STACK_HEIGHT); // mask.in <== parsing_state; - mask.in <== [matcher.out[0],matcher.out[1],parsing_string,parsing_number]; // TODO: This is awkward. Things need to be rewritten + mask.in <== [matcher.out[0],parsing_string,parsing_number]; // TODO: This is awkward. Things need to be rewritten // * multiply the mask array elementwise with the instruction array * - component mulMaskAndOut = ArrayMul(4); + component mulMaskAndOut = ArrayMul(3); mulMaskAndOut.lhs <== mask.out; mulMaskAndOut.rhs <== matcher.out; // * add the masked instruction to the state to get new state * - component addToState = ArrayAdd(4); + component addToState = ArrayAdd(3); addToState.lhs <== parsing_state; addToState.rhs <== mulMaskAndOut.out; // * set the new state * component newStack = RewriteStack(MAX_STACK_HEIGHT); - newStack.stack <== stack; - newStack.pushpop <== addToState.out[0]; - newStack.read_write_value <== addToState.out[1]; - next_stack <== newStack.next_stack; - next_parsing_string <== addToState.out[2]; - next_parsing_number <== addToState.out[3]; + newStack.stack <== stack; + newStack.read_write_value <== addToState.out[0]; + next_stack <== newStack.next_stack; + next_parsing_string <== addToState.out[1]; + next_parsing_number <== addToState.out[2]; // for(var i = 0; i < 4; i++) { // log("matcher.out[",i,"]: ", matcher.out[i]); @@ -88,23 +86,18 @@ template StateUpdate(MAX_STACK_HEIGHT) { template StateToMask(n) { // TODO: Probably need to assert things are bits where necessary. - signal input in[4]; - signal output out[4]; + signal input in[3]; + signal output out[3]; - signal pushpop <== in[0]; - signal read_write_value <== in[1]; - signal parsing_string <== in[2]; - signal parsing_number <== in[3]; - - // TODO: Pushpop is probably unecessary actually - // `pushpop` can change: IF NOT `parsing_string` - out[0] <== (1 - parsing_string); + signal read_write_value <== in[0]; + signal parsing_string <== in[1]; + signal parsing_number <== in[2]; // `read_write_value`can change: IF NOT `parsing_string` - out[1] <== (1 - parsing_string); + out[0] <== (1 - parsing_string); // `parsing_string` can change: - out[2] <== 1 - 2 * parsing_string; + out[1] <== 1 - 2 * parsing_string; // `parsing_number` can change: component isDelimeter = InRange(8); @@ -134,7 +127,7 @@ template StateToMask(n) { // log("stateToNum: ", stateToNum.out); // log("toParseNumber: ", toParseNumber.out); - out[3] <== toParseNumber.out; + out[2] <== toParseNumber.out; } // TODO: Check if underconstrained @@ -164,9 +157,7 @@ template GetTopOfStack(n) { template RewriteStack(n) { assert(n < 2**8); signal input stack[n][2]; - signal input pushpop; signal input read_write_value; - signal output next_pointer; signal output next_stack[n][2]; /* @@ -211,16 +202,34 @@ template RewriteStack(n) { //-----------------------------------------------------------------------------// // * check what value was read * - // * read in a comma - component readComma = IsEqual(); - readComma.in[0] <== 4; - readComma.in[1] <== read_write_value; + // * read in a start brace `{` * + component readStartBrace = IsEqual(); + readStartBrace.in <== [read_write_value, 1]; + // * read in a start bracket `[` * + component readStartBracket = IsEqual(); + readStartBracket.in <== [read_write_value, 2]; + // * read in an end brace `}` * + component readEndBrace = IsEqual(); + readEndBrace.in <== [read_write_value, -1]; + // * read in an end bracket `]` * + component readEndBracket = IsEqual(); + readEndBracket.in <== [read_write_value, -2]; // * read in either an end brace `}` or an end bracket `]` * component readEndChar = IsZero(); readEndChar.in <== (read_write_value + 1) * (read_write_value + 2); - // * read in an end bracket `]` * - component readEndArr = IsZero(); - readEndArr.in <== read_write_value + 2; + // * read in a comma `,` * + component readComma = IsEqual(); + readComma.in[0] <== 4; + readComma.in[1] <== read_write_value; + //-----------------------------------------------------------------------------// + + //-----------------------------------------------------------------------------// + // * determine whether we are pushing or popping from the stack * + component isPop = IsZero(); + isPop.in <== readStartBrace.out + readStartBracket.out; + component isPush = IsZero(); + isPush.in <== readEndBrace.out + readEndBracket.out; + // TODO: Can remove all the pushpop stuff by checking what character we got. E.g., if it is "negative" or comma, we pop, if it is positive we push, basically signal READ_COMMA_AND_IN_ARRAY <== (1 - readComma.out) + (1 - inArray.out); // POORLY NAMED. THIS IS MORE LIKE XNOR or something. @@ -230,25 +239,16 @@ template RewriteStack(n) { signal read_comma_in_array <== readComma.out * inArray.out; - - - component isPop = IsZero(); - isPop.in <== (1 - isReadCommaAndInArray.out) * pushpop + 1; // TODO: can simplify? - - component isPush = IsZero(); - isPush.in <== pushpop - 1; component prev_indicator[n]; component indicator[n]; signal isPopAt[n]; signal isPushAt[n]; - - signal NOT_READ_COMMA <== (1 - readComma.out) * read_write_value; signal READ_COMMA <== readComma.out * ((1-inArray.out) * (-3) + inArray.out * (-2)); signal corrected_read_write_value <== READ_COMMA + NOT_READ_COMMA; - signal isPopArr <== isPop.out * readEndArr.out; + signal isPopArr <== isPop.out * readEndBracket.out; for(var i = 0; i < n; i++) { // points to 1 value back from top @@ -295,14 +295,9 @@ template RewriteStack(n) { // TODO: Constrain next_stack entries to be 0,1,2,3 } - // TODO: Reimplement these! - // component isOverflow = GreaterThan(8); - // isOverflow.in[0] <== next_pointer; - // isOverflow.in[1] <== n; - // isOverflow.out === 0; - - // component isUnderflow = LessThan(8); - // isUnderflow.in[0] <== next_pointer; - // isUnderflow.in[1] <== 0; - // isUnderflow.out === 0; + // TODO: WE CAN'T LEAVE 8 HERE, THIS HAS TO DEPEND ON THE STACK HEIGHT AS IT IS THE NUM BITS NEEDED TO REPR STACK HEIGHT IN BINARY + component isUnderflowOrOverflow = InRange(8); + isUnderflowOrOverflow.in <== pointer - isPop.out + isPush.out; + isUnderflowOrOverflow.range <== [0,n]; + isUnderflowOrOverflow.out === 1; } \ No newline at end of file diff --git a/circuits/test/parser/index.ts b/circuits/test/parser/index.ts index a658951..3bde941 100644 --- a/circuits/test/parser/index.ts +++ b/circuits/test/parser/index.ts @@ -44,14 +44,12 @@ export const Escape = { export const INITIAL_IN = { byte: 0, - pointer: 0, stack: [[0, 0], [0, 0], [0, 0], [0, 0]], parsing_string: 0, parsing_number: 0, }; export const INITIAL_OUT = { - next_pointer: INITIAL_IN.pointer, next_stack: INITIAL_IN.stack, next_parsing_string: INITIAL_IN.parsing_string, next_parsing_number: INITIAL_IN.parsing_number, diff --git a/circuits/test/parser/stack.test.ts b/circuits/test/parser/stack.test.ts index 685bf34..b073db6 100644 --- a/circuits/test/parser/stack.test.ts +++ b/circuits/test/parser/stack.test.ts @@ -39,8 +39,8 @@ describe("GetTopOfStack", () => { describe("StateUpdate :: RewriteStack", () => { let circuit: WitnessTester< - ["byte", "pointer", "stack", "parsing_string", "parsing_number"], - ["next_pointer", "next_stack", "next_parsing_string", "next_parsing_number"] + ["byte", "stack", "parsing_string", "parsing_number"], + ["next_stack", "next_parsing_string", "next_parsing_number"] >; before(async () => { circuit = await circomkit.WitnessTester(`GetTopOfStack`, { @@ -68,102 +68,70 @@ describe("StateUpdate :: RewriteStack", () => { } - // //-TEST_1----------------------------------------------------------// - // // state: pointer == 1, stack == [1,0,0,0] - // // read: `{` - // // expect: pointer --> 2 - // // stack --> [1,1,0,0] - // let in_object = { ...INITIAL_IN }; - // in_object.pointer = 1; - // in_object.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - // in_object.byte = Delimiters.START_BRACE; - // let in_object_out = { ...INITIAL_OUT }; - // in_object_out.next_pointer = 2; - // in_object_out.next_stack = [[1, 0], [1, 0], [0, 0], [0, 0]]; - // generatePassCase(in_object, in_object_out, ">>>> `{` read"); - - // //-TEST_2----------------------------------------------------------// - // // state: pointer == 1, stack == [1,0,0,0] - // // read: `}` - // // expect: pointer --> 0 - // // stack --> [0,0,0,0] - // let in_object_to_leave = { ...INITIAL_IN }; - // in_object_to_leave.pointer = 1; - // in_object_to_leave.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - // in_object_to_leave.byte = Delimiters.END_BRACE; - // let in_object_to_leave_out = { ...INITIAL_OUT }; - // generatePassCase(in_object_to_leave, - // in_object_to_leave_out, - // ">>>> `}` read" - // ); - - // //-TEST_3----------------------------------------------------------// - // // init: read `{`, then read `[` - // // expect: pointer --> 2 - // // stack --> [1,2,0,0] - // let in_object_to_read_start_bracket = { ...INITIAL_IN }; - // in_object_to_read_start_bracket.byte = Delimiters.START_BRACKET; - // in_object_to_read_start_bracket.pointer = 1; - // in_object_to_read_start_bracket.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - // let in_object_to_read_start_bracket_out = { ...INITIAL_OUT }; - // in_object_to_read_start_bracket_out.next_pointer = 2; - // in_object_to_read_start_bracket_out.next_stack = [[1, 0], [2, 0], [0, 0], [0, 0]]; - // generatePassCase(in_object_to_read_start_bracket, - // in_object_to_read_start_bracket_out, - // ">>>> `[` read" - // ); - - // //-TEST_4----------------------------------------------------------// - // // init: read 4x `{`, then read `{` - // // expect: pointer --> 4 - // // stack --> [1,1,1,1] - // let in_max_stack = { ...INITIAL_IN }; - // in_max_stack.byte = Delimiters.START_BRACE; - // in_max_stack.pointer = 4; - // in_max_stack.stack = [[1, 0], [1, 0], [1, 0], [1, 0]]; - // generateFailCase(in_max_stack, ">>>> `{` read --> (stack overflow)"); - - // //-TEST_5----------------------------------------------------------// - // // init: read 4x `{`, then read `[` - // // expect: pointer --> 4 - // // stack --> [1,1,1,1] - // let in_max_stack_2 = { ...INITIAL_IN }; - // in_max_stack_2.byte = Delimiters.START_BRACKET; - // in_max_stack_2.pointer = 4; - // in_max_stack_2.stack = [[1, 0], [1, 0], [1, 0], [1, 0]]; - // generateFailCase(in_max_stack, ">>>> `[` read --> (stack overflow)"); - - // //-TEST_6----------------------------------------------------------// - // // init: read `{` and `[`, then read `]` - // // expect: pointer --> 2 - // // stack --> [1,0,0,0] - // let in_object_and_array = { ...INITIAL_IN }; - // in_object_and_array.byte = Delimiters.END_BRACKET; - // in_object_and_array.pointer = 2; - // in_object_and_array.stack = [[1, 0], [2, 0], [0, 0], [0, 0]]; - // let in_object_and_array_out = { ...INITIAL_OUT }; - // in_object_and_array_out.next_pointer = 1; - // in_object_and_array_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - // generatePassCase(in_object_and_array, - // in_object_and_array_out, - // ">>>> `]` read" - // ); - - // //-TEST_7----------------------------------------------------------// - // // init: read `{` and `:`, then read `,` - // // expect: pointer --> 2 - // // stack --> [1,3,0,0] - // let in_object_and_value = { ...INITIAL_IN }; - // in_object_and_value.byte = Delimiters.COMMA; - // in_object_and_value.pointer = 2; - // in_object_and_value.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - // let in_object_and_value_out = { ...INITIAL_OUT }; - // in_object_and_value_out.next_pointer = 1; - // in_object_and_value_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - // generatePassCase(in_object_and_value, - // in_object_and_value_out, - // ">>>> `,` read" - // ); + //-TEST_1----------------------------------------------------------// + // state: stack == [[1, 0], [0, 0], [0, 0], [0, 0]] + // read: `{` + // expect: stack --> [[1, 0], [1, 0], [0, 0], [0, 0]] + let in_object = { ...INITIAL_IN }; + in_object.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + in_object.byte = Delimiters.START_BRACE; + let in_object_out = { ...INITIAL_OUT }; + in_object_out.next_stack = [[1, 0], [1, 0], [0, 0], [0, 0]]; + generatePassCase(in_object, in_object_out, ">>>> `{` read"); + + //-TEST_2----------------------------------------------------------// + // state: stack == [[1, 0], [0, 0], [0, 0], [0, 0]] + // read: `}` + // expect: stack --> [[0, 0], [0, 0], [0, 0], [0, 0]] + let in_object_to_leave = { ...INITIAL_IN }; + in_object_to_leave.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + in_object_to_leave.byte = Delimiters.END_BRACE; + let in_object_to_leave_out = { ...INITIAL_OUT }; + generatePassCase(in_object_to_leave, + in_object_to_leave_out, + ">>>> `}` read" + ); + + //-TEST_3----------------------------------------------------------// + // init: stack == [[1, 0], [0, 0], [0, 0], [0, 0]] + // read: `[` + // expect: stack --> [[1, 0], [2, 0], [0, 0], [0, 0]] + let in_object_to_read_start_bracket = { ...INITIAL_IN }; + in_object_to_read_start_bracket.byte = Delimiters.START_BRACKET; + in_object_to_read_start_bracket.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + let in_object_to_read_start_bracket_out = { ...INITIAL_OUT }; + in_object_to_read_start_bracket_out.next_stack = [[1, 0], [2, 0], [0, 0], [0, 0]]; + generatePassCase(in_object_to_read_start_bracket, + in_object_to_read_start_bracket_out, + ">>>> `[` read" + ); + + //-TEST_4----------------------------------------------------------// + // init: stack == [[1, 0], [2, 0], [0, 0], [0, 0]] + // read: `]` + // expect: stack --> [[1, 0], [0, 0], [0, 0], [0, 0]] + let in_object_and_array = { ...INITIAL_IN }; + in_object_and_array.byte = Delimiters.END_BRACKET; + in_object_and_array.stack = [[1, 0], [2, 0], [0, 0], [0, 0]]; + let in_object_and_array_out = { ...INITIAL_OUT }; + in_object_and_array_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + generatePassCase(in_object_and_array, + in_object_and_array_out, + ">>>> `]` read" + ); + + //-TEST_7----------------------------------------------------------// + // init: stack == [[1, 0], [3, 0], [0, 0], [0, 0]] + // expect: stack --> [[1, 0], [0, 0], [0, 0], [0, 0]] + let in_object_and_value = { ...INITIAL_IN }; + in_object_and_value.byte = Delimiters.COMMA; + in_object_and_value.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + let in_object_and_value_out = { ...INITIAL_OUT }; + in_object_and_value_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + generatePassCase(in_object_and_value, + in_object_and_value_out, + ">>>> `,` read" + ); // //-TEST_8----------------------------------------------------------// // // init: pointer == 2, stack == [1,3,0,0] @@ -200,4 +168,36 @@ describe("StateUpdate :: RewriteStack", () => { // ); + // TODO: FAIL CASES, ADD STACK UNDERFLOW CASES TOO + //-TEST_4----------------------------------------------------------// + // init: stack == [[1, 0], [1, 0], [1, 0], [1, 0]] + // expect: FAIL, STACK OVERFLOW + let in_max_stack = { ...INITIAL_IN }; + in_max_stack.byte = Delimiters.START_BRACE; + in_max_stack.stack = [[1, 0], [1, 0], [1, 0], [1, 0]]; + generateFailCase(in_max_stack, ">>>> `{` read --> (stack overflow)"); + + //-TEST_5----------------------------------------------------------// + // init: stack == [[1, 0], [1, 0], [1, 0], [1, 0]] + // expect: FAIL, STACK OVERFLOW + let in_max_stack_2 = { ...INITIAL_IN }; + in_max_stack_2.byte = Delimiters.START_BRACKET; + in_max_stack_2.stack = [[1, 0], [1, 0], [1, 0], [1, 0]]; + generateFailCase(in_max_stack, ">>>> `[` read --> (stack overflow)"); + + // //-TEST_3----------------------------------------------------------// + // // init: stack == [1,0,0,0] + // // read: `]` + // // expect: FAIL, INVALID CHAR + // let in_object_to_read_start_bracket = { ...INITIAL_IN }; + // in_object_to_read_start_bracket.byte = Delimiters.START_BRACKET; + // in_object_to_read_start_bracket.pointer = 1; + // in_object_to_read_start_bracket.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + // let in_object_to_read_start_bracket_out = { ...INITIAL_OUT }; + // in_object_to_read_start_bracket_out.next_pointer = 2; + // in_object_to_read_start_bracket_out.next_stack = [[1, 0], [2, 0], [0, 0], [0, 0]]; + // generatePassCase(in_object_to_read_start_bracket, + // in_object_to_read_start_bracket_out, + // ">>>> `[` read" + // ); }); \ No newline at end of file From a34f328dcc3a99482dc1f06b5d18b0f8a9d187fc Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 15 Aug 2024 18:47:50 -0600 Subject: [PATCH 87/99] WIP: reduced many variables Lot's of complexity cleared away and many important tests are passing again. Still WIP, but getting close. --- circuits/parser.circom | 111 ++++++++++----------------- circuits/test/parser/parser.test.ts | 16 ---- circuits/test/parser/stack.test.ts | 114 +++++++++++++++------------- 3 files changed, 99 insertions(+), 142 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index c2ef9b6..9288152 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -190,14 +190,18 @@ template RewriteStack(n) { //-----------------------------------------------------------------------------// // * scan value on top of stack * - component topOfStack = GetTopOfStack(n); - topOfStack.stack <== stack; - signal pointer <== topOfStack.pointer; - signal current_value[2] <== topOfStack.value; + component topOfStack = GetTopOfStack(n); + topOfStack.stack <== stack; + signal pointer <== topOfStack.pointer; + signal current_value[2] <== topOfStack.value; + // * check if we are currently in a value of an object * + component inObjectValue = IsEqualArray(2); + inObjectValue.in[0] <== current_value; // TODO: Move colon to be a toggle in the second stack position. + inObjectValue.in[1] <== [1,1]; // * check if value indicates currently in an array * - component inArray = IsEqual(); - inArray.in[0] <== current_value[0]; - inArray.in[1] <== 2; + component inArray = IsEqual(); + inArray.in[0] <== current_value[0]; + inArray.in[1] <== 2; //-----------------------------------------------------------------------------// //-----------------------------------------------------------------------------// @@ -214,90 +218,53 @@ template RewriteStack(n) { // * read in an end bracket `]` * component readEndBracket = IsEqual(); readEndBracket.in <== [read_write_value, -2]; - // * read in either an end brace `}` or an end bracket `]` * - component readEndChar = IsZero(); - readEndChar.in <== (read_write_value + 1) * (read_write_value + 2); + // * read in a colon `:` * + component readColon = IsEqual(); + readColon.in[0] <== 3; + readColon.in[1] <== read_write_value; // * read in a comma `,` * component readComma = IsEqual(); readComma.in[0] <== 4; readComma.in[1] <== read_write_value; + // * composite signals * + signal readEndChar <== readEndBrace.out + readEndBracket.out; + signal readCommaInArray <== readComma.out * inArray.out; + signal readCommaNotInArray <== readComma.out * (1 - inArray.out); //-----------------------------------------------------------------------------// //-----------------------------------------------------------------------------// // * determine whether we are pushing or popping from the stack * - component isPop = IsZero(); - isPop.in <== readStartBrace.out + readStartBracket.out; - component isPush = IsZero(); - isPush.in <== readEndBrace.out + readEndBracket.out; - - - // TODO: Can remove all the pushpop stuff by checking what character we got. E.g., if it is "negative" or comma, we pop, if it is positive we push, basically - signal READ_COMMA_AND_IN_ARRAY <== (1 - readComma.out) + (1 - inArray.out); // POORLY NAMED. THIS IS MORE LIKE XNOR or something. - component isReadCommaAndInArray = IsZero(); - isReadCommaAndInArray.in <== READ_COMMA_AND_IN_ARRAY; - - signal read_comma_in_array <== readComma.out * inArray.out; - - - component prev_indicator[n]; + component isPush = IsEqual(); + isPush.in <== [readStartBrace.out + readStartBracket.out, 1]; + component isPop = IsEqual(); + isPop.in <== [readEndBrace.out + readEndBracket.out, 1]; + // * set an indicator array for where we are pushing to or popping from* component indicator[n]; - signal isPopAt[n]; - signal isPushAt[n]; - - signal NOT_READ_COMMA <== (1 - readComma.out) * read_write_value; - signal READ_COMMA <== readComma.out * ((1-inArray.out) * (-3) + inArray.out * (-2)); - signal corrected_read_write_value <== READ_COMMA + NOT_READ_COMMA; - - signal isPopArr <== isPop.out * readEndBracket.out; - for(var i = 0; i < n; i++) { - // points to 1 value back from top - prev_indicator[i] = IsZero(); - prev_indicator[i].in <== pointer - 1 - isPop.out - i; - - // Points to top of stack if POP else it points to unallocated position + // Points indicator[i] = IsZero(); - indicator[i].in <== pointer - isPop.out - i; + indicator[i].in <== pointer - isPop.out - readColon.out - readComma.out - i; // Note, pointer points to unallocated region! } + //-----------------------------------------------------------------------------// - component atColon = IsEqual(); - atColon.in[0] <== current_value[0]; // TODO: Move colon to be a toggle in the second stack position. - atColon.in[1] <== 3; - signal isDoublePop <== atColon.out * readEndChar.out; - - signal isPopAtPrev[n]; - signal second_pop_val[n]; - signal first_pop_val[n]; - signal temp_val[n]; - signal temp_val2[n]; -// log("read_comma_in_array: ", read_comma_in_array); + signal stack_change_value[2] <== [(isPush.out + isPop.out) * read_write_value, readColon.out + readCommaInArray - readCommaNotInArray]; + log("isPush: ", isPush.out); + log("isPop: ", isPop.out); + log("readColon: ", readColon.out); + log("read_write_value: ", read_write_value); + signal second_index_clear[n]; for(var i = 0; i < n; i++) { - - // Indicators for index to PUSH to or POP from - isPopAtPrev[i] <== prev_indicator[i].out * isDoublePop; // temp signal - isPopAt[i] <== indicator[i].out * isPop.out; // want to add: `prev_indicator[i] * isDoublePop` - - isPushAt[i] <== indicator[i].out * isPush.out; - - // Leave the stack alone except for where we indicate change - second_pop_val[i] <== isPopAtPrev[i] * corrected_read_write_value; - temp_val[i] <== corrected_read_write_value - (3 + corrected_read_write_value) * isDoublePop; - first_pop_val[i] <== isPopAt[i] * temp_val[i]; // = isPopAt[i] * (corrected_read_write_value * (1 - isDoublePop) - 3 * isDoublePop) - - next_stack[i][0] <== stack[i][0] + isPushAt[i] * corrected_read_write_value + first_pop_val[i] + second_pop_val[i]; - - temp_val2[i] <== prev_indicator[i].out * read_comma_in_array; - next_stack[i][1] <== stack[i][1] + temp_val2[i] - stack[i][1] * isPopArr; - - // log("prev_indicator[i]: ", prev_indicator[i].out); - // log("next_stack[", i,"] ", "= [",next_stack[i][0], "][", next_stack[i][1],"]" ); - // TODO: Constrain next_stack entries to be 0,1,2,3 + next_stack[i][0] <== stack[i][0] + indicator[i].out * stack_change_value[0]; + second_index_clear[i] <== stack[i][1] * readEndChar; + next_stack[i][1] <== stack[i][1] + indicator[i].out * (stack_change_value[1] - second_index_clear[i]); + log("next_stack[", i,"] ", "= [",next_stack[i][0], "][", next_stack[i][1],"]" ); } // TODO: WE CAN'T LEAVE 8 HERE, THIS HAS TO DEPEND ON THE STACK HEIGHT AS IT IS THE NUM BITS NEEDED TO REPR STACK HEIGHT IN BINARY + log("next_pointer: ", pointer - isPop.out + isPush.out); component isUnderflowOrOverflow = InRange(8); - isUnderflowOrOverflow.in <== pointer - isPop.out + isPush.out; + isUnderflowOrOverflow.in <== pointer - isPop.out + isPush.out; isUnderflowOrOverflow.range <== [0,n]; isUnderflowOrOverflow.out === 1; } \ No newline at end of file diff --git a/circuits/test/parser/parser.test.ts b/circuits/test/parser/parser.test.ts index c3872ae..ba80cdb 100644 --- a/circuits/test/parser/parser.test.ts +++ b/circuits/test/parser/parser.test.ts @@ -40,22 +40,6 @@ describe("StateUpdate", () => { // // expect: ZEROS // generatePassCase(INITIAL_IN, INITIAL_OUT, ">>>> `NUL` read"); - // // TODO: Consider moving to `stack.test.ts` - // //-TEST_2----------------------------------------------------------// - // // init: INIT - // // read: `{` - // // expect: pointer --> 1 - // // stack --> [1,0,0,0] - // let read_start_brace = { ...INITIAL_IN }; - // read_start_brace.byte = Delimiters.START_BRACE; - // let read_start_brace_out = { ...INITIAL_OUT }; - // read_start_brace_out.next_pointer = 1; - // read_start_brace_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - // generatePassCase(read_start_brace, - // read_start_brace_out, - // ">>>> `{` read" - // ); - // //-TEST_3----------------------------------------------------------// // // state: INIT // // read: `}` diff --git a/circuits/test/parser/stack.test.ts b/circuits/test/parser/stack.test.ts index b073db6..df0228b 100644 --- a/circuits/test/parser/stack.test.ts +++ b/circuits/test/parser/stack.test.ts @@ -67,8 +67,20 @@ describe("StateUpdate :: RewriteStack", () => { }); } - //-TEST_1----------------------------------------------------------// + // init: stack == [[0, 0], [0, 0], [0, 0], [0, 0]] + // read: `{` + // expect: stack --> [[1, 0], [0, 0], [0, 0], [0, 0]] + let read_start_brace = { ...INITIAL_IN }; + read_start_brace.byte = Delimiters.START_BRACE; + let read_start_brace_out = { ...INITIAL_OUT }; + read_start_brace_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + generatePassCase(read_start_brace, + read_start_brace_out, + ">>>> `{` read" + ); + + //-TEST_2----------------------------------------------------------// // state: stack == [[1, 0], [0, 0], [0, 0], [0, 0]] // read: `{` // expect: stack --> [[1, 0], [1, 0], [0, 0], [0, 0]] @@ -79,7 +91,7 @@ describe("StateUpdate :: RewriteStack", () => { in_object_out.next_stack = [[1, 0], [1, 0], [0, 0], [0, 0]]; generatePassCase(in_object, in_object_out, ">>>> `{` read"); - //-TEST_2----------------------------------------------------------// + //-TEST_3----------------------------------------------------------// // state: stack == [[1, 0], [0, 0], [0, 0], [0, 0]] // read: `}` // expect: stack --> [[0, 0], [0, 0], [0, 0], [0, 0]] @@ -92,7 +104,7 @@ describe("StateUpdate :: RewriteStack", () => { ">>>> `}` read" ); - //-TEST_3----------------------------------------------------------// + //-TEST_4----------------------------------------------------------// // init: stack == [[1, 0], [0, 0], [0, 0], [0, 0]] // read: `[` // expect: stack --> [[1, 0], [2, 0], [0, 0], [0, 0]] @@ -106,7 +118,7 @@ describe("StateUpdate :: RewriteStack", () => { ">>>> `[` read" ); - //-TEST_4----------------------------------------------------------// + //-TEST_5----------------------------------------------------------// // init: stack == [[1, 0], [2, 0], [0, 0], [0, 0]] // read: `]` // expect: stack --> [[1, 0], [0, 0], [0, 0], [0, 0]] @@ -120,12 +132,26 @@ describe("StateUpdate :: RewriteStack", () => { ">>>> `]` read" ); + //-TEST_6-----------------------------------------------------------// + // state: stack == [[1, 0], [0, 0], [0, 0], [0, 0]] + // read: `:` + // expect: stack --> [[1, 1], [0, 0], [0, 0], [0, 0]] + let parsed_key_wait_to_parse_value = { ...INITIAL_IN }; + parsed_key_wait_to_parse_value.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + parsed_key_wait_to_parse_value.byte = Delimiters.COLON; + let parsed_key_wait_to_parse_value_out = { ...INITIAL_OUT }; + parsed_key_wait_to_parse_value_out.next_stack = [[1, 1], [0, 0], [0, 0], [0, 0]]; + generatePassCase(parsed_key_wait_to_parse_value, + parsed_key_wait_to_parse_value_out, + ">>>> `:` read" + ); + //-TEST_7----------------------------------------------------------// - // init: stack == [[1, 0], [3, 0], [0, 0], [0, 0]] + // init: stack == [[1, 0], [0, 0], [0, 0], [0, 0]] // expect: stack --> [[1, 0], [0, 0], [0, 0], [0, 0]] let in_object_and_value = { ...INITIAL_IN }; in_object_and_value.byte = Delimiters.COMMA; - in_object_and_value.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; + in_object_and_value.stack = [[1, 1], [0, 0], [0, 0], [0, 0]]; let in_object_and_value_out = { ...INITIAL_OUT }; in_object_and_value_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; generatePassCase(in_object_and_value, @@ -133,57 +159,37 @@ describe("StateUpdate :: RewriteStack", () => { ">>>> `,` read" ); - // //-TEST_8----------------------------------------------------------// - // // init: pointer == 2, stack == [1,3,0,0] - // // read: `}` - // // expect: pointer --> 2 - // // stack --> [1,3,0,0] - // let in_object_and_value_to_leave_object = { ...INITIAL_IN }; - // in_object_and_value_to_leave_object.byte = Delimiters.END_BRACE; - // in_object_and_value_to_leave_object.pointer = 2; - // in_object_and_value_to_leave_object.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - // let in_object_and_value_to_leave_object_out = { ...INITIAL_OUT }; - // in_object_and_value_to_leave_object_out.next_pointer = 0; - // in_object_and_value_to_leave_object_out.next_stack = [[0, 0], [0, 0], [0, 0], [0, 0]]; - // generatePassCase(in_object_and_value_to_leave_object, - // in_object_and_value_to_leave_object_out, - // ">>>> `}` read" - // ); - - // //-TEST_9-----------------------------------------------------------// - // // state: pointer == 1, stack == [1,0,0,0] - // // read: `:` - // // expect: pointer --> 2 - // // stack --> [1,3,0,0] - // let parsed_key_wait_to_parse_value = { ...INITIAL_IN }; - // parsed_key_wait_to_parse_value.pointer = 1; - // parsed_key_wait_to_parse_value.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - // parsed_key_wait_to_parse_value.byte = Delimiters.COLON; - // let parsed_key_wait_to_parse_value_out = { ...INITIAL_OUT }; - // parsed_key_wait_to_parse_value_out.next_pointer = 2; - // parsed_key_wait_to_parse_value_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - // generatePassCase(parsed_key_wait_to_parse_value, - // parsed_key_wait_to_parse_value_out, - // ">>>> `:` read" - // ); + //-TEST_8----------------------------------------------------------// + // init: stack == [[1, 1], [0, 0], [0, 0], [0, 0]] + // read: `}` + // expect: stack --> [[0, 0], [0, 0], [0, 0], [0, 0]] + let in_object_and_value_to_leave_object = { ...INITIAL_IN }; + in_object_and_value_to_leave_object.byte = Delimiters.END_BRACE; + in_object_and_value_to_leave_object.stack = [[1, 1], [0, 0], [0, 0], [0, 0]]; + let in_object_and_value_to_leave_object_out = { ...INITIAL_OUT }; + in_object_and_value_to_leave_object_out.next_stack = [[0, 0], [0, 0], [0, 0], [0, 0]]; + generatePassCase(in_object_and_value_to_leave_object, + in_object_and_value_to_leave_object_out, + ">>>> `}` read" + ); // TODO: FAIL CASES, ADD STACK UNDERFLOW CASES TOO - //-TEST_4----------------------------------------------------------// - // init: stack == [[1, 0], [1, 0], [1, 0], [1, 0]] - // expect: FAIL, STACK OVERFLOW - let in_max_stack = { ...INITIAL_IN }; - in_max_stack.byte = Delimiters.START_BRACE; - in_max_stack.stack = [[1, 0], [1, 0], [1, 0], [1, 0]]; - generateFailCase(in_max_stack, ">>>> `{` read --> (stack overflow)"); - - //-TEST_5----------------------------------------------------------// - // init: stack == [[1, 0], [1, 0], [1, 0], [1, 0]] - // expect: FAIL, STACK OVERFLOW - let in_max_stack_2 = { ...INITIAL_IN }; - in_max_stack_2.byte = Delimiters.START_BRACKET; - in_max_stack_2.stack = [[1, 0], [1, 0], [1, 0], [1, 0]]; - generateFailCase(in_max_stack, ">>>> `[` read --> (stack overflow)"); + // //-TEST_4----------------------------------------------------------// + // // init: stack == [[1, 0], [1, 0], [1, 0], [1, 0]] + // // expect: FAIL, STACK OVERFLOW + // let in_max_stack = { ...INITIAL_IN }; + // in_max_stack.byte = Delimiters.START_BRACE; + // in_max_stack.stack = [[1, 0], [1, 0], [1, 0], [1, 0]]; + // generateFailCase(in_max_stack, ">>>> `{` read --> (stack overflow)"); + + // //-TEST_5----------------------------------------------------------// + // // init: stack == [[1, 0], [1, 0], [1, 0], [1, 0]] + // // expect: FAIL, STACK OVERFLOW + // let in_max_stack_2 = { ...INITIAL_IN }; + // in_max_stack_2.byte = Delimiters.START_BRACKET; + // in_max_stack_2.stack = [[1, 0], [1, 0], [1, 0], [1, 0]]; + // generateFailCase(in_max_stack, ">>>> `[` read --> (stack overflow)"); // //-TEST_3----------------------------------------------------------// // // init: stack == [1,0,0,0] From afff6a84fe68e888f761e8180d3cc044c9846954 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 15 Aug 2024 19:06:58 -0600 Subject: [PATCH 88/99] all tests pass --- circuits/parser.circom | 12 +- circuits/test/parser/parser.test.ts | 175 +++++++++++++--------------- circuits/test/parser/stack.test.ts | 60 +++++++--- circuits/test/parser/values.test.ts | 124 ++++++++------------ 4 files changed, 178 insertions(+), 193 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 9288152..5a3ec4d 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -249,20 +249,20 @@ template RewriteStack(n) { signal stack_change_value[2] <== [(isPush.out + isPop.out) * read_write_value, readColon.out + readCommaInArray - readCommaNotInArray]; - log("isPush: ", isPush.out); - log("isPop: ", isPop.out); - log("readColon: ", readColon.out); - log("read_write_value: ", read_write_value); + // log("isPush: ", isPush.out); + // log("isPop: ", isPop.out); + // log("readColon: ", readColon.out); + // log("read_write_value: ", read_write_value); signal second_index_clear[n]; for(var i = 0; i < n; i++) { next_stack[i][0] <== stack[i][0] + indicator[i].out * stack_change_value[0]; second_index_clear[i] <== stack[i][1] * readEndChar; next_stack[i][1] <== stack[i][1] + indicator[i].out * (stack_change_value[1] - second_index_clear[i]); - log("next_stack[", i,"] ", "= [",next_stack[i][0], "][", next_stack[i][1],"]" ); + // log("next_stack[", i,"] ", "= [",next_stack[i][0], "][", next_stack[i][1],"]" ); } // TODO: WE CAN'T LEAVE 8 HERE, THIS HAS TO DEPEND ON THE STACK HEIGHT AS IT IS THE NUM BITS NEEDED TO REPR STACK HEIGHT IN BINARY - log("next_pointer: ", pointer - isPop.out + isPush.out); + // log("next_pointer: ", pointer - isPop.out + isPush.out); component isUnderflowOrOverflow = InRange(8); isUnderflowOrOverflow.in <== pointer - isPop.out + isPush.out; isUnderflowOrOverflow.range <== [0,n]; diff --git a/circuits/test/parser/parser.test.ts b/circuits/test/parser/parser.test.ts index ba80cdb..11e1bea 100644 --- a/circuits/test/parser/parser.test.ts +++ b/circuits/test/parser/parser.test.ts @@ -5,8 +5,8 @@ import { Delimiters, WhiteSpace, Numbers, Escape, INITIAL_IN, INITIAL_OUT } from describe("StateUpdate", () => { let circuit: WitnessTester< - ["byte", "pointer", "stack", "parsing_string", "parsing_number"], - ["next_pointer", "next_stack", "next_parsing_string", "next_parsing_number"] + ["byte", "stack", "parsing_string", "parsing_number"], + ["next_stack", "next_parsing_string", "next_parsing_number"] >; function generatePassCase(input: any, expected: any, desc: string) { @@ -35,101 +35,82 @@ describe("StateUpdate", () => { }); - // //-TEST_1----------------------------------------------------------// - // // init: ZEROS then read `do_nothing` byte - // // expect: ZEROS - // generatePassCase(INITIAL_IN, INITIAL_OUT, ">>>> `NUL` read"); - - // //-TEST_3----------------------------------------------------------// - // // state: INIT - // // read: `}` - // // expect: FAIL (stack underflow) - // let read_end_brace = { ...INITIAL_IN }; - // read_end_brace.byte = Delimiters.END_BRACE; - // generateFailCase(read_end_brace, - // ">>>> `}` read --> (stack underflow)" - // ); - - // //-TEST_4----------------------------------------------------------// - // // state: pointer == 1, stack == [1,0,0,0] - // // read: `"` - // // expect: parsing_string --> 1 - // let in_object_find_key = { ...INITIAL_IN }; - // in_object_find_key.pointer = 1; - // in_object_find_key.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - // in_object_find_key.byte = Delimiters.QUOTE; - // let in_object_find_key_out = { ...INITIAL_OUT }; - // in_object_find_key_out.next_pointer = 1; - // in_object_find_key_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - // in_object_find_key_out.next_parsing_string = 1; - // generatePassCase(in_object_find_key, - // in_object_find_key_out, - // ">>>> `\"` read" - // ); - - // //-TEST_5----------------------------------------------------------// - // // state: pointer == 1, stack = [1,0,0,0], parsing_string == 1 - // // read: ` ` - // // expect: NIL - // let in_key = { ...INITIAL_IN }; - // in_key.pointer = 1; - // in_key.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - // in_key.parsing_string = 1; - // in_key.byte = WhiteSpace.SPACE; - // let in_key_out = { ...INITIAL_OUT }; - // in_key_out.next_pointer = 1; - // in_key_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - // in_key_out.next_parsing_string = 1; - // generatePassCase(in_key, in_key_out, ">>>> ` ` read"); - - // //-TEST_6----------------------------------------------------------// - // // init: pointer == 1, stack == [1,0,0,0] - // // read: `"` - // // expect: parsing_string --> 0 - // // - // let in_key_to_exit = { ...INITIAL_IN }; - // in_key_to_exit.pointer = 1; - // in_key_to_exit.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - // in_key_to_exit.parsing_string = 1 - // in_key_to_exit.byte = Delimiters.QUOTE; - // let in_key_to_exit_out = { ...INITIAL_OUT }; - // in_key_to_exit_out.next_pointer = 1; - // in_key_to_exit_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - // generatePassCase(in_key_to_exit, in_key_to_exit_out, "`\"` read"); - - // //-TEST_7----------------------------------------------------------// - // // state: pointer == 2, stack == [1,3,0,0] - // // read: `"` - // // expect: parsing_string --> 1 - // let in_tree_find_value = { ...INITIAL_IN }; - // in_tree_find_value.pointer = 1; - // in_tree_find_value.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - // in_tree_find_value.byte = Delimiters.QUOTE; - // let in_tree_find_value_out = { ...INITIAL_OUT }; - // in_tree_find_value_out.next_pointer = 1; - // in_tree_find_value_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - // in_tree_find_value_out.next_parsing_string = 1; - // generatePassCase(in_tree_find_value, - // in_tree_find_value_out, - // ">>>> `\"` read" - // ); - - // //-TEST_8----------------------------------------------------------// - // // state: pointer == 2, stack == [1,3,0,0], parsing_string == 1 - // // read: `"` - // // expect: parsing_string == 0, - // let in_value_to_exit = { ...INITIAL_IN }; - // in_value_to_exit.pointer = 2; - // in_value_to_exit.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - // in_value_to_exit.parsing_string = 1; - // in_value_to_exit.byte = Delimiters.QUOTE; - // let in_value_to_exit_out = { ...INITIAL_OUT }; - // in_value_to_exit_out.next_pointer = 2; - // in_value_to_exit_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - // generatePassCase(in_value_to_exit, - // in_value_to_exit_out, - // ">>>> `\"` is read" - // ); + //-TEST_1----------------------------------------------------------// + // init: ZEROS then read `do_nothing` byte + // expect: ZEROS + generatePassCase(INITIAL_IN, INITIAL_OUT, ">>>> `NUL` read"); + + + //-TEST_2----------------------------------------------------------// + // state: stack == [[1, 0], [0, 0], [0, 0], [0, 0]] + // read: `"` + // expect: parsing_string --> 1 + let in_object_find_key = { ...INITIAL_IN }; + in_object_find_key.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + in_object_find_key.byte = Delimiters.QUOTE; + let in_object_find_key_out = { ...INITIAL_OUT }; + in_object_find_key_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + in_object_find_key_out.next_parsing_string = 1; + generatePassCase(in_object_find_key, + in_object_find_key_out, + ">>>> `\"` read" + ); + + //-TEST_3----------------------------------------------------------// + // state: stack = [[1, 0], [0, 0], [0, 0], [0, 0]], parsing_string == 1 + // read: ` ` + // expect: NIL + let in_key = { ...INITIAL_IN }; + in_key.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + in_key.parsing_string = 1; + in_key.byte = WhiteSpace.SPACE; + let in_key_out = { ...INITIAL_OUT }; + in_key_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + in_key_out.next_parsing_string = 1; + generatePassCase(in_key, in_key_out, ">>>> ` ` read"); + + //-TEST_4----------------------------------------------------------// + // init: stack == [[1, 0], [0, 0], [0, 0], [0, 0]] + // read: `"` + // expect: parsing_string --> 0 + // + let in_key_to_exit = { ...INITIAL_IN }; + in_key_to_exit.stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + in_key_to_exit.parsing_string = 1 + in_key_to_exit.byte = Delimiters.QUOTE; + let in_key_to_exit_out = { ...INITIAL_OUT }; + in_key_to_exit_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + generatePassCase(in_key_to_exit, in_key_to_exit_out, "`\"` read"); + + //-TEST_5----------------------------------------------------------// + // state: stack == [[1, 1], [0, 0], [0, 0], [0, 0]] + // read: `"` + // expect: parsing_string --> 1 + let in_tree_find_value = { ...INITIAL_IN }; + in_tree_find_value.stack = [[1, 1], [0, 0], [0, 0], [0, 0]]; + in_tree_find_value.byte = Delimiters.QUOTE; + let in_tree_find_value_out = { ...INITIAL_OUT }; + in_tree_find_value_out.next_stack = [[1, 1], [0, 0], [0, 0], [0, 0]]; + in_tree_find_value_out.next_parsing_string = 1; + generatePassCase(in_tree_find_value, + in_tree_find_value_out, + ">>>> `\"` read" + ); + + //-TEST_6----------------------------------------------------------// + // state: stack == [[1, 1], [0, 0], [0, 0], [0, 0]];, parsing_string == 1 + // read: `"` + // expect: parsing_string == 0, + let in_value_to_exit = { ...INITIAL_IN }; + in_value_to_exit.stack = [[1, 1], [0, 0], [0, 0], [0, 0]]; + in_value_to_exit.parsing_string = 1; + in_value_to_exit.byte = Delimiters.QUOTE; + let in_value_to_exit_out = { ...INITIAL_OUT }; + in_value_to_exit_out.next_stack = [[1, 1], [0, 0], [0, 0], [0, 0]]; + generatePassCase(in_value_to_exit, + in_value_to_exit_out, + ">>>> `\"` is read" + ); }); diff --git a/circuits/test/parser/stack.test.ts b/circuits/test/parser/stack.test.ts index df0228b..69e0b1a 100644 --- a/circuits/test/parser/stack.test.ts +++ b/circuits/test/parser/stack.test.ts @@ -173,24 +173,50 @@ describe("StateUpdate :: RewriteStack", () => { ">>>> `}` read" ); + //-TEST_9----------------------------------------------------------// + // idea: Inside a number value after a key in an object. + // state: stack == [[1, 1], [0, 0], [0, 0], [0, 0]], parsing_number == 1 + // read: `,` + // expect: pointer --> 2 + // stack --> [[1, 0], [0, 0], [0, 0], [0, 0]] + // parsing_number --> 0 + let inside_number = { ...INITIAL_IN }; + inside_number.stack = [[1, 1], [0, 0], [0, 0], [0, 0]]; + inside_number.parsing_number = 1; + inside_number.byte = Delimiters.COMMA; + let inside_number_out = { ...INITIAL_OUT }; + inside_number_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + generatePassCase(inside_number, inside_number_out, ">>>> `,` read"); + + + // TODO: FAIL CASES, ADD STACK UNDERFLOW CASES TOO and RENUMBER + //-TEST_3----------------------------------------------------------// + // state: INIT + // read: `}` + // expect: FAIL (stack underflow) + let read_end_brace = { ...INITIAL_IN }; + read_end_brace.byte = Delimiters.END_BRACE; + generateFailCase(read_end_brace, + ">>>> `}` read --> (stack underflow)" + ); - // TODO: FAIL CASES, ADD STACK UNDERFLOW CASES TOO - // //-TEST_4----------------------------------------------------------// - // // init: stack == [[1, 0], [1, 0], [1, 0], [1, 0]] - // // expect: FAIL, STACK OVERFLOW - // let in_max_stack = { ...INITIAL_IN }; - // in_max_stack.byte = Delimiters.START_BRACE; - // in_max_stack.stack = [[1, 0], [1, 0], [1, 0], [1, 0]]; - // generateFailCase(in_max_stack, ">>>> `{` read --> (stack overflow)"); - - // //-TEST_5----------------------------------------------------------// - // // init: stack == [[1, 0], [1, 0], [1, 0], [1, 0]] - // // expect: FAIL, STACK OVERFLOW - // let in_max_stack_2 = { ...INITIAL_IN }; - // in_max_stack_2.byte = Delimiters.START_BRACKET; - // in_max_stack_2.stack = [[1, 0], [1, 0], [1, 0], [1, 0]]; - // generateFailCase(in_max_stack, ">>>> `[` read --> (stack overflow)"); - + //-TEST_9----------------------------------------------------------// + // init: stack == [[1, 0], [1, 0], [1, 0], [1, 0]] + // expect: FAIL, STACK OVERFLOW + let in_max_stack = { ...INITIAL_IN }; + in_max_stack.byte = Delimiters.START_BRACE; + in_max_stack.stack = [[1, 0], [1, 0], [1, 0], [1, 0]]; + generateFailCase(in_max_stack, ">>>> `{` read --> (stack overflow)"); + + //-TEST_10----------------------------------------------------------// + // init: stack == [[1, 0], [1, 0], [1, 0], [1, 0]] + // expect: FAIL, STACK OVERFLOW + let in_max_stack_2 = { ...INITIAL_IN }; + in_max_stack_2.byte = Delimiters.START_BRACKET; + in_max_stack_2.stack = [[1, 0], [1, 0], [1, 0], [1, 0]]; + generateFailCase(in_max_stack, ">>>> `[` read --> (stack overflow)"); + + // TODO: This requires a more careful check of the stack that popping clears the current value. Use an IsZero // //-TEST_3----------------------------------------------------------// // // init: stack == [1,0,0,0] // // read: `]` diff --git a/circuits/test/parser/values.test.ts b/circuits/test/parser/values.test.ts index dd437c8..6661ad6 100644 --- a/circuits/test/parser/values.test.ts +++ b/circuits/test/parser/values.test.ts @@ -22,83 +22,61 @@ describe("StateUpdate :: Values", () => { }); } - // //-TEST_1----------------------------------------------------------// - // // idea: Read a number value after a key in an object. - // // state: pointer == 2, stack == [1,3,0,0] - // // read: `0` - // // expect: pointer --> 2 - // // stack --> [1,3,0,0] - // // parsing_number --> 1 - // let read_number = { ...INITIAL_IN }; - // read_number.pointer = 2; - // read_number.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - // read_number.byte = Numbers.ZERO; - // let read_number_out = { ...INITIAL_OUT }; - // read_number_out.next_pointer = 2; - // read_number_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - // read_number_out.next_parsing_number = 1; - // generatePassCase(read_number, read_number_out, ">>>> `0` read"); - - // //-TEST_2----------------------------------------------------------// - // // idea: Inside a number value after a key in an object. - // // state: pointer == 2, stack == [1,3,0,0], parsing_number == 1 - // // read: `,` - // // expect: pointer --> 2 - // // stack --> [1,3,0,0] - // // parsing_number --> 0 - // let inside_number = { ...INITIAL_IN }; - // inside_number.pointer = 2; - // inside_number.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - // inside_number.parsing_number = 1; - // inside_number.byte = Delimiters.COMMA; - // let inside_number_out = { ...INITIAL_OUT }; - // inside_number_out.next_pointer = 1; - // inside_number_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - // generatePassCase(inside_number, inside_number_out, ">>>> `,` read"); + //-TEST_1----------------------------------------------------------// + // idea: Read a number value after a key in an object. + // state: stack == [[1, 1], [0, 0], [0, 0], [0, 0]] + // read: `0` + // expect: stack --> [[1, 1], [0, 0], [0, 0], [0, 0]] + // parsing_number --> 1 + let read_number = { ...INITIAL_IN }; + read_number.stack = [[1, 1], [0, 0], [0, 0], [0, 0]]; + read_number.byte = Numbers.ZERO; + let read_number_out = { ...INITIAL_OUT }; + read_number_out.next_stack = [[1, 1], [0, 0], [0, 0], [0, 0]]; + read_number_out.next_parsing_number = 1; + generatePassCase(read_number, read_number_out, ">>>> `0` read"); // // TODO: Note that reading a space while reading a number will not throw an error! - // //-TEST_2----------------------------------------------------------// - // // idea: Inside a number value after a key in an object. - // // state: pointer == 2, stack == [1,3,0,0], parsing_number == 1 - // // read: `1` - // // expect: pointer --> 2 - // // stack --> [1,3,0,0] - // // parsing_number --> 0 - // let inside_number_continue = { ...INITIAL_IN }; - // inside_number_continue.pointer = 2; - // inside_number_continue.stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - // inside_number_continue.parsing_number = 1; - // inside_number_continue.byte = Numbers.ONE; - // let inside_number_continue_out = { ...INITIAL_OUT }; - // inside_number_continue_out.next_pointer = 2; - // inside_number_continue_out.next_stack = [[1, 0], [3, 0], [0, 0], [0, 0]]; - // inside_number_continue_out.next_parsing_number = 1; - // generatePassCase(inside_number_continue, inside_number_continue_out, ">>>> `1` read"); + //-TEST_2----------------------------------------------------------// + // idea: Inside a number value after a key in an object. + // state: pointer == 2, stack == [1,3,0,0], parsing_number == 1 + // read: `1` + // expect: pointer --> 2 + // stack --> [1,3,0,0] + // parsing_number --> 0 + let inside_number_continue = { ...INITIAL_IN }; + inside_number_continue.stack = [[1, 1], [0, 0], [0, 0], [0, 0]]; + inside_number_continue.parsing_number = 1; + inside_number_continue.byte = Numbers.ONE; + let inside_number_continue_out = { ...INITIAL_OUT }; + inside_number_continue_out.next_stack = [[1, 1], [0, 0], [0, 0], [0, 0]]; + inside_number_continue_out.next_parsing_number = 1; + generatePassCase(inside_number_continue, inside_number_continue_out, ">>>> `1` read"); - // describe("StateUpdate :: Values :: Array", () => { - // // Internal array parsing -----------------------------------------// + describe("StateUpdate :: Values :: Array", () => { + // Internal array parsing -----------------------------------------// - // //-TEST_10----------------------------------------------------------// - // // init: pointer = 1, stack = [1,2,0,0] -> `,` is read - // let in_arr = { ...INITIAL_IN }; - // in_arr.pointer = 2; - // in_arr.stack = [[1, 0], [2, 0], [0, 0], [0, 0]]; - // in_arr.byte = Delimiters.COMMA; - // let in_arr_out = { ...INITIAL_OUT }; - // in_arr_out.next_pointer = 2; - // in_arr_out.next_stack = [[1, 0], [2, 1], [0, 0], [0, 0]]; - // generatePassCase(in_arr, in_arr_out, ">>>> `,` read"); + //-TEST_10----------------------------------------------------------// + // init: stack == [[1, 0], [2, 0], [0, 0], [0, 0]] + // read: `,` + // expext: stack --> [[1, 0], [2, 1], [0, 0], [0, 0]] + let in_arr = { ...INITIAL_IN }; + in_arr.stack = [[1, 0], [2, 0], [0, 0], [0, 0]]; + in_arr.byte = Delimiters.COMMA; + let in_arr_out = { ...INITIAL_OUT }; + in_arr_out.next_stack = [[1, 0], [2, 1], [0, 0], [0, 0]]; + generatePassCase(in_arr, in_arr_out, ">>>> `,` read"); - // //-TEST_10----------------------------------------------------------// - // // init: pointer = 1, stack = [1,2,0,0] -> `,` is read - // let in_arr_idx_to_leave = { ...INITIAL_IN }; - // in_arr_idx_to_leave.pointer = 2; - // in_arr_idx_to_leave.stack = [[1, 0], [2, 1], [0, 0], [0, 0]]; - // in_arr_idx_to_leave.byte = Delimiters.END_BRACKET; - // let in_arr_idx_to_leave_out = { ...INITIAL_OUT }; - // in_arr_idx_to_leave_out.next_pointer = 1; - // in_arr_idx_to_leave_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; - // generatePassCase(in_arr_idx_to_leave, in_arr_idx_to_leave_out, ">>>> `]` read"); - // }); + //-TEST_10----------------------------------------------------------// + // init: stack == [[1, 0], [2, 1], [0, 0], [0, 0]] + // read: `]` + // expect: stack --> [[1, 0], [0, 0], [0, 0], [0, 0]] + let in_arr_idx_to_leave = { ...INITIAL_IN }; + in_arr_idx_to_leave.stack = [[1, 0], [2, 1], [0, 0], [0, 0]]; + in_arr_idx_to_leave.byte = Delimiters.END_BRACKET; + let in_arr_idx_to_leave_out = { ...INITIAL_OUT }; + in_arr_idx_to_leave_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + generatePassCase(in_arr_idx_to_leave, in_arr_idx_to_leave_out, ">>>> `]` read"); + }); }); \ No newline at end of file From b7bbc74123863cff2f8135aa21b0456be9b0d2ee Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 15 Aug 2024 19:14:36 -0600 Subject: [PATCH 89/99] updated circuits --- circuits.json | 14 +++++++------- circuits/extract.circom | 2 -- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/circuits.json b/circuits.json index b845556..c931162 100644 --- a/circuits.json +++ b/circuits.json @@ -12,7 +12,7 @@ "template": "Extract", "params": [ 12, - 2 + 1 ] }, "value_number": { @@ -20,7 +20,7 @@ "template": "Extract", "params": [ 12, - 3 + 2 ] }, "value_array": { @@ -28,7 +28,7 @@ "template": "Extract", "params": [ 18, - 3 + 2 ] }, "value_array_nested": { @@ -36,7 +36,7 @@ "template": "Extract", "params": [ 24, - 5 + 4 ] }, "value_array_object": { @@ -44,7 +44,7 @@ "template": "Extract", "params": [ 25, - 5 + 4 ] }, "value_array_object_array": { @@ -52,7 +52,7 @@ "template": "Extract", "params": [ 31, - 6 + 5 ] }, "value_object": { @@ -60,7 +60,7 @@ "template": "Extract", "params": [ 21, - 4 + 3 ] } } \ No newline at end of file diff --git a/circuits/extract.circom b/circuits/extract.circom index 2311f7a..cffeeee 100644 --- a/circuits/extract.circom +++ b/circuits/extract.circom @@ -44,10 +44,8 @@ template Extract(DATA_BYTES, MAX_STACK_HEIGHT) { // State[DATA_BYTES - 1].next_tree_depth === 0; // Debugging - log("State[", DATA_BYTES, "].pointer ", "= ", State[DATA_BYTES -1].next_pointer); for(var i = 0; i < MAX_STACK_HEIGHT; i++) { log("State[", DATA_BYTES, "].stack[", i,"] ", "= [",State[DATA_BYTES -1].next_stack[i][0], "][", State[DATA_BYTES - 1].next_stack[i][1],"]" ); - // log("State[", DATA_BYTES, "].stack[", i,"] ", "= ", State[DATA_BYTES -1 ].next_stack[i]); } log("State[", DATA_BYTES, "].parsing_string", "= ", State[DATA_BYTES-1].next_parsing_string); log("State[", DATA_BYTES, "].parsing_number", "= ", State[DATA_BYTES-1].next_parsing_number); From 6d10e1b62240b3fce3890f859ed2f4016c715595 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 15 Aug 2024 19:19:06 -0600 Subject: [PATCH 90/99] formatting --- circuits/parser.circom | 93 +++++++++++++----------------------------- 1 file changed, 29 insertions(+), 64 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 5a3ec4d..2aa072e 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -153,40 +153,11 @@ template GetTopOfStack(n) { } // TODO: IMPORTANT NOTE, THE STACK IS CONSTRAINED TO 2**8 so the LessThan and GreaterThan work (could be changed) -// TODO: Might be good to change value before increment pointer AND decrement pointer before changing value template RewriteStack(n) { assert(n < 2**8); signal input stack[n][2]; signal input read_write_value; signal output next_stack[n][2]; - - /* - IDEA: - - We want to look at the old data - - if pushpop is 0, we are going to just return the old stack - - if pushpop is 1, we are going to increment the pointer and write a new value - - if pushpop is -1, we are going to decrement the pointer and delete an old value if it was the same value - - TODO: There's the weird case of "no trailing commas" for KVs in JSON. - This constitutes valid JSON, fortunately, and is NOT optional. Or, at least, - we should NOT consider it to be for this current impl. - Basically, JSON must be like: - ``` - { - "a": "valA", - "b": "valB" - } - ``` - so there is the one end here where we have to go from: - stack == [1,3,0,0,...] - to - next_stack == [0,0,0,0,...] - on the case we get a POP instruction reading an object OR an array (no trailing commas in arrays either) - - // top of stack is a 3, then we need to pop off 3, and check the value underneath - // is correct match (i.e., a brace or bracket (1 or 2)) - */ //-----------------------------------------------------------------------------// // * scan value on top of stack * @@ -196,8 +167,8 @@ template RewriteStack(n) { signal current_value[2] <== topOfStack.value; // * check if we are currently in a value of an object * component inObjectValue = IsEqualArray(2); - inObjectValue.in[0] <== current_value; // TODO: Move colon to be a toggle in the second stack position. - inObjectValue.in[1] <== [1,1]; + inObjectValue.in[0] <== current_value; + inObjectValue.in[1] <== [1,1]; // * check if value indicates currently in an array * component inArray = IsEqual(); inArray.in[0] <== current_value[0]; @@ -207,64 +178,58 @@ template RewriteStack(n) { //-----------------------------------------------------------------------------// // * check what value was read * // * read in a start brace `{` * - component readStartBrace = IsEqual(); - readStartBrace.in <== [read_write_value, 1]; + component readStartBrace = IsEqual(); + readStartBrace.in <== [read_write_value, 1]; // * read in a start bracket `[` * - component readStartBracket = IsEqual(); - readStartBracket.in <== [read_write_value, 2]; + component readStartBracket = IsEqual(); + readStartBracket.in <== [read_write_value, 2]; // * read in an end brace `}` * - component readEndBrace = IsEqual(); - readEndBrace.in <== [read_write_value, -1]; + component readEndBrace = IsEqual(); + readEndBrace.in <== [read_write_value, -1]; // * read in an end bracket `]` * component readEndBracket = IsEqual(); readEndBracket.in <== [read_write_value, -2]; // * read in a colon `:` * - component readColon = IsEqual(); - readColon.in[0] <== 3; - readColon.in[1] <== read_write_value; + component readColon = IsEqual(); + readColon.in[0] <== 3; + readColon.in[1] <== read_write_value; // * read in a comma `,` * - component readComma = IsEqual(); - readComma.in[0] <== 4; - readComma.in[1] <== read_write_value; + component readComma = IsEqual(); + readComma.in[0] <== 4; + readComma.in[1] <== read_write_value; // * composite signals * - signal readEndChar <== readEndBrace.out + readEndBracket.out; - signal readCommaInArray <== readComma.out * inArray.out; + signal readEndChar <== readEndBrace.out + readEndBracket.out; + signal readCommaInArray <== readComma.out * inArray.out; signal readCommaNotInArray <== readComma.out * (1 - inArray.out); //-----------------------------------------------------------------------------// //-----------------------------------------------------------------------------// // * determine whether we are pushing or popping from the stack * - component isPush = IsEqual(); - isPush.in <== [readStartBrace.out + readStartBracket.out, 1]; - component isPop = IsEqual(); - isPop.in <== [readEndBrace.out + readEndBracket.out, 1]; + component isPush = IsEqual(); + isPush.in <== [readStartBrace.out + readStartBracket.out, 1]; + component isPop = IsEqual(); + isPop.in <== [readEndBrace.out + readEndBracket.out, 1]; // * set an indicator array for where we are pushing to or popping from* component indicator[n]; for(var i = 0; i < n; i++) { // Points - indicator[i] = IsZero(); - indicator[i].in <== pointer - isPop.out - readColon.out - readComma.out - i; // Note, pointer points to unallocated region! + indicator[i] = IsZero(); + indicator[i].in <== pointer - isPop.out - readColon.out - readComma.out - i; // Note, pointer points to unallocated region! } //-----------------------------------------------------------------------------// - signal stack_change_value[2] <== [(isPush.out + isPop.out) * read_write_value, readColon.out + readCommaInArray - readCommaNotInArray]; - // log("isPush: ", isPush.out); - // log("isPop: ", isPop.out); - // log("readColon: ", readColon.out); - // log("read_write_value: ", read_write_value); + signal stack_change_value[2] <== [(isPush.out + isPop.out) * read_write_value, readColon.out + readCommaInArray - readCommaNotInArray]; signal second_index_clear[n]; for(var i = 0; i < n; i++) { - next_stack[i][0] <== stack[i][0] + indicator[i].out * stack_change_value[0]; - second_index_clear[i] <== stack[i][1] * readEndChar; - next_stack[i][1] <== stack[i][1] + indicator[i].out * (stack_change_value[1] - second_index_clear[i]); - // log("next_stack[", i,"] ", "= [",next_stack[i][0], "][", next_stack[i][1],"]" ); + next_stack[i][0] <== stack[i][0] + indicator[i].out * stack_change_value[0]; + second_index_clear[i] <== stack[i][1] * readEndChar; + next_stack[i][1] <== stack[i][1] + indicator[i].out * (stack_change_value[1] - second_index_clear[i]); } // TODO: WE CAN'T LEAVE 8 HERE, THIS HAS TO DEPEND ON THE STACK HEIGHT AS IT IS THE NUM BITS NEEDED TO REPR STACK HEIGHT IN BINARY - // log("next_pointer: ", pointer - isPop.out + isPush.out); component isUnderflowOrOverflow = InRange(8); - isUnderflowOrOverflow.in <== pointer - isPop.out + isPush.out; - isUnderflowOrOverflow.range <== [0,n]; - isUnderflowOrOverflow.out === 1; + isUnderflowOrOverflow.in <== pointer - isPop.out + isPush.out; + isUnderflowOrOverflow.range <== [0,n]; + isUnderflowOrOverflow.out === 1; } \ No newline at end of file From 2cd22ce3e36e61eb468f07048bfbce6756f40e05 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 16 Aug 2024 14:57:46 -0600 Subject: [PATCH 91/99] cut down on constraints --- circuits/parser.circom | 70 ++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 2aa072e..a37b0b1 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -4,7 +4,8 @@ include "utils.circom"; include "language.circom"; /* -TODO: Change the values to push onto stack to be given by START_BRACE, COLON, etc. +TODO: OKAY, so one big thing to notice is that we are effectively doubling up (if not tripling up) on checking what byte we have just read. If we mess with the Commands, matcher, mask, and rewrite stack, I think we can reduce the times +we call these sorts of things and consolidate this greatly. Probably can cut constraints down by a factor of 2. */ template StateUpdate(MAX_STACK_HEIGHT) { @@ -63,12 +64,6 @@ template StateUpdate(MAX_STACK_HEIGHT) { next_parsing_string <== addToState.out[1]; next_parsing_number <== addToState.out[2]; - // for(var i = 0; i < 4; i++) { - // log("matcher.out[",i,"]: ", matcher.out[i]); - // log("mask.out[",i,"]: ", mask.out[i]); - // log("mulMaskAndOut[",i,"]: ", mulMaskAndOut.out[i]); - // } - //--------------------------------------------------------------------------------------------// //-Constraints--------------------------------------------------------------------------------// // * constrain bit flags * @@ -99,35 +94,38 @@ template StateToMask(n) { // `parsing_string` can change: out[1] <== 1 - 2 * parsing_string; - // `parsing_number` can change: - component isDelimeter = InRange(8); - isDelimeter.in <== read_write_value; - isDelimeter.range[0] <== 1; - isDelimeter.range[1] <== 4; - component isNumber = IsEqual(); - isNumber.in <== [read_write_value, 256]; - component isParsingString = IsEqual(); - isParsingString.in[0] <== parsing_string; - isParsingString.in[1] <== 1; - component isParsingNumber = IsEqual(); - isParsingNumber.in[0] <== parsing_number; - isParsingNumber.in[1] <== 1; - component toParseNumber = Switch(16); - // TODO: Could combine this into something that returns arrays so that we can set the mask more easily. - toParseNumber.branches <== [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; - toParseNumber.vals <== [0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]; - component stateToNum = Bits2Num(4); - stateToNum.in <== [isParsingString.out, isParsingNumber.out, isNumber.out, isDelimeter.out]; - // 1 2 4 8 - toParseNumber.case <== stateToNum.out; - // log("isNumber: ", isNumber.out); - // log("isParsingString: ", isParsingString.out); - // log("isParsingNumber: ", isParsingNumber.out); - // log("isDelimeter: ", isDelimeter.out); - // log("stateToNum: ", stateToNum.out); - // log("toParseNumber: ", toParseNumber.out); - - out[2] <== toParseNumber.out; + // // `parsing_number` can change: + component readDelimeter = InRange(8); + readDelimeter.in <== read_write_value; + readDelimeter.range[0] <== 1; + readDelimeter.range[1] <== 4; + log("readDelimeter:", readDelimeter.out); + component readNumber = IsEqual(); + readNumber.in <== [read_write_value, 256]; + log("readNumber: ", readNumber.out); + // component isParsingString = IsEqual(); + // isParsingString.in[0] <== parsing_string; + // isParsingString.in[1] <== 1; + // component isParsingNumber = IsEqual(); + // isParsingNumber.in[0] <== parsing_number; + // isParsingNumber.in[1] <== 1; + // component toParseNumber = Switch(16); + // // TODO: Could combine this into something that returns arrays so that we can set the mask more easily. + // toParseNumber.branches <== [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + // toParseNumber.vals <== [0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]; // These cases are useful to think about + // component stateToNum = Bits2Num(4); + // stateToNum.in <== [isParsingString.out, isParsingNumber.out, readNumber.out, readDelimeter.out]; + // // 1 2 4 8 + // toParseNumber.case <== stateToNum.out; + + // out[2] <== toParseNumber.out; + signal parsingNumberReadDelimeter <== parsing_number * (readDelimeter.out); // 10 above used + signal readNumberNotParsingNumber <== (1 - parsing_number) * readNumber.out; // 4 above + signal notParsingStringAndParsingNumberReadDelimeterOrReadNumberNotParsingNumber <== (1 - parsing_string) * (parsingNumberReadDelimeter + readNumberNotParsingNumber); + // 10 above ^^^^^^^^^^^^^^^^^ 4 above ^^^^^^^^^^^^^^^ + signal temp <== parsing_number * (1 - readNumber.out) ; + signal parsingNumberNotReadNumberNotReadDelimeter <== temp * (1-readDelimeter.out); + out[2] <== notParsingStringAndParsingNumberReadDelimeterOrReadNumberNotParsingNumber + parsingNumberNotReadNumberNotReadDelimeter; } // TODO: Check if underconstrained From 019fcd89acbfa8a3ffd195bfc7b55addcd23a79b Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 16 Aug 2024 15:52:22 -0600 Subject: [PATCH 92/99] save state --- circuits/parser.circom | 130 ++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 59 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index a37b0b1..42d879e 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -23,28 +23,52 @@ template StateUpdate(MAX_STACK_HEIGHT) { signal output next_parsing_number; component Syntax = Syntax(); - component Command = Command(); - - var read_write_value = 0; - var parsing_state[3] = [read_write_value, parsing_string, parsing_number]; + component Command = Command(); //--------------------------------------------------------------------------------------------// - //-State machine updating---------------------------------------------------------------------// + // Read new byte // * yield instruction based on what byte we read * component matcher = SwitchArray(8, 3); matcher.branches <== [Syntax.START_BRACE, Syntax.END_BRACE, Syntax.QUOTE, Syntax.COLON, Syntax.COMMA, Syntax.START_BRACKET, Syntax.END_BRACKET, Syntax.NUMBER ]; matcher.vals <== [Command.START_BRACE, Command.END_BRACE, Command.QUOTE, Command.COLON, Command.COMMA, Command.START_BRACKET, Command.END_BRACKET, Command.NUMBER]; - component numeral_range_check = InRange(8); - numeral_range_check.in <== byte; - numeral_range_check.range <== [48, 57]; // ASCII NUMERALS - // log("isNumeral:", numeral_range_check.out); - signal IS_NUMBER <== numeral_range_check.out * Syntax.NUMBER; - matcher.case <== (1 - numeral_range_check.out) * byte + IS_NUMBER; // IF (NOT is_number) THEN byte ELSE 256 - + component readNumber = InRange(8); + readNumber.in <== byte; + readNumber.range <== [48, 57]; // ASCII NUMERALS + signal IS_NUMBER <== readNumber.out * Syntax.NUMBER; + matcher.case <== (1 - readNumber.out) * byte + IS_NUMBER; // IF (NOT is_number) THEN byte ELSE 256 + //--------------------------------------------------------------------------------------------// + + //--------------------------------------------------------------------------------------------// + // Break down what was read + // * read in a start brace `{` * + component readStartBrace = IsEqual(); + readStartBrace.in <== [matcher.out[0], 1]; + // * read in a start bracket `[` * + component readStartBracket = IsEqual(); + readStartBracket.in <== [matcher.out[0], 2]; + // * read in an end brace `}` * + component readEndBrace = IsEqual(); + readEndBrace.in <== [matcher.out[0], -1]; + // * read in an end bracket `]` * + component readEndBracket = IsEqual(); + readEndBracket.in <== [matcher.out[0], -2]; + // * read in a colon `:` * + component readColon = IsEqual(); + readColon.in <== [matcher.out[0], 3]; + // * read in a comma `,` * + component readComma = IsEqual(); + readComma.in <== [matcher.out[0], 4]; + + component readDelimeter = Contains(6); + readDelimeter.in <== matcher.out[0]; + readDelimeter.array <== [1,-1,2,-2,3,4]; + // * get the instruction mask based on current state * component mask = StateToMask(MAX_STACK_HEIGHT); - // mask.in <== parsing_state; - mask.in <== [matcher.out[0],parsing_string,parsing_number]; // TODO: This is awkward. Things need to be rewritten + mask.readDelimeter <== readDelimeter.out; + mask.readNumber <== readNumber.out; + mask.parsing_string <== parsing_string; + mask.parsing_number <== parsing_number; // * multiply the mask array elementwise with the instruction array * @@ -53,13 +77,21 @@ template StateUpdate(MAX_STACK_HEIGHT) { mulMaskAndOut.rhs <== matcher.out; // * add the masked instruction to the state to get new state * component addToState = ArrayAdd(3); - addToState.lhs <== parsing_state; + addToState.lhs <== [0, parsing_string, parsing_number]; addToState.rhs <== mulMaskAndOut.out; // * set the new state * component newStack = RewriteStack(MAX_STACK_HEIGHT); newStack.stack <== stack; newStack.read_write_value <== addToState.out[0]; + newStack.readStartBrace <== readStartBrace.out; + newStack.readStartBracket <== readStartBracket.out; + newStack.readEndBrace <== readEndBrace.out; + newStack.readEndBracket <== readEndBracket.out; + newStack.readColon <== readColon.out; + newStack.readComma <== readComma.out; + + next_stack <== newStack.next_stack; next_parsing_string <== addToState.out[1]; next_parsing_number <== addToState.out[2]; @@ -81,12 +113,12 @@ template StateUpdate(MAX_STACK_HEIGHT) { template StateToMask(n) { // TODO: Probably need to assert things are bits where necessary. - signal input in[3]; + signal input readDelimeter; + signal input readNumber; + signal input parsing_string; + signal input parsing_number; signal output out[3]; - signal read_write_value <== in[0]; - signal parsing_string <== in[1]; - signal parsing_number <== in[2]; // `read_write_value`can change: IF NOT `parsing_string` out[0] <== (1 - parsing_string); @@ -95,14 +127,8 @@ template StateToMask(n) { out[1] <== 1 - 2 * parsing_string; // // `parsing_number` can change: - component readDelimeter = InRange(8); - readDelimeter.in <== read_write_value; - readDelimeter.range[0] <== 1; - readDelimeter.range[1] <== 4; - log("readDelimeter:", readDelimeter.out); - component readNumber = IsEqual(); - readNumber.in <== [read_write_value, 256]; - log("readNumber: ", readNumber.out); + + // log("readNumber: ", readNumber.out); // component isParsingString = IsEqual(); // isParsingString.in[0] <== parsing_string; // isParsingString.in[1] <== 1; @@ -119,12 +145,12 @@ template StateToMask(n) { // toParseNumber.case <== stateToNum.out; // out[2] <== toParseNumber.out; - signal parsingNumberReadDelimeter <== parsing_number * (readDelimeter.out); // 10 above used - signal readNumberNotParsingNumber <== (1 - parsing_number) * readNumber.out; // 4 above + signal parsingNumberReadDelimeter <== parsing_number * (readDelimeter); // 10 above used + signal readNumberNotParsingNumber <== (1 - parsing_number) * readNumber; // 4 above signal notParsingStringAndParsingNumberReadDelimeterOrReadNumberNotParsingNumber <== (1 - parsing_string) * (parsingNumberReadDelimeter + readNumberNotParsingNumber); // 10 above ^^^^^^^^^^^^^^^^^ 4 above ^^^^^^^^^^^^^^^ - signal temp <== parsing_number * (1 - readNumber.out) ; - signal parsingNumberNotReadNumberNotReadDelimeter <== temp * (1-readDelimeter.out); + signal temp <== parsing_number * (1 - readNumber) ; + signal parsingNumberNotReadNumberNotReadDelimeter <== temp * (1-readDelimeter); out[2] <== notParsingStringAndParsingNumberReadDelimeterOrReadNumberNotParsingNumber + parsingNumberNotReadNumberNotReadDelimeter; } @@ -155,6 +181,13 @@ template RewriteStack(n) { assert(n < 2**8); signal input stack[n][2]; signal input read_write_value; + signal input readStartBrace; + signal input readStartBracket; + signal input readEndBrace; + signal input readEndBracket; + signal input readColon; + signal input readComma; + signal output next_stack[n][2]; //-----------------------------------------------------------------------------// @@ -174,50 +207,29 @@ template RewriteStack(n) { //-----------------------------------------------------------------------------// //-----------------------------------------------------------------------------// - // * check what value was read * - // * read in a start brace `{` * - component readStartBrace = IsEqual(); - readStartBrace.in <== [read_write_value, 1]; - // * read in a start bracket `[` * - component readStartBracket = IsEqual(); - readStartBracket.in <== [read_write_value, 2]; - // * read in an end brace `}` * - component readEndBrace = IsEqual(); - readEndBrace.in <== [read_write_value, -1]; - // * read in an end bracket `]` * - component readEndBracket = IsEqual(); - readEndBracket.in <== [read_write_value, -2]; - // * read in a colon `:` * - component readColon = IsEqual(); - readColon.in[0] <== 3; - readColon.in[1] <== read_write_value; - // * read in a comma `,` * - component readComma = IsEqual(); - readComma.in[0] <== 4; - readComma.in[1] <== read_write_value; // * composite signals * - signal readEndChar <== readEndBrace.out + readEndBracket.out; - signal readCommaInArray <== readComma.out * inArray.out; - signal readCommaNotInArray <== readComma.out * (1 - inArray.out); + signal readEndChar <== readEndBrace + readEndBracket; + signal readCommaInArray <== readComma * inArray.out; + signal readCommaNotInArray <== readComma * (1 - inArray.out); //-----------------------------------------------------------------------------// //-----------------------------------------------------------------------------// // * determine whether we are pushing or popping from the stack * component isPush = IsEqual(); - isPush.in <== [readStartBrace.out + readStartBracket.out, 1]; + isPush.in <== [readStartBrace + readStartBracket, 1]; component isPop = IsEqual(); - isPop.in <== [readEndBrace.out + readEndBracket.out, 1]; + isPop.in <== [readEndBrace + readEndBracket, 1]; // * set an indicator array for where we are pushing to or popping from* component indicator[n]; for(var i = 0; i < n; i++) { // Points indicator[i] = IsZero(); - indicator[i].in <== pointer - isPop.out - readColon.out - readComma.out - i; // Note, pointer points to unallocated region! + indicator[i].in <== pointer - isPop.out - readColon - readComma - i; // Note, pointer points to unallocated region! } //-----------------------------------------------------------------------------// - signal stack_change_value[2] <== [(isPush.out + isPop.out) * read_write_value, readColon.out + readCommaInArray - readCommaNotInArray]; + signal stack_change_value[2] <== [(isPush.out + isPop.out) * read_write_value, readColon + readCommaInArray - readCommaNotInArray]; signal second_index_clear[n]; for(var i = 0; i < n; i++) { next_stack[i][0] <== stack[i][0] + indicator[i].out * stack_change_value[0]; From 6cfe96b139c8bbbb00576ba3ee4225689024477b Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 16 Aug 2024 16:28:36 -0600 Subject: [PATCH 93/99] lots of cleanup --- circuits/language.circom | 4 +- circuits/parser.circom | 107 +++++++++++++++------------------------ 2 files changed, 43 insertions(+), 68 deletions(-) diff --git a/circuits/language.circom b/circuits/language.circom index 3528c80..3aced66 100644 --- a/circuits/language.circom +++ b/circuits/language.circom @@ -33,9 +33,9 @@ template Command() { signal output NOTHING[3] <== [0, 0, -1 ]; // Command returned by switch if we want to do nothing, e.g. read a whitespace char while looking for a key signal output START_BRACE[3] <== [1, 0, 0 ]; // Command returned by switch if we hit a start brace `{` signal output END_BRACE[3] <== [-1, 0, -1 ]; // Command returned by switch if we hit a end brace `}` - signal output START_BRACKET[3] <== [2, 0, 0 ]; // TODO: Might want `in_value` to toggle. Command returned by switch if we hit a start bracket `[` (TODO: could likely be combined with end bracket) + signal output START_BRACKET[3] <== [2, 0, 0 ]; // Command returned by switch if we hit a start bracket `[` signal output END_BRACKET[3] <== [-2, 0, -1 ]; // Command returned by switch if we hit a start bracket `]` - signal output QUOTE[3] <== [0, 1, 0 ]; // TODO: Mightn ot want this to toglle `parsing_array`. Command returned by switch if we hit a quote `"` + signal output QUOTE[3] <== [0, 1, 0 ]; // Command returned by switch if we hit a quote `"` signal output COLON[3] <== [3, 0, 0 ]; // Command returned by switch if we hit a colon `:` signal output COMMA[3] <== [4, 0, -1 ]; // Command returned by switch if we hit a comma `,` signal output NUMBER[3] <== [256, 0, 1 ]; // Command returned by switch if we hit some decimal number (e.g., ASCII 48-57) diff --git a/circuits/parser.circom b/circuits/parser.circom index 42d879e..04f90ee 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -3,20 +3,12 @@ pragma circom 2.1.9; include "utils.circom"; include "language.circom"; -/* -TODO: OKAY, so one big thing to notice is that we are effectively doubling up (if not tripling up) on checking what byte we have just read. If we mess with the Commands, matcher, mask, and rewrite stack, I think we can reduce the times -we call these sorts of things and consolidate this greatly. Probably can cut constraints down by a factor of 2. -*/ - template StateUpdate(MAX_STACK_HEIGHT) { signal input byte; - signal input stack[MAX_STACK_HEIGHT][2]; // STACK -- how deep in a JSON nest we are and what type we are currently inside (e.g., `1` for object, `-1` for array). + signal input stack[MAX_STACK_HEIGHT][2]; signal input parsing_string; signal input parsing_number; - // TODO - // signal parsing_boolean; - // signal parsing_null; signal output next_stack[MAX_STACK_HEIGHT][2]; signal output next_parsing_string; @@ -57,20 +49,18 @@ template StateUpdate(MAX_STACK_HEIGHT) { readColon.in <== [matcher.out[0], 3]; // * read in a comma `,` * component readComma = IsEqual(); - readComma.in <== [matcher.out[0], 4]; - - component readDelimeter = Contains(6); - readDelimeter.in <== matcher.out[0]; - readDelimeter.array <== [1,-1,2,-2,3,4]; - + readComma.in <== [matcher.out[0], 4]; + component readDelimeter = Contains(6); + readDelimeter.in <== matcher.out[0]; + readDelimeter.array <== [1,-1,2,-2,3,4]; + //--------------------------------------------------------------------------------------------// + // Apply state changing data // * get the instruction mask based on current state * - component mask = StateToMask(MAX_STACK_HEIGHT); + component mask = StateToMask(MAX_STACK_HEIGHT); mask.readDelimeter <== readDelimeter.out; mask.readNumber <== readNumber.out; mask.parsing_string <== parsing_string; mask.parsing_number <== parsing_number; - - // * multiply the mask array elementwise with the instruction array * component mulMaskAndOut = ArrayMul(3); mulMaskAndOut.lhs <== mask.out; @@ -79,8 +69,7 @@ template StateUpdate(MAX_STACK_HEIGHT) { component addToState = ArrayAdd(3); addToState.lhs <== [0, parsing_string, parsing_number]; addToState.rhs <== mulMaskAndOut.out; - - // * set the new state * + // * compute the new stack * component newStack = RewriteStack(MAX_STACK_HEIGHT); newStack.stack <== stack; newStack.read_write_value <== addToState.out[0]; @@ -90,24 +79,10 @@ template StateUpdate(MAX_STACK_HEIGHT) { newStack.readEndBracket <== readEndBracket.out; newStack.readColon <== readColon.out; newStack.readComma <== readComma.out; - - + // * set all the next state of the parser * next_stack <== newStack.next_stack; next_parsing_string <== addToState.out[1]; next_parsing_number <== addToState.out[2]; - - //--------------------------------------------------------------------------------------------// - //-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_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?) - // component isMinusOne = IsEqual(); - // isMinusOne.in[0] <== -1; - // isMinusOne.in[1] <== next_tree_depth; - // isMinusOne.out === 0; //--------------------------------------------------------------------------------------------// } @@ -126,32 +101,28 @@ template StateToMask(n) { // `parsing_string` can change: out[1] <== 1 - 2 * parsing_string; - // // `parsing_number` can change: - - // log("readNumber: ", readNumber.out); - // component isParsingString = IsEqual(); - // isParsingString.in[0] <== parsing_string; - // isParsingString.in[1] <== 1; - // component isParsingNumber = IsEqual(); - // isParsingNumber.in[0] <== parsing_number; - // isParsingNumber.in[1] <== 1; - // component toParseNumber = Switch(16); - // // TODO: Could combine this into something that returns arrays so that we can set the mask more easily. - // toParseNumber.branches <== [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; - // toParseNumber.vals <== [0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]; // These cases are useful to think about - // component stateToNum = Bits2Num(4); - // stateToNum.in <== [isParsingString.out, isParsingNumber.out, readNumber.out, readDelimeter.out]; - // // 1 2 4 8 - // toParseNumber.case <== stateToNum.out; - - // out[2] <== toParseNumber.out; - signal parsingNumberReadDelimeter <== parsing_number * (readDelimeter); // 10 above used - signal readNumberNotParsingNumber <== (1 - parsing_number) * readNumber; // 4 above + + //--------------------------------------------------------------------------------------------// + // `parsing_number` is more complicated to deal with + /* We have the possible relevant states below: + [isParsingString, isParsingNumber, readNumber, readDelimeter]; + 1 2 4 8 + Above is the binary value for each if is individually enabled + This is a total of 2^4 states + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + [0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]; + and the above is what we want to set `next_parsing_number` to given those + possible. + Below is an optimized version that could instead be done with a `Switch` + */ + signal parsingNumberReadDelimeter <== parsing_number * (readDelimeter); + signal readNumberNotParsingNumber <== (1 - parsing_number) * readNumber; signal notParsingStringAndParsingNumberReadDelimeterOrReadNumberNotParsingNumber <== (1 - parsing_string) * (parsingNumberReadDelimeter + readNumberNotParsingNumber); - // 10 above ^^^^^^^^^^^^^^^^^ 4 above ^^^^^^^^^^^^^^^ + // 10 above ^^^^^^^^^^^^^^^^^ 4 above ^^^^^^^^^^^^^^^^^^ signal temp <== parsing_number * (1 - readNumber) ; signal parsingNumberNotReadNumberNotReadDelimeter <== temp * (1-readDelimeter); out[2] <== notParsingStringAndParsingNumberReadDelimeterOrReadNumberNotParsingNumber + parsingNumberNotReadNumberNotReadDelimeter; + // Sorry about the long names, but they hopefully read clearly! } // TODO: Check if underconstrained @@ -176,7 +147,7 @@ template GetTopOfStack(n) { pointer <== selector; } -// TODO: IMPORTANT NOTE, THE STACK IS CONSTRAINED TO 2**8 so the LessThan and GreaterThan work (could be changed) +// TODO: IMPORTANT NOTE, THE STACK IS CONSTRAINED TO 2**8 so the InRange work (could be changed) template RewriteStack(n) { assert(n < 2**8); signal input stack[n][2]; @@ -190,7 +161,7 @@ template RewriteStack(n) { signal output next_stack[n][2]; - //-----------------------------------------------------------------------------// + //--------------------------------------------------------------------------------------------// // * scan value on top of stack * component topOfStack = GetTopOfStack(n); topOfStack.stack <== stack; @@ -204,16 +175,16 @@ template RewriteStack(n) { component inArray = IsEqual(); inArray.in[0] <== current_value[0]; inArray.in[1] <== 2; - //-----------------------------------------------------------------------------// + //--------------------------------------------------------------------------------------------// - //-----------------------------------------------------------------------------// + //--------------------------------------------------------------------------------------------// // * composite signals * signal readEndChar <== readEndBrace + readEndBracket; signal readCommaInArray <== readComma * inArray.out; signal readCommaNotInArray <== readComma * (1 - inArray.out); - //-----------------------------------------------------------------------------// + //--------------------------------------------------------------------------------------------// - //-----------------------------------------------------------------------------// + //--------------------------------------------------------------------------------------------// // * determine whether we are pushing or popping from the stack * component isPush = IsEqual(); isPush.in <== [readStartBrace + readStartBracket, 1]; @@ -226,9 +197,10 @@ template RewriteStack(n) { indicator[i] = IsZero(); indicator[i].in <== pointer - isPop.out - readColon - readComma - i; // Note, pointer points to unallocated region! } - //-----------------------------------------------------------------------------// - + //--------------------------------------------------------------------------------------------// + //--------------------------------------------------------------------------------------------// + // * loop to modify the stack by rebuilding it * signal stack_change_value[2] <== [(isPush.out + isPop.out) * read_write_value, readColon + readCommaInArray - readCommaNotInArray]; signal second_index_clear[n]; for(var i = 0; i < n; i++) { @@ -236,10 +208,13 @@ template RewriteStack(n) { second_index_clear[i] <== stack[i][1] * readEndChar; next_stack[i][1] <== stack[i][1] + indicator[i].out * (stack_change_value[1] - second_index_clear[i]); } + //--------------------------------------------------------------------------------------------// - // TODO: WE CAN'T LEAVE 8 HERE, THIS HAS TO DEPEND ON THE STACK HEIGHT AS IT IS THE NUM BITS NEEDED TO REPR STACK HEIGHT IN BINARY + //--------------------------------------------------------------------------------------------// + // * check for under or overflow component isUnderflowOrOverflow = InRange(8); isUnderflowOrOverflow.in <== pointer - isPop.out + isPush.out; isUnderflowOrOverflow.range <== [0,n]; isUnderflowOrOverflow.out === 1; + //--------------------------------------------------------------------------------------------// } \ No newline at end of file From 65e3a6aa9c29df516cd02319b97bc3fd3651558f Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 16 Aug 2024 16:35:05 -0600 Subject: [PATCH 94/99] Delete mask.test.ts --- circuits/test/parser/mask.test.ts | 1 - 1 file changed, 1 deletion(-) delete mode 100644 circuits/test/parser/mask.test.ts diff --git a/circuits/test/parser/mask.test.ts b/circuits/test/parser/mask.test.ts deleted file mode 100644 index 0ffdd02..0000000 --- a/circuits/test/parser/mask.test.ts +++ /dev/null @@ -1 +0,0 @@ -// TODO \ No newline at end of file From 957922ac31c6e5371fda575632054294d61c6773 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 16 Aug 2024 16:39:47 -0600 Subject: [PATCH 95/99] example test --- circuits/test/parser/values.test.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/circuits/test/parser/values.test.ts b/circuits/test/parser/values.test.ts index 6661ad6..eb1c1f3 100644 --- a/circuits/test/parser/values.test.ts +++ b/circuits/test/parser/values.test.ts @@ -54,6 +54,21 @@ describe("StateUpdate :: Values", () => { inside_number_continue_out.next_parsing_number = 1; generatePassCase(inside_number_continue, inside_number_continue_out, ">>>> `1` read"); + //-TEST_3----------------------------------------------------------// + // idea: Inside a string key inside an object + // state: stack == [[1, 0], [0, 0], [0, 0], [0, 0]], parsing_string == 1 + // read: `,` + // expect: stack --> [[1, 0], [0, 0], [0, 0], [0, 0]] + // parsing_string --> 0 + let inside_number = { ...INITIAL_IN }; + inside_number.stack = [[1, 1], [0, 0], [0, 0], [0, 0]]; + inside_number.parsing_string = 1; + inside_number.byte = Delimiters.COMMA; + let inside_number_out = { ...INITIAL_OUT }; + inside_number_out.next_stack = [[1, 0], [0, 0], [0, 0], [0, 0]]; + inside_number_out.next_parsing_string = 1; + generatePassCase(inside_number, inside_number_out, ">>>> `,` read"); + describe("StateUpdate :: Values :: Array", () => { // Internal array parsing -----------------------------------------// From 8a7d334c1f079f1dd463b44f45797c89819869b2 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 22 Aug 2024 13:20:15 -0600 Subject: [PATCH 96/99] docs + refactor: `parser` Decreased `StateUpdate` from 332 constraints to 311 --- circuits/parser.circom | 70 ++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 04f90ee..5ed41b8 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -1,10 +1,30 @@ +/* +# `parser` +This module consists of the core parsing components for generating proofs of selective disclosure in JSON. + +## Layout +The key ingredients of `parser` are: + - `StateUpdate`: has as input a current state of a stack-machine parser. + Also takes in a `byte` as input which combines with the current state + to produce the `next_*` states. + - `StateToMask`: Reads the current state to decide whether accept instruction tokens + or ignore them for the current task (e.g., ignore `[` if `parsing_string == 1`). + - `GetTopOfStack`: Helper function that yields the topmost allocated stack value + and a pointer (index) to that value. + - `RewriteStack`: Combines all the above data and produces the `next_stack`. + +`parser` brings in many functions from the `utils` module and `language`. +The inclusion of `langauge` allows for this file to (eventually) be generic over +a grammar for different applications (e.g., HTTP, YAML, TOML, etc.). +*/ + pragma circom 2.1.9; include "utils.circom"; include "language.circom"; template StateUpdate(MAX_STACK_HEIGHT) { - signal input byte; + signal input byte; // TODO: Does this need to be constrained within here? signal input stack[MAX_STACK_HEIGHT][2]; signal input parsing_string; @@ -16,48 +36,46 @@ template StateUpdate(MAX_STACK_HEIGHT) { component Syntax = Syntax(); component Command = Command(); - - //--------------------------------------------------------------------------------------------// - // Read new byte - // * yield instruction based on what byte we read * - component matcher = SwitchArray(8, 3); - matcher.branches <== [Syntax.START_BRACE, Syntax.END_BRACE, Syntax.QUOTE, Syntax.COLON, Syntax.COMMA, Syntax.START_BRACKET, Syntax.END_BRACKET, Syntax.NUMBER ]; - matcher.vals <== [Command.START_BRACE, Command.END_BRACE, Command.QUOTE, Command.COLON, Command.COMMA, Command.START_BRACKET, Command.END_BRACKET, Command.NUMBER]; - component readNumber = InRange(8); - readNumber.in <== byte; - readNumber.range <== [48, 57]; // ASCII NUMERALS - signal IS_NUMBER <== readNumber.out * Syntax.NUMBER; - matcher.case <== (1 - readNumber.out) * byte + IS_NUMBER; // IF (NOT is_number) THEN byte ELSE 256 - //--------------------------------------------------------------------------------------------// //--------------------------------------------------------------------------------------------// // Break down what was read // * read in a start brace `{` * component readStartBrace = IsEqual(); - readStartBrace.in <== [matcher.out[0], 1]; - // * read in a start bracket `[` * - component readStartBracket = IsEqual(); - readStartBracket.in <== [matcher.out[0], 2]; + readStartBrace.in <== [byte, Syntax.START_BRACE]; // * read in an end brace `}` * component readEndBrace = IsEqual(); - readEndBrace.in <== [matcher.out[0], -1]; + readEndBrace.in <== [byte, Syntax.END_BRACE]; + // * read in a start bracket `[` * + component readStartBracket = IsEqual(); + readStartBracket.in <== [byte, Syntax.START_BRACKET]; // * read in an end bracket `]` * component readEndBracket = IsEqual(); - readEndBracket.in <== [matcher.out[0], -2]; + readEndBracket.in <== [byte, Syntax.END_BRACKET]; // * read in a colon `:` * component readColon = IsEqual(); - readColon.in <== [matcher.out[0], 3]; + readColon.in <== [byte, Syntax.COLON]; // * read in a comma `,` * component readComma = IsEqual(); - readComma.in <== [matcher.out[0], 4]; - component readDelimeter = Contains(6); - readDelimeter.in <== matcher.out[0]; - readDelimeter.array <== [1,-1,2,-2,3,4]; + readComma.in <== [byte, Syntax.COMMA]; + // * read in some delimeter * + signal readDelimeter <== readStartBrace.out + readEndBrace.out + readStartBracket.out + readEndBracket.out + + readColon.out + readComma.out; + // * read in some number * + component readNumber = InRange(8); + readNumber.in <== byte; + readNumber.range <== [48, 57]; // ASCII NUMERALS + signal isNumberSyntax <== readNumber.out * Syntax.NUMBER; + //--------------------------------------------------------------------------------------------// + // Yield instruction based on what byte we read * + component matcher = SwitchArray(8, 3); + matcher.branches <== [Syntax.START_BRACE, Syntax.END_BRACE, Syntax.QUOTE, Syntax.COLON, Syntax.COMMA, Syntax.START_BRACKET, Syntax.END_BRACKET, Syntax.NUMBER ]; + matcher.vals <== [Command.START_BRACE, Command.END_BRACE, Command.QUOTE, Command.COLON, Command.COMMA, Command.START_BRACKET, Command.END_BRACKET, Command.NUMBER]; + matcher.case <== (1 - readNumber.out) * byte + isNumberSyntax; // IF (NOT readNumber) THEN byte ELSE Syntax.NUMBER //--------------------------------------------------------------------------------------------// // Apply state changing data // * get the instruction mask based on current state * component mask = StateToMask(MAX_STACK_HEIGHT); - mask.readDelimeter <== readDelimeter.out; + mask.readDelimeter <== readDelimeter; mask.readNumber <== readNumber.out; mask.parsing_string <== parsing_string; mask.parsing_number <== parsing_number; From 91c35642b437f3f8ec47795fc1cd24976d8a86bb Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 22 Aug 2024 13:50:24 -0600 Subject: [PATCH 97/99] redo instruction Cut down to 252 constraints now for StateUpdate --- circuits/parser.circom | 73 +++++++++++++++++++++---------- circuits/test/utils/utils.test.ts | 20 +++++++++ circuits/utils.circom | 23 ++++++++++ 3 files changed, 93 insertions(+), 23 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index 5ed41b8..b36e2fa 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -40,37 +40,64 @@ template StateUpdate(MAX_STACK_HEIGHT) { //--------------------------------------------------------------------------------------------// // Break down what was read // * read in a start brace `{` * - component readStartBrace = IsEqual(); - readStartBrace.in <== [byte, Syntax.START_BRACE]; + component readStartBrace = IsEqual(); + readStartBrace.in <== [byte, Syntax.START_BRACE]; // * read in an end brace `}` * - component readEndBrace = IsEqual(); - readEndBrace.in <== [byte, Syntax.END_BRACE]; + component readEndBrace = IsEqual(); + readEndBrace.in <== [byte, Syntax.END_BRACE]; // * read in a start bracket `[` * - component readStartBracket = IsEqual(); - readStartBracket.in <== [byte, Syntax.START_BRACKET]; + component readStartBracket = IsEqual(); + readStartBracket.in <== [byte, Syntax.START_BRACKET]; // * read in an end bracket `]` * - component readEndBracket = IsEqual(); - readEndBracket.in <== [byte, Syntax.END_BRACKET]; + component readEndBracket = IsEqual(); + readEndBracket.in <== [byte, Syntax.END_BRACKET]; // * read in a colon `:` * - component readColon = IsEqual(); - readColon.in <== [byte, Syntax.COLON]; + component readColon = IsEqual(); + readColon.in <== [byte, Syntax.COLON]; // * read in a comma `,` * - component readComma = IsEqual(); - readComma.in <== [byte, Syntax.COMMA]; + component readComma = IsEqual(); + readComma.in <== [byte, Syntax.COMMA]; // * read in some delimeter * - signal readDelimeter <== readStartBrace.out + readEndBrace.out + readStartBracket.out + readEndBracket.out - + readColon.out + readComma.out; + signal readDelimeter <== readStartBrace.out + readEndBrace.out + readStartBracket.out + readEndBracket.out + + readColon.out + readComma.out; // * read in some number * - component readNumber = InRange(8); - readNumber.in <== byte; - readNumber.range <== [48, 57]; // ASCII NUMERALS - signal isNumberSyntax <== readNumber.out * Syntax.NUMBER; + component readNumber = InRange(8); + readNumber.in <== byte; + readNumber.range <== [48, 57]; // This is the range where ASCII digits are + component readQuote = IsEqual(); + readQuote.in <== [byte, Syntax.QUOTE]; //--------------------------------------------------------------------------------------------// // Yield instruction based on what byte we read * - component matcher = SwitchArray(8, 3); - matcher.branches <== [Syntax.START_BRACE, Syntax.END_BRACE, Syntax.QUOTE, Syntax.COLON, Syntax.COMMA, Syntax.START_BRACKET, Syntax.END_BRACKET, Syntax.NUMBER ]; - matcher.vals <== [Command.START_BRACE, Command.END_BRACE, Command.QUOTE, Command.COLON, Command.COMMA, Command.START_BRACKET, Command.END_BRACKET, Command.NUMBER]; - matcher.case <== (1 - readNumber.out) * byte + isNumberSyntax; // IF (NOT readNumber) THEN byte ELSE Syntax.NUMBER + component readStartBraceInstruction = ScalarArrayMul(3); + readStartBraceInstruction.scalar <== readStartBrace.out; + readStartBraceInstruction.array <== Command.START_BRACE; + component readEndBraceInstruction = ScalarArrayMul(3); + readEndBraceInstruction.scalar <== readEndBrace.out; + readEndBraceInstruction.array <== Command.END_BRACE; + component readStartBracketInstruction = ScalarArrayMul(3); + readStartBracketInstruction.scalar <== readStartBracket.out; + readStartBracketInstruction.array <== Command.START_BRACKET; + component readEndBracketInstruction = ScalarArrayMul(3); + readEndBracketInstruction.scalar <== readEndBracket.out; + readEndBracketInstruction.array <== Command.END_BRACKET; + component readColonInstruction = ScalarArrayMul(3); + readColonInstruction.scalar <== readColon.out; + readColonInstruction.array <== Command.COLON; + component readCommaInstruction = ScalarArrayMul(3); + readCommaInstruction.scalar <== readComma.out; + readCommaInstruction.array <== Command.COMMA; + component readNumberInstruction = ScalarArrayMul(3); + readNumberInstruction.scalar <== readNumber.out; + readNumberInstruction.array <== Command.NUMBER; + component readQuoteInstruction = ScalarArrayMul(3); + readQuoteInstruction.scalar <== readQuote.out; + readQuoteInstruction.array <== Command.QUOTE; + + component Instruction = GenericArrayAdd(3,8); + Instruction.arrays <== [readStartBraceInstruction.out, readEndBraceInstruction.out, + readStartBracketInstruction.out, readEndBracketInstruction.out, + readColonInstruction.out, readCommaInstruction.out, + readNumberInstruction.out, readQuoteInstruction.out]; //--------------------------------------------------------------------------------------------// // Apply state changing data // * get the instruction mask based on current state * @@ -82,7 +109,7 @@ template StateUpdate(MAX_STACK_HEIGHT) { // * multiply the mask array elementwise with the instruction array * component mulMaskAndOut = ArrayMul(3); mulMaskAndOut.lhs <== mask.out; - mulMaskAndOut.rhs <== matcher.out; + mulMaskAndOut.rhs <== Instruction.out; // * add the masked instruction to the state to get new state * component addToState = ArrayAdd(3); addToState.lhs <== [0, parsing_string, parsing_number]; diff --git a/circuits/test/utils/utils.test.ts b/circuits/test/utils/utils.test.ts index c92fd42..3721566 100644 --- a/circuits/test/utils/utils.test.ts +++ b/circuits/test/utils/utils.test.ts @@ -159,6 +159,26 @@ describe("ArrayMul", () => { }); +describe("GenericArrayAdd", () => { + let circuit: WitnessTester<["arrays"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`ArrayAdd`, { + file: "circuits/utils", + template: "GenericArrayAdd", + params: [3, 2], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("witness: arrays = [[0,1,2],[3,5,7]]", async () => { + await circuit.expectPass( + { arrays: [[0, 1, 2], [3, 5, 7]] }, + { out: [3, 6, 9] } + ); + }); + +}); + describe("InRange", () => { let circuit: WitnessTester<["in", "range"], ["out"]>; before(async () => { diff --git a/circuits/utils.circom b/circuits/utils.circom index 3a58c24..1112eba 100644 --- a/circuits/utils.circom +++ b/circuits/utils.circom @@ -109,6 +109,29 @@ template ArrayMul(n) { } } +template GenericArrayAdd(m,n) { + signal input arrays[n][m]; + signal output out[m]; + + var accum[m]; + for(var i = 0; i < m; i++) { + for(var j = 0; j < n; j++) { + accum[i] += arrays[j][i]; + } + } + out <== accum; +} + +template ScalarArrayMul(n) { + signal input array[n]; + signal input scalar; + signal output out[n]; + + for(var i = 0; i < n; i++) { + out[i] <== scalar * array[i]; + } +} + template InRange(n) { signal input in; signal input range[2]; From 167da256e5dafc26875442306e9be6c65dda7395 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 22 Aug 2024 14:45:52 -0600 Subject: [PATCH 98/99] add documentation --- circuits/parser.circom | 14 ++- circuits/utils.circom | 214 ++++++++++++++++++++++++++++++++--------- 2 files changed, 178 insertions(+), 50 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index b36e2fa..dd96289 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -16,6 +16,9 @@ The key ingredients of `parser` are: `parser` brings in many functions from the `utils` module and `language`. The inclusion of `langauge` allows for this file to (eventually) be generic over a grammar for different applications (e.g., HTTP, YAML, TOML, etc.). + +## Testing +Tests for this module are located in the files: `circuits/test/parser/*.test.ts */ pragma circom 2.1.9; @@ -64,6 +67,7 @@ template StateUpdate(MAX_STACK_HEIGHT) { component readNumber = InRange(8); readNumber.in <== byte; readNumber.range <== [48, 57]; // This is the range where ASCII digits are + // * read in a quote `"` * component readQuote = IsEqual(); readQuote.in <== [byte, Syntax.QUOTE]; //--------------------------------------------------------------------------------------------// @@ -164,8 +168,8 @@ template StateToMask(n) { signal readNumberNotParsingNumber <== (1 - parsing_number) * readNumber; signal notParsingStringAndParsingNumberReadDelimeterOrReadNumberNotParsingNumber <== (1 - parsing_string) * (parsingNumberReadDelimeter + readNumberNotParsingNumber); // 10 above ^^^^^^^^^^^^^^^^^ 4 above ^^^^^^^^^^^^^^^^^^ - signal temp <== parsing_number * (1 - readNumber) ; - signal parsingNumberNotReadNumberNotReadDelimeter <== temp * (1-readDelimeter); + signal parsingNumberNotReadNumber <== parsing_number * (1 - readNumber) ; + signal parsingNumberNotReadNumberNotReadDelimeter <== parsingNumberNotReadNumber * (1-readDelimeter); out[2] <== notParsingStringAndParsingNumberReadDelimeterOrReadNumberNotParsingNumber + parsingNumberNotReadNumberNotReadDelimeter; // Sorry about the long names, but they hopefully read clearly! } @@ -193,6 +197,9 @@ template GetTopOfStack(n) { } // TODO: IMPORTANT NOTE, THE STACK IS CONSTRAINED TO 2**8 so the InRange work (could be changed) +/* + +*/ template RewriteStack(n) { assert(n < 2**8); signal input stack[n][2]; @@ -224,7 +231,6 @@ template RewriteStack(n) { //--------------------------------------------------------------------------------------------// // * composite signals * - signal readEndChar <== readEndBrace + readEndBracket; signal readCommaInArray <== readComma * inArray.out; signal readCommaNotInArray <== readComma * (1 - inArray.out); //--------------------------------------------------------------------------------------------// @@ -250,7 +256,7 @@ template RewriteStack(n) { signal second_index_clear[n]; for(var i = 0; i < n; i++) { next_stack[i][0] <== stack[i][0] + indicator[i].out * stack_change_value[0]; - second_index_clear[i] <== stack[i][1] * readEndChar; + second_index_clear[i] <== stack[i][1] * (readEndBrace + readEndBracket); // Checking if we read some end char next_stack[i][1] <== stack[i][1] + indicator[i].out * (stack_change_value[1] - second_index_clear[i]); } //--------------------------------------------------------------------------------------------// diff --git a/circuits/utils.circom b/circuits/utils.circom index 1112eba..ed0f485 100644 --- a/circuits/utils.circom +++ b/circuits/utils.circom @@ -1,12 +1,42 @@ +/* +# `utils` +This module consists of helper templates for convencience. +It mostly extends the `bitify` and `comparators` modules from Circomlib. + +## Layout +The key ingredients of `utils` are: + - `ASCII`: Verify if a an input array contains valid ASCII values (e.g., u8 vals). + - `IsEqualArray`: Check if two arrays are equal component by component. + - `Contains`: Check if an element is contained in a given array. + - `ArrayAdd`: Add two arrays together component by component. + - `ArrayMul`: Multiply two arrays together component by component. + - `GenericArrayAdd`: Add together an arbitrary amount of arrays. + - `ScalarArrayMul`: Multiply each array element by a scalar value. + - `InRange`: Check if a given number is in a given range. + - `Switch`: Return a scalar value given a specific case. + - `SwitchArray`: Return an array given a specific case. + + +## Testing +Tests for this module are located in the file: `./test/utils/utils.test.ts` +*/ + pragma circom 2.1.9; include "circomlib/circuits/bitify.circom"; include "circomlib/circuits/comparators.circom"; + + /* -All tests for this file are located in: `./test/utils/utils.test.ts` -*/ +This template passes if a given array contains only valid ASCII values (e.g., u8 vals). + +# Params: + - `n`: the length of the array +# Inputs: + - `in[n]`: array to check +*/ template ASCII(n) { signal input in[n]; @@ -18,12 +48,16 @@ template ASCII(n) { } /* -This function is an indicator for two equal array inputs. +This template is an indicator for two equal array inputs. + +# Params: + - `n`: the length of arrays to compare # Inputs: -- `n`: the length of arrays to compare -- `in[2][n]`: two arrays of `n` numbers -- `out`: either `0` or `1` + - `in[2][n]`: two arrays of `n` numbers + +# Outputs: + - `out`: either `0` or `1` - `1` if `in[0]` is equal to `in[1]` as arrays (i.e., component by component) - `0` otherwise */ @@ -50,20 +84,24 @@ template IsEqualArray(n) { // TODO: There should be a way to have the below assertion come from the field itself. /* -This function is an indicator for if an array contains an element. +This template is an indicator for if an array contains an element. + +# Params: + - `n`: the size of the array to search through # Inputs: -- `n`: the size of the array to search through -- `in`: a number -- `array[n]`: the array we want to search through -- `out`: either `0` or `1` + - `in`: a number + - `array[n]`: the array we want to search through + +# Outputs: + - `out`: either `0` or `1` - `1` if `in` is found inside `array` - `0` otherwise */ template Contains(n) { assert(n > 0); /* - If `n = p` for this large `p`, then it could be that this function + If `n = p` for this large `p`, then it could be that this template returns the wrong value if every element in `array` was equal to `in`. This is EXTREMELY unlikely and iterating this high is impossible anyway. But it is better to check than miss something, so we bound it by `2**254` for now. @@ -89,6 +127,18 @@ template Contains(n) { out <== 1 - someEqual.out; } +/* +This template adds two arrays component by component. + +# Params: + - `n`: the length of arrays to compare + +# Inputs: + - `in[2][n]`: two arrays of `n` numbers + +# Outputs: + - `out[n]`: the array sum value +*/ template ArrayAdd(n) { signal input lhs[n]; signal input rhs[n]; @@ -99,6 +149,18 @@ template ArrayAdd(n) { } } +/* +This template multiplies two arrays component by component. + +# Params: + - `n`: the length of arrays to compare + +# Inputs: + - `in[2][n]`: two arrays of `n` numbers + +# Outputs: + - `out[n]`: the array multiplication value +*/ template ArrayMul(n) { signal input lhs[n]; signal input rhs[n]; @@ -109,6 +171,19 @@ template ArrayMul(n) { } } +/* +This template multiplies two arrays component by component. + +# Params: + - `m`: the length of the arrays to add + - `n`: the number of arrays to add + +# Inputs: + - `arrays[m][n]`: `n` arrays of `m` numbers + +# Outputs: + - `out[m]`: the sum of all the arrays +*/ template GenericArrayAdd(m,n) { signal input arrays[n][m]; signal output out[m]; @@ -122,6 +197,18 @@ template GenericArrayAdd(m,n) { out <== accum; } +/* +This template multiplies each component of an array by a scalar value. + +# Params: + - `n`: the length of the array + +# Inputs: + - `array[n]`: an array of `n` numbers + +# Outputs: + - `out[n]`: the scalar multiplied array +*/ template ScalarArrayMul(n) { signal input array[n]; signal input scalar; @@ -132,6 +219,20 @@ template ScalarArrayMul(n) { } } +/* +This template checks if a given `n`-bit value is contained in a range of `n`-bit values + +# Params: + - `n`: the number of bits to use + +# Inputs: + - `range[2]`: the lower and upper bound of the array, respectively + +# Outputs: + - `out`: either `0` or `1` + - `1` if `in` is within the range + - `0` otherwise +*/ template InRange(n) { signal input in; signal input range[2]; @@ -147,44 +248,42 @@ template InRange(n) { } /* -This function is creates an exhaustive switch statement from `0` up to `n`. +This template is creates an exhaustive switch statement from a list of branch values. +# Params: + - `n`: the number of switch cases # Inputs: -- `m`: the number of switch cases -- `n`: the output array length -- `case`: which case of the switch to select -- `branches[m]`: the values that enable taking different branches in the switch + - `case`: which case of the switch to select + - `branches[n]`: the values that enable taking different branches in the switch (e.g., if `branch[i] == 10` then if `case == 10` we set `out == `vals[i]`) -- `vals[m][n]`: the value that is emitted for a given switch case + - `vals[n]`: the value that is emitted for a given switch case (e.g., `val[i]` array is emitted on `case == `branch[i]`) # Outputs -- `match`: is set to `0` if `case` does not match on any of `branches` -- `out[n]`: the selected output value if one of `branches` is selected (will be `[0,0,...]` otherwise) + - `match`: is set to `0` if `case` does not match on any of `branches` + - `out[n]`: the selected output value if one of `branches` is selected (will be `0` otherwise) + ^^^^^^ BEWARE OF THIS FACT ABOVE! */ -template SwitchArray(m, n) { - assert(m > 0); +template Switch(n) { assert(n > 0); signal input case; - signal input branches[m]; - signal input vals[m][n]; + signal input branches[n]; + signal input vals[n]; signal output match; - signal output out[n]; + signal output out; // Verify that the `case` is in the possible set of branches - component indicator[m]; - component matchChecker = Contains(m); - signal component_out[m][n]; - var sum[n]; - for(var i = 0; i < m; i++) { + component indicator[n]; + component matchChecker = Contains(n); + signal temp_val[n]; + var sum; + for(var i = 0; i < n; i++) { indicator[i] = IsZero(); indicator[i].in <== case - branches[i]; matchChecker.array[i] <== 1 - indicator[i].out; - for(var j = 0; j < n; j++) { - component_out[i][j] <== indicator[i].out * vals[i][j]; - sum[j] += component_out[i][j]; - } + temp_val[i] <== indicator[i].out * vals[i]; + sum += temp_val[i]; } matchChecker.in <== 0; match <== matchChecker.out; @@ -192,29 +291,52 @@ template SwitchArray(m, n) { out <== sum; } -template Switch(n) { +/* +This template is creates an exhaustive switch statement from a list of branch values. +# Params: + - `m`: the number of switch cases + - `n`: the output array length + +# Inputs: + + - `case`: which case of the switch to select + - `branches[m]`: the values that enable taking different branches in the switch + (e.g., if `branch[i] == 10` then if `case == 10` we set `out == `vals[i]`) + - `vals[m][n]`: the value that is emitted for a given switch case + (e.g., `val[i]` array is emitted on `case == `branch[i]`) + +# Outputs + - `match`: is set to `0` if `case` does not match on any of `branches` + - `out[n]`: the selected output value if one of `branches` is selected (will be `[0,0,...]` otherwise) + ^^^^^^ BEWARE OF THIS FACT ABOVE! +*/ +template SwitchArray(m, n) { + assert(m > 0); assert(n > 0); signal input case; - signal input branches[n]; - signal input vals[n]; + signal input branches[m]; + signal input vals[m][n]; signal output match; - signal output out; + signal output out[n]; // Verify that the `case` is in the possible set of branches - component indicator[n]; - component matchChecker = Contains(n); - signal temp_val[n]; - var sum; - for(var i = 0; i < n; i++) { + component indicator[m]; + component matchChecker = Contains(m); + signal component_out[m][n]; + var sum[n]; + for(var i = 0; i < m; i++) { indicator[i] = IsZero(); indicator[i].in <== case - branches[i]; matchChecker.array[i] <== 1 - indicator[i].out; - temp_val[i] <== indicator[i].out * vals[i]; - sum += temp_val[i]; + for(var j = 0; j < n; j++) { + component_out[i][j] <== indicator[i].out * vals[i][j]; + sum[j] += component_out[i][j]; + } } matchChecker.in <== 0; match <== matchChecker.out; out <== sum; -} \ No newline at end of file +} + From 9c71481a94ecf7fd084f5893450b6f373db233d1 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 22 Aug 2024 15:03:30 -0600 Subject: [PATCH 99/99] Update parser.circom --- circuits/parser.circom | 73 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 7 deletions(-) diff --git a/circuits/parser.circom b/circuits/parser.circom index dd96289..068cb46 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -26,6 +26,23 @@ pragma circom 2.1.9; include "utils.circom"; include "language.circom"; +/* +This template is for updating the state of the parser from a current state to a next state. + +# Params: + - `MAX_STACK_HEIGHT`: the maximum stack height that can be used before triggering overflow. + +# Inputs: + - `byte` : the byte value of ASCII that was read by the parser. + - `stack[MAX_STACK_HEIGHT][2]`: the stack machine's current stack. + - `parsing_number` : a bool flag that indicates whether the parser is currently parsing a string or not. + - `parsing_number` : a bool flag that indicates whether the parser is currently parsing a number or not. + +# Outputs: + - `next_stack[MAX_STACK_HEIGHT][2]`: the stack machine's stack after reading `byte`. + - `next_parsing_number` : a bool flag that indicates whether the parser is currently parsing a string or not after reading `byte`. + - `next_parsing_number` : a bool flag that indicates whether the parser is currently parsing a number or not after reading `byte`. +*/ template StateUpdate(MAX_STACK_HEIGHT) { signal input byte; // TODO: Does this need to be constrained within here? @@ -114,14 +131,10 @@ template StateUpdate(MAX_STACK_HEIGHT) { component mulMaskAndOut = ArrayMul(3); mulMaskAndOut.lhs <== mask.out; mulMaskAndOut.rhs <== Instruction.out; - // * add the masked instruction to the state to get new state * - component addToState = ArrayAdd(3); - addToState.lhs <== [0, parsing_string, parsing_number]; - addToState.rhs <== mulMaskAndOut.out; // * compute the new stack * component newStack = RewriteStack(MAX_STACK_HEIGHT); newStack.stack <== stack; - newStack.read_write_value <== addToState.out[0]; + newStack.read_write_value <== mulMaskAndOut.out[0]; newStack.readStartBrace <== readStartBrace.out; newStack.readStartBracket <== readStartBracket.out; newStack.readEndBrace <== readEndBrace.out; @@ -130,11 +143,29 @@ template StateUpdate(MAX_STACK_HEIGHT) { newStack.readComma <== readComma.out; // * set all the next state of the parser * next_stack <== newStack.next_stack; - next_parsing_string <== addToState.out[1]; - next_parsing_number <== addToState.out[2]; + next_parsing_string <== parsing_string + mulMaskAndOut.out[1]; + next_parsing_number <== parsing_number + mulMaskAndOut.out[2]; //--------------------------------------------------------------------------------------------// } +/* +This template is for updating the state of the parser from a current state to a next state. + +# Params: + - `n`: tunable parameter for the number of `parsing_states` needed (TODO: could be removed). + +# Inputs: + - `readDelimeter` : a bool flag that indicates whether the byte value read was a delimeter. + - `readNumber` : a bool flag that indicates whether the byte value read was a number. + - `parsing_number`: a bool flag that indicates whether the parser is currently parsing a string or not. + - `parsing_number`: a bool flag that indicates whether the parser is currently parsing a number or not. + +# Outputs: + - `out[3]`: an array of values fed to update the stack and the parsing state flags. + - 0: mask for `read_write_value` + - 1: mask for `parsing_string` + - 2: mask for `parsing_number` +*/ template StateToMask(n) { // TODO: Probably need to assert things are bits where necessary. signal input readDelimeter; @@ -175,6 +206,19 @@ template StateToMask(n) { } // TODO: Check if underconstrained +/* +This template is for getting the values at the top of the stack as well as the pointer to the top. + +# Params: + - `n`: tunable parameter for the stack height. + +# Inputs: + - `stack[n][2]` : the stack to get the values and pointer of. + +# Outputs: + - `value[2]`: the value at the top of the stack + - `pointer` : the pointer for the top of stack index +*/ template GetTopOfStack(n) { signal input stack[n][2]; signal output value[2]; @@ -198,7 +242,22 @@ template GetTopOfStack(n) { // TODO: IMPORTANT NOTE, THE STACK IS CONSTRAINED TO 2**8 so the InRange work (could be changed) /* +This template is for updating the stack given the current stack and the byte we read in `StateUpdate`. + +# Params: + - `n`: tunable parameter for the number of bits needed to represent the `MAX_STACK_HEIGHT`. + +# Inputs: + - `read_write_value` : what value should be pushed to or popped from the stack. + - `readStartBrace` : a bool flag that indicates whether the byte value read was a start brace `{`. + - `readEndBrace` : a bool flag that indicates whether the byte value read was a end brace `}`. + - `readStartBracket` : a bool flag that indicates whether the byte value read was a start bracket `[`. + - `readEndBracket` : a bool flag that indicates whether the byte value read was a end bracket `]`. + - `readColon` : a bool flag that indicates whether the byte value read was a colon `:`. + - `readComma` : a bool flag that indicates whether the byte value read was a comma `,`. +# Outputs: + - `next_stack[n][2]`: the next stack of the parser. */ template RewriteStack(n) { assert(n < 2**8);