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 };