diff --git a/react/src/Verifier.js b/react/src/Verifier.js index 4cae269..6132608 100644 --- a/react/src/Verifier.js +++ b/react/src/Verifier.js @@ -1,20 +1,21 @@ -import React, { useEffect, useMemo, useState } from 'react'; -import CreatableSelect from 'react-select/creatable'; -import JournalParser from './JournalParser.js'; +import React, { useEffect, useMemo, useState } from "react"; +import CreatableSelect from "react-select/creatable"; +import JournalParser from "./JournalParser.js"; -const DEFAULT_REGISTRY = 'https://risc0.verify.eqtylab.io/registry.json'; -const DEFAULT_IPFS_GATEWAY = 'https://w3s.link'; +const DEFAULT_REGISTRY = "https://risc0.verify.eqtylab.io/registry.json"; +const DEFAULT_IPFS_GATEWAY = "https://w3s.link"; const cssPrefix = "risc-zero-verifier"; export const defaultText = { verifyButtonLabel: "Verify", restartButtonLabel: "Restart", - instructions: "Upload a receipt file as JSON or binary (bincode format), or paste it as JSON into the form field.", + instructions: + "Upload a receipt file as JSON or binary (bincode format), or paste it as JSON into the form field.", fieldLabels: { guestCodeId: "Guest code id (hex value):", receiptFile: "Receipt (bincode format binary file or JSON):", - receiptJson: "Receipt JSON:" + receiptJson: "Receipt JSON:", }, verificationResults: { verified: "Receipt verified.", @@ -22,32 +23,39 @@ export const defaultText = { }, errors: { guestCodeIdMissing: "Please enter a guest code id.", - receiptFileMissing: "Please provide a receipt file." - } -} + receiptFileMissing: "Please provide a receipt file.", + }, +}; function Verifier({ text = defaultText, instanceNumber = 0, enableJournalParser = false, registryUrl = DEFAULT_REGISTRY, - ipfsGateway = DEFAULT_IPFS_GATEWAY + ipfsGateway = DEFAULT_IPFS_GATEWAY, + onStatus = (result) => { + console.log(result); + }, }) { const [verifier, setVerifier] = useState(null); useEffect(() => { (async () => { const wasmPackage = await import("@eqty/risc-zero-verifier"); - const verifier = await wasmPackage.default + const verifier = await wasmPackage.default; setVerifier(verifier); })(); }, []); - const [guestCodeId, setGuestCodeId] = useState(''); - const [receiptBinary, setReceiptBinary] = useState(''); - const [receiptJson, setReceiptJson] = useState(''); + const [guestCodeId, setGuestCodeId] = useState(""); + const [receiptBinary, setReceiptBinary] = useState(""); + const [receiptJson, setReceiptJson] = useState(""); const [verificationResult, setVerificationResult] = useState(undefined); + useEffect(() => { + onStatus(verificationResult); + }, [verificationResult]); + const receiptJournalBytes = useMemo(() => { try { const journal = JSON.parse(receiptJson).journal; @@ -71,19 +79,16 @@ function Verifier({ })(); }, [registryUrl]); - const parsers = useMemo(() => - registry ? registry.parsers : [], - [registry] - ); + const parsers = useMemo(() => (registry ? registry.parsers : []), [registry]); function verifyRiscZeroReceipt(guestCodeId, receiptJson) { if (!guestCodeId) { return { result: false, - error: text.errors.guestCodeIdMissing + error: text.errors.guestCodeIdMissing, }; } - + try { if (receiptBinary) { return verifier.verify_receipt_binary(guestCodeId, receiptBinary); @@ -92,7 +97,7 @@ function Verifier({ } else { return { result: false, - error: text.errors.receiptFileMissing + error: text.errors.receiptFileMissing, }; } } catch (error) { @@ -136,7 +141,9 @@ function Verifier({ function guestCodeIdOptions() { return parsers.map((parser) => ({ value: parser.guestCodeId, - label: `${shortenGuestCodeId(parser.guestCodeId)} (${parser.profile.name} ${parser.profile.version})` + label: `${shortenGuestCodeId(parser.guestCodeId)} (${ + parser.profile.name + } ${parser.profile.version})`, })); } @@ -153,39 +160,77 @@ function Verifier({

{text.instructions}

{verifier && ( -

Receipts must be generated with risc0-zkvm rust crate version {verifier.get_risc0_version()}.

+

+ Receipts must be generated with risc0-zkvm rust crate + version {verifier.get_risc0_version()}. +

)}
- + setGuestCodeId(option ? option.value : '')} + onChange={(option) => setGuestCodeId(option ? option.value : "")} isDisabled={verificationResult !== undefined} placeholder="Select or enter a guest code id..." formatCreateLabel={(inputValue) => `Use "${inputValue}"`} />
+
+
+
- - + +
- {verificationResult === undefined ? - - : - - } + {verificationResult === undefined ? ( + + ) : ( + + )}
{verificationResult && ( -
+

- { - verificationResult.verified === true ? text.verificationResults.verified : `${text.verificationResults.notVerified} ${verificationResult.error}` - } + {verificationResult.verified === true + ? text.verificationResults.verified + : `${text.verificationResults.notVerified} ${verificationResult.error}`}

{verificationResult.verified === true && enableJournalParser && ( )} -
)} -
); } diff --git a/web/package.json b/web/package.json index ac3515c..10dc6f8 100644 --- a/web/package.json +++ b/web/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@eqty/risc-zero-verifier-react": "*", + "classnames": "^2.5.1", "copy-webpack-plugin": "^12.0.2", "css-loader": "^6.10.0", "react": "^18.2.0", diff --git a/web/src/App.css b/web/src/App.css index 1f33de3..94a81a6 100644 --- a/web/src/App.css +++ b/web/src/App.css @@ -1,67 +1,198 @@ +@font-face { + font-family: "TWKLausanne"; + src: url("./assets/fonts/TWKLausanne-300.woff2") format("woff2"); + font-weight: 300; + font-style: normal; +} +@font-face { + font-family: "TWKLausanne"; + src: url("./assets/fonts/TWKLausanne-550.woff2") format("woff2"); + font-weight: 550; + font-style: normal; +} +@font-face { + font-family: "IBM Plex Mono"; + src: url("./assets/fonts/ibm-plex-mono-latin-400-normal.woff2") + format("woff2"); + font-weight: 400; + font-style: normal; +} + +:root { + --failed-border: #ff6682; + --failed-color: #99001c; + --failed-bg: #ffccd5; + --failed-icon-bg: #99001c; + + --pending-color: #994000; + --pending-bg: #ffe1cc; + + --success-icon-bg: #178235; + --success-bg: #ceffdc; + --success-color: black; + --success-border: #475569; +} + +body { + position: relative; + padding: 0; + margin: 0; + font-size: 16px; + + font-family: "TWKLausanne", serif; + box-sizing: border-box; + background-color: #000; + color: white; +} + +.verified-bg { + opacity: 0; + transition: opacity 0.5s; + z-index: -1; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(237deg, #76b900 0%, #2b7e00 68.13%); +} + +.all-verified .verified-bg { + opacity: 1; +} + .App { text-align: center; + background: black; } .risc-zero-journal-parser-journal-raw { - width: 75%; height: 80px; } - + .risc-zero-journal-parser-statement-markdown { padding: 24px; text-align: left; } - .risc-zero-verifier-main { font-family: Arial, sans-serif; - max-width: 600px; + max-width: 75%; margin: auto; padding: 20px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); -label { - display: block; - margin-bottom: 5px; - font-weight: bold; -} + display: grid; + gap: 10px; + grid-template-areas: + /* i: intro */ + /* g: guest id */ + /* a: animation */ + /* r: receipt */ + /* b: button */ + /* v: verified result */ + "i i i" + "g a r" + ". b ." + "v v v"; + + label { + display: block; + margin-bottom: 5px; + font-weight: bold; + } + + button { + display: flex; + padding: 8px 16px; + justify-content: center; + align-items: center; + cursor: pointer; + flex-grow: 0; + height: 33px; + + border: 0; + border-radius: 0; + + background: black; + color: white; + border: 1px solid white; + } input[type="text"], input[type="file"], - textarea, - button { + textarea { width: 100%; padding: 8px; - border: 1px solid #ccc; - border-radius: 4px; + border: 1px solid white; + box-sizing: border-box; margin-bottom: 10px; cursor: pointer; } + input:disabled { + color: white; + } + textarea { + border: 2px solid var(--success-border); + } } - .risc-zero-verifier-instructions-container { - background-color: lightblue; - margin-bottom: 20px; - border-radius: 4px; - border: 1px solid #ccc; - p { - padding: 0px 20px; + grid-area: i; +} +.risc-zero-verifier-guest-code-id-container { + grid-area: g; + color: black; + label { + color: white; } } +.risc-zero-verifier-verification-animation-container { + grid-area: a; + display: grid; + place-items: center; +} +.risc-zero-verifier-verification-animation { + width: 80px; + height: 80px; + border-radius: 100px; + background-repeat: no-repeat; + background-position: center; +} + +.App.is-verified .risc-zero-verifier-verification-animation { + background-color: var(--success-icon-bg); + background-image: url('data:image/svg+xml,'); +} +.App.is-unverified .risc-zero-verifier-verification-animation { + background-color: var(--failed-icon-bg); + background-image: url('data:image/svg+xml,'); +} + +.risc-zero-verifier-receipt-file-input-container { + grid-area: r; +} +.risc-zero-verifier-verify-button-container { + grid-area: b; + display: grid; + place-items: center; + padding-top: 20px; + padding-bottom: 20px; +} +.risc-zero-verifier-receipt-verification-result { + grid-area: v; +} + +.risc-zero-verifier-instructions-container { + margin-bottom: 50px; + border: 1px solid white; + padding: 0px 20px; +} .risc-zero-verifier-guest-code-id-container, .risc-zero-verifier-guest-code-id-select-container, -.risc-zero-verifier-receipt-input-container, -.risc-zero-verifier-verify-button-container { - margin-bottom: 20px; - .risc-zero-verifier-verify-button { - background-color: #4CAF50; - color: white; - } - .risc-zero-verifier-verify-button button:hover { - background-color: #45a049; - } +.risc-zero-verifier-receipt-input-container { + margin-bottom: 40px; } .risc-zero-verifier-receipt-json-input-container textarea { @@ -77,11 +208,13 @@ label { } .risc-zero-verifier-receipt-verification-result-verified { - background-color: lightgreen; + background-color: var(--success-bg); + color: var(--success-color); } .risc-zero-verifier-receipt-verification-result-unverified { - background-color: lightcoral; + background-color: var(--failed-bg); + color: var(--failed-color); } .risc-zero-verifier-receipt-verification-result-message { @@ -89,12 +222,12 @@ label { } .risc-zero-journal-parser-main { - font-family: Arial, sans-serif; + /* font-family: Arial, sans-serif; */ max-width: 800px; margin: auto; padding: 20px; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); - + /* box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); */ + h2 { text-align: center; margin-bottom: 20px; @@ -104,10 +237,9 @@ label { margin-bottom: 10px; } select { - width: 100%; padding: 8px; - border: 1px solid #ccc; - border-radius: 4px; + border: 1px solid white; + /* border-radius: 4px; */ box-sizing: border-box; margin-bottom: 10px; } @@ -125,27 +257,81 @@ label { } .risc-zero-journal-parser-statement-markdown { - border: 1px solid #ccc; - border-radius: 4px; + border: 1px solid white; padding: 10px; background-color: #f9f9f9; + + color: white; + font-family: "IBM Plex Mono", sans-serif; + + font-size: 10px; + font-style: normal; + font-weight: 400; + line-height: 120%; + + border: 2px solid var(--success-border); + + background: var(--Grey-900, #0f172a); + box-shadow: 2px 4px 24px 0px rgba(0, 0, 0, 0.24); + margin-bottom: 5px; + padding: 5px; } .risc-zero-journal-parser-journal-raw { width: 100%; height: 200px; - border: 1px solid #ccc; - border-radius: 4px; - padding: 10px; box-sizing: border-box; margin-bottom: 10px; background-color: #f9f9f9; resize: vertical; + width: 100%; + color: white; + font-family: "IBM Plex Mono", sans-serif; + + font-size: 10px; + font-style: normal; + font-weight: 400; + line-height: 120%; /* 16.8px */ + + border: 2px solid var(--success-border); + + /* border-radius: 10px; */ + background: var(--Grey-900, #0f172a); + box-shadow: 2px 4px 24px 0px rgba(0, 0, 0, 0.24); + margin-bottom: 5px; + padding: 5px; } .risc-zero-journal-parser-JSONTree { - border: 1px solid #ccc; - border-radius: 4px; + border: 2px solid var(--success-border); + /* border-radius: 4px; */ padding: 10px; background-color: #f9f9f9; } +.risc-zero-journal-parser-journal-json-container ul { + background: transparent; +} +.risc-zero-journal-parser-journal-json-container { +} + +.risc-zero-journal-parser-journal-json-container > div > h3 + div { + font-size: 10px; + font-style: normal; + font-weight: 400; + text-align: left; + + color: white; + font-family: "IBM Plex Mono", sans-serif; + + border: 2px solid var(--success-border); + + background: var(--Grey-900, #0f172a); + box-shadow: 2px 4px 24px 0px rgba(0, 0, 0, 0.24); + margin-bottom: 5px; + padding: 15px; + + ul { + /* overwrite an inline style.... */ + background-color: transparent !important; + } +} diff --git a/web/src/App.js b/web/src/App.js index 1a16017..365de4b 100644 --- a/web/src/App.js +++ b/web/src/App.js @@ -1,21 +1,56 @@ -import React, { useState } from 'react'; -import './App.css'; +import React, { useState } from "react"; +import "./App.css"; import { Verifier } from "@eqty/risc-zero-verifier-react"; +import cx from "classnames"; + +const VERIFICATION = { + NONE: "none", + VERIFIED: "verified", + UNVERIFIED: "un-verified", +}; function App() { + const [isVerified, setIsVerified] = useState(VERIFICATION.NONE); + const onVerifierStatus = (result) => { + if (result) { + console.log("🚀 ~ onVerifierStatus ~ result:", result.verified); + + setIsVerified( + result.verified ? VERIFICATION.VERIFIED : VERIFICATION.UNVERIFIED + ); + } + }; + + const classList = cx("App", { + "is-verified": isVerified === VERIFICATION.VERIFIED, + "is-unverified": isVerified === VERIFICATION.UNVERIFIED, + }); + return ( -
+

RISC Zero Verifier

-

This is a verifier for RISC Zero zkVM execution receipts.

+

+ This is a verifier for{" "} + RISC Zero zkVM{" "} + execution receipts. +

-

This verifier is developed by EQTY Lab and the code is available on GitHub.

+

+ This verifier is developed by{" "} + EQTY Lab and the code is{" "} + + available on GitHub + + . +

); diff --git a/web/src/assets/fonts/TWKLausanne-300.woff2 b/web/src/assets/fonts/TWKLausanne-300.woff2 new file mode 100644 index 0000000..b1ab539 Binary files /dev/null and b/web/src/assets/fonts/TWKLausanne-300.woff2 differ diff --git a/web/src/assets/fonts/TWKLausanne-550.woff2 b/web/src/assets/fonts/TWKLausanne-550.woff2 new file mode 100644 index 0000000..3a671c2 Binary files /dev/null and b/web/src/assets/fonts/TWKLausanne-550.woff2 differ diff --git a/web/src/assets/fonts/ibm-plex-mono-latin-400-normal.woff2 b/web/src/assets/fonts/ibm-plex-mono-latin-400-normal.woff2 new file mode 100644 index 0000000..a6c77d6 Binary files /dev/null and b/web/src/assets/fonts/ibm-plex-mono-latin-400-normal.woff2 differ diff --git a/yarn.lock b/yarn.lock index 6cd6175..5955839 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2762,6 +2762,11 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +classnames@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" + integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== + clean-css@^5.2.2: version "5.3.3" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.3.tgz#b330653cd3bd6b75009cc25c714cae7b93351ccd"