├── .gitignore
├── LICENSE
├── README.md
├── package-lock.json
├── package.json
├── packages
├── app
│ ├── .eslintignore
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── .prettierignore
│ ├── .prettierrc.json
│ ├── README.md
│ ├── next.config.js
│ ├── package.json
│ ├── pages
│ │ ├── _app.tsx
│ │ ├── _document.tsx
│ │ ├── api
│ │ │ └── hello.ts
│ │ └── index.tsx
│ ├── public
│ │ ├── favicon.ico
│ │ ├── next.svg
│ │ ├── snarkjs.min.js
│ │ ├── sudoku.wasm
│ │ ├── sudoku_1.zkey
│ │ ├── sudoku_verify_key.json
│ │ ├── thirteen.svg
│ │ └── vercel.svg
│ ├── src
│ │ ├── Constants.tsx
│ │ ├── components
│ │ │ ├── KeyboardView.tsx
│ │ │ ├── PlayPannel.tsx
│ │ │ ├── ProofView.tsx
│ │ │ ├── PuzzleView.tsx
│ │ │ └── VerifyPannel.tsx
│ │ ├── images
│ │ │ └── sudoku.png
│ │ ├── layout
│ │ │ └── AppLayout.tsx
│ │ └── utils
│ │ │ └── GameUtils.ts
│ ├── styles
│ │ ├── Home.module.css
│ │ └── globals.css
│ └── tsconfig.json
└── circuit
│ ├── circuits
│ ├── circomlib
│ │ ├── aliascheck.circom
│ │ ├── binsum.circom
│ │ ├── bitify.circom
│ │ ├── comparators.circom
│ │ ├── compconstant.circom
│ │ └── gates.circom
│ ├── sudoku.circom
│ └── utils.circom
│ ├── index.js
│ ├── package.json
│ └── test
│ ├── circuits
│ ├── get_number_group_for_box.circom
│ ├── get_number_group_for_column.circom
│ ├── get_number_group_for_row.circom
│ ├── is_number_in_range.circom
│ ├── is_valid_puzzle.circom
│ ├── is_valid_puzzle_number_group.circom
│ ├── is_valid_solution.circom
│ ├── is_valid_solution_number_group.circom
│ └── is_valid_solution_of_puzzle.circom
│ ├── sudoku_test.js
│ ├── test_data.js
│ └── utils_test.js
├── screen-capture.gif
├── screen-capture.mp4
├── screen-capture.webm
└── screenshot.jpg
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 | **/node_modules
3 |
4 | # dependencies
5 | /node_modules
6 |
7 | # testing
8 | coverage
9 |
10 | # production
11 | build
12 | # yarn / eslint
13 | .yarn/cache
14 | .yarn/install-state.gz
15 | .yarn/build-state.yml
16 | .eslintcache
17 | # testing
18 | coverage
19 |
20 | # production
21 | build
22 |
23 | # misc
24 | .DS_Store
25 | .env*
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 |
32 | .idea
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Boost Software License - Version 1.0 - August 17th, 2003
2 |
3 | Permission is hereby granted, free of charge, to any person or organization
4 | obtaining a copy of the software and accompanying documentation covered by
5 | this license (the "Software") to use, reproduce, display, distribute,
6 | execute, and transmit the Software, and to prepare derivative works of the
7 | Software, and to permit third-parties to whom the Software is furnished to
8 | do so, all subject to the following:
9 |
10 | The copyright notices in the Software and this entire statement, including
11 | the above license grant, this restriction and the following disclaimer,
12 | must be included in all copies of the Software, in whole or in part, and
13 | all derivative works of the Software, unless such copies or derivative
14 | works are solely in the form of machine-executable object code generated by
15 | a source language processor.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 | DEALINGS IN THE SOFTWARE.
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # 🏆🏆🏆 zkSNARK Sudoku 🏆🏆🏆
4 |
5 | Sudoku verifier using zkSNARK.
6 |
7 | ## 📺 LIVE ON
8 |
9 | https://zksnark-sudoku.surge.sh/
10 |
11 | ## 📜 zksnark
12 |
13 | ### ⚔️ Used technologies
14 |
15 | > Groth16: zksnark implementation scheme.
16 |
17 | > Circom: zksnark circuit compilation toolkit.
18 |
19 | > snarkjs: zksnark library.
20 |
21 | ### 📝 Description
22 |
23 | ## 📺 Application
24 |
25 | ### ⚔️ Used technologies
26 |
27 | > antd: Excellent UI template library for react.js.
28 |
29 | > React.js: For our front end building.
30 |
31 | ### 📝 Description
32 |
33 | This is react.js based web application for sudoku game play and verify.
34 | Now it has the following features.
35 |
36 | 1. Game play.
37 | Application will generate puzzles for you and you can solve it.
38 | 2. Proof generation.
39 | If you solve a puzzle, you can generate its proof.
40 | 3. Verify.
41 | You can make sure others that you have solved a puzzle without sharing your solution.
42 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "zksnark-sudoku",
3 | "version": "1.0.0",
4 | "description": "Sudoku verifier using zkSNARK.",
5 | "scripts": {
6 | "compile-circuit": "npm run compile --workspace circuit",
7 | "test-circuit": "npm run test --workspace circuit",
8 | "dev": "npm run dev --workspace app",
9 | "build": "npm run build --workspace app"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/web3-master/zksnark-sudoku.git"
14 | },
15 | "keywords": [
16 | "zksnark",
17 | "circom",
18 | "snarkjs",
19 | "sudoku"
20 | ],
21 | "author": "web3-master",
22 | "license": "BSL-1.0",
23 | "bugs": {
24 | "url": "https://github.com/web3-master/zksnark-sudoku/issues"
25 | },
26 | "homepage": "https://github.com/web3-master/zksnark-sudoku#readme",
27 | "workspaces": [
28 | "./packages/*"
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/packages/app/.eslintignore:
--------------------------------------------------------------------------------
1 | # Ignore artifacts:
2 | .next
3 | node_modules
4 | out
5 | pages
6 | public
7 | styles
8 | next.config.js
--------------------------------------------------------------------------------
/packages/app/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "eslint:recommended",
4 | "plugin:@typescript-eslint/recommended",
5 | "next/core-web-vitals",
6 | "prettier"
7 | ],
8 | "parser": "@typescript-eslint/parser",
9 | "plugins": ["@typescript-eslint"],
10 | "parserOptions": {
11 | "project": "./tsconfig.json"
12 | },
13 | "root": true
14 | }
15 |
--------------------------------------------------------------------------------
/packages/app/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 | .pnpm-debug.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/packages/app/.prettierignore:
--------------------------------------------------------------------------------
1 | # Ignore artifacts:
2 | .next
3 | node_modules
4 | out
5 | pages
6 | public
7 | styles
--------------------------------------------------------------------------------
/packages/app/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "bracketSpacing": true,
4 | "bracketSameLine": false,
5 | "printWidth": 80
6 | }
7 |
--------------------------------------------------------------------------------
/packages/app/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | ```
12 |
13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
14 |
15 | You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
16 |
17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
18 |
19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
20 |
21 | ## Learn More
22 |
23 | To learn more about Next.js, take a look at the following resources:
24 |
25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
27 |
28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
29 |
30 | ## Deploy on Vercel
31 |
32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
33 |
34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
35 |
--------------------------------------------------------------------------------
/packages/app/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | eslint: {
5 | // Warning: This allows production builds to successfully complete even if
6 | // your project has ESLint errors.
7 | ignoreDuringBuilds: true,
8 | },
9 | typescript: {
10 | // !! WARN !!
11 | // Dangerously allow production builds to successfully complete even if
12 | // your project has type errors.
13 | // !! WARN !!
14 | ignoreBuildErrors: true,
15 | },
16 | };
17 |
18 | module.exports = nextConfig;
19 |
--------------------------------------------------------------------------------
/packages/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build && next export",
8 | "start": "next start",
9 | "lint": "eslint .",
10 | "prettier": "prettier --write ."
11 | },
12 | "dependencies": {
13 | "@ant-design/colors": "^6.0.0",
14 | "@next/font": "13.0.6",
15 | "@types/node": "18.11.15",
16 | "@types/react": "18.0.26",
17 | "@types/react-dom": "18.0.9",
18 | "antd": "^5.0.7",
19 | "eslint-config-next": "13.0.6",
20 | "next": "13.0.6",
21 | "react": "18.2.0",
22 | "react-dom": "18.2.0",
23 | "snarkjs": "^0.5.0",
24 | "sudoku": "^0.0.3"
25 | },
26 | "devDependencies": {
27 | "@typescript-eslint/eslint-plugin": "^5.47.0",
28 | "@typescript-eslint/parser": "^5.47.0",
29 | "eslint": "^8.30.0",
30 | "eslint-config-prettier": "^8.5.0",
31 | "prettier": "2.8.1",
32 | "typescript": "^4.9.4"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/packages/app/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import "../styles/globals.css";
2 | import "antd/dist/reset.css";
3 | import type { AppProps } from "next/app";
4 | import { ConfigProvider, theme } from "antd";
5 | import { purple } from "@ant-design/colors";
6 |
7 | export default function App({ Component, pageProps }: AppProps) {
8 | return (
9 |
17 |
18 |
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/packages/app/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import { Html, Head, Main, NextScript } from 'next/document'
2 |
3 | export default function Document() {
4 | return (
5 |
6 |
8 |
9 |
10 |
11 |
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/packages/app/pages/api/hello.ts:
--------------------------------------------------------------------------------
1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
2 | import type { NextApiRequest, NextApiResponse } from 'next'
3 |
4 | type Data = {
5 | name: string
6 | }
7 |
8 | export default function handler(
9 | req: NextApiRequest,
10 | res: NextApiResponse
11 | ) {
12 | res.status(200).json({ name: 'John Doe' })
13 | }
14 |
--------------------------------------------------------------------------------
/packages/app/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import { Inter } from "@next/font/google";
2 | import { Col, Row } from "antd";
3 | import Head from "next/head";
4 | import Script from "next/script";
5 | import PlayPannel from "../src/components/PlayPannel";
6 | import VerifyPannel from "../src/components/VerifyPannel";
7 | import { APP_DESCRIPTION, APP_NAME } from "../src/Constants";
8 | import AppLayout from "../src/layout/AppLayout";
9 |
10 | const inter = Inter({ subsets: ["latin"] });
11 |
12 | export default function Home() {
13 | return (
14 | <>
15 |
16 | {APP_NAME}
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | >
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/packages/app/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/web3-master/zksnark-sudoku/b06a4e86cb89bd3bd3768a3b747bdfacc11cbbd5/packages/app/public/favicon.ico
--------------------------------------------------------------------------------
/packages/app/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/app/public/sudoku.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/web3-master/zksnark-sudoku/b06a4e86cb89bd3bd3768a3b747bdfacc11cbbd5/packages/app/public/sudoku.wasm
--------------------------------------------------------------------------------
/packages/app/public/sudoku_1.zkey:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/web3-master/zksnark-sudoku/b06a4e86cb89bd3bd3768a3b747bdfacc11cbbd5/packages/app/public/sudoku_1.zkey
--------------------------------------------------------------------------------
/packages/app/public/sudoku_verify_key.json:
--------------------------------------------------------------------------------
1 | {
2 | "protocol": "groth16",
3 | "curve": "bn128",
4 | "nPublic": 82,
5 | "vk_alpha_1": [
6 | "14697039967689562445643154749404926280040184070425578913349915835224562669743",
7 | "16762804555569869340240899823811983785085138436845348976337256647923989676000",
8 | "1"
9 | ],
10 | "vk_beta_2": [
11 | [
12 | "13550926666431006549801226074126957386008980951214409428333081501478022926788",
13 | "1176684022864160869238684719840086328356349483194938715709165734779863931895"
14 | ],
15 | [
16 | "17739626953086780111315816644086363405007832879759095388896319338468462542869",
17 | "7023860828889943776829894124180587337170588553884189292967446251878161018771"
18 | ],
19 | [
20 | "1",
21 | "0"
22 | ]
23 | ],
24 | "vk_gamma_2": [
25 | [
26 | "10857046999023057135944570762232829481370756359578518086990519993285655852781",
27 | "11559732032986387107991004021392285783925812861821192530917403151452391805634"
28 | ],
29 | [
30 | "8495653923123431417604973247489272438418190587263600148770280649306958101930",
31 | "4082367875863433681332203403145435568316851327593401208105741076214120093531"
32 | ],
33 | [
34 | "1",
35 | "0"
36 | ]
37 | ],
38 | "vk_delta_2": [
39 | [
40 | "14293547728327097023659898855692185102916488530394255721134319994422521158590",
41 | "5781714651090919500007736568292853835134632498748384749907285465077821196701"
42 | ],
43 | [
44 | "11298912066756850517046593998607557991456364741046979386186352759468197783143",
45 | "4062619176113148082972093170522879980818946350503776441401108545511436585369"
46 | ],
47 | [
48 | "1",
49 | "0"
50 | ]
51 | ],
52 | "vk_alphabeta_12": [
53 | [
54 | [
55 | "10894177156777821190736381027104306534550191450663660209619805995046358412628",
56 | "1167079936483964445063725112749366946890311195593188554585133581695653351287"
57 | ],
58 | [
59 | "19671677362511439495298854129364732501151075665517804315504480643694981466326",
60 | "14552450315423715380763280216788995145523221758579940273388701218055964758746"
61 | ],
62 | [
63 | "16444016929034354662400368540778980450632425613850269520403943673675145998269",
64 | "15627884264321903085016687999148208180207190285645282939744898504114277660302"
65 | ]
66 | ],
67 | [
68 | [
69 | "2639684955853089776462219576319685056707708769767539700217149212076026301949",
70 | "16819243589328580116163334851161995224925102456879006780162197980840008066247"
71 | ],
72 | [
73 | "16284703933794593660691353597026897697087210836061824531848935107578124344696",
74 | "19007401541832073411690480031847198043204625264355445910780799816810713398545"
75 | ],
76 | [
77 | "16226068678019113949308574092865153411470058751858878584263693687842563268238",
78 | "8029012422125134015457010393705598851260957538609917723430381188688736829305"
79 | ]
80 | ]
81 | ],
82 | "IC": [
83 | [
84 | "16674621126636918612064463295663362064830612256857853203859781283671698970388",
85 | "3526210784959585889419956435987726379507084510110683502318328302087304228532",
86 | "1"
87 | ],
88 | [
89 | "3809474937022566554529861704724100717605504430132076876475342813552271657484",
90 | "3844525447144267982719051905164660169680652537434046633518338904458887450812",
91 | "1"
92 | ],
93 | [
94 | "3756407382264748171805931335808497545859849885879446303491790034434582938270",
95 | "8968199726872772680956315928530992492247819664472812866088062031148398524941",
96 | "1"
97 | ],
98 | [
99 | "21075902855299675846972196665457955938357165169225209902135057077281885828230",
100 | "7038985870328408743423798913067685105922652868188823696583528247462474346725",
101 | "1"
102 | ],
103 | [
104 | "4655722081613004971570427758947669491021351024490253771047877763465277701652",
105 | "12936086987646697385522060443564248099429167121675837750681390895191943612490",
106 | "1"
107 | ],
108 | [
109 | "21729733723568717732675896624108625294043798912121250253285744057679094909043",
110 | "5778216447125295063798090335781865511168765489490378874322342682752853220665",
111 | "1"
112 | ],
113 | [
114 | "7765092036792312337412191378944922692294985909752793538596346804582412095813",
115 | "12558414816692265201909448003382715254788087023794769676432322021637366604116",
116 | "1"
117 | ],
118 | [
119 | "4330250602547281171284053263035775436237584361815405105775242643302010456964",
120 | "18719075561915998757887690297177031283043006889210153891903688754539000174425",
121 | "1"
122 | ],
123 | [
124 | "6604692899800007787849247770784120615977917412223926757903094204396256599630",
125 | "4013668717815173904865175408666785966899531030755814823455323107718177810935",
126 | "1"
127 | ],
128 | [
129 | "10459237416263531632787623487507079016048522103070220468766058361024189684326",
130 | "970678808318979705584468667448527821721043843324794739363480703275190697967",
131 | "1"
132 | ],
133 | [
134 | "7764692652586235112664589830855318048225057188674647424578878267594879415560",
135 | "4284164769666612630470186995528438631681086170159952774311195516612842138835",
136 | "1"
137 | ],
138 | [
139 | "1498269887384910329829041281906025536368827061924760894484320577823116649276",
140 | "8317098498081901849110788463269529736778430667752664703130737759609985597356",
141 | "1"
142 | ],
143 | [
144 | "15073909422953960186937239575332701616197436417847676496333392774820663193159",
145 | "13502776060545359795294341454867235903684520070807459006412078738633429974527",
146 | "1"
147 | ],
148 | [
149 | "16198702452881691376199474373057937481812061607230555712919785235209421769366",
150 | "18617765112758237650328739103937397205991562513910246907649471918214287555646",
151 | "1"
152 | ],
153 | [
154 | "11872547646973499560962969470872097513046754108136288789108608959678648082334",
155 | "4950732638752235234005433002882360797930928588763878669523652760062129275578",
156 | "1"
157 | ],
158 | [
159 | "7601296981571603949808448562237922334972099778554411371727884724395095430219",
160 | "9029212065816224803836695653539440613250529062102753952210071285744976613033",
161 | "1"
162 | ],
163 | [
164 | "10984177271098619184748190669569954363863422696872415873717171718294119078627",
165 | "13386488196378928116120281043420184576722201991847630290878065229111682693931",
166 | "1"
167 | ],
168 | [
169 | "21382629759814722044024932604174764480361509393329638944837151643768782640498",
170 | "3708975492610678850503082085359462465868736565190294221067245192691554146766",
171 | "1"
172 | ],
173 | [
174 | "16144308410865868451101441537944756979638840420610196225539079881132535433069",
175 | "14257346620073674365446994661226297292835748079015540955005259019923102098513",
176 | "1"
177 | ],
178 | [
179 | "20665512740325766794035745586043077570735658589267131032997396193325276800688",
180 | "19408694872368224850476153551995481222983359317534461373020436719108671204409",
181 | "1"
182 | ],
183 | [
184 | "16155987006372466466135023180174237493001350437568467921227975504681859965672",
185 | "21507916025149581941817996442115105142178613664520174300827325439432947154989",
186 | "1"
187 | ],
188 | [
189 | "3435956956286364419193371856173429590578673128404934453260251617887275863419",
190 | "8925844179035492500085383712664955624316104350353665193958706438122855983313",
191 | "1"
192 | ],
193 | [
194 | "18309746903977251069446707805243726069014115985784826049128599623482409449320",
195 | "11909290000313827654716911780002645868775442750293739592424476799898017994283",
196 | "1"
197 | ],
198 | [
199 | "769092563918852593698287045640170391027334753405391603299750052015313726897",
200 | "19727133611246811049244444527668793231065208319203752349364748754610159112053",
201 | "1"
202 | ],
203 | [
204 | "13425005873683569914368179660175292210677997398931975396844297357620515549201",
205 | "16819514864464287855125076933410052214792318037390961634637590964574922799841",
206 | "1"
207 | ],
208 | [
209 | "18891216565345031498893818024909505984775187012834906324236561319023124707890",
210 | "10105152553717671395628325687463067937795680399117703357853044026767523646923",
211 | "1"
212 | ],
213 | [
214 | "5883773224086516406822457871301756065855251641602704587622370048175463046188",
215 | "6589503027112239334438392516819017329448207061701182293789140387145067854230",
216 | "1"
217 | ],
218 | [
219 | "2805733294529766023079966231831306278300430423402835986187984503850690205815",
220 | "5614131641901406757331257711308128046712617863634798455225883569715989851651",
221 | "1"
222 | ],
223 | [
224 | "15727842461240015694117584390817472626124833631306341171207158159409988528073",
225 | "2097495061292545193544247718060469206742187997956923161080935301271370709590",
226 | "1"
227 | ],
228 | [
229 | "17824428186735621630038529129189704647148083282328703055457997289859958600412",
230 | "8333819176907038094586793079679283784533429597535630586861821807311133026560",
231 | "1"
232 | ],
233 | [
234 | "9524753365581405247854251099029347904245443025772416427037125228205955228423",
235 | "10361483211574658344579820719431638604166384962751593041903828925960417309854",
236 | "1"
237 | ],
238 | [
239 | "8947774354391555175971825816666896483195605303321466713122199008834896317093",
240 | "1840557646372289447965985592577754675599388717372307176788194001657360735449",
241 | "1"
242 | ],
243 | [
244 | "14196373200146151532233407035888334836516669891699218946535309738501354996420",
245 | "20812049815545436173775668632084363693464839733680049737202301957379318312008",
246 | "1"
247 | ],
248 | [
249 | "16463492389427668453955568044302828641961948085843211631183339898057436236730",
250 | "4924506503402774482940006010993445498360457360405195732028647207110876738647",
251 | "1"
252 | ],
253 | [
254 | "8765115602903288504465229759779514556068664964326849252782355088689617591637",
255 | "20005249662473191897692587097864006627879367826876997516109775424323163424340",
256 | "1"
257 | ],
258 | [
259 | "15439239144324854345845241411837789623181756104024591701982438401953310880447",
260 | "21472830735684438475541804455373675367907556052879948569627850535463595993035",
261 | "1"
262 | ],
263 | [
264 | "9124659021395985434721218386941630351362996712204974260726833900610937853740",
265 | "12761068180528502577524199695093978989936083612354437456393846070611116979572",
266 | "1"
267 | ],
268 | [
269 | "9720759036581103188809915819071447162312155518296031775870474782504667567835",
270 | "7543780737377981146018584144785287246863466723545330944913523398977893882826",
271 | "1"
272 | ],
273 | [
274 | "4036521169657801189675155456121114662461382648179924838713868898199846794532",
275 | "17306665787801073505444502489975124742849626527839037416072849567622774776459",
276 | "1"
277 | ],
278 | [
279 | "11289463242065675523503192306365835410314665958896956975386960256473505911669",
280 | "14819694129947386960337457646435699093150117391948174547568602856985866610701",
281 | "1"
282 | ],
283 | [
284 | "2669691218885299377890038289311308292150141879249357386591579571021214799262",
285 | "12381723979720949446876126551639971833114115465132601016528325670281101476566",
286 | "1"
287 | ],
288 | [
289 | "15944883222984750802414856813707795401268205023038592580030973217572593443455",
290 | "18402586254628956944339379473585911485637304693841535273534575034734099414822",
291 | "1"
292 | ],
293 | [
294 | "10613666869635248601740522252624815479348095525769531643893753135534495379014",
295 | "19001362092369838608607269408640284796195000906291401320759615708607589246099",
296 | "1"
297 | ],
298 | [
299 | "153805562806423893693387892991275342898449662847107428127056095644387435908",
300 | "12894674877634760551681985026603925836225037734448499810924167930151707415082",
301 | "1"
302 | ],
303 | [
304 | "11315115765452573750424629436860793245100476516151929614557351574441321991889",
305 | "8977046505922180220417033516488265095295420150143306613677776823442511361183",
306 | "1"
307 | ],
308 | [
309 | "677264861567434849453758836058847982723593791359484751963786822815482386537",
310 | "13639031752467340096530937656857598728664367194161441880045105842051076633563",
311 | "1"
312 | ],
313 | [
314 | "15524628540254999879446058741329655575909325260339465463709430340876377432371",
315 | "16173788331440839240889516690696663039238661168335848329710943530219482745832",
316 | "1"
317 | ],
318 | [
319 | "16262039457541042754810856146443792478836227954735174548431551711371773984188",
320 | "10527921928624114739602186972860851880434204047479415770953417876707624441560",
321 | "1"
322 | ],
323 | [
324 | "8118328726631953237338932818081409446867223137618749718940849149203106745477",
325 | "18595606472194311399253424570071544028223662301114950738433239388324855301586",
326 | "1"
327 | ],
328 | [
329 | "20678274171186229212851085360704949115656610534388288252511932735230960817370",
330 | "207529854981571700629972040997122843446609116901336501106142910884448740778",
331 | "1"
332 | ],
333 | [
334 | "18888943455760916583004364237846465181757068351723594362877109587148992769161",
335 | "6465301537880590565482979460178249617254362554858575494993616111852042024212",
336 | "1"
337 | ],
338 | [
339 | "12364384296970216980120425677211252584534528787419868950589114768462828338478",
340 | "11418642590703424291333166272345828357113662251311534409982838581810140971287",
341 | "1"
342 | ],
343 | [
344 | "5976828498319669951277566324249282103221381236844228262698583007960129534066",
345 | "20874497646620493179651783482056541221705542125525093591790333702665999750045",
346 | "1"
347 | ],
348 | [
349 | "437760574634593577183852092277514078982177299526717663465788890917380867718",
350 | "14975020822869171978342022227419003562681198301358062892172140891024873131166",
351 | "1"
352 | ],
353 | [
354 | "17991264877228786467630792112401130529928117820158369902524357184951592271628",
355 | "892182492587674357184480547249851071656410627596878803579333072541078581851",
356 | "1"
357 | ],
358 | [
359 | "1852236106659837289112725159735587636863747829823583403093666075127670166962",
360 | "16163147010509138271888151923598202211356094901810751332549445869763359255138",
361 | "1"
362 | ],
363 | [
364 | "6613583267135840121545191529174233446950241548634590141340413261717030278886",
365 | "16927876490186237324193111540013082195310713993352125341923469534767770544784",
366 | "1"
367 | ],
368 | [
369 | "11050405497305221479865293680826405838970966607149780304651899759646252315936",
370 | "5434192229904751352713741049722897741562199019983505268206124349260478549020",
371 | "1"
372 | ],
373 | [
374 | "7919640964502670779204384890608780230384056804182783448800198873227207217138",
375 | "17639585322122293197301736927548414828772881574384752699645521419954337937521",
376 | "1"
377 | ],
378 | [
379 | "17680302238424286118565398092333148677990826388522586763565181862062440463131",
380 | "16202759656076042281370446477092835390360212754547046400106627110733448682101",
381 | "1"
382 | ],
383 | [
384 | "1890437479826035121162777489145735215094872138171865550549872586333787465995",
385 | "6602016050402460422459705906625745385039932268107694323748110376938877262335",
386 | "1"
387 | ],
388 | [
389 | "186379946752709761698242326675783753144493157488568037576948783627481827598",
390 | "10383774420570169444632388792682058474662310853988907605188288150489486317883",
391 | "1"
392 | ],
393 | [
394 | "21554901522539003254132966920059071813889284777655558662924641568939692358290",
395 | "3850666581310799687478471663031690988769089951040767276596199047223226862334",
396 | "1"
397 | ],
398 | [
399 | "4649775329254027418773752843848555972412520841754609610514702595658043196821",
400 | "4361586990351916252807019330220749010407865774549994625952216570521602929584",
401 | "1"
402 | ],
403 | [
404 | "14411145121715597440713212747299377311992489475574799818935403353759967269617",
405 | "2913285003577477265094033759257670170123030271713776415527914230059072194307",
406 | "1"
407 | ],
408 | [
409 | "1783010565462317554053576516748202623476871807066245189687206604719258006747",
410 | "13419207804743633676004717854405370312772117030114218051351155851389247086862",
411 | "1"
412 | ],
413 | [
414 | "16121093808377413110462788084725457400815914314082340581179821131476585729364",
415 | "21290338987497251903131466264457008112511646662319196584667868434400306868398",
416 | "1"
417 | ],
418 | [
419 | "3306791940129005399172709477455653901586235324248663015090492200654652801745",
420 | "7482777674250129859976469020178479149120024397574575567284791973686289627231",
421 | "1"
422 | ],
423 | [
424 | "4500416405967560413771688588232393796279049442586700752905692843867329707443",
425 | "971233739240996914591086576801766787706934095173140571395811360627104276791",
426 | "1"
427 | ],
428 | [
429 | "13099111911840512372619562550800762159975819246905615675127307822590297412897",
430 | "2875677763475325596754817376253897696722301299620769703434111502535384520072",
431 | "1"
432 | ],
433 | [
434 | "9474241222861421853993298207808619259860528212009787609419459022297006664327",
435 | "14851051637686448653794141107662753844061841087898990441698499842866508529231",
436 | "1"
437 | ],
438 | [
439 | "8248540969212177216484699675106190326166250096851080183830842661310302877840",
440 | "20522406142980659130822329487728757747199069841517884531555597451488517852405",
441 | "1"
442 | ],
443 | [
444 | "17217055997193478737463162416601371407036251853384869521408335160920324514759",
445 | "16261794228913686763714866592174164191259294527164008175254887182043739386468",
446 | "1"
447 | ],
448 | [
449 | "9118931945645363593476602146923185247460764027761921068781596440306050983378",
450 | "11028231179508415567982988795056597033815754850386850779586981375597838671731",
451 | "1"
452 | ],
453 | [
454 | "15933471501629838920945510172826648040995834242425761193696143062052138724402",
455 | "10159733377605861831006935852206572842546327543602669043169745068384734366230",
456 | "1"
457 | ],
458 | [
459 | "13534791629953017785770527992715703374100133667016603911220660732198190661187",
460 | "8242843443929702558127219219440647206994731468158164369327287902553072285076",
461 | "1"
462 | ],
463 | [
464 | "594371332889861206802387507489706636119647920024795919865235396568934495303",
465 | "6742875188611565656853832182448859955736178961918243350895917146491773527183",
466 | "1"
467 | ],
468 | [
469 | "20790540670521869081432366812298105849339057511475021810271632371131305602964",
470 | "3174437989743997173381602680980815393748565080585165714978539236961477621787",
471 | "1"
472 | ],
473 | [
474 | "15054642055490718631628732937276748816293584257797956569049776725817578187286",
475 | "5477329138321798263414375682555511677131638410010840961814187674725732973532",
476 | "1"
477 | ],
478 | [
479 | "7156821136076554828169897384582936425524128421572433116694478941118284236100",
480 | "6738642874823334321543250703957772585258826407629659857558138037584026300332",
481 | "1"
482 | ],
483 | [
484 | "17019807554900492831335914539004680275514350536833534214219268927348968295285",
485 | "15317920988520552634309290858926294722588593524051136255266960590393260331285",
486 | "1"
487 | ],
488 | [
489 | "6588916274759987090984843844682109024513750708665305634671891694615026692675",
490 | "19624536060889918192687061673015827981963692536285191890015157631482093337414",
491 | "1"
492 | ],
493 | [
494 | "5430688957538527075136560661073457336340487530276788150829795788575907782176",
495 | "9898910962334823660044078404319950243010186165215844627514324815613093652992",
496 | "1"
497 | ]
498 | ]
499 | }
--------------------------------------------------------------------------------
/packages/app/public/thirteen.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/app/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/app/src/Constants.tsx:
--------------------------------------------------------------------------------
1 | export const APP_NAME = 'zkSNARK Sudoku';
2 | export const APP_DESCRIPTION =
3 | 'This is zkSNARK Sudoku play and verify application.';
4 |
5 | export const GMAIL = 'mailto:dany.armstrong90@gmail.com';
6 | export const LINKEDIN = 'http://www.linkedin.com/in/daniel-armstrong90';
7 | export const GITHUB = 'https://github.com/web3-master';
8 | export const GITHUB_PROJECT = 'https://github.com/web3-master/zksnark-sudoku';
9 |
10 | export const CELL_SIZE = 40;
11 |
--------------------------------------------------------------------------------
/packages/app/src/components/KeyboardView.tsx:
--------------------------------------------------------------------------------
1 | import { Button, Col, Row } from 'antd';
2 | import { CELL_SIZE } from '../Constants';
3 |
4 | interface Props {
5 | onButtonClick: (value: number) => void;
6 | }
7 |
8 | const KeyboardView: React.FC = ({ onButtonClick }) => {
9 | return (
10 |
11 | {Array(9)
12 | .fill(0)
13 | .map((_, index) => (
14 |
15 |
24 |
25 | ))}
26 |
27 |
36 |
37 |
38 | );
39 | };
40 |
41 | export default KeyboardView;
42 |
--------------------------------------------------------------------------------
/packages/app/src/components/PlayPannel.tsx:
--------------------------------------------------------------------------------
1 | import { Button, Card, Col, Row, Spin, Typography, message } from 'antd';
2 | import React, { useState } from 'react';
3 | import { generatePuzzle, getSolutionOfPuzzle } from '../utils/GameUtils';
4 | import KeyboardView from './KeyboardView';
5 | import ProofView from './ProofView';
6 | import PuzzleView from './PuzzleView';
7 |
8 | const PlayPannel: React.FC = () => {
9 | const [puzzle, setPuzzle] = useState(Array(81).fill(0));
10 | const [solution, setSolution] = useState(Array(81).fill(0));
11 | const [selectedCellIndex, setSelectedCellIndex] = useState(-1);
12 |
13 | const [proof, setProof] = useState('');
14 | const [proofCalculating, setProofCalculating] = useState(false);
15 |
16 | const [messageApi, contextHolder] = message.useMessage();
17 |
18 | const onKeyButtonClick = (value: number) => {
19 | if (selectedCellIndex == -1) return;
20 | if (puzzle[selectedCellIndex] > 0) return;
21 |
22 | const newSolution = [...solution];
23 | newSolution[selectedCellIndex] = value;
24 | setSolution(newSolution);
25 | };
26 |
27 | const onCellClick = (index: number) => {
28 | setSelectedCellIndex(index);
29 | };
30 |
31 | const onNewPuzzle = () => {
32 | const newPuzzle = generatePuzzle();
33 | setPuzzle(newPuzzle);
34 | setSolution(Array(81).fill(0));
35 | setSelectedCellIndex(-1);
36 | };
37 |
38 | const onSolvePuzzle = () => {
39 | const solution = getSolutionOfPuzzle(puzzle);
40 | setSolution(solution);
41 | };
42 |
43 | const onEraseSolution = () => {
44 | setSolution(Array(81).fill(0));
45 | setSelectedCellIndex(-1);
46 | };
47 |
48 | const onSavePuzzle = () => {
49 | const puzzleData = JSON.stringify(puzzle);
50 | const blob = new Blob([puzzleData], { type: 'application/json' });
51 | const url = URL.createObjectURL(blob);
52 | const link = document.createElement('a');
53 | link.download = 'puzzle.json';
54 | link.href = url;
55 | link.click();
56 | };
57 |
58 | const onGenerateProof = async () => {
59 | const input = {
60 | puzzle: puzzle,
61 | solution: solution,
62 | };
63 |
64 | setProofCalculating(true);
65 |
66 | const { proof, publicSignals } = await snarkjs.groth16.fullProve(
67 | input,
68 | 'sudoku.wasm',
69 | 'sudoku_1.zkey'
70 | );
71 |
72 | setProofCalculating(false);
73 |
74 | const circuitOutputSignal = publicSignals[0];
75 | if (circuitOutputSignal === '1') {
76 | setProof(JSON.stringify(proof));
77 | messageApi.success(
78 | 'Your proof is generated. You can make sure others that you have solved this puzzle without sharing solution.',
79 | 5
80 | );
81 | } else {
82 | messageApi.error(
83 | "Your solution isn't correct. Please solve the puzzle correctly!",
84 | 5
85 | );
86 | }
87 | };
88 |
89 | const onSaveProof = () => {
90 | if (proof === '') {
91 | messageApi.error('Please generate proof for your solution!');
92 | return;
93 | }
94 |
95 | const blob = new Blob([proof], { type: 'application/json' });
96 | const url = URL.createObjectURL(blob);
97 | const link = document.createElement('a');
98 | link.download = 'proof.json';
99 | link.href = url;
100 | link.click();
101 | };
102 |
103 | return (
104 | <>
105 | {contextHolder}
106 |
107 |
108 | PLAY
109 |
110 |
111 |
112 |
113 |
114 |
115 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
131 |
132 |
133 |
136 |
137 |
138 |
141 |
142 |
143 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
156 |
157 |
158 |
161 |
162 |
163 |
164 |
165 | >
166 | );
167 | };
168 |
169 | export default PlayPannel;
170 |
--------------------------------------------------------------------------------
/packages/app/src/components/ProofView.tsx:
--------------------------------------------------------------------------------
1 | import { Input } from 'antd';
2 |
3 | interface Props {
4 | proof: string;
5 | disabled: boolean;
6 | }
7 |
8 | const ProofView: React.FC = ({ proof, disabled }) => {
9 | return ;
10 | };
11 |
12 | export default ProofView;
13 |
--------------------------------------------------------------------------------
/packages/app/src/components/PuzzleView.tsx:
--------------------------------------------------------------------------------
1 | import { Button, Col, Row } from 'antd';
2 | import { CELL_SIZE } from '../Constants';
3 |
4 | interface PuzzleViewProps {
5 | puzzle: number[];
6 | solution?: number[];
7 | selectedCellIndex?: number;
8 | onCellClick?: (index: number) => void;
9 | }
10 |
11 | const PuzzleView: React.FC = ({
12 | puzzle,
13 | solution = Array(81).fill(0),
14 | selectedCellIndex = -1,
15 | onCellClick = undefined,
16 | }) => {
17 | return (
18 |
23 | {Array(9)
24 | .fill(0)
25 | .map((_, row) => (
26 |
27 | {Array(9)
28 | .fill(0)
29 | .map((_, col) => {
30 | const index = row * 9 + col;
31 | return (
32 |
33 | 0}
40 | onClick={onCellClick}
41 | />
42 |
43 | );
44 | })}
45 | |
46 | ))}
47 |
48 | );
49 | };
50 |
51 | interface CellProps {
52 | value: number;
53 | index: number;
54 | selected: boolean;
55 | disabled?: boolean;
56 | onClick?: (index: number) => void;
57 | }
58 | const Cell: React.FC = ({
59 | value,
60 | index,
61 | selected,
62 | disabled = false,
63 | onClick = undefined,
64 | }) => {
65 | const row = Math.floor(index / 9);
66 | const col = index % 9;
67 | return (
68 |
83 | );
84 | };
85 |
86 | export default PuzzleView;
87 |
--------------------------------------------------------------------------------
/packages/app/src/components/VerifyPannel.tsx:
--------------------------------------------------------------------------------
1 | import { Button, Card, Col, message, Row, Spin, Typography } from 'antd';
2 | import { ChangeEvent, useRef, useState } from 'react';
3 | import ProofView from './ProofView';
4 | import PuzzleView from './PuzzleView';
5 |
6 | export default function VerifyPannel() {
7 | const [puzzle, setPuzzle] = useState(Array(81).fill(0));
8 | const [proof, setProof] = useState('');
9 | const [verified, setVerified] = useState(false);
10 |
11 | const puzzleFile = useRef(null);
12 | const proofFile = useRef(null);
13 | const [verifying, setVerifying] = useState(false);
14 |
15 | const [messageApi, contextHolder] = message.useMessage();
16 |
17 | const isValidPuzzleData = (puzzleData: number[]): boolean => {
18 | return puzzleData.length == 81;
19 | };
20 |
21 | const isValidProofData = (proofData: {
22 | pi_a: [];
23 | pi_b: [];
24 | pi_c: [];
25 | protocol: string;
26 | curve: string;
27 | }): boolean => {
28 | return proofData.protocol === 'groth16' && proofData.curve === 'bn128';
29 | };
30 |
31 | const onLoadPuzzle = () => {
32 | if (puzzleFile.current != null) puzzleFile.current.click();
33 | setVerified(false);
34 | };
35 |
36 | const onFileSelected = (event: ChangeEvent) => {
37 | event.stopPropagation();
38 | event.preventDefault();
39 | if (event.target.files != null && event.target.files.length > 0) {
40 | const file = event.target.files[0];
41 | const fileReader = new FileReader();
42 | fileReader.onload = (e) => {
43 | if (e.target != null) {
44 | try {
45 | const fileContent = JSON.parse(e.target.result as string);
46 | if (event.target == puzzleFile.current) {
47 | if (isValidPuzzleData(fileContent)) {
48 | setPuzzle(fileContent);
49 | } else {
50 | messageApi.error('Selected file is not valid puzzle file!');
51 | }
52 | } else if (event.target == proofFile.current) {
53 | if (isValidProofData(fileContent)) {
54 | setProof(JSON.stringify(fileContent));
55 | } else {
56 | messageApi.error('Selected file is not valid proof file!');
57 | }
58 | }
59 | } catch (error) {
60 | messageApi.error('You selected wrong file!');
61 | }
62 | }
63 | };
64 | fileReader.readAsText(file, 'UTF-8');
65 | }
66 | };
67 |
68 | const onLoadProof = () => {
69 | setVerified(false);
70 | if (proofFile.current != null) proofFile.current.click();
71 | };
72 |
73 | const onVerifyProof = async () => {
74 | if (proof === '') {
75 | messageApi.error('Please select your proof file!');
76 | return;
77 | }
78 |
79 | const vkey = await fetch('sudoku_verify_key.json').then((res) => {
80 | return res.json();
81 | });
82 |
83 | setVerifying(true);
84 |
85 | //
86 | // This first "1" is the circuit's output signal, which means the solution is correct.
87 | //
88 | const publicSignals = ['1', ...puzzle];
89 | const proofData = JSON.parse(proof);
90 | const res = await snarkjs.groth16.verify(vkey, publicSignals, proofData);
91 |
92 | setVerifying(false);
93 |
94 | if (res) {
95 | messageApi.success('Your proof is verified. You solved the puzzle.', 5);
96 | } else {
97 | messageApi.error("Your proof isn't correct.", 5);
98 | }
99 | setVerified(res);
100 | };
101 |
102 | return (
103 | <>
104 | {contextHolder}
105 |
112 |
119 |
120 |
121 | VERIFY
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
132 |
133 |
134 |
135 |
139 |
140 |
141 |
142 |
145 |
146 |
147 |
150 |
151 |
152 |
153 |
154 | >
155 | );
156 | }
157 |
--------------------------------------------------------------------------------
/packages/app/src/images/sudoku.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/web3-master/zksnark-sudoku/b06a4e86cb89bd3bd3768a3b747bdfacc11cbbd5/packages/app/src/images/sudoku.png
--------------------------------------------------------------------------------
/packages/app/src/layout/AppLayout.tsx:
--------------------------------------------------------------------------------
1 | import { purple } from '@ant-design/colors';
2 | import {
3 | GithubOutlined,
4 | GoogleOutlined,
5 | LinkedinOutlined,
6 | } from '@ant-design/icons';
7 | import { Col, Layout, Row } from 'antd';
8 | import { Content, Footer, Header } from 'antd/lib/layout/layout';
9 | import Image from 'next/image';
10 | import { ReactNode } from 'react';
11 | import {
12 | APP_NAME,
13 | GITHUB,
14 | GITHUB_PROJECT,
15 | GMAIL,
16 | LINKEDIN,
17 | } from '../Constants';
18 | import logo from '../images/sudoku.png';
19 |
20 | export default function AppLayout({ children }: { children: ReactNode }) {
21 | return (
22 |
23 |
24 |
25 |
33 |
34 |
35 |
36 |
37 |
38 | {APP_NAME}
39 |
40 |
41 |
50 |
51 |
52 |
53 | {children}
54 |
72 |
73 |
74 |
75 | );
76 | }
77 |
--------------------------------------------------------------------------------
/packages/app/src/utils/GameUtils.ts:
--------------------------------------------------------------------------------
1 | import { makepuzzle, solvepuzzle } from 'sudoku';
2 |
3 | function nullToZero(board: number[]) {
4 | return board.map((number) => (number === null ? 0 : number + 1));
5 | }
6 |
7 | function zeroToNull(board: number[]) {
8 | return board.map((number) => (number === 0 ? null : number - 1));
9 | }
10 |
11 | export function generatePuzzle(): number[] {
12 | const puzzle = makepuzzle();
13 | return nullToZero(puzzle);
14 | }
15 |
16 | export function getSolutionOfPuzzle(puzzle: number[]): number[] {
17 | const solution = solvepuzzle(zeroToNull(puzzle));
18 | return nullToZero(solution);
19 | }
20 |
--------------------------------------------------------------------------------
/packages/app/styles/Home.module.css:
--------------------------------------------------------------------------------
1 | .main {
2 | display: flex;
3 | flex-direction: column;
4 | justify-content: space-between;
5 | align-items: center;
6 | padding: 6rem;
7 | min-height: 100vh;
8 | }
9 |
10 | .description {
11 | display: inherit;
12 | justify-content: inherit;
13 | align-items: inherit;
14 | font-size: 0.85rem;
15 | max-width: var(--max-width);
16 | width: 100%;
17 | z-index: 2;
18 | font-family: var(--font-mono);
19 | }
20 |
21 | .description a {
22 | display: flex;
23 | justify-content: center;
24 | align-items: center;
25 | gap: 0.5rem;
26 | }
27 |
28 | .description p {
29 | position: relative;
30 | margin: 0;
31 | padding: 1rem;
32 | background-color: rgba(var(--callout-rgb), 0.5);
33 | border: 1px solid rgba(var(--callout-border-rgb), 0.3);
34 | border-radius: var(--border-radius);
35 | }
36 |
37 | .code {
38 | font-weight: 700;
39 | font-family: var(--font-mono);
40 | }
41 |
42 | .grid {
43 | display: grid;
44 | grid-template-columns: repeat(4, minmax(25%, auto));
45 | width: var(--max-width);
46 | max-width: 100%;
47 | }
48 |
49 | .card {
50 | padding: 1rem 1.2rem;
51 | border-radius: var(--border-radius);
52 | background: rgba(var(--card-rgb), 0);
53 | border: 1px solid rgba(var(--card-border-rgb), 0);
54 | transition: background 200ms, border 200ms;
55 | }
56 |
57 | .card span {
58 | display: inline-block;
59 | transition: transform 200ms;
60 | }
61 |
62 | .card h2 {
63 | font-weight: 600;
64 | margin-bottom: 0.7rem;
65 | }
66 |
67 | .card p {
68 | margin: 0;
69 | opacity: 0.6;
70 | font-size: 0.9rem;
71 | line-height: 1.5;
72 | max-width: 30ch;
73 | }
74 |
75 | .center {
76 | display: flex;
77 | justify-content: center;
78 | align-items: center;
79 | position: relative;
80 | padding: 4rem 0;
81 | }
82 |
83 | .center::before {
84 | background: var(--secondary-glow);
85 | border-radius: 50%;
86 | width: 480px;
87 | height: 360px;
88 | margin-left: -400px;
89 | }
90 |
91 | .center::after {
92 | background: var(--primary-glow);
93 | width: 240px;
94 | height: 180px;
95 | z-index: -1;
96 | }
97 |
98 | .center::before,
99 | .center::after {
100 | content: '';
101 | left: 50%;
102 | position: absolute;
103 | filter: blur(45px);
104 | transform: translateZ(0);
105 | }
106 |
107 | .logo,
108 | .thirteen {
109 | position: relative;
110 | }
111 |
112 | .thirteen {
113 | display: flex;
114 | justify-content: center;
115 | align-items: center;
116 | width: 75px;
117 | height: 75px;
118 | padding: 25px 10px;
119 | margin-left: 16px;
120 | transform: translateZ(0);
121 | border-radius: var(--border-radius);
122 | overflow: hidden;
123 | box-shadow: 0px 2px 8px -1px #0000001a;
124 | }
125 |
126 | .thirteen::before,
127 | .thirteen::after {
128 | content: '';
129 | position: absolute;
130 | z-index: -1;
131 | }
132 |
133 | /* Conic Gradient Animation */
134 | .thirteen::before {
135 | animation: 6s rotate linear infinite;
136 | width: 200%;
137 | height: 200%;
138 | background: var(--tile-border);
139 | }
140 |
141 | /* Inner Square */
142 | .thirteen::after {
143 | inset: 0;
144 | padding: 1px;
145 | border-radius: var(--border-radius);
146 | background: linear-gradient(
147 | to bottom right,
148 | rgba(var(--tile-start-rgb), 1),
149 | rgba(var(--tile-end-rgb), 1)
150 | );
151 | background-clip: content-box;
152 | }
153 |
154 | /* Enable hover only on non-touch devices */
155 | @media (hover: hover) and (pointer: fine) {
156 | .card:hover {
157 | background: rgba(var(--card-rgb), 0.1);
158 | border: 1px solid rgba(var(--card-border-rgb), 0.15);
159 | }
160 |
161 | .card:hover span {
162 | transform: translateX(4px);
163 | }
164 | }
165 |
166 | @media (prefers-reduced-motion) {
167 | .thirteen::before {
168 | animation: none;
169 | }
170 |
171 | .card:hover span {
172 | transform: none;
173 | }
174 | }
175 |
176 | /* Mobile */
177 | @media (max-width: 700px) {
178 | .content {
179 | padding: 4rem;
180 | }
181 |
182 | .grid {
183 | grid-template-columns: 1fr;
184 | margin-bottom: 120px;
185 | max-width: 320px;
186 | text-align: center;
187 | }
188 |
189 | .card {
190 | padding: 1rem 2.5rem;
191 | }
192 |
193 | .card h2 {
194 | margin-bottom: 0.5rem;
195 | }
196 |
197 | .center {
198 | padding: 8rem 0 6rem;
199 | }
200 |
201 | .center::before {
202 | transform: none;
203 | height: 300px;
204 | }
205 |
206 | .description {
207 | font-size: 0.8rem;
208 | }
209 |
210 | .description a {
211 | padding: 1rem;
212 | }
213 |
214 | .description p,
215 | .description div {
216 | display: flex;
217 | justify-content: center;
218 | position: fixed;
219 | width: 100%;
220 | }
221 |
222 | .description p {
223 | align-items: center;
224 | inset: 0 0 auto;
225 | padding: 2rem 1rem 1.4rem;
226 | border-radius: 0;
227 | border: none;
228 | border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25);
229 | background: linear-gradient(
230 | to bottom,
231 | rgba(var(--background-start-rgb), 1),
232 | rgba(var(--callout-rgb), 0.5)
233 | );
234 | background-clip: padding-box;
235 | backdrop-filter: blur(24px);
236 | }
237 |
238 | .description div {
239 | align-items: flex-end;
240 | pointer-events: none;
241 | inset: auto 0 0;
242 | padding: 2rem;
243 | height: 200px;
244 | background: linear-gradient(
245 | to bottom,
246 | transparent 0%,
247 | rgb(var(--background-end-rgb)) 40%
248 | );
249 | z-index: 1;
250 | }
251 | }
252 |
253 | /* Tablet and Smaller Desktop */
254 | @media (min-width: 701px) and (max-width: 1120px) {
255 | .grid {
256 | grid-template-columns: repeat(2, 50%);
257 | }
258 | }
259 |
260 | @media (prefers-color-scheme: dark) {
261 | .vercelLogo {
262 | filter: invert(1);
263 | }
264 |
265 | .logo,
266 | .thirteen img {
267 | filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70);
268 | }
269 | }
270 |
271 | @keyframes rotate {
272 | from {
273 | transform: rotate(360deg);
274 | }
275 | to {
276 | transform: rotate(0deg);
277 | }
278 | }
279 |
--------------------------------------------------------------------------------
/packages/app/styles/globals.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --max-width: 1100px;
3 | --border-radius: 12px;
4 | --font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono',
5 | 'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro',
6 | 'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace;
7 |
8 | --foreground-rgb: 0, 0, 0;
9 | --background-start-rgb: 214, 219, 220;
10 | --background-end-rgb: 255, 255, 255;
11 |
12 | --primary-glow: conic-gradient(
13 | from 180deg at 50% 50%,
14 | #16abff33 0deg,
15 | #0885ff33 55deg,
16 | #54d6ff33 120deg,
17 | #0071ff33 160deg,
18 | transparent 360deg
19 | );
20 | --secondary-glow: radial-gradient(
21 | rgba(255, 255, 255, 1),
22 | rgba(255, 255, 255, 0)
23 | );
24 |
25 | --tile-start-rgb: 239, 245, 249;
26 | --tile-end-rgb: 228, 232, 233;
27 | --tile-border: conic-gradient(
28 | #00000080,
29 | #00000040,
30 | #00000030,
31 | #00000020,
32 | #00000010,
33 | #00000010,
34 | #00000080
35 | );
36 |
37 | --callout-rgb: 238, 240, 241;
38 | --callout-border-rgb: 172, 175, 176;
39 | --card-rgb: 180, 185, 188;
40 | --card-border-rgb: 131, 134, 135;
41 | }
42 |
43 | @media (prefers-color-scheme: dark) {
44 | :root {
45 | --foreground-rgb: 255, 255, 255;
46 | --background-start-rgb: 0, 0, 0;
47 | --background-end-rgb: 0, 0, 0;
48 |
49 | --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0));
50 | --secondary-glow: linear-gradient(
51 | to bottom right,
52 | rgba(1, 65, 255, 0),
53 | rgba(1, 65, 255, 0),
54 | rgba(1, 65, 255, 0.3)
55 | );
56 |
57 | --tile-start-rgb: 2, 13, 46;
58 | --tile-end-rgb: 2, 5, 19;
59 | --tile-border: conic-gradient(
60 | #ffffff80,
61 | #ffffff40,
62 | #ffffff30,
63 | #ffffff20,
64 | #ffffff10,
65 | #ffffff10,
66 | #ffffff80
67 | );
68 |
69 | --callout-rgb: 20, 20, 20;
70 | --callout-border-rgb: 108, 108, 108;
71 | --card-rgb: 100, 100, 100;
72 | --card-border-rgb: 200, 200, 200;
73 | }
74 | }
75 |
76 | * {
77 | box-sizing: border-box;
78 | padding: 0;
79 | margin: 0;
80 | }
81 |
82 | html,
83 | body {
84 | max-width: 100vw;
85 | overflow-x: hidden;
86 | }
87 |
88 | body {
89 | color: rbg(--foreground-rgb);
90 | background: linear-gradient(
91 | to bottom,
92 | transparent,
93 | rgb(var(--background-end-rgb))
94 | )
95 | rgb(var(--background-start-rgb));
96 | }
97 |
98 | a {
99 | color: inherit;
100 | text-decoration: none;
101 | }
102 |
103 | @media (prefers-color-scheme: dark) {
104 | html {
105 | color-scheme: dark;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/packages/app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true
17 | },
18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------
/packages/circuit/circuits/circomlib/aliascheck.circom:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 0KIMS association.
3 |
4 | This file is part of circom (Zero Knowledge Circuit Compiler).
5 |
6 | circom is a free software: you can redistribute it and/or modify it
7 | under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | circom is distributed in the hope that it will be useful, but WITHOUT
12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 | License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with circom. If not, see .
18 | */
19 | pragma circom 2.0.0;
20 |
21 | include "compconstant.circom";
22 |
23 |
24 | template AliasCheck() {
25 |
26 | signal input in[254];
27 |
28 | component compConstant = CompConstant(-1);
29 |
30 | for (var i=0; i<254; i++) in[i] ==> compConstant.in[i];
31 |
32 | compConstant.out === 0;
33 | }
34 |
--------------------------------------------------------------------------------
/packages/circuit/circuits/circomlib/binsum.circom:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 0KIMS association.
3 |
4 | This file is part of circom (Zero Knowledge Circuit Compiler).
5 |
6 | circom is a free software: you can redistribute it and/or modify it
7 | under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | circom is distributed in the hope that it will be useful, but WITHOUT
12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 | License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with circom. If not, see .
18 | */
19 |
20 | /*
21 |
22 | Binary Sum
23 | ==========
24 |
25 | This component creates a binary sum componet of ops operands and n bits each operand.
26 |
27 | e is Number of carries: Depends on the number of operands in the input.
28 |
29 | Main Constraint:
30 | in[0][0] * 2^0 + in[0][1] * 2^1 + ..... + in[0][n-1] * 2^(n-1) +
31 | + in[1][0] * 2^0 + in[1][1] * 2^1 + ..... + in[1][n-1] * 2^(n-1) +
32 | + ..
33 | + in[ops-1][0] * 2^0 + in[ops-1][1] * 2^1 + ..... + in[ops-1][n-1] * 2^(n-1) +
34 | ===
35 | out[0] * 2^0 + out[1] * 2^1 + + out[n+e-1] *2(n+e-1)
36 |
37 | To waranty binary outputs:
38 |
39 | out[0] * (out[0] - 1) === 0
40 | out[1] * (out[0] - 1) === 0
41 | .
42 | .
43 | .
44 | out[n+e-1] * (out[n+e-1] - 1) == 0
45 |
46 | */
47 |
48 |
49 | /*
50 | This function calculates the number of extra bits in the output to do the full sum.
51 | */
52 | pragma circom 2.0.0;
53 |
54 | function nbits(a) {
55 | var n = 1;
56 | var r = 0;
57 | while (n-1> k) & 1;
89 |
90 | // Ensure out is binary
91 | out[k] * (out[k] - 1) === 0;
92 |
93 | lout += out[k] * e2;
94 |
95 | e2 = e2+e2;
96 | }
97 |
98 | // Ensure the sum;
99 |
100 | lin === lout;
101 | }
102 |
--------------------------------------------------------------------------------
/packages/circuit/circuits/circomlib/bitify.circom:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 0KIMS association.
3 |
4 | This file is part of circom (Zero Knowledge Circuit Compiler).
5 |
6 | circom is a free software: you can redistribute it and/or modify it
7 | under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | circom is distributed in the hope that it will be useful, but WITHOUT
12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 | License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with circom. If not, see .
18 | */
19 | pragma circom 2.0.0;
20 |
21 | include "comparators.circom";
22 | include "aliascheck.circom";
23 |
24 |
25 | template Num2Bits(n) {
26 | signal input in;
27 | signal output out[n];
28 | var lc1=0;
29 |
30 | var e2=1;
31 | for (var i = 0; i> i) & 1;
33 | out[i] * (out[i] -1 ) === 0;
34 | lc1 += out[i] * e2;
35 | e2 = e2+e2;
36 | }
37 |
38 | lc1 === in;
39 | }
40 |
41 | template Num2Bits_strict() {
42 | signal input in;
43 | signal output out[254];
44 |
45 | component aliasCheck = AliasCheck();
46 | component n2b = Num2Bits(254);
47 | in ==> n2b.in;
48 |
49 | for (var i=0; i<254; i++) {
50 | n2b.out[i] ==> out[i];
51 | n2b.out[i] ==> aliasCheck.in[i];
52 | }
53 | }
54 |
55 | template Bits2Num(n) {
56 | signal input in[n];
57 | signal output out;
58 | var lc1=0;
59 |
60 | var e2 = 1;
61 | for (var i = 0; i out;
67 | }
68 |
69 | template Bits2Num_strict() {
70 | signal input in[254];
71 | signal output out;
72 |
73 | component aliasCheck = AliasCheck();
74 | component b2n = Bits2Num(254);
75 |
76 | for (var i=0; i<254; i++) {
77 | in[i] ==> b2n.in[i];
78 | in[i] ==> aliasCheck.in[i];
79 | }
80 |
81 | b2n.out ==> out;
82 | }
83 |
84 | template Num2BitsNeg(n) {
85 | signal input in;
86 | signal output out[n];
87 | var lc1=0;
88 |
89 | component isZero;
90 |
91 | isZero = IsZero();
92 |
93 | var neg = n == 0 ? 0 : 2**n - in;
94 |
95 | for (var i = 0; i> i) & 1;
97 | out[i] * (out[i] -1 ) === 0;
98 | lc1 += out[i] * 2**i;
99 | }
100 |
101 | in ==> isZero.in;
102 |
103 |
104 |
105 | lc1 + isZero.out * 2**n === 2**n - in;
106 | }
107 |
--------------------------------------------------------------------------------
/packages/circuit/circuits/circomlib/comparators.circom:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 0KIMS association.
3 |
4 | This file is part of circom (Zero Knowledge Circuit Compiler).
5 |
6 | circom is a free software: you can redistribute it and/or modify it
7 | under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | circom is distributed in the hope that it will be useful, but WITHOUT
12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 | License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with circom. If not, see .
18 | */
19 | pragma circom 2.0.0;
20 |
21 | include "bitify.circom";
22 | include "binsum.circom";
23 |
24 | template IsZero() {
25 | signal input in;
26 | signal output out;
27 |
28 | signal inv;
29 |
30 | inv <-- in!=0 ? 1/in : 0;
31 |
32 | out <== -in*inv +1;
33 | in*out === 0;
34 | }
35 |
36 |
37 | template IsEqual() {
38 | signal input in[2];
39 | signal output out;
40 |
41 | component isz = IsZero();
42 |
43 | in[1] - in[0] ==> isz.in;
44 |
45 | isz.out ==> out;
46 | }
47 |
48 | template ForceEqualIfEnabled() {
49 | signal input enabled;
50 | signal input in[2];
51 |
52 | component isz = IsZero();
53 |
54 | in[1] - in[0] ==> isz.in;
55 |
56 | (1 - isz.out)*enabled === 0;
57 | }
58 |
59 | /*
60 | // N is the number of bits the input have.
61 | // The MSF is the sign bit.
62 | template LessThan(n) {
63 | signal input in[2];
64 | signal output out;
65 |
66 | component num2Bits0;
67 | component num2Bits1;
68 |
69 | component adder;
70 |
71 | adder = BinSum(n, 2);
72 |
73 | num2Bits0 = Num2Bits(n);
74 | num2Bits1 = Num2BitsNeg(n);
75 |
76 | in[0] ==> num2Bits0.in;
77 | in[1] ==> num2Bits1.in;
78 |
79 | var i;
80 | for (i=0;i adder.in[0][i];
82 | num2Bits1.out[i] ==> adder.in[1][i];
83 | }
84 |
85 | adder.out[n-1] ==> out;
86 | }
87 | */
88 |
89 | template LessThan(n) {
90 | assert(n <= 252);
91 | signal input in[2];
92 | signal output out;
93 |
94 | component n2b = Num2Bits(n+1);
95 |
96 | n2b.in <== in[0]+ (1< out;
114 | }
115 |
116 | // N is the number of bits the input have.
117 | // The MSF is the sign bit.
118 | template GreaterThan(n) {
119 | signal input in[2];
120 | signal output out;
121 |
122 | component lt = LessThan(n);
123 |
124 | lt.in[0] <== in[1];
125 | lt.in[1] <== in[0];
126 | lt.out ==> out;
127 | }
128 |
129 | // N is the number of bits the input have.
130 | // The MSF is the sign bit.
131 | template GreaterEqThan(n) {
132 | signal input in[2];
133 | signal output out;
134 |
135 | component lt = LessThan(n);
136 |
137 | lt.in[0] <== in[1];
138 | lt.in[1] <== in[0]+1;
139 | lt.out ==> out;
140 | }
141 |
142 |
--------------------------------------------------------------------------------
/packages/circuit/circuits/circomlib/compconstant.circom:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 0KIMS association.
3 |
4 | This file is part of circom (Zero Knowledge Circuit Compiler).
5 |
6 | circom is a free software: you can redistribute it and/or modify it
7 | under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | circom is distributed in the hope that it will be useful, but WITHOUT
12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 | License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with circom. If not, see .
18 | */
19 | pragma circom 2.0.0;
20 |
21 | include "bitify.circom";
22 |
23 | // Returns 1 if in (in binary) > ct
24 |
25 | template CompConstant(ct) {
26 | signal input in[254];
27 | signal output out;
28 |
29 | signal parts[127];
30 | signal sout;
31 |
32 | var clsb;
33 | var cmsb;
34 | var slsb;
35 | var smsb;
36 |
37 | var sum=0;
38 |
39 | var b = (1 << 128) -1;
40 | var a = 1;
41 | var e = 1;
42 | var i;
43 |
44 | for (i=0;i<127; i++) {
45 | clsb = (ct >> (i*2)) & 1;
46 | cmsb = (ct >> (i*2+1)) & 1;
47 | slsb = in[i*2];
48 | smsb = in[i*2+1];
49 |
50 | if ((cmsb==0)&&(clsb==0)) {
51 | parts[i] <== -b*smsb*slsb + b*smsb + b*slsb;
52 | } else if ((cmsb==0)&&(clsb==1)) {
53 | parts[i] <== a*smsb*slsb - a*slsb + b*smsb - a*smsb + a;
54 | } else if ((cmsb==1)&&(clsb==0)) {
55 | parts[i] <== b*smsb*slsb - a*smsb + a;
56 | } else {
57 | parts[i] <== -a*smsb*slsb + a;
58 | }
59 |
60 | sum = sum + parts[i];
61 |
62 | b = b -e;
63 | a = a +e;
64 | e = e*2;
65 | }
66 |
67 | sout <== sum;
68 |
69 | component num2bits = Num2Bits(135);
70 |
71 | num2bits.in <== sout;
72 |
73 | out <== num2bits.out[127];
74 | }
75 |
--------------------------------------------------------------------------------
/packages/circuit/circuits/circomlib/gates.circom:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2018 0KIMS association.
3 |
4 | This file is part of circom (Zero Knowledge Circuit Compiler).
5 |
6 | circom is a free software: you can redistribute it and/or modify it
7 | under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | circom is distributed in the hope that it will be useful, but WITHOUT
12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 | License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with circom. If not, see .
18 | */
19 | pragma circom 2.0.0;
20 |
21 | template XOR() {
22 | signal input a;
23 | signal input b;
24 | signal output out;
25 |
26 | out <== a + b - 2*a*b;
27 | }
28 |
29 | template AND() {
30 | signal input a;
31 | signal input b;
32 | signal output out;
33 |
34 | out <== a*b;
35 | }
36 |
37 | template OR() {
38 | signal input a;
39 | signal input b;
40 | signal output out;
41 |
42 | out <== a + b - a*b;
43 | }
44 |
45 | template NOT() {
46 | signal input in;
47 | signal output out;
48 |
49 | out <== 1 + in - 2*in;
50 | }
51 |
52 | template NAND() {
53 | signal input a;
54 | signal input b;
55 | signal output out;
56 |
57 | out <== 1 - a*b;
58 | }
59 |
60 | template NOR() {
61 | signal input a;
62 | signal input b;
63 | signal output out;
64 |
65 | out <== a*b + 1 - a - b;
66 | }
67 |
68 | template MultiAND(n) {
69 | signal input in[n];
70 | signal output out;
71 | component and1;
72 | component and2;
73 | component ands[2];
74 | if (n==1) {
75 | out <== in[0];
76 | } else if (n==2) {
77 | and1 = AND();
78 | and1.a <== in[0];
79 | and1.b <== in[1];
80 | out <== and1.out;
81 | } else {
82 | and2 = AND();
83 | var n1 = n\2;
84 | var n2 = n-n\2;
85 | ands[0] = MultiAND(n1);
86 | ands[1] = MultiAND(n2);
87 | var i;
88 | for (i=0; i {
23 | const circuit = await wasm_tester(
24 | path.join(__dirname, "../circuits", "sudoku.circom"),
25 | { output: path.join(__dirname, "../build") }
26 | );
27 |
28 | const witness = await circuit.calculateWitness(
29 | { puzzle: validPuzzle, solution: validSolution },
30 | true
31 | );
32 |
33 | // console.log('witness', witness);
34 | assert(Fr.eq(Fr.e(witness[1]), Fr.e(1)));
35 | });
36 |
37 | it(`Valid puzzle, invalid solution test: puzzle = ${validPuzzle}, solution = ${invalidSolution}`, async () => {
38 | const circuit = await wasm_tester(
39 | path.join(__dirname, "../circuits", "sudoku.circom"),
40 | { output: path.join(__dirname, "../build") }
41 | );
42 |
43 | const witness = await circuit.calculateWitness(
44 | { puzzle: validPuzzle, solution: invalidSolution },
45 | true
46 | );
47 |
48 | // console.log('witness', witness);
49 | assert(Fr.eq(Fr.e(witness[1]), Fr.e(0)));
50 | });
51 | });
52 |
--------------------------------------------------------------------------------
/packages/circuit/test/test_data.js:
--------------------------------------------------------------------------------
1 | const sudoku = require('sudoku');
2 |
3 | function postprocessBoard(board) {
4 | return board.map((number, _) => number === null ? 0 : number + 1);
5 | }
6 |
7 | function generateTestData() {
8 | let validPuzzle;
9 | let validSolution;
10 | let invalidSolution;
11 |
12 | puzzle = sudoku.makepuzzle();
13 | solution = sudoku.solvepuzzle(puzzle);
14 | validPuzzle = postprocessBoard(puzzle);
15 | validSolution = postprocessBoard(solution);
16 | invalidSolution = [...validSolution];
17 | invalidSolution[0] = invalidSolution[1];
18 |
19 | return {validPuzzle, validSolution, invalidSolution};
20 | };
21 |
22 | module.exports = { generateTestData: generateTestData };
--------------------------------------------------------------------------------
/packages/circuit/test/utils_test.js:
--------------------------------------------------------------------------------
1 | const chai = require("chai");
2 | const path = require("path");
3 | const { generateTestData } = require("./test_data");
4 |
5 | const wasm_tester = require("circom_tester").wasm;
6 |
7 | const F1Field = require("ffjavascript").F1Field;
8 | const Scalar = require("ffjavascript").Scalar;
9 | exports.p = Scalar.fromString(
10 | "21888242871839275222246405745257275088548364400416034343698204186575808495617"
11 | );
12 | const Fr = new F1Field(exports.p);
13 |
14 | const assert = chai.assert;
15 |
16 | describe("GetNumberGroupForRow() test", function () {
17 | it("Test case: board = [1~81], row index = 1, result = [10~18]", async () => {
18 | const circuit = await wasm_tester(
19 | path.join(__dirname, "circuits", "get_number_group_for_row.circom"),
20 | { output: path.join(__dirname, "../build") }
21 | );
22 |
23 | const witness = await circuit.calculateWitness(
24 | {
25 | board: Array(81)
26 | .fill()
27 | .map((_, i) => i + 1),
28 | },
29 | true
30 | );
31 |
32 | // console.log('witness', witness);
33 | let expectedResult = [10, 11, 12, 13, 14, 15, 16, 17, 18];
34 | for (i = 0; i < 9; i++) {
35 | assert(Fr.eq(Fr.e(witness[1 + i]), Fr.e(expectedResult[i])));
36 | }
37 | });
38 | });
39 |
40 | describe("GetNumberGroupForColumn() test", function () {
41 | it("Test case: board = [1~81], column index = 1, result = [2,11,...,74]", async () => {
42 | const circuit = await wasm_tester(
43 | path.join(__dirname, "circuits", "get_number_group_for_column.circom"),
44 | { output: path.join(__dirname, "../build") }
45 | );
46 |
47 | const witness = await circuit.calculateWitness(
48 | {
49 | board: Array(81)
50 | .fill()
51 | .map((_, i) => i + 1),
52 | },
53 | true
54 | );
55 |
56 | // console.log('witness', witness);
57 | let expectedResult = [2, 11, 20, 29, 38, 47, 56, 65, 74];
58 | for (i = 0; i < 9; i++) {
59 | assert(Fr.eq(Fr.e(witness[1 + i]), Fr.e(expectedResult[i])));
60 | }
61 | });
62 | });
63 |
64 | describe("GetNumberGroupForBox() test", function () {
65 | it("Test case: board = [1~81], box index = 1, result = [4,5,6,13,14,15,22,23,24]", async () => {
66 | const circuit = await wasm_tester(
67 | path.join(__dirname, "circuits", "get_number_group_for_box.circom"),
68 | { output: path.join(__dirname, "../build") }
69 | );
70 |
71 | const witness = await circuit.calculateWitness(
72 | {
73 | board: Array(81)
74 | .fill()
75 | .map((_, i) => i + 1),
76 | },
77 | true
78 | );
79 |
80 | // console.log('witness', witness);
81 | let expectedResult = [4, 5, 6, 13, 14, 15, 22, 23, 24];
82 | for (i = 0; i < 9; i++) {
83 | assert(Fr.eq(Fr.e(witness[1 + i]), Fr.e(expectedResult[i])));
84 | }
85 | });
86 | });
87 |
88 | describe("IsNumberInRange() test", function () {
89 | it("In range test: range = [0~9], number = 5", async () => {
90 | const circuit = await wasm_tester(
91 | path.join(__dirname, "circuits", "is_number_in_range.circom"),
92 | { output: path.join(__dirname, "../build") }
93 | );
94 |
95 | const witness = await circuit.calculateWitness({ number: 5 }, true);
96 |
97 | // console.log('witness', witness);
98 | assert(Fr.eq(Fr.e(witness[1]), Fr.e(1)));
99 | });
100 |
101 | it("Out of range test: range = [0~9], number = 15", async () => {
102 | const circuit = await wasm_tester(
103 | path.join(__dirname, "circuits", "is_number_in_range.circom"),
104 | { output: path.join(__dirname, "../build") }
105 | );
106 |
107 | const witness = await circuit.calculateWitness({ number: 15 }, true);
108 |
109 | // console.log('witness', witness);
110 | assert(Fr.eq(Fr.e(witness[1]), Fr.e(0)));
111 | });
112 | });
113 |
114 | describe("IsValidSolutionNumberGroup() test", function () {
115 | it("Valid group test: numberGroup = [1, 3, 2, 9, 7, 5, 6, 4, 8]", async () => {
116 | const circuit = await wasm_tester(
117 | path.join(__dirname, "circuits", "is_valid_solution_number_group.circom"),
118 | { output: path.join(__dirname, "../build") }
119 | );
120 |
121 | const witness = await circuit.calculateWitness(
122 | { numberGroup: [1, 3, 2, 9, 7, 5, 6, 4, 8] },
123 | true
124 | );
125 |
126 | // console.log("witness", witness);
127 | assert(Fr.eq(Fr.e(witness[1]), Fr.e(1)));
128 | });
129 |
130 | it("Invalid group test with duplicated numbers: numberGroup = [1, 1, 2, 9, 7, 5, 6, 4, 8]", async () => {
131 | const circuit = await wasm_tester(
132 | path.join(__dirname, "circuits", "is_valid_solution_number_group.circom"),
133 | { output: path.join(__dirname, "../build") }
134 | );
135 |
136 | const witness = await circuit.calculateWitness(
137 | { numberGroup: [1, 1, 2, 9, 7, 5, 6, 4, 8] },
138 | true
139 | );
140 |
141 | // console.log("witness", witness);
142 | assert(Fr.eq(Fr.e(witness[1]), Fr.e(0)));
143 | });
144 |
145 | it("Invalid group test with out-of-range number: numberGroup = [1, 11, 2, 9, 7, 5, 6, 4, 8]", async () => {
146 | const circuit = await wasm_tester(
147 | path.join(__dirname, "circuits", "is_valid_solution_number_group.circom"),
148 | { output: path.join(__dirname, "../build") }
149 | );
150 |
151 | const witness = await circuit.calculateWitness(
152 | { numberGroup: [1, 11, 2, 9, 7, 5, 6, 4, 8] },
153 | true
154 | );
155 |
156 | // console.log("witness", witness);
157 | assert(Fr.eq(Fr.e(witness[1]), Fr.e(0)));
158 | });
159 | });
160 |
161 | describe("IsValidPuzzleNumberGroup() test", function () {
162 | it("Valid group test: numberGroup = [1, 0, 2, 0, 0, 5, 6, 0, 8]", async () => {
163 | const circuit = await wasm_tester(
164 | path.join(__dirname, "circuits", "is_valid_puzzle_number_group.circom"),
165 | { output: path.join(__dirname, "../build") }
166 | );
167 |
168 | const witness = await circuit.calculateWitness(
169 | { numberGroup: [1, 0, 2, 0, 0, 5, 6, 0, 8] },
170 | true
171 | );
172 |
173 | // console.log("witness", witness);
174 | assert(Fr.eq(Fr.e(witness[1]), Fr.e(1)));
175 | });
176 |
177 | it("Invalid group test with duplicated numbers: numberGroup = [1, 0, 1, 0, 0, 5, 6, 0, 8]", async () => {
178 | const circuit = await wasm_tester(
179 | path.join(__dirname, "circuits", "is_valid_puzzle_number_group.circom"),
180 | { output: path.join(__dirname, "../build") }
181 | );
182 |
183 | const witness = await circuit.calculateWitness(
184 | { numberGroup: [1, 0, 1, 0, 0, 5, 6, 0, 8] },
185 | true
186 | );
187 |
188 | // console.log("witness", witness);
189 | assert(Fr.eq(Fr.e(witness[1]), Fr.e(0)));
190 | });
191 |
192 | it("Invalid group test with out-of-range number: numberGroup = [1, 0, 11, 0, 0, 5, 6, 0, 8]", async () => {
193 | const circuit = await wasm_tester(
194 | path.join(__dirname, "circuits", "is_valid_puzzle_number_group.circom"),
195 | { output: path.join(__dirname, "../build") }
196 | );
197 |
198 | const witness = await circuit.calculateWitness(
199 | { numberGroup: [1, 0, 11, 0, 0, 5, 6, 0, 8] },
200 | true
201 | );
202 |
203 | // console.log("witness", witness);
204 | assert(Fr.eq(Fr.e(witness[1]), Fr.e(0)));
205 | });
206 | });
207 |
208 | describe("IsValidPuzzle() test", function () {
209 | let testData = generateTestData();
210 | let validPuzzle = testData.validPuzzle;
211 |
212 | it(`Valid puzzle test: puzzle = ${validPuzzle}`, async () => {
213 | const circuit = await wasm_tester(
214 | path.join(__dirname, "circuits", "is_valid_puzzle.circom"),
215 | { output: path.join(__dirname, "../build") }
216 | );
217 |
218 | const witness = await circuit.calculateWitness(
219 | { puzzle: validPuzzle },
220 | true
221 | );
222 |
223 | // console.log("witness", witness);
224 | assert(Fr.eq(Fr.e(witness[1]), Fr.e(1)));
225 | });
226 |
227 | it("Invalid puzzle test: puzzle = [1~9,1~9,...]", async () => {
228 | const circuit = await wasm_tester(
229 | path.join(__dirname, "circuits", "is_valid_puzzle.circom"),
230 | { output: path.join(__dirname, "../build") }
231 | );
232 |
233 | const witness = await circuit.calculateWitness(
234 | { puzzle: Array(81).fill().map((_, i) => (i % 9) + 1) },
235 | true
236 | );
237 |
238 | // console.log("witness", witness);
239 | assert(Fr.eq(Fr.e(witness[1]), Fr.e(0)));
240 | });
241 | });
242 |
243 | describe("IsValidSolution() test", function () {
244 | let testData = generateTestData();
245 | let validSolution = testData.validSolution;
246 |
247 | it(`Valid solution test: solution = ${validSolution}`, async () => {
248 | const circuit = await wasm_tester(
249 | path.join(__dirname, "circuits", "is_valid_solution.circom"),
250 | { output: path.join(__dirname, "../build") }
251 | );
252 |
253 | const witness = await circuit.calculateWitness(
254 | { solution: validSolution },
255 | true
256 | );
257 |
258 | // console.log("witness", witness);
259 | assert(Fr.eq(Fr.e(witness[1]), Fr.e(1)));
260 | });
261 |
262 | it("Invalid solution test: solution = [1~9,1~9,...]", async () => {
263 | const circuit = await wasm_tester(
264 | path.join(__dirname, "circuits", "is_valid_solution.circom"),
265 | { output: path.join(__dirname, "../build") }
266 | );
267 |
268 | const witness = await circuit.calculateWitness(
269 | { solution: Array(81).fill().map((_, i) => (i % 9) + 1) },
270 | true
271 | );
272 |
273 | // console.log("witness", witness);
274 | assert(Fr.eq(Fr.e(witness[1]), Fr.e(0)));
275 | });
276 | });
277 |
278 | describe("IsValidSolutionOfPuzzle() test", function () {
279 | let testData = generateTestData();
280 | let validPuzzle = testData.validPuzzle;
281 | let validSolution = testData.validSolution;
282 | let invalidSolution = testData.invalidSolution;
283 |
284 | it(`Valid solution test: solution = ${validSolution}, puzzle = ${validPuzzle}`, async () => {
285 | const circuit = await wasm_tester(
286 | path.join(__dirname, "circuits", "is_valid_solution_of_puzzle.circom"),
287 | { output: path.join(__dirname, "../build") }
288 | );
289 |
290 | const witness = await circuit.calculateWitness(
291 | { solution: validSolution, puzzle: validPuzzle },
292 | true
293 | );
294 |
295 | // console.log("witness", witness);
296 | assert(Fr.eq(Fr.e(witness[1]), Fr.e(1)));
297 | });
298 |
299 | it(`Invalid solution test: solution = ${invalidSolution}, puzzle = ${validPuzzle}`, async () => {
300 | const circuit = await wasm_tester(
301 | path.join(__dirname, "circuits", "is_valid_solution_of_puzzle.circom"),
302 | { output: path.join(__dirname, "../build") }
303 | );
304 |
305 | const witness = await circuit.calculateWitness(
306 | { solution: invalidSolution, puzzle: validPuzzle },
307 | true
308 | );
309 |
310 | // console.log("witness", witness);
311 | assert(Fr.eq(Fr.e(witness[1]), Fr.e(0)));
312 | });
313 | });
314 |
--------------------------------------------------------------------------------
/screen-capture.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/web3-master/zksnark-sudoku/b06a4e86cb89bd3bd3768a3b747bdfacc11cbbd5/screen-capture.gif
--------------------------------------------------------------------------------
/screen-capture.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/web3-master/zksnark-sudoku/b06a4e86cb89bd3bd3768a3b747bdfacc11cbbd5/screen-capture.mp4
--------------------------------------------------------------------------------
/screen-capture.webm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/web3-master/zksnark-sudoku/b06a4e86cb89bd3bd3768a3b747bdfacc11cbbd5/screen-capture.webm
--------------------------------------------------------------------------------
/screenshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/web3-master/zksnark-sudoku/b06a4e86cb89bd3bd3768a3b747bdfacc11cbbd5/screenshot.jpg
--------------------------------------------------------------------------------