diff --git a/circuits.json b/circuits.json index 3308dae..43017d8 100644 --- a/circuits.json +++ b/circuits.json @@ -7,6 +7,30 @@ 21 ] }, + "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 + ] + }, "test_extract_hard": { "file": "extract", "template": "Extract", diff --git a/circuits/extract.circom b/circuits/extract.circom index 2997cc7..76ebcae 100644 --- a/circuits/extract.circom +++ b/circuits/extract.circom @@ -24,35 +24,28 @@ 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].parsing_to_key <== 1; // Initialize by saying we are parsing to the first key - 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; + 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_to_key <== State[data_pointer - 1].next_parsing_to_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].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; - + 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; // 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("State[", data_pointer, "].end_of_kv", "= ", State[data_pointer].end_of_kv); log("---"); } 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 3eb446d..e4ec021 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -22,40 +22,30 @@ State[20]| " | COMPLETE WITH KV PARSING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ State[20].next_tree_depth == 0 | VALID JSON 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. */ /* 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 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_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 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 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 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; // 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`. - 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! + // 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---------------------------------------------------------------------------------// @@ -85,51 +75,67 @@ 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(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]; - 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. - // 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` - - 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] + //--------------------------------------------------------------------------------------------// + //-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 `,` + //--------------------------------------------------------------------------------------------// - signal NOT_PARSING_TO_KEY_AND_NOT_INSIDE_KEY <== (1 - parsing_to_key) * (1 - 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_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` - - - // 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` + //--------------------------------------------------------------------------------------------// + //-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; + // * 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]; + //--------------------------------------------------------------------------------------------// - // Constrain bit flags - next_parsing_to_key * (1 - next_parsing_to_key) === 0; // - constrain that `next_parsing_to_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_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 + //--------------------------------------------------------------------------------------------// + // // 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]); + // } + //--------------------------------------------------------------------------------------------// - // TODO: Can hit comma and then be sent to next KV, so comma will engage `parsing_to_key` + //--------------------------------------------------------------------------------------------// + //-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; //--------------------------------------------------------------------------------------------// } @@ -177,4 +183,37 @@ 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[5]; + signal output mask[5]; + + 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! + + // `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; + + // `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 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; } \ 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 5f3488d..3e6fbf2 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", () => { @@ -56,59 +57,213 @@ 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"] + ["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 generateTestCase(input: any, expected: any) { + function generatePassCase(input: any, expected: any, desc: string) { const description = Object.entries(input) .map(([key, value]) => `${key} = ${value}`) .join(", "); - it(`witness: ${description}`, async () => { + 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", }); console.log("#constraints:", await circuit.getConstraintCount()); + }); let init = { byte: 0, tree_depth: 0, - parsing_to_key: 1, + parsing_key: 0, inside_key: 0, - parsing_to_value: 0, + parsing_value: 0, inside_value: 0, - escaping: 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_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, - next_end_of_kv: init.end_of_kv }; - generateTestCase(init, out); + + // 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 = 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); + 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) + 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"); + + // 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.byte = space; + let in_key_out = { ...out }; + in_key_out.next_inside_key = 1; + 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_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.byte = quote; + let in_key_to_exit_out = { ...out }; + in_key_to_exit_out.next_tree_depth = 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"); }); }); diff --git a/create_witness/src/main.rs b/create_witness/src/main.rs index 469478a..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.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/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_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/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/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/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 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 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 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