Skip to content

Commit

Permalink
support outputing matched reveal and start position based on index of…
Browse files Browse the repository at this point in the history
… matches
  • Loading branch information
katat committed Feb 5, 2023
1 parent c9a0ff3 commit e81c6eb
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 42 deletions.
2 changes: 1 addition & 1 deletion compiler/gen.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ async function generateCircuit(regex, circuitLibPath) {

const outputPath = `${__dirname}/../build/compiled.circom`;
await fs.writeFile(outputPath, tpl);
console.log(`Circuit compiled to ${path.normalize(outputPath)}`);
process.env.DEBUG && console.log(`Circuit compiled to ${path.normalize(outputPath)}`);
} catch (error) {
console.log(error)
}
Expand Down
66 changes: 49 additions & 17 deletions compiler/tpl.circom
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ pragma circom 2.0.3;

include "CIRCUIT_FOLDER/regex_helpers.circom";

template TEMPLATE_NAME_PLACEHOLDER (msg_bytes) {
template TEMPLATE_NAME_PLACEHOLDER (msg_bytes, reveal_bytes) {
signal input msg[msg_bytes];
signal input match_idx;
signal output start_idx;
signal output out;

signal output reveal_shifted[reveal_bytes][msg_bytes];

var num_bytes = msg_bytes;
signal in[num_bytes];
for (var i = 0; i < msg_bytes; i++) {
Expand All @@ -14,35 +18,63 @@ template TEMPLATE_NAME_PLACEHOLDER (msg_bytes) {

COMPILED_CONTENT_PLACEHOLDER

// a flag to indicate the start position of the match
var start_index = 0;
// for counting the number of matches
var count = 0;

// lengths to be consistent with states signal
component check_cur[num_bytes + 1];
component check_start[num_bytes + 1];
signal states_count[num_bytes + 1];
var count = 0;
component check_match[num_bytes + 1];
component check_start_index[num_bytes + 1];
component matched_idx_eq[msg_bytes];

// counting the matches by deterining the start positions of the matches
// note that the valid index of states signal starts from 1
for (var i = 0; i < num_bytes; i++) {
if (i == 0) {
check_cur[i] = IsEqual();
check_cur[i].in[0] <== states[1][1];
check_cur[i].in[1] <== 1;

count += states[1][1];
}
else {
check_cur[i] = IsEqual();
check_cur[i].in[0] <== states[i + 1][1];
check_cur[i].in[1] <== 1;

check_start[i] = AND();
check_start[i].a <== check_cur[i].out;
check_start[i].b <== 1 - check_cur[i-1].out;
check_start[i].a <== states[i + 1][1];
check_start[i].b <== 1 - states[i][1];

count += check_start[i].out;

check_match[i] = IsEqual();
check_match[i].in[0] <== count;
check_match[i].in[1] <== match_idx;

check_start_index[i] = AND();
check_start_index[i].a <== check_match[i].out;
check_start_index[i].b <== check_start[i].out;
start_index += check_start_index[i].out * i;
}

matched_idx_eq[i] = IsEqual();
matched_idx_eq[i].in[0] <== states[i + 1][1] * count;
matched_idx_eq[i].in[1] <== match_idx;
}

component match_start_index[msg_bytes];
for (var i = 0; i < msg_bytes; i++) {
match_start_index[i] = IsEqual();
match_start_index[i].in[0] <== i;
match_start_index[i].in[1] <== start_index;
}

signal reveal_match[msg_bytes];
for (var i = 0; i < msg_bytes; i++) {
reveal_match[i] <== matched_idx_eq[i].out * reveal[i];
}

for (var j = 0; j < reveal_bytes; j++) {
reveal_shifted[j][j] <== 0;
for (var i = j + 1; i < msg_bytes; i++) {
// This shifts matched string back to the beginning.
reveal_shifted[j][i] <== reveal_shifted[j][i - 1] + match_start_index[i-j].out * reveal_match[i];
}
states_count[i] <== states[i + 1][1] * count;
}

out <== count;
start_idx <== start_index;
}
5 changes: 3 additions & 2 deletions test/circuits/test_regex_compiler.circom
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ include "../../build/compiled.circom";

component main {
public [
msg
msg,
match_idx
]
} = Regex(1536);
} = Regex(1536, 44);
51 changes: 29 additions & 22 deletions test/regex-compiler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,44 @@ describe("regex compiler tests", function () {

[
[
'matches in the middle',
`email was meant for @(${generator.word_char}+)`,
'1st match in the middle',
[`email was meant for @(${generator.word_char}+)`, 1],
(signals: any) => {
for (let i = 28; i <= 32; i++) {
expect(signals.main.states_count[i]).to.equal(1n)
const to_reveal = 'katat'.split('').map((x: any) => BigInt(x.charCodeAt(0)))
for (let m in signals.main.reveal_shifted) {
const index = signals.main.reveal_shifted[m]
const last_pos = index.length - 1
if (to_reveal[m as any]) {
expect(index[last_pos]).to.equal(to_reveal[m as any])
}
}
for (let i = 67; i <= 71; i++) {
expect(signals.main.states_count[i]).to.equal(2n)
expect(signals.main.out).to.equal(2n)
expect(signals.main.start_idx).to.equal(28n)
}
],
[
'2nd match in the middle',
[`email was meant for @(${generator.word_char}+)`, 2],
(signals: any) => {
const to_reveal = 'katat'.split('').map((x: any) => BigInt(x.charCodeAt(0)))
for (let m in signals.main.reveal_shifted) {
const index = signals.main.reveal_shifted[m]
const last_pos = index.length - 1
if (to_reveal[m as any]) {
expect(index[last_pos]).to.equal(to_reveal[m as any])
}
}
expect(signals.main.out).to.equal(2n)
expect(signals.main.start_idx).to.equal(67n)
}
],
// [
// 'match at the beginning',
// `@${generator.word_char}+ email was meant`,
// (signals: any) => {
// console.log(signals.main.states_count)
// for (let i = 0; i < 6; i++) {
// expect(signals.main.states_count[i]).to.equal(1n)
// }
// // for (let i = 67; i <= 71; i++) {
// // expect(signals.main.states_count[i]).to.equal(2n)
// // }
// expect(signals.main.out).to.equal(2n)
// }
// ],
].forEach((test) => {
//@ts-ignore
const name: string = test[0]
//@ts-ignore
const regex: string = test[1]
const regex: string = test[1][0]
//@ts-ignore
const match_idx: number = test[1][1]
//@ts-ignore
const checkSignals: Function = test[2]

Expand All @@ -64,7 +71,7 @@ describe("regex compiler tests", function () {
});

it('checks witness', async function() {
let witness = await circuit.calculateWitness({msg: in_body_padded});
let witness = await circuit.calculateWitness({msg: in_body_padded, match_idx});
const signals = await circuit.getJSONOutput('main', witness);
checkSignals(signals)
await circuit.checkConstraints(witness);
Expand Down

0 comments on commit e81c6eb

Please sign in to comment.