Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add extra key index to match end quote #98

Merged
merged 1 commit into from
Oct 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions circuits/json/interpreter.circom
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ template MatchPaddedKey(n) {
}

signal isEndOfKeyEqualToQuote <== IsEqual()([endOfKeyAccum[n], 1]);
// log("isEndOfKeyEqualToQuote", isEndOfKeyEqualToQuote);

component totalEqual = IsEqual();
totalEqual.in[0] <== n;
Expand Down Expand Up @@ -448,19 +449,26 @@ template KeyMatchAtIndex(dataLen, maxKeyLen, index) {
signal input keyLen;
signal input parsing_key;

signal paddedKey[maxKeyLen + 1];
for (var i = 0 ; i < maxKeyLen ; i++) {
paddedKey[i] <== key[i];
}
paddedKey[maxKeyLen] <== 0;
// `"` -> 34

// start of key equal to quote
signal startOfKeyEqualToQuote <== IsEqual()([data[index - 1], 34]);
signal isParsingCorrectKey <== parsing_key * startOfKeyEqualToQuote;

// key matches
component isSubstringMatch = MatchPaddedKey(maxKeyLen);
isSubstringMatch.in[0] <== key;
component isSubstringMatch = MatchPaddedKey(maxKeyLen+1);
isSubstringMatch.in[0] <== paddedKey;
isSubstringMatch.keyLen <== keyLen;
for(var matcher_idx = 0; matcher_idx < maxKeyLen; matcher_idx++) {
for(var matcher_idx = 0; matcher_idx <= maxKeyLen; matcher_idx++) {
// log("matcher_idx", index, matcher_idx, data[index + matcher_idx]);
isSubstringMatch.in[1][matcher_idx] <== data[index + matcher_idx];
}
// log("keyMatchAtIndex", isParsingCorrectKey, isSubstringMatch.out);

signal output out <== isSubstringMatch.out * isParsingCorrectKey;
}
21 changes: 14 additions & 7 deletions circuits/json/nivc/masker.circom
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,23 @@ template JsonMaskObjectNIVC(DATA_BYTES, MAX_STACK_HEIGHT, MAX_KEY_LENGTH) {
// Total number of variables in the parser for each byte of data
assert(MAX_STACK_HEIGHT >= 2);
var PER_ITERATION_DATA_LENGTH = MAX_STACK_HEIGHT * 2 + 2;
var TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES * (PER_ITERATION_DATA_LENGTH + 1) + 1;
var TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES * (PER_ITERATION_DATA_LENGTH + 1) + 1;
// ------------------------------------------------------------------------------------------------------------------ //

// ------------------------------------------------------------------------------------------------------------------ //
// ~ Unravel from previous NIVC step ~
// Read in from previous NIVC step (JsonParseNIVC)
signal input step_in[TOTAL_BYTES_ACROSS_NIVC];
signal input step_in[TOTAL_BYTES_ACROSS_NIVC];

// Grab the raw data bytes from the `step_in` variable
signal data[DATA_BYTES];
var paddedDataLen = DATA_BYTES + MAX_KEY_LENGTH + 1;
signal data[paddedDataLen];
for (var i = 0 ; i < DATA_BYTES ; i++) {
data[i] <== step_in[i];
}
for (var i = 0 ; i <= MAX_KEY_LENGTH ; i++) {
data[DATA_BYTES + i] <== 0;
}

// Decode the encoded data in `step_in` back into parser variables
signal stack[DATA_BYTES][MAX_STACK_HEIGHT][2];
Expand All @@ -39,7 +43,7 @@ template JsonMaskObjectNIVC(DATA_BYTES, MAX_STACK_HEIGHT, MAX_KEY_LENGTH) {
// ~ Object masking ~
// Key data to use to point to which object to extract
signal input key[MAX_KEY_LENGTH];
signal input keyLen;
signal input keyLen;

// Signals to detect if we are parsing a key or value with initial setup
signal parsing_key[DATA_BYTES - MAX_KEY_LENGTH];
Expand All @@ -56,7 +60,7 @@ template JsonMaskObjectNIVC(DATA_BYTES, MAX_STACK_HEIGHT, MAX_KEY_LENGTH) {
// Initialize values knowing 0th bit of data will never be a key/value
parsing_key[0] <== 0;
parsing_value[0] <== 0;
is_key_match[0] <== 0;
is_key_match[0] <== 0;

component stackSelector[DATA_BYTES];
stackSelector[0] = ArraySelector(MAX_STACK_HEIGHT, 2);
Expand All @@ -76,6 +80,7 @@ template JsonMaskObjectNIVC(DATA_BYTES, MAX_STACK_HEIGHT, MAX_KEY_LENGTH) {
stackSelector[data_idx].in <== stack[data_idx];
stackSelector[data_idx].index <== step_in[TOTAL_BYTES_ACROSS_NIVC - 1];

log("step_in[", data_idx, "] =", step_in[data_idx]);
log("stackSelector[", data_idx, "].out[0] = ", stackSelector[data_idx].out[0]);
log("stackSelector[", data_idx, "].out[1] = ", stackSelector[data_idx].out[1]);

Expand All @@ -90,17 +95,19 @@ template JsonMaskObjectNIVC(DATA_BYTES, MAX_STACK_HEIGHT, MAX_KEY_LENGTH) {
// - key matches at current index and depth of key is as specified
// - whether next KV pair starts
// - whether key matched for a value (propogate key match until new KV pair of lower depth starts)
is_key_match[data_idx] <== KeyMatchAtIndex(DATA_BYTES, MAX_KEY_LENGTH, data_idx)(data, key, keyLen, parsing_key[data_idx]);
is_key_match[data_idx] <== KeyMatchAtIndex(paddedDataLen, MAX_KEY_LENGTH, data_idx)(data, key, keyLen, parsing_key[data_idx]);
is_next_pair_at_depth[data_idx] <== NextKVPairAtDepth(MAX_STACK_HEIGHT)(stack[data_idx], data[data_idx], step_in[TOTAL_BYTES_ACROSS_NIVC - 1]);

log("is_key_match[", data_idx, "] = ", is_key_match[data_idx]);
log("is_next_pair_at_depth[", data_idx, "] = ", is_next_pair_at_depth[data_idx]);

is_key_match_for_value[data_idx+1] <== Mux1()([is_key_match_for_value[data_idx] * (1-is_next_pair_at_depth[data_idx]), is_key_match[data_idx] * (1-is_next_pair_at_depth[data_idx])], is_key_match[data_idx]);
is_value_match[data_idx] <== is_key_match_for_value[data_idx+1] * parsing_value[data_idx];

// Set the next NIVC step to only have the masked data
log("is_value_match", is_value_match[data_idx]);
step_out[data_idx] <== data[data_idx] * is_value_match[data_idx];
log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
}
for (var i = 0 ; i < MAX_KEY_LENGTH ; i++) {
step_out[DATA_BYTES - MAX_KEY_LENGTH + i] <== 0;
Expand Down
29 changes: 19 additions & 10 deletions circuits/test/json/nivc/masker_nivc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { join } from "path";
// Notes:
// - "data"'s object appears at byte 14
// - colon after "items" appears at byte 31
// - 0th index of arr appears at byte 47
// - 0th index of arr appears at byte 47
// - byte 64 is `"` for the data inside the array obj
// - byte 81 is where `Artist",` ends
// - byte 100 is where `"profile"` starts
Expand All @@ -46,6 +46,7 @@ let nivc_parse = readJsonFile<NIVCData>(join(__dirname, "..", "nivc/nivc_parse.j
let nivc_extract_key0 = readJsonFile<NIVCData>(join(__dirname, "..", "nivc/nivc_extract_key0.json"));
let nivc_extract_key1 = readJsonFile<NIVCData>(join(__dirname, "..", "nivc/nivc_extract_key1.json"));
let nivc_extract_arr = readJsonFile<NIVCData>(join(__dirname, "..", "nivc/nivc_extract_arr.json"));
let nivc_extract_key3 = readJsonFile<NIVCData>(join(__dirname, "..", "nivc/nivc_extract_key3.json"));

describe("JsonParseNIVC", async () => {
let circuit: WitnessTester<["step_in"], ["step_out"]>;
Expand Down Expand Up @@ -77,7 +78,7 @@ describe("JsonParseNIVC", async () => {

let extended_json_input = json_input.concat(Array(Math.max(0, TOTAL_BYTES_ACROSS_NIVC - json_input.length)).fill(0));

await generatePassCase({ step_in: extended_json_input }, { step_out: nivc_parse.step_out }, "parsing JSON");
generatePassCase({ step_in: extended_json_input }, { step_out: nivc_parse.step_out }, "parsing JSON");

});

Expand All @@ -87,6 +88,7 @@ describe("JsonMaskObjectNIVC", async () => {
let DATA_BYTES = 202;
let MAX_STACK_HEIGHT = 5;
let MAX_KEY_LENGTH = 7;
let step_out: bigint[] = [];

before(async () => {
circuit = await circomkit.WitnessTester(`JsonMaskObjectNIVC`, {
Expand All @@ -100,24 +102,31 @@ describe("JsonMaskObjectNIVC", async () => {
function generatePassCase(input: any, expected: any, desc: string) {
const description = generateDescription(input);

it(`(valid) witness: ${description} ${desc}`, async () => {
it(`(valid) witness: ${desc}`, async () => {
// console.log(JSON.stringify(await circuit.compute(input, ["step_out"])))
await circuit.expectPass(input, expected);
let wit = await circuit.calculateWitness(input);
console.log("wit", wit.slice(0, 100));
// step_out = wit;
// await circuit.expectPass(input, expected);
});
}

// let key0 = [100, 97, 116, 97, 0, 0, 0]; // "data"
// let key0Len = 4;
// generatePassCase({ step_in: nivc_parse.step_out, key: key0, keyLen: key0Len }, { step_out: nivc_extract_key0.step_out }, "masking json object at depth 0");

// let key1 = [105, 116, 101, 109, 115, 0, 0]; // "items"
// let key1Len = 5;
// generatePassCase({ step_in: nivc_extract_key0.step_out, key: key1, keyLen: key1Len }, { step_out: nivc_extract_key1.step_out }, "masking json object at depth 0");
let key1 = [105, 116, 101, 109, 115, 0, 0]; // "items"
let key1Len = 5;
generatePassCase({ step_in: nivc_extract_key0.step_out, key: key1, keyLen: key1Len }, { step_out: nivc_extract_key1.step_out }, "masking json object at depth 0");

// Ran after doing arr masking
let key2 = [112, 114, 111, 102, 105, 108, 101]; // "profile"
let key2Len = 7;
generatePassCase({ step_in: nivc_extract_arr.step_out, key: key2, keyLen: key2Len }, { step_out: nivc_extract_key1.step_out }, "masking json object at depth 0");
// let key2 = [112, 114, 111, 102, 105, 108, 101]; // "profile"
// let key2Len = 7;
// generatePassCase({ step_in: nivc_extract_arr.step_out, key: key2, keyLen: key2Len }, { step_out: nivc_extract_key1.step_out }, "masking json object at depth 0");

// let key3 = [110, 97, 109, 101, 0, 0, 0]; // "name"
// let key3Len = 4;
// generatePassCase({ step_in: nivc_extract_key3.step_out, key: key3, keyLen: key3Len }, {}, "masking json at depth 4");
});

describe("JsonMaskArrayIndexNIVC", async () => {
Expand Down
Loading