├── .gitignore ├── README.md ├── package.json ├── public ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json ├── robots.txt └── zkxzk.png ├── src ├── App.css ├── App.test.tsx ├── App.tsx ├── bls.ts ├── index.css ├── index.tsx ├── logo.svg ├── reportWebVitals.tsx ├── setupTests.tsx └── vkey.json └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zkPairing 2 | 3 | This is the frontend for a demo that took place at DevConnect Amsterdam 2022. The demo can be found [here](https://zkpairing.xyz). We allow users to submit their public key, a BLS signature, and a message (represented as a point on the BLS12-381 curve); these inputs are submitted to a zk-SNARK which checks the validity of the signature. If valid, the server returns a proof to the user; if invalid, an error is displayed. 4 | 5 | The implementation of BLS signatures (which required implementation of elliptic curve pairings in Circom) can be found [here](https://github.com/yi-sun/circom-pairing/). The server setup can be found [here](https://github.com/vincenthuang75025/zk-node-server-c/). 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zkxzk", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@chainsafe/lodestar-types": "^0.34.4", 7 | "@noble/bls12-381": "^1.2.0", 8 | "@testing-library/jest-dom": "^5.16.4", 9 | "@testing-library/react": "^13.0.1", 10 | "@testing-library/user-event": "^13.5.0", 11 | "@types/jest": "^27.4.1", 12 | "@types/node": "^17.0.29", 13 | "@types/react": "^18.0.8", 14 | "@types/react-copy-to-clipboard": "^5.0.2", 15 | "@types/react-dom": "^18.0.0", 16 | "create-react-app": "^5.0.1", 17 | "js-file-download": "^0.4.12", 18 | "react": "^18.0.0", 19 | "react-copy-to-clipboard": "^5.1.0", 20 | "react-dom": "^18.0.0", 21 | "react-scripts": "4.0.3", 22 | "tar-pack": "^3.4.1", 23 | "typescript": "^4.6.3", 24 | "web-vitals": "^2.1.4" 25 | }, 26 | "scripts": { 27 | "start": "react-scripts start", 28 | "build": "react-scripts build", 29 | "test": "react-scripts test", 30 | "eject": "react-scripts eject" 31 | }, 32 | "browser": { 33 | "crypto": false 34 | }, 35 | "eslintConfig": { 36 | "extends": [ 37 | "react-app", 38 | "react-app/jest" 39 | ] 40 | }, 41 | "browserslist": { 42 | "production": [ 43 | "chrome >= 67", 44 | "edge >= 79", 45 | "firefox >= 68", 46 | "opera >= 54", 47 | "safari >= 14" 48 | ], 49 | "development": [ 50 | "last 1 chrome version", 51 | "last 1 firefox version", 52 | "last 1 safari version" 53 | ] 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | zkPairing 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincenthuang75025/zkxzk/5edc5c2e0c2255c57d3dc4372968089244d24158/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincenthuang75025/zkxzk/5edc5c2e0c2255c57d3dc4372968089244d24158/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/zkxzk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincenthuang75025/zkxzk/5edc5c2e0c2255c57d3dc4372968089244d24158/public/zkxzk.png -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | padding: 0px 0px; 4 | } 5 | 6 | .App-logo { 7 | width: 25%; 8 | pointer-events: none; 9 | } 10 | 11 | @media (prefers-reduced-motion: no-preference) { 12 | .App-logo { 13 | animation: App-logo-spin infinite 20s linear; 14 | } 15 | } 16 | a { 17 | color: #66bce9; 18 | color: var(--link-color, #66bce9); 19 | text-decoration: none; 20 | background-color: transparent; 21 | } 22 | 23 | a:not(.nav-link) { 24 | text-decoration: none !important; 25 | background-repeat: no-repeat; 26 | background-image: linear-gradient( 27 | 180deg, 28 | transparent 65%, 29 | var(--highlight) 0 30 | ); 31 | transition: background-size 0.25s ease; 32 | background-size: 0% 100%; 33 | } 34 | 35 | a:hover { 36 | color: var(--link-hover-color); 37 | } 38 | 39 | .container { 40 | width: 100%; 41 | padding-right: 15px; 42 | padding-left: 15px; 43 | margin-right: auto; 44 | margin-left: auto; 45 | text-align: left; 46 | } 47 | 48 | @media (min-width: 640px) { 49 | .container, 50 | .container-sm { 51 | max-width: 620px; 52 | } 53 | } 54 | @media (min-width: 820px) { 55 | .container, 56 | .container-md, 57 | .container-sm { 58 | max-width: 790px; 59 | } 60 | } 61 | @media (min-width: 1140px) { 62 | .container, 63 | .container-lg, 64 | .container-md, 65 | .container-sm { 66 | max-width: 1110px; 67 | } 68 | } 69 | @media (min-width: 1440px) { 70 | .container, 71 | .container-lg, 72 | .container-md, 73 | .container-sm, 74 | .container-xl { 75 | max-width: 1400px; 76 | } 77 | } 78 | .row { 79 | display: flex; 80 | flex-wrap: wrap; 81 | margin-right: -15px; 82 | margin-left: -15px; 83 | } 84 | .col, 85 | .col-1, 86 | .col-10, 87 | .col-11, 88 | .col-12, 89 | .col-2, 90 | .col-3, 91 | .col-4, 92 | .col-5, 93 | .col-6, 94 | .col-7, 95 | .col-8, 96 | .col-9, 97 | .col-auto, 98 | .col-lg, 99 | .col-lg-1, 100 | .col-lg-10, 101 | .col-lg-11, 102 | .col-lg-12, 103 | .col-lg-2, 104 | .col-lg-3, 105 | .col-lg-4, 106 | .col-lg-5, 107 | .col-lg-6, 108 | .col-lg-7, 109 | .col-lg-8, 110 | .col-lg-9, 111 | .col-lg-auto, 112 | .col-md, 113 | .col-md-1, 114 | .col-md-10, 115 | .col-md-11, 116 | .col-md-12, 117 | .col-md-2, 118 | .col-md-3, 119 | .col-md-4, 120 | .col-md-5, 121 | .col-md-6, 122 | .col-md-7, 123 | .col-md-8, 124 | .col-md-9, 125 | .col-md-auto, 126 | .col-sm, 127 | .col-sm-1, 128 | .col-sm-10, 129 | .col-sm-11, 130 | .col-sm-12, 131 | .col-sm-2, 132 | .col-sm-3, 133 | .col-sm-4, 134 | .col-sm-5, 135 | .col-sm-6, 136 | .col-sm-7, 137 | .col-sm-8, 138 | .col-sm-9, 139 | .col-sm-auto, 140 | .col-xl, 141 | .col-xl-1, 142 | .col-xl-10, 143 | .col-xl-11, 144 | .col-xl-12, 145 | .col-xl-2, 146 | .col-xl-3, 147 | .col-xl-4, 148 | .col-xl-5, 149 | .col-xl-6, 150 | .col-xl-7, 151 | .col-xl-8, 152 | .col-xl-9, 153 | .col-xl-auto { 154 | position: relative; 155 | width: 100%; 156 | padding-right: 15px; 157 | padding-left: 15px; 158 | } 159 | .block-card { 160 | border-top-left-radius: 0; 161 | border-top-right-radius: 0; 162 | } 163 | .card { 164 | background-color: var(--bg-color); 165 | border-radius: 8px; 166 | border-top-left-radius: 8px; 167 | border-top-right-radius: 8px; 168 | box-shadow: 0 2px 8px 0 var(--shadow-color); 169 | position: relative; 170 | display: -ms-flexbox; 171 | display: flex; 172 | -ms-flex-direction: column; 173 | flex-direction: column; 174 | min-width: 0; 175 | word-wrap: break-word; 176 | background-color: #232024; 177 | background-color: var(--bg-color, #232024); 178 | background-clip: border-box; 179 | border: 1px solid rgba(255, 255, 255, 0.125); 180 | border-radius: 0.25rem; 181 | } 182 | .card-body { 183 | -ms-flex: 1 1 auto; 184 | flex: 1 1 auto; 185 | min-height: 1px; 186 | padding: 1.25rem; 187 | } 188 | 189 | .mt-2, 190 | .my-2 { 191 | margin-top: 0.5rem !important; 192 | } 193 | .pb-1, 194 | .py-1 { 195 | padding-bottom: 0.25rem !important; 196 | } 197 | .pt-1, 198 | .py-1 { 199 | padding-top: 0.25rem !important; 200 | } 201 | .pl-0, 202 | .px-0 { 203 | padding-left: 0 !important; 204 | } 205 | .pr-0, 206 | .px-0 { 207 | padding-right: 0 !important; 208 | } 209 | .p-3 { 210 | padding: 1rem !important; 211 | } 212 | .ml-0, 213 | .mx-0 { 214 | margin-left: 0 !important; 215 | } 216 | .mr-0, 217 | .mx-0 { 218 | margin-right: 0 !important; 219 | } 220 | .col-md-2 { 221 | -ms-flex: 0 0 16.666667%; 222 | flex: 0 0 16.666667%; 223 | max-width: 16.666667%; 224 | } 225 | .col-md-10 { 226 | flex: 0 0 83.333333%; 227 | max-width: 83.333333%; 228 | display: flex; 229 | flex-direction: column; 230 | } 231 | .col-md-12 { 232 | max-width: 100%; 233 | display: flex; 234 | flex-direction: column; 235 | } 236 | .col-input { 237 | display: flex; 238 | flex-direction: row; 239 | } 240 | .col-button { 241 | display: flex; 242 | } 243 | *, 244 | ::after, 245 | ::before { 246 | box-sizing: border-box; 247 | } 248 | 249 | .border-bottom { 250 | border-bottom: 1px solid #5c4e4e !important; 251 | } 252 | .App-input { 253 | flex: 1; 254 | background-color: var(--bg-color, #232024); 255 | color: var(--body-color, #dee2e6); 256 | font-family: "Roboto Mono", SFMono-Regular, Menlo, Monaco, Consolas, 257 | "Liberation Mono", "Courier New", monospace !important; 258 | font-size: 1rem; 259 | border: 1px solid rgba(255, 255, 255, 0.125); 260 | border-radius: 0.25rem; 261 | } 262 | .App-input:focus { 263 | outline: none; 264 | border-color: var(--border-color); 265 | } 266 | 267 | .App-button { 268 | display: block; 269 | text-align: center; 270 | width: 100%; 271 | font-family: Inter, sans-serif; 272 | font-size: 1rem; 273 | font-weight: 400; 274 | line-height: 1.5; 275 | background-color: var(--bg); 276 | color: var(--body-color); 277 | border-color: transparent; 278 | padding: 14px 28px; 279 | } 280 | .App-button:hover { 281 | background-color: var(--shadow-dark); 282 | color: var(--shadow-color); 283 | } 284 | 285 | .download-contents { 286 | background: #7d8293; 287 | border-radius: 4px; 288 | bottom: 10px; 289 | color: #fff; 290 | cursor: pointer; 291 | font-family: sans-serif; 292 | font-size: 14px; 293 | font-weight: 600; 294 | height: 30px; 295 | padding: 5px; 296 | position: absolute; 297 | right: 25px; 298 | text-align: center; 299 | } 300 | 301 | .copy-to-clipboard { 302 | background: #7d8293; 303 | border: none; 304 | border-radius: 4px; 305 | bottom: 10px; 306 | height: 30px; 307 | position: absolute; 308 | right: 115px; 309 | width: 30px; 310 | } 311 | .copy-to-clipboard button { 312 | background: url('data:image/svg+xml;charset=utf-8,') 313 | 50% no-repeat; 314 | border: none; 315 | height: 25px; 316 | padding-left: 25px; 317 | cursor: pointer; 318 | } 319 | pre { 320 | display: block; 321 | overflow-x: auto; 322 | padding: 0.5em; 323 | background: rgb(51, 51, 51) none repeat scroll 0% 0%; 324 | color: white; 325 | word-wrap: break-word; 326 | border-radius: 4px; 327 | font-family: monospace; 328 | font-size: 12px; 329 | font-weight: 600; 330 | hyphens: auto; 331 | margin: 0; 332 | white-space: pre-wrap; 333 | word-break: break-word; 334 | max-height: 400px; 335 | min-height: 6em; 336 | overflow-y: auto; 337 | } 338 | -------------------------------------------------------------------------------- /src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, screen } from "@testing-library/react"; 3 | import App from "./App"; 4 | 5 | test("renders learn react link", () => { 6 | render(); 7 | const linkElement = screen.getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { PointG1, PointG2 } from "@noble/bls12-381"; 2 | import { ssz } from "@chainsafe/lodestar-types"; 3 | import React, { useState, useEffect, WheelEvent } from "react"; 4 | import "./App.css"; 5 | import { 6 | bigint_to_array, 7 | formatHex, 8 | hexToBytes, 9 | utils, 10 | bytesToHex, 11 | } from "./bls"; 12 | import { ReactComponent as Logo } from "./logo.svg"; 13 | import saveAs from "js-file-download"; 14 | import { CopyToClipboard } from "react-copy-to-clipboard"; 15 | // @ts-ignore 16 | import vkey from "./vkey.json"; 17 | 18 | const hashToField = utils.hashToField; 19 | 20 | const backend_url = "https://api.zkxzk.xyz/"; 21 | 22 | const PrettyPrintJson: React.FC<{ 23 | data: string; 24 | fileName?: string; 25 | }> = ({ data, fileName }) => { 26 | const downloadText = () => { 27 | saveAs(data, fileName || "response.txt"); 28 | }; 29 | 30 | // (destructured) data could be a prop for example 31 | return ( 32 |
33 |
{fileName}
34 |
35 | Download 36 |
37 |
38 | 39 | 40 | 41 |
42 |
{data}
43 |
44 | ); 45 | }; 46 | 47 | /* global BigInt */ 48 | function App(): JSX.Element { 49 | const [slot, setSlot] = useState(""); 50 | const [epoch, setEpoch] = useState(""); 51 | const [pubkeyHex, setPubkeyHex] = useState(""); 52 | const [blockRoot, setBlockRoot] = useState(""); 53 | const [signatureHex, setSignatureHex] = useState(""); 54 | 55 | const [signingRoot, setSigningRoot] = useState(""); 56 | const [pubkey, setPubkey] = useState({ 0: "", 1: "" }); 57 | const [signature, setSignature] = useState({ 0: "", 1: "", 2: "", 3: "" }); 58 | const [message, setMessage] = useState({ 0: "", 1: "", 2: "", 3: "" }); 59 | const [id, setId] = useState(""); 60 | const [inputJson, setInputJson] = useState(""); 61 | const [publicJson, setPublicJson] = useState(""); 62 | const [proof, setProof] = useState(""); 63 | const [status, setStatus] = useState(0); 64 | 65 | /*useEffect(() => { 66 | msg_hash("abc").then(res => console.log(res)); 67 | }, []);*/ 68 | 69 | const handlePubkeyChange = ( 70 | index: number, 71 | event: React.ChangeEvent 72 | ) => { 73 | setPubkey({ ...pubkey, [index]: event.target.value }); 74 | }; 75 | 76 | const handleSignatureChange = ( 77 | index: number, 78 | event: React.ChangeEvent 79 | ) => { 80 | setSignature({ ...signature, [index]: event.target.value }); 81 | }; 82 | 83 | const handleMessageChange = ( 84 | index: number, 85 | event: React.ChangeEvent 86 | ) => { 87 | setMessage({ ...message, [index]: event.target.value }); 88 | }; 89 | 90 | async function submitSlot(event: any) { 91 | if (slot === "" || isNaN(Number(slot))) { 92 | alert("Please provide a valid slot number."); 93 | return; 94 | } 95 | try { 96 | let slotNum: number = Number(slot); 97 | let blockJson = await fetch( 98 | "https://beaconcha.in/api/v1/block/" + slotNum.toString() 99 | ).then((res) => { 100 | return res.json(); 101 | }); 102 | setBlockRoot(blockJson["data"]["blockroot"]); 103 | setEpoch(blockJson["data"]["epoch"]); 104 | setSignatureHex(blockJson["data"]["signature"]); 105 | let proposer: number = Number(blockJson["data"]["proposer"]); 106 | let validatorJson = await fetch( 107 | "https://beaconcha.in/api/v1/validator/" + proposer.toString() 108 | ).then((res) => { 109 | return res.json(); 110 | }); 111 | setPubkeyHex(validatorJson["data"]["pubkey"]); 112 | } catch { 113 | alert("API call to get block failed."); 114 | } 115 | } 116 | async function submitBlock(event: any) { 117 | if ( 118 | epoch === "" || 119 | pubkeyHex === "" || 120 | blockRoot === "" || 121 | signatureHex === "" 122 | ) { 123 | alert("Please fill in all fields"); 124 | return; 125 | } 126 | try { 127 | let publicKey = PointG1.fromHex(formatHex(pubkeyHex)); 128 | publicKey.assertValidity(); 129 | let sig: PointG2 = PointG2.fromSignature(formatHex(signatureHex)); 130 | sig.assertValidity(); 131 | let root: Uint8Array = hexToBytes(blockRoot); 132 | 133 | // infura API call gives: 134 | const genesisValidatorsRoot: Uint8Array = hexToBytes( 135 | "0x4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95" 136 | ); 137 | 138 | const ForkData = ssz.phase0.ForkData; 139 | let fork_data = ForkData.defaultValue(); 140 | 141 | const ALTAIR_FORK_EPOCH: number = 74240; 142 | if (Number(epoch) < ALTAIR_FORK_EPOCH) 143 | fork_data.currentVersion = hexToBytes("0x00000000"); 144 | else fork_data.currentVersion = hexToBytes("0x01000000"); // altair 145 | 146 | fork_data.genesisValidatorsRoot = genesisValidatorsRoot; 147 | let fork_data_root = ForkData.hashTreeRoot(fork_data); 148 | 149 | let domain = new Uint8Array(32); 150 | const DOMAIN_BEACON_PROPOSER = Uint8Array.from([0, 0, 0, 0]); 151 | for (let i = 0; i < 4; i++) domain[i] = DOMAIN_BEACON_PROPOSER[i]; 152 | for (let i = 0; i < 28; i++) domain[i + 4] = fork_data_root[i]; 153 | 154 | const SigningData = ssz.phase0.SigningData; 155 | let signing_data = SigningData.defaultValue(); 156 | signing_data.objectRoot = hexToBytes(blockRoot); 157 | signing_data.domain = domain; 158 | let signing_root: Uint8Array = SigningData.hashTreeRoot(signing_data); 159 | 160 | setSigningRoot("0x" + bytesToHex(signing_root)); 161 | let u: bigint[][] = await hashToField(signing_root, 2); 162 | 163 | setPubkey({ 164 | 0: "0x" + publicKey.toAffine()[0].value.toString(16), 165 | 1: "0x" + publicKey.toAffine()[1].value.toString(16), 166 | }); 167 | 168 | setSignature({ 169 | 0: "0x" + sig.toAffine()[0].c[0].value.toString(16), 170 | 1: "0x" + sig.toAffine()[0].c[1].value.toString(16), 171 | 2: "0x" + sig.toAffine()[1].c[0].value.toString(16), 172 | 3: "0x" + sig.toAffine()[1].c[1].value.toString(16), 173 | }); 174 | 175 | setMessage({ 176 | 0: "0x" + u[0][0].toString(16), 177 | 1: "0x" + u[0][1].toString(16), 178 | 2: "0x" + u[1][0].toString(16), 179 | 3: "0x" + u[1][1].toString(16), 180 | }); 181 | } catch { 182 | alert("Input values are not valid. Please try again."); 183 | } 184 | } 185 | 186 | const submitInput = (event: any) => { 187 | if ( 188 | message[0] === "" || 189 | message[1] === "" || 190 | message[2] === "" || 191 | message[3] === "" || 192 | pubkey[0] === "" || 193 | pubkey[1] === "" || 194 | signature[0] === "" || 195 | signature[1] === "" || 196 | signature[2] === "" || 197 | signature[3] === "" 198 | ) { 199 | alert("Please fill in all fields"); 200 | return; 201 | } 202 | // console.log(JSON.stringify({ 203 | // pubkey: [bigint_to_array(55, 7, BigInt(pubkey[0])), bigint_to_array(55, 7, BigInt(pubkey[1]))], 204 | // signature: [[bigint_to_array(55, 7, BigInt(signature[0])), bigint_to_array(55, 7, BigInt(signature[1]))], 205 | // [bigint_to_array(55, 7, BigInt(signature[2])), bigint_to_array(55, 7, BigInt(signature[3]))]], 206 | // hash: [[bigint_to_array(55, 7, BigInt(message[0])), bigint_to_array(55, 7, BigInt(message[1]))], 207 | // [bigint_to_array(55, 7, BigInt(message[2])), bigint_to_array(55, 7, BigInt(message[3]))]] 208 | // })); 209 | let pubkeyArray: string[][] = [ 210 | bigint_to_array(55, 7, BigInt(pubkey[0])), 211 | bigint_to_array(55, 7, BigInt(pubkey[1])), 212 | ]; 213 | let signatureArray: string[][][] = [ 214 | [ 215 | bigint_to_array(55, 7, BigInt(signature[0])), 216 | bigint_to_array(55, 7, BigInt(signature[1])), 217 | ], 218 | [ 219 | bigint_to_array(55, 7, BigInt(signature[2])), 220 | bigint_to_array(55, 7, BigInt(signature[3])), 221 | ], 222 | ]; 223 | let hashArray: string[][][] = [ 224 | [ 225 | bigint_to_array(55, 7, BigInt(message[0])), 226 | bigint_to_array(55, 7, BigInt(message[1])), 227 | ], 228 | [ 229 | bigint_to_array(55, 7, BigInt(message[2])), 230 | bigint_to_array(55, 7, BigInt(message[3])), 231 | ], 232 | ]; 233 | let inputStr: string = JSON.stringify( 234 | { 235 | pubkey: pubkeyArray, 236 | signature: signatureArray, 237 | hash: hashArray, 238 | }, 239 | null, 240 | 2 241 | ); 242 | setInputJson(inputStr); 243 | 244 | let publicArray: string[] = []; 245 | publicArray = publicArray.concat( 246 | pubkeyArray.flat(), 247 | signatureArray[0].flat(), 248 | signatureArray[1].flat(), 249 | hashArray[0].flat(), 250 | hashArray[1].flat() 251 | ); 252 | setPublicJson(JSON.stringify(publicArray)); 253 | 254 | setProof(""); 255 | setStatus(0); 256 | 257 | fetch(backend_url + "generate_proof", { 258 | method: "POST", 259 | headers: { 260 | "Content-Type": "application/json", 261 | }, 262 | body: inputStr, 263 | }) 264 | .then((res) => res.json()) 265 | .then((data) => setId(data.id)); 266 | }; 267 | 268 | const submitHash = (event: any) => { 269 | if (id !== "") { 270 | fetch(backend_url + "result", { 271 | method: "POST", 272 | headers: { 273 | "Content-Type": "application/json", 274 | }, 275 | body: '{"id":"' + id + '"}', 276 | }) 277 | .then((response) => { 278 | setStatus(response.status); 279 | console.log(response.status); 280 | return response.json(); 281 | }) 282 | .then((data) => { 283 | setProof(JSON.stringify(data, null, 2)); 284 | }); 285 | } 286 | }; 287 | 288 | return ( 289 | <> 290 |
291 |
292 |

zkPairing

293 |
294 |
295 |
296 |
297 |
298 | Enter a slot from the Beacon Chain to autofill block 299 | information from{" "} 300 | beaconcha.in: 301 |
302 |
303 |
304 |
305 | Slot: 306 |
307 |
308 | { 314 | setSlot(e.currentTarget.value); 315 | }} 316 | /> 317 |
318 |
319 | 320 |
321 |
322 | 325 |
326 |
327 | 328 |
329 |
330 | Enter the following information directly from a block: 331 |
332 |
333 |
334 |
335 | Epoch: 336 |
337 |
338 | { 344 | setEpoch(e.currentTarget.value); 345 | }} 346 | /> 347 |
348 |
349 |
350 |
351 | Proposer Public Key: 352 |
353 |
354 | { 360 | setPubkeyHex(e.currentTarget.value); 361 | }} 362 | /> 363 |
364 |
365 |
366 |
367 | Block Root: 368 |
369 |
370 | { 376 | setBlockRoot(e.currentTarget.value); 377 | }} 378 | /> 379 |
380 |
381 |
382 |
383 | Signature: 384 |
385 |
386 |