diff --git a/README.md b/README.md
index 1ab1b13..306e3b9 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ By leveraging ZK Regex, developers can securely verify the presence of specific
1. Start by providing a raw or parsed (expanded) regex pattern, then execute the following command:
```sh
- npm run zk-regex ''
+ npm run zk-regex ''
```
**NOTE:** `countEnabled` and `substringEnabled` arguments are set to **false** by default.
@@ -23,7 +23,7 @@ By leveraging ZK Regex, developers can securely verify the presence of specific
2. In your TypeScript file, import the necessary modules at the top:
```typescript
- import { Field, Bool } from 'o1js';
+ import { Field, Bool, UInt8 } from 'o1js';
```
3. Add the function body and paste your specific ZK Regex circuit code.
@@ -115,6 +115,12 @@ npm run testw # watch mode
npm run coverage
```
+## How to run zkApp example
+
+```sh
+npm run zkapp
+```
+
## Raw Regex Syntax Guide
- **Alteration:** The | character can be used to denote alternation between two expressions. For example: A|B.
@@ -182,6 +188,8 @@ For more details, you can visit this amazing [ZK Regex Tools](https://zkregex.co
- A huge thanks to the [zkemail](https://github.com/zkemail) team for their magnificent work on the original [zk-regex](https://github.com/zkemail/zk-regex), which inspired and aided in the development of this project. Their reference materials and documentation were invaluable in understanding the technical intricacies.
+- Special thanks to [Gregor Mitscha-Baude](https://twitter.com/mitschabaude) for identifying the inefficiency in processing full field elements. This change significantly improved the efficiency of the o1js-regex circuits.
+
## License
[Apache-2.0](LICENSE)
diff --git a/images/alpha-low-zk-regex.png b/images/alpha-low-zk-regex.png
index e5bf2d6..3012d44 100644
Binary files a/images/alpha-low-zk-regex.png and b/images/alpha-low-zk-regex.png differ
diff --git a/src/examples.ts b/src/examples.ts
index ede205c..fa156ab 100644
--- a/src/examples.ts
+++ b/src/examples.ts
@@ -1,4 +1,4 @@
-import { Bool, Field } from 'o1js';
+import { Bool, Field, UInt8 } from 'o1js';
export {
simpleRegex,
@@ -8,8 +8,8 @@ export {
negateRegex,
}
-// "1=(a|b) (2=(b|c)+ )+d" & revealing ['(a|b)', '(b|c)', 'd']
-function simpleRegex(input: Field[]) {
+// "1=(a|b) (2=(b|c)+ )+d" & revealing ["(a|b)", "(b|c)", "d"]
+function simpleRegex(input: UInt8[]) {
const num_bytes = input.length;
let states: Bool[][] = Array.from({ length: num_bytes + 1 }, () => []);
let state_changed: Bool[] = Array.from({ length: num_bytes }, () => Bool(false));
@@ -20,27 +20,27 @@ function simpleRegex(input: Field[]) {
}
for (let i = 0; i < num_bytes; i++) {
- const eq0 = input[i].equals(49);
+ const eq0 = input[i].value.equals(49);
const and0 = states[i][0].and(eq0);
states[i+1][1] = and0;
state_changed[i] = state_changed[i].or(states[i+1][1]);
- const eq1 = input[i].equals(61);
+ const eq1 = input[i].value.equals(61);
const and1 = states[i][1].and(eq1);
states[i+1][2] = and1;
state_changed[i] = state_changed[i].or(states[i+1][2]);
- const eq2 = input[i].equals(97);
- const eq3 = input[i].equals(98);
+ const eq2 = input[i].value.equals(97);
+ const eq3 = input[i].value.equals(98);
let multi_or0 = Bool(false);
multi_or0 = multi_or0.or(eq2);
multi_or0 = multi_or0.or(eq3);
const and2 = states[i][2].and(multi_or0);
states[i+1][3] = and2;
state_changed[i] = state_changed[i].or(states[i+1][3]);
- const eq4 = input[i].equals(32);
+ const eq4 = input[i].value.equals(32);
const and3 = states[i][3].and(eq4);
states[i+1][4] = and3;
state_changed[i] = state_changed[i].or(states[i+1][4]);
- const eq5 = input[i].equals(50);
+ const eq5 = input[i].value.equals(50);
const and4 = states[i][4].and(eq5);
const and5 = states[i][8].and(eq5);
let multi_or1 = Bool(false);
@@ -51,7 +51,7 @@ function simpleRegex(input: Field[]) {
const and6 = states[i][5].and(eq1);
states[i+1][6] = and6;
state_changed[i] = state_changed[i].or(states[i+1][6]);
- const eq6 = input[i].equals(99);
+ const eq6 = input[i].value.equals(99);
let multi_or2 = Bool(false);
multi_or2 = multi_or2.or(eq3);
multi_or2 = multi_or2.or(eq6);
@@ -65,7 +65,7 @@ function simpleRegex(input: Field[]) {
const and9 = states[i][7].and(eq4);
states[i+1][8] = and9;
state_changed[i] = state_changed[i].or(states[i+1][8]);
- const eq7 = input[i].equals(100);
+ const eq7 = input[i].value.equals(100);
const and10 = states[i][8].and(eq7);
states[i+1][9] = and10;
state_changed[i] = state_changed[i].or(states[i+1][9]);
@@ -76,7 +76,6 @@ function simpleRegex(input: Field[]) {
for (let i = 0; i <= num_bytes; i++) {
final_state_result = final_state_result.or(states[i][9]);
}
-
const out = final_state_result;
const msg_bytes = num_bytes - 1;
@@ -98,7 +97,7 @@ function simpleRegex(input: Field[]) {
is_substr0[i][0] = Bool(false);
is_substr0[i][1] = is_substr0[i][0].or(states[i+1][2].and(states[i+2][3]));
is_reveal0[i] = is_substr0[i][1].and(is_consecutive[i][1]);
- reveal0[i] = input[i+1].mul(is_reveal0[i].toField());
+ reveal0[i] = input[i+1].value.mul(is_reveal0[i].toField());
}
reveal.push(reveal0);
@@ -111,7 +110,7 @@ function simpleRegex(input: Field[]) {
is_substr1[i][1] = is_substr1[i][0].or(states[i+1][6].and(states[i+2][7]));
is_substr1[i][2] = is_substr1[i][1].or(states[i+1][7].and(states[i+2][7]));
is_reveal1[i] = is_substr1[i][2].and(is_consecutive[i][1]);
- reveal1[i] = input[i+1].mul(is_reveal1[i].toField());
+ reveal1[i] = input[i+1].value.mul(is_reveal1[i].toField());
}
reveal.push(reveal1);
@@ -123,16 +122,16 @@ function simpleRegex(input: Field[]) {
is_substr2[i][0] = Bool(false);
is_substr2[i][1] = is_substr2[i][0].or(states[i+1][8].and(states[i+2][9]));
is_reveal2[i] = is_substr2[i][1].and(is_consecutive[i][1]);
- reveal2[i] = input[i+1].mul(is_reveal2[i].toField());
+ reveal2[i] = input[i+1].value.mul(is_reveal2[i].toField());
}
reveal.push(reveal2);
return { out, reveal };
}
-// "([a-zA-Z0-9._%-=]+@[a-zA-Z0-9-]+.[a-z]+)" & revealing ['[a-zA-Z0-9._%-=]', '[a-zA-Z0-9-]', '[a-z]']
+// "([a-zA-Z0-9._%-=]+@[a-zA-Z0-9-]+.[a-z]+)" & revealing ["[a-zA-Z0-9._%-=]", "[a-zA-Z0-9-]", "[a-z]"]
// Note: this is not the perfect regex pattern for an email: this is just for testing purposes!
-function emailRegex(input: Field[]) {
+function emailRegex(input: UInt8[]) {
const num_bytes = input.length;
let states: Bool[][] = Array.from({ length: num_bytes + 1 }, () => []);
let state_changed: Bool[] = Array.from({ length: num_bytes }, () => Bool(false));
@@ -143,27 +142,27 @@ function emailRegex(input: Field[]) {
}
for (let i = 0; i < num_bytes; i++) {
- const lt0 = Field(65).lessThanOrEqual(input[i]);
+ const lt0 = new UInt8(65).lessThanOrEqual(input[i]);
const lt1 = input[i].lessThanOrEqual(90);
const and0 = lt0.and(lt1);
- const lt2 = Field(97).lessThanOrEqual(input[i]);
+ const lt2 = new UInt8(97).lessThanOrEqual(input[i]);
const lt3 = input[i].lessThanOrEqual(122);
const and1 = lt2.and(lt3);
- const eq0 = input[i].equals(37);
- const eq1 = input[i].equals(45);
- const eq2 = input[i].equals(46);
- const eq3 = input[i].equals(48);
- const eq4 = input[i].equals(49);
- const eq5 = input[i].equals(50);
- const eq6 = input[i].equals(51);
- const eq7 = input[i].equals(52);
- const eq8 = input[i].equals(53);
- const eq9 = input[i].equals(54);
- const eq10 = input[i].equals(55);
- const eq11 = input[i].equals(56);
- const eq12 = input[i].equals(57);
- const eq13 = input[i].equals(61);
- const eq14 = input[i].equals(95);
+ const eq0 = input[i].value.equals(37);
+ const eq1 = input[i].value.equals(45);
+ const eq2 = input[i].value.equals(46);
+ const eq3 = input[i].value.equals(48);
+ const eq4 = input[i].value.equals(49);
+ const eq5 = input[i].value.equals(50);
+ const eq6 = input[i].value.equals(51);
+ const eq7 = input[i].value.equals(52);
+ const eq8 = input[i].value.equals(53);
+ const eq9 = input[i].value.equals(54);
+ const eq10 = input[i].value.equals(55);
+ const eq11 = input[i].value.equals(56);
+ const eq12 = input[i].value.equals(57);
+ const eq13 = input[i].value.equals(61);
+ const eq14 = input[i].value.equals(95);
let multi_or0 = Bool(false);
multi_or0 = multi_or0.or(and0);
multi_or0 = multi_or0.or(and1);
@@ -189,7 +188,7 @@ function emailRegex(input: Field[]) {
multi_or1 = multi_or1.or(and3);
states[i+1][1] = multi_or1;
state_changed[i] = state_changed[i].or(states[i+1][1]);
- const eq15 = input[i].equals(64);
+ const eq15 = input[i].value.equals(64);
const and4 = states[i][1].and(eq15);
states[i+1][2] = and4;
state_changed[i] = state_changed[i].or(states[i+1][2]);
@@ -231,7 +230,6 @@ function emailRegex(input: Field[]) {
for (let i = 0; i <= num_bytes; i++) {
final_state_result = final_state_result.or(states[i][5]);
}
-
const out = final_state_result;
const msg_bytes = num_bytes - 1;
@@ -254,9 +252,9 @@ function emailRegex(input: Field[]) {
is_substr0[i][1] = is_substr0[i][0].or(states[i+1][0].and(states[i+2][1]));
is_substr0[i][2] = is_substr0[i][1].or(states[i+1][1].and(states[i+2][1]));
is_reveal0[i] = is_substr0[i][2].and(is_consecutive[i][1]);
- reveal0[i] = input[i+1].mul(is_reveal0[i].toField());
+ reveal0[i] = input[i+1].value.mul(is_reveal0[i].toField());
}
- reveal0.unshift(input[0].mul(states[1][1].toField()));
+ reveal0.unshift(input[0].value.mul(states[1][1].toField()));
reveal.push(reveal0);
// the 1-th substring transitions: [[2,3],[3,3]]
@@ -268,7 +266,7 @@ function emailRegex(input: Field[]) {
is_substr1[i][1] = is_substr1[i][0].or(states[i+1][2].and(states[i+2][3]));
is_substr1[i][2] = is_substr1[i][1].or(states[i+1][3].and(states[i+2][3]));
is_reveal1[i] = is_substr1[i][2].and(is_consecutive[i][1]);
- reveal1[i] = input[i+1].mul(is_reveal1[i].toField());
+ reveal1[i] = input[i+1].value.mul(is_reveal1[i].toField());
}
reveal.push(reveal1);
@@ -281,15 +279,15 @@ function emailRegex(input: Field[]) {
is_substr2[i][1] = is_substr2[i][0].or(states[i+1][4].and(states[i+2][5]));
is_substr2[i][2] = is_substr2[i][1].or(states[i+1][5].and(states[i+2][5]));
is_reveal2[i] = is_substr2[i][2].and(is_consecutive[i][1]);
- reveal2[i] = input[i+1].mul(is_reveal2[i].toField());
+ reveal2[i] = input[i+1].value.mul(is_reveal2[i].toField());
}
reveal.push(reveal2);
return { out, reveal };
}
-// ([a-zA-Z0-9]|\\+|/|=)+ & revealing all
-function base64Regex(input: Field[]) {
+// "([a-zA-Z0-9]|\\+|/|=)+" & revealing all
+function base64Regex(input: UInt8[]) {
const num_bytes = input.length;
let states: Bool[][] = Array.from({ length: num_bytes + 1 }, () => []);
let state_changed: Bool[] = Array.from({ length: num_bytes }, () => Bool(false));
@@ -300,25 +298,25 @@ function base64Regex(input: Field[]) {
}
for (let i = 0; i < num_bytes; i++) {
- const lt0 = Field(65).lessThanOrEqual(input[i]);
+ const lt0 = new UInt8(65).lessThanOrEqual(input[i]);
const lt1 = input[i].lessThanOrEqual(90);
const and0 = lt0.and(lt1);
- const lt2 = Field(97).lessThanOrEqual(input[i]);
+ const lt2 = new UInt8(97).lessThanOrEqual(input[i]);
const lt3 = input[i].lessThanOrEqual(122);
const and1 = lt2.and(lt3);
- const eq0 = input[i].equals(43);
- const eq1 = input[i].equals(47);
- const eq2 = input[i].equals(48);
- const eq3 = input[i].equals(49);
- const eq4 = input[i].equals(50);
- const eq5 = input[i].equals(51);
- const eq6 = input[i].equals(52);
- const eq7 = input[i].equals(53);
- const eq8 = input[i].equals(54);
- const eq9 = input[i].equals(55);
- const eq10 = input[i].equals(56);
- const eq11 = input[i].equals(57);
- const eq12 = input[i].equals(61);
+ const eq0 = input[i].value.equals(43);
+ const eq1 = input[i].value.equals(47);
+ const eq2 = input[i].value.equals(48);
+ const eq3 = input[i].value.equals(49);
+ const eq4 = input[i].value.equals(50);
+ const eq5 = input[i].value.equals(51);
+ const eq6 = input[i].value.equals(52);
+ const eq7 = input[i].value.equals(53);
+ const eq8 = input[i].value.equals(54);
+ const eq9 = input[i].value.equals(55);
+ const eq10 = input[i].value.equals(56);
+ const eq11 = input[i].value.equals(57);
+ const eq12 = input[i].value.equals(61);
let multi_or0 = Bool(false);
multi_or0 = multi_or0.or(and0);
multi_or0 = multi_or0.or(and1);
@@ -372,16 +370,16 @@ function base64Regex(input: Field[]) {
is_substr0[i][1] = is_substr0[i][0].or(states[i+1][0].and(states[i+2][1]));
is_substr0[i][2] = is_substr0[i][1].or(states[i+1][1].and(states[i+2][1]));
is_reveal0[i] = is_substr0[i][2].and(is_consecutive[i][1]);
- reveal0[i] = input[i+1].mul(is_reveal0[i].toField());
+ reveal0[i] = input[i+1].value.mul(is_reveal0[i].toField());
}
- reveal0.unshift(input[0].mul(states[1][1].toField()));
+ reveal0.unshift(input[0].value.mul(states[1][1].toField()));
reveal.push(reveal0);
return { out, reveal };
}
-// "(mina|MINA)+" & revealing ['mina', 'MINA']
-function minaRegex(input: Field[]) {
+// "(mina|MINA)+" & revealing ["mina", "MINA"]
+function minaRegex(input: UInt8[]) {
const num_bytes = input.length;
let states: Bool[][] = Array.from({ length: num_bytes + 1 }, () => []);
let state_changed: Bool[] = Array.from({ length: num_bytes }, () => Bool(false));
@@ -392,7 +390,7 @@ function minaRegex(input: Field[]) {
}
for (let i = 0; i < num_bytes; i++) {
- const eq0 = input[i].equals(77);
+ const eq0 = input[i].value.equals(77);
const and0 = states[i][0].and(eq0);
const and1 = states[i][7].and(eq0);
let multi_or0 = Bool(false);
@@ -400,7 +398,7 @@ function minaRegex(input: Field[]) {
multi_or0 = multi_or0.or(and1);
states[i+1][1] = multi_or0;
state_changed[i] = state_changed[i].or(states[i+1][1]);
- const eq1 = input[i].equals(109);
+ const eq1 = input[i].value.equals(109);
const and2 = states[i][0].and(eq1);
const and3 = states[i][7].and(eq1);
let multi_or1 = Bool(false);
@@ -408,25 +406,25 @@ function minaRegex(input: Field[]) {
multi_or1 = multi_or1.or(and3);
states[i+1][2] = multi_or1;
state_changed[i] = state_changed[i].or(states[i+1][2]);
- const eq2 = input[i].equals(73);
+ const eq2 = input[i].value.equals(73);
const and4 = states[i][1].and(eq2);
states[i+1][3] = and4;
state_changed[i] = state_changed[i].or(states[i+1][3]);
- const eq3 = input[i].equals(105);
+ const eq3 = input[i].value.equals(105);
const and5 = states[i][2].and(eq3);
states[i+1][4] = and5;
state_changed[i] = state_changed[i].or(states[i+1][4]);
- const eq4 = input[i].equals(78);
+ const eq4 = input[i].value.equals(78);
const and6 = states[i][3].and(eq4);
states[i+1][5] = and6;
state_changed[i] = state_changed[i].or(states[i+1][5]);
- const eq5 = input[i].equals(110);
+ const eq5 = input[i].value.equals(110);
const and7 = states[i][4].and(eq5);
states[i+1][6] = and7;
state_changed[i] = state_changed[i].or(states[i+1][6]);
- const eq6 = input[i].equals(65);
+ const eq6 = input[i].value.equals(65);
const and8 = states[i][5].and(eq6);
- const eq7 = input[i].equals(97);
+ const eq7 = input[i].value.equals(97);
const and9 = states[i][6].and(eq7);
let multi_or2 = Bool(false);
multi_or2 = multi_or2.or(and8);
@@ -466,9 +464,9 @@ function minaRegex(input: Field[]) {
is_substr0[i][4] = is_substr0[i][3].or(states[i+1][4].and(states[i+2][6]));
is_substr0[i][5] = is_substr0[i][4].or(states[i+1][6].and(states[i+2][7]));
is_reveal0[i] = is_substr0[i][5].and(is_consecutive[i][1]);
- reveal0[i] = input[i+1].mul(is_reveal0[i].toField());
+ reveal0[i] = input[i+1].value.mul(is_reveal0[i].toField());
}
- reveal0.unshift(input[0].mul(states[1][2].toField()));
+ reveal0.unshift(input[0].value.mul(states[1][2].toField()));
reveal.push(reveal0);
// the 1-th substring transitions: [[0,1],[7,1],[1,3],[3,5],[5,7]]
@@ -483,16 +481,16 @@ function minaRegex(input: Field[]) {
is_substr1[i][4] = is_substr1[i][3].or(states[i+1][3].and(states[i+2][5]));
is_substr1[i][5] = is_substr1[i][4].or(states[i+1][5].and(states[i+2][7]));
is_reveal1[i] = is_substr1[i][5].and(is_consecutive[i][1]);
- reveal1[i] = input[i+1].mul(is_reveal1[i].toField());
+ reveal1[i] = input[i+1].value.mul(is_reveal1[i].toField());
}
- reveal1.unshift(input[0].mul(states[1][1].toField()));
+ reveal1.unshift(input[0].value.mul(states[1][1].toField()));
reveal.push(reveal1);
return { out, reveal };
}
-// "a:[^abcdefghijklmnopqrstuvwxyz]+." & revealing ['[^abcdefghijklmnopqrstuvwxyz]']
-function negateRegex(input: Field[]) {
+// "a:[^abcdefghijklmnopqrstuvwxyz]+." & revealing ["[^abcdefghijklmnopqrstuvwxyz]"]
+function negateRegex(input: UInt8[]) {
const num_bytes = input.length;
let states: Bool[][] = Array.from({ length: num_bytes + 1 }, () => []);
let state_changed: Bool[] = Array.from({ length: num_bytes }, () => Bool(false));
@@ -503,10 +501,10 @@ function negateRegex(input: Field[]) {
}
for (let i = 0; i < num_bytes; i++) {
- const lt0 = Field(97).lessThanOrEqual(input[i]);
+ const lt0 = new UInt8(97).lessThanOrEqual(input[i]);
const lt1 = input[i].lessThanOrEqual(122);
const and0 = lt0.and(lt1);
- const eq0 = input[i].equals(46);
+ const eq0 = input[i].value.equals(46);
let multi_or0 = Bool(false);
multi_or0 = multi_or0.or(and0);
multi_or0 = multi_or0.or(eq0);
@@ -520,11 +518,11 @@ function negateRegex(input: Field[]) {
const and3 = states[i][1].and(eq0);
states[i+1][2] = and3;
state_changed[i] = state_changed[i].or(states[i+1][2]);
- const eq1 = input[i].equals(97);
+ const eq1 = input[i].value.equals(97);
const and4 = states[i][0].and(eq1);
states[i+1][3] = and4;
state_changed[i] = state_changed[i].or(states[i+1][3]);
- const eq2 = input[i].equals(58);
+ const eq2 = input[i].value.equals(58);
const and5 = states[i][3].and(eq2);
states[i+1][4] = and5;
state_changed[i] = state_changed[i].or(states[i+1][4]);
@@ -535,7 +533,6 @@ function negateRegex(input: Field[]) {
for (let i = 0; i <= num_bytes; i++) {
final_state_result = final_state_result.or(states[i][2]);
}
-
const out = final_state_result;
const msg_bytes = num_bytes - 1;
@@ -558,7 +555,7 @@ function negateRegex(input: Field[]) {
is_substr0[i][1] = is_substr0[i][0].or(states[i+1][1].and(states[i+2][1]));
is_substr0[i][2] = is_substr0[i][1].or(states[i+1][4].and(states[i+2][1]));
is_reveal0[i] = is_substr0[i][2].and(is_consecutive[i][1]);
- reveal0[i] = input[i+1].mul(is_reveal0[i].toField());
+ reveal0[i] = input[i+1].value.mul(is_reveal0[i].toField());
}
reveal.push(reveal0);
@@ -566,7 +563,7 @@ function negateRegex(input: Field[]) {
}
// "[^aeiou]+" & revealing all
-export function negateVowel(input: Field[]) {
+export function negateVowel(input: UInt8[]) {
const num_bytes = input.length;
let states: Bool[][] = Array.from({ length: num_bytes + 1 }, () => []);
let state_changed: Bool[] = Array.from({ length: num_bytes }, () => Bool(false));
@@ -577,11 +574,11 @@ export function negateVowel(input: Field[]) {
}
for (let i = 0; i < num_bytes; i++) {
- const eq0 = input[i].equals(97);
- const eq1 = input[i].equals(101);
- const eq2 = input[i].equals(105);
- const eq3 = input[i].equals(111);
- const eq4 = input[i].equals(117);
+ const eq0 = input[i].value.equals(97);
+ const eq1 = input[i].value.equals(101);
+ const eq2 = input[i].value.equals(105);
+ const eq3 = input[i].value.equals(111);
+ const eq4 = input[i].value.equals(117);
let multi_or0 = Bool(false);
multi_or0 = multi_or0.or(eq0);
multi_or0 = multi_or0.or(eq1);
@@ -602,7 +599,6 @@ export function negateVowel(input: Field[]) {
for (let i = 1; i <= num_bytes; i++) {
final_state_result = final_state_result.and(states[i][1]);
}
-
const out = final_state_result;
const msg_bytes = num_bytes - 1;
@@ -625,9 +621,9 @@ export function negateVowel(input: Field[]) {
is_substr0[i][1] = is_substr0[i][0].or(states[i+1][0].and(states[i+2][1]));
is_substr0[i][2] = is_substr0[i][1].or(states[i+1][1].and(states[i+2][1]));
is_reveal0[i] = is_substr0[i][2].and(is_consecutive[i][1]);
- reveal0[i] = input[i+1].mul(is_reveal0[i].toField());
+ reveal0[i] = input[i+1].value.mul(is_reveal0[i].toField());
}
- reveal0.unshift(input[0].mul(states[1][1].toField()));
+ reveal0.unshift(input[0].value.mul(states[1][1].toField()));
reveal.push(reveal0);
return { out, reveal };
diff --git a/src/zkRegex.test.ts b/src/zkRegex.test.ts
index ce2dd3b..8de150d 100644
--- a/src/zkRegex.test.ts
+++ b/src/zkRegex.test.ts
@@ -1,4 +1,4 @@
-import { Bool, Field, Bytes } from 'o1js';
+import { Bool, Field, Bytes, UInt8 } from 'o1js';
import {
simpleRegex,
emailRegex,
@@ -47,7 +47,7 @@ function utf8BytesToString(bytes: bigint[]): string {
type OutputType = Field | Bool;
-type ZkRegexFunction = (input: Field[]) => {
+type ZkRegexFunction = (input: UInt8[]) => {
out: T;
reveal: Field[][];
}
@@ -68,7 +68,7 @@ function testZkRegex(
isValidExpected: T,
expectedSubstring?: string[]
) {
- const inputBytes = Bytes.fromString(input).toFields();
+ const inputBytes = Bytes.fromString(input).bytes;
const { out, reveal } = zkRegexFunction(inputBytes);
diff --git a/src/zkRegex.ts b/src/zkRegex.ts
index 0775771..40b9bbc 100644
--- a/src/zkRegex.ts
+++ b/src/zkRegex.ts
@@ -6,7 +6,7 @@ import {
} from "./regexToDfa.js";
// Handle different commands based on 'countEnabled' and 'substringEnabled' boolean arguments
-const rawRegex = process.argv[2] ?? "name: [A-Z][a-z]+";
+const rawRegex = process.argv[2];
let countEnabled = false;
let substringEnabled = false;
if (process.argv[3]) {
@@ -164,7 +164,7 @@ for (let i = 1; i < N; i++) {
const min = min_max[0];
const max = min_max[1];
if (range_checks[min][max] === undefined) {
- lines.push(`\tconst lt${lt_i} = Field(${min}).lessThanOrEqual(input[i]);`);
+ lines.push(`\tconst lt${lt_i} = new UInt8(${min}).lessThanOrEqual(input[i]);`);
lines.push(`\tconst lt${lt_i + 1} = input[i].lessThanOrEqual(${max});`);
@@ -183,7 +183,7 @@ for (let i = 1; i < N; i++) {
for (let code of vals) {
if (eq_checks[code] === undefined) {
- lines.push(`\tconst eq${eq_i} = input[i].equals(${code});`);
+ lines.push(`\tconst eq${eq_i} = input[i].value.equals(${code});`);
eq_outputs.push(['eq', eq_i]);
eq_checks[code] = eq_i;
@@ -338,9 +338,9 @@ function substring_lines(substrDefsArray: [number, number][][]): string {
}
reveal += `\t\tis_reveal${idx}[i] = is_substr${idx}[i][${numDefs}].and(is_consecutive[i][1]);\n`;
- reveal += `\t\treveal${idx}[i] = input[i+1].mul(is_reveal${idx}[i].toField());\n`;
+ reveal += `\t\treveal${idx}[i] = input[i+1].value.mul(is_reveal${idx}[i].toField());\n`;
reveal += "\t}\n";
- reveal += includes_start ? `\treveal${idx}.unshift(input[0].mul(states[1][${startIndex}].toField()));\n` : '';
+ reveal += includes_start ? `\treveal${idx}.unshift(input[0].value.mul(states[1][${startIndex}].toField()));\n` : '';
reveal += `\treveal.push(reveal${idx});`;
}
@@ -366,7 +366,7 @@ if (substringEnabled) {
}
export const functionString =
- "\n(input: Field[]) {\n" +
+ "\n(input: UInt8[]) {\n" +
lines.join('\n\t') +
reveal_lines +
"\n}";
@@ -414,6 +414,6 @@ function extractSubstrTransitions(
//TODO Declare state_changed outside of the loop declaration
//TODO Refactor is_substr calculation
//TODO Fix occurence compiler code when regex ends with repetition operator +
-//TODO organize zkRegex into a class
+//TODO Organize zkRegex compiler into a class
//TODO Add the option to reveal substrings based on search functions .i.e. isDigit, isNumber etc...
//TODO Refine notations for both compile and compiled code
\ No newline at end of file
diff --git a/src/zkapp.ts b/src/zkapp.ts
index bad0eee..e772994 100644
--- a/src/zkapp.ts
+++ b/src/zkapp.ts
@@ -12,6 +12,7 @@ import {
Mina,
PrivateKey,
AccountUpdate,
+ UInt8,
} from 'o1js';
class Bytes16 extends Bytes(16) {}
@@ -29,7 +30,7 @@ export class RegexZkApp extends SmartContract {
@method async guessName(statement: Bytes16) {
// This regex pattern specifies an alphabetic name of length 4 where the first letter is capitalized
- let { out, reveal } = nameRegex(statement.toFields());
+ let { out, reveal } = nameRegex(statement.bytes);
out.assertEquals(1, "Please enter only one valid name!");
const name = reveal[0];
@@ -86,9 +87,9 @@ console.log(
console.log('Admin name is discovered: ', zkAppInstance.isDiscovered.get().toBoolean());
-// "name: [A-Z][a-z][a-z][a-z]" & revealing all
+// "[A-Z][a-z][a-z][a-z]" & revealing all
// Basically, the regex pattern is to give an alphabetic name of length 4 where the first letter is capital
-function nameRegex(input: Field[]) {
+function nameRegex(input: UInt8[]) {
const num_bytes = input.length;
let states: Bool[][] = Array.from({ length: num_bytes + 1 }, () => []);
let state_changed: Bool[] = Array.from({ length: num_bytes }, () => Bool(false));
@@ -99,13 +100,13 @@ function nameRegex(input: Field[]) {
}
for (let i = 0; i < num_bytes; i++) {
- const lt0 = Field(65).lessThanOrEqual(input[i]);
+ const lt0 = new UInt8(65).lessThanOrEqual(input[i]);
const lt1 = input[i].lessThanOrEqual(90);
const and0 = lt0.and(lt1);
const and1 = states[i][0].and(and0);
states[i+1][1] = and1;
state_changed[i] = state_changed[i].or(states[i+1][1]);
- const lt2 = Field(97).lessThanOrEqual(input[i]);
+ const lt2 = new UInt8(97).lessThanOrEqual(input[i]);
const lt3 = input[i].lessThanOrEqual(122);
const and2 = lt2.and(lt3);
const and3 = states[i][1].and(and2);
@@ -149,9 +150,9 @@ function nameRegex(input: Field[]) {
is_substr0[i][3] = is_substr0[i][2].or(states[i+1][2].and(states[i+2][3]));
is_substr0[i][4] = is_substr0[i][3].or(states[i+1][3].and(states[i+2][4]));
is_reveal0[i] = is_substr0[i][4].and(is_consecutive[i][1]);
- reveal0[i] = input[i+1].mul(is_reveal0[i].toField());
+ reveal0[i] = input[i+1].value.mul(is_reveal0[i].toField());
}
- reveal0.unshift(input[0].mul(states[1][1].toField()));
+ reveal0.unshift(input[0].value.mul(states[1][1].toField()));
reveal.push(reveal0);
return { out, reveal };