diff --git a/.env.example b/.env.example index 04a56112..42a66d33 100644 --- a/.env.example +++ b/.env.example @@ -8,6 +8,4 @@ ENABLE_SIG_CARDS="true" NEXT_PUBLIC_SUPABASE_URL="" NEXT_PUBLIC_SUPABASE_ANON_KEY="" KEYGEN_PASSWORD="password" -NEXT_PUBLIC_NOVA_BUCKET_URL="https://bjj-ecdsa-nova.us-southeast-1.linodeobjects.com/depth_9" -NEXT_PUBLIC_NOVA_INDEXDB_NAME="zksummit_folded" NEXT_PUBLIC_ENABLE_METRICS="false" diff --git a/README.md b/README.md index 572a93cf..5a56d8ad 100644 --- a/README.md +++ b/README.md @@ -1,41 +1 @@ -# ZK Summit 11 x Cursive - -This app is built for [ZK Summit 11](https://www.zksummit.com/) in Athens. Every attendee will get an NFC card alongside their badge, and additional NFC cards will be placed in front of rooms where talks and workshops are held. Tapping another attendee's card allows you to see their contact information, as well as receive a digital signature that verifiably represents the fact that you met them. Tapping a card associated with a talk gives you a digital signature proving you attended that talk. There are 3 main things you can do with these signatures: make zero knowledge proofs, privately compute the things you've done in common with someone else, and aggregate the signatures into one big proof of your entire event experience - ZK Summit Folded! - -## Zero Knowledge Proofs - -There are 3 types of zero knowledge proofs an attendee can make at ZK Summit 11: - -1. Prove you met 10 other attendees at the event -2. Prove you attended 5 talks -3. Prove you met 3 speakers - -These proofs serve as basic examples of what's possible from verifiably digitizing in-person interactions, but one can generalize to far more complicated statements! For each proof we generate a merkle tree of all the signature public keys corresponding to the set of people/talks that comprise a proof. When a user makes a proof, lets say for showing they attended 5 talks, they are demonstrating the statement "I have 5 distinct signatures originating from different public keys within the merkle tree of talks". Notably, it is never revealed **which** talks they attended, and the signatures themselves always remain private. - -#### Proving stack - -To go a bit deeper, the actual proof being generated is a combination of a ECDSA signature proof + a Merkle membership proof. We use the [Baby Jubjub](https://eips.ethereum.org/EIPS/eip-2494) elliptic curve for ECDSA - having a representation in Twisted Edwards form allows complete addition formulae which reduce the number of constraints in our circuit - the circuits and proving code can be found [here](https://github.com/cursive-team/babyjubjub-ecdsa/tree/main). To avoid wrong field arithmetic and greatly save constraints, we use the [efficient ECDSA representation](https://personaelabs.org/posts/efficient-ecdsa-1/) from Personae. - -#### Nullifiers - -To prevent signatures from being reused, we make use of [nullifiers](https://nmohnblatt.github.io/zk-jargon-decoder/definitions/nullifier.html). Each attendee and location requirement is associated with a random value. When a signature is fed into the circuit, it is hashed with this random value, and the resulting output is known as the nullifier. We store these nullifiers, and if someone tries to use the same signature for a given requirement, they will deterministically produce the same nullifier, and we can invalidate their proof. - -## Private Set Intersection (PSI) with Fully Homomorphic Encryption (FHE) - -After collecting a few signatures each, two attendees can privately see which people they've both met and talks they've both attended. This is known as computing the Private Set Intersection of their two collections of signatures. At a high level, both users generate bit vectors corresponding to the list of all possible public keys that can be collected. A 1 represents that the user has collected a signature corresponding to that public key, a 0 represents the fact that they have not. Both users engage in a two-party computation to encrypt their respective bit vectors. Then, they compute the Hadamard product of the two encrypted bit vectors. Finally, they perform another two-party computation to decrypt the overlap bit vector and are left with the indices of the public keys they have collected in common. PSI was implemented by our good friends at [Gauss](https://github.com/gausslabs), you can see the code [here](https://github.com/gausslabs/MP-PSI). - -#### Ensuring integrity of FHE inputs - -You might have noticed one flaw in the PSI computation above. Namely, a user can simply claim that they have collected a signature from every single public key, i.e. they have a bit vector of all 1's. After running PSI, they would then know exactly what public keys the other user has collected. The solution to this is to have each user additionally include a zero knowledge proof that their bit vector was computed correctly, i.e. with valid signatures! In addition, it is important to ensure that both users perform valid encryptions of their data, as the FHE ciphertext must be well formed. This is solved by a tool like [Greco](https://github.com/privacy-scaling-explorations/greco), which allows the user to generate a zero knowledge proof that encryption was performed correctly. Due to time limitations, we were not able to implement either of these improvements for ZK Summit 11 - but they will be fun explorations for the future! - -## ZK Summit Folded - -One of the more exciting additions to this activation is ZK Summit Folded - a play on Spotify Wrapped, but using folding schemes. Folding schemes are an efficient way to aggregate proofs about a particularly structured type of statement. In our case, the statement is about the signatures one collects - "I have a signature corresponding to a public key, and this public key represents one of the attendees of ZK Summit 11". Folding schemes allow a user to produce a single proof that represent the aggregate of multiple of these statements, i.e. "I have 100 signatures corresponding to different public keys, and these public keys all represent attendees of ZK Summit 11". The beauty of folding schemes compared to say, naive Groth 16 proofs, is 1. you can **incrementally** generate this folding proof - every time you get a new signature, you can build the next step of the proof, instead of requiring knowledge of all the signatures at one time, and 2. the proof size is constant **irrespective** of the number of signatures you are proving - showing you have 100 attendee signatures results in a proof of the same size as showing you have 1000. - -#### Proving stack - -We are using the [Nova](https://eprint.iacr.org/2021/370) folding scheme, which notably is not state of the art, but has the most robust tooling at the moment (we would love to experiment with implementations of more recent folding work). [Nova-Scotia](https://github.com/nalinbhardwaj/Nova-Scotia) allows us to take our existing circom circuits and express them in a format that [Nova](https://github.com/microsoft/Nova) understands. The implementation of folding schemes for this app was done by our friends at [Mach34](https://mach34.space/). - -## Interested in more projects like this? - -[Cursive](http://cursive.team) is a cryptography and design lab building human-first applications of signed data. The code for the web app is all [open-source](https://github.com/cursive-team/zk-summit). If you’re interested in practical applications of digital signatures or advanced cryptography and would like to chat and/or collaborate, please reach out! +# MPC Starter diff --git a/next.config.mjs b/next.config.mjs index c5b45c9f..1d890af4 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -3,30 +3,27 @@ const nextConfig = { reactStrictMode: true, webpack: (config) => { // Needed to make snarkJs work client side - config.resolve.fallback = { fs: false, readline: false }; + config.resolve.fallback = { + net: false, + tls: false, + fs: false, + readline: false, + }; return config; }, - images: { - remotePatterns: [ - { - protocol: 'https', - hostname: 'picsum.photos', - }, - ], - }, headers: async () => { // needed to allow calls by wasm to remote resources return [ { - source: '/(.*)', + source: "/(.*)", headers: [ { - key: 'Cross-Origin-Opener-Policy', - value: 'same-origin', + key: "Cross-Origin-Opener-Policy", + value: "same-origin", }, { - key: 'Cross-Origin-Embedder-Policy', - value: 'require-corp', + key: "Cross-Origin-Embedder-Policy", + value: "require-corp", }, ], }, diff --git a/package-lock.json b/package-lock.json index 856c1ab5..b2fe16f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,19 @@ { - "name": "zk-summit", + "name": "mpc-starter", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "zk-summit", + "name": "mpc-starter", "version": "0.1.0", "hasInstallScript": true, "dependencies": { + "@emotion/react": "^11.13.0", + "@emotion/styled": "^11.13.0", "@headlessui/react": "^1.7.18", "@hookform/resolvers": "^3.3.4", + "@mui/material": "^5.16.5", "@next/font": "^14.1.4", "@prisma/client": "^5.8.1", "@simplewebauthn/browser": "^9.0.1", @@ -20,24 +23,23 @@ "@tw-classed/react": "^1.7.0", "@vercel/analytics": "^1.2.2", "@vercel/blob": "^0.22.3", + "aes-js": "^3.1.2", "babyjubjub-ecdsa": "^1.2.0", - "bjj_ecdsa_nova_wasm": "file:public/bjj_ecdsa_nova_wasm", + "bignumber.js": "^9.1.2", "circomlibjs": "^0.1.7", "clsx": "^2.1.0", "comlink": "^4.4.1", "dayjs": "^1.11.10", "detectincognitojs": "^1.3.0", "idb": "^8.0.0", + "jiff-mpc": "^1.0.0", "js-sha256": "^0.11.0", - "jsonwebtoken": "^9.0.2", "little-state-machine": "^4.8.0", "mobile-detect": "^1.4.5", "next": "^14.2.3", "react": "^18", "react-dom": "^18", - "react-error-boundary": "^4.0.12", "react-hook-form": "^7.50.1", - "react-linkify": "^1.0.0-alpha", "react-qr-code": "^2.0.12", "sonner": "^1.4.0", "swiper": "^11.1.0", @@ -47,12 +49,8 @@ }, "devDependencies": { "@tanstack/eslint-plugin-query": "^5.17.22", - "@types/jest": "^29.5.11", - "@types/jsonwebtoken": "^9.0.6", "@types/node": "^20", "@types/react": "^18", - "@types/react-dom": "^18", - "@types/react-linkify": "^1.0.4", "@types/uuid": "^9.0.7", "autoprefixer": "^10.0.1", "daisyui": "^4.6.0", @@ -104,7 +102,6 @@ "version": "7.23.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", - "dev": true, "dependencies": { "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" @@ -117,7 +114,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -129,7 +125,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -143,7 +138,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -151,14 +145,12 @@ "node_modules/@babel/code-frame/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, "engines": { "node": ">=0.8.0" } @@ -167,7 +159,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, "engines": { "node": ">=4" } @@ -176,7 +167,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -349,8 +339,6 @@ "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "dev": true, - "peer": true, "dependencies": { "@babel/types": "^7.22.15" }, @@ -418,8 +406,6 @@ "version": "7.23.4", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", - "dev": true, - "peer": true, "engines": { "node": ">=6.9.0" } @@ -428,7 +414,6 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -462,7 +447,6 @@ "version": "7.23.4", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", - "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", @@ -476,7 +460,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -488,7 +471,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -502,7 +484,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -510,14 +491,12 @@ "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, "engines": { "node": ">=0.8.0" } @@ -526,7 +505,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, "engines": { "node": ">=4" } @@ -535,7 +513,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -748,9 +725,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.23.8", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.8.tgz", - "integrity": "sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", + "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -809,8 +786,6 @@ "version": "7.23.9", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", - "dev": true, - "peer": true, "dependencies": { "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", @@ -827,6 +802,152 @@ "dev": true, "peer": true }, + "node_modules/@emotion/babel-plugin": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz", + "integrity": "sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.2.0", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + }, + "node_modules/@emotion/babel-plugin/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.13.1", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz", + "integrity": "sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.0", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.0.tgz", + "integrity": "sha512-SHetuSLvJDzuNbOdtPVbq6yMMMlLoW5Q94uDqJZqy50gcmAjxFkVqmzqSGEFq9gT2iMuIeKV1PXVWmvUhuZLlQ==", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" + }, + "node_modules/@emotion/react": { + "version": "11.13.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.13.0.tgz", + "integrity": "sha512-WkL+bw1REC2VNV1goQyfxjx1GYJkcc23CRQkXX+vZNLINyfI7o+uUn/rTGPt/xJ3bJHd5GcljgnxHf4wRw5VWQ==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.12.0", + "@emotion/cache": "^11.13.0", + "@emotion/serialize": "^1.3.0", + "@emotion/use-insertion-effect-with-fallbacks": "^1.1.0", + "@emotion/utils": "^1.4.0", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.0.tgz", + "integrity": "sha512-jACuBa9SlYajnpIVXB+XOXnfJHyckDfe6fOpORIM6yhBDlqGuExvDdZYHDQGoDf3bZXGv7tNr+LpLjJqiEQ6EA==", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.9.0", + "@emotion/utils": "^1.4.0", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==" + }, + "node_modules/@emotion/styled": { + "version": "11.13.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.13.0.tgz", + "integrity": "sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.12.0", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.0", + "@emotion/use-insertion-effect-with-fallbacks": "^1.1.0", + "@emotion/utils": "^1.4.0" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.9.0.tgz", + "integrity": "sha512-TP6GgNZtmtFaFcsOgExdnfxLLpRDla4Q66tnenA9CktvVSdNKDvMVuUah4QvWPIpNjrWsGg3qeGo9a43QooGZQ==" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz", + "integrity": "sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.0.tgz", + "integrity": "sha512-spEnrA1b6hDR/C68lC2M7m6ALPUHZC0lIY7jAS/B/9DuuO1ZP04eov8SMv/6fwRd8pzmsn2AuJEznRREWlQrlQ==" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -1187,6 +1308,11 @@ "scrypt-js": "3.0.1" } }, + "node_modules/@ethersproject/json-wallets/node_modules/aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==" + }, "node_modules/@ethersproject/keccak256": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", @@ -1924,6 +2050,7 @@ "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, + "peer": true, "dependencies": { "jest-get-type": "^29.6.3" }, @@ -2186,6 +2313,207 @@ "resolved": "https://registry.npmjs.org/@levischuck/tiny-cbor/-/tiny-cbor-0.2.2.tgz", "integrity": "sha512-f5CnPw997Y2GQ8FAvtuVVC19FX8mwNNC+1XJcIi16n/LTJifKO6QBgGLgN3YEmqtGMk17SKSuoWES3imJVxAVw==" }, + "node_modules/@mui/core-downloads-tracker": { + "version": "5.16.5", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.16.5.tgz", + "integrity": "sha512-ziFn1oPm6VjvHQcdGcAO+fXvOQEgieIj0BuSqcltFU+JXIxjPdVYNTdn2HU7/Ak5Gabk6k2u7+9PV7oZ6JT5sA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/material": { + "version": "5.16.5", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.16.5.tgz", + "integrity": "sha512-eQrjjg4JeczXvh/+8yvJkxWIiKNHVptB/AqpsKfZBWp5mUD5U3VsjODMuUl1K2BSq0omV3CiO/mQmWSSMKSmaA==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/core-downloads-tracker": "^5.16.5", + "@mui/system": "^5.16.5", + "@mui/types": "^7.2.15", + "@mui/utils": "^5.16.5", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.10", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^18.3.1", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + }, + "node_modules/@mui/private-theming": { + "version": "5.16.5", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.5.tgz", + "integrity": "sha512-CSLg0YkpDqg0aXOxtjo3oTMd3XWMxvNb5d0v4AYVqwOltU8q6GvnZjhWyCLjGSCrcgfwm6/VDjaKLPlR14wxIA==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/utils": "^5.16.5", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "5.16.4", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.4.tgz", + "integrity": "sha512-0+mnkf+UiAmTVB8PZFqOhqf729Yh0Cxq29/5cA3VAyDVTRIUUQ8FXQhiAhUIbijFmM72rY80ahFPXIm4WDbzcA==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@emotion/cache": "^11.11.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "5.16.5", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.16.5.tgz", + "integrity": "sha512-uzIUGdrWddUx1HPxW4+B2o4vpgKyRxGe/8BxbfXVDPNPHX75c782TseoCnR/VyfnZJfqX87GcxDmnZEE1c031g==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/private-theming": "^5.16.5", + "@mui/styled-engine": "^5.16.4", + "@mui/types": "^7.2.15", + "@mui/utils": "^5.16.5", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.2.15", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.15.tgz", + "integrity": "sha512-nbo7yPhtKJkdf9kcVOF8JZHPZTmqXjJ/tI0bdWgHg5tp9AnIN4Y7f7wm9T+0SyGYJk76+GYZ8Q5XaTYAsUHN0Q==", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "5.16.5", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.5.tgz", + "integrity": "sha512-CwhcA9y44XwK7k2joL3Y29mRUnoBt+gOZZdGyw7YihbEwEErJYBtDwbZwVgH68zAljGe/b+Kd5bzfl63Gi3R2A==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/types": "^7.2.15", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^18.3.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + }, "node_modules/@next/env": { "version": "14.2.3", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz", @@ -2442,6 +2770,15 @@ "node": ">=14" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@prisma/client": { "version": "5.8.1", "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.8.1.tgz", @@ -2615,9 +2952,9 @@ } }, "node_modules/@supabase/realtime-js/node_modules/ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "engines": { "node": ">=10.0.0" }, @@ -2829,16 +3166,6 @@ "@types/istanbul-lib-report": "*" } }, - "node_modules/@types/jest": { - "version": "29.5.11", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", - "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", - "dev": true, - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -2851,15 +3178,6 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, - "node_modules/@types/jsonwebtoken": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.6.tgz", - "integrity": "sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/node": { "version": "20.11.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.5.tgz", @@ -2868,42 +3186,35 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" + }, "node_modules/@types/phoenix": { "version": "1.6.4", "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.4.tgz", "integrity": "sha512-B34A7uot1Cv0XtaHRYDATltAdKx0BvVKNgYNqE4WjtPUa4VQJM7kxeXcVKaH+KS+kCmZ+6w+QaUdcljiheiBJA==" }, "node_modules/@types/prop-types": { - "version": "15.7.11", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", - "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", - "dev": true + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" }, "node_modules/@types/react": { "version": "18.2.48", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.48.tgz", "integrity": "sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w==", - "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", "csstype": "^3.0.2" } }, - "node_modules/@types/react-dom": { - "version": "18.2.18", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.18.tgz", - "integrity": "sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==", - "dev": true, - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/react-linkify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@types/react-linkify/-/react-linkify-1.0.4.tgz", - "integrity": "sha512-NOMw4X3kjvjY0lT5kXQdxZCXpPNi2hOuuqG+Kz+5EOQpi9rDUJJDitdE1j2JRNmrTnNIjrLnYG0HKyuOWN/uKA==", - "dev": true, + "node_modules/@types/react-transition-group": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", + "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", "dependencies": { "@types/react": "*" } @@ -2911,8 +3222,7 @@ "node_modules/@types/scheduler": { "version": "0.16.8", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", - "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", - "dev": true + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==" }, "node_modules/@types/semver": { "version": "7.5.6", @@ -2924,7 +3234,8 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@types/uuid": { "version": "9.0.7", @@ -3243,6 +3554,18 @@ "node": ">=16.14" } }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", @@ -3265,15 +3588,19 @@ } }, "node_modules/aes-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", - "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz", + "integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==" + }, + "node_modules/after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA==" }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -3507,6 +3834,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/arraybuffer.slice": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==" + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, "node_modules/asn1js": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz", @@ -3520,6 +3860,14 @@ "node": ">=12.0.0" } }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/ast-types-flow": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", @@ -3548,10 +3896,15 @@ "has-symbols": "^1.0.3" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/autoprefixer": { - "version": "10.4.17", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.17.tgz", - "integrity": "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==", + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", "dev": true, "funding": [ { @@ -3568,11 +3921,11 @@ } ], "dependencies": { - "browserslist": "^4.22.2", - "caniuse-lite": "^1.0.30001578", + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -3597,6 +3950,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.0.tgz", + "integrity": "sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g==" + }, "node_modules/axe-core": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", @@ -3702,6 +4068,20 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, "node_modules/babel-preset-current-node-syntax": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", @@ -3756,15 +4136,44 @@ "uuid": "^9.0.1" } }, + "node_modules/backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==" + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "node_modules/bech32": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", - "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" + "node_modules/base64-arraybuffer": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", + "integrity": "sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" }, "node_modules/bfj": { "version": "7.1.0", @@ -3781,6 +4190,14 @@ "node": ">= 8.0.0" } }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -3790,10 +4207,6 @@ "node": ">=8" } }, - "node_modules/bjj_ecdsa_nova_wasm": { - "resolved": "public/bjj_ecdsa_nova_wasm", - "link": true - }, "node_modules/blake-hash": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/blake-hash/-/blake-hash-2.0.0.tgz", @@ -3826,6 +4239,11 @@ "nanoassert": "^2.0.0" } }, + "node_modules/blob": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -3846,12 +4264,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -3863,9 +4281,9 @@ "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" }, "node_modules/browserslist": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", - "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", "dev": true, "funding": [ { @@ -3882,10 +4300,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001565", - "electron-to-chromium": "^1.4.601", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" }, "bin": { "browserslist": "cli.js" @@ -3916,11 +4334,6 @@ "node-int64": "^0.4.0" } }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -3928,6 +4341,18 @@ "dev": true, "peer": true }, + "node_modules/bufferutil": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", + "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -3965,7 +4390,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "engines": { "node": ">=6" } @@ -3990,9 +4414,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001579", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz", - "integrity": "sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA==", + "version": "1.0.30001651", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz", + "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==", "funding": [ { "type": "opencollective", @@ -4008,6 +4432,11 @@ } ] }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -4192,9 +4621,9 @@ } }, "node_modules/clsx": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", - "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "engines": { "node": ">=6" } @@ -4233,6 +4662,17 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/comlink": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/comlink/-/comlink-4.4.1.tgz", @@ -4247,6 +4687,32 @@ "node": ">= 6" } }, + "node_modules/complex.js": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.0.11.tgz", + "integrity": "sha512-6IArJLApNtdg1P1dFtn3dnyzoZBEF0MwMnrfF1exSBRpZYoy4yieMkpZhQDC0uwctw48vii0CFVyHfpgZ/DfGw==", + "engines": { + "node": "*" + } + }, + "node_modules/component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw==" + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA==" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -4259,6 +4725,42 @@ "dev": true, "peer": true }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, "node_modules/create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", @@ -4281,6 +4783,82 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/cross-env": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.2.1.tgz", + "integrity": "sha512-1yHhtcfAd1r4nwQgknowuUNfIT9E8dOMMspC36g45dN+iD1blloi7xp8X/xAIDnjHWyt1uQ8PHk2fkNaym7soQ==", + "dependencies": { + "cross-spawn": "^6.0.5" + }, + "bin": { + "cross-env": "dist/bin/cross-env.js", + "cross-env-shell": "dist/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/cross-env/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/cross-env/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/cross-env/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/cross-env/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cross-env/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cross-env/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/cross-fetch": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", @@ -4328,8 +4906,7 @@ "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/culori": { "version": "3.3.0", @@ -4340,6 +4917,18 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/daisyui": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.6.0.tgz", @@ -4365,6 +4954,17 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/dayjs": { "version": "1.11.10", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", @@ -4387,6 +4987,11 @@ } } }, + "node_modules/decimal.js": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz", + "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==" + }, "node_modules/dedent": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", @@ -4448,6 +5053,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -4483,6 +5096,7 @@ "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, + "peer": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -4505,6 +5119,11 @@ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", "dev": true }, + "node_modules/docdash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/docdash/-/docdash-1.2.0.tgz", + "integrity": "sha512-IYZbgYthPTspgqYeciRJNPhSwL51yer7HAwDXhF5p+H7mTDbPvY3PCk/QDjNxdPCpWkaJVFC4t7iCNB/t9E5Kw==" + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -4517,18 +5136,28 @@ "node": ">=6.0.0" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dependencies": { - "safe-buffer": "^5.0.1" + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" } }, "node_modules/ejs": { @@ -4546,9 +5175,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.640", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.640.tgz", - "integrity": "sha512-z/6oZ/Muqk4BaE7P69bXhUhpJbUM9ZJeka43ZwxsDshKtePns4mhBlh8bU5+yrnOnz3fhG82XLzGUXazOmsWnA==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.5.tgz", + "integrity": "sha512-QR7/A7ZkMS8tZuoftC/jfqNkZLQO779SSW3YuZHP4eXpj3EffGLFcB/Xu9AAZQzLccTiCV+EmUo3ha4mQ9wnlA==", "dev": true }, "node_modules/elliptic": { @@ -4584,6 +5213,114 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/engine.io": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.6.2.tgz", + "integrity": "sha512-C4JjGQZLY3kWlIDx0BQNKizbrfpb7NahxDztGdN5jrPK2ghmXiNDN+E/t0JzDeNRZxPVaszxEng42Pmj27X/0w==", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "debug": "~4.1.0", + "engine.io-parser": "~2.2.0", + "ws": "~7.5.10" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/engine.io-client": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.4.tgz", + "integrity": "sha512-ydc8uuMMDxC5KCKNJN3zZKYJk2sgyTuTZQ7Aj1DJSsLKAcizA/PzWivw8fZMIjJVBo2CJOYzntv4FSjY/Lr//g==", + "dependencies": { + "component-emitter": "~1.3.0", + "component-inherit": "0.0.3", + "debug": "~3.1.0", + "engine.io-parser": "~2.2.0", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "ws": "~7.5.10", + "xmlhttprequest-ssl": "~1.6.2", + "yeast": "0.1.2" + } + }, + "node_modules/engine.io-client/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/engine.io-client/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz", + "integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==", + "dependencies": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.4", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/engine.io/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/enhanced-resolve": { "version": "5.15.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", @@ -4601,8 +5338,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "peer": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -4722,20 +5457,61 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "dev": true, "engines": { "node": ">=6" } }, + "node_modules/escape-latex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", + "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "engines": { "node": ">=10" }, @@ -5182,6 +5958,20 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -5299,6 +6089,15 @@ "@ethersproject/wordlists": "5.7.0" } }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -5345,6 +6144,7 @@ "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, + "peer": true, "dependencies": { "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", @@ -5356,11 +6156,31 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ] + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { "version": "3.3.2", @@ -5393,8 +6213,7 @@ "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -5481,9 +6300,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -5492,6 +6311,11 @@ "node": ">=8" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -5553,6 +6377,27 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -5569,8 +6414,7 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", @@ -5590,7 +6434,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5708,6 +6551,14 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, "node_modules/glob": { "version": "10.3.10", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", @@ -5839,6 +6690,27 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -5848,6 +6720,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-binary2": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "dependencies": { + "isarray": "2.0.1" + } + }, + "node_modules/has-binary2/node_modules/isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ==" + }, + "node_modules/has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA==" + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -5920,7 +6810,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -5938,6 +6827,14 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -5953,6 +6850,20 @@ "dev": true, "peer": true }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -5981,7 +6892,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -6022,11 +6932,46 @@ "node": ">=0.8.19" } }, + "node_modules/increase-memory-limit": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/increase-memory-limit/-/increase-memory-limit-1.0.7.tgz", + "integrity": "sha512-ozyn+HHAPD9VxMT1U50A7G8XXlktUWhnnEDYSeDYnQjKsNrBWHqq6XfEA0uhMdDPD+q/7rXWlzF1CbXX/c1LiQ==", + "dependencies": { + "glob": "^7.1.1" + }, + "bin": { + "increase-memory-limit": "index.js" + } + }, + "node_modules/increase-memory-limit/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==" + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -6076,9 +7021,7 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "peer": true + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "node_modules/is-async-function": { "version": "2.0.0", @@ -6173,7 +7116,6 @@ "version": "2.13.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, "dependencies": { "hasown": "^2.0.0" }, @@ -6412,6 +7354,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, "node_modules/is-weakmap": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", @@ -6455,8 +7402,20 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", @@ -6577,6 +7536,11 @@ "node": ">=10" } }, + "node_modules/javascript-natural-sort": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", + "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==" + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -6757,6 +7721,7 @@ "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, + "peer": true, "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", @@ -6820,6 +7785,7 @@ "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true, + "peer": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -6869,6 +7835,7 @@ "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, + "peer": true, "dependencies": { "chalk": "^4.0.0", "jest-diff": "^29.7.0", @@ -6884,6 +7851,7 @@ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, + "peer": true, "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", @@ -7207,6 +8175,35 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jiff-mpc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jiff-mpc/-/jiff-mpc-1.0.0.tgz", + "integrity": "sha512-IbONjZ9/zVuHSho72OxCgMBMQo4k6uYVMKjsIlc5rZ0kb7rcMZxoZSNqzw/lgz0TV+v+S/cW7edWMQFnW+zXMw==", + "dependencies": { + "bignumber.js": "^5.0.0", + "cross-env": "^5.0.5", + "docdash": "^1.1.1", + "increase-memory-limit": "^1.0.3", + "isomorphic-ws": "^4.0.1", + "jquery-deferred": "^0.3.1", + "libsodium-wrappers": "^0.7.2", + "mathjs": "^5.0.4", + "numeric": "^1.2.6", + "request": "^2.88.0", + "socket.io": "^2.2.0", + "socket.io-client": "^2.2.0", + "websocket": "^1.0.32", + "ws": "^7.3.1" + } + }, + "node_modules/jiff-mpc/node_modules/bignumber.js": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-5.0.0.tgz", + "integrity": "sha512-KWTu6ZMVk9sxlDJQh2YH1UOnfDP8O8TpxUxgQG/vKASoSnEjK9aVuOueFaPcQEYQ5fyNXNTOYwYw3099RYebWg==", + "engines": { + "node": "*" + } + }, "node_modules/jiti": { "version": "1.21.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", @@ -7216,6 +8213,14 @@ "jiti": "bin/jiti.js" } }, + "node_modules/jquery-deferred": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/jquery-deferred/-/jquery-deferred-0.3.1.tgz", + "integrity": "sha512-YTzoTYR/yrjmNh6B6exK7lC1jlDazEzt9ZlZvdRscv+I1AJqN1SmU3ZAn4iMGiVhwAavCrbijDVyTc0lmr9ZCA==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/js-sha256": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.11.0.tgz", @@ -7243,6 +8248,11 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -7265,15 +8275,17 @@ "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "peer": true + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -7281,6 +8293,11 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, "node_modules/json5": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", @@ -7303,25 +8320,18 @@ "underscore": "1.12.1" } }, - "node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" }, "engines": { - "node": ">=12", - "npm": ">=6" + "node": ">=0.6.0" } }, "node_modules/jsx-ast-utils": { @@ -7339,25 +8349,6 @@ "node": ">=4.0" } }, - "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -7418,6 +8409,19 @@ "node": ">= 0.8.0" } }, + "node_modules/libsodium": { + "version": "0.7.14", + "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.14.tgz", + "integrity": "sha512-/pOd7eO6oZrfORquRTC4284OUJFcMi8F3Vnc9xtRBT0teLfOUxWIItaBFF3odYjZ7nlJNwnLdUVEUFHxVyX/Sw==" + }, + "node_modules/libsodium-wrappers": { + "version": "0.7.14", + "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.14.tgz", + "integrity": "sha512-300TtsePizhJZ7HjLmWr6hLHAgJUxIGhapSw+EwfCtDuWaEmEdGXSQv6j6qFw0bs9l4vS2NH9BtOHfXAq6h5kQ==", + "dependencies": { + "libsodium": "^0.7.14" + } + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -7430,16 +8434,7 @@ "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/linkify-it": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", - "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", - "dependencies": { - "uc.micro": "^1.0.1" - } + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/little-state-machine": { "version": "4.8.0", @@ -7464,36 +8459,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" - }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -7506,11 +8471,6 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" - }, "node_modules/logplease": { "version": "1.2.15", "resolved": "https://registry.npmjs.org/logplease/-/logplease-1.2.15.tgz", @@ -7568,6 +8528,35 @@ "tmpl": "1.0.5" } }, + "node_modules/mathjs": { + "version": "5.10.3", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-5.10.3.tgz", + "integrity": "sha512-ySjg30BC3dYjQm73ILZtwcWzFJde0VU6otkXW/57IjjuYRa3Qaf0Kb8pydEuBZYtqW2OxreAtsricrAmOj3jIw==", + "dependencies": { + "complex.js": "2.0.11", + "decimal.js": "10.2.0", + "escape-latex": "1.2.0", + "fraction.js": "4.0.12", + "javascript-natural-sort": "0.7.1", + "seed-random": "2.2.0", + "tiny-emitter": "2.1.0", + "typed-function": "1.1.0" + }, + "bin": { + "mathjs": "bin/cli.js" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/mathjs/node_modules/fraction.js": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.0.12.tgz", + "integrity": "sha512-8Z1K0VTG4hzYY7kA/1sj4/r1/RWLBD3xwReT/RCrUCbzPszjNQCCsy3ktkU/eaEqX3MYa4pY37a52eiBlPMlhA==", + "engines": { + "node": "*" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -7597,6 +8586,25 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -7695,6 +8703,14 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/next": { "version": "14.2.3", "resolved": "https://registry.npmjs.org/next/-/next-14.2.3.tgz", @@ -7744,6 +8760,11 @@ } } }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -7771,6 +8792,11 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, "node_modules/node-addon-api": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", @@ -7813,9 +8839,9 @@ "peer": true }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true }, "node_modules/normalize-path": { @@ -7849,6 +8875,19 @@ "node": ">=8" } }, + "node_modules/numeric": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/numeric/-/numeric-1.2.6.tgz", + "integrity": "sha512-avBiDAP8siMa7AfJgYyuxw1oyII4z2sswS23+O+ZfV28KrtNzy0wxUFwi4f3RyM4eeeXNs1CThxR7pb5QQcMiw==" + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "engines": { + "node": "*" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -7979,7 +9018,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -8061,7 +9099,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -8073,8 +9110,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "peer": true, "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -8088,6 +9123,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parseqs": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", + "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" + }, + "node_modules/parseuri": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", + "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -8101,7 +9146,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -8118,8 +9162,7 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-scurry": { "version": "1.10.1", @@ -8141,15 +9184,19 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, "engines": { "node": ">=8" } }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -8410,6 +9457,7 @@ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, + "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -8424,6 +9472,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, + "peer": true, "engines": { "node": ">=10" }, @@ -8435,7 +9484,8 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true + "dev": true, + "peer": true }, "node_modules/prisma": { "version": "5.8.1", @@ -8482,11 +9532,15 @@ "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==" }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "engines": { "node": ">=6" } @@ -8529,6 +9583,14 @@ "resolved": "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz", "integrity": "sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ==" }, + "node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "engines": { + "node": ">=0.6" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -8593,17 +9655,6 @@ "react": "^18.2.0" } }, - "node_modules/react-error-boundary": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.12.tgz", - "integrity": "sha512-kJdxdEYlb7CPC1A0SeUY38cHpjuu6UkvzKiAmqmOFL21VRfMhOcWxTCBgLVCO0VEMh9JhFNcVaXlV4/BTpiwOA==", - "dependencies": { - "@babel/runtime": "^7.12.5" - }, - "peerDependencies": { - "react": ">=16.13.1" - } - }, "node_modules/react-hook-form": { "version": "7.50.1", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.50.1.tgz", @@ -8624,15 +9675,6 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, - "node_modules/react-linkify": { - "version": "1.0.0-alpha", - "resolved": "https://registry.npmjs.org/react-linkify/-/react-linkify-1.0.0-alpha.tgz", - "integrity": "sha512-7gcIUvJkAXXttt1fmBK9cwn+1jTa4hbKLGCZ9J1U6EOkyb2/+LKL1Z28d9rtDLMnpvImlNlLPdTPooorl5cpmg==", - "dependencies": { - "linkify-it": "^2.0.3", - "tlds": "^1.199.0" - } - }, "node_modules/react-qr-code": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/react-qr-code/-/react-qr-code-2.0.12.tgz", @@ -8651,6 +9693,21 @@ } } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -8727,6 +9784,46 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -8741,7 +9838,6 @@ "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -8781,7 +9877,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "engines": { "node": ">=4" } @@ -8935,6 +10030,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -8948,10 +10048,16 @@ "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" }, + "node_modules/seed-random": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz", + "integrity": "sha512-34EQV6AAHQGhoc0tn/96a9Fsi6v2xdqe/dMUwljGRaFOzR3EgRmECvD0O8vi8X+/uQ50LGHfkNu/Eue5TPKZkQ==" + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -8966,6 +10072,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -9091,6 +10198,111 @@ "snarkjs": "build/cli.cjs" } }, + "node_modules/socket.io": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.5.1.tgz", + "integrity": "sha512-eaTE4tBKRD6RFoetquMbxgvcpvoDtRyIlkIMI/SMK2bsKvbENTsDeeu4GJ/z9c90yOWxB7b/eC+yKLPbHnH6bA==", + "dependencies": { + "debug": "~4.1.0", + "engine.io": "~3.6.0", + "has-binary2": "~1.0.2", + "socket.io-adapter": "~1.1.0", + "socket.io-client": "2.5.0", + "socket.io-parser": "~3.4.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", + "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==" + }, + "node_modules/socket.io-client": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.5.0.tgz", + "integrity": "sha512-lOO9clmdgssDykiOmVQQitwBAF3I6mYcQAo7hQ7AM6Ny5X7fp8hIJ3HcQs3Rjz4SoggoxA1OgrQyY8EgTbcPYw==", + "dependencies": { + "backo2": "1.0.2", + "component-bind": "1.0.0", + "component-emitter": "~1.3.0", + "debug": "~3.1.0", + "engine.io-client": "~3.5.0", + "has-binary2": "~1.0.2", + "indexof": "0.0.1", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "socket.io-parser": "~3.3.0", + "to-array": "0.1.4" + } + }, + "node_modules/socket.io-client/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/socket.io-client/node_modules/isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ==" + }, + "node_modules/socket.io-client/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/socket.io-client/node_modules/socket.io-parser": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.4.tgz", + "integrity": "sha512-z/pFQB3x+EZldRRzORYW1vwVO8m/3ILkswtnpoeU6Ve3cbMWkmHEWDAVJn4QJtchiiFTo5j7UG2QvwxvaA9vow==", + "dependencies": { + "component-emitter": "~1.3.0", + "debug": "~3.1.0", + "isarray": "2.0.1" + } + }, + "node_modules/socket.io-parser": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.3.tgz", + "integrity": "sha512-1rE4dZN3kCI/E5wixd393hmbqa78vVpkKmnEJhLeWoS/C5hbFYAbcSfnWoaVH43u9ToUVtzKjguxEZq+1XZfCQ==", + "dependencies": { + "component-emitter": "1.2.1", + "debug": "~4.1.0", + "isarray": "2.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA==" + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/socket.io-parser/node_modules/isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ==" + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/sonner": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.4.0.tgz", @@ -9135,11 +10347,36 @@ "dev": true, "peer": true }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, + "peer": true, "dependencies": { "escape-string-regexp": "^2.0.0" }, @@ -9152,6 +10389,7 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, + "peer": true, "engines": { "node": ">=8" } @@ -9402,6 +10640,11 @@ } } }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -9439,7 +10682,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -9591,13 +10833,10 @@ "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==" }, - "node_modules/tlds": { - "version": "1.250.0", - "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.250.0.tgz", - "integrity": "sha512-rWsBfFCWKrjM/o2Q1TTUeYQv6tHSd/umUutDjVs6taTuEgRDIreVYIBgWRWW4ot7jp6n0UVUuxhTLWBtUmPu/w==", - "bin": { - "tlds": "bin.js" - } + "node_modules/tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" }, "node_modules/tmpl": { "version": "1.0.5", @@ -9606,12 +10845,15 @@ "dev": true, "peer": true }, + "node_modules/to-array": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A==" + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "peer": true, "engines": { "node": ">=4" } @@ -9633,6 +10875,18 @@ "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" }, + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -9754,6 +11008,27 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + }, + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -9853,6 +11128,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typed-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-1.1.0.tgz", + "integrity": "sha512-TuQzwiT4DDg19beHam3E66oRXhyqlyfgjHB/5fcvsRXbfmWPJfto9B4a0TBdTrQAPGlGmXh/k7iUI+WsObgORA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, "node_modules/typescript": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", @@ -9866,11 +11157,6 @@ "node": ">=14.17" } }, - "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" - }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -9908,9 +11194,9 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", "dev": true, "funding": [ { @@ -9927,8 +11213,8 @@ } ], "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.1.2", + "picocolors": "^1.0.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -9941,11 +11227,22 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "dependencies": { "punycode": "^2.1.0" } }, + "node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -9978,6 +11275,19 @@ "node": ">=10.12.0" } }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -10011,6 +11321,35 @@ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, + "node_modules/websocket": { + "version": "1.0.35", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.35.tgz", + "integrity": "sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==", + "dependencies": { + "bufferutil": "^4.0.1", + "debug": "^2.2.0", + "es5-ext": "^0.10.63", + "typedarray-to-buffer": "^3.1.5", + "utf-8-validate": "^5.0.2", + "yaeti": "^0.0.6" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/websocket/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/websocket/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -10216,8 +11555,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/write-file-atomic": { "version": "4.0.2", @@ -10260,6 +11598,14 @@ } } }, + "node_modules/xmlhttprequest-ssl": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz", + "integrity": "sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -10270,10 +11616,19 @@ "node": ">=10" } }, + "node_modules/yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", + "engines": { + "node": ">=0.10.32" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "node_modules/yaml": { "version": "2.3.4", @@ -10334,6 +11689,11 @@ "node": ">=8" } }, + "node_modules/yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg==" + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -10367,10 +11727,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "public/bjj_ecdsa_nova_wasm": { - "version": "0.1.0", - "license": "MIT or Apache-2.0" } } } diff --git a/package.json b/package.json index df51389d..2c7781d2 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "zk-summit", + "name": "mpc-starter", "version": "0.1.0", "private": true, "scripts": { @@ -11,9 +11,11 @@ "postinstall": "prisma migrate deploy && prisma generate" }, "dependencies": { + "@emotion/react": "^11.13.0", + "@emotion/styled": "^11.13.0", "@headlessui/react": "^1.7.18", "@hookform/resolvers": "^3.3.4", - "@next/font": "^14.1.4", + "@mui/material": "^5.16.5", "@prisma/client": "^5.8.1", "@simplewebauthn/browser": "^9.0.1", "@simplewebauthn/server": "^9.0.3", @@ -22,24 +24,23 @@ "@tw-classed/react": "^1.7.0", "@vercel/analytics": "^1.2.2", "@vercel/blob": "^0.22.3", + "aes-js": "^3.1.2", "babyjubjub-ecdsa": "^1.2.0", - "bjj_ecdsa_nova_wasm": "file:public/bjj_ecdsa_nova_wasm", + "bignumber.js": "^9.1.2", "circomlibjs": "^0.1.7", "clsx": "^2.1.0", "comlink": "^4.4.1", "dayjs": "^1.11.10", "detectincognitojs": "^1.3.0", "idb": "^8.0.0", + "jiff-mpc": "^1.0.0", "js-sha256": "^0.11.0", - "jsonwebtoken": "^9.0.2", "little-state-machine": "^4.8.0", "mobile-detect": "^1.4.5", "next": "^14.2.3", "react": "^18", "react-dom": "^18", - "react-error-boundary": "^4.0.12", "react-hook-form": "^7.50.1", - "react-linkify": "^1.0.0-alpha", "react-qr-code": "^2.0.12", "sonner": "^1.4.0", "swiper": "^11.1.0", @@ -49,12 +50,8 @@ }, "devDependencies": { "@tanstack/eslint-plugin-query": "^5.17.22", - "@types/jest": "^29.5.11", - "@types/jsonwebtoken": "^9.0.6", "@types/node": "^20", "@types/react": "^18", - "@types/react-dom": "^18", - "@types/react-linkify": "^1.0.4", "@types/uuid": "^9.0.7", "autoprefixer": "^10.0.1", "daisyui": "^4.6.0", @@ -66,4 +63,4 @@ "ts-jest": "^29.1.2", "typescript": "^5" } -} +} \ No newline at end of file diff --git a/prisma/migrations/20240726030848_create_room/migration.sql b/prisma/migrations/20240726030848_create_room/migration.sql new file mode 100644 index 00000000..51898a29 --- /dev/null +++ b/prisma/migrations/20240726030848_create_room/migration.sql @@ -0,0 +1,30 @@ +-- CreateTable +CREATE TABLE "Room" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "numParties" INTEGER NOT NULL, + "isActive" BOOLEAN NOT NULL DEFAULT true, + "creatorId" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Room_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "RoomMember" ( + "id" SERIAL NOT NULL, + "roomId" INTEGER NOT NULL, + "userId" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "RoomMember_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "Room" ADD CONSTRAINT "Room_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "RoomMember" ADD CONSTRAINT "RoomMember_roomId_fkey" FOREIGN KEY ("roomId") REFERENCES "Room"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "RoomMember" ADD CONSTRAINT "RoomMember_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20240727021644_add_password/migration.sql b/prisma/migrations/20240727021644_add_password/migration.sql new file mode 100644 index 00000000..05dd15cf --- /dev/null +++ b/prisma/migrations/20240727021644_add_password/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Room" ADD COLUMN "password" TEXT NOT NULL DEFAULT ''; diff --git a/prisma/migrations/20240803122448_remove_folding/migration.sql b/prisma/migrations/20240803122448_remove_folding/migration.sql new file mode 100644 index 00000000..66a1e6ed --- /dev/null +++ b/prisma/migrations/20240803122448_remove_folding/migration.sql @@ -0,0 +1,11 @@ +/* + Warnings: + + - You are about to drop the `FoldedProof` table. If the table is not empty, all the data it contains will be lost. + +*/ +-- DropForeignKey +ALTER TABLE "FoldedProof" DROP CONSTRAINT "FoldedProof_userId_fkey"; + +-- DropTable +DROP TABLE "FoldedProof"; diff --git a/prisma/migrations/20240808105239_room_creator_id_optional/migration.sql b/prisma/migrations/20240808105239_room_creator_id_optional/migration.sql new file mode 100644 index 00000000..76c2ee25 --- /dev/null +++ b/prisma/migrations/20240808105239_room_creator_id_optional/migration.sql @@ -0,0 +1,8 @@ +-- DropForeignKey +ALTER TABLE "Room" DROP CONSTRAINT "Room_creatorId_fkey"; + +-- AlterTable +ALTER TABLE "Room" ALTER COLUMN "creatorId" DROP NOT NULL; + +-- AddForeignKey +ALTER TABLE "Room" ADD CONSTRAINT "Room_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/migrations/20240808105328_room_member_id_optional/migration.sql b/prisma/migrations/20240808105328_room_member_id_optional/migration.sql new file mode 100644 index 00000000..7e91607c --- /dev/null +++ b/prisma/migrations/20240808105328_room_member_id_optional/migration.sql @@ -0,0 +1,8 @@ +-- DropForeignKey +ALTER TABLE "RoomMember" DROP CONSTRAINT "RoomMember_userId_fkey"; + +-- AlterTable +ALTER TABLE "RoomMember" ALTER COLUMN "userId" DROP NOT NULL; + +-- AddForeignKey +ALTER TABLE "RoomMember" ADD CONSTRAINT "RoomMember_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/migrations/20240808123834_add_optional_display_name/migration.sql b/prisma/migrations/20240808123834_add_optional_display_name/migration.sql new file mode 100644 index 00000000..9a5333ca --- /dev/null +++ b/prisma/migrations/20240808123834_add_optional_display_name/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "RoomMember" ADD COLUMN "displayName" TEXT; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 344c8325..80048d35 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -36,7 +36,8 @@ model User { receivedBenchmarkMessages BenchmarkMessage[] @relation("BenchmarkMessageRecipient") questProofs QuestProof[] @relation("QuestProofUser") admin Admin? - foldedProofs FoldedProof[] + Room Room[] + RoomMember RoomMember[] } model Admin { @@ -168,19 +169,6 @@ model QuestProof { @@unique([questId, userId]) } -model FoldedProof { - id String @id @default(uuid()) - userId Int - attendeeProofLink String? - attendeeNumFolded Int? - speakerProofLink String? - speakerNumFolded Int? - talkProofLink String? - talkNumFolded Int? - createdAt DateTime @default(now()) - user User @relation(fields: [userId], references: [id]) -} - model UserSigNullifier { id String @id @default(uuid()) questProofId String @@ -219,3 +207,25 @@ model CmacChipRegistration { isLocationChip Boolean createdAt DateTime @default(now()) } + +model Room { + id Int @id @default(autoincrement()) + name String + numParties Int + isActive Boolean @default(true) + creatorId Int? + createdAt DateTime @default(now()) + creator User? @relation(fields: [creatorId], references: [id]) + members RoomMember[] + password String @default("") +} + +model RoomMember { + id Int @id @default(autoincrement()) + roomId Int + userId Int? + createdAt DateTime @default(now()) + room Room @relation(fields: [roomId], references: [id]) + user User? @relation(fields: [userId], references: [id]) + displayName String? +} diff --git a/public/bg-glitter.png b/public/bg-glitter.png deleted file mode 100644 index e83ccbd1..00000000 Binary files a/public/bg-glitter.png and /dev/null differ diff --git a/public/bg-gradient-card.png b/public/bg-gradient-card.png deleted file mode 100644 index e543f367..00000000 Binary files a/public/bg-gradient-card.png and /dev/null differ diff --git a/public/bg-gradient.jpg b/public/bg-gradient.jpg deleted file mode 100644 index 2415badb..00000000 Binary files a/public/bg-gradient.jpg and /dev/null differ diff --git a/public/bjj_ecdsa_nova_wasm/bjj_ecdsa_nova_wasm.d.ts b/public/bjj_ecdsa_nova_wasm/bjj_ecdsa_nova_wasm.d.ts deleted file mode 100644 index c6f20da0..00000000 --- a/public/bjj_ecdsa_nova_wasm/bjj_ecdsa_nova_wasm.d.ts +++ /dev/null @@ -1,184 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -/** -* Verify a proof -* @param {string} params_string -* @param {string} proof_string -* @param {string} root -* @param {number} num_steps -* @returns {Promise>} -*/ -export function verify_proof(params_string: string, proof_string: string, root: string, num_steps: number): Promise>; -/** -* -* * Generates the first fold in a proof -* * -* * @param r1cs_url - the url of the r1cs file to load -* * @param wasm_url - the url of the wasm file to load -* * @param params_string - the stringified public parameters file -* * @param root - the root of the tree to prove membership in -* * @param membership_string - the stringified membership inputs -* * -* @param {string} r1cs_url -* @param {string} wasm_url -* @param {string} params_string -* @param {string} root -* @param {string} membership_string -* @returns {Promise} -*/ -export function generate_proof(r1cs_url: string, wasm_url: string, params_string: string, root: string, membership_string: string): Promise; -/** -* -* * Compute the next step of a proof -* * -* * @param params_string - the stringified public parameters file -* * @param proof_string - the stringified proof file -* * @param membership_string - the stringified membership inputs -* * @param zi_primary - the step_out of previous proof and step_in for this proof -* * @return - the stringified proof file -* -* @param {string} r1cs_url -* @param {string} wasm_url -* @param {string} params_string -* @param {string} proof_string -* @param {string} membership_string -* @param {Array} zi_primary -* @returns {Promise} -*/ -export function continue_proof(r1cs_url: string, wasm_url: string, params_string: string, proof_string: string, membership_string: string, zi_primary: Array): Promise; -/** -* -* * Obfuscate a proof by adding in random data to the witness -* @param {string} r1cs_url -* @param {string} wasm_url -* @param {string} params_string -* @param {string} proof_string -* @param {Array} zi_primary -* @returns {Promise} -*/ -export function obfuscate_proof(r1cs_url: string, wasm_url: string, params_string: string, proof_string: string, zi_primary: Array): Promise; -/** -* -* * Gzip compress a proof -* * -* * @param proof_string - the stringified json proof to compress -* * @return - the compressed proof as a Uint8Array -* -* @param {string} proof -* @returns {Uint8Array} -*/ -export function compress_proof(proof: string): Uint8Array; -/** -* -* * Gzip decompress a proof -* * -* * @param compressed - the compressed proof as a Uint8Array -* * @return - the decompressed proof as a string -* -* @param {Uint8Array} compressed -* @returns {string} -*/ -export function decompress_proof(compressed: Uint8Array): string; -/** -*/ -export function init_panic_hook(): void; -/** -* -* * Get a random Fr element as a string for circuit input -* * -* * @return - a random Fr element as a string -* -* @returns {string} -*/ -export function random_fr(): string; -/** -* @param {string} path -* @returns {Promise} -*/ -export function read_file(path: string): Promise; -/** -* @param {string} input_json_string -* @param {string} wasm_file -* @returns {Promise} -*/ -export function generate_witness_browser(input_json_string: string, wasm_file: string): Promise; -/** -* @param {number} num_threads -* @returns {Promise} -*/ -export function initThreadPool(num_threads: number): Promise; -/** -* @param {number} receiver -*/ -export function wbg_rayon_start_worker(receiver: number): void; -/** -*/ -export class wbg_rayon_PoolBuilder { - free(): void; -/** -* @returns {number} -*/ - numThreads(): number; -/** -* @returns {number} -*/ - receiver(): number; -/** -*/ - build(): void; -} - -export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; - -export interface InitOutput { - readonly verify_proof: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => number; - readonly generate_proof: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number) => number; - readonly continue_proof: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number) => number; - readonly obfuscate_proof: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number) => number; - readonly compress_proof: (a: number, b: number) => number; - readonly decompress_proof: (a: number, b: number) => void; - readonly random_fr: (a: number) => void; - readonly init_panic_hook: () => void; - readonly __wbg_wbg_rayon_poolbuilder_free: (a: number) => void; - readonly wbg_rayon_poolbuilder_numThreads: (a: number) => number; - readonly wbg_rayon_poolbuilder_receiver: (a: number) => number; - readonly wbg_rayon_poolbuilder_build: (a: number) => void; - readonly initThreadPool: (a: number) => number; - readonly wbg_rayon_start_worker: (a: number) => void; - readonly read_file: (a: number, b: number) => number; - readonly generate_witness_browser: (a: number, b: number, c: number, d: number) => number; - readonly memory: WebAssembly.Memory; - readonly __wbindgen_malloc: (a: number, b: number) => number; - readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number; - readonly __wbindgen_export_3: WebAssembly.Table; - readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hc60c9e3b09e25594: (a: number, b: number, c: number) => void; - readonly __wbindgen_add_to_stack_pointer: (a: number) => number; - readonly __wbindgen_free: (a: number, b: number, c: number) => void; - readonly __wbindgen_exn_store: (a: number) => void; - readonly wasm_bindgen__convert__closures__invoke2_mut__h2cb2639cde712b77: (a: number, b: number, c: number, d: number) => void; - readonly __wbindgen_thread_destroy: (a?: number, b?: number) => void; - readonly __wbindgen_start: () => void; -} - -export type SyncInitInput = BufferSource | WebAssembly.Module; -/** -* Instantiates the given `module`, which can either be bytes or -* a precompiled `WebAssembly.Module`. -* -* @param {SyncInitInput} module -* @param {WebAssembly.Memory} maybe_memory -* -* @returns {InitOutput} -*/ -export function initSync(module: SyncInitInput, maybe_memory?: WebAssembly.Memory): InitOutput; - -/** -* If `module_or_path` is {RequestInfo} or {URL}, makes a request and -* for everything else, calls `WebAssembly.instantiate` directly. -* -* @param {InitInput | Promise} module_or_path -* @param {WebAssembly.Memory} maybe_memory -* -* @returns {Promise} -*/ -export default function __wbg_init (module_or_path?: InitInput | Promise, maybe_memory?: WebAssembly.Memory): Promise; diff --git a/public/bjj_ecdsa_nova_wasm/bjj_ecdsa_nova_wasm.js b/public/bjj_ecdsa_nova_wasm/bjj_ecdsa_nova_wasm.js deleted file mode 100644 index af4ee6fa..00000000 --- a/public/bjj_ecdsa_nova_wasm/bjj_ecdsa_nova_wasm.js +++ /dev/null @@ -1,839 +0,0 @@ -import { read_file_async, generate_witness_browser_async } from './snippets/nova-scotia-a8e7e2c19d37f2f2/src/circom/wasm_deps/generate_witness_browser.js'; -import { startWorkers } from './snippets/wasm-bindgen-rayon-3e04391371ad0a8e/src/workerHelpers.js'; - -let wasm; - -const heap = new Array(128).fill(undefined); - -heap.push(undefined, null, true, false); - -function getObject(idx) { return heap[idx]; } - -let heap_next = heap.length; - -function dropObject(idx) { - if (idx < 132) return; - heap[idx] = heap_next; - heap_next = idx; -} - -function takeObject(idx) { - const ret = getObject(idx); - dropObject(idx); - return ret; -} - -let WASM_VECTOR_LEN = 0; - -let cachedUint8Memory0 = null; - -function getUint8Memory0() { - if (cachedUint8Memory0 === null || cachedUint8Memory0.buffer !== wasm.memory.buffer) { - cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); - } - return cachedUint8Memory0; -} - -const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } ); - -const encodeString = function (arg, view) { - const buf = cachedTextEncoder.encode(arg); - view.set(buf); - return { - read: arg.length, - written: buf.length - }; -}; - -function passStringToWasm0(arg, malloc, realloc) { - - if (realloc === undefined) { - const buf = cachedTextEncoder.encode(arg); - const ptr = malloc(buf.length, 1) >>> 0; - getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); - WASM_VECTOR_LEN = buf.length; - return ptr; - } - - let len = arg.length; - let ptr = malloc(len, 1) >>> 0; - - const mem = getUint8Memory0(); - - let offset = 0; - - for (; offset < len; offset++) { - const code = arg.charCodeAt(offset); - if (code > 0x7F) break; - mem[ptr + offset] = code; - } - - if (offset !== len) { - if (offset !== 0) { - arg = arg.slice(offset); - } - ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; - const view = getUint8Memory0().subarray(ptr + offset, ptr + len); - const ret = encodeString(arg, view); - - offset += ret.written; - ptr = realloc(ptr, len, offset, 1) >>> 0; - } - - WASM_VECTOR_LEN = offset; - return ptr; -} - -function isLikeNone(x) { - return x === undefined || x === null; -} - -let cachedInt32Memory0 = null; - -function getInt32Memory0() { - if (cachedInt32Memory0 === null || cachedInt32Memory0.buffer !== wasm.memory.buffer) { - cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); - } - return cachedInt32Memory0; -} - -const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } ); - -if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); }; - -function getStringFromWasm0(ptr, len) { - ptr = ptr >>> 0; - return cachedTextDecoder.decode(getUint8Memory0().slice(ptr, ptr + len)); -} - -function addHeapObject(obj) { - if (heap_next === heap.length) heap.push(heap.length + 1); - const idx = heap_next; - heap_next = heap[idx]; - - heap[idx] = obj; - return idx; -} - -let cachedFloat64Memory0 = null; - -function getFloat64Memory0() { - if (cachedFloat64Memory0 === null || cachedFloat64Memory0.buffer !== wasm.memory.buffer) { - cachedFloat64Memory0 = new Float64Array(wasm.memory.buffer); - } - return cachedFloat64Memory0; -} - -const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined') - ? { register: () => {}, unregister: () => {} } - : new FinalizationRegistry(state => { - wasm.__wbindgen_export_3.get(state.dtor)(state.a, state.b) -}); - -function makeMutClosure(arg0, arg1, dtor, f) { - const state = { a: arg0, b: arg1, cnt: 1, dtor }; - const real = (...args) => { - // First up with a closure we increment the internal reference - // count. This ensures that the Rust closure environment won't - // be deallocated while we're invoking it. - state.cnt++; - const a = state.a; - state.a = 0; - try { - return f(a, state.b, ...args); - } finally { - if (--state.cnt === 0) { - wasm.__wbindgen_export_3.get(state.dtor)(a, state.b); - CLOSURE_DTORS.unregister(state); - } else { - state.a = a; - } - } - }; - real.original = state; - CLOSURE_DTORS.register(real, state, state); - return real; -} -function __wbg_adapter_32(arg0, arg1, arg2) { - wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hc60c9e3b09e25594(arg0, arg1, addHeapObject(arg2)); -} - -/** -* Verify a proof -* @param {string} params_string -* @param {string} proof_string -* @param {string} root -* @param {number} num_steps -* @returns {Promise>} -*/ -export function verify_proof(params_string, proof_string, root, num_steps) { - const ptr0 = passStringToWasm0(params_string, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len0 = WASM_VECTOR_LEN; - const ptr1 = passStringToWasm0(proof_string, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len1 = WASM_VECTOR_LEN; - const ptr2 = passStringToWasm0(root, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len2 = WASM_VECTOR_LEN; - const ret = wasm.verify_proof(ptr0, len0, ptr1, len1, ptr2, len2, addHeapObject(num_steps)); - return takeObject(ret); -} - -/** -* -* * Generates the first fold in a proof -* * -* * @param r1cs_url - the url of the r1cs file to load -* * @param wasm_url - the url of the wasm file to load -* * @param params_string - the stringified public parameters file -* * @param root - the root of the tree to prove membership in -* * @param membership_string - the stringified membership inputs -* * -* @param {string} r1cs_url -* @param {string} wasm_url -* @param {string} params_string -* @param {string} root -* @param {string} membership_string -* @returns {Promise} -*/ -export function generate_proof(r1cs_url, wasm_url, params_string, root, membership_string) { - const ptr0 = passStringToWasm0(r1cs_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len0 = WASM_VECTOR_LEN; - const ptr1 = passStringToWasm0(wasm_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len1 = WASM_VECTOR_LEN; - const ptr2 = passStringToWasm0(params_string, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len2 = WASM_VECTOR_LEN; - const ptr3 = passStringToWasm0(root, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len3 = WASM_VECTOR_LEN; - const ptr4 = passStringToWasm0(membership_string, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len4 = WASM_VECTOR_LEN; - const ret = wasm.generate_proof(ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4); - return takeObject(ret); -} - -/** -* -* * Compute the next step of a proof -* * -* * @param params_string - the stringified public parameters file -* * @param proof_string - the stringified proof file -* * @param membership_string - the stringified membership inputs -* * @param zi_primary - the step_out of previous proof and step_in for this proof -* * @return - the stringified proof file -* -* @param {string} r1cs_url -* @param {string} wasm_url -* @param {string} params_string -* @param {string} proof_string -* @param {string} membership_string -* @param {Array} zi_primary -* @returns {Promise} -*/ -export function continue_proof(r1cs_url, wasm_url, params_string, proof_string, membership_string, zi_primary) { - const ptr0 = passStringToWasm0(r1cs_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len0 = WASM_VECTOR_LEN; - const ptr1 = passStringToWasm0(wasm_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len1 = WASM_VECTOR_LEN; - const ptr2 = passStringToWasm0(params_string, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len2 = WASM_VECTOR_LEN; - const ptr3 = passStringToWasm0(proof_string, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len3 = WASM_VECTOR_LEN; - const ptr4 = passStringToWasm0(membership_string, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len4 = WASM_VECTOR_LEN; - const ret = wasm.continue_proof(ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4, addHeapObject(zi_primary)); - return takeObject(ret); -} - -/** -* -* * Obfuscate a proof by adding in random data to the witness -* @param {string} r1cs_url -* @param {string} wasm_url -* @param {string} params_string -* @param {string} proof_string -* @param {Array} zi_primary -* @returns {Promise} -*/ -export function obfuscate_proof(r1cs_url, wasm_url, params_string, proof_string, zi_primary) { - const ptr0 = passStringToWasm0(r1cs_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len0 = WASM_VECTOR_LEN; - const ptr1 = passStringToWasm0(wasm_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len1 = WASM_VECTOR_LEN; - const ptr2 = passStringToWasm0(params_string, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len2 = WASM_VECTOR_LEN; - const ptr3 = passStringToWasm0(proof_string, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len3 = WASM_VECTOR_LEN; - const ret = wasm.obfuscate_proof(ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, addHeapObject(zi_primary)); - return takeObject(ret); -} - -/** -* -* * Gzip compress a proof -* * -* * @param proof_string - the stringified json proof to compress -* * @return - the compressed proof as a Uint8Array -* -* @param {string} proof -* @returns {Uint8Array} -*/ -export function compress_proof(proof) { - const ptr0 = passStringToWasm0(proof, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len0 = WASM_VECTOR_LEN; - const ret = wasm.compress_proof(ptr0, len0); - return takeObject(ret); -} - -/** -* -* * Gzip decompress a proof -* * -* * @param compressed - the compressed proof as a Uint8Array -* * @return - the decompressed proof as a string -* -* @param {Uint8Array} compressed -* @returns {string} -*/ -export function decompress_proof(compressed) { - let deferred1_0; - let deferred1_1; - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - wasm.decompress_proof(retptr, addHeapObject(compressed)); - var r0 = getInt32Memory0()[retptr / 4 + 0]; - var r1 = getInt32Memory0()[retptr / 4 + 1]; - deferred1_0 = r0; - deferred1_1 = r1; - return getStringFromWasm0(r0, r1); - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); - } -} - -/** -*/ -export function init_panic_hook() { - wasm.init_panic_hook(); -} - -/** -* -* * Get a random Fr element as a string for circuit input -* * -* * @return - a random Fr element as a string -* -* @returns {string} -*/ -export function random_fr() { - let deferred1_0; - let deferred1_1; - try { - const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); - wasm.random_fr(retptr); - var r0 = getInt32Memory0()[retptr / 4 + 0]; - var r1 = getInt32Memory0()[retptr / 4 + 1]; - deferred1_0 = r0; - deferred1_1 = r1; - return getStringFromWasm0(r0, r1); - } finally { - wasm.__wbindgen_add_to_stack_pointer(16); - wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); - } -} - -/** -* @param {string} path -* @returns {Promise} -*/ -export function read_file(path) { - const ptr0 = passStringToWasm0(path, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len0 = WASM_VECTOR_LEN; - const ret = wasm.read_file(ptr0, len0); - return takeObject(ret); -} - -/** -* @param {string} input_json_string -* @param {string} wasm_file -* @returns {Promise} -*/ -export function generate_witness_browser(input_json_string, wasm_file) { - const ptr0 = passStringToWasm0(input_json_string, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len0 = WASM_VECTOR_LEN; - const ptr1 = passStringToWasm0(wasm_file, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len1 = WASM_VECTOR_LEN; - const ret = wasm.generate_witness_browser(ptr0, len0, ptr1, len1); - return takeObject(ret); -} - -function handleError(f, args) { - try { - return f.apply(this, args); - } catch (e) { - wasm.__wbindgen_exn_store(addHeapObject(e)); - } -} -function __wbg_adapter_109(arg0, arg1, arg2, arg3) { - wasm.wasm_bindgen__convert__closures__invoke2_mut__h2cb2639cde712b77(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3)); -} - -/** -* @param {number} num_threads -* @returns {Promise} -*/ -export function initThreadPool(num_threads) { - const ret = wasm.initThreadPool(num_threads); - return takeObject(ret); -} - -/** -* @param {number} receiver -*/ -export function wbg_rayon_start_worker(receiver) { - wasm.wbg_rayon_start_worker(receiver); -} - -const wbg_rayon_PoolBuilderFinalization = (typeof FinalizationRegistry === 'undefined') - ? { register: () => {}, unregister: () => {} } - : new FinalizationRegistry(ptr => wasm.__wbg_wbg_rayon_poolbuilder_free(ptr >>> 0)); -/** -*/ -export class wbg_rayon_PoolBuilder { - - static __wrap(ptr) { - ptr = ptr >>> 0; - const obj = Object.create(wbg_rayon_PoolBuilder.prototype); - obj.__wbg_ptr = ptr; - wbg_rayon_PoolBuilderFinalization.register(obj, obj.__wbg_ptr, obj); - return obj; - } - - __destroy_into_raw() { - const ptr = this.__wbg_ptr; - this.__wbg_ptr = 0; - wbg_rayon_PoolBuilderFinalization.unregister(this); - return ptr; - } - - free() { - const ptr = this.__destroy_into_raw(); - wasm.__wbg_wbg_rayon_poolbuilder_free(ptr); - } - /** - * @returns {number} - */ - numThreads() { - const ret = wasm.wbg_rayon_poolbuilder_numThreads(this.__wbg_ptr); - return ret >>> 0; - } - /** - * @returns {number} - */ - receiver() { - const ret = wasm.wbg_rayon_poolbuilder_receiver(this.__wbg_ptr); - return ret >>> 0; - } - /** - */ - build() { - wasm.wbg_rayon_poolbuilder_build(this.__wbg_ptr); - } -} - -async function __wbg_load(module, imports) { - if (typeof Response === 'function' && module instanceof Response) { - if (typeof WebAssembly.instantiateStreaming === 'function') { - try { - return await WebAssembly.instantiateStreaming(module, imports); - - } catch (e) { - if (module.headers.get('Content-Type') != 'application/wasm') { - console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); - - } else { - throw e; - } - } - } - - const bytes = await module.arrayBuffer(); - return await WebAssembly.instantiate(bytes, imports); - - } else { - const instance = await WebAssembly.instantiate(module, imports); - - if (instance instanceof WebAssembly.Instance) { - return { instance, module }; - - } else { - return instance; - } - } -} - -function __wbg_get_imports() { - const imports = {}; - imports.wbg = {}; - imports.wbg.__wbindgen_cb_drop = function(arg0) { - const obj = takeObject(arg0).original; - if (obj.cnt-- == 1) { - obj.a = 0; - return true; - } - const ret = false; - return ret; - }; - imports.wbg.__wbindgen_object_drop_ref = function(arg0) { - takeObject(arg0); - }; - imports.wbg.__wbg_log_c9a02fea9342ce27 = function(arg0, arg1) { - console.log(getStringFromWasm0(arg0, arg1)); - }; - imports.wbg.__wbindgen_string_get = function(arg0, arg1) { - const obj = getObject(arg1); - const ret = typeof(obj) === 'string' ? obj : undefined; - var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len1 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len1; - getInt32Memory0()[arg0 / 4 + 0] = ptr1; - }; - imports.wbg.__wbindgen_string_new = function(arg0, arg1) { - const ret = getStringFromWasm0(arg0, arg1); - return addHeapObject(ret); - }; - imports.wbg.__wbindgen_number_get = function(arg0, arg1) { - const obj = getObject(arg1); - const ret = typeof(obj) === 'number' ? obj : undefined; - getFloat64Memory0()[arg0 / 8 + 1] = isLikeNone(ret) ? 0 : ret; - getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret); - }; - imports.wbg.__wbg_new_abda76e883ba8a5f = function() { - const ret = new Error(); - return addHeapObject(ret); - }; - imports.wbg.__wbg_stack_658279fe44541cf6 = function(arg0, arg1) { - const ret = getObject(arg1).stack; - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len1 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len1; - getInt32Memory0()[arg0 / 4 + 0] = ptr1; - }; - imports.wbg.__wbg_error_f851667af71bcfc6 = function(arg0, arg1) { - let deferred0_0; - let deferred0_1; - try { - deferred0_0 = arg0; - deferred0_1 = arg1; - console.error(getStringFromWasm0(arg0, arg1)); - } finally { - wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); - } - }; - imports.wbg.__wbg_readfileasync_631b508da962cc69 = function(arg0, arg1) { - const ret = read_file_async(getStringFromWasm0(arg0, arg1)); - return addHeapObject(ret); - }; - imports.wbg.__wbg_generatewitnessbrowserasync_b382537b674d8915 = function(arg0, arg1, arg2, arg3) { - const ret = generate_witness_browser_async(getStringFromWasm0(arg0, arg1), getStringFromWasm0(arg2, arg3)); - return addHeapObject(ret); - }; - imports.wbg.__wbindgen_object_clone_ref = function(arg0) { - const ret = getObject(arg0); - return addHeapObject(ret); - }; - imports.wbg.__wbindgen_link_c83fa9fa3333cb90 = function(arg0) { - const ret = "data:application/javascript," + encodeURIComponent(`onmessage = function (ev) { - let [ia, index, value] = ev.data; - ia = new Int32Array(ia.buffer); - let result = Atomics.wait(ia, index, value); - postMessage(result); - }; - `); - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len1 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len1; - getInt32Memory0()[arg0 / 4 + 0] = ptr1; - }; - imports.wbg.__wbindgen_number_new = function(arg0) { - const ret = arg0; - return addHeapObject(ret); - }; - imports.wbg.__wbg_waitAsync_92219692955aa445 = function() { - const ret = Atomics.waitAsync; - return addHeapObject(ret); - }; - imports.wbg.__wbindgen_is_undefined = function(arg0) { - const ret = getObject(arg0) === undefined; - return ret; - }; - imports.wbg.__wbg_waitAsync_ecc6bb0101f0b119 = function(arg0, arg1, arg2) { - const ret = Atomics.waitAsync(getObject(arg0), arg1, arg2); - return addHeapObject(ret); - }; - imports.wbg.__wbg_async_d412da4fadd37b75 = function(arg0) { - const ret = getObject(arg0).async; - return ret; - }; - imports.wbg.__wbg_value_ceb95bfbc794ce19 = function(arg0) { - const ret = getObject(arg0).value; - return addHeapObject(ret); - }; - imports.wbg.__wbg_queueMicrotask_f61ee94ee663068b = function(arg0) { - queueMicrotask(getObject(arg0)); - }; - imports.wbg.__wbg_queueMicrotask_f82fc5d1e8f816ae = function(arg0) { - const ret = getObject(arg0).queueMicrotask; - return addHeapObject(ret); - }; - imports.wbg.__wbindgen_is_function = function(arg0) { - const ret = typeof(getObject(arg0)) === 'function'; - return ret; - }; - imports.wbg.__wbg_instanceof_Window_cee7a886d55e7df5 = function(arg0) { - let result; - try { - result = getObject(arg0) instanceof Window; - } catch (_) { - result = false; - } - const ret = result; - return ret; - }; - imports.wbg.__wbg_data_bbdd2d77ab2f7e78 = function(arg0) { - const ret = getObject(arg0).data; - return addHeapObject(ret); - }; - imports.wbg.__wbg_setonmessage_69d6948a76937c04 = function(arg0, arg1) { - getObject(arg0).onmessage = getObject(arg1); - }; - imports.wbg.__wbg_new_cc1b6504e92f2e3c = function() { return handleError(function (arg0, arg1) { - const ret = new Worker(getStringFromWasm0(arg0, arg1)); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_postMessage_64df7b91855fc1fb = function() { return handleError(function (arg0, arg1) { - getObject(arg0).postMessage(getObject(arg1)); - }, arguments) }; - imports.wbg.__wbg_crypto_d05b68a3572bb8ca = function(arg0) { - const ret = getObject(arg0).crypto; - return addHeapObject(ret); - }; - imports.wbg.__wbindgen_is_object = function(arg0) { - const val = getObject(arg0); - const ret = typeof(val) === 'object' && val !== null; - return ret; - }; - imports.wbg.__wbg_process_b02b3570280d0366 = function(arg0) { - const ret = getObject(arg0).process; - return addHeapObject(ret); - }; - imports.wbg.__wbg_versions_c1cb42213cedf0f5 = function(arg0) { - const ret = getObject(arg0).versions; - return addHeapObject(ret); - }; - imports.wbg.__wbg_node_43b1089f407e4ec2 = function(arg0) { - const ret = getObject(arg0).node; - return addHeapObject(ret); - }; - imports.wbg.__wbindgen_is_string = function(arg0) { - const ret = typeof(getObject(arg0)) === 'string'; - return ret; - }; - imports.wbg.__wbg_require_9a7e0f667ead4995 = function() { return handleError(function () { - const ret = module.require; - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_msCrypto_10fc94afee92bd76 = function(arg0) { - const ret = getObject(arg0).msCrypto; - return addHeapObject(ret); - }; - imports.wbg.__wbg_getRandomValues_7e42b4fb8779dc6d = function() { return handleError(function (arg0, arg1) { - getObject(arg0).getRandomValues(getObject(arg1)); - }, arguments) }; - imports.wbg.__wbg_randomFillSync_b70ccbdf4926a99d = function() { return handleError(function (arg0, arg1) { - getObject(arg0).randomFillSync(takeObject(arg1)); - }, arguments) }; - imports.wbg.__wbg_get_0ee8ea3c7c984c45 = function(arg0, arg1) { - const ret = getObject(arg0)[arg1 >>> 0]; - return addHeapObject(ret); - }; - imports.wbg.__wbg_newnoargs_cfecb3965268594c = function(arg0, arg1) { - const ret = new Function(getStringFromWasm0(arg0, arg1)); - return addHeapObject(ret); - }; - imports.wbg.__wbg_call_3f093dd26d5569f8 = function() { return handleError(function (arg0, arg1) { - const ret = getObject(arg0).call(getObject(arg1)); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_self_05040bd9523805b9 = function() { return handleError(function () { - const ret = self.self; - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_window_adc720039f2cb14f = function() { return handleError(function () { - const ret = window.window; - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_globalThis_622105db80c1457d = function() { return handleError(function () { - const ret = globalThis.globalThis; - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_global_f56b013ed9bcf359 = function() { return handleError(function () { - const ret = global.global; - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_newwithlength_a20dc3b27e1cb1b2 = function(arg0) { - const ret = new Array(arg0 >>> 0); - return addHeapObject(ret); - }; - imports.wbg.__wbg_set_79c308ecd9a1d091 = function(arg0, arg1, arg2) { - getObject(arg0)[arg1 >>> 0] = takeObject(arg2); - }; - imports.wbg.__wbg_of_94ac9e20a3c46ec5 = function(arg0, arg1, arg2) { - const ret = Array.of(getObject(arg0), getObject(arg1), getObject(arg2)); - return addHeapObject(ret); - }; - imports.wbg.__wbg_call_67f2111acd2dfdb6 = function() { return handleError(function (arg0, arg1, arg2) { - const ret = getObject(arg0).call(getObject(arg1), getObject(arg2)); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_new_70828a4353259d4b = function(arg0, arg1) { - try { - var state0 = {a: arg0, b: arg1}; - var cb0 = (arg0, arg1) => { - const a = state0.a; - state0.a = 0; - try { - return __wbg_adapter_109(a, state0.b, arg0, arg1); - } finally { - state0.a = a; - } - }; - const ret = new Promise(cb0); - return addHeapObject(ret); - } finally { - state0.a = state0.b = 0; - } - }; - imports.wbg.__wbg_resolve_5da6faf2c96fd1d5 = function(arg0) { - const ret = Promise.resolve(getObject(arg0)); - return addHeapObject(ret); - }; - imports.wbg.__wbg_then_f9e58f5a50f43eae = function(arg0, arg1) { - const ret = getObject(arg0).then(getObject(arg1)); - return addHeapObject(ret); - }; - imports.wbg.__wbg_then_20a5920e447d1cb1 = function(arg0, arg1, arg2) { - const ret = getObject(arg0).then(getObject(arg1), getObject(arg2)); - return addHeapObject(ret); - }; - imports.wbg.__wbg_buffer_b914fb8b50ebbc3e = function(arg0) { - const ret = getObject(arg0).buffer; - return addHeapObject(ret); - }; - imports.wbg.__wbg_new_e4dd61c29af24331 = function(arg0) { - const ret = new Int32Array(getObject(arg0)); - return addHeapObject(ret); - }; - imports.wbg.__wbg_newwithbyteoffsetandlength_0de9ee56e9f6ee6e = function(arg0, arg1, arg2) { - const ret = new Uint8Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0); - return addHeapObject(ret); - }; - imports.wbg.__wbg_new_b1f2d6842d615181 = function(arg0) { - const ret = new Uint8Array(getObject(arg0)); - return addHeapObject(ret); - }; - imports.wbg.__wbg_set_7d988c98e6ced92d = function(arg0, arg1, arg2) { - getObject(arg0).set(getObject(arg1), arg2 >>> 0); - }; - imports.wbg.__wbg_length_21c4b0ae73cba59d = function(arg0) { - const ret = getObject(arg0).length; - return ret; - }; - imports.wbg.__wbg_newwithlength_0d03cef43b68a530 = function(arg0) { - const ret = new Uint8Array(arg0 >>> 0); - return addHeapObject(ret); - }; - imports.wbg.__wbg_subarray_adc418253d76e2f1 = function(arg0, arg1, arg2) { - const ret = getObject(arg0).subarray(arg1 >>> 0, arg2 >>> 0); - return addHeapObject(ret); - }; - imports.wbg.__wbindgen_throw = function(arg0, arg1) { - throw new Error(getStringFromWasm0(arg0, arg1)); - }; - imports.wbg.__wbindgen_rethrow = function(arg0) { - throw takeObject(arg0); - }; - imports.wbg.__wbindgen_module = function() { - const ret = __wbg_init.__wbindgen_wasm_module; - return addHeapObject(ret); - }; - imports.wbg.__wbindgen_memory = function() { - const ret = wasm.memory; - return addHeapObject(ret); - }; - imports.wbg.__wbg_startWorkers_2ee336a9694dda13 = function(arg0, arg1, arg2) { - const ret = startWorkers(takeObject(arg0), takeObject(arg1), wbg_rayon_PoolBuilder.__wrap(arg2)); - return addHeapObject(ret); - }; - imports.wbg.__wbindgen_closure_wrapper1452 = function(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 300, __wbg_adapter_32); - return addHeapObject(ret); - }; - imports.wbg.__wbindgen_closure_wrapper1454 = function(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 300, __wbg_adapter_32); - return addHeapObject(ret); - }; - - return imports; -} - -function __wbg_init_memory(imports, maybe_memory) { - imports.wbg.memory = maybe_memory || new WebAssembly.Memory({initial:19,maximum:65536,shared:true}); -} - -function __wbg_finalize_init(instance, module) { - wasm = instance.exports; - __wbg_init.__wbindgen_wasm_module = module; - cachedFloat64Memory0 = null; - cachedInt32Memory0 = null; - cachedUint8Memory0 = null; - - wasm.__wbindgen_start(); - return wasm; -} - -function initSync(module, maybe_memory) { - if (wasm !== undefined) return wasm; - - const imports = __wbg_get_imports(); - - __wbg_init_memory(imports, maybe_memory); - - if (!(module instanceof WebAssembly.Module)) { - module = new WebAssembly.Module(module); - } - - const instance = new WebAssembly.Instance(module, imports); - - return __wbg_finalize_init(instance, module); -} - -async function __wbg_init(input, maybe_memory) { - if (wasm !== undefined) return wasm; - - if (typeof input === 'undefined') { - input = new URL('bjj_ecdsa_nova_wasm_bg.wasm', import.meta.url); - } - const imports = __wbg_get_imports(); - - if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { - input = fetch(input); - } - - __wbg_init_memory(imports, maybe_memory); - - const { instance, module } = await __wbg_load(await input, imports); - - return __wbg_finalize_init(instance, module); -} - -export { initSync } -export default __wbg_init; diff --git a/public/bjj_ecdsa_nova_wasm/bjj_ecdsa_nova_wasm_bg.wasm b/public/bjj_ecdsa_nova_wasm/bjj_ecdsa_nova_wasm_bg.wasm deleted file mode 100644 index ed736ea0..00000000 Binary files a/public/bjj_ecdsa_nova_wasm/bjj_ecdsa_nova_wasm_bg.wasm and /dev/null differ diff --git a/public/bjj_ecdsa_nova_wasm/bjj_ecdsa_nova_wasm_bg.wasm.d.ts b/public/bjj_ecdsa_nova_wasm/bjj_ecdsa_nova_wasm_bg.wasm.d.ts deleted file mode 100644 index 78d53c03..00000000 --- a/public/bjj_ecdsa_nova_wasm/bjj_ecdsa_nova_wasm_bg.wasm.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -export function verify_proof(a: number, b: number, c: number, d: number, e: number, f: number, g: number): number; -export function generate_proof(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number): number; -export function continue_proof(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number, j: number, k: number): number; -export function obfuscate_proof(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number): number; -export function compress_proof(a: number, b: number): number; -export function decompress_proof(a: number, b: number): void; -export function random_fr(a: number): void; -export function init_panic_hook(): void; -export function __wbg_wbg_rayon_poolbuilder_free(a: number): void; -export function wbg_rayon_poolbuilder_numThreads(a: number): number; -export function wbg_rayon_poolbuilder_receiver(a: number): number; -export function wbg_rayon_poolbuilder_build(a: number): void; -export function initThreadPool(a: number): number; -export function wbg_rayon_start_worker(a: number): void; -export function read_file(a: number, b: number): number; -export function generate_witness_browser(a: number, b: number, c: number, d: number): number; -export const memory: WebAssembly.Memory; -export function __wbindgen_malloc(a: number, b: number): number; -export function __wbindgen_realloc(a: number, b: number, c: number, d: number): number; -export const __wbindgen_export_3: WebAssembly.Table; -export function _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hc60c9e3b09e25594(a: number, b: number, c: number): void; -export function __wbindgen_add_to_stack_pointer(a: number): number; -export function __wbindgen_free(a: number, b: number, c: number): void; -export function __wbindgen_exn_store(a: number): void; -export function wasm_bindgen__convert__closures__invoke2_mut__h2cb2639cde712b77(a: number, b: number, c: number, d: number): void; -export function __wbindgen_thread_destroy(a: number, b: number): void; -export function __wbindgen_start(): void; diff --git a/public/bjj_ecdsa_nova_wasm/package.json b/public/bjj_ecdsa_nova_wasm/package.json deleted file mode 100644 index 34c0bff0..00000000 --- a/public/bjj_ecdsa_nova_wasm/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "bjj_ecdsa_nova_wasm", - "version": "0.1.0", - "license": "MIT or Apache-2.0", - "files": [ - "bjj_ecdsa_nova_wasm_bg.wasm", - "bjj_ecdsa_nova_wasm.js", - "bjj_ecdsa_nova_wasm.d.ts" - ], - "module": "bjj_ecdsa_nova_wasm.js", - "types": "bjj_ecdsa_nova_wasm.d.ts", - "sideEffects": [ - "./snippets/*" - ] -} \ No newline at end of file diff --git a/public/bjj_ecdsa_nova_wasm/snippets/nova-scotia-a8e7e2c19d37f2f2/src/circom/wasm_deps/generate_witness_browser.js b/public/bjj_ecdsa_nova_wasm/snippets/nova-scotia-a8e7e2c19d37f2f2/src/circom/wasm_deps/generate_witness_browser.js deleted file mode 100644 index 6e84c28b..00000000 --- a/public/bjj_ecdsa_nova_wasm/snippets/nova-scotia-a8e7e2c19d37f2f2/src/circom/wasm_deps/generate_witness_browser.js +++ /dev/null @@ -1,351 +0,0 @@ -// TODO: can these reads be cached? -export async function read_file_async(path) { - const response = await fetch(path); - const bytes = await response.arrayBuffer(); - const res = new Uint8Array(bytes); - return res; -} - -export async function generate_witness_browser_async( - input_json_string, - wasm_file -) { - const input = JSON.parse(input_json_string); - const buffer = await read_file_async(wasm_file); - const witnessCalculator = await wc(buffer); - const buff = await witnessCalculator.calculateWTNSBin(input, 0); - return buff; -} - -// It gives me great pain to do this, but witness_calculator.js needs to be copied here -// so wasm-pack would pick it up :( -const wc = async function builder(code, options) { - options = options || {}; - - let wasmModule; - try { - wasmModule = await WebAssembly.compile(code); - } catch (err) { - console.log(err); - console.log( - "\nTry to run circom --c in order to generate c++ code instead\n" - ); - throw new Error(err); - } - - let wc; - - let errStr = ""; - let msgStr = ""; - - const instance = await WebAssembly.instantiate(wasmModule, { - runtime: { - exceptionHandler: function (code) { - let err; - if (code == 1) { - err = "Signal not found.\n"; - } else if (code == 2) { - err = "Too many signals set.\n"; - } else if (code == 3) { - err = "Signal already set.\n"; - } else if (code == 4) { - err = "Assert Failed.\n"; - } else if (code == 5) { - err = "Not enough memory.\n"; - } else if (code == 6) { - err = "Input signal array access exceeds the size.\n"; - } else { - err = "Unknown error.\n"; - } - throw new Error(err + errStr); - }, - printErrorMessage: function () { - errStr += getMessage() + "\n"; - // console.error(getMessage()); - }, - writeBufferMessage: function () { - const msg = getMessage(); - // Any calls to `log()` will always end with a `\n`, so that's when we print and reset - if (msg === "\n") { - console.log(msgStr); - msgStr = ""; - } else { - // If we've buffered other content, put a space in between the items - if (msgStr !== "") { - msgStr += " "; - } - // Then append the message to the message we are creating - msgStr += msg; - } - }, - showSharedRWMemory: function () { - printSharedRWMemory(); - }, - }, - }); - - const sanityCheck = options; - // options && - // ( - // options.sanityCheck || - // options.logGetSignal || - // options.logSetSignal || - // options.logStartComponent || - // options.logFinishComponent - // ); - - wc = new WitnessCalculator(instance, sanityCheck); - return wc; - - function getMessage() { - var message = ""; - var c = instance.exports.getMessageChar(); - while (c != 0) { - message += String.fromCharCode(c); - c = instance.exports.getMessageChar(); - } - return message; - } - - function printSharedRWMemory() { - const shared_rw_memory_size = instance.exports.getFieldNumLen32(); - const arr = new Uint32Array(shared_rw_memory_size); - for (let j = 0; j < shared_rw_memory_size; j++) { - arr[shared_rw_memory_size - 1 - j] = - instance.exports.readSharedRWMemory(j); - } - - // If we've buffered other content, put a space in between the items - if (msgStr !== "") { - msgStr += " "; - } - // Then append the value to the message we are creating - msgStr += fromArray32(arr).toString(); - } -}; - -class WitnessCalculator { - constructor(instance, sanityCheck) { - this.instance = instance; - - this.version = this.instance.exports.getVersion(); - this.n32 = this.instance.exports.getFieldNumLen32(); - - this.instance.exports.getRawPrime(); - const arr = new Uint32Array(this.n32); - for (let i = 0; i < this.n32; i++) { - arr[this.n32 - 1 - i] = this.instance.exports.readSharedRWMemory(i); - } - this.prime = fromArray32(arr); - - this.witnessSize = this.instance.exports.getWitnessSize(); - - this.sanityCheck = sanityCheck; - } - - circom_version() { - return this.instance.exports.getVersion(); - } - - async _doCalculateWitness(input, sanityCheck) { - //input is assumed to be a map from signals to arrays of bigints - this.instance.exports.init(this.sanityCheck || sanityCheck ? 1 : 0); - const keys = Object.keys(input); - var input_counter = 0; - keys.forEach((k) => { - const h = fnvHash(k); - const hMSB = parseInt(h.slice(0, 8), 16); - const hLSB = parseInt(h.slice(8, 16), 16); - const fArr = flatArray(input[k]); - let signalSize = this.instance.exports.getInputSignalSize(hMSB, hLSB); - if (signalSize < 0) { - throw new Error(`Signal ${k} not found\n`); - } - if (fArr.length < signalSize) { - throw new Error(`Not enough values for input signal ${k}\n`); - } - if (fArr.length > signalSize) { - throw new Error(`Too many values for input signal ${k}\n`); - } - for (let i = 0; i < fArr.length; i++) { - const arrFr = toArray32(normalize(fArr[i], this.prime), this.n32); - for (let j = 0; j < this.n32; j++) { - this.instance.exports.writeSharedRWMemory(j, arrFr[this.n32 - 1 - j]); - } - try { - this.instance.exports.setInputSignal(hMSB, hLSB, i); - input_counter++; - } catch (err) { - // console.log(`After adding signal ${i} of ${k}`) - throw new Error(err); - } - } - }); - if (input_counter < this.instance.exports.getInputSize()) { - throw new Error( - `Not all inputs have been set. Only ${input_counter} out of ${this.instance.exports.getInputSize()}` - ); - } - } - - async calculateWitness(input, sanityCheck) { - const w = []; - - await this._doCalculateWitness(input, sanityCheck); - - for (let i = 0; i < this.witnessSize; i++) { - this.instance.exports.getWitness(i); - const arr = new Uint32Array(this.n32); - for (let j = 0; j < this.n32; j++) { - arr[this.n32 - 1 - j] = this.instance.exports.readSharedRWMemory(j); - } - w.push(fromArray32(arr)); - } - - return w; - } - - async calculateBinWitness(input, sanityCheck) { - const buff32 = new Uint32Array(this.witnessSize * this.n32); - const buff = new Uint8Array(buff32.buffer); - await this._doCalculateWitness(input, sanityCheck); - - for (let i = 0; i < this.witnessSize; i++) { - this.instance.exports.getWitness(i); - const pos = i * this.n32; - for (let j = 0; j < this.n32; j++) { - buff32[pos + j] = this.instance.exports.readSharedRWMemory(j); - } - } - - return buff; - } - - async calculateWTNSBin(input, sanityCheck) { - const buff32 = new Uint32Array(this.witnessSize * this.n32 + this.n32 + 11); - const buff = new Uint8Array(buff32.buffer); - await this._doCalculateWitness(input, sanityCheck); - - //"wtns" - buff[0] = "w".charCodeAt(0); - buff[1] = "t".charCodeAt(0); - buff[2] = "n".charCodeAt(0); - buff[3] = "s".charCodeAt(0); - - //version 2 - buff32[1] = 2; - - //number of sections: 2 - buff32[2] = 2; - - //id section 1 - buff32[3] = 1; - - const n8 = this.n32 * 4; - //id section 1 length in 64bytes - const idSection1length = 8 + n8; - const idSection1lengthHex = idSection1length.toString(16); - buff32[4] = parseInt(idSection1lengthHex.slice(0, 8), 16); - buff32[5] = parseInt(idSection1lengthHex.slice(8, 16), 16); - - //this.n32 - buff32[6] = n8; - - //prime number - this.instance.exports.getRawPrime(); - - var pos = 7; - for (let j = 0; j < this.n32; j++) { - buff32[pos + j] = this.instance.exports.readSharedRWMemory(j); - } - pos += this.n32; - - // witness size - buff32[pos] = this.witnessSize; - pos++; - - //id section 2 - buff32[pos] = 2; - pos++; - - // section 2 length - const idSection2length = n8 * this.witnessSize; - const idSection2lengthHex = idSection2length.toString(16); - buff32[pos] = parseInt(idSection2lengthHex.slice(0, 8), 16); - buff32[pos + 1] = parseInt(idSection2lengthHex.slice(8, 16), 16); - - pos += 2; - for (let i = 0; i < this.witnessSize; i++) { - this.instance.exports.getWitness(i); - for (let j = 0; j < this.n32; j++) { - buff32[pos + j] = this.instance.exports.readSharedRWMemory(j); - } - pos += this.n32; - } - - return buff; - } -} - -function toArray32(rem, size) { - const res = []; //new Uint32Array(size); //has no unshift - const radix = BigInt(0x100000000); - while (rem) { - res.unshift(Number(rem % radix)); - rem = rem / radix; - } - if (size) { - var i = size - res.length; - while (i > 0) { - res.unshift(0); - i--; - } - } - return res; -} - -function fromArray32(arr) { - //returns a BigInt - var res = BigInt(0); - const radix = BigInt(0x100000000); - for (let i = 0; i < arr.length; i++) { - res = res * radix + BigInt(arr[i]); - } - return res; -} - -function flatArray(a) { - var res = []; - fillArray(res, a); - return res; - - function fillArray(res, a) { - if (Array.isArray(a)) { - for (let i = 0; i < a.length; i++) { - fillArray(res, a[i]); - } - } else { - res.push(a); - } - } -} - -function normalize(n, prime) { - let res = BigInt(n) % prime; - if (res < 0) res += prime; - return res; -} - -function fnvHash(str) { - const uint64_max = BigInt(2) ** BigInt(64); - let hash = BigInt("0xCBF29CE484222325"); - for (var i = 0; i < str.length; i++) { - hash ^= BigInt(str[i].charCodeAt()); - hash *= BigInt(0x100000001b3); - hash %= uint64_max; - } - let shash = hash.toString(16); - let n = 16 - shash.length; - shash = "0".repeat(n).concat(shash); - return shash; -} diff --git a/public/bjj_ecdsa_nova_wasm/snippets/wasm-bindgen-futures-aa33b75deb2868e3/src/task/worker.js b/public/bjj_ecdsa_nova_wasm/snippets/wasm-bindgen-futures-aa33b75deb2868e3/src/task/worker.js deleted file mode 100644 index d25dab66..00000000 --- a/public/bjj_ecdsa_nova_wasm/snippets/wasm-bindgen-futures-aa33b75deb2868e3/src/task/worker.js +++ /dev/null @@ -1,6 +0,0 @@ -onmessage = function (ev) { - let [ia, index, value] = ev.data; - ia = new Int32Array(ia.buffer); - let result = Atomics.wait(ia, index, value); - postMessage(result); -}; diff --git a/public/bjj_ecdsa_nova_wasm/snippets/wasm-bindgen-rayon-3e04391371ad0a8e/src/workerHelpers.js b/public/bjj_ecdsa_nova_wasm/snippets/wasm-bindgen-rayon-3e04391371ad0a8e/src/workerHelpers.js deleted file mode 100644 index fdbc924f..00000000 --- a/public/bjj_ecdsa_nova_wasm/snippets/wasm-bindgen-rayon-3e04391371ad0a8e/src/workerHelpers.js +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2022 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Note: this is never used, but necessary to prevent a bug in Firefox -// (https://bugzilla.mozilla.org/show_bug.cgi?id=1702191) where it collects -// Web Workers that have a shared WebAssembly memory with the main thread, -// but are not explicitly rooted via a `Worker` instance. -// -// By storing them in a variable, we can keep `Worker` objects around and -// prevent them from getting GC-d. -let _workers; - -export async function startWorkers(module, memory, builder) { - if (builder.numThreads() === 0) { - throw new Error(`num_threads must be > 0.`); - } - - const workerInit = { - module, - memory, - receiver: builder.receiver() - }; - - _workers = await Promise.all( - Array.from({ length: builder.numThreads() }, async () => { - // Self-spawn into a new Worker. - // - // TODO: while `new URL('...', import.meta.url) becomes a semi-standard - // way to get asset URLs relative to the module across various bundlers - // and browser, ideally we should switch to `import.meta.resolve` - // once it becomes a standard. - const worker = new Worker( - new URL('./workerHelpers.worker.js', import.meta.url), - { - type: 'module' - } - ); - worker.postMessage(workerInit); - await new Promise(resolve => - worker.addEventListener('message', resolve, { once: true }) - ); - return worker; - }) - ); - builder.build(); -} diff --git a/public/bjj_ecdsa_nova_wasm/snippets/wasm-bindgen-rayon-3e04391371ad0a8e/src/workerHelpers.worker.js b/public/bjj_ecdsa_nova_wasm/snippets/wasm-bindgen-rayon-3e04391371ad0a8e/src/workerHelpers.worker.js deleted file mode 100644 index c791501f..00000000 --- a/public/bjj_ecdsa_nova_wasm/snippets/wasm-bindgen-rayon-3e04391371ad0a8e/src/workerHelpers.worker.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2022 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Note: our JS should have been generated in -// `[out-dir]/snippets/wasm-bindgen-rayon-[hash]/workerHelpers.worker.js`, -// resolve the main module via `../../..`. -// -// This might need updating if the generated structure changes on wasm-bindgen -// side ever in the future, but works well with bundlers today. The whole -// point of this crate, after all, is to abstract away unstable features -// and temporary bugs so that you don't need to deal with them in your code. -import initWbg, { wbg_rayon_start_worker } from '../../../'; - -onmessage = async ({ data: { module, memory, receiver } }) => { - await initWbg(module, memory); - postMessage(true); - wbg_rayon_start_worker(receiver); -}; diff --git a/public/wrapped-bg.png b/public/wrapped-bg.png deleted file mode 100644 index 310b04d9..00000000 Binary files a/public/wrapped-bg.png and /dev/null differ diff --git a/src/components/AppFooter.tsx b/src/components/AppFooter.tsx index 71514315..d3e40298 100644 --- a/src/components/AppFooter.tsx +++ b/src/components/AppFooter.tsx @@ -50,6 +50,12 @@ const AppFooter = () => { icon: Icons.Proof, iconSize: 14, }, + { + label: "Queries", + href: "/mpc", + icon: Icons.Social, + iconSize: 14, + }, ]; return ( @@ -57,7 +63,7 @@ const AppFooter = () => { id="footer" className="fixed border-t border-iron-50 w-full bottom-0 mt-4 z-[50]" > -
+
{routerItems?.map((route, index) => { const pathParts = route.href.split("/").filter(Boolean); const isHome = pathname === "/" && route.href === "/"; diff --git a/src/components/AppHeader.tsx b/src/components/AppHeader.tsx index f5ff6135..b011db3f 100644 --- a/src/components/AppHeader.tsx +++ b/src/components/AppHeader.tsx @@ -12,7 +12,6 @@ import { supabase } from "@/lib/client/realtime"; import { Button } from "./Button"; import { LINKS } from "@/hooks/useSettings"; import { cn } from "@/lib/client/utils"; -import { IndexDBWrapper } from "@/lib/client/indexDB"; const Title = classed.h3("block font-sans text-iron-950", { variants: { @@ -33,6 +32,7 @@ interface AppHeaderContentProps { isMenuOpen: boolean; setIsMenuOpen: (value: boolean) => void; handleSignout: () => void; + isPreview?: boolean; } interface AppBackHeaderProps { @@ -83,10 +83,11 @@ export const AppBackHeader = ({ ); }; -const AppHeaderContent = ({ +export const AppHeaderContent = ({ isMenuOpen, setIsMenuOpen, handleSignout, + isPreview, }: AppHeaderContentProps) => { const { actions, getState } = useStateMachine({ updateStateFromAction }); const [activeMenuIndex, setActiveMenuIndex] = useState(null); @@ -98,23 +99,17 @@ const AppHeaderContent = ({ const MenuItems: { label: string; children: ReactNode }[] = [ { - label: "Profile & settings", - children: , - }, - { - label: "About", + label: "About this app", children: ( <> - About the app + About Backpocket - This app allows you to verifiably digitize your Signature - Signularity residency experience and make provable claims about - the people you have met. Every single tap gives you a digital - signature representing the fact that you met someone. You can make - zk proofs about these signatures, like proving that you met 3 - residents without revealing who they were or the signatures - themselves. + This app allows you to verifiably digitize in-person experiences + and make ZK provable claims about the people you have met. You can + also do a variety of multi-party computation queries, enabling you + to learn information about your connections in a safe and + efficient way. Crucially, all the data you collect in this app is yours - our @@ -130,10 +125,8 @@ const AppHeaderContent = ({ > (cursive.team) {" "} - is a team building applications of signed data. We want to build - experiences where people own their data and use it in powerful - ways. If this is something you are interested in, please reach - out! + is a team building cryptography for human connection. If this is + something you are interested in, please reach out! @@ -165,6 +158,38 @@ const AppHeaderContent = ({ }, ]; + if (isPreview === undefined) { + MenuItems.unshift({ + label: "Profile & settings", + children: , + }); + } + + if (isPreview) { + MenuItems.push({ + label: "Register", + children: ( + + Register + + Once you register, you will need to retap the NFC ring to save these + socials. + + + + + + ), + }); + } + const onBack = () => { if ( profileViewState === ProfileDisplayState.CHOOSE_PASSWORD || @@ -249,12 +274,9 @@ interface AppHeaderProps { const AppHeader = ({ isMenuOpen, setIsMenuOpen }: AppHeaderProps) => { const { actions, getState } = useStateMachine({ updateStateFromAction }); const handleSignout = async () => { - const db = new IndexDBWrapper(); - await db.init(); - await db.logoutIndexDB(); deleteAccountFromLocalStorage(); supabase.auth.signOut(); - window.location.href = "/"; + window.location.href = "/register"; }; const toggleMenu = () => { @@ -277,7 +299,7 @@ const AppHeader = ({ isMenuOpen, setIsMenuOpen }: AppHeaderProps) => { {!isMenuOpen && ( )} diff --git a/src/components/Icons.tsx b/src/components/Icons.tsx index f89a7a68..23ee57c5 100644 --- a/src/components/Icons.tsx +++ b/src/components/Icons.tsx @@ -1,300 +1,362 @@ +interface IconsProps { + size?: number; + width?: number; + height?: number; +} + export const Icons: Record = { ControllerPause: (props: any) => ( + + ), + Logo: ({ size, width = 189, height = 22, ...props }: IconsProps) => ( + + + + + + + + + + + + ), ControllerClose: (props: any) => ( ), ZKFolded: (props: any) => ( ), Cursive: (props: any) => ( ), Close: (...props: any) => ( - + - - + + ), Burgher: (...props: any) => ( ), Home: (props: any) => ( ), Proof: (props: any) => ( ), Social: (props: any) => ( ), Zoom: (props: any) => ( - + x="0.546631" + y="0.306641" + width="23.1467" + height="23.1467" + rx="3.30667" + fill="#1B1B1B" + fillOpacity="0.1" + /> + - + @@ -302,326 +364,326 @@ export const Icons: Record = { ), ExternalLink: (props: any) => ( ), ArrowRight: (props: any) => ( ), ArrowLeft: (...props: any) => ( ), Pin: (...props: any) => ( ), Unpin: (props: any) => ( ), Loading: (props: any) => ( ), CheckCircle: (props: any) => ( ), Person: (props: any) => ( ), Location: (props: any) => ( ), Twitter: (props: any) => ( ), ArrowUp: (props: any) => ( ), quest: (props: any) => ( ), checkedCircle: (props: any) => ( ), candy: (...props: any) => ( ), twitter: (props: any) => ( ), location: (props: any) => ( ), home: (props: any) => ( ), store: (props: any) => ( - - - - - - + + + + + + ), diff --git a/src/components/Profile.tsx b/src/components/Profile.tsx index 80d51060..0a882b67 100644 --- a/src/components/Profile.tsx +++ b/src/components/Profile.tsx @@ -55,7 +55,7 @@ const Profile = ({ handleSignout }: ProfileProps) => { if (!authToken || authToken.expiresAt < new Date()) { handleSignout(); toast.error("You must be logged in to update your profile"); - router.push("/login"); + router.push("/register"); return; } @@ -130,7 +130,7 @@ const Profile = ({ handleSignout }: ProfileProps) => { if (!authToken || authToken.expiresAt < new Date()) { handleSignout(); toast.error("You must be logged in to update your profile"); - router.push("/login"); + router.push("/register"); return; } diff --git a/src/components/cards/FoldedCard.tsx b/src/components/cards/FoldedCard.tsx deleted file mode 100644 index fb984645..00000000 --- a/src/components/cards/FoldedCard.tsx +++ /dev/null @@ -1,628 +0,0 @@ -import dayjs from "dayjs"; -import duration from "dayjs/plugin/duration"; -import { Swiper, SwiperSlide } from "swiper/react"; -import { Controller, EffectFade, Pagination, Autoplay } from "swiper/modules"; -import "swiper/css"; -import "swiper/css/effect-fade"; -import "swiper/css/pagination"; -import { classed } from "@tw-classed/react"; -import { Card } from "./Card"; -import { ReactNode, useCallback, useEffect, useMemo, useState } from "react"; -import { cn } from "@/lib/client/utils"; -import { Icons } from "../Icons"; -import { - getAuthToken, - getFoldedProof, - getKeys, - getLocationSignatures, - getProfile, - getUsers, -} from "@/lib/client/localStorage"; -import { Button } from "../Button"; -import Link from "next/link"; -import { logClientEvent } from "@/lib/client/metrics"; -import { toast } from "sonner"; -import { type PutBlobResult } from "@vercel/blob"; -import { upload } from "@vercel/blob/client"; -import { encryptFoldedProofMessage } from "@/lib/client/jubSignal"; -import { loadMessages } from "@/lib/client/jubSignalClient"; -import { useWorker } from "@/hooks/useWorker"; -import { IndexDBWrapper, TreeType } from "@/lib/client/indexDB"; -import { Spinner } from "../Spinner"; - -dayjs.extend(duration); -const UNFOLDED_DATE = "2024-04-10 15:59:59"; -const CountdownLabel = classed.span("text-primary font-semibold text-xs"); - -interface FoldedItemProps { - image?: string; - children?: ReactNode; - title?: ReactNode; - subtitle?: ReactNode; - description?: (param: number) => ReactNode; -} - -interface FolderCardProps { - items: FoldedItemProps[]; - onClose?: () => void; -} - -export type ProofData = { - uri: string; - numFolded: number; -}; - -export type ProofPost = { - attendees: ProofData | undefined; - speakers: ProofData | undefined; - talks: ProofData | undefined; -}; - -export const FOLDED_MOCKS: FolderCardProps["items"] = [ - { - image: "/bg-gradient-card.png", - children: ( - <> - - - ), - }, - { - subtitle: - "We're so happy you joined us at the Signature Singularity Residency!", - description: () => "Ready to review your memories?", - }, - { - title: "SigSing - a symposium for brilliant minds.", - description: (param: number) => `You connected with ${param} residents`, - }, - { - title: "Dialogue catalyzed the evolution of zk research.", - description: (param: number) => `You met ${param} speakers`, - }, - { - title: "Knowledge blossomed through interaction.", - description: () => `You are 1 of 1!`, - }, -]; - -const FoldedCardSteps = ({ items = [], onClose }: FolderCardProps) => { - // const { foldingCompleted, progress, updateProgress } = useProgress(); - const { work, worker, finalize } = useWorker(); - const [activeIndex, setActiveIndex] = useState(0); - const [numAttendees, setNumAttendees] = useState(0); - const [numAttendeesFolded, setNumAttendeesFolded] = useState(0); - const [numParamsDownloaded, setNumParamsDownloaded] = useState(0); - const [numTalks, setNumTalks] = useState(0); - const [numTalksFolded, setNumTalksFolded] = useState(0); - const [numSpeakers, setNumSpeakers] = useState(0); - const [numSpeakersFolded, setNumSpeakersFolded] = useState(0); - const [provingStarted, setProvingStarted] = useState(false); - - const [numTotalProvingRequirements, setNumTotalProvingRequirements] = - useState(0); - const [proofId, setProofId] = useState(); - - useEffect(() => { - (async () => { - const db = new IndexDBWrapper(); - await db.init(); - const attendeeFold = await db.getFold(TreeType.Attendee); - setNumAttendeesFolded(attendeeFold ? attendeeFold.numFolds : 0); - const speakerFold = await db.getFold(TreeType.Speaker); - setNumSpeakersFolded(speakerFold ? speakerFold.numFolds : 0); - const talkFold = await db.getFold(TreeType.Talk); - setNumTalksFolded(talkFold ? talkFold.numFolds : 0); - })(); - const users = getUsers(); - const talks = getLocationSignatures(); - // const foldedProof = getFoldedProof(); - - const userSignatures = Object.values(users).filter((user) => user.sig); - setNumAttendees( - userSignatures.filter((user) => !user.isSpeaker && user.pkId !== "0") - .length - ); - setNumSpeakers(userSignatures.filter((user) => user.isSpeaker).length); - setNumTalks(Object.keys(talks).length); - }, []); - - const pagination = { - clickable: true, - bulletActiveClass: "folded-dot-active", - renderBullet: (index: number, className: string) => { - return `
`; - }, - }; - - const getLinkToProof = () => { - if (!proofId) return ""; - - return `/folded/${proofId}`; - }; - - const getTwitterShareUrl = () => { - if (!proofId) return ""; - - return `https://twitter.com/intent/tweet?text=${encodeURIComponent( - `🧺 I made a Nova folding proof attesting to the people I met at the Signature Singularity Residency, built by @cursive_team! 🧺` - )}&url=${encodeURIComponent( - `https://ring.cursive.team/folded/${proofId}` - )}`; - }; - - /** - * Upload a proof blob and return the url to the blob - * - * @param proof - the compressed obfuscated proof - * @param treeType - the type of tree the proof is for - * @returns the url to the uploaded proof - */ - const uploadProof = async ( - proof: Blob, - treeType: TreeType - ): Promise => { - const name = `${treeType}Proof`; - const newBlob: PutBlobResult = await upload(name, proof, { - access: "public", - handleUploadUrl: "/api/folding/upload", - }); - return newBlob.url; - }; - - const saveFinalizedProofs = async (data: ProofPost): Promise => { - const token = getAuthToken(); - const keys = getKeys(); - const profile = getProfile(); - if (!token || token.expiresAt < new Date() || !keys || !profile) { - throw new Error("Please sign in to save your proof."); - } - - const response = await fetch("/api/folding/proof", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ authToken: token.value, data }), - }); - - if (!response.ok) { - throw new Error("Failed to save proof"); - } - - const { proofUuid } = await response.json(); - - const senderPrivateKey = keys.encryptionPrivateKey; - const recipientPublicKey = profile.encryptionPublicKey; - const encryptedMessage = await encryptFoldedProofMessage({ - proofId: proofUuid, - proofLink: proofUuid, - senderPrivateKey, - recipientPublicKey, - }); - - // Send folded proof info as encrypted jubSignal message to self - // Simultaneously refresh activity feed - try { - await loadMessages({ - forceRefresh: false, - messageRequests: [ - { - encryptedMessage, - recipientPublicKey, - }, - ], - }); - } catch (error) { - console.error( - "Error sending encrypted folded proof info to server: ", - error - ); - toast.error("An error occured while saving the proof. Please try again."); - } - - return proofUuid; - }; - - // Regenerate indicates if proof should be regenerated from scratch - const beginProving = async (regenerate: boolean) => { - if (provingStarted) return true; - setProofId(undefined); - logClientEvent("foldedProvingStarted", {}); - - if (numAttendees === 0 && numTalks === 0 && numSpeakers === 0) { - toast.error("Nothing to prove! Tap some cards to get started."); - return; - } - setProvingStarted(true); - - const db = new IndexDBWrapper(); - await db.init(); - - if (regenerate) await db.logoutIndexDB(); - - // ensure all proofs are folded - await work( - Object.values(getUsers()), - Object.values(getLocationSignatures()) - ); - - let proofUris: Map = new Map(); - const finalizeProof = async (treeType: TreeType) => { - // check that the proof is not already finalized - const storedProofData = await db.getFold(treeType); - if (storedProofData && storedProofData.obfuscated) { - console.log(`Not obfuscating ${treeType} proof: already obfuscated`); - return; - } - // obfuscate the proof - let success = await finalize(treeType); - if (!success) { - console.log(`No membership proof of type ${treeType} was ever made`); - return; - } - console.log("Finalized proof for treeType: ", treeType); - // get the proof from the db - const proofData = await db.getFold(treeType); - if (proofData === undefined) { - console.log(`No proof data found for ${treeType} tree`); - } else { - // post the proof to blob store - let proofBlobUri = await uploadProof(proofData!.proof, treeType); - console.log(`Posted ${treeType} proof to ${proofBlobUri}`); - // track the proof and numFolded for each tree - proofUris.set(treeType, { - uri: proofBlobUri, - numFolded: proofData!.numFolds, - }); - } - }; - await Promise.all([ - finalizeProof(TreeType.Attendee), - finalizeProof(TreeType.Speaker), - finalizeProof(TreeType.Talk), - ]); - - // post the results to the server - const proofPost = { - attendees: proofUris.get(TreeType.Attendee), - speakers: proofUris.get(TreeType.Speaker), - talks: proofUris.get(TreeType.Talk), - }; - - const proofUuid = await saveFinalizedProofs(proofPost); - setProofId(proofUuid); - setProvingStarted(false); - }; - - const progress = useMemo(() => { - let progressPercent = 0; - let progressText = ""; - console.log("numAttendees", numAttendees); - console.log("numFoldedAttendees", numAttendeesFolded); - if (numParamsDownloaded < 10) { - progressPercent = numParamsDownloaded / 10; - progressText = `Downloaded ${numParamsDownloaded} out of 10 chunked params`; - } else if (numAttendeesFolded !== numAttendees) { - progressPercent = numAttendeesFolded / numAttendees; - progressText = `Folded ${numAttendeesFolded} of ${numAttendees} attendees`; - } else if (numSpeakersFolded !== numSpeakers) { - progressPercent = numSpeakersFolded / numSpeakers; - progressText = `Folded ${numSpeakersFolded} of ${numSpeakers} speakers`; - } else if (numTalksFolded !== numTalks) { - progressPercent = numTalksFolded / numTalks; - progressText = `Folded ${numTalksFolded} of ${numTalks} talks`; - } else if (numTalksFolded === numTalks) { - progressPercent = 1; - progressText = `Finalizing proof`; - } - - return ( -
-
{progressText}
-
- -
-
- ); - }, [ - numAttendees, - numAttendeesFolded, - numParamsDownloaded, - numSpeakers, - numSpeakersFolded, - numTalks, - numTalksFolded, - ]); - - useEffect(() => { - // If num params is 0 then fetch previously downloaded - if (!numParamsDownloaded) { - (async () => { - const db = new IndexDBWrapper(); - await db.init(); - const downloaded = await db.countChunks(); - setNumParamsDownloaded(downloaded); - })(); - } - if (!worker) return; - worker.onmessage = async (event) => { - const { updateType } = event.data; - if (updateType === "paramDownloaded") { - setNumParamsDownloaded((prev) => prev + 1); - } else if (updateType === "attendeeFolded") { - setNumAttendeesFolded((prev) => prev + 1); - } else if (updateType === "speakerFolded") { - setNumSpeakersFolded((prev) => prev + 1); - } else if (updateType === "talkFolded") { - setNumTalksFolded((prev) => prev + 1); - } - }; - }, [worker]); - - return ( -
- -
- -
- { - const isLastSlide = swiper.activeIndex === items.length - 1; - if (isLastSlide) { - swiper.autoplay.stop(); - } - setActiveIndex(swiper?.activeIndex ?? 0); - }} - > - {items?.map( - ({ title, subtitle, description, children, image }, itemIndex) => { - return ( - -
- {itemIndex !== items.length - 1 && ( - <> - {children} - {title && ( -

- {title} -

- )} - {subtitle && ( - - {subtitle} - - )} - {description && ( - - {itemIndex === 2 && description(numAttendees)} - {itemIndex === 3 && description(numTalks)} - {itemIndex === 4 && description(numSpeakers)} - {![2, 3, 4].includes(itemIndex) && description(0)} - - )} - - )} - {itemIndex === items.length - 1 && ( - <> - {proofId && ( - <> -

- {"Proof is ready"} -

- - { - "Allow anyone to verify your residency experience." - } - - - - - - - - - - - - )} - {!proofId && provingStarted && ( - <> -

- {"Generating your proof..."} -

- - {"This may take a minute. Please be patient!"} - - - {progress} - - )} - {!proofId && !provingStarted && ( - <> - {children} - {title && ( -

- {title} -

- )} - {subtitle && ( - - {subtitle} - - )} - {description && ( - - {itemIndex === 2 && description(numAttendees)} - {itemIndex === 3 && description(numTalks)} - {itemIndex === 4 && description(numSpeakers)} - {![2, 3, 4].includes(itemIndex) && description(0)} - - )} - - - )} - {} - - )} -
-
- ); - } - )} -
-
- ); -}; - -export const FolderCard = ({ items }: FolderCardProps) => { - const [isOpened, setIsOpened] = useState(false); - const [hasCountdown, setHasCountdown] = useState(false); - const [countdown, setCountdown] = useState({ - days: 0, - hours: 0, - minutes: 0, - seconds: 0, - }); - - useEffect(() => { - const isDatePassed = dayjs().isAfter(dayjs(UNFOLDED_DATE)); - setHasCountdown(!isDatePassed); - - if (isDatePassed) return; - const interval = setInterval(() => { - const targetDate = dayjs(UNFOLDED_DATE); - const currentDate = dayjs(); - const duration = dayjs.duration(targetDate.diff(currentDate)); - const days = duration.days(); - const hours = duration.hours(); - const minutes = duration.minutes(); - const seconds = duration.seconds(); - - setCountdown({ days, hours, minutes, seconds }); - }, 1000); - - return () => clearInterval(interval); - }, []); - - const { days, hours, minutes, seconds } = countdown; - - return ( -
-
- { - setIsOpened(false); - }} - /> -
- setIsOpened(!isOpened)} - className={cn({ - "pointer-events-none": hasCountdown, - "py-4": !hasCountdown, - })} - style={{ - backgroundImage: "url('/bg-glitter.png')", - }} - > -
- {hasCountdown && ( - - Available in:{" "} - {days === 1 - ? `${days} day, ` - : days === 0 - ? "" - : `${days} days, `} - {hours.toString().padStart(2, "0")}: - {minutes.toString().padStart(2, "0")}: - {seconds.toString().padStart(2, "0")} - - )} -

- SigSing Folded -

- - Using client-side Nova folding proofs, create and share a Spotify - Wrapped-like summary of who you met at the Signature Singularity - Residency! - -
-
-
- ); -}; diff --git a/src/components/modals/CompleteQuestModal.tsx b/src/components/modals/CompleteQuestModal.tsx index 85a50c92..e17a9956 100644 --- a/src/components/modals/CompleteQuestModal.tsx +++ b/src/components/modals/CompleteQuestModal.tsx @@ -73,7 +73,7 @@ const CompleteQuestModal = ({ if (!authToken || authToken.expiresAt < new Date() || !profile || !keys) { toast.error("You must be logged in to generate a proof"); - router.push("/login"); + router.push("/register"); return; } @@ -204,7 +204,7 @@ const CompleteQuestModal = ({ }; const twitterShareUrl = `https://twitter.com/intent/tweet?text=${encodeURIComponent( - `I am a verified ${quest.name} at zkSummit11. Here's my ZK proof:` + `I am a verified ${quest.name}. Here's my ZK proof from @cursive_team:` )}&url=${encodeURIComponent(qrCodeUrl)}`; const getModalContent = (): JSX.Element => { @@ -264,14 +264,14 @@ const CompleteQuestModal = ({ - {/* + - */} +
); diff --git a/src/components/modals/Modal.tsx b/src/components/modals/Modal.tsx index 56dc687f..224aeef7 100644 --- a/src/components/modals/Modal.tsx +++ b/src/components/modals/Modal.tsx @@ -2,7 +2,7 @@ import { Transition, Dialog } from "@headlessui/react"; import React, { Fragment } from "react"; import { Icons } from "../Icons"; import { cn } from "@/lib/client/utils"; -import { DM_Sans } from "@next/font/google"; +import { DM_Sans } from "next/font/google"; const dmSans = DM_Sans({ subsets: ["latin"], diff --git a/src/components/modals/SliderModal.tsx b/src/components/modals/SliderModal.tsx index f1c7d5c6..8665c36c 100644 --- a/src/components/modals/SliderModal.tsx +++ b/src/components/modals/SliderModal.tsx @@ -51,7 +51,7 @@ const SliderModal = ({ isOpen, setIsOpen, size = 320 }: SliderModalProps) => {
- Your SigSing Flower Garden + Your Backpocket Flower Garden
{ queryFn: async (): Promise => { const authToken = getAuthToken(); if (!authToken || authToken.expiresAt < new Date()) { - toast.error("You must be logged in to connect"); - router.push("/login"); + toast.error("You must be registered to view proofs"); + router.push("/register"); return []; } diff --git a/src/hooks/useSettings.ts b/src/hooks/useSettings.ts index 063260f6..74e9e85e 100644 --- a/src/hooks/useSettings.ts +++ b/src/hooks/useSettings.ts @@ -5,7 +5,7 @@ import { detectIncognito } from "detectincognitojs"; export const MAX_LEADERBOARD_LENGTH = 100; export const LINKS = { - GITHUB: "https://github.com/cursive-team/sig-sing-workshop", + GITHUB: "https://github.com/cursive-team/ring.backpocket.me", CURSIVE_SITE: "https://cursive.team", }; diff --git a/src/hooks/useWorker.ts b/src/hooks/useWorker.ts deleted file mode 100644 index 7208648e..00000000 --- a/src/hooks/useWorker.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { TreeType } from "@/lib/client/indexDB"; -import { LocationSignature, User } from "@/lib/client/localStorage"; -import { Remote, wrap } from "comlink"; -import { useRef, useState } from "react"; - -export const useWorker = () => { - const [folding, setFolding] = useState(false); - const [obfuscating, setObfuscating] = useState(false); - const [completed, setCompleted] = useState(false); - const [downloadingChunks, setDownloadingChunks] = useState(false); - const [chunksDownloaded, setChunksDownloaded] = useState(false); - const [worker, setWorker] = useState(null); - - const workerAPIRef = useRef Promise; - finalize: (treeType: TreeType) => Promise; - verify: (proof: Blob, numFolded: number, treeType: TreeType) => Promise; - }> | null>(); - - const init = () => { - const worker = new Worker( - new URL("../lib/client/worker.ts", import.meta.url) - ); - const workerAPI = - wrap(worker); - workerAPIRef.current = workerAPI; - setWorker(worker); - }; - - const work = async (users: User[], talks: LocationSignature[]) => { - init(); - setFolding(true); - await workerAPIRef.current?.work(users, talks); - setFolding(false); - setCompleted(true); - terminate(); - }; - - const finalize = async (treeType: TreeType): Promise => { - init(); - setObfuscating(true); - const success = await workerAPIRef.current?.finalize(treeType); - setObfuscating(false); - terminate(); - return success!; - }; - - const verify = async (proof: Blob, numFolded: number, treeType: TreeType): Promise => { - init(); - const success = await workerAPIRef.current?.verify(proof, numFolded, treeType); - terminate(); - return success!; - }; - - const terminate = () => { - if (!worker) return; - worker.terminate(); - workerAPIRef.current = null; - }; - - return { - work, - finalize, - verify, - worker, - obfuscating, - folding, - completed, - downloadingChunks, - chunksDownloaded, - }; -}; diff --git a/src/lib/client/indexDB.ts b/src/lib/client/indexDB.ts deleted file mode 100644 index 17c2eddd..00000000 --- a/src/lib/client/indexDB.ts +++ /dev/null @@ -1,299 +0,0 @@ -import { IDBPDatabase, openDB } from "idb"; -import { User } from "@/lib/client/localStorage"; -import { INDEXDB_STORES } from "@/shared/constants"; - -export type FoldProof = { - proof: Blob; // the actual proof, compressed - numFolds: number; // the number of folds in the proof - locked: boolean; // whether or not the proof is locked - obfuscated: boolean; // whether or not the proof has been obfuscated - included: string[]; // the public key of the user who has been folded in -}; - -// timeout period for a worker lock -export const LOCK_STALE_TIME = 1000 * 5; - -export enum TreeType { - Attendee = "attendee", - Speaker = "speaker", - Talk = "talk", -} - -/** - * Wrapper class for index db - */ -export class IndexDBWrapper { - db: IDBPDatabase | null = null; - - constructor() { } - - /** - * Initialize db and store - */ - async init() { - const res = await openDB(process.env.NEXT_PUBLIC_NOVA_INDEXDB_NAME!, 1, { - upgrade(db) { - db.createObjectStore(INDEXDB_STORES.PARAMS); - db.createObjectStore(INDEXDB_STORES.FOLDS); - db.createObjectStore(INDEXDB_STORES.LOCKS); - }, - }); - this.db = res; - } - - /** - * Checks whether db has been initialized - * - * @returns {boolean} - Whether db is null - */ - initialized(): boolean { - return !!this.db; - } - - /// PARAMS FUNCTIONS /// - - /** - * Adds a params chunk to the store - * - * @param key - the index of the chunk - * @param chunk - chunk of gzipped public_params.json - */ - async addChunk(key: number, chunk: Blob) { - if (this.db) { - const tx = this.db.transaction(INDEXDB_STORES.PARAMS, "readwrite"); - const store = tx.objectStore(INDEXDB_STORES.PARAMS); - await store.add(chunk, key); - } else { - throw Error("DB not initialized"); - } - } - - /** - * Returns the number of params chunks in the store - * - * @returns the number of chunks - */ - async countChunks(): Promise { - if (this.db) { - const tx = this.db.transaction(INDEXDB_STORES.PARAMS, "readonly"); - const store = tx.objectStore(INDEXDB_STORES.PARAMS); - return await store.count(); - } else { - throw Error("DB not initialized"); - } - } - - /** - * Returns all the param chunks in the store - * - * @returns all of the chunks downloaded so far - */ - async getChunks(): Promise> { - if (this.db) { - const tx = this.db.transaction(INDEXDB_STORES.PARAMS, "readonly"); - const store = tx.objectStore(INDEXDB_STORES.PARAMS); - const data = await store.getAll(); - return data; - } else { - throw Error("DB not initialized"); - } - } - - /// FOLDS FUNCTIONS /// - - /** - * Add a new proof to the store - * @param key - the membership type - * @param proof - the proof to add - * @param pubkey - the public key of the user who has been folded in - */ - async addFold(key: TreeType, proof: Blob, pubkey: string) { - if (this.db) { - const tx = this.db.transaction(INDEXDB_STORES.FOLDS, "readwrite"); - const store = tx.objectStore(INDEXDB_STORES.FOLDS); - const res = await store.get(key); - if (res !== undefined) { - throw new Error(`AddProof: Proof for ${key} already exists`); - } - const data: FoldProof = { - proof, - numFolds: 1, - locked: false, - obfuscated: false, - included: [pubkey], - }; - await store.add(data, key); - } else { - throw Error("DB not initialized"); - } - } - - /** - * Given a proof type, update it with new proof and increment number of folds - * @param key - the key of the proof type to increment - * @param newProof - the new proof to update - */ - async incrementFold(key: TreeType, newProof: Blob, pubkey: string) { - if (this.db) { - const tx = this.db.transaction(INDEXDB_STORES.FOLDS, "readwrite"); - const store = tx.objectStore(INDEXDB_STORES.FOLDS); - const data = await store.get(key); - if (data === undefined) { - throw new Error(`IncrementFold: Proof for ${key} does not exist`); - } - data.numFolds += 1; - data.proof = newProof; - data.included.push(pubkey); - await store.put(data, key); - } else { - throw Error("DB not initialized"); - } - } - - /** - * Update a proof and mark it as obfuscated - * @param key - the key of the proof type to obfuscate - * @param newProof - the new proof to update - */ - async obfuscateFold(key: TreeType, newProof: Blob) { - if (this.db) { - const tx = this.db.transaction(INDEXDB_STORES.FOLDS, "readwrite"); - const store = tx.objectStore(INDEXDB_STORES.FOLDS); - const data = await store.get(key); - if (data === undefined) { - throw new Error(`ObfuscateFold: Proof for ${key} does not exist`); - } - data.obfuscated = true; - data.proof = newProof; - await store.put(data, key); - } else { - throw Error("DB not initialized"); - } - } - - /** - * Get a folding proof from the store - * @param key - the type of proof to retrieve - * @returns - the proof if found, null otherwise - */ - async getFold(key: TreeType): Promise { - if (this.db) { - const tx = this.db.transaction(INDEXDB_STORES.FOLDS, "readwrite"); - const store = tx.objectStore(INDEXDB_STORES.FOLDS); - return await store.get(key); - } else { - throw Error("DB not initialized"); - } - } - - /** - * Filters out all members of a given tree type that have not yet been folded in - * @notice expects other checks on users to have been performed already - * @param key - the type of proof to fold - * @param memberPk - the member to filter - * @ - * @returns - members that can be folded into the membership proof for this type - */ - async getUnincluded(key: TreeType, memberPk: string[]): Promise { - if (this.db) { - // get pubkeys already folded in - const tx = this.db.transaction(INDEXDB_STORES.FOLDS, "readwrite"); - const store = tx.objectStore(INDEXDB_STORES.FOLDS); - const data: FoldProof = await store.get(key); - const foldedPks = data === undefined ? [] : data.included; - - // filter out users that are not available to be folded in - return memberPk.filter((member) => { - return (!foldedPks.includes(member)); - }); - } else { - throw Error("DB not initialized"); - } - } - - /** - * Attempt to set the db lock (from a worker) - * If same worker previously set the lock, pass prevLock to update the lock - * Otherwise supply no arguments to set a new lock - * - * @param prevLock - the optional previous timestamp of a lock to update - * @returns - the current timestamp set for the lock, or undefined if locked by another worker - */ - async setLock(prevLock?: number): Promise { - if (this.db) { - const tx = this.db.transaction(INDEXDB_STORES.LOCKS, "readwrite"); - const store = tx.objectStore(INDEXDB_STORES.LOCKS); - // look for existing locks - const existingLocks = await store.getAll(); - if (existingLocks.length !== 0) { - // if lock set by different worker - if (!prevLock || existingLocks[0] !== prevLock) { - // see if the lock is stale - if (Date.now() - existingLocks[0] < LOCK_STALE_TIME) { - // if lock has not timed out, return undefined - return undefined; - } - // delete the stale lock - await store.delete(1); - } - // otherwise, delete the worker's previous lock - await store.delete(1); - } - // add a lock at current time - let timestamp = Date.now(); - await store.add(timestamp, 1); - return timestamp; - } else { - throw Error("DB not initialized"); - } - } - - /** - * Checks that a given lock is valid - * - * @param number - the lock timestamp to check - * @returns - true if the lock is still held, and false otherwise - */ - async checkLock(lock: number): Promise { - if (this.db) { - const tx = this.db.transaction(INDEXDB_STORES.LOCKS, "readwrite"); - const store = tx.objectStore(INDEXDB_STORES.LOCKS); - const existingLocks = await store.getAll(); - return existingLocks.length !== 0 && existingLocks[0] === lock; - } else { - throw Error("DB not initialized"); - } - } - - /** - * Release the lock on the db by knowing the timestamp of the lock - * - * @param timestamp - the timestamp of the lock to release - * @return - true if lock was released, false if lock was not found - */ - async releaseLock(timestamp: number): Promise { - if (this.db) { - const tx = this.db.transaction(INDEXDB_STORES.LOCKS, "readwrite"); - const store = tx.objectStore(INDEXDB_STORES.LOCKS); - // look for given lock - const lock = await store.get(1); - if (lock === undefined || lock != timestamp) { - console.log("failed to release lock"); - return false; - } - await store.delete(1); - return true; - } else { - throw Error("DB not initialized"); - } - - } - - async logoutIndexDB() { - if (this.db) { - const tx = this.db.transaction(INDEXDB_STORES.FOLDS, "readwrite"); - const foldsStore = tx.objectStore(INDEXDB_STORES.FOLDS); - await foldsStore.clear(); - } - } -} diff --git a/src/lib/client/jubSignal/foldedProof.ts b/src/lib/client/jubSignal/foldedProof.ts deleted file mode 100644 index d2d959aa..00000000 --- a/src/lib/client/jubSignal/foldedProof.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { object, string } from "yup"; -import { JUB_SIGNAL_MESSAGE_TYPE, encryptMessage } from "."; - -export type FoldedProofMessage = { - pfId: string; // Id of folding proof - pfLink: string; // Link to folding proof -}; - -export const foldedProofMessageSchema = object({ - pfId: string().required(), - pfLink: string().required(), -}); - -export type EncryptFoldedProofMessageArgs = { - proofId: string; - proofLink: string; - senderPrivateKey: string; - recipientPublicKey: string; -}; - -export async function encryptFoldedProofMessage({ - proofId, - proofLink, - senderPrivateKey, - recipientPublicKey, -}: EncryptFoldedProofMessageArgs): Promise { - const messageData: FoldedProofMessage = { - pfId: proofId, - pfLink: proofLink, - }; - - const encryptedMessage = await encryptMessage( - JUB_SIGNAL_MESSAGE_TYPE.FOLDED_PROOF, - messageData, - senderPrivateKey, - recipientPublicKey - ); - - return encryptedMessage; -} diff --git a/src/lib/client/jubSignal/index.ts b/src/lib/client/jubSignal/index.ts index df047886..3bff2941 100644 --- a/src/lib/client/jubSignal/index.ts +++ b/src/lib/client/jubSignal/index.ts @@ -6,7 +6,6 @@ export * from "./inboundTap"; export * from "./locationTap"; export * from "./questCompleted"; export * from "./itemRedeemed"; -export * from "./foldedProof"; export enum JUB_SIGNAL_MESSAGE_TYPE { REGISTERED = "R", // A message you send to yourself indicating you are registered @@ -16,7 +15,6 @@ export enum JUB_SIGNAL_MESSAGE_TYPE { QUEST_COMPLETED = "QC", // A message you send to yourself indicating you completed a quest ITEM_REDEEMED = "IR", // A message sent to you indicating you redeemed an item OVERLAP_COMPUTED = "OC", // A message you send to yourself indicating PSI completion - FOLDED_PROOF = "FP", // A message you send to yourself indicating you made a folded proof } export type MessageContents = { diff --git a/src/lib/client/jubSignalClient.ts b/src/lib/client/jubSignalClient.ts index f891e292..57c8489b 100644 --- a/src/lib/client/jubSignalClient.ts +++ b/src/lib/client/jubSignalClient.ts @@ -4,7 +4,6 @@ import { PlaintextMessage, decryptMessage, encryptedMessageSchema, - foldedProofMessageSchema, inboundTapMessageSchema, itemRedeemedMessageSchema, locationTapMessageSchema, @@ -13,7 +12,6 @@ import { } from "./jubSignal"; import { Activity, - FoldedProof, ItemRedeemed, LocationSignature, QuestCompleted, @@ -21,7 +19,6 @@ import { getActivities, getAllItemRedeemed, getAllQuestCompleted, - getFoldedProof, getKeys, getLocationSignatures, getProfile, @@ -30,7 +27,6 @@ import { saveActivities, saveAllItemRedeemed, saveAllQuestCompleted, - saveFoldedProof, saveLocationSignatures, saveSession, saveUsers, @@ -124,14 +120,12 @@ export const loadMessages = async ({ const existingQuestCompleted = forceRefresh ? {} : getAllQuestCompleted(); const existingItemRedeemed = forceRefresh ? {} : getAllItemRedeemed(); const existingActivities = forceRefresh ? [] : getActivities(); - const existingFoldedProof = forceRefresh ? undefined : getFoldedProof(); const { newUsers, newLocationSignatures, newQuestCompleted, newItemRedeemed, newActivities, - newFoldedProof, } = await processEncryptedMessages({ messages, recipientPrivateKey: keys.encryptionPrivateKey, @@ -141,7 +135,6 @@ export const loadMessages = async ({ existingQuestCompleted, existingItemRedeemed, existingActivities, - existingFoldedProof, }); // Save users, location signatures, activities to localStorage @@ -150,7 +143,6 @@ export const loadMessages = async ({ saveAllQuestCompleted(newQuestCompleted); saveAllItemRedeemed(newItemRedeemed); saveActivities(newActivities); - saveFoldedProof(newFoldedProof); // Update the session session.lastMessageFetchTimestamp = new Date(mostRecentMessageTimestamp); @@ -167,14 +159,12 @@ const processEncryptedMessages = async (args: { existingQuestCompleted: Record; existingItemRedeemed: Record; existingActivities: Activity[]; - existingFoldedProof: FoldedProof | undefined; }): Promise<{ newUsers: Record; newLocationSignatures: Record; newQuestCompleted: Record; newItemRedeemed: Record; newActivities: Activity[]; - newFoldedProof: FoldedProof | undefined; }> => { const { messages, @@ -185,11 +175,8 @@ const processEncryptedMessages = async (args: { existingQuestCompleted: questCompleted, existingItemRedeemed: itemRedeemed, existingActivities: activities, - existingFoldedProof, } = args; - let foldedProof = existingFoldedProof; - activities.reverse(); // We will reverse the activities array at the end - this is for faster array operations for (const message of messages) { @@ -493,39 +480,6 @@ const processEncryptedMessages = async (args: { } finally { break; } - case JUB_SIGNAL_MESSAGE_TYPE.FOLDED_PROOF: - try { - if (metadata.fromPublicKey !== recipientPublicKey) { - throw new Error( - "Invalid message: folded proof messages must be sent from self" - ); - } - - const { pfId, pfLink } = await foldedProofMessageSchema.validate( - data - ); - const newFoldedProof: FoldedProof = { - pfId, - pfLink, - ts: metadata.timestamp.toISOString(), - }; - foldedProof = newFoldedProof; - - const activity = { - type: JUB_SIGNAL_MESSAGE_TYPE.FOLDED_PROOF, - name: pfLink, - id: pfId, - ts: metadata.timestamp.toISOString(), - }; - activities.push(activity); - } catch (error) { - console.error( - "Invalid folded proof message received from server: ", - message - ); - } finally { - break; - } case JUB_SIGNAL_MESSAGE_TYPE.ITEM_REDEEMED: try { const { id, name, qrId } = await itemRedeemedMessageSchema.validate( @@ -576,6 +530,5 @@ const processEncryptedMessages = async (args: { newQuestCompleted: questCompleted, newItemRedeemed: itemRedeemed, newActivities: activities, - newFoldedProof: foldedProof, }; }; diff --git a/src/lib/client/localStorage/foldedProof.ts b/src/lib/client/localStorage/foldedProof.ts deleted file mode 100644 index a5fe90fc..00000000 --- a/src/lib/client/localStorage/foldedProof.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { - deleteFromLocalStorage, - getFromLocalStorage, - saveToLocalStorage, -} from "."; - -export const FOLDED_PROOF_STORAGE_KEY = "foldedProof"; - -export type FoldedProof = { - pfId: string; // Id for proof of folding - pfLink: string; // Link to proof of folding - ts: string; // Timestamp as ISO string -}; - -export const saveFoldedProof = (foldedProof: FoldedProof | undefined): void => { - if (foldedProof) { - saveToLocalStorage(FOLDED_PROOF_STORAGE_KEY, JSON.stringify(foldedProof)); - } -}; - -export const getFoldedProof = (): FoldedProof | undefined => { - const foldedProof = getFromLocalStorage(FOLDED_PROOF_STORAGE_KEY); - if (foldedProof) { - return JSON.parse(foldedProof); - } - - return undefined; -}; - -export const deleteAllFoldedProof = (): void => { - deleteFromLocalStorage(FOLDED_PROOF_STORAGE_KEY); -}; diff --git a/src/lib/client/localStorage/index.ts b/src/lib/client/localStorage/index.ts index 1de74e9c..a17d79a1 100644 --- a/src/lib/client/localStorage/index.ts +++ b/src/lib/client/localStorage/index.ts @@ -1,5 +1,4 @@ import { deleteAllActivities } from "./activities"; -import { deleteAllFoldedProof } from "./foldedProof"; import { deleteAllItemRedeemed } from "./itemRedeemed"; import { deleteAllKeys } from "./keys"; import { deleteAllLocationSignatures } from "./locationSignatures"; @@ -17,7 +16,6 @@ export * from "./locationSignatures"; export * from "./activities"; export * from "./questCompleted"; export * from "./itemRedeemed"; -export * from "./foldedProof"; export const saveToLocalStorage = (key: string, value: string): void => { localStorage.setItem(key, value); @@ -41,5 +39,4 @@ export const deleteAccountFromLocalStorage = (): void => { deleteAllActivities(); deleteAllQuestCompleted(); deleteAllItemRedeemed(); - deleteAllFoldedProof(); }; diff --git a/src/lib/client/nova.ts b/src/lib/client/nova.ts deleted file mode 100644 index 7d182236..00000000 --- a/src/lib/client/nova.ts +++ /dev/null @@ -1,344 +0,0 @@ -import { merkleProofFromObject } from "../shared/utils"; -import { LocationSignature, User } from "./localStorage"; -import { - derDecodeSignature, - getPublicInputsFromSignature, - publicKeyFromString, - hexToBigInt, - getECDSAMessageHash, - MerkleProof, - bigIntToHex, -} from "babyjubjub-ecdsa"; -import { TreeRoots } from "@/pages/api/tree/root"; -import { TreeType } from "./indexDB"; -import { fetchWithRetry } from "./utils"; - -export type NovaWasm = typeof import("bjj_ecdsa_nova_wasm"); - -/** Private inputs to the folded membership circuit */ -export type NovaPrivateInputs = { - s: string; - Tx: string; - Ty: string; - Ux: string; - Uy: string; - pathIndices: number[]; - siblings: string[]; -}; - -export class MembershipFolder { - // private - - public readonly r1cs_url = `${process.env.NEXT_PUBLIC_NOVA_BUCKET_URL}/bjj_ecdsa_batch_fold.r1cs`; - public readonly wasm_url = `${process.env.NEXT_PUBLIC_NOVA_BUCKET_URL}/bjj_ecdsa_batch_fold.wasm`; - - constructor( - /** The wasm binary for membership folding operations */ - public readonly wasm: NovaWasm, - /** The public params used to prove folds */ - public readonly params: string, - /** Get the roots for the tree types */ - public readonly roots: TreeRoots - ) { } - - /** - * Initializes a new instance of the membership folder class - */ - static async init(): Promise { - // get wasm - let wasm = await getWasm(); - // get tree roots - let roots: TreeRoots = await fetchWithRetry("/api/tree/root").then( - async (res) => await res.json() - ); - // get params - let params = await getAllParamsByChunk(); - return new MembershipFolder(wasm, params, roots); - } - - /** - * Initializes a new instance of the membership folder class - */ - static async initWithIndexDB( - compressedParams: Blob, - wasm: NovaWasm - ): Promise { - // get wasm - // let wasm = await getWasm(); - // get tree roots - let roots: TreeRoots = await fetchWithRetry("/api/tree/root").then( - async (res) => await res.json() - ); - - // decompress params - let ds = new DecompressionStream("gzip"); - let reader = compressedParams.stream().pipeThrough(ds).getReader(); - let done = false; - let params = ""; - while (!done) { - let decompressed = await reader.read(); - done = decompressed.done; - params += new TextDecoder().decode(decompressed.value); - } - return new MembershipFolder(wasm, params, roots); - } - - /** - * Prove the first fold in a membership tree - * - * @param pk - the public key of the member - * @param sig - the signature of the member - * @param msg - the message signed by the member - * @param treeType - the type of tree to fold into - * @returns - the proof of folding for the membership circuit - */ - async startFold( - pk: string, - sig: string, - msg: string, - treeType: TreeType - ): Promise { - // fetch merkle proof for the user - const merkleProof = await fetchWithRetry( - `/api/tree/proof?treeType=${treeType}&pubkey=${pk}` - ) - .then(async (res) => await res.json()) - .then(merkleProofFromObject); - - // generate the private inputs for the folded membership circuit - let inputs = await MembershipFolder.makePrivateInputs(sig, pk, msg, merkleProof); - - // prove the membership - return await this.wasm.generate_proof( - this.r1cs_url, - this.wasm_url, - this.params, - merkleProof.root.toString(), - JSON.stringify(inputs) - ); - } - - /** - * Fold subsequent membership proofs - * - * @param proof - the previous fold to increment from - * @param numFolds - the number of memberships verified in the fold - * @param sig - the signature by the member - * @param pk - the public key of the member - * @param msg - the message signed by the member - * @param treeType - the type of tree to fold into - * @returns The folding proof of membership - */ - async continueFold( - proof: string, - numFolds: number, - pk: string, - sig: string, - msg: string, - treeType: "attendee" | "speaker" | "talk" - ): Promise { - // fetch merkle proof for the user - const merkleProof = await fetchWithRetry( - `/api/tree/proof?treeType=${treeType}&pubkey=${pk}` - ) - .then(async (res) => await res.json()) - .then(merkleProofFromObject); - - // generate the private inputs for the folded membership circuit - let inputs = await MembershipFolder.makePrivateInputs( - sig, - pk, - msg, - merkleProof - ); - - // build the zi_primary (output of previous fold) - // this is predictable and getting it from verification doubles the work - let zi_primary = [merkleProof.root.toString(), BigInt(numFolds).toString()]; - - // prove the membership - return await this.wasm.continue_proof( - this.r1cs_url, - this.wasm_url, - this.params, - proof, - JSON.stringify(inputs), - zi_primary - ); - } - - /** - * Perform the chaff step with random witness for this instance to obfuscate folded total witness - * @param proof - the proof to obfuscate - * @param numFolds - the number of memberships verified in the fold - * @param root - the root of the tree to prove membership in - * @returns the obfuscated "final" proof - */ - async obfuscate(proof: string, numFolds: number, treeType: TreeType): Promise { - // build the zi_primary (output of previous fold) - let root; - if (treeType === TreeType.Attendee) - root = this.roots.attendeeMerkleRoot; - else if (treeType === TreeType.Speaker) - root = this.roots.speakerMerkleRoot; - else - root = this.roots.talksMerkleRoot; - let zi_primary = [ - hexToBigInt(root).toString(), - BigInt(numFolds).toString(), - ]; - - return await this.wasm.obfuscate_proof( - this.r1cs_url, - this.wasm_url, - this.params, - proof, - zi_primary - ); - } - - /** - * Verifies a folded membership proofs - * - * @param proof - the proof to verify - * @param numFolds - the number of memberships verified in the fold - * @param obfuscated - whether the proof is obfuscated - */ - async verify( - proof: string, - numFolds: number, - treeType: TreeType, - obfuscated: boolean = false - ): Promise { - // set num verified based on obfuscation - const iterations = obfuscated ? numFolds + 1 : numFolds; - // let iterations = 2; - let root; - if (treeType === TreeType.Attendee) - root = this.roots.attendeeMerkleRoot; - else if (treeType === TreeType.Speaker) - root = this.roots.speakerMerkleRoot; - else - root = this.roots.talksMerkleRoot; - try { - let res = await this.wasm.verify_proof( - this.params, - proof, - hexToBigInt(root).toString(), - Number(iterations) - ); - console.log( - `Verification output of ${obfuscated ? "chaffed " : "" - }proof of ${numFolds} memberships:`, - res - ); - return true; - } catch (e) { - console.error(`Failed to verify proof: ${e}`); - return false; - } - } - - /** - * Gzip deflates a proof - * @param proof - the proof to compress - * @returns the compressed proof - */ - async compressProof(proof: string): Promise { - return await this.wasm.compress_proof(proof); - } - - /** - * Gzip inflates a proof - * @param compressed - the compressed proof - * @returns the decompressed proof - */ - async decompressProof(compressed: Uint8Array): Promise { - return await this.wasm.decompress_proof(compressed); - } - - /** - * Builds private inputs for a folded membership proof - * - * @param sig - the signature by the member - * @param pk - the public key of the member - * @param msg - the message signed by the member - * @param merkleProof - the merkle inclusion proof for the member in the tree - * @returns The private inputs for the folded membership circuit - */ - static async makePrivateInputs( - sig: string, - pk: string, - msg: string, - merkleProof: MerkleProof - ): Promise { - // decode the user's signature - const decodedSig = derDecodeSignature(sig); - const messageHash = hexToBigInt(getECDSAMessageHash(msg)); - const pubkey = publicKeyFromString(pk); - const { T, U } = getPublicInputsFromSignature(decodedSig, messageHash, pubkey); - return { - s: decodedSig.s.toString(), - Tx: T.x.toString(), - Ty: T.y.toString(), - Ux: U.x.toString(), - Uy: U.y.toString(), - pathIndices: merkleProof.pathIndices, - siblings: merkleProof.siblings.map((sibling) => sibling.toString()), - }; - } -} - -export const getAllParamsByChunk = async (): Promise => { - // get chunked files - let requests = []; - let data: Map = new Map(); - for (let i = 0; i < 10; i++) { - let req = async () => { - let full_url = `${process.env.NEXT_PUBLIC_NOVA_BUCKET_URL}/params_${i}.gz`; - let res = await fetchWithRetry(full_url, { - headers: { "Content-Type": "application/x-binary" }, - }).then(async (res) => await res.blob()); - data.set(i, res); - }; - requests.push(req()); - } - - // await all requests - await Promise.all(requests); - - // build into one blob - let chunks = []; - for (let i = 0; i < 10; i++) { - chunks.push(data.get(i)!); - } - let compressed = new Blob(chunks); - - // decompress blob - let ds = new DecompressionStream("gzip"); - let reader = compressed.stream().pipeThrough(ds).getReader(); - let done = false; - let params = ""; - while (!done) { - let decompressed = await reader.read(); - done = decompressed.done; - params += new TextDecoder().decode(decompressed.value); - } - - return params; -}; - -/** - * Import and instantiate the Nova WASM module - * - * @return - The Nova WASM module - */ -export const getWasm = async (): Promise => { - const wasm = await import("bjj_ecdsa_nova_wasm"); - await wasm.default(); - // let concurrency = Math.floor(navigator.hardwareConcurrency / 3) * 2; - // if (concurrency < 1) concurrency = 1; - let concurrency = navigator.hardwareConcurrency - 1; - await wasm.initThreadPool(concurrency); - return wasm; -}; diff --git a/src/lib/client/worker.ts b/src/lib/client/worker.ts deleted file mode 100644 index feb62b11..00000000 --- a/src/lib/client/worker.ts +++ /dev/null @@ -1,354 +0,0 @@ -import { expose } from "comlink"; -import { MembershipFolder, NovaWasm } from "@/lib/client/nova"; -import { LocationSignature, User } from "@/lib/client/localStorage"; -import { IndexDBWrapper, TreeType } from "@/lib/client/indexDB"; - -/** - * A general thread for handling all folding operations in the background - * 0. Checks indexdb that no valid lock is present - * 1. Downloads params - * 2. Folds all attendees - * 3. Folds all speakers - * 4. Folds all talks - * - * @param users - all users that exist in local storage - * @param talks - all talks that exist in local storage - */ -async function work(users: User[], talks: LocationSignature[]) { - // instantiate indexdb - const db = new IndexDBWrapper(); - await db.init(); - - console.log("Filtering users and talks"); - // sort attendees and speakers - let attendees = users.filter((user) => { - return ( - !user.isSpeaker && user.pkId !== "0" && user.sig && user.sigPk && user.msg - ); - }); - let speakers = users.filter((user) => { - return ( - user.isSpeaker && user.pkId !== "0" && user.sig && user.sigPk && user.msg - ); - }); - - // filter out attendees, speakers, and talks with no talks - const attendeePks = await db.getUnincluded( - TreeType.Attendee, - attendees.map((user) => user.sigPk!) - ); - attendees = attendees.filter((user) => attendeePks.includes(user.sigPk!)); - console.log(`Found ${attendees.length} attendees to fold`); - const speakerPks = await db.getUnincluded( - TreeType.Speaker, - speakers.map((user) => user.sigPk!) - ); - speakers = speakers.filter((user) => speakerPks.includes(user.sigPk!)); - console.log(`Found ${speakers.length} speakers to fold`); - - const talkPks = await db.getUnincluded( - TreeType.Talk, - talks.map((talk) => talk.pk) - ); - talks = talks.filter((talk) => talkPks.includes(talk.pk)); - console.log(`Found ${talks.length} talks to fold`); - - // attempt to set a lock on the db - let lock = await db.setLock(); - // terminate the lock if it is undefined - if (lock === undefined) return; - - // download params - console.log("Beginning params download"); - lock = await downloadParams(lock); - if (lock === undefined) return; - // todo: sort speakers and attendees - - // instantiate wasm - const wasm = await import("bjj_ecdsa_nova_wasm"); - await wasm.default(); - // let concurrency = Math.floor(navigator.hardwareConcurrency / 3) * 2; - // if (concurrency < 1) concurrency = 1; - let concurrency = navigator.hardwareConcurrency - 1; - await wasm.initThreadPool(concurrency); - - // prove attendee folds - if (attendees.length > 0) { - console.log("Beginning attendee folding"); - lock = await fold( - attendees.map((attendee) => attendee.sigPk!), - attendees.map((attendee) => attendee.sig!), - attendees.map((attendee) => attendee.msg!), - TreeType.Attendee, - lock, - wasm - ); - if (lock === undefined) return; - } - - // prove speaker folds - if (speakers.length > 0) { - console.log("Beginning speaker folding"); - lock = await fold( - speakers.map((speaker) => speaker.sigPk!), - speakers.map((speaker) => speaker.sig!), - speakers.map((speaker) => speaker.msg!), - TreeType.Speaker, - lock, - wasm - ); - if (lock === undefined) return; - } - // todo: prove talk folds - if (talks.length > 0) { - lock = await fold( - talks.map((talk) => talk.pk), - talks.map((talk) => talk.sig), - talks.map((talk) => talk.msg), - TreeType.Talk, - lock, - wasm - ); - if (lock === undefined) return; - } - - // remove the lock - console.log("Nova worker terminating successfully"); - await db.releaseLock(lock); -} - -/** - * Fold all { speakers | attendees } given a set of users - * - * @param users - valid users to fold into the membership proof - * @param lock - the previously set timelock - * @returns the last lock set during execution, or undefined if timeout - */ -async function fold( - pks: string[], - sigs: string[], - msgs: string[], - treeType: TreeType, - lock: number, - wasm: NovaWasm -): Promise { - console.log(`${sigs.length} ${treeType}s to fold`); - - // define new lock - let newLock: number | undefined = lock; - - // Initialize indexdb - const db = new IndexDBWrapper(); - await db.init(); - - // get params - const params = new Blob(await db.getChunks()); - // Initialize membership folder - const membershipFolder = await MembershipFolder.initWithIndexDB(params, wasm); - - // Check if fold already exists - let previousProof = await db.getFold(treeType); - - let startIndex = previousProof ? 0 : 1; - // If no previous attendee proof, start a new fold - if (!previousProof) { - const proof = await membershipFolder.startFold( - pks[0], - sigs[0], - msgs[0], - treeType - ); - // compress the proof - const compressed = await membershipFolder.compressProof(proof); - const proofBlob = new Blob([compressed]); - // check that timelock has not expired - let res = await db.checkLock(newLock); - if (res === false) { - console.log(`Worker lock expired, terminating...`); - return; - } else { - await db.addFold(treeType, proofBlob, pks[0]); - console.log(`First ${treeType} membership proof folded`); - newLock = await db.setLock(newLock); - if (newLock === undefined) { - console.log(`Worker lock expired, terminating...`); - return; - } - } - postMessage({ index: 0, updateType: `${treeType}Folded` }); - } - - // fold sequentially - for (let i = startIndex; i < sigs.length; i++) { - const proofData = await db.getFold(treeType); - let proof = await membershipFolder.decompressProof( - new Uint8Array(await proofData!.proof.arrayBuffer()) - ); - // fold in membership - proof = await membershipFolder.continueFold( - proof, - proofData!.numFolds, - pks[i], - sigs[i], - msgs[i], - treeType - ); - // compress the proof - const compressed = await membershipFolder.compressProof(proof); - const proofBlob = new Blob([compressed]); - // check that timelock has not expired - let res = await db.checkLock(newLock); - if (res === false) { - console.log(`Worker lock expired, terminating...`); - return; - } else { - await db.incrementFold(treeType, proofBlob, pks[i]); - console.log(`${i} of ${sigs.length} ${treeType}s folded`); - newLock = await db.setLock(newLock); - if (newLock === undefined) { - console.log(`Worker lock expired, terminating...`); - return; - } - } - postMessage({ index: i, updateType: `${treeType}Folded` }); - } - return newLock; -} - - - -/** - * Obfuscate a fold for via web worker - * - * @param params - gzip compressed params - */ -async function finalize(treeType: TreeType): Promise { - // Initialize indexdb - const db = new IndexDBWrapper(); - await db.init(); - // get params - const params = new Blob(await db.getChunks()); - - // instantiate wasm - const wasm = await import("bjj_ecdsa_nova_wasm"); - await wasm.default(); - // let concurrency = Math.floor(navigator.hardwareConcurrency / 3) * 2; - // if (concurrency < 1) concurrency = 1; - let concurrency = Math.floor(navigator.hardwareConcurrency) / 3; - await wasm.initThreadPool(concurrency); - - // Initialize membership folder - const membershipFolder = await MembershipFolder.initWithIndexDB(params, wasm); - - const proofData = await db.getFold(treeType); - if (proofData === undefined) { - return false; - } - - // decompress proof - let proof = await membershipFolder.decompressProof( - new Uint8Array(await proofData!.proof.arrayBuffer()) - ); - // obfuscate proof - let obfuscatedProof = await membershipFolder.obfuscate( - proof, - proofData!.numFolds, - treeType - ); - // compress the proof - const compressed = await membershipFolder.compressProof(obfuscatedProof); - const proofBlob = new Blob([compressed]); - // store the compressed proof - await db.obfuscateFold(treeType, proofBlob); - return true; -} - -/** - * Verify that a proof is valid - * - * @param params - gzip compressed params - */ -async function verify(proofBlob: Blob, numFolded: number, treeType: TreeType): Promise { - // Initialize indexdb - const db = new IndexDBWrapper(); - await db.init(); - // get params - const params = new Blob(await db.getChunks()); - - // instantiate wasm - const wasm = await import("bjj_ecdsa_nova_wasm"); - await wasm.default(); - // let concurrency = Math.floor(navigator.hardwareConcurrency / 3) * 2; - // if (concurrency < 1) concurrency = 1; - let concurrency = Math.floor(navigator.hardwareConcurrency) / 3; - await wasm.initThreadPool(concurrency); - - // Initialize membership folder - const membershipFolder = await MembershipFolder.initWithIndexDB(params, wasm); - - // decompress proof - let proof = await membershipFolder.decompressProof( - new Uint8Array(await proofBlob.arrayBuffer()) - ); - - // verify proof - try { - await membershipFolder.verify(proof, numFolded, treeType, true); - console.log("Verified proof"); - return true; - } catch (e) { - console.error("Failed to verify proof"); - return false; - } -} - -/** - * Get chunks of public_params.json and store in indexdb - * - * @param lock - the timestamp of the lock to start with - * @return - the last lock set - */ -async function downloadParams(lock: number): Promise { - let newLock: number | undefined = lock; - // instantiate indexdb - const db = new IndexDBWrapper(); - await db.init(); - // get chunk count - const chunkIndex = await db.countChunks(); - if (chunkIndex === 10) { - console.log("Chunks previously cached"); - return lock; - } - // get the next chunk - console.log(`${chunkIndex} of 10 chunks stored`); - for (let i = chunkIndex; i < 10; i++) { - const chunkURI = `${process.env.NEXT_PUBLIC_NOVA_BUCKET_URL}/params_${i}.gz`; - const chunk = await fetch(chunkURI, { - headers: { "Content-Type": "application/x-binary" }, - }).then(async (res) => await res.blob()); - // check the lock hasn't expired - let res = await db.checkLock(newLock); - if (res === false) { - return; - } else { - console.log(`Chunk ${i + 1} of 10 stored`); - await db.addChunk(i, chunk); - newLock = await db.setLock(newLock); - if (newLock === undefined) { - return; - } - } - postMessage({ index: i, updateType: 'paramDownloaded' }); - } - return newLock; -} - -const exports = { - work, - finalize, - verify -}; - -export type FoldingWorker = typeof exports; - -expose(exports); diff --git a/src/lib/server/auth.ts b/src/lib/server/auth.ts index 8cec0f22..557d56c3 100644 --- a/src/lib/server/auth.ts +++ b/src/lib/server/auth.ts @@ -4,63 +4,6 @@ import { object, string, boolean } from "yup"; export const MAX_SIGNIN_CODE_GUESS_ATTEMPTS = 5; -/** - * Generates a one-time signin code as a 6 digit integer represented as a string, with leading zeros if necessary. - * Send this code to the user via email. - * @returns Boolean indicating success. - */ -export const generateAndSendSigninCode = async ( - email: string -): Promise => { - // Delete all signin codes associated with the email - await prisma.signinCode.deleteMany({ - where: { email }, - }); - - // Generate a one-time signin code as a 6 digit integer represented as a string, with leading zeros if necessary - const newSigninCode = Math.floor(Math.random() * 1000000) - .toString() - .padStart(6, "0"); - - // Set the expiration time to 30 minutes from the current time - // 30 minutes * 60 seconds per minute * 1000 milliseconds per second - const expiresAt = new Date(new Date().getTime() + 30 * 60 * 1000); - - // Save the signin code and expiration time associated with the email in the database - const signinCodeEntry = await prisma.signinCode.create({ - data: { value: newSigninCode, email, expiresAt, usedGuessAttempts: 0 }, - }); - const signinCode = signinCodeEntry.value; - - const response = await fetch( - "https://platform.iyk.app/api/admin/eth-denver-login-email", - { - method: "POST", - headers: { - "x-cursive-secret": process.env.IYK_EMAIL_API_SECRET!, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - recipient: email, - code: signinCode, - }), - } - ); - - if (!response.ok) { - console.error(`Error sending email to user ${email}: `, response); - return false; - } - - const { success } = await response.json(); - if (!success) { - console.error("Failed to send email to user ${email}: ", response); - return false; - } - - return true; -}; - export type AuthTokenResponse = { value: string; expiresAt: Date; diff --git a/src/lib/server/cmac.ts b/src/lib/server/cmac.ts new file mode 100644 index 00000000..2eaeba6b --- /dev/null +++ b/src/lib/server/cmac.ts @@ -0,0 +1,51 @@ +import { keyUids } from "@/shared/keygen"; +const aesjs = require("aes-js"); + +export const verifyCmac = (hexData: string): string | undefined => { + if (hexData.startsWith("CURSIVE")) { + const lastTwoChars = hexData.slice(-2); + const num = parseInt(lastTwoChars, 10); + if (isNaN(num) || num < 1 || num > 50) return undefined; + return hexData; + } + + if (hexData.startsWith("TALK")) { + const lastTwoChars = hexData.slice(-2); + const num = parseInt(lastTwoChars, 10); + if (isNaN(num) || num < 1 || num > 10) return undefined; + return hexData; + } + + const cardKeys = process.env.CARD_KEYS!.split(","); + + for (const key of cardKeys) { + const keyBytes = aesjs.utils.hex.toBytes(key); + const iv = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + var encryptedBytes = aesjs.utils.hex.toBytes(hexData); + const aesCbc = new aesjs.ModeOfOperation.cbc(keyBytes, iv); + const decryptedBytes = aesCbc.decrypt(encryptedBytes); + // Assuming decryptedBytes is a Uint8Array or similar + const p_stream = new Uint8Array(decryptedBytes); + // Read the first byte as picc_data_tag + const picc_data_tag = p_stream[0]; + // Bitwise operations for flags + const uid_mirroring_en = (picc_data_tag & 0x80) === 0x80; + const uid_length = picc_data_tag & 0x0f; + // Error handling for unsupported UID length + if (uid_length !== 0x07) { + continue; + } + // Read UID if mirroring is enabled + if (uid_mirroring_en) { + let uid = Buffer.from(p_stream.slice(1, 1 + uid_length)) + .toString("hex") + .toUpperCase(); + console.log(uid); + if (keyUids.includes(uid)) { + return uid; + } + } + } + + return undefined; +}; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 9a89b733..878b78b6 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,21 +1,21 @@ -import { AppFooter } from '@/components/AppFooter'; -import { AppHeader } from '@/components/AppHeader'; -import { FullPageBanner } from '@/components/FullPageBanner'; -import { TransitionWrapper } from '@/components/Transition'; -import useSettings from '@/hooks/useSettings'; -import OnlyMobileLayout from '@/layouts/OnlyMobileLayout'; -import { DM_Sans } from 'next/font/google'; -import '@/styles/globals.css'; +import { AppFooter } from "@/components/AppFooter"; +import { AppHeader } from "@/components/AppHeader"; +import { FullPageBanner } from "@/components/FullPageBanner"; +import { TransitionWrapper } from "@/components/Transition"; +import useSettings from "@/hooks/useSettings"; +import OnlyMobileLayout from "@/layouts/OnlyMobileLayout"; +import { DM_Sans } from "next/font/google"; +import "@/styles/globals.css"; import { QueryCache, QueryClient, QueryClientProvider, -} from '@tanstack/react-query'; -import { StateMachineProvider } from 'little-state-machine'; -import type { AppProps } from 'next/app'; -import { useEffect, useState } from 'react'; -import { toast, Toaster } from 'sonner'; -import { Analytics } from '@vercel/analytics/react'; +} from "@tanstack/react-query"; +import { StateMachineProvider } from "little-state-machine"; +import type { AppProps } from "next/app"; +import { useEffect, useState } from "react"; +import { toast, Toaster } from "sonner"; +import { Analytics } from "@vercel/analytics/react"; const queryClient = new QueryClient({ queryCache: new QueryCache({ @@ -24,8 +24,8 @@ const queryClient = new QueryClient({ }); const dmSans = DM_Sans({ - subsets: ['latin'], - variable: '--font-dm-sans', + subsets: ["latin"], + variable: "--font-dm-sans", }); export default function App({ Component, pageProps }: AppProps) { @@ -42,25 +42,14 @@ export default function App({ Component, pageProps }: AppProps) { setPageHeight(window?.innerHeight); }, []); - useEffect(() => { - var _mtm = (window._mtm = window._mtm || []); - _mtm.push({ 'mtm.startTime': new Date().getTime(), event: 'mtm.Start' }); - var d = document, - g = d.createElement('script'), - s = d.getElementsByTagName('script')[0]; - g.async = true; - g.src = '/api/proxy/container_8YPThdSd.js'; - s.parentNode?.insertBefore(g, s); - }, []); - const footerVisible = showFooter && !fullPage; if (isMaintenance) { return ( ); } @@ -81,7 +70,7 @@ export default function App({ Component, pageProps }: AppProps) { height: `${pageHeight}px`, }} > -
+
{showHeader && !fullPage && ( @@ -103,10 +92,10 @@ export default function App({ Component, pageProps }: AppProps) { diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx index a0b8381c..5b21929b 100644 --- a/src/pages/_document.tsx +++ b/src/pages/_document.tsx @@ -3,17 +3,17 @@ import { Html, Head, Main, NextScript } from "next/document"; export default function Document() { return ( - Cursive NFC Demo + Backpocket Alpha - + ({ id })), - }, - }, - ], - }, - locationRequirements: { - create: [], - }, - }, - }); - - // Quest 2: Meet 3 speakers - await prisma.quest.create({ - data: { - name: "🎤 Oracle Encounter", - description: - "Ask 3 speakers a question or share feedback about their talk. Ask to tap their ring to collect a link to their presentation slides (if available)", - userRequirements: { - create: [ - { - name: "Connect with 3 speakers at the Sig Sing workshop", - numSigsRequired: 3, - sigNullifierRandomness: getServerRandomNullifierRandomness(), // Ensures signatures cannot be reused to meet this requirement - users: { - connect: speakerUserIds.map((id) => ({ id })), - }, - }, - ], - }, - locationRequirements: { - create: [], - }, - }, - }); - - // Quest 3: Attend 5 talks // await prisma.quest.create({ // data: { - // name: "👩‍🏫 Acropolis Assembler", + // name: "🦋 Social Butterfly", // description: - // "Tap in to 5 talks at Zk Summit 11 to make this proof. Look for cards on posters at conference room entrances.", + // "Connect with 10 people to make this proof. Ask to tap their ring, share socials, and discover event activity that you have in common.", // userRequirements: { - // create: [], + // create: [ + // { + // name: "Connect with 10 people at SigSing", + // numSigsRequired: 10, + // sigNullifierRandomness: getServerRandomNullifierRandomness(), // Ensures signatures cannot be reused to meet this requirement + // users: { + // connect: allUserIds.map((id) => ({ id })), + // }, + // }, + // ], // }, // locationRequirements: { + // create: [], + // }, + // }, + // }); + + // Quest 2: Meet 3 speakers + // await prisma.quest.create({ + // data: { + // name: "🎤 Meet the speakers", + // description: + // "Ask 3 speakers a question or share feedback about their talk. Ask to tap their ring to collect a link to their presentation slides (if available)", + // userRequirements: { // create: [ // { - // name: "Attend 5 talks at ZK Summit 11", - // numSigsRequired: 5, + // name: "Connect with 3 speakers at the Sig Sing workshop", + // numSigsRequired: 3, // sigNullifierRandomness: getServerRandomNullifierRandomness(), // Ensures signatures cannot be reused to meet this requirement - // locations: { - // connect: allTalkIds.map((id) => ({ id })), + // users: { + // connect: speakerUserIds.map((id) => ({ id })), // }, // }, // ], // }, + // locationRequirements: { + // create: [], + // }, // }, // }); + + // Quest 3: Attend 5 talks + await prisma.quest.create({ + data: { + name: "👩‍🏫 Collect 5 Talk ZK-POAPs", + description: + "Collect 5 ZK-POAPs for talks you enjoyed to make this proof. Find the NFC stickers corresponding to each one.", + userRequirements: { + create: [], + }, + locationRequirements: { + create: [ + { + name: "Attend 5 talks at ZK Summit 11", + numSigsRequired: 5, + sigNullifierRandomness: getServerRandomNullifierRandomness(), // Ensures signatures cannot be reused to meet this requirement + locations: { + connect: allTalkIds.map((id) => ({ id })), + }, + }, + ], + }, + }, + }); // END HARDCODED QUESTS FOR SIG SING WORKSHOP res.status(200).json({}); diff --git a/src/pages/api/folding/proof.ts b/src/pages/api/folding/proof.ts deleted file mode 100644 index 195842e3..00000000 --- a/src/pages/api/folding/proof.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { NextApiRequest, NextApiResponse } from "next"; -import prisma from "@/lib/server/prisma"; -import { verifyAuthToken } from "@/lib/server/auth"; -import { ErrorResponse } from "@/types"; - -export type GetFoldingProofResponse = { - id: string; - userName: string; - userPublicKey: string; - attendeeProofUrl?: string; - attendeeProofCount?: number; - speakerProofUrl?: string; - speakerProofCount?: number; - talkProofUrl?: string; - talkProofCount?: number; -}; - -// GET request handler -async function handleGetRequest( - req: NextApiRequest, - res: NextApiResponse -) { - const { proofUuid } = req.query; - if (typeof proofUuid !== "string") { - return res.status(400).json({ error: "Invalid proofUuid parameter" }); - } - - try { - const foldingProof = await prisma.foldedProof.findUnique({ - where: { id: proofUuid }, - select: { - attendeeProofLink: true, - attendeeNumFolded: true, - speakerProofLink: true, - speakerNumFolded: true, - talkProofLink: true, - talkNumFolded: true, - user: { - select: { - displayName: true, - signaturePublicKey: true, - }, - }, - }, - }); - - if (!foldingProof) { - return res.status(404).json({ error: "Proof not found" }); - } - - return res.status(200).json({ - id: proofUuid, - userName: foldingProof.user.displayName, - userPublicKey: foldingProof.user.signaturePublicKey, - attendeeProofUrl: foldingProof.attendeeProofLink - ? foldingProof.attendeeProofLink - : undefined, - attendeeProofCount: foldingProof.attendeeNumFolded - ? foldingProof.attendeeNumFolded - : undefined, - speakerProofUrl: foldingProof.speakerProofLink - ? foldingProof.speakerProofLink - : undefined, - speakerProofCount: foldingProof.speakerNumFolded - ? foldingProof.speakerNumFolded - : undefined, - talkProofUrl: foldingProof.talkProofLink - ? foldingProof.talkProofLink - : undefined, - talkProofCount: foldingProof.talkNumFolded - ? foldingProof.talkNumFolded - : undefined, - }); - } catch (error) { - console.log('Error: ', error); - return res.status(500).json({ error: "Internal server error" }); - } -} - -// POST request handler -async function handlePostRequest(req: NextApiRequest, res: NextApiResponse) { - const { authToken, data } = req.body; - - if (!authToken) { - return res.status(400).json({ error: "Invalid input parameters" }); - } - - try { - const userId = await verifyAuthToken(authToken); - if (!userId) { - return res.status(401).json({ error: "Invalid or expired auth token" }); - } - - const user = await prisma.user.findUnique({ - where: { id: userId }, - }); - if (!user) { - return res.status(404).json({ error: "User not found" }); - } - - const { attendees, speakers, talks } = data; - - const newFoldingProof = await prisma.foldedProof.create({ - data: { - userId, - attendeeProofLink: attendees ? attendees.uri : null, - attendeeNumFolded: attendees ? attendees.numFolded : null, - speakerProofLink: speakers ? speakers.uri : null, - speakerNumFolded: speakers ? speakers.numFolded : null, - talkProofLink: talks ? talks.uri : null, - talkNumFolded: talks ? talks.numFolded : null, - }, - }); - - return res.status(201).json({ proofUuid: newFoldingProof.id }); - } catch (error) { - return res.status(500).json({ error: "Internal server error" }); - } -} - -export default async function handler( - req: NextApiRequest, - res: NextApiResponse -) { - switch (req.method) { - case "GET": - await handleGetRequest(req, res); - break; - case "POST": - await handlePostRequest(req, res); - break; - default: - res.setHeader("Allow", ["GET", "POST"]); - res.status(405).end(`Method ${req.method} Not Allowed`); - } -} diff --git a/src/pages/api/folding/upload.ts b/src/pages/api/folding/upload.ts deleted file mode 100644 index 0f6efe7d..00000000 --- a/src/pages/api/folding/upload.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { handleUpload, type HandleUploadBody } from "@vercel/blob/client"; -import type { NextApiResponse, NextApiRequest } from "next"; - -export default async function handler( - request: NextApiRequest, - response: NextApiResponse -) { - const body = request.body as HandleUploadBody; - - try { - const jsonResponse = await handleUpload({ - body, - request, - onBeforeGenerateToken: async (pathname) => { - return {}; - }, - onUploadCompleted: async ({ blob, tokenPayload }) => {}, - }); - - return response.status(200).json(jsonResponse); - } catch (error) { - // The webhook will retry 5 times waiting for a 200 - return response.status(400).json({ error: (error as Error).message }); - } -} diff --git a/src/pages/api/mpc/create_room.ts b/src/pages/api/mpc/create_room.ts new file mode 100644 index 00000000..4bb6addf --- /dev/null +++ b/src/pages/api/mpc/create_room.ts @@ -0,0 +1,46 @@ +import type { NextApiRequest, NextApiResponse } from "next"; +import prisma from "@/lib/server/prisma"; +import { ErrorResponse, EmptyResponse } from "@/types"; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + if (req.method !== "POST") { + return res.status(405).json({ error: "Method Not Allowed" }); + } + + const { name, numParties, password, displayName } = JSON.parse(req.body); + + try { + const existingRoom = await prisma.room.findMany({ + where: { name: name as string }, + }); + + if (existingRoom.length > 0) { + return res + .status(400) + .json({ error: "Room with this name already exists" }); + } + + const room = await prisma.room.create({ + data: { + name, + numParties, + password: password, + }, + }); + + await prisma.roomMember.create({ + data: { + roomId: room.id, + displayName, + }, + }); + + return res.status(201).json({}); + } catch (error) { + console.error("Room creation failed", error); + return res.status(500).json({ error: "Internal Server Error" }); + } +} diff --git a/src/pages/api/mpc/expire_room.ts b/src/pages/api/mpc/expire_room.ts new file mode 100644 index 00000000..74b99782 --- /dev/null +++ b/src/pages/api/mpc/expire_room.ts @@ -0,0 +1,46 @@ +import type { NextApiRequest, NextApiResponse } from "next"; +import prisma from "@/lib/server/prisma"; +import { verifyAuthToken } from "@/lib/server/auth"; +import { ErrorResponse, EmptyResponse } from "@/types"; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + if (req.method !== "POST") { + return res.status(405).json({ error: "Method Not Allowed" }); + } + + const { authToken, roomId } = JSON.parse(req.body); + + const userId = await verifyAuthToken(authToken); + if (!userId) { + return res.status(401).json({ error: "Invalid or expired token" }); + } + + try { + const room = await prisma.room.findUnique({ + where: { id: roomId }, + }); + + if (!room) { + return res.status(404).json({ error: "Room not found" }); + } + + if (room.creatorId !== userId) { + return res + .status(403) + .json({ error: "Forbidden: Only the creator can expire the room" }); + } + + await prisma.room.update({ + where: { id: roomId }, + data: { isActive: false }, + }); + + return res.status(200).json({}); + } catch (error) { + console.error("Expiring room failed", error); + return res.status(500).json({ error: "Internal Server Error" }); + } +} diff --git a/src/pages/api/mpc/get_all_rooms.ts b/src/pages/api/mpc/get_all_rooms.ts new file mode 100644 index 00000000..30ac2334 --- /dev/null +++ b/src/pages/api/mpc/get_all_rooms.ts @@ -0,0 +1,35 @@ +import type { NextApiRequest, NextApiResponse } from "next"; +import prisma from "@/lib/server/prisma"; +import { ErrorResponse } from "@/types"; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse<{ rooms: any[] } | ErrorResponse> +) { + if (req.method !== "GET") { + return res.status(405).json({ error: "Method Not Allowed" }); + } + + try { + const rooms = await prisma.room.findMany({ + include: { + members: true, + creator: true, + }, + where: { + isActive: true, + createdAt: { + gte: new Date(new Date().getTime() - 60 * 60 * 1000), + }, + }, + orderBy: { + createdAt: "desc", + }, + }); + + return res.status(200).json({ rooms }); + } catch (error) { + console.error("Fetching rooms failed", error); + return res.status(500).json({ error: "Internal Server Error" }); + } +} diff --git a/src/pages/api/mpc/get_room_names.ts b/src/pages/api/mpc/get_room_names.ts new file mode 100644 index 00000000..af4f1bf0 --- /dev/null +++ b/src/pages/api/mpc/get_room_names.ts @@ -0,0 +1,39 @@ +import type { NextApiRequest, NextApiResponse } from "next"; +import prisma from "@/lib/server/prisma"; +import { ErrorResponse } from "@/types"; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse<{ roomNames: any[] } | ErrorResponse> +) { + if (req.method !== "GET") { + return res.status(405).json({ error: "Method Not Allowed" }); + } + + const { roomId } = req.query; + + if (!roomId) { + return res.status(400).json({ error: "Bad Request" }); + } + + try { + const roomMembers = await prisma.roomMember.findMany({ + where: { + roomId: parseInt(roomId.toString()), + }, + }); + + if (!roomMembers) { + return res.status(404).json({ error: "Room not found" }); + } + + const roomNames = roomMembers + .map((member) => member.displayName || `User-${member.id}`) + .sort((a, b) => a.localeCompare(b)); + + return res.status(200).json({ roomNames }); + } catch (error) { + console.error("Fetching rooms failed", error); + return res.status(500).json({ error: "Internal Server Error" }); + } +} diff --git a/src/pages/api/mpc/join_room.ts b/src/pages/api/mpc/join_room.ts new file mode 100644 index 00000000..63c1c8fb --- /dev/null +++ b/src/pages/api/mpc/join_room.ts @@ -0,0 +1,50 @@ +import type { NextApiRequest, NextApiResponse } from "next"; +import prisma from "@/lib/server/prisma"; +import { verifyAuthToken } from "@/lib/server/auth"; +import { ErrorResponse, EmptyResponse } from "@/types"; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + if (req.method !== "POST") { + return res.status(405).json({ error: "Method Not Allowed" }); + } + + const { roomId, password, displayName } = JSON.parse(req.body); + + try { + const room = await prisma.room.findUnique({ + where: { id: roomId }, + }); + + if (!room) { + return res.status(404).json({ error: "Room not found" }); + } + + if (password !== room.password) { + return res.status(400).json({ error: "Incorrect password" }); + } + + const memberCount = await prisma.roomMember.count({ + where: { + roomId: roomId, + }, + }); + if (memberCount >= room.numParties) { + return res.status(400).json({ error: "Room is full" }); + } + + await prisma.roomMember.create({ + data: { + roomId, + displayName, + }, + }); + + return res.status(200).json({}); + } catch (error) { + console.error("Joining room failed", error); + return res.status(500).json({ error: "Internal Server Error" }); + } +} diff --git a/src/pages/api/proxy/[...path].ts b/src/pages/api/proxy/[...path].ts deleted file mode 100644 index 013cffd0..00000000 --- a/src/pages/api/proxy/[...path].ts +++ /dev/null @@ -1,39 +0,0 @@ -// pages/api/proxy/[...path].ts - -import type { NextApiRequest, NextApiResponse } from "next"; - -export default async function handler( - req: NextApiRequest, - res: NextApiResponse -) { - const { path } = req.query; - - // Ensure path is a string or array of strings - const matomoPath = Array.isArray(path) ? path.join("/") : path; - const matomoUrl = `https://cdn.matomo.cloud/psedev.matomo.cloud/${matomoPath}`; - - try { - const matomoResponse = await fetch(matomoUrl); - - if (!matomoResponse.ok) { - // If the response from Matomo is not OK, forward the status code and message - res.status(matomoResponse.status).send(matomoResponse.statusText); - return; - } - - // Forward the content-type from the Matomo server - const contentType = matomoResponse.headers.get("content-type"); - if (contentType) { - res.setHeader("Content-Type", contentType); - } - - // Read the response body as buffer - const data = await matomoResponse.arrayBuffer(); - - // Send the buffer as the response - res.status(200).send(Buffer.from(data)); - } catch (error) { - console.error("Error in proxying Matomo script:", error); - res.status(500).send("Internal server error"); - } -} diff --git a/src/pages/api/register/create_account.ts b/src/pages/api/register/create_account.ts index 19fea3ac..1317b04f 100644 --- a/src/pages/api/register/create_account.ts +++ b/src/pages/api/register/create_account.ts @@ -7,14 +7,10 @@ import { telegramUsernameRegex, twitterUsernameRegex, } from "@/lib/shared/utils"; -import { - ChipType, - getChipIdFromIykRef, - getChipTypeFromChipId, -} from "@/lib/server/iyk"; +import { verifyCmac } from "@/lib/server/cmac"; const createAccountSchema = object({ - chipId: string().optional().default(undefined), + chipEnc: string().optional().default(undefined), mockRef: string().optional().default(undefined), displayName: string().trim().required(), encryptionPublicKey: string().required(), @@ -62,7 +58,7 @@ export default async function handler( } const { - chipId, + chipEnc, mockRef, displayName, encryptionPublicKey, @@ -89,7 +85,7 @@ export default async function handler( } if (telegram && telegram !== "@" && !telegramUsernameRegex.test(telegram)) { - return res.status(400).json({ error: "Invalid Telegram username" }); + return res.status(400).json({ error: "Invalid Daimo username" }); } if (bio && bio.length > 200) { @@ -100,7 +96,12 @@ export default async function handler( let userSignaturePublicKey; let userSignaturePrivateKey; - if (chipId) { + if (chipEnc) { + let chipId = verifyCmac(chipEnc); + if (!chipId) { + return res.status(400).json({ error: "Invalid chipEnc provided" }); + } + const chipKey = await prisma.chipKey.findUnique({ where: { chipId, @@ -146,7 +147,11 @@ export default async function handler( // Check if user is already created let isExistingChipUser = false; let isUserRegistered = false; - if (chipId) { + if (chipEnc) { + let chipId = verifyCmac(chipEnc); + if (!chipId) { + return res.status(400).json({ error: "Invalid chipEnc provided" }); + } const existingChipUser: any = await prisma.user.findUnique({ where: { chipId, @@ -160,7 +165,12 @@ export default async function handler( // If user is created and registered, return error if (isExistingChipUser && isUserRegistered) { return res.status(400).json({ error: "Card already registered" }); - } else if (isExistingChipUser && chipId) { + } else if (isExistingChipUser && chipEnc) { + let chipId = verifyCmac(chipEnc); + if (!chipId) { + return res.status(400).json({ error: "Invalid chipEnc provided" }); + } + // If user is created but not registered, update user const updatedUser = await prisma.user.update({ where: { @@ -194,7 +204,7 @@ export default async function handler( // If user is not created, create user const user = await prisma.user.create({ data: { - chipId, + chipId: chipEnc, // @ts-ignore isRegistered: true, displayName, diff --git a/src/pages/api/tap/plain.ts b/src/pages/api/tap/plain.ts index efd51e5d..3bb0e98c 100644 --- a/src/pages/api/tap/plain.ts +++ b/src/pages/api/tap/plain.ts @@ -4,11 +4,7 @@ import { boolean, object, string } from "yup"; import { ErrorResponse } from "@/types"; import { sign } from "@/lib/shared/signature"; import { getCounterMessage } from "babyjubjub-ecdsa"; -import { - ChipType, - getChipIdFromIykRef, - getChipTypeFromChipId, -} from "@/lib/server/iyk"; +import { verifyCmac } from "@/lib/server/cmac"; const crypto = require("crypto"); export enum TapResponseCode { @@ -123,100 +119,24 @@ export const generateChipSignature = async ( return { message, signature }; }; -/** - * GET - * Receives an iyk chip iykRef - * Responds with person tap data, location tap data, or an error - */ export default async function handler( req: NextApiRequest, - res: NextApiResponse + res: NextApiResponse<{ url: string } | ErrorResponse> ) { if (req.method !== "GET") { return res.status(405).json({ error: "Method Not Allowed" }); } - // chipId must be provided - const chipId = req.query.chipId; - if (!chipId || typeof chipId !== "string") { - return res.status(400).json({ error: "Invalid chipId provided" }); - } - - // chip key must exist - const chipKey = await prisma.chipKey.findFirst({ - where: { - chipId, - }, - }); - if (!chipKey) { - return res.status(400).json({ - code: TapResponseCode.CHIP_KEY_NOT_FOUND, - error: "Chip key not found", - }); - } - - // if user is registered, return user data - const user = await prisma.user.findUnique({ - where: { - chipId, - }, - }); - if (user) { - // If user is not registered, return person not registered response - if (!user.isRegistered) { - return res - .status(200) - .json({ code: TapResponseCode.PERSON_NOT_REGISTERED }); - } - - // Get signature from chip - const { message, signature } = await generateChipSignature(chipId); - - const personTapResponse: PersonTapResponse = { - displayName: user.displayName, - pkId: user.id.toString(), - psiPublicKeysLink: user.psiPublicKeysLink, - encryptionPublicKey: user.encryptionPublicKey, - twitter: user.twitter ? user.twitter : undefined, - telegram: user.telegram ? user.telegram : undefined, - bio: user.bio ? user.bio : undefined, - isUserSpeaker: user.isUserSpeaker, - signaturePublicKey: user.signaturePublicKey, - signatureMessage: message, - signature, - }; - return res - .status(200) - .json({ code: TapResponseCode.VALID_PERSON, person: personTapResponse }); - } - - // if location is registered, return location data - const location = await prisma.location.findUnique({ - where: { - chipId, - }, - }); - if (location) { - // Get signature from chip - const { message, signature } = await generateChipSignature(chipId); - - const locationTapResponse: LocationTapResponse = { - id: location.id.toString(), - name: location.name, - stage: location.stage, - speaker: location.speaker, - description: location.description, - startTime: location.startTime, - endTime: location.endTime, - signaturePublicKey: location.signaturePublicKey, - signatureMessage: message, - signature, - }; - return res.status(200).json({ - code: TapResponseCode.VALID_LOCATION, - location: locationTapResponse, - }); + // chipEnc must be provided + const chipEnc = req.query.chipEnc; + if (!chipEnc || typeof chipEnc !== "string") { + return res.status(400).json({ error: "Invalid chipEnc provided" }); } - return res.status(200).json({ code: TapResponseCode.PERSON_NOT_REGISTERED }); + // verify encryption + const chipId = verifyCmac(chipEnc); + console.log("ChipId", chipId); + return res + .status(200) + .json({ url: `https://connections.cursive.team/tap?chipId=${chipId}` }); } diff --git a/src/pages/api/user/update_profile.ts b/src/pages/api/user/update_profile.ts index 19270335..2cc4c268 100644 --- a/src/pages/api/user/update_profile.ts +++ b/src/pages/api/user/update_profile.ts @@ -53,7 +53,7 @@ export default async function handler( telegramUsername !== "@" && !telegramUsernameRegex.test(telegramUsername) ) { - return res.status(400).json({ error: "Invalid Telegram username" }); + return res.status(400).json({ error: "Invalid Daimo username" }); } if (bio && bio.length > 200) { diff --git a/src/pages/fold/index.tsx b/src/pages/fold/index.tsx deleted file mode 100644 index dbbe6d10..00000000 --- a/src/pages/fold/index.tsx +++ /dev/null @@ -1,175 +0,0 @@ -import { Button } from '@/components/Button'; -import { useEffect, useState } from 'react'; -import { getLocationSignatures, getUsers } from '@/lib/client/localStorage'; -import { MembershipFolder } from '@/lib/client/nova'; -import { Spinner } from '@/components/Spinner'; -import { toast } from 'sonner'; -import { useWorker } from '@/hooks/useWorker'; -import { TreeType } from '@/lib/client/indexDB'; -import { IndexDBWrapper } from '@/lib/client/indexDB'; - -export default function Fold() { - const { work, finalize, folding, completed } = useWorker(); - const [canFinalize, setCanFinalize] = useState(false); - const [canVerify, setCanVerify] = useState(false); - const [chunks, setChunks] = useState>([]); - const [db, setDB] = useState(null); - const [isProving, setIsProving] = useState(false); - const [numFolded, setNumFolded] = useState(0); - - useEffect(() => { - if (db) return; - (async () => { - // Init IndexDB - const db = new IndexDBWrapper(); - await db.init(); - setDB(db); - })(); - }, []); - - useEffect(() => { - if (completed || !db || folding) return; - // get the proof attendee type - (async () => { - // get params - const params = new Blob(await db.getChunks()); - - // instantiate wasm - const wasm = await import("bjj_ecdsa_nova_wasm"); - await wasm.default(); - // let concurrency = Math.floor(navigator.hardwareConcurrency / 3) * 2; - // if (concurrency < 1) concurrency = 1; - // let concurrency = Math.floor(navigator.hardwareConcurrency) / 3; - // await wasm.initThreadPool(concurrency); - let membershipFolder = await MembershipFolder.initWithIndexDB(params, wasm); - // get all attendees - const talkProof = await db.getFold(TreeType.Talk); - - const speakerProof = await db.getFold(TreeType.Speaker); - const attendeeProof = await db.getFold(TreeType.Attendee); - if (talkProof) { - let decompressed = await membershipFolder.decompressProof(new Uint8Array(await talkProof.proof.arrayBuffer())); - let res = await membershipFolder.verify(decompressed, talkProof.numFolds, TreeType.Talk, false); - console.log("Res: ", res) - } - if (speakerProof) { - let decompressed = await membershipFolder.decompressProof(new Uint8Array(await speakerProof.proof.arrayBuffer())); - let res = await membershipFolder.verify(decompressed, speakerProof.numFolds, TreeType.Speaker, false); - console.log("Res: ", res) - } - if (attendeeProof) { - let decompressed = await membershipFolder.decompressProof(new Uint8Array(await attendeeProof.proof.arrayBuffer())); - let res = await membershipFolder.verify(decompressed, attendeeProof.numFolds, TreeType.Attendee, false); - console.log("Res: ", res) - } - })(); - }, [db, completed]); - - // const finalize = async () => { - // if (!db) return; - // // get proof from indexdb - // setIsProving(true); - // const proofData = await db.getFold(TreeType.Attendee); - // if (proofData === undefined) { - // toast.error('No proof to finalize!'); - // setIsProving(false); - // return; - // } else if (proofData.obfuscated === true) { - // toast.error('Proof has already been finalized!'); - // setIsProving(false); - // return; - // } - - // // Obfuscate in web worker - // await obfuscateFold(); - // setCanFinalize(false); - // setCanVerify(true); - // setIsProving(false); - // toast.success( - // `Finalized folded proof of ${proofData.numFolds} attendees met!` - // ); - // }; - - // const fold = async () => { - // if (!db) return; - // setIsProving(true); - // // get users who are not speakers - - - // // get user that can be folded in - // let foldableUsers = await db.getUsersToFold(TreeType.Attendee, users); - // if (foldableUsers === undefined) { - // toast.info('No attendees to fold in!'); - // setIsProving(false); - // return; - // } - - // // Get proof count - // const proof = await db.getFold(TreeType.Attendee); - // const proofCount = proof?.numFolds ?? 0; - - // await foldAll(foldableUsers); - // setCanFinalize(true); - // setIsProving(false); - // toast.success( - // `Folded proofs of ${proofCount + foldableUsers.length} attendees met!` - // ); - // }; - - // const verify = async () => { - // if (!db) return; - // setIsProving(true); - // // get proof from indexdb - // const proofData = await db.getFold(TreeType.Attendee); - // if (proofData === undefined) { - // toast.error('No proof to verify!'); - // return; - // } else if (proofData.obfuscated === false) { - // toast.error('Proof has not been finalized!'); - // return; - // } - - // const params = new Blob(chunks); - // // Initialize membership folder - // const membershipFolder = await MembershipFolder.initWithIndexDB(params); - - // // decompress proof - // const proof = await membershipFolder.decompressProof( - // new Uint8Array(await proofData.proof.arrayBuffer()) - // ); - // await membershipFolder.verify(proof, proofData.numFolds, true); - // setIsProving(false); - // toast.success( - // `Verified folded proof of ${proofData.numFolds} attendees met!` - // ); - // }; - - return ( -
- {/* {!chunks.length ? ( - <> - ) : ( - <> - {numFolded !== 0 ? ( - <> -

Number of proofs folded: {numFolded}

- {canFinalize && !isProving && ( - - )} - {canVerify && !isProving && ( - - )} - - ) : ( - <> - {!isProving && ( - - )} - - )} - {isProving && } - - )} */} -
- ); -} \ No newline at end of file diff --git a/src/pages/folded/[id].tsx b/src/pages/folded/[id].tsx deleted file mode 100644 index 969daeac..00000000 --- a/src/pages/folded/[id].tsx +++ /dev/null @@ -1,340 +0,0 @@ -import { Button } from "@/components/Button"; -import { createFlower } from "@/lib/client/flower"; -import { useScripts } from "@/hooks/useScripts"; -import { useEffect, useMemo, useState } from "react"; -import { Icons } from "@/components/Icons"; -import { Card } from "@/components/cards/Card"; -import { useParams } from "next/navigation"; -import { IndexDBWrapper, TreeType } from "@/lib/client/indexDB"; -import { GetFoldingProofResponse } from "../api/folding/proof"; -import { Spinner } from "@/components/Spinner"; -import { useWorker } from "@/hooks/useWorker"; -import Link from "next/link"; -import { fetchWithRetry } from "@/lib/client/utils"; - -type UserProofs = { - attendee?: { - proof: Blob; - count: number; - }; - speaker?: { - proof: Blob; - count: number; - }; - talk?: { - proof: Blob; - count: number; - }; -}; - -const Folded = (): JSX.Element => { - const { id } = useParams(); - const { verify } = useWorker(); - const isLoaded = useScripts(); - const [dowloadingParams, setDownloadingParams] = useState(0); - const [fetchingProof, setFetchingProof] = useState(false); - const [numToVerify, setNumToVerify] = useState(0); - const [verified, setVerified] = useState(false); - const [verifyingCount, setVerifyingCount] = useState(0); - const [user, setUser] = useState(null); - const [userProofs, setUserProofs] = useState({}); - const [isVerifying, setIsVerifying] = useState(false); - const [isDownloadingParams, setIsDownloadingParams] = - useState(false); - - const flowerSize = 128; - - const downloadParams = async () => { - // Check how many params are stored - const db = new IndexDBWrapper(); - await db.init(); - - const chunkIndex = await db.countChunks(); - - if (chunkIndex !== 10) { - setDownloadingParams(chunkIndex * 10 + 0.0001); - - for (let i = chunkIndex; i < 10; i++) { - const chunkURI = `${process.env.NEXT_PUBLIC_NOVA_BUCKET_URL}/params_${i}.gz`; - const chunk = await fetch(chunkURI, { - headers: { "Content-Type": "application/x-binary" }, - }).then(async (res) => await res.blob()); - await db.addChunk(i, chunk); - setDownloadingParams((prev) => prev + 10); - } - setTimeout(() => { - setDownloadingParams(0); - }, 500); - } - }; - - const handleVerify = async () => { - setIsVerifying(true); - setIsDownloadingParams(true); - await downloadParams(); - setIsDownloadingParams(false); - // spawn worker if proof exists for type - let requests = []; - - const verifyProof = async ( - proof: Blob, - numVerified: number, - treeType: TreeType - ) => { - const success = await verify(proof, numVerified, treeType); - if (success) setVerifyingCount((prev) => prev + 1); - }; - - if (userProofs.attendee) { - requests.push( - verifyProof( - userProofs.attendee.proof, - userProofs.attendee.count, - TreeType.Attendee - ) - ); - setNumToVerify((prev) => prev + 1); - } - if (userProofs.speaker) { - requests.push( - verifyProof( - userProofs.speaker.proof, - userProofs.speaker.count, - TreeType.Speaker - ) - ); - setNumToVerify((prev) => prev + 1); - } - if (userProofs.talk) { - requests.push( - verifyProof(userProofs.talk.proof, userProofs.talk.count, TreeType.Talk) - ); - setNumToVerify((prev) => prev + 1); - } - await Promise.all(requests); - setVerified(true); - - setIsVerifying(false); - }; - - const stats = useMemo(() => { - if (!user) return []; - const attendeeCount = user.attendeeProofCount ?? 0; - const attendeeText = `Connection${attendeeCount === 1 ? "" : "s"} made`; - const speakerCount = user.speakerProofCount ?? 0; - const speakerText = `Speaker${speakerCount === 1 ? "" : "s"} met`; - const talkCount = user.talkProofCount ?? 0; - const talkText = `Talk${talkCount === 1 ? "" : "s"} attended`; - return [ - { count: talkCount, title: talkText }, - { count: attendeeCount, title: attendeeText }, - { count: speakerCount ?? 0, title: speakerText }, - ]; - }, [user]); - - useEffect(() => { - if (!isLoaded || !user) return; - const stage = new window.createjs.Stage( - document.getElementById("propic-modal") - ); - const center_x = stage.canvas.width / 2; - const center_y = stage.canvas.height / 2; - createFlower(stage, user.userPublicKey, center_x, center_y, flowerSize / 4); - }, [isLoaded, user]); - - useEffect(() => { - (async () => { - // Check if proof id exists or not - const response = await fetchWithRetry( - `/api/folding/proof?proofUuid=${id}` - ); - if (response.ok) { - // get proof data for the user - const foldingData: GetFoldingProofResponse = await response.json(); - // get blobs for each proof type - const proofBlobs: Map = new Map(); - const getProof = async (uri: string, treeType: TreeType) => { - const proof = await fetchWithRetry(uri).then( - async (res) => await res.blob() - ); - proofBlobs.set(treeType, proof); - }; - let requests = []; - if (foldingData.attendeeProofCount && foldingData.attendeeProofUrl) - requests.push( - getProof(foldingData.attendeeProofUrl, TreeType.Attendee) - ); - if (foldingData.speakerProofCount && foldingData.speakerProofUrl) - requests.push( - getProof(foldingData.speakerProofUrl, TreeType.Speaker) - ); - if (foldingData.talkProofCount && foldingData.talkProofUrl) - requests.push(getProof(foldingData.talkProofUrl, TreeType.Talk)); - await Promise.all(requests); - - // set the user data - const data: UserProofs = {}; - const attendeeBlob = proofBlobs.get(TreeType.Attendee); - if (attendeeBlob) { - data.attendee = { - proof: attendeeBlob, - count: foldingData.attendeeProofCount!, - }; - } - const speakerBlob = proofBlobs.get(TreeType.Speaker); - if (speakerBlob) { - data.speaker = { - proof: speakerBlob, - count: foldingData.speakerProofCount!, - }; - } - const talkBlob = proofBlobs.get(TreeType.Talk); - if (talkBlob) { - data.talk = { - proof: talkBlob, - count: foldingData.talkProofCount!, - }; - } - setUserProofs(data); - - setUser(foldingData); - } else { - const { error } = await response.json(); - if (error === "Proof not found") { - // TODO: User not found - } - } - setFetchingProof(false); - })(); - }, []); - - const isProofLoading = fetchingProof || !user; - - if (isProofLoading) { - return ( -
- -
Loading proof...
-
- ); - } - - return ( -
-
- -
-
-
- -
-
-
{user?.userName}
-
- went to the SigSing workshop -
-
-
- {stats.map((stat, index) => ( -
-
- {stat.count} -
-
{stat.title}
-
- ))} -
-
- {dowloadingParams ? ( -
-
- Downloading params {Math.floor(dowloadingParams) / 10} of 10 -
-
- -
-
- ) : verified ? ( -
-
- -
Valid proof
-
- - How was this proof generated? - - - View proof - -
- ) : ( -
- {isVerifying ? ( - <> - {isDownloadingParams ? ( -
-
- Downloading public params {dowloadingParams / 10} of{" "} - {10}... -
-
- -
-
- ) : ( -
-
- Verifying proof {verifyingCount} of {numToVerify}... -
-
- -
-
- )} - - ) : ( - - )} -
- )} -
-
-
- ); -}; - -Folded.getInitialProps = () => { - return { showFooter: false, showHeader: false }; -}; - -export default Folded; diff --git a/src/pages/folded/proof/[id].tsx b/src/pages/folded/proof/[id].tsx deleted file mode 100644 index 7a2cb046..00000000 --- a/src/pages/folded/proof/[id].tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { Icons } from '@/components/Icons'; -import { useEffect, useState } from 'react'; -import { useParams } from 'next/navigation'; -import { GetFoldingProofResponse } from '@/pages/api/folding/proof'; -import { Spinner } from '@/components/Spinner'; -import { Button } from '@/components/Button'; - -const ProofDownload = (): JSX.Element => { - const { id } = useParams(); - const [fetchingProof, setFetchingProof] = useState(true); - const [foldingResponse, setFoldingResponse] = - useState(null); - - useEffect(() => { - (async () => { - try { - const response = await fetch(`/api/folding/proof?proofUuid=${id}`); - const data = await response.json(); - setFoldingResponse(data); - } catch (err) { - console.log('Error: ', err); - } finally { - setFetchingProof(false); - } - })(); - }, [id]); - - if (!fetchingProof) { - return ( -
-
- -
-
- -
-
- ); - } - - return ( -
-
- -
-
- {foldingResponse?.attendeeProofUrl && ( - - )} - {foldingResponse?.speakerProofUrl && ( - - )} - {foldingResponse?.talkProofUrl && ( - - )} -
-
- ); -}; - -ProofDownload.getInitialProps = () => { - return { showFooter: false, showHeader: false }; -}; - -export default ProofDownload; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 0f388364..797386ea 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,5 +1,4 @@ import { Icons } from "@/components/Icons"; -import { ProfileImage } from "@/components/ProfileImage"; import { TabsProps, Tabs } from "@/components/Tabs"; import { Card } from "@/components/cards/Card"; import { ListLayout } from "@/layouts/ListLayout"; @@ -32,7 +31,7 @@ import { IconCircle } from "@/components/IconCircle"; import { NoResultContent } from "@/components/NoResultContent"; import { classed } from "@tw-classed/react"; import { logClientEvent } from "@/lib/client/metrics"; -import { useWorker } from "@/hooks/useWorker"; +import { toast } from "sonner"; interface LinkCardProps { name: string; @@ -211,8 +210,6 @@ export default function Social() { const [tabsItems, setTabsItems] = useState(); const [isLoading, setLoading] = useState(false); - const { work, folding } = useWorker(); - const isMenuOpen = getState().isMenuOpen ?? false; // Helper function to compute data needed to populate tabs @@ -285,7 +282,7 @@ export default function Social() {
{activities.length === 1 && ( - Get started by tapping badges and talk stickers! + Get started by tapping rings and NFC stickers! )} {activities.length > 1 && @@ -322,7 +319,7 @@ export default function Social() {
{contactUsersList.length === 0 && ( - Tap badges to share socials and connect with others! + Tap rings to share socials and connect with others! )} {contactUsersList.length !== 0 && @@ -335,13 +332,17 @@ export default function Social() { {users.map((user, index) => { const { name, inTs, bio } = user; const date = inTs ? formatDate(inTs) : "-"; + const bioMatch = bio?.match(/^@(.*)\|/); + const actualBio = bioMatch + ? bio?.substring(bioMatch[0].length) + : bio; return ( ); @@ -395,6 +396,7 @@ export default function Social() { authToken.expiresAt < new Date() ) { setLoading(false); + toast.error("Need to be registered to view home page."); router.push("/register"); return; } @@ -415,13 +417,6 @@ export default function Social() { } catch (error) { console.error("Failed to load messages upon page reload:", error); } - - // Begin running folding worker on refresh - if (!folding) { - const users = getUsers(); - const locationSignatures = getLocationSignatures(); - work(Object.values(users), Object.values(locationSignatures)); - } } } @@ -476,18 +471,16 @@ export default function Social() {
-
-
-
-

- {profile?.displayName} -

- - {numConnections === 1 - ? `1 contact` - : `${numConnections} contacts`} - -
+
+
+

+ {profile?.displayName} +

+ + {numConnections === 1 + ? `1 contact` + : `${numConnections} contacts`} +
- + +
+ + { + e.preventDefault(); + handlePasswordLogin(); + }} + > + Login with password instead + +
); } else if (displayState === DisplayState.PASSWORD) { return ( setPassword(e.target.value)} /> - - + +
+ + + Login with passkey instead + +
); } diff --git a/src/pages/mpc/fruits.tsx b/src/pages/mpc/fruits.tsx new file mode 100644 index 00000000..bbbc4ca4 --- /dev/null +++ b/src/pages/mpc/fruits.tsx @@ -0,0 +1,516 @@ +import React, { useEffect, useState } from "react"; +import { AppBackHeader } from "@/components/AppHeader"; +import { Button } from "@/components/Button"; +/* @ts-ignore */ +import { JIFFClient, JIFFClientBigNumber } from "jiff-mpc"; +import { toast } from "sonner"; +import { BigNumber } from "bignumber.js"; +import Rating from "@mui/material/Rating"; +import { Input } from "@/components/Input"; +import { Room, RoomMember } from "@prisma/client"; +import { Spinner } from "@/components/Spinner"; +import { classed } from "@tw-classed/react"; + +enum OutputState { + NOT_CONNECTED, + AWAITING_OTHER_PARTIES_CONNECTION, + CONNECTED, + AWAITING_OTHER_PARTIES_INPUTS, + COMPUTING, + SHOW_RESULTS, + ERROR, +} + +const Title = classed.h3("block font-sans text-iron-950", { + variants: { + size: { + small: "text-base leading-1 font-semibold", + medium: "text-[21px] leading-5 font-medium", + }, + }, + defaultVariants: { + size: "small", + }, +}); + +const Description = classed.span("text-md text-iron-600 leading-5"); + +const fruits = [ + "Apple", + "Banana", + "Cherry", + "Date", + "Elderberry", + "Fig", + "Grape", + "Honeydew", + "Kiwi", + "Lemon", +]; + +export default function Fruits() { + const [createRoomName, setCreateRoomName] = useState(); + const [createRoomPassword, setCreateRoomPassword] = useState(""); + const [createRoomPartyCount, setCreateRoomPartyCount] = useState(); + const [hasCreatedRoom, setHasCreatedRoom] = useState(false); + const [roomName, setRoomName] = useState(); + const [allRooms, setAllRooms] = useState< + Record + >({}); + const [jiffClient, setJiffClient] = useState(null); + const [ratings, setRatings] = useState( + Array(fruits.length).fill(0) + ); + const [output, setOutput] = useState(OutputState.NOT_CONNECTED); + const [avgResults, setAvgResults] = useState([]); + const [stdResults, setStdResults] = useState([]); + const [loadingRooms, setLoadingRooms] = useState(true); + const [loadingCreateRoom, setLoadingCreateRoom] = useState(false); + const [loadingJoin, setLoadingJoin] = useState(); + + useEffect(() => { + const fetchRooms = async () => { + const response = await fetch("/api/mpc/get_all_rooms"); + const { rooms } = await response.json(); + const roomsMapping = rooms.reduce( + (acc: Record, room: Room) => { + acc[room.name] = room; + return acc; + }, + {} + ); + setCreateRoomName(undefined); + setCreateRoomPartyCount(undefined); + setCreateRoomPassword(""); + setLoadingRooms(false); + setAllRooms(roomsMapping); + }; + fetchRooms(); + }, []); + + const handleCreateRoom = async () => { + setLoadingCreateRoom(true); + if (!createRoomName || !createRoomPartyCount) { + setLoadingCreateRoom(false); + return toast.error("Please fill in all fields"); + } + + const newRoomName = "FRUIT-" + createRoomName; + + if (createRoomPartyCount < 2) { + setLoadingCreateRoom(false); + return toast.error("Party count must be at least 2"); + } + + if (/\s/.test(createRoomName)) { + setLoadingCreateRoom(false); + return toast.error("Room name cannot contain whitespace"); + } + + const response = await fetch("/api/mpc/create_room", { + method: "POST", + body: JSON.stringify({ + name: newRoomName, + numParties: createRoomPartyCount, + password: createRoomPassword, + }), + }); + + if (response.ok) { + const response = await fetch("/api/mpc/get_all_rooms"); + const { rooms } = await response.json(); + const roomsMapping = rooms.reduce( + (acc: Record, room: Room) => { + acc[room.name] = room; + return acc; + }, + {} + ); + setAllRooms(roomsMapping); + setRoomName(newRoomName); + setHasCreatedRoom(true); + connect(newRoomName, createRoomPartyCount); + toast.success("Room created successfully"); + } else { + const { error } = await response.json(); + toast.error(error); + } + setLoadingCreateRoom(false); + }; + + const connect = (roomName: string, numParties: number) => { + if (!roomName || numParties < 2) { + toast.error("Please enter a valid room name and party count."); + return; + } + + const client = new JIFFClient( + process.env.NODE_ENV === "development" + ? "http://localhost:8080" + : "https://mpc-fruits.onrender.com", + roomName, + { + autoConnect: false, + party_count: numParties, + crypto_provider: true, + // @ts-ignore + onError: (_, error) => { + console.error(error); + if ( + error.includes("Maximum parties capacity reached") || + error.includes("contradicting party count") + ) { + setLoadingJoin(undefined); + setLoadingCreateRoom(false); + toast.error("Computation is full. Try another computation ID."); + } + setOutput(OutputState.ERROR); + }, + onConnect: () => { + console.log("Connected to server"); + setOutput(OutputState.CONNECTED); + }, + } + ); + + client.apply_extension(JIFFClientBigNumber, {}); + client.connect(); + setOutput(OutputState.AWAITING_OTHER_PARTIES_CONNECTION); + setJiffClient(client); + }; + + const submit = async () => { + if (ratings.some((rating) => rating < 1 || rating > 5)) { + toast.error("All ratings must be between 1 and 5."); + return; + } + + setOutput(OutputState.AWAITING_OTHER_PARTIES_INPUTS); + + if (jiffClient) { + console.log(`Beginning MPC with ratings ${ratings}`); + let shares = await jiffClient.share_array(ratings); + console.log("Shares: ", shares); + setOutput(OutputState.COMPUTING); + + // Start average computation + const startAverageTime = Date.now(); + + let sumShares: any[] = []; + for (let i = 1; i <= jiffClient.party_count; i++) { + for (let j = 0; j < fruits.length; j++) { + if (i === 1) { + sumShares.push(shares[i][j]); + } else { + sumShares[j] = sumShares[j].sadd(shares[i][j]); + } + } + } + + // for (let k = 0; k < sumShares.length; k++) { + // sumShares[k] = sumShares[k].cdiv(jiffClient.party_count); + // } + // console.log("Averaged Sum Shares: ", sumShares); + + const sumResults = await Promise.all( + sumShares.map((share: any) => jiffClient.open(share)) + ); + console.log("Sum Results: ", sumResults); + const averageResults = sumResults.map( + (result: BigNumber) => result.toNumber() / jiffClient.party_count + ); + console.log("Average Results: ", averageResults); + + const averageTime = Date.now() - startAverageTime; + console.log("Average Time: ", averageTime); + + // Start standard deviation computation + const startStdTime = Date.now(); + + let squaredSumShares: any[] = []; + for (let i = 0; i < sumShares.length; i++) { + squaredSumShares.push(sumShares[i].smult(sumShares[i])); + } + + let sumOfSquaresShares: any[] = []; + for (let i = 1; i <= jiffClient.party_count; i++) { + for (let j = 0; j < sumShares.length; j++) { + const shareSquared = shares[i][j].smult(shares[i][j]); + if (i === 1) { + sumOfSquaresShares.push(shareSquared); + } else { + sumOfSquaresShares[j] = sumOfSquaresShares[j].sadd(shareSquared); + } + } + } + for (let k = 0; k < sumOfSquaresShares.length; k++) { + sumOfSquaresShares[k] = sumOfSquaresShares[k].cmult( + jiffClient.party_count + ); + } + + let stdResultShares: any[] = []; + for (let i = 0; i < sumShares.length; i++) { + const squaredSum = squaredSumShares[i]; + const sumOfSquares = sumOfSquaresShares[i]; + const stdResult = sumOfSquares.ssub(squaredSum); + stdResultShares.push(stdResult); + } + + const rawStdResults = await Promise.all( + stdResultShares.map((diff: any) => jiffClient.open(diff)) + ); + const stdResults = rawStdResults.map((result: BigNumber) => + Math.sqrt( + result.toNumber() / + (jiffClient.party_count * (jiffClient.party_count - 1)) + ) + ); + console.log("Std Results:", stdResults); + + const stdTime = Date.now() - startStdTime; + console.log("Std Time: ", stdTime); + + setAvgResults(averageResults); + setStdResults(stdResults); + setOutput(OutputState.SHOW_RESULTS); + toast.success( + `MPC runtime: ${averageTime}ms for average, ${stdTime}ms for standard deviation` + ); + } + }; + + const getButtonDisplay = (): string => { + switch (output) { + case OutputState.NOT_CONNECTED: + return "Connect"; + case OutputState.AWAITING_OTHER_PARTIES_CONNECTION: + return "Awaiting other parties connection..."; + case OutputState.CONNECTED: + return "Submit ratings to proceed!"; + case OutputState.AWAITING_OTHER_PARTIES_INPUTS: + return "Awaiting other parties inputs..."; + case OutputState.COMPUTING: + return "Computing..."; + case OutputState.SHOW_RESULTS: + return "The fruits have been rated by the crowd!"; + case OutputState.ERROR: + return "Error - please try again"; + } + }; + + if (roomName) { + return ( +
+ { + if (output !== OutputState.SHOW_RESULTS) { + let choice = window.confirm( + "Are you sure you want to leave? This will end the computation for the entire room." + ); + if (choice) { + setRoomName(undefined); + } + } else { + setRoomName(undefined); + } + }} + /> + +
+
+ + 🍎 Rate fruits + + {`Rate some fruits with your friends, discover how aligned you + are without revealing any specific votes.`} +
+
+ + {roomName.slice(6)} + + {getButtonDisplay()} +
+
+ {output === OutputState.CONNECTED && ( +
+ {fruits.map((fruit, index) => ( +
+ + { + const newRatings = [...ratings]; + newRatings[index] = newValue || 0; + setRatings(newRatings); + }} + max={5} + /> +
+ ))} + +
+ )} + {output === OutputState.SHOW_RESULTS && ( +
+
+ {fruits + .map((fruit, index) => ({ + fruit, + rating: avgResults[index], + })) + .sort((a, b) => b.rating - a.rating) + .map(({ fruit, rating }, index) => ( +
+ {`${fruit} `} + + {`(${rating.toFixed(1)}, std: ${stdResults[ + fruits.indexOf(fruit) + ].toFixed(2)})`} +
+ ))} +
+
+ )} +
+
+
+ ); + } + + return ( +
+ + +
+
+ + 🍎 Rate fruits + +
+ + {`Rate some fruits with your friends, discover how aligned you + are without revealing any specific votes.`} + + + {`Find a group of 3 or more people + and set your party size accordingly.`} + +
+
+
+
+ Join a room +
+ {loadingRooms ? ( + + ) : Object.values(allRooms).filter( + (room) => + room.members.length < room.numParties && + room.name.startsWith("FRUIT-") + ).length === 0 ? ( + + No rooms available + + ) : ( + Object.values(allRooms).map((room) => + room.members.length < room.numParties && + room.name.startsWith("FRUIT-") ? ( +
+ + {room.name.slice(6)} ({room.members.length}/ + {room.numParties} members) + +
+ +
+
+ ) : ( + <> + ) + ) + )} +
+
+
+
+ Create new room +
+ setCreateRoomName(e.target.value)} + /> + + e.target.value + ? setCreateRoomPartyCount(Number(e.target.value)) + : setCreateRoomPartyCount(undefined) + } + /> + setCreateRoomPassword(e.target.value)} + /> + +
+
+
+
+ ); +} + +Fruits.getInitialProps = () => { + return { showHeader: false, showFooter: false }; +}; diff --git a/src/pages/mpc/index.tsx b/src/pages/mpc/index.tsx new file mode 100644 index 00000000..3aa984e8 --- /dev/null +++ b/src/pages/mpc/index.tsx @@ -0,0 +1,80 @@ +import { Placeholder } from "@/components/placeholders/Placeholder"; +import { QuestCard } from "@/components/cards/QuestCard"; +import { LoadingWrapper } from "@/components/wrappers/LoadingWrapper"; +import { useFetchQuests } from "@/hooks/useFetchQuests"; + +import Link from "next/link"; +import React, { useMemo, useRef, useState } from "react"; + +import { QuestWithCompletion } from "@/types"; +import { getPinnedQuest } from "@/lib/client/localStorage/questPinned"; +import { useQuestRequirements } from "@/hooks/useQuestRequirements"; +import { Card } from "@/components/cards/Card"; + +export default function MPCPage() { + return ( +
+ + Discover connections with your social graph, using MPC for efficient + results while maintaining your data privacy. + + + + +
+
+
+ + 🍎 Rate fruits + + + Rate some fruits with your friends, discover how aligned you + are. Computes average and standard deviation without revealing + individual ratings. + +
+
+
+
+ + + + +
+
+
+ + 3️⃣ Top 3 Talks + + + {`Rate some talks, only reveal the top 3 after everyone votes. + Learn about which ones were most successful without putting + down other speakers.`} + +
+
+
+
+ + + + +
+
+
+ + ✨ Karma Calculator + + + {`Update each other's karma privately, only reveal the net karma + given/received at the end of the round. Inspired by Barry & + CC.`} + +
+
+
+
+ +
+ ); +} diff --git a/src/pages/mpc/karma.tsx b/src/pages/mpc/karma.tsx new file mode 100644 index 00000000..57988b18 --- /dev/null +++ b/src/pages/mpc/karma.tsx @@ -0,0 +1,488 @@ +import React, { useEffect, useState } from "react"; +import { AppBackHeader } from "@/components/AppHeader"; +import { Button } from "@/components/Button"; +/* @ts-ignore */ +import { JIFFClient, JIFFClientNegative, JIFFClientBigNumber } from "jiff-mpc"; +import { toast } from "sonner"; +import { BigNumber } from "bignumber.js"; +import Slider from "@mui/material/Slider"; +import { Input } from "@/components/Input"; +import { Room, RoomMember } from "@prisma/client"; +import { Spinner } from "@/components/Spinner"; +import { classed } from "@tw-classed/react"; + +enum OutputState { + NOT_CONNECTED, + AWAITING_OTHER_PARTIES_CONNECTION, + CONNECTED, + AWAITING_OTHER_PARTIES_INPUTS, + COMPUTING, + SHOW_RESULTS, + ERROR, +} + +const Title = classed.h3("block font-sans text-iron-950", { + variants: { + size: { + small: "text-base leading-1 font-semibold", + medium: "text-[21px] leading-5 font-medium", + }, + }, + defaultVariants: { + size: "small", + }, +}); + +const Description = classed.span("text-md text-iron-600 leading-5"); + +export default function Karma() { + const [createRoomName, setCreateRoomName] = useState(); + const [displayName, setDisplayName] = useState(""); + const [createRoomPartyCount, setCreateRoomPartyCount] = useState(); + const [roomName, setRoomName] = useState(); + const [allRooms, setAllRooms] = useState< + Record + >({}); + const [jiffClient, setJiffClient] = useState(null); + const [output, setOutput] = useState(OutputState.NOT_CONNECTED); + const [karmaResults, setKarmaResults] = useState([]); + const [loadingRooms, setLoadingRooms] = useState(true); + const [loadingCreateRoom, setLoadingCreateRoom] = useState(false); + const [loadingJoin, setLoadingJoin] = useState(); + + const [names, setNames] = useState([]); + const [karmas, setKarmas] = useState([]); + + useEffect(() => { + const fetchRooms = async () => { + const response = await fetch("/api/mpc/get_all_rooms"); + const { rooms } = await response.json(); + const roomsMapping = rooms.reduce( + (acc: Record, room: Room) => { + acc[room.name] = room; + return acc; + }, + {} + ); + setCreateRoomName(undefined); + setCreateRoomPartyCount(undefined); + setDisplayName(""); + setLoadingRooms(false); + setAllRooms(roomsMapping); + }; + fetchRooms(); + }, []); + + const handleCreateRoom = async () => { + setLoadingCreateRoom(true); + if (!createRoomName || !createRoomPartyCount) { + setLoadingCreateRoom(false); + return toast.error("Please fill in all fields"); + } + + const newRoomName = "KARMA-" + createRoomName; + + if (createRoomPartyCount < 2) { + setLoadingCreateRoom(false); + return toast.error("Party count must be at least 2"); + } + + if (/\s/.test(createRoomName)) { + setLoadingCreateRoom(false); + return toast.error("Room name cannot contain whitespace"); + } + + const response = await fetch("/api/mpc/create_room", { + method: "POST", + body: JSON.stringify({ + name: newRoomName, + numParties: createRoomPartyCount, + password: "", + displayName, + }), + }); + + if (response.ok) { + const response = await fetch("/api/mpc/get_all_rooms"); + const { rooms } = await response.json(); + const roomsMapping = rooms.reduce( + (acc: Record, room: Room) => { + acc[room.name] = room; + return acc; + }, + {} + ); + setAllRooms(roomsMapping); + setRoomName(newRoomName); + connect(newRoomName, roomsMapping[newRoomName].id, createRoomPartyCount); + toast.success("Room created successfully"); + } else { + const { error } = await response.json(); + toast.error(error); + } + setLoadingCreateRoom(false); + }; + + const connect = (roomName: string, roomId: number, numParties: number) => { + if (!roomName || numParties < 2) { + toast.error("Please enter a valid room name and party count."); + return; + } + + const client = new JIFFClient( + process.env.NODE_ENV === "development" + ? "http://localhost:8080" + : "https://mpc-fruits.onrender.com", + roomName, + { + autoConnect: false, + party_count: numParties, + crypto_provider: true, + // @ts-ignore + onError: (_, error) => { + console.error(error); + if ( + error.includes("Maximum parties capacity reached") || + error.includes("contradicting party count") + ) { + setLoadingJoin(undefined); + setLoadingCreateRoom(false); + toast.error("Computation is full. Try another computation ID."); + } + setOutput(OutputState.ERROR); + }, + onConnect: async () => { + console.log("Connected to server"); + + const response = await fetch( + `/api/mpc/get_room_names?roomId=${roomId}` + ); + const { roomNames } = await response.json(); + if (!roomNames) { + setOutput(OutputState.ERROR); + return; + } + console.log(roomNames); + setNames(roomNames); + setKarmas(new Array(numParties).fill(0)); + setOutput(OutputState.CONNECTED); + }, + } + ); + + client.apply_extension(JIFFClientBigNumber, {}); + client.apply_extension(JIFFClientNegative, {}); + client.connect(); + setOutput(OutputState.AWAITING_OTHER_PARTIES_CONNECTION); + setJiffClient(client); + }; + + const submit = async () => { + if (!karmas) { + toast.error("Please update everyone's karma."); + return; + } + + setOutput(OutputState.AWAITING_OTHER_PARTIES_INPUTS); + + if (karmas.length !== jiffClient.party_count) { + toast.error("Error with karma data. Please restart."); + return; + } + + if (jiffClient) { + console.log(`Beginning MPC with karmas ${karmas}`); + let shares = await jiffClient.share_array(karmas); + console.log("Shares: ", shares); + setOutput(OutputState.COMPUTING); + + // Start average computation + const startKarmaTime = Date.now(); + + let rowSumShares: any[] = []; + let colSumShares: any[] = []; + + for (let j = 0; j < jiffClient.party_count; j++) { + for (let i = 1; i <= jiffClient.party_count; i++) { + if (j === 0) { + colSumShares.push(shares[i][j]); + } else { + colSumShares[i - 1] = colSumShares[i - 1].sadd(shares[i][j]); + } + } + } + + for (let i = 1; i <= jiffClient.party_count; i++) { + for (let j = 0; j < jiffClient.party_count; j++) { + if (i === 1) { + rowSumShares.push(shares[i][j]); + } else { + rowSumShares[j] = rowSumShares[j].sadd(shares[i][j]); + } + } + } + + const rowResults = await Promise.all( + rowSumShares.map((share: any) => jiffClient.open(share)) + ); + const colResults = await Promise.all( + colSumShares.map((share: any) => jiffClient.open(share)) + ); + console.log("Row Results: ", rowResults); + console.log("Col Results: ", colResults); + + const karmaTime = Date.now() - startKarmaTime; + console.log("Karma Time: ", karmaTime); + + const karmaResults = rowResults.map( + (rowResult: any, index: number) => rowResult - colResults[index] + ); + setKarmaResults(karmaResults); + setOutput(OutputState.SHOW_RESULTS); + toast.success(`MPC runtime: ${karmaTime}ms`); + } + }; + + const getButtonDisplay = (): string => { + switch (output) { + case OutputState.NOT_CONNECTED: + return "Connect"; + case OutputState.AWAITING_OTHER_PARTIES_CONNECTION: + return "Awaiting other parties connection..."; + case OutputState.CONNECTED: + return "Submit ratings to proceed!"; + case OutputState.AWAITING_OTHER_PARTIES_INPUTS: + return "Awaiting other parties inputs..."; + case OutputState.COMPUTING: + return "Computing..."; + case OutputState.SHOW_RESULTS: + return "Karma has been assigned."; + case OutputState.ERROR: + return "Error - please try again"; + } + }; + + if (roomName) { + return ( +
+ { + if (output !== OutputState.SHOW_RESULTS) { + let choice = window.confirm( + "Are you sure you want to leave? This will end the computation for the entire room." + ); + if (choice) { + setRoomName(undefined); + } + } else { + setRoomName(undefined); + } + }} + /> + +
+
+ + ✨ Karma Calculator + + + {`Update each other's karma privately, only reveal the net karma + given/received at the end of the round. Inspired by Barry & CC.`} + +
+
+ + {roomName.slice(6)} + + {getButtonDisplay()} +
+
+ {output === OutputState.CONNECTED && ( +
+ {names.map((name, index) => ( +
+ +
+ { + const newRatings = [...karmas]; + newRatings[index] = Array.isArray(newValue) + ? newValue[0] || 0 + : newValue || 0; + setKarmas(newRatings); + }} + min={-100} + max={100} + marks={[ + { value: -100, label: "-100" }, + { value: 0, label: "0" }, + { value: 100, label: "100" }, + ]} + valueLabelDisplay="auto" + /> +
+
+ ))} + +
+ )} + {output === OutputState.SHOW_RESULTS && ( +
+
+ {names + .map((name, index) => ({ + name, + karma: karmaResults[index], + })) + .sort((a, b) => b.karma - a.karma) + .map(({ name, karma }, index) => ( +
+ {`${name}, Karma: ${karma}`} +
+ ))} +
+
+ )} +
+
+
+ ); + } + + return ( +
+ + +
+
+ + ✨ Karma Calculator + +
+ + {`Update each other's karma privately, only reveal the net karma + given/received at the end of the round. Inspired by Barry & CC.`} + + + {`Find a group of 3 or more people + and set your party size accordingly.`} + +
+
+
+
+ Join a room +
+ {loadingRooms ? ( + + ) : Object.values(allRooms).filter( + (room) => + room.members.length < room.numParties && + room.name.startsWith("KARMA-") + ).length === 0 ? ( + + No rooms available + + ) : ( + Object.values(allRooms).map((room) => + room.members.length < room.numParties && + room.name.startsWith("KARMA-") ? ( +
+ + {room.name.slice(6)} ({room.members.length}/ + {room.numParties} members) + +
+ +
+
+ ) : ( + <> + ) + ) + )} +
+
+
+
+ Create new room +
+ setCreateRoomName(e.target.value)} + /> + + e.target.value + ? setCreateRoomPartyCount(Number(e.target.value)) + : setCreateRoomPartyCount(undefined) + } + /> + setDisplayName(e.target.value)} + /> + +
+
+
+
+ ); +} + +Karma.getInitialProps = () => { + return { showHeader: false, showFooter: false }; +}; diff --git a/src/pages/mpc/talks.tsx b/src/pages/mpc/talks.tsx new file mode 100644 index 00000000..cf171c43 --- /dev/null +++ b/src/pages/mpc/talks.tsx @@ -0,0 +1,509 @@ +import React, { useEffect, useState } from "react"; +import { AppBackHeader } from "@/components/AppHeader"; +import { Button } from "@/components/Button"; +/* @ts-ignore */ +import { JIFFClient, JIFFClientBigNumber } from "jiff-mpc"; +import { toast } from "sonner"; +import Rating from "@mui/material/Rating"; +import { Input } from "@/components/Input"; +import { getAuthToken } from "@/lib/client/localStorage"; +import { Room, RoomMember } from "@prisma/client"; +import { Spinner } from "@/components/Spinner"; +import { classed } from "@tw-classed/react"; + +enum OutputState { + NOT_CONNECTED, + AWAITING_OTHER_PARTIES_CONNECTION, + CONNECTED, + AWAITING_OTHER_PARTIES_INPUTS, + COMPUTING, + SHOW_RESULTS, + ERROR, +} + +const Title = classed.h3("block font-sans text-iron-950", { + variants: { + size: { + small: "text-base leading-1 font-semibold", + medium: "text-[21px] leading-5 font-medium", + }, + }, + defaultVariants: { + size: "small", + }, +}); + +const Description = classed.span("text-md text-iron-600 leading-5"); + +const fruits = [ + "Cypherpunk Mission-Driven Cryptography", + "Introduction to MPC", + "Introduction to FHE", + "Privacy-preserving Statistics", + "Oblivious Message Retrieval for Zcash", + "Encrypted Scholarship", + "mpz-play", + "Write a Circuit in TypeScript", + "Private Collaborative Research", + "Backpocket Multiplayer Vault Demo", +]; + +export default function Talks() { + const [createRoomName, setCreateRoomName] = useState(); + const [createRoomPassword, setCreateRoomPassword] = useState(""); + const [createRoomPartyCount, setCreateRoomPartyCount] = useState(); + const [hasCreatedRoom, setHasCreatedRoom] = useState(false); + const [roomName, setRoomName] = useState(); + const [allRooms, setAllRooms] = useState< + Record + >({}); + const [jiffClient, setJiffClient] = useState(null); + const [ratings, setRatings] = useState( + Array(fruits.length).fill(0) + ); + const [output, setOutput] = useState(OutputState.NOT_CONNECTED); + const [avgResults, setAvgResults] = useState([]); + const [stdResults, setStdResults] = useState([]); + const [loadingRooms, setLoadingRooms] = useState(true); + const [loadingCreateRoom, setLoadingCreateRoom] = useState(false); + const [loadingJoin, setLoadingJoin] = useState(); + + useEffect(() => { + const fetchRooms = async () => { + const response = await fetch("/api/mpc/get_all_rooms"); + const { rooms } = await response.json(); + const roomsMapping = rooms.reduce( + (acc: Record, room: Room) => { + acc[room.name] = room; + return acc; + }, + {} + ); + setCreateRoomName(undefined); + setCreateRoomPartyCount(undefined); + setCreateRoomPassword(""); + setLoadingRooms(false); + setAllRooms(roomsMapping); + }; + fetchRooms(); + }, []); + + const handleCreateRoom = async () => { + setLoadingCreateRoom(true); + if (!createRoomName || !createRoomPartyCount) { + setLoadingCreateRoom(false); + return toast.error("Please fill in all fields"); + } + + const newRoomName = "TALKS-" + createRoomName; + + if (createRoomPartyCount < 2) { + setLoadingCreateRoom(false); + return toast.error("Party count must be at least 2"); + } + + if (/\s/.test(createRoomName)) { + setLoadingCreateRoom(false); + return toast.error("Room name cannot contain whitespace"); + } + + const response = await fetch("/api/mpc/create_room", { + method: "POST", + body: JSON.stringify({ + name: newRoomName, + numParties: createRoomPartyCount, + password: createRoomPassword, + }), + }); + + if (response.ok) { + const response = await fetch("/api/mpc/get_all_rooms"); + const { rooms } = await response.json(); + const roomsMapping = rooms.reduce( + (acc: Record, room: Room) => { + acc[room.name] = room; + return acc; + }, + {} + ); + setAllRooms(roomsMapping); + setRoomName(newRoomName); + setHasCreatedRoom(true); + connect(newRoomName, createRoomPartyCount); + toast.success("Room created successfully"); + } else { + const { error } = await response.json(); + toast.error(error); + } + setLoadingCreateRoom(false); + }; + + const connect = (roomName: string, numParties: number) => { + if (!roomName || numParties < 2) { + toast.error("Please enter a valid room name and party count."); + return; + } + + const client = new JIFFClient( + process.env.NODE_ENV === "development" + ? "http://localhost:8080" + : "https://mpc-fruits.onrender.com", + roomName, + { + autoConnect: false, + party_count: numParties, + crypto_provider: true, + // @ts-ignore + onError: (_, error) => { + console.error(error); + if ( + error.includes("Maximum parties capacity reached") || + error.includes("contradicting party count") + ) { + setLoadingJoin(undefined); + setLoadingCreateRoom(false); + toast.error("Computation is full. Try another computation ID."); + } + setOutput(OutputState.ERROR); + }, + onConnect: () => { + console.log("Connected to server"); + setOutput(OutputState.CONNECTED); + }, + } + ); + + client.apply_extension(JIFFClientBigNumber, {}); + client.connect(); + setOutput(OutputState.AWAITING_OTHER_PARTIES_CONNECTION); + setJiffClient(client); + }; + + /** + * MPC sorts + */ + + const mpcBubbleSort = (arr: any[]) => { + for (let i = 0; i < 3; i++) { + for (let j = 0; j < arr.length - i - 1; j++) { + const a = arr[j]; + const b = arr[j + 1]; + const cmp = a.slt(b); + arr[j] = cmp.if_else(a, b); + arr[j + 1] = cmp.if_else(b, a); + } + } + + return arr; + }; + + function oddEvenSort(a: any, lo: any, n: any) { + if (n > 1) { + const m = Math.floor(n / 2); + oddEvenSort(a, lo, m); + oddEvenSort(a, lo + m, m); + oddEvenMerge(a, lo, n, 1); + } + } + + // lo: lower bound of indices, n: number of elements, r: step + function oddEvenMerge(a: any, lo: any, n: any, r: any) { + const m = r * 2; + if (m < n) { + oddEvenMerge(a, lo, n, m); + oddEvenMerge(a, lo + r, n, m); + + for (let i = lo + r; i + r < lo + n; i += m) { + compareExchange(a, i, i + r); + } + } else if (m === n) { + compareExchange(a, lo, lo + r); + } + } + + function compareExchange(a: any, i: any, j: any) { + if (j >= a.length || i >= a.length) { + return; + } + + const x = a[i]; + const y = a[j]; + + const cmp = x.lt(y); + a[i] = cmp.if_else(x, y); + a[j] = cmp.if_else(y, x); + } + + const submit = async () => { + if (ratings.some((rating) => rating < 1 || rating > 5)) { + toast.error("All ratings must be between 1 and 5."); + return; + } + + const newRatings = ratings.map((rating) => rating * 10); + + setOutput(OutputState.AWAITING_OTHER_PARTIES_INPUTS); + + if (jiffClient) { + console.log(`Beginning MPC with ratings ${newRatings}`); + let shares = await jiffClient.share_array(newRatings); + console.log("Shares: ", shares); + setOutput(OutputState.COMPUTING); + + // Start average computation + const startAverageTime = Date.now(); + + let sumShares: any[] = []; + for (let i = 1; i <= jiffClient.party_count; i++) { + for (let j = 0; j < fruits.length; j++) { + if (i === 1) { + sumShares.push(shares[i][j]); + } else { + sumShares[j] = sumShares[j].sadd(shares[i][j]); + } + } + } + + for (let j = 0; j < fruits.length; j++) { + sumShares[j] = sumShares[j].cadd(j); + } + + mpcBubbleSort(sumShares); + + const results = await Promise.all( + sumShares + .slice(-3) + .reverse() + .map((share: any) => jiffClient.open(share)) + ); + + const averageTime = Date.now() - startAverageTime; + console.log("Ranking Time: ", averageTime); + + setAvgResults(results); + setOutput(OutputState.SHOW_RESULTS); + toast.success(`MPC runtime: ${averageTime}`); + + console.log("ending", hasCreatedRoom); + } + }; + + const getButtonDisplay = (): string => { + switch (output) { + case OutputState.NOT_CONNECTED: + return "Connect"; + case OutputState.AWAITING_OTHER_PARTIES_CONNECTION: + return "Awaiting other parties connection..."; + case OutputState.CONNECTED: + return "Submit ratings to proceed!"; + case OutputState.AWAITING_OTHER_PARTIES_INPUTS: + return "Awaiting other parties inputs..."; + case OutputState.COMPUTING: + return "Computing..."; + case OutputState.SHOW_RESULTS: + return "Here are the top 3 talks!"; + case OutputState.ERROR: + return "Error - please try again"; + } + }; + + if (roomName) { + return ( +
+ { + if (output !== OutputState.SHOW_RESULTS) { + let choice = window.confirm( + "Are you sure you want to leave? This will end the computation for the entire room." + ); + if (choice) { + setRoomName(undefined); + } + } else { + setRoomName(undefined); + } + }} + /> + +
+
+ + {`📓 Find your group's top 3 talks`} + + {`Rate the workshop talks with your friends, and find out the top 3 most loved talks without revealing the ratings of any other ones!`} +
+
+ + {roomName.slice(6)} + + {getButtonDisplay()} +
+
+ {output === OutputState.CONNECTED && ( +
+ {fruits.map((fruit, index) => ( +
+ + { + const newRatings = [...ratings]; + newRatings[index] = newValue || 0; + setRatings(newRatings); + }} + max={5} + /> +
+ ))} + +
+ )} + {output === OutputState.SHOW_RESULTS && ( +
+ {avgResults.map((el, ind) => ( +
+ {`#${ind + 1} `} + {fruits[el % fruits.length]} +
+ ))} +
+ )} +
+
+
+ ); + } + + return ( +
+ + +
+
+ + {`📓 Find your group's top 3 talks`} + +
+ + {`Rate the workshop talks with your friends, and find out the top 3 most loved + talks without revealing the ratings of any other ones!`} + + + {`Find a group of 3 or more people + and set your party size accordingly.`} + +
+
+
+
+ Join a room +
+ {loadingRooms ? ( + + ) : Object.values(allRooms).filter( + (room) => + room.members.length < room.numParties && + room.name.startsWith("TALKS-") + ).length === 0 ? ( + + No rooms available + + ) : ( + Object.values(allRooms).map((room) => + room.members.length < room.numParties && + room.name.startsWith("TALKS-") ? ( +
+ + {room.name.slice(6)} ({room.members.length}/ + {room.numParties} members) + +
+ +
+
+ ) : ( + <> + ) + ) + )} +
+
+
+
+ Create new room +
+ setCreateRoomName(e.target.value)} + /> + + e.target.value + ? setCreateRoomPartyCount(Number(e.target.value)) + : setCreateRoomPartyCount(undefined) + } + /> + setCreateRoomPassword(e.target.value)} + /> + +
+
+
+
+ ); +} + +Talks.getInitialProps = () => { + return { showHeader: false, showFooter: false }; +}; diff --git a/src/pages/preview/index.tsx b/src/pages/preview/index.tsx new file mode 100644 index 00000000..7bd9d5f2 --- /dev/null +++ b/src/pages/preview/index.tsx @@ -0,0 +1,252 @@ +import { useRouter } from "next/router"; +import { useEffect, useState } from "react"; +import { + deleteAccountFromLocalStorage, + fetchUserByUUID, + getKeys, + getLocationSignatures, + getProfile, + getUsers, + User, +} from "@/lib/client/localStorage"; +import { + AppBackHeader, + AppHeader, + AppHeaderContent, +} from "@/components/AppHeader"; +import { Card } from "@/components/cards/Card"; +import Link from "next/link"; +import { classed } from "@tw-classed/react"; +import { labelStartWith, removeLabelStartWith } from "@/lib/shared/utils"; +import { InputWrapper } from "@/components/input/InputWrapper"; +import { ArtworkSnapshot } from "@/components/artwork/ArtworkSnapshot"; +import { Button } from "@/components/Button"; +import { supabase } from "@/lib/client/realtime"; +import { toast } from "sonner"; +import { generateSelfBitVector, psiBlobUploadClient } from "@/lib/client/psi"; +import init, { round1_js, round2_js, round3_js } from "@/lib/mp_psi/mp_psi"; +import { encryptOverlapComputedMessage } from "@/lib/client/jubSignal/overlapComputed"; +import { loadMessages } from "@/lib/client/jubSignalClient"; +import { CircleCard } from "@/components/cards/CircleCard"; +import { IconCircle } from "@/components/IconCircle"; +import { ProfilePicModal } from "@/components/modals/ProfilePicModal"; +import useSettings from "@/hooks/useSettings"; +import { Accordion } from "@/components/Accordion"; +import { cn, handleUsername } from "@/lib/client/utils"; +import { Icons } from "@/components/Icons"; +import { logClientEvent } from "@/lib/client/metrics"; + +const Label = classed.span("text-sm text-gray-12"); + +interface LinkCardProps { + label?: string; + href: string; + value?: string; +} + +const LinkCard = ({ label, value, href }: LinkCardProps) => { + return ( + +
+ {label} +
+ {handleUsername(value) ?? "N/A"} +
+ + ); +}; + +const UserProfilePage = () => { + const router = useRouter(); + const [user, setUser] = useState(); + const alreadyConnected = router?.query?.alreadyConnected === "true"; + const [showProfilePicModal, setShowProfilePicModal] = + useState(false); + const [isMenuOpen, setIsMenuOpen] = useState(false); + + const { pageWidth } = useSettings(); + + const handleSignout = () => { + deleteAccountFromLocalStorage(); + supabase.auth.signOut(); + window.location.href = "/register"; + }; + + useEffect(() => { + const fetchedUser = fetchUserByUUID("0"); + const bioMatch = fetchedUser?.bio?.match(/^@(.*)\|/); + const realTg = bioMatch ? bioMatch[1] : null; + const actualBio = bioMatch + ? fetchedUser?.bio?.substring(bioMatch[0].length) + : fetchedUser?.bio; + + if (fetchedUser) { + fetchedUser.bio = actualBio ?? undefined; + fetchedUser.fc = realTg ?? undefined; + } + + setUser(fetchedUser); + }, [router]); + + if (!user) { + return
User not found
; + } + + return ( +
+
+ {!isMenuOpen && ( + + )} +
+ {isMenuOpen && "Close"} + + +
+
+ + {alreadyConnected && ( +
+ + You have already connected with this user! + +
+ )} +
+
+
{ + logClientEvent("artShowProfilePicModal", {}); + setShowProfilePicModal(true); + }} + className="w-32 h-32 rounded-[4px] relative flex-shrink-0" + > + + +
+ +
+

{user.name}

+
+ {user.bio && ( + + {user.bio} + + )} +
+
+
+ {!user.inTs && ( +
+ + + If {user.name} taps you back and shares their socials, they will + appear here. + + +
+ )} + + {user?.isSpeaker && ( +
+ + Workshop Speaker + +
+ )} + + {user?.note && ( + + + {user?.note} + + + )} + + {(user.x || user.tg || user.fc) && ( + +
+ {(user.x?.length ?? 0) > 1 && ( + + )} + {(user.fc?.length ?? 0) > 1 && ( + + )} + {(user.tg?.length ?? 0) > 1 && ( + + )} +
+
+ )} + +
+
+ ); +}; + +UserProfilePage.getInitialProps = () => { + return { showHeader: false, showFooter: false }; +}; + +export default UserProfilePage; diff --git a/src/pages/proofs/index.tsx b/src/pages/proofs/index.tsx index 5825d3a3..d8b7410e 100644 --- a/src/pages/proofs/index.tsx +++ b/src/pages/proofs/index.tsx @@ -9,7 +9,6 @@ import React, { useMemo, useRef, useState } from "react"; import { QuestWithCompletion } from "@/types"; import { getPinnedQuest } from "@/lib/client/localStorage/questPinned"; import { useQuestRequirements } from "@/hooks/useQuestRequirements"; -import { FOLDED_MOCKS, FolderCard } from "@/components/cards/FoldedCard"; export default function QuestsPage() { const pinnedQuests = useRef>(getPinnedQuest()); @@ -42,8 +41,8 @@ export default function QuestsPage() { return (
- Post proofs about your SigSing experience on Twitter, while preserving - privacy over the people you met and talks you attended. + Post proofs about your Backpocket experience on Twitter, while + preserving privacy over the people you met and talks you attended. <> - {displayQuests.map( ( { diff --git a/src/pages/register.tsx b/src/pages/register.tsx index 10fcf248..84408afd 100644 --- a/src/pages/register.tsx +++ b/src/pages/register.tsx @@ -31,7 +31,6 @@ import { Card } from "@/components/cards/Card"; import { Spinner } from "@/components/Spinner"; import Link from "next/link"; import { logClientEvent } from "@/lib/client/metrics"; -import { useWorker } from "@/hooks/useWorker"; enum DisplayState { PASSKEY, @@ -53,18 +52,17 @@ export default function Register() { const [displayName, setDisplayName] = useState(); const [twitter, setTwitter] = useState("@"); const [telegram, setTelegram] = useState("@"); + const [realTg, setRealTg] = useState("@"); const [bio, setBio] = useState(); const [password, setPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState(""); - const [chipId, setChipId] = useState(); + const [chipEnc, setChipEnc] = useState(); const [loading, setLoading] = useState(false); const [isAccountReady, setIsAccountReady] = useState(false); - const { work } = useWorker(); - useEffect(() => { - if (router.query.chipId) { - setChipId(router.query.chipId as string); + if (router.query.chipEnc) { + setChipEnc(router.query.chipEnc as string); } }, [router.query]); @@ -261,13 +259,18 @@ export default function Register() { passwordSalt = generateSalt(); passwordHash = await hashPassword(password, passwordSalt); + let realBio = bio; + if (realTg !== "@") { + realBio = realTg + "|" + (bio ?? ""); + } + const response = await fetch("/api/register/create_account", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ - chipId, + chipEnc, displayName, encryptionPublicKey: publicKey, signaturePublicKey, @@ -278,7 +281,7 @@ export default function Register() { authPublicKey, twitter, telegram, - bio, + bio: realBio, }), }); @@ -387,9 +390,6 @@ export default function Register() { return; } - // Begin downloading params in web worker - work([], []); - setIsAccountReady(true); setLoading(false); }; @@ -397,15 +397,23 @@ export default function Register() { const StateContent: Record = { [DisplayState.PASSKEY]: ( +
+ Tap NFC rings to build your social graph, use MPC to query + efficiently. Set up socials to share, register to maintain an + encrypted backup of your data. +
+
+ } className="pt-4" onSubmit={handleSubmitWithPasskey} > setDisplayName(e.target.value)} @@ -426,7 +434,21 @@ export default function Register() { /> + setRealTg( + e.target.value.charAt(0) === "@" + ? e.target.value + : "@" + e.target.value + ) + } + /> + setBio(e.target.value)} /> - - - Register with password instead - +
+ +
+ + Register with password + +
+
+ router.push("/login")}> + Already have account + +
+
), [DisplayState.PASSWORD]: ( @@ -480,12 +511,17 @@ export default function Register() { value={confirmPassword} onChange={(e) => setConfirmPassword(e.target.value)} /> - - - Register with passkey instead - +
+ + + Register with passkey instead + +
), [DisplayState.CREATING]: ( @@ -521,12 +557,8 @@ export default function Register() { {" "} - Use 2PC+FHE to{" "} - - {" "} - discover who you{"'"}ve met in common - {" "} - with others. + Use MPC to discover common contacts and + to query your social graph.
diff --git a/src/pages/tap.tsx b/src/pages/tap.tsx index c9c9baf5..0d6e6a7b 100644 --- a/src/pages/tap.tsx +++ b/src/pages/tap.tsx @@ -15,6 +15,8 @@ import { fetchUserByUUID, getUsers, getLocationSignatures, + saveUsers, + User, } from "@/lib/client/localStorage"; import { encryptInboundTapMessage, @@ -45,8 +47,23 @@ export default function Tap() { const keys = getKeys(); if (!authToken || authToken.expiresAt < new Date() || !profile || !keys) { - toast.error("You must be logged in to connect"); - router.push("/register"); + let users: Record = {}; + users["0"] = { + pkId: person.pkId, + name: person.displayName, + encPk: person.encryptionPublicKey, + psiPkLink: person.psiPublicKeysLink, + x: person.twitter, + tg: person.telegram, + bio: person.bio, + sigPk: person.signaturePublicKey, + msg: person.signatureMessage, + sig: person.signature, + isSpeaker: person.isUserSpeaker, + inTs: new Date().toISOString(), + }; + saveUsers(users); + router.push("/preview"); return; } @@ -178,17 +195,17 @@ export default function Tap() { return mockRef ? `&mockRef=${mockRef}` : ""; }; - const handlePersonRegistration = (chipId: string) => { + const handlePersonRegistration = (chipEnc: string) => { const authToken = getAuthToken(); if (authToken) { router.push(`/friend_not_registered`); return; } - router.push(`/register?chipId=${chipId}`); + router.push(`/register?chipEnc=${chipEnc}`); }; - const handleLocationRegistration = (chipId: string) => { - router.push(`/register_location?chipId=${chipId}`); + const handleLocationRegistration = (chipEnc: string) => { + router.push(`/register_location?chipEnc=${chipEnc}`); }; const handleSigCardLocationRegistration = (signaturePublicKey: string) => { @@ -196,14 +213,7 @@ export default function Tap() { }; const handlePersonTap = async (person: PersonTapResponse) => { - const authToken = getAuthToken(); - if (!authToken || authToken.expiresAt < new Date()) { - // If user is not logged in, redirect to login - router.push("/register"); - // setPendingPersonTapResponse(person); - } else { - processPersonTap(person); - } + processPersonTap(person); }; const handleLocationTap = async (location: LocationTapResponse) => { @@ -218,8 +228,8 @@ export default function Tap() { }; // ----- HANDLE CMAC TAP ----- - const chipId = router.query.chipId as string; - if (!chipId) { + + if (!location.hash) { toast.error("Invalid tap! Please try again."); router.push("/"); return; @@ -227,53 +237,39 @@ export default function Tap() { logClientEvent("tapProcessNewTap", {}); - fetch(`/api/tap/plain?chipId=${chipId}`, { + const urlParams = new URLSearchParams(location.hash.slice(1)); + const chipEnc = urlParams.get("e"); + + if (!chipEnc) { + toast.error("Invalid tap! Please try again."); + router.push("/"); + } + + fetch(`/api/tap/plain?chipEnc=${chipEnc}`, { method: "GET", headers: { "Content-Type": "application/json", }, }) .then((response) => { - if (!response.ok) - throw new Error(`HTTP error! status: ${response.status}`); + if (!response.ok) { + toast.error("Invalid tap! Please try again."); + router.push("/"); + return; + } return response.json(); }) - .then(async (data) => { - const tapResponse = tapResponseSchema.validateSync(data); - switch (tapResponse.code) { - case TapResponseCode.CMAC_INVALID: - throw new Error("CMAC invalid!"); - case TapResponseCode.PERSON_NOT_REGISTERED: - logClientEvent("tapPersonNotRegistered", {}); - handlePersonRegistration(chipId); - break; - case TapResponseCode.LOCATION_NOT_REGISTERED: - logClientEvent("tapLocationNotRegistered", {}); - handleLocationRegistration(chipId); - break; - case TapResponseCode.VALID_PERSON: - logClientEvent("tapValidPerson", {}); - if (!tapResponse.person) { - throw new Error("Person is null!"); - } - await handlePersonTap(tapResponse.person); - break; - case TapResponseCode.VALID_LOCATION: - logClientEvent("tapValidLocation", {}); - if (!tapResponse.location) { - throw new Error("Location is null!"); - } - await handleLocationTap(tapResponse.location); - break; - case TapResponseCode.CHIP_KEY_NOT_FOUND: - throw new Error("Chip key not found!"); - default: - throw new Error("Invalid tap response code!"); - } + .then((response) => { + const finalUrl = response.url; + console.log(finalUrl); + window.location.href = finalUrl; }) .catch((error) => { - console.error(error); - toast.error("Error! Please contact a member of the Cursive team."); + console.error("Error fetching tap response: ", error); + toast.error( + "An error occured while processing the tap. Please try again." + ); + router.push("/"); }); }, [router, processPersonTap, processLocationTap]); diff --git a/src/pages/users/[id]/index.tsx b/src/pages/users/[id]/index.tsx index c47db412..d73ce792 100644 --- a/src/pages/users/[id]/index.tsx +++ b/src/pages/users/[id]/index.tsx @@ -53,7 +53,6 @@ const LinkCard = ({ label, value, href }: LinkCardProps) => { enum PSIState { NOT_STARTED, - WAITING, ROUND1, ROUND2, ROUND3, @@ -63,7 +62,6 @@ enum PSIState { const PSIStateMapping: Record = { [PSIState.NOT_STARTED]: "Not started", - [PSIState.WAITING]: "Waiting for other user to connect...", [PSIState.ROUND1]: "Creating collective encryption pubkey with 2PC...", [PSIState.ROUND2]: "Performing PSI with FHE...", [PSIState.ROUND3]: "Decrypting encrypted results with 2PC...", @@ -95,6 +93,14 @@ const UserProfilePage = () => { useState(); const [selfRound3Output, setSelfRound3Output] = useState(); + const [wantsToInitiatePSI, setWantsToInitiatePSI] = useState(false); + const [otherUserWantsToInitiatePSI, setOtherUserWantsToInitiatePSI] = + useState(false); + const [currentUserInChannel, setCurrentUserInChannel] = useState(false); + const [otherUserInChannel, setOtherUserInChannel] = useState(false); + const [otherUserTemporarilyLeft, setOtherUserTemporarilyLeft] = + useState(false); + const [userOverlap, setUserOverlap] = useState< { userId: string; name: string }[] >([]); @@ -102,14 +108,23 @@ const UserProfilePage = () => { { locationId: string; name: string }[] >([]); - // set up channel for PSI - const setupChannel = () => { - if (!selfEncPk || !otherEncPk || !channelName) return; + useEffect(() => { + if (wantsToInitiatePSI && otherUserWantsToInitiatePSI) { + setWantsToInitiatePSI(false); + setOtherUserWantsToInitiatePSI(false); + setPsiState(PSIState.ROUND1); + console.log("Both users want to initiate psi, starting psi..."); + } + }, [wantsToInitiatePSI, otherUserWantsToInitiatePSI]); + // set up channel for PSI + const setupChannel = ( + selfEncPk: string, + otherEncPk: string, + channelName: string + ) => { logClientEvent("psiSetupChannel", {}); - setPsiState(PSIState.WAITING); - const channel = supabase.channel(channelName, { config: { presence: { key: selfEncPk }, @@ -118,22 +133,29 @@ const UserProfilePage = () => { channel .on("presence", { event: "sync" }, () => { + setCurrentUserInChannel(true); const newState = channel.presenceState(); if (Object.keys(newState).includes(otherEncPk)) { - setPsiState((prevState) => { - if (prevState === PSIState.WAITING) { - return PSIState.ROUND1; - } - return prevState; - }); + console.log("Other user in channel ", otherEncPk); + setOtherUserInChannel(true); + setOtherUserTemporarilyLeft(false); } }) .on("presence", { event: "leave" }, async ({ key }) => { if (key === otherEncPk) { - setPsiState(PSIState.NOT_STARTED); - await closeChannel(); + console.log("Other user left channel", otherEncPk); + setOtherUserTemporarilyLeft(true); + setOtherUserInChannel(false); + } else { + setCurrentUserInChannel(false); } }) + .on("broadcast", { event: "initiatePSI" }, async (event) => { + // only respond to initiatePSI if it's for this user + if (event.payload.to === selfEncPk) return; + console.log("Other user wants to initiate psi", otherEncPk); + setOtherUserWantsToInitiatePSI(true); + }) .on("broadcast", { event: "message" }, (event) => { setBroadcastEvent(event); }) @@ -252,7 +274,6 @@ const UserProfilePage = () => { selfBitVector ); setSelfRound1Output(round1Output); - const round2MessageLink = await psiBlobUploadClient( "round2Message", JSON.stringify(round1Output.message_round2) @@ -285,7 +306,6 @@ const UserProfilePage = () => { round2Order! ); setSelfRound2Output(round2Output); - const round3MessageLink = await psiBlobUploadClient( "round3Message", JSON.stringify(round2Output.message_round3) @@ -315,7 +335,6 @@ const UserProfilePage = () => { overlapIndices.push(i); } } - setSelfRound3Output(overlapIndices); } else if (psiState === PSIState.JUBSIGNAL) { logClientEvent("psiRoundJubsSignal", {}); @@ -356,8 +375,36 @@ const UserProfilePage = () => { } handleOverlapRounds(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [psiState, selfEncPk, otherEncPk, channelName]); + useEffect(() => { + if (otherUserTemporarilyLeft) { + const currentState = psiState; + const timer = setTimeout(() => { + if (psiState === currentState) { + console.log( + "Resetting PSI due to other user temporarily leaving", + currentState, + psiState + ); + setPsiState(PSIState.NOT_STARTED); + setSelfRound1Output(undefined); + setOtherRound2MessageLink(undefined); + setSelfRound2Output(undefined); + setOtherRound3MessageLink(undefined); + setSelfRound3Output(undefined); + setOtherUserInChannel(false); + setOtherUserTemporarilyLeft(false); + setWantsToInitiatePSI(false); + setOtherUserWantsToInitiatePSI(false); + } + }, 10000); + + return () => clearTimeout(timer); + } + }, [otherUserTemporarilyLeft, psiState]); + useEffect(() => { if (typeof id === "string") { const profile = getProfile(); @@ -369,14 +416,33 @@ const UserProfilePage = () => { } const fetchedUser = fetchUserByUUID(id); + const bioMatch = fetchedUser?.bio?.match(/^@(.*)\|/); + const realTg = bioMatch ? bioMatch[1] : null; + const actualBio = bioMatch + ? fetchedUser?.bio?.substring(bioMatch[0].length) + : fetchedUser?.bio; + + if (fetchedUser) { + fetchedUser.bio = actualBio ?? undefined; + fetchedUser.fc = realTg ?? undefined; + } + setUser(fetchedUser); if (fetchedUser) { + // set psi info setOtherEncPk(fetchedUser.encPk); setSelfEncPk(profile.encryptionPublicKey); setChannelName( [fetchedUser.encPk, profile.encryptionPublicKey].sort().join("") ); + // always set up channel + setupChannel( + fetchedUser.encPk, + profile.encryptionPublicKey, + [fetchedUser.encPk, profile.encryptionPublicKey].sort().join("") + ); + if (fetchedUser.oI) { processOverlap(JSON.parse(fetchedUser.oI)); setPsiState(PSIState.COMPLETE); @@ -385,6 +451,70 @@ const UserProfilePage = () => { } }, [id, router]); + const handleInitiatePSI = () => { + if (!user || !channelName || !otherEncPk) return; + + console.log( + "Initiating psi...", + wantsToInitiatePSI, + otherUserWantsToInitiatePSI + ); + + logClientEvent("psiInitiatePSI", {}); + setWantsToInitiatePSI(true); + supabase.channel(channelName).send({ + type: "broadcast", + event: "initiatePSI", + payload: { + to: otherEncPk, + }, + }); + + // start psi if other user is already interested + if (otherUserWantsToInitiatePSI) { + console.log("Starting psi after other user initiating PSI", otherEncPk); + setWantsToInitiatePSI(false); + setOtherUserWantsToInitiatePSI(false); + setPsiState(PSIState.ROUND1); + } + }; + + const handleUpdatePSI = () => { + if (!user || !channelName || !otherEncPk) return; + + console.log( + "Updating psi...", + wantsToInitiatePSI, + otherUserWantsToInitiatePSI + ); + + logClientEvent("psiUpdatePSI", {}); + setSelfRound1Output(undefined); + setOtherRound2MessageLink(undefined); + setSelfRound2Output(undefined); + setOtherRound3MessageLink(undefined); + setSelfRound3Output(undefined); + supabase.channel(channelName).send({ + type: "broadcast", + event: "initiatePSI", + payload: { + to: otherEncPk, + }, + }); + + // start psi if other user is already interested + if (otherUserWantsToInitiatePSI) { + console.log("Starting psi after other user initiating PSI", otherEncPk); + setWantsToInitiatePSI(false); + setOtherUserWantsToInitiatePSI(false); + setPsiState(PSIState.ROUND1); + } else { + console.log("Setting wants to initiate PSI after update", otherEncPk); + setWantsToInitiatePSI(true); + setPsiState(PSIState.NOT_STARTED); + } + }; + if (!user) { return
User not found
; } @@ -458,7 +588,7 @@ const UserProfilePage = () => { {user?.isSpeaker && (
- Speaker at SigSing + Workshop Speaker
)} @@ -481,6 +611,13 @@ const UserProfilePage = () => { value={labelStartWith(user.x, "@")} /> )} + {(user.fc?.length ?? 0) > 1 && ( + + )} {(user.tg?.length ?? 0) > 1 && ( { value={labelStartWith(user.tg, "@")} /> )} - {(user.fc?.length ?? 0) > 1 && ( - - )}
)} @@ -508,23 +635,18 @@ const UserProfilePage = () => {
- Which contacts and talks do you have in common? + What do you both have in common? - {isOverlapComputed ? ( - "Overlap computed at the time you both opted into " - ) : ( - <> - If you both tap Discover at the same time we will - privately compute any overlap using - - )} + <> + This feature requires both users to opt in synchronously. + Built using{" "} + - {" "} 2PC + FHE. @@ -566,24 +688,29 @@ const UserProfilePage = () => { })}
) : psiState === PSIState.NOT_STARTED ? ( ) : (
diff --git a/src/pages/verify/[id].tsx b/src/pages/verify/[id].tsx index 30dd01ea..7d5137ea 100644 --- a/src/pages/verify/[id].tsx +++ b/src/pages/verify/[id].tsx @@ -81,7 +81,7 @@ const QRPage = () => { if (!authToken || authToken.expiresAt < new Date() || !keys) { toast.error("You must be logged in to nullify this proof"); - router.push("/login"); + router.push("/register"); return; } diff --git a/src/shared/constants.ts b/src/shared/constants.ts index 4ca1e309..9b289e49 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -1,12 +1,6 @@ export const APP_CONFIG = { - APP_NAME: "Cursive NFC Demo", + APP_NAME: "Backpocket Alpha", SUPPORT_EMAIL: "hello@cursive.team", }; -export const INDEXDB_STORES = { - PARAMS: "params", - FOLDS: "folds", - LOCKS: "locks", -}; - export const MERKLE_TREE_DEPTH = 9; diff --git a/src/shared/keygen.ts b/src/shared/keygen.ts index b86634e4..4fc812e0 100644 --- a/src/shared/keygen.ts +++ b/src/shared/keygen.ts @@ -11,7 +11,211 @@ export type KeygenData = { talkEndTime?: string; // End time of the talk ex: 10:00 }; -export const initialKeygenData: Record = { +export const keyUids = [ + "0428728A376B80", + "04B86A8A376B80", + "047E6A8A376B80", + "0444728A376B80", + "0440718A376B80", + "04BB828A376B80", + "04BE6A8A376B80", + "04296E8A376B80", + "0471988A376B80", + "04B76A8A376B80", + "048DA08A376B80", + "049C6E8A376B80", + "044A988A376B80", + "0449718A376B80", + "04336E8A376B80", + "0423718A376B80", + "041D6E8A376B80", + "0454A18A376B80", + "043E758A376B80", + "0471688A376B80", + "042C7C8A376B80", + "04A6838A376B80", + "044D758A376B80", + "0464758A376B80", + "0476838A376B80", + "048E838A376B80", + "041D6C8A376B80", + "0496538A376B80", + "048B6B8A376B80", + "0453718A376B80", + "04396C8A376B80", + "042F538A376B80", + "04B1908A376B80", + "0449538A376B80", + "048E6E8A376B80", + "045C728A376B80", + "044E718A376B80", + "04A4908A376B80", + "04B16B8A376B80", + "049F6E8A376B80", + "048D838A376B80", + "042E6E8A376B80", + "04C06A8A376B80", + "0447718A376B80", + "04A56A8A376B80", + "047A998A376B80", + "04A1918A376B80", + "04BC6A8A376B80", + "04376E8A376B80", + "04898BBA851B90", + "04801DBA851B90", + "04891CBA851B90", + "04A71CBA851B90", + "045C8BBA851B90", + "049A1CBA851B90", + "04818BBA851B90", + "047C8BBA851B90", + "048B1DBA851B90", + "04911DBA851B90", + "04848BBA851B90", + "04751DBA851B90", + "04961CBA851B90", + "04588BBA851B90", + "049C1CBA851B90", + "04981DBA851B90", + "046F8BBA851B90", + "04AE1DBA851B90", + "04721DBA851B90", + "04518BBA851B90", + "046A1DBA851B90", + "04AA1DBA851B90", + "047D8BBA851B90", + "04931DBA851B90", + "04598BBA851B90", + "04A11CBA851B90", + "04921CBA851B90", + "047C1DBA851B90", + "04651DBA851B90", + "04A81CBA851B90", + "044F8BBA851B90", + "04908BBA851B90", + "04A91CBA851B90", + "04911CBA851B90", + "04A41DBA851B90", + "04A21DBA851B90", + "04A51CBA851B90", + "04991DBA851B90", + "046B8BBA851B90", + "046C8BBA851B90", + "04941CBA851B90", + "047A8BBA851B90", + "04798BBA851B90", + "04828BBA851B90", + "04A01DBA851B90", + "048B8BBA851B90", + "045E8BBA851B90", + "047B8BBA851B90", + "04918BBA851B90", + "044D8BBA851B90", + "04971CBA851B90", + "048B1CBA851B90", + "04681DBA851B90", + "048C1DBA851B90", + "04731DBA851B90", + "047E1DBA851B90", + "04638BBA851B90", + "04691DBA851B90", + "048E8BBA851B90", + "04778BBA851B90", + "04768BBA851B90", + "049A1DBA851B90", + "04658BBA851B90", + "04AC1CBA851B90", + "04698BBA851B90", + "04758BBA851B90", + "04748BBA851B90", + "04951CBA851B90", + "04881DBA851B90", + "04808BBA851B90", + "04608BBA851B90", + "04741DBA851B90", + "047A1DBA851B90", + "048A8BBA851B90", + "048D1CBA851B90", + "04878BBA851B90", + "04661DBA851B90", + "04A11DBA851B90", + "04AB1DBA851B90", + "04AB1CBA851B90", + "04508BBA851B90", + "04538BBA851B90", + "048D1DBA851B90", + "04838BBA851B90", + "04A51DBA851B90", + "04921DBA851B90", + "04A61CBA851B90", + "04668BBA851B90", + "04888BBA851B90", + "04788BBA851B90", + "04761DBA851B90", + "04821DBA851B90", + "047E8BBA851B90", + "049F1DBA851B90", + "048E1DBA851B90", + "04AD1CBA851B90", + "048E1CBA851B90", + "04951DBA851B90", + "04A31DBA851B90", + "04931CBA851B90", + "04971DBA851B90", + "048C8BBA851B90", + "049E1CBA851B90", + "04A61DBA851B90", + "045F8BBA851B90", + "04AD1DBA851B90", + "047B1DBA851B90", + "045D8BBA851B90", + "04A21CBA851B90", + "046F1DBA851B90", + "049F1CBA851B90", + "044E8BBA851B90", + "046B1DBA851B90", + "048F8BBA851B90", + "04711DBA851B90", + "048A1DBA851B90", + "044C8BBA851B90", + "04688BBA851B90", + "04628BBA851B90", + "04771DBA851B90", + "04941DBA851B90", + "04648BBA851B90", + "04831DBA851B90", + "04901CBA851B90", + "04641DBA851B90", + "046E8BBA851B90", + "04528BBA851B90", + "04961DBA851B90", + "04A71DBA851B90", + "04811DBA851B90", + "048C1CBA851B90", + "04728BBA851B90", + "04A91DBA851B90", + "048F1CBA851B90", + "046A8BBA851B90", + "045A8BBA851B90", + "04701DBA851B90", + "04868BBA851B90", + "047F1DBA851B90", + "04991CBA851B90", + "04A01CBA851B90", + "048A1CBA851B90", + "049B1CBA851B90", + "048F1DBA851B90", + "047F8BBA851B90", + "04678BBA851B90", + "04791DBA851B90", + "048D8BBA851B90", + "04718BBA851B90", + "04981CBA851B90", +]; + +export const initialKeygenData: Record = {}; + +export const oldKeygenData: Record = { // BEGIN TEST CARDS "1": { box: "brown", @@ -313,122 +517,117 @@ export const initialKeygenData: Record = { label: "speaker 39", isPersonSpeaker: false, }, - "51": { +}; + +export const initialLocationData: Record = { + TALK01: { box: "brown", type: "talk", label: "talk 1", - talkName: "MynaWallet", + talkName: "Cypherpunk Mission-Driven Cryptography", talkStage: "main", - talkDescription: `​MynaWallet is AA wallet operated by Japanese national ID card that guarantees the security of a hardware wallet and sybil resistance. By leveraging zero-knowledge proofs, one's on-chain activity is not linked with their national ID card and users can publicly prove properties about their identity without actually revealing who they are. - - ​MynaWalletは、日本のマイナンバーカードによって運営されるAAウォレットであり、ハードウェアウォレットの安全性と耐シビル性を保証します。 - - ​ゼロ知識証明を活用することで、チェーン上での活動がマイナンバーカードとリンクされることはなく、ユーザーは自分が誰であるかを実際に明かすことなく、自身の身元に関する特性を公に証明することができます。 - `, - talkSpeaker: "Nico and Hiro", - talkStartTime: "10:00", - talkEndTime: "10:30", - }, - "52": { + talkDescription: `Presentation`, + talkSpeaker: "Wanseob", + talkStartTime: "2:00", + talkEndTime: "2:20", + }, + TALK02: { box: "brown", type: "talk", label: "talk 2", - talkName: "Proof of Passport", + talkName: "Introduction to MPC", talkStage: "main", - talkDescription: `Proof of Passport lets users use their passport to generate zero-knowledge proofs of humanity, nationality or age while staying privacy-preserving. - - ​Proof of Passportは、パスポートを使用して、プライバシーを保護しながら、人間であること、国籍、年齢をゼロ知識で証明することができます。 - `, - talkSpeaker: "Florent and Rémi", - talkStartTime: "10:30", - talkEndTime: "11:00", + talkDescription: `Presentation`, + talkSpeaker: "Nam", + talkStartTime: "2:20", + talkEndTime: "2:40", }, - "53": { + TALK03: { box: "brown", type: "talk", label: "talk 3", - talkName: "Cursive NFC Activations", + talkName: "Introduction to FHE", talkStage: "main", - talkDescription: `​Cursive focuses on consumer-facing apps of signed data to provide users with data ownership and authenticity. We create tangible, interactive experiences to better educate average people on signatures and other advanced cryptography. We are primarily focused on signatures from NFC and emails. - - ​Cursiveは、署名付きデータの消費者向けアプリケーションに焦点を当て、ユーザーにデータの所有権と信頼性を提供しています。 - - ​私たちは、署名やその他の高度な暗号技術について一般の人々をよりよく啓発するために、具体的でインタラクティブな体験を創造します。主にNFCと電子メールによる署名に焦点を当てています。 - `, - talkSpeaker: "Vivek and Andrew", - talkStartTime: "11:00", - talkEndTime: "11:30", - }, - "54": { - box: "two", - type: "person", - label: "speaker 25", - isPersonSpeaker: false, - }, - "55": { - box: "two", - type: "person", - label: "speaker 26", - isPersonSpeaker: false, - }, - "56": { - box: "two", - type: "person", - label: "speaker 27", - isPersonSpeaker: false, - }, - "57": { - box: "two", - type: "person", - label: "speaker 28", - isPersonSpeaker: false, - }, - "58": { - box: "two", - type: "person", - label: "speaker 29", - isPersonSpeaker: false, + talkDescription: `Presentation`, + talkSpeaker: "Enrico", + talkStartTime: "2:40", + talkEndTime: "3:00", }, - "59": { - box: "two", - type: "person", - label: "speaker 30", - isPersonSpeaker: false, + TALK04: { + box: "brown", + type: "talk", + label: "talk 4", + talkName: "Privacy-preserving Statistics", + talkStage: "main", + talkDescription: `Lightning Talk`, + talkSpeaker: "Kevin", + talkStartTime: "3:10", + talkEndTime: "3:20", }, - "60": { - box: "two", - type: "person", - label: "speaker 31", - isPersonSpeaker: false, + TALK05: { + box: "brown", + type: "talk", + label: "talk 5", + talkName: "Oblivious Message Retrieval for Zcash", + talkStage: "main", + talkDescription: `Lightning Talk`, + talkSpeaker: "Ying Tong, Keewoo Lee", + talkStartTime: "3:20", + talkEndTime: "3:30", }, - "61": { - box: "two", - type: "person", - label: "speaker 32", - isPersonSpeaker: false, + TALK06: { + box: "brown", + type: "talk", + label: "talk 6", + talkName: "Encrypted Scholarship", + talkStage: "main", + talkDescription: `Lightning talk`, + talkSpeaker: "Shouki Tsuda", + talkStartTime: "3:30", + talkEndTime: "3:40", }, - "62": { - box: "two", - type: "person", - label: "speaker 33", - isPersonSpeaker: false, + TALK07: { + box: "brown", + type: "talk", + label: "talk 7", + talkName: "mpz play - A workshop for PSE's mpz library", + talkStage: "main", + talkDescription: `Demo / workshop`, + talkSpeaker: "Thomas", + talkStartTime: "3:40", + talkEndTime: "3:50", }, - "63": { - box: "two", - type: "person", - label: "speaker 34", - isPersonSpeaker: false, + TALK08: { + box: "brown", + type: "talk", + label: "talk 8", + talkName: "Write a Circuit in TypeScript", + talkStage: "main", + talkDescription: `Demo / workshop`, + talkSpeaker: "Andrew", + talkStartTime: "3:50", + talkEndTime: "4:00", }, - "64": { - box: "two", - type: "person", - label: "speaker 35", - isPersonSpeaker: false, + TALK09: { + box: "brown", + type: "talk", + label: "talk 9", + talkName: "Private Collaborative Research", + talkStage: "main", + talkDescription: `Lightning talk`, + talkSpeaker: "Giacomo", + talkStartTime: "4:00", + talkEndTime: "4:10", }, - "65": { - box: "two", - type: "person", - label: "speaker 36", - isPersonSpeaker: false, + TALK10: { + box: "brown", + type: "talk", + label: "talk 10", + talkName: "Backpocket Multiplayer Vault Demo", + talkStage: "main", + talkDescription: `Live workshop, trying out different ZK and MPC apps built by Cursive (https://cursive.team).`, + talkSpeaker: "Vivek", + talkStartTime: "4:10", + talkEndTime: "5:00", }, }; diff --git a/src/styles/globals.css b/src/styles/globals.css index 5c74e8b2..7e0d2c97 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -7,11 +7,11 @@ --background-start-rgb: 214, 219, 220; --background-end-rgb: 255, 255, 255; /* Colors */ - --bg-primary: #DFF1FF; - --color-primary: #4015EC; - --color-secondary: #8C73F4; - --color-tertiary: #E9F3FF; - --color-error: #D40018; + --bg-primary: #dff1ff; + --color-primary: #4015ec; + --color-secondary: #8c73f4; + --color-tertiary: #e9f3ff; + --color-error: #d40018; } @media (prefers-color-scheme: dark) { @@ -28,7 +28,7 @@ body { padding: 0; height: 100%; @apply text-iron-950; - background-image: url('/bg-gradient.jpg'); + background: linear-gradient(180deg, #bde1f9 0%, #bdd6fc 100%); background-repeat: no-repeat; background-size: cover; text-rendering: geometricPrecision; @@ -67,7 +67,7 @@ body { display: grid !important; gap: 8px !important; height: 10px !important; - grid-template-columns: repeat( auto-fit, minmax(50px, 1fr) ); + grid-template-columns: repeat(auto-fit, minmax(50px, 1fr)); } .swiper-pagination .folded-dot { @@ -76,4 +76,4 @@ body { .swiper-pagination .folded-dot-active { @apply !bg-white !opacity-100; -} \ No newline at end of file +}