|
1 |
| -import { Modal } from "react-bootstrap"; |
2 |
| -import { MithrilClient } from "@mithril-dev/mithril-client-wasm"; |
| 1 | +import { Modal, Spinner } from "react-bootstrap"; |
| 2 | +import init, { MithrilClient } from "@mithril-dev/mithril-client-wasm"; |
| 3 | +import { useEffect, useState } from "react"; |
| 4 | +import { useSelector } from "react-redux"; |
| 5 | +import LocalDateTime from "../LocalDateTime"; |
3 | 6 |
|
4 | 7 | export default function VerifyCertificate({ show, onClose, certificateHash }) {
|
5 |
| - let aggregatorEdpoint = "https://aggregator.testing-preview.api.mithril.network/aggregator"; |
6 |
| - let genesisVerificationKey = "5b3132372c37332c3132342c3136312c362c3133372c3133312c3231332c3230372c3131372c3139382c38352c3137362c3139392c3136322c3234312c36382c3132332c3131392c3134352c31332c3233322c3234332c34392c3232392c322c3234392c3230352c3230352c33392c3233352c34345d"; |
| 8 | + const currentAggregator = useSelector((state) => state.settings.selectedAggregator); |
| 9 | + const [loading, setLoading] = useState(false); |
| 10 | + const [certificateData, setCertificateData] = useState(null); |
| 11 | + const [verificationDuration, setVerificationDuration] = useState(null); |
7 | 12 |
|
8 |
| - let client = new MithrilClient(aggregatorEdpoint, genesisVerificationKey); |
9 |
| - console.log(client); |
| 13 | + useEffect(() => { |
| 14 | + let startTime; |
| 15 | + async function buildClientAndVerifyChain() { |
| 16 | + try { |
| 17 | + const client = await initializeClient(); |
| 18 | + if (certificateHash !== null && certificateHash !== undefined) { |
| 19 | + startTime = performance.now(); |
| 20 | + const certificate = await client.get_mithril_certificate(certificateHash); |
| 21 | + setCertificateData(certificate); |
| 22 | + await client.verify_certificate_chain(certificateHash); |
10 | 23 |
|
11 |
| - return ( |
12 |
| - <Modal show={show} onHide={onClose} size="xl" aria-labelledby="contained-modal-title-vcenter" centered> |
| 24 | + // Process duration |
| 25 | + const duration = performance.now() - startTime; |
| 26 | + const minutes = Math.floor(duration / 60000); |
| 27 | + const seconds = Math.floor((duration % 60000) / 1000); |
| 28 | + setVerificationDuration( |
| 29 | + minutes > 0 ? `${minutes} minutes and ${seconds} seconds` : `${seconds} seconds`, |
| 30 | + ); |
| 31 | + } |
| 32 | + } catch (error) { |
| 33 | + console.error("Error:", error); |
| 34 | + } finally { |
| 35 | + setLoading(false); |
| 36 | + } |
| 37 | + } |
| 38 | + |
| 39 | + const broadcast_channel = new BroadcastChannel("mithril-client"); |
| 40 | + broadcast_channel.onmessage = eventListener; |
| 41 | + |
| 42 | + if (certificateHash && show) { |
| 43 | + setLoading(true); |
| 44 | + buildClientAndVerifyChain(); |
| 45 | + } else { |
| 46 | + // Reset state when the modal is closed |
| 47 | + setCertificateData(null); |
| 48 | + setLoading(false); |
| 49 | + } |
| 50 | + |
| 51 | + return () => { |
| 52 | + // Cleanup: remove the previous event listener |
| 53 | + broadcast_channel.onmessage = null; |
| 54 | + }; |
| 55 | + }, [show]); // eslint-disable-line react-hooks/exhaustive-deps |
| 56 | + |
| 57 | + async function initializeClient() { |
| 58 | + await init(); |
| 59 | + const genesisVerificationKey = await fetchGenesisVerificationKey(); |
| 60 | + return new MithrilClient(currentAggregator, genesisVerificationKey); |
| 61 | + } |
| 62 | + |
| 63 | + async function fetchGenesisVerificationKey() { |
| 64 | + try { |
| 65 | + let network = currentAggregator.match(/aggregator\.(.*?)\.api/); |
| 66 | + network = network && network[1] ? network[1] : null; |
| 67 | + |
| 68 | + const response = await fetch( |
| 69 | + "https://raw.githubusercontent.com/input-output-hk/mithril/main/mithril-infra/configuration/" + |
| 70 | + network + |
| 71 | + "/genesis.vkey", |
| 72 | + ); |
| 73 | + |
| 74 | + if (!response.ok) { |
| 75 | + throw new Error(`Failed to fetch the genesis verification key. Status: ${response.status}`); |
| 76 | + } |
| 77 | + |
| 78 | + const genesisVerificationKey = await response.text(); |
| 79 | + return genesisVerificationKey; |
| 80 | + } catch (error) { |
| 81 | + console.error("Error fetching genesis verification key:", error.message); |
| 82 | + throw error; |
| 83 | + } |
| 84 | + } |
| 85 | + |
| 86 | + function eventListener(e) { |
| 87 | + let event = e.data; |
| 88 | + if (event.type === "CertificateChainValidationStarted") { |
| 89 | + displayEventInDOM("The certificate chain validation has started..."); |
| 90 | + } else if (event.type === "CertificateValidated") { |
| 91 | + displayEventInDOM( |
| 92 | + "A certificate has been validated, hash: " + event.payload.certificate_hash, |
| 93 | + ); |
| 94 | + } else if (event.type === "CertificateChainValidated") { |
| 95 | + displayEventInDOM("The certificate chain is valid ✅"); |
| 96 | + } else { |
| 97 | + displayEventInDOM(event); |
| 98 | + } |
| 99 | + } |
| 100 | + |
| 101 | + function displayEventInDOM(message) { |
| 102 | + let eventDiv = document.createElement("div"); |
| 103 | + eventDiv.innerHTML = message; |
| 104 | + let mithrilEventsDiv = document.getElementById("mithril-events"); |
| 105 | + mithrilEventsDiv && |
| 106 | + (mithrilEventsDiv.appendChild(eventDiv), |
| 107 | + (mithrilEventsDiv.scrollTop = mithrilEventsDiv.scrollHeight)); |
| 108 | + } |
| 109 | + |
| 110 | + return ( |
| 111 | + <Modal |
| 112 | + show={show} |
| 113 | + onHide={onClose} |
| 114 | + size="xl" |
| 115 | + aria-labelledby="contained-modal-title-vcenter" |
| 116 | + centered> |
13 | 117 | <Modal.Header closeButton>
|
14 |
| - <Modal.Title>Verify Mithril certificate</Modal.Title> |
| 118 | + <Modal.Title>Verify certificate and certificate chain</Modal.Title> |
15 | 119 | </Modal.Header>
|
16 | 120 | <Modal.Body>
|
| 121 | + {certificateData && ( |
| 122 | + <> |
| 123 | + <h4>Certificate Details</h4> |
| 124 | + <div>Certificate hash: {certificateData.hash}</div> |
| 125 | + <div style={{ display: "flex", justifyContent: "space-between" }}> |
| 126 | + <div> |
| 127 | + Sealed at: <LocalDateTime datetime={certificateData.metadata.sealed_at} /> |
| 128 | + </div> |
| 129 | + {loading ? ( |
| 130 | + <div style={{ display: "flex", alignItems: "center" }}> |
| 131 | + <div style={{ marginLeft: "10px", paddingRight: "10px" }}> |
| 132 | + Verifying the certificate chain... |
| 133 | + </div> |
| 134 | + <Spinner animation="border" variant="primary" /> |
| 135 | + </div> |
| 136 | + ) : ( |
| 137 | + <div style={{ marginLeft: "10px", paddingRight: "10px" }}> |
| 138 | + Verification duration: {verificationDuration} |
| 139 | + </div> |
| 140 | + )} |
| 141 | + </div> |
| 142 | + <hr /> |
| 143 | + <div> |
| 144 | + <div |
| 145 | + id="mithril-events" |
| 146 | + style={{ |
| 147 | + height: "400px", |
| 148 | + maxHeight: "400px", |
| 149 | + overflowY: "auto", |
| 150 | + marginTop: "10px", |
| 151 | + }}></div> |
| 152 | + </div> |
| 153 | + </> |
| 154 | + )} |
17 | 155 | </Modal.Body>
|
18 |
| - <Modal.Footer> |
19 |
| - </Modal.Footer> |
| 156 | + <Modal.Footer></Modal.Footer> |
20 | 157 | </Modal>
|
21 | 158 | );
|
22 | 159 | }
|
0 commit comments