diff --git a/circuits/extract.circom b/circuits/extract.circom index 03c7a79..8fa4bdb 100644 --- a/circuits/extract.circom +++ b/circuits/extract.circom @@ -1,6 +1,6 @@ pragma circom 2.1.9; -include "utils.circom"; +include "./utils/bytes.circom"; include "parser.circom"; template Extract(DATA_BYTES, MAX_STACK_HEIGHT) { diff --git a/circuits/parser.circom b/circuits/parser.circom index 71e347a..0f63120 100644 --- a/circuits/parser.circom +++ b/circuits/parser.circom @@ -23,7 +23,9 @@ Tests for this module are located in the files: `circuits/test/parser/*.test.ts pragma circom 2.1.9; -include "utils.circom"; +include "./utils/array.circom"; +include "./utils/bytes.circom"; +include "./utils/operators.circom"; include "language.circom"; /* diff --git a/circuits/search.circom b/circuits/search.circom index 3823e3e..cfd8c12 100644 --- a/circuits/search.circom +++ b/circuits/search.circom @@ -1,8 +1,9 @@ -pragma circom 2.1.9; +pragma circom 2.1.9; include "circomlib/circuits/mux1.circom"; include "./utils/hash.circom"; include "./utils/operators.circom"; +include "./utils/array.circom"; /* SubstringSearch diff --git a/circuits/test/array.test.ts b/circuits/test/array.test.ts deleted file mode 100644 index 04f4437..0000000 --- a/circuits/test/array.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { circomkit, WitnessTester } from "./common"; - -describe("array", () => { - describe("Slice", () => { - let circuit: WitnessTester<["in"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`Slice`, { - file: "circuits/utils/array", - template: "Slice", - params: [10, 2, 4], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("witness: [random*10], start: 2, end: 4", async () => { - const input = Array.from({ length: 10 }, () => Math.floor(Math.random() * 256)); - await circuit.expectPass( - { in: input }, - { out: input.slice(2, 4) } - ); - }); - - it("witness: [random*9], start: 2, end: 4", async () => { - const input = Array.from({ length: 9 }, () => Math.floor(Math.random() * 256)); - await circuit.expectFail( - { in: input }, - ); - }); - }); -}); \ No newline at end of file diff --git a/circuits/test/utils/array.test.ts b/circuits/test/utils/array.test.ts new file mode 100644 index 0000000..ae4a42e --- /dev/null +++ b/circuits/test/utils/array.test.ts @@ -0,0 +1,185 @@ +import { circomkit, WitnessTester } from "../common"; + +describe("array", () => { + describe("Slice", () => { + let circuit: WitnessTester<["in"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`Slice`, { + file: "circuits/utils/array", + template: "Slice", + params: [10, 2, 4], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("witness: [random*10], start: 2, end: 4", async () => { + const input = Array.from({ length: 10 }, () => Math.floor(Math.random() * 256)); + await circuit.expectPass( + { in: input }, + { out: input.slice(2, 4) } + ); + }); + + it("witness: [random*9], start: 2, end: 4", async () => { + const input = Array.from({ length: 9 }, () => Math.floor(Math.random() * 256)); + await circuit.expectFail( + { in: input }, + ); + }); + }); +}); + +describe("IsEqualArray", () => { + let circuit: WitnessTester<["in"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`IsEqualArray`, { + file: "circuits/utils/array", + template: "IsEqualArray", + params: [3], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("witness: [[0,0,0],[0,0,0]]", async () => { + await circuit.expectPass( + { in: [[0, 0, 0], [0, 0, 0]] }, + { out: 1 } + ); + }); + + it("witness: [[1,420,69],[1,420,69]]", async () => { + await circuit.expectPass( + { in: [[1, 420, 69], [1, 420, 69]] }, + { out: 1 }, + ); + }); + + it("witness: [[0,0,0],[1,420,69]]", async () => { + await circuit.expectPass( + { in: [[0, 0, 0], [1, 420, 69]] }, + { out: 0 }, + ); + }); + + it("witness: [[1,420,0],[1,420,69]]", async () => { + await circuit.expectPass( + { in: [[1, 420, 0], [1, 420, 69]] }, + { out: 0 }, + ); + }); + + it("witness: [[1,0,69],[1,420,69]]", async () => { + await circuit.expectPass( + { in: [[1, 0, 69], [1, 420, 69]] }, + { out: 0 }, + ); + }); + + it("witness: [[0,420,69],[1,420,69]]", async () => { + await circuit.expectPass( + { in: [[0, 420, 69], [1, 420, 69]] }, + { out: 0 }, + ); + }); +}); + +describe("Contains", () => { + let circuit: WitnessTester<["in", "array"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`Contains`, { + file: "circuits/utils/array", + template: "Contains", + params: [3], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("witness: in = 0, array = [0,1,2]", async () => { + await circuit.expectPass( + { in: 0, array: [0, 1, 2] }, + { out: 1 } + ); + }); + + it("witness: in = 1, array = [0,1,2]", async () => { + await circuit.expectPass( + { in: 1, array: [0, 1, 2] }, + { out: 1 } + ); + }); + + it("witness: in = 2, array = [0,1,2]", async () => { + await circuit.expectPass( + { in: 2, array: [0, 1, 2] }, + { out: 1 } + ); + }); + + it("witness: in = 42069, array = [0,1,2]", async () => { + await circuit.expectPass( + { in: 42069, array: [0, 1, 2] }, + { out: 0 } + ); + }); + +}); + +describe("ArrayAdd", () => { + let circuit: WitnessTester<["lhs", "rhs"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`ArrayAdd`, { + file: "circuits/utils/array", + 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/utils/array", + 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] } + ); + }); + +}); + +describe("GenericArrayAdd", () => { + let circuit: WitnessTester<["arrays"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`ArrayAdd`, { + file: "circuits/utils/array", + template: "GenericArrayAdd", + params: [3, 2], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("witness: arrays = [[0,1,2],[3,5,7]]", async () => { + await circuit.expectPass( + { arrays: [[0, 1, 2], [3, 5, 7]] }, + { out: [3, 6, 9] } + ); + }); + +}); \ No newline at end of file diff --git a/circuits/test/utils/bytes.test.ts b/circuits/test/utils/bytes.test.ts new file mode 100644 index 0000000..5ee55ef --- /dev/null +++ b/circuits/test/utils/bytes.test.ts @@ -0,0 +1,25 @@ +import { circomkit, WitnessTester } from "../common"; + +describe("ASCII", () => { + let circuit: WitnessTester<["in"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`ASCII`, { + file: "circuits/utils/bytes", + template: "ASCII", + params: [13], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("(valid) witness: in = b\"Hello, world!\"", async () => { + await circuit.expectPass( + { in: [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] }, + ); + }); + + it("(invalid) witness: in = [256, ...]", async () => { + await circuit.expectFail( + { in: [256, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] } + ); + }); +}); \ No newline at end of file diff --git a/circuits/test/hash.test.ts b/circuits/test/utils/hash.test.ts similarity index 93% rename from circuits/test/hash.test.ts rename to circuits/test/utils/hash.test.ts index d545d1c..e834cc7 100644 --- a/circuits/test/hash.test.ts +++ b/circuits/test/utils/hash.test.ts @@ -1,5 +1,5 @@ -import { circomkit, WitnessTester } from "./common"; -import { PoseidonModular } from "./common/poseidon"; +import { circomkit, WitnessTester } from "../common"; +import { PoseidonModular } from "../common/poseidon"; describe("hash", () => { describe("PoseidonModular_16", () => { diff --git a/circuits/test/utils/operators.test.ts b/circuits/test/utils/operators.test.ts new file mode 100644 index 0000000..7c82c61 --- /dev/null +++ b/circuits/test/utils/operators.test.ts @@ -0,0 +1,131 @@ +import { circomkit, WitnessTester } from "../common"; + +describe("SwitchArray", () => { + let circuit: WitnessTester<["case", "branches", "vals"], ["match", "out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`SwitchArray`, { + file: "circuits/utils/operators", + template: "SwitchArray", + params: [3, 2], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("witness: case = 0, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { + await circuit.expectPass( + { case: 0, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, + { match: 1, out: [69, 0] }, + ); + }); + + it("witness: case = 1, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { + await circuit.expectPass( + { case: 1, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, + { match: 1, out: [420, 1] }, + ); + }); + + it("witness: case = 2, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { + await circuit.expectPass( + { case: 2, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, + { match: 1, out: [1337, 2] }, + ); + }); + + it("witness: case = 3, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { + await circuit.expectPass( + { case: 3, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, + { match: 0, out: [0, 0] } + ); + }); + + it("witness: case = 420, branches = [69, 420, 1337], vals = [[10,3], [20,5], [30,7]]", async () => { + await circuit.expectPass( + { case: 420, branches: [69, 420, 1337], vals: [[10, 3], [20, 5], [30, 7]] }, + { match: 1, out: [20, 5] } + ); + }); + + it("witness: case = 0, branches = [69, 420, 1337], vals = [[10,3], [20,5], [30,7]]", async () => { + await circuit.expectPass( + { case: 0, branches: [69, 420, 1337], vals: [[10, 3], [20, 5], [30, 7]] }, + { match: 0, out: [0, 0] } + ); + }); + +}); + +describe("Switch", () => { + let circuit: WitnessTester<["case", "branches", "vals"], ["match", "out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`Switch`, { + file: "circuits/utils/operators", + template: "Switch", + params: [3], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("witness: case = 0, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { + await circuit.expectPass( + { case: 0, branches: [0, 1, 2], vals: [69, 420, 1337] }, + { match: 1, out: 69 }, + ); + }); + + it("witness: case = 1, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { + await circuit.expectPass( + { case: 1, branches: [0, 1, 2], vals: [69, 420, 1337] }, + { match: 1, out: 420 }, + ); + }); + + it("witness: case = 2, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { + await circuit.expectPass( + { case: 2, branches: [0, 1, 2], vals: [69, 420, 1337] }, + { match: 1, out: 1337 }, + ); + }); + + it("witness: case = 3, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { + await circuit.expectPass( + { case: 3, branches: [0, 1, 2], vals: [69, 420, 1337] }, + { match: 0, out: 0 }, + ); + }); + + +}); + +describe("InRange", () => { + let circuit: WitnessTester<["in", "range"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`InRange`, { + file: "circuits/utils/operators", + template: "InRange", + params: [8], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("witness: in = 1, range = [0,2]", async () => { + await circuit.expectPass( + { in: 1, range: [0, 2] }, + { out: 1 } + ); + }); + + it("witness: in = 69, range = [128,255]", async () => { + await circuit.expectPass( + { in: 69, range: [128, 255] }, + { out: 0 } + ); + }); + + it("witness: in = 200, range = [128,255]", async () => { + await circuit.expectPass( + { in: 1, range: [0, 2] }, + { out: 1 } + ); + }); +}); \ No newline at end of file diff --git a/circuits/test/utils/utils.test.ts b/circuits/test/utils/utils.test.ts deleted file mode 100644 index 3721566..0000000 --- a/circuits/test/utils/utils.test.ts +++ /dev/null @@ -1,311 +0,0 @@ -import { circomkit, WitnessTester } from "../common"; - -describe("ASCII", () => { - let circuit: WitnessTester<["in"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`ASCII`, { - file: "circuits/utils", - template: "ASCII", - params: [13], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("(valid) witness: in = b\"Hello, world!\"", async () => { - await circuit.expectPass( - { in: [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] }, - ); - }); - - it("(invalid) witness: in = [256, ...]", async () => { - await circuit.expectFail( - { in: [256, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] } - ); - }); -}); - -describe("IsEqualArray", () => { - let circuit: WitnessTester<["in"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`IsEqualArray`, { - file: "circuits/utils", - template: "IsEqualArray", - params: [3], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("witness: [[0,0,0],[0,0,0]]", async () => { - await circuit.expectPass( - { in: [[0, 0, 0], [0, 0, 0]] }, - { out: 1 } - ); - }); - - it("witness: [[1,420,69],[1,420,69]]", async () => { - await circuit.expectPass( - { in: [[1, 420, 69], [1, 420, 69]] }, - { out: 1 }, - ); - }); - - it("witness: [[0,0,0],[1,420,69]]", async () => { - await circuit.expectPass( - { in: [[0, 0, 0], [1, 420, 69]] }, - { out: 0 }, - ); - }); - - it("witness: [[1,420,0],[1,420,69]]", async () => { - await circuit.expectPass( - { in: [[1, 420, 0], [1, 420, 69]] }, - { out: 0 }, - ); - }); - - it("witness: [[1,0,69],[1,420,69]]", async () => { - await circuit.expectPass( - { in: [[1, 0, 69], [1, 420, 69]] }, - { out: 0 }, - ); - }); - - it("witness: [[0,420,69],[1,420,69]]", async () => { - await circuit.expectPass( - { in: [[0, 420, 69], [1, 420, 69]] }, - { out: 0 }, - ); - }); -}); - -describe("Contains", () => { - let circuit: WitnessTester<["in", "array"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`Contains`, { - file: "circuits/utils", - template: "Contains", - params: [3], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("witness: in = 0, array = [0,1,2]", async () => { - await circuit.expectPass( - { in: 0, array: [0, 1, 2] }, - { out: 1 } - ); - }); - - it("witness: in = 1, array = [0,1,2]", async () => { - await circuit.expectPass( - { in: 1, array: [0, 1, 2] }, - { out: 1 } - ); - }); - - it("witness: in = 2, array = [0,1,2]", async () => { - await circuit.expectPass( - { in: 2, array: [0, 1, 2] }, - { out: 1 } - ); - }); - - it("witness: in = 42069, array = [0,1,2]", async () => { - await circuit.expectPass( - { in: 42069, array: [0, 1, 2] }, - { out: 0 } - ); - }); - -}); - -describe("ArrayAdd", () => { - let circuit: WitnessTester<["lhs", "rhs"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`ArrayAdd`, { - file: "circuits/utils", - 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/utils", - 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] } - ); - }); - -}); - -describe("GenericArrayAdd", () => { - let circuit: WitnessTester<["arrays"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`ArrayAdd`, { - file: "circuits/utils", - template: "GenericArrayAdd", - params: [3, 2], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("witness: arrays = [[0,1,2],[3,5,7]]", async () => { - await circuit.expectPass( - { arrays: [[0, 1, 2], [3, 5, 7]] }, - { out: [3, 6, 9] } - ); - }); - -}); - -describe("InRange", () => { - let circuit: WitnessTester<["in", "range"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`InRange`, { - file: "circuits/utils", - template: "InRange", - params: [8], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("witness: in = 1, range = [0,2]", async () => { - await circuit.expectPass( - { in: 1, range: [0, 2] }, - { out: 1 } - ); - }); - - it("witness: in = 69, range = [128,255]", async () => { - await circuit.expectPass( - { in: 69, range: [128, 255] }, - { out: 0 } - ); - }); - - it("witness: in = 200, range = [128,255]", async () => { - await circuit.expectPass( - { in: 1, range: [0, 2] }, - { out: 1 } - ); - }); -}); - -describe("Switch", () => { - let circuit: WitnessTester<["case", "branches", "vals"], ["match", "out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`Switch`, { - file: "circuits/utils", - template: "Switch", - params: [3], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("witness: case = 0, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { - await circuit.expectPass( - { case: 0, branches: [0, 1, 2], vals: [69, 420, 1337] }, - { match: 1, out: 69 }, - ); - }); - - it("witness: case = 1, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { - await circuit.expectPass( - { case: 1, branches: [0, 1, 2], vals: [69, 420, 1337] }, - { match: 1, out: 420 }, - ); - }); - - it("witness: case = 2, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { - await circuit.expectPass( - { case: 2, branches: [0, 1, 2], vals: [69, 420, 1337] }, - { match: 1, out: 1337 }, - ); - }); - - it("witness: case = 3, branches = [0, 1, 2], vals = [69, 420, 1337]", async () => { - await circuit.expectPass( - { case: 3, branches: [0, 1, 2], vals: [69, 420, 1337] }, - { match: 0, out: 0 }, - ); - }); - - -}); - -describe("SwitchArray", () => { - let circuit: WitnessTester<["case", "branches", "vals"], ["match", "out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`SwitchArray`, { - file: "circuits/utils", - template: "SwitchArray", - params: [3, 2], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("witness: case = 0, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { - await circuit.expectPass( - { case: 0, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, - { match: 1, out: [69, 0] }, - ); - }); - - it("witness: case = 1, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { - await circuit.expectPass( - { case: 1, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, - { match: 1, out: [420, 1] }, - ); - }); - - it("witness: case = 2, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { - await circuit.expectPass( - { case: 2, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, - { match: 1, out: [1337, 2] }, - ); - }); - - it("witness: case = 3, branches = [0, 1, 2], vals = [[69,0], [420,1], [1337,2]]", async () => { - await circuit.expectPass( - { case: 3, branches: [0, 1, 2], vals: [[69, 0], [420, 1], [1337, 2]] }, - { match: 0, out: [0, 0] } - ); - }); - - it("witness: case = 420, branches = [69, 420, 1337], vals = [[10,3], [20,5], [30,7]]", async () => { - await circuit.expectPass( - { case: 420, branches: [69, 420, 1337], vals: [[10, 3], [20, 5], [30, 7]] }, - { match: 1, out: [20, 5] } - ); - }); - - it("witness: case = 0, branches = [69, 420, 1337], vals = [[10,3], [20,5], [30,7]]", async () => { - await circuit.expectPass( - { case: 0, branches: [69, 420, 1337], vals: [[10, 3], [20, 5], [30, 7]] }, - { match: 0, out: [0, 0] } - ); - }); - -}); - diff --git a/circuits/utils.circom b/circuits/utils.circom deleted file mode 100644 index ed0f485..0000000 --- a/circuits/utils.circom +++ /dev/null @@ -1,342 +0,0 @@ -/* -# `utils` -This module consists of helper templates for convencience. -It mostly extends the `bitify` and `comparators` modules from Circomlib. - -## Layout -The key ingredients of `utils` are: - - `ASCII`: Verify if a an input array contains valid ASCII values (e.g., u8 vals). - - `IsEqualArray`: Check if two arrays are equal component by component. - - `Contains`: Check if an element is contained in a given array. - - `ArrayAdd`: Add two arrays together component by component. - - `ArrayMul`: Multiply two arrays together component by component. - - `GenericArrayAdd`: Add together an arbitrary amount of arrays. - - `ScalarArrayMul`: Multiply each array element by a scalar value. - - `InRange`: Check if a given number is in a given range. - - `Switch`: Return a scalar value given a specific case. - - `SwitchArray`: Return an array given a specific case. - - -## Testing -Tests for this module are located in the file: `./test/utils/utils.test.ts` -*/ - -pragma circom 2.1.9; - -include "circomlib/circuits/bitify.circom"; -include "circomlib/circuits/comparators.circom"; - - - -/* -This template passes if a given array contains only valid ASCII values (e.g., u8 vals). - -# Params: - - `n`: the length of the array - -# Inputs: - - `in[n]`: array to check -*/ -template ASCII(n) { - signal input in[n]; - - component Byte[n]; - for(var i = 0; i < n; i++) { - Byte[i] = Num2Bits(8); - Byte[i].in <== in[i]; - } -} - -/* -This template is an indicator for two equal array inputs. - -# Params: - - `n`: the length of arrays to compare - -# Inputs: - - `in[2][n]`: two arrays of `n` numbers - -# Outputs: - - `out`: either `0` or `1` - - `1` if `in[0]` is equal to `in[1]` as arrays (i.e., component by component) - - `0` otherwise -*/ -template IsEqualArray(n) { - signal input in[2][n]; - signal output out; - - var accum = 0; - component equalComponent[n]; - - for(var i = 0; i < n; i++) { - equalComponent[i] = IsEqual(); - equalComponent[i].in[0] <== in[0][i]; - equalComponent[i].in[1] <== in[1][i]; - accum += equalComponent[i].out; - } - - component totalEqual = IsEqual(); - totalEqual.in[0] <== n; - totalEqual.in[1] <== accum; - out <== totalEqual.out; -} - - -// TODO: There should be a way to have the below assertion come from the field itself. -/* -This template is an indicator for if an array contains an element. - -# Params: - - `n`: the size of the array to search through - -# Inputs: - - `in`: a number - - `array[n]`: the array we want to search through - -# Outputs: - - `out`: either `0` or `1` - - `1` if `in` is found inside `array` - - `0` otherwise -*/ -template Contains(n) { - assert(n > 0); - /* - If `n = p` for this large `p`, then it could be that this template - returns the wrong value if every element in `array` was equal to `in`. - This is EXTREMELY unlikely and iterating this high is impossible anyway. - But it is better to check than miss something, so we bound it by `2**254` for now. - */ - assert(n < 2**254); - signal input in; - signal input array[n]; - signal output out; - - var accum = 0; - component equalComponent[n]; - for(var i = 0; i < n; i++) { - equalComponent[i] = IsEqual(); - equalComponent[i].in[0] <== in; - equalComponent[i].in[1] <== array[i]; - accum = accum + equalComponent[i].out; - } - - component someEqual = IsZero(); - someEqual.in <== accum; - - // Apply `not` to this by 1-x - out <== 1 - someEqual.out; -} - -/* -This template adds two arrays component by component. - -# Params: - - `n`: the length of arrays to compare - -# Inputs: - - `in[2][n]`: two arrays of `n` numbers - -# Outputs: - - `out[n]`: the array sum value -*/ -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]; - } -} - -/* -This template multiplies two arrays component by component. - -# Params: - - `n`: the length of arrays to compare - -# Inputs: - - `in[2][n]`: two arrays of `n` numbers - -# Outputs: - - `out[n]`: the array multiplication value -*/ -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]; - } -} - -/* -This template multiplies two arrays component by component. - -# Params: - - `m`: the length of the arrays to add - - `n`: the number of arrays to add - -# Inputs: - - `arrays[m][n]`: `n` arrays of `m` numbers - -# Outputs: - - `out[m]`: the sum of all the arrays -*/ -template GenericArrayAdd(m,n) { - signal input arrays[n][m]; - signal output out[m]; - - var accum[m]; - for(var i = 0; i < m; i++) { - for(var j = 0; j < n; j++) { - accum[i] += arrays[j][i]; - } - } - out <== accum; -} - -/* -This template multiplies each component of an array by a scalar value. - -# Params: - - `n`: the length of the array - -# Inputs: - - `array[n]`: an array of `n` numbers - -# Outputs: - - `out[n]`: the scalar multiplied array -*/ -template ScalarArrayMul(n) { - signal input array[n]; - signal input scalar; - signal output out[n]; - - for(var i = 0; i < n; i++) { - out[i] <== scalar * array[i]; - } -} - -/* -This template checks if a given `n`-bit value is contained in a range of `n`-bit values - -# Params: - - `n`: the number of bits to use - -# Inputs: - - `range[2]`: the lower and upper bound of the array, respectively - -# Outputs: - - `out`: either `0` or `1` - - `1` if `in` is within the range - - `0` otherwise -*/ -template InRange(n) { - signal input in; - signal input range[2]; - signal output out; - - component gte = GreaterEqThan(n); - gte.in <== [in, range[0]]; - - component lte = LessEqThan(n); - lte.in <== [in, range[1]]; - - out <== gte.out * lte.out; -} - -/* -This template is creates an exhaustive switch statement from a list of branch values. -# Params: - - `n`: the number of switch cases - -# Inputs: - - `case`: which case of the switch to select - - `branches[n]`: the values that enable taking different branches in the switch - (e.g., if `branch[i] == 10` then if `case == 10` we set `out == `vals[i]`) - - `vals[n]`: the value that is emitted for a given switch case - (e.g., `val[i]` array is emitted on `case == `branch[i]`) - -# Outputs - - `match`: is set to `0` if `case` does not match on any of `branches` - - `out[n]`: the selected output value if one of `branches` is selected (will be `0` otherwise) - ^^^^^^ BEWARE OF THIS FACT ABOVE! -*/ -template Switch(n) { - assert(n > 0); - signal input case; - signal input branches[n]; - signal input vals[n]; - signal output match; - signal output out; - - - // Verify that the `case` is in the possible set of branches - component indicator[n]; - component matchChecker = Contains(n); - signal temp_val[n]; - var sum; - for(var i = 0; i < n; i++) { - indicator[i] = IsZero(); - indicator[i].in <== case - branches[i]; - matchChecker.array[i] <== 1 - indicator[i].out; - temp_val[i] <== indicator[i].out * vals[i]; - sum += temp_val[i]; - } - matchChecker.in <== 0; - match <== matchChecker.out; - - out <== sum; -} - -/* -This template is creates an exhaustive switch statement from a list of branch values. -# Params: - - `m`: the number of switch cases - - `n`: the output array length - -# Inputs: - - - `case`: which case of the switch to select - - `branches[m]`: the values that enable taking different branches in the switch - (e.g., if `branch[i] == 10` then if `case == 10` we set `out == `vals[i]`) - - `vals[m][n]`: the value that is emitted for a given switch case - (e.g., `val[i]` array is emitted on `case == `branch[i]`) - -# Outputs - - `match`: is set to `0` if `case` does not match on any of `branches` - - `out[n]`: the selected output value if one of `branches` is selected (will be `[0,0,...]` otherwise) - ^^^^^^ BEWARE OF THIS FACT ABOVE! -*/ -template SwitchArray(m, n) { - assert(m > 0); - assert(n > 0); - signal input case; - signal input branches[m]; - signal input vals[m][n]; - signal output match; - signal output out[n]; - - - // Verify that the `case` is in the possible set of branches - component indicator[m]; - component matchChecker = Contains(m); - signal component_out[m][n]; - var sum[n]; - for(var i = 0; i < m; i++) { - indicator[i] = IsZero(); - indicator[i].in <== case - branches[i]; - matchChecker.array[i] <== 1 - indicator[i].out; - for(var j = 0; j < n; j++) { - component_out[i][j] <== indicator[i].out * vals[i][j]; - sum[j] += component_out[i][j]; - } - } - matchChecker.in <== 0; - match <== matchChecker.out; - - out <== sum; -} - diff --git a/circuits/utils/array.circom b/circuits/utils/array.circom index 59c7ca8..dc65b28 100644 --- a/circuits/utils/array.circom +++ b/circuits/utils/array.circom @@ -1,5 +1,6 @@ pragma circom 2.1.9; +include "circomlib/circuits/comparators.circom"; /// Extract a fixed portion of an array /// @@ -28,4 +29,175 @@ template Slice(n, start, end) { for (var i = start; i < end; i++) { out[i - start] <== in[i]; } +} + +/* +This template is an indicator for two equal array inputs. + +# Params: + - `n`: the length of arrays to compare + +# Inputs: + - `in[2][n]`: two arrays of `n` numbers + +# Outputs: + - `out`: either `0` or `1` + - `1` if `in[0]` is equal to `in[1]` as arrays (i.e., component by component) + - `0` otherwise +*/ +template IsEqualArray(n) { + signal input in[2][n]; + signal output out; + + var accum = 0; + component equalComponent[n]; + + for(var i = 0; i < n; i++) { + equalComponent[i] = IsEqual(); + equalComponent[i].in[0] <== in[0][i]; + equalComponent[i].in[1] <== in[1][i]; + accum += equalComponent[i].out; + } + + component totalEqual = IsEqual(); + totalEqual.in[0] <== n; + totalEqual.in[1] <== accum; + out <== totalEqual.out; +} + +// TODO: There should be a way to have the below assertion come from the field itself. +/* +This template is an indicator for if an array contains an element. + +# Params: + - `n`: the size of the array to search through + +# Inputs: + - `in`: a number + - `array[n]`: the array we want to search through + +# Outputs: + - `out`: either `0` or `1` + - `1` if `in` is found inside `array` + - `0` otherwise +*/ +template Contains(n) { + assert(n > 0); + /* + If `n = p` for this large `p`, then it could be that this template + returns the wrong value if every element in `array` was equal to `in`. + This is EXTREMELY unlikely and iterating this high is impossible anyway. + But it is better to check than miss something, so we bound it by `2**254` for now. + */ + assert(n < 2**254); + signal input in; + signal input array[n]; + signal output out; + + var accum = 0; + component equalComponent[n]; + for(var i = 0; i < n; i++) { + equalComponent[i] = IsEqual(); + equalComponent[i].in[0] <== in; + equalComponent[i].in[1] <== array[i]; + accum = accum + equalComponent[i].out; + } + + component someEqual = IsZero(); + someEqual.in <== accum; + + // Apply `not` to this by 1-x + out <== 1 - someEqual.out; +} + +/* +This template adds two arrays component by component. + +# Params: + - `n`: the length of arrays to compare + +# Inputs: + - `in[2][n]`: two arrays of `n` numbers + +# Outputs: + - `out[n]`: the array sum value +*/ +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]; + } +} + +/* +This template multiplies two arrays component by component. + +# Params: + - `n`: the length of arrays to compare + +# Inputs: + - `in[2][n]`: two arrays of `n` numbers + +# Outputs: + - `out[n]`: the array multiplication value +*/ +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]; + } +} + +/* +This template multiplies two arrays component by component. + +# Params: + - `m`: the length of the arrays to add + - `n`: the number of arrays to add + +# Inputs: + - `arrays[m][n]`: `n` arrays of `m` numbers + +# Outputs: + - `out[m]`: the sum of all the arrays +*/ +template GenericArrayAdd(m,n) { + signal input arrays[n][m]; + signal output out[m]; + + var accum[m]; + for(var i = 0; i < m; i++) { + for(var j = 0; j < n; j++) { + accum[i] += arrays[j][i]; + } + } + out <== accum; +} + +/* +This template multiplies each component of an array by a scalar value. + +# Params: + - `n`: the length of the array + +# Inputs: + - `array[n]`: an array of `n` numbers + +# Outputs: + - `out[n]`: the scalar multiplied array +*/ +template ScalarArrayMul(n) { + signal input array[n]; + signal input scalar; + signal output out[n]; + + for(var i = 0; i < n; i++) { + out[i] <== scalar * array[i]; + } } \ No newline at end of file diff --git a/circuits/utils/bytes.circom b/circuits/utils/bytes.circom new file mode 100644 index 0000000..65f8f80 --- /dev/null +++ b/circuits/utils/bytes.circom @@ -0,0 +1,22 @@ +pragma circom 2.1.9; + +include "circomlib/circuits/bitify.circom"; + +/* +This template passes if a given array contains only valid ASCII values (e.g., u8 vals). + +# Params: + - `n`: the length of the array + +# Inputs: + - `in[n]`: array to check +*/ +template ASCII(n) { + signal input in[n]; + + component Byte[n]; + for(var i = 0; i < n; i++) { + Byte[i] = Num2Bits(8); + Byte[i].in <== in[i]; + } +} \ No newline at end of file diff --git a/circuits/utils/operators.circom b/circuits/utils/operators.circom new file mode 100644 index 0000000..925005e --- /dev/null +++ b/circuits/utils/operators.circom @@ -0,0 +1,151 @@ +/* +# `utils` +This module consists of helper templates for convencience. +It mostly extends the `bitify` and `comparators` modules from Circomlib. + +## Layout +The key ingredients of `utils` are: + - `ASCII`: Verify if a an input array contains valid ASCII values (e.g., u8 vals). + - `IsEqualArray`: Check if two arrays are equal component by component. + - `Contains`: Check if an element is contained in a given array. + - `ArrayAdd`: Add two arrays together component by component. + - `ArrayMul`: Multiply two arrays together component by component. + - `GenericArrayAdd`: Add together an arbitrary amount of arrays. + - `ScalarArrayMul`: Multiply each array element by a scalar value. + - `InRange`: Check if a given number is in a given range. + - `Switch`: Return a scalar value given a specific case. + - `SwitchArray`: Return an array given a specific case. + + +## Testing +Tests for this module are located in the file: `./test/utils/utils.test.ts` +*/ + +pragma circom 2.1.9; + +include "circomlib/circuits/bitify.circom"; +include "circomlib/circuits/comparators.circom"; +include "array.circom"; + + +/* +This template checks if a given `n`-bit value is contained in a range of `n`-bit values + +# Params: + - `n`: the number of bits to use + +# Inputs: + - `range[2]`: the lower and upper bound of the array, respectively + +# Outputs: + - `out`: either `0` or `1` + - `1` if `in` is within the range + - `0` otherwise +*/ +template InRange(n) { + signal input in; + signal input range[2]; + signal output out; + + component gte = GreaterEqThan(n); + gte.in <== [in, range[0]]; + + component lte = LessEqThan(n); + lte.in <== [in, range[1]]; + + out <== gte.out * lte.out; +} + +/* +This template is creates an exhaustive switch statement from a list of branch values. +# Params: + - `n`: the number of switch cases + +# Inputs: + - `case`: which case of the switch to select + - `branches[n]`: the values that enable taking different branches in the switch + (e.g., if `branch[i] == 10` then if `case == 10` we set `out == `vals[i]`) + - `vals[n]`: the value that is emitted for a given switch case + (e.g., `val[i]` array is emitted on `case == `branch[i]`) + +# Outputs + - `match`: is set to `0` if `case` does not match on any of `branches` + - `out[n]`: the selected output value if one of `branches` is selected (will be `0` otherwise) + ^^^^^^ BEWARE OF THIS FACT ABOVE! +*/ +template Switch(n) { + assert(n > 0); + signal input case; + signal input branches[n]; + signal input vals[n]; + signal output match; + signal output out; + + + // Verify that the `case` is in the possible set of branches + component indicator[n]; + component matchChecker = Contains(n); + signal temp_val[n]; + var sum; + for(var i = 0; i < n; i++) { + indicator[i] = IsZero(); + indicator[i].in <== case - branches[i]; + matchChecker.array[i] <== 1 - indicator[i].out; + temp_val[i] <== indicator[i].out * vals[i]; + sum += temp_val[i]; + } + matchChecker.in <== 0; + match <== matchChecker.out; + + out <== sum; +} + +/* +This template is creates an exhaustive switch statement from a list of branch values. +# Params: + - `m`: the number of switch cases + - `n`: the output array length + +# Inputs: + + - `case`: which case of the switch to select + - `branches[m]`: the values that enable taking different branches in the switch + (e.g., if `branch[i] == 10` then if `case == 10` we set `out == `vals[i]`) + - `vals[m][n]`: the value that is emitted for a given switch case + (e.g., `val[i]` array is emitted on `case == `branch[i]`) + +# Outputs + - `match`: is set to `0` if `case` does not match on any of `branches` + - `out[n]`: the selected output value if one of `branches` is selected (will be `[0,0,...]` otherwise) + ^^^^^^ BEWARE OF THIS FACT ABOVE! +*/ +template SwitchArray(m, n) { + assert(m > 0); + assert(n > 0); + signal input case; + signal input branches[m]; + signal input vals[m][n]; + signal output match; + signal output out[n]; + + + // Verify that the `case` is in the possible set of branches + component indicator[m]; + component matchChecker = Contains(m); + signal component_out[m][n]; + var sum[n]; + for(var i = 0; i < m; i++) { + indicator[i] = IsZero(); + indicator[i].in <== case - branches[i]; + matchChecker.array[i] <== 1 - indicator[i].out; + for(var j = 0; j < n; j++) { + component_out[i][j] <== indicator[i].out * vals[i][j]; + sum[j] += component_out[i][j]; + } + } + matchChecker.in <== 0; + match <== matchChecker.out; + + out <== sum; +} +