mirror of
https://github.com/tlsnotary/tlsn-plugin-demo.git
synced 2026-01-09 21:37:55 -05:00
Interactive verifier demo (#35)
* Add interactive verifier demo * Serve and load plugin from local file * Show screenname at the end + removed attestation code * No more need for tlsn-js * Updated Docker container * Use environment variable to enable/disable POAPs * Improved UI * use LRU for tracking sessions Co-authored-by: Hendrik Eeckhaut <hendrik@eeckhaut.org>
This commit is contained in:
@@ -37,7 +37,10 @@ export default function Button(props: Props): ReactElement {
|
||||
{...btnProps}
|
||||
>
|
||||
{loading ? (
|
||||
<Icon className="animate-spin" fa="fa-solid fa-spinner" size={2} />
|
||||
<>
|
||||
<span>Running TLSNotary plugin...</span>
|
||||
<div className="animate-spin rounded-full h-5 w-5 border-2 border-white border-t-transparent"></div>
|
||||
</>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
|
||||
@@ -5,10 +5,9 @@ import Step from '@mui/material/Step';
|
||||
import Box from '@mui/material/Box';
|
||||
import StepLabel from '@mui/material/StepLabel';
|
||||
import classNames from 'classnames';
|
||||
import type { PresentationJSON } from 'tlsn-js/build/types';
|
||||
import Button from '../Button';
|
||||
import ConfettiExplosion, { ConfettiProps } from 'react-confetti-explosion';
|
||||
import { formatDataPreview } from '../../utils/utils';
|
||||
import OverviewSvg from '../../../static/overview_prover_verifier.svg';
|
||||
|
||||
const steps = ['Connect Extension', 'Run Plugin'];
|
||||
|
||||
@@ -16,9 +15,8 @@ export default function Steps(): ReactElement {
|
||||
const [extensionInstalled, setExtensionInstalled] = useState(false);
|
||||
const [step, setStep] = useState<number>(0);
|
||||
const [client, setClient] = useState<any>(null);
|
||||
const [pluginData, setPluginData] = useState<PresentationJSON | null>(null);
|
||||
const [sessionId, setSessionId] = useState<string>('');
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [transcript, setTranscript] = useState<any>(null);
|
||||
const [screenName, setScreenName] = useState<string>('');
|
||||
const [exploding, setExploding] = useState<boolean>(false);
|
||||
|
||||
@@ -53,17 +51,6 @@ export default function Steps(): ReactElement {
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (transcript) {
|
||||
const match = transcript.recv.match(/"screen_name":"([^"]+)"/);
|
||||
const screenName = match ? match[1] : null;
|
||||
setScreenName(screenName);
|
||||
if (screenName) {
|
||||
setExploding(true);
|
||||
}
|
||||
}
|
||||
}, [transcript]);
|
||||
|
||||
async function handleConnect() {
|
||||
try {
|
||||
//@ts-ignore
|
||||
@@ -77,21 +64,24 @@ export default function Steps(): ReactElement {
|
||||
async function handleRunPlugin() {
|
||||
try {
|
||||
setLoading(true);
|
||||
const pluginData = await client.runPlugin(
|
||||
'https://raw.githubusercontent.com/tlsnotary/tlsn-extension/cc3264f058ad2ebb0791830a1217fdd8bffd543f/src/assets/plugins/twitter_profile.wasm',
|
||||
const _sessionId = await client.runPlugin(
|
||||
window.location.origin + '/twitter_profile.tlsn.wasm',
|
||||
);
|
||||
setPluginData(pluginData);
|
||||
console.log(pluginData);
|
||||
const response = await fetch('/verify-attestation', {
|
||||
setSessionId(_sessionId);
|
||||
console.log('Session ID:', _sessionId);
|
||||
|
||||
const response = await fetch('/check-session', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ attestation: pluginData }),
|
||||
body: JSON.stringify({ session_id: _sessionId }),
|
||||
});
|
||||
console.log('Check session response:', response);
|
||||
if (response.status === 200) {
|
||||
const data = await response.json();
|
||||
setTranscript(data.presentationObj);
|
||||
console.log('Response: Plugin data:', data);
|
||||
setScreenName(data.screen_name);
|
||||
setStep(1);
|
||||
} else {
|
||||
console.log(await response.text());
|
||||
@@ -108,7 +98,7 @@ export default function Steps(): ReactElement {
|
||||
{extensionInstalled ? (
|
||||
<>
|
||||
<div className="flex flex-row items-center gap-2 text-slate-600 font-bold pb-2">
|
||||
Connected{' '}
|
||||
Extension Connected{' '}
|
||||
<div
|
||||
className={classNames(
|
||||
'rounded-full h-[10px] w-[10px] border-[2px]',
|
||||
@@ -119,61 +109,197 @@ export default function Steps(): ReactElement {
|
||||
)}
|
||||
></div>
|
||||
</div>
|
||||
<Box className="w-full max-w-3xl mt-6 pb-4">
|
||||
<Stepper activeStep={step} alternativeLabel>
|
||||
{steps.map((label) => (
|
||||
<Step key={label}>
|
||||
<StepLabel>{label}</StepLabel>
|
||||
</Step>
|
||||
))}
|
||||
</Stepper>
|
||||
</Box>
|
||||
<div className="flex gap-3">
|
||||
{step === 0 && (
|
||||
<button onClick={handleConnect} className="button">
|
||||
Connect
|
||||
</button>
|
||||
)}
|
||||
{step === 1 && !pluginData && (
|
||||
<div className="flex flex-col items-center justify-center gap-2">
|
||||
<ul className="flex flex-col items-center justify-center gap-1">
|
||||
<li className="text-base font-light">
|
||||
This will open a new tab to Twitter/X and the sidebar for
|
||||
the extension
|
||||
</li>
|
||||
<li className="text-base font-light">
|
||||
Click through the steps in the sidebar
|
||||
</li>
|
||||
<li className="text-base font-light">
|
||||
Don't close the sidebar or refresh the page until
|
||||
notarization is finished
|
||||
</li>
|
||||
<li className="text-base font-light">
|
||||
If successful, the attestation and verified data will be
|
||||
displayed below
|
||||
</li>
|
||||
</ul>
|
||||
<Button onClick={handleRunPlugin} loading={loading}>
|
||||
Run Plugin
|
||||
</Button>
|
||||
{step === 1 && !sessionId && (
|
||||
<div className="flex flex-col items-center justify-center gap-6 max-w-4xl">
|
||||
<div className="text-center space-y-4 w-full flex flex-col items-center">
|
||||
<h2 className="text-2xl font-semibold text-gray-900">
|
||||
Ready to Prove Your Twitter Identity
|
||||
</h2>
|
||||
<p className="text-lg text-gray-600 max-w-2xl">
|
||||
Click the button below to start the verification process
|
||||
</p>
|
||||
|
||||
<Button
|
||||
onClick={handleRunPlugin}
|
||||
loading={loading}
|
||||
className="bg-blue-600 hover:bg-blue-700 !text-white px-12 py-4 text-xl font-semibold min-w-[300px] shadow-lg rounded-lg transition-all duration-200"
|
||||
>
|
||||
{loading ? (
|
||||
<>
|
||||
<div className="animate-spin rounded-full h-6 w-6 border-2 border-white border-t-transparent"></div>
|
||||
<span>Processing...</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
🔐 Prove Twitter Screen Name
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="bg-blue-50 border border-blue-200 rounded-lg p-6 w-full">
|
||||
|
||||
<h3 className="text-lg font-semibold text-blue-900 mb-4 text-center">
|
||||
What happens when you click "Prove Twitter Screen Name"?
|
||||
</h3>
|
||||
|
||||
<div className="space-y-4 text-gray-700">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="bg-blue-500 text-white rounded-full w-6 h-6 flex items-center justify-center text-sm font-semibold flex-shrink-0 mt-0.5">
|
||||
1
|
||||
</div>
|
||||
<p className="text-base leading-relaxed">
|
||||
The TLSNotary extension will open a popup, asking permission to run the plugin and send the unredacted data (just the screen name) to the verifier server.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="bg-blue-500 text-white rounded-full w-6 h-6 flex items-center justify-center text-sm font-semibold flex-shrink-0 mt-0.5">
|
||||
2
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-base leading-relaxed mb-2">
|
||||
If you accept, the extension will open X/Twitter in a new tab with a sidebar showing these steps:
|
||||
</p>
|
||||
<div className="ml-4 space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 bg-blue-300 rounded-full"></div>
|
||||
<span className="text-sm text-gray-600">Go to your Twitter profile</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 bg-blue-300 rounded-full"></div>
|
||||
<span className="text-sm text-gray-600">Log in if you haven't yet</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 bg-blue-300 rounded-full"></div>
|
||||
<span className="text-sm text-gray-600">The extension proves your Twitter handle to the verifier server</span>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-base leading-relaxed mb-2">
|
||||
Click on the buttons in the sidebar to proceed.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="bg-blue-500 text-white rounded-full w-6 h-6 flex items-center justify-center text-sm font-semibold flex-shrink-0 mt-0.5">
|
||||
3
|
||||
</div>
|
||||
<p className="text-base leading-relaxed">
|
||||
{process.env.POAP === 'true' ? (
|
||||
<>
|
||||
If successful, your screen name will be shown here and you can claim a POAP.
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
If successful, your screen name will be shown here.
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 p-3 bg-yellow-50 border border-yellow-200 rounded">
|
||||
<p className="text-sm text-yellow-800">
|
||||
💡 <strong>Tip:</strong> When step 3 is running, you can close the Twitter/X tab, but don't close the sidebar.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{step === 1 && pluginData && screenName && (
|
||||
<div className="flex flex-col items-center justify-center gap-2">
|
||||
<h3 className="text-lg font-semibold text-center">
|
||||
Optional: Claim Your POAP
|
||||
</h3>
|
||||
<ClaimPoap screen_name={screenName} exploding={exploding} />
|
||||
{step === 1 && sessionId && screenName && (
|
||||
<div className="flex flex-col items-center justify-center gap-6 max-w-4xl w-full">
|
||||
|
||||
{/* Success Header with Animation */}
|
||||
<div className="text-center space-y-4 animate-fade-in">
|
||||
<div className="bg-green-100 border-2 border-green-300 rounded-full w-20 h-20 flex items-center justify-center mx-auto mb-4 animate-bounce">
|
||||
<span className="text-4xl">✅</span>
|
||||
</div>
|
||||
|
||||
<h2 className="text-3xl font-bold text-green-800 mb-2">
|
||||
🎉 Verification Successful! 🎉
|
||||
</h2>
|
||||
|
||||
<div className="bg-gradient-to-r from-green-50 to-emerald-50 border-2 border-green-200 rounded-xl p-6 shadow-lg">
|
||||
<h3 className="text-xl font-semibold text-gray-900 mb-2">
|
||||
Successfully verified your Twitter identity
|
||||
</h3>
|
||||
<div className="bg-white rounded-lg p-4 border border-green-200">
|
||||
<p className="text-lg text-gray-700">
|
||||
Screen name: <span className="font-bold text-green-700 text-xl">@{screenName}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* POAP Section */}
|
||||
{process.env.POAP === 'true' && (
|
||||
<div className="bg-gradient-to-r from-yellow-50 to-amber-50 border-2 border-yellow-300 rounded-xl p-8 w-full shadow-lg">
|
||||
<div className="text-center space-y-4">
|
||||
<div className="bg-yellow-100 border-2 border-yellow-300 rounded-full w-16 h-16 flex items-center justify-center mx-auto">
|
||||
<span className="text-3xl">🎁</span>
|
||||
</div>
|
||||
|
||||
<h3 className="text-2xl font-bold text-yellow-800">
|
||||
Claim Your Reward!
|
||||
</h3>
|
||||
|
||||
<p className="text-lg text-yellow-700 max-w-2xl mx-auto">
|
||||
You've successfully proven your Twitter identity! Now claim your exclusive POAP token as proof of this achievement.
|
||||
</p>
|
||||
|
||||
<div className="pt-4">
|
||||
<ClaimPoap sessionId={sessionId} exploding={exploding} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* What's Next Section */}
|
||||
<div className="bg-blue-50 border border-blue-200 rounded-xl p-6 w-full">
|
||||
<h4 className="text-lg font-semibold text-blue-900 mb-3 text-center">
|
||||
What just happened?
|
||||
</h4>
|
||||
<div className="grid md:grid-cols-2 gap-4 text-sm text-blue-800">
|
||||
<div className="flex items-start gap-2">
|
||||
<span className="text-blue-600">🔒</span>
|
||||
<div>
|
||||
<p className="font-semibold">Privacy Preserved</p>
|
||||
<p>Your sensitive data stayed private - only your screen name was verified</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-2">
|
||||
<span className="text-blue-600">🛡️</span>
|
||||
<div>
|
||||
<p className="font-semibold">Cryptographic Proof</p>
|
||||
<p>TLSNotary created a verifiable proof without exposing your credentials to the verifier</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Try Again Button */}
|
||||
<div className="text-center">
|
||||
<button
|
||||
onClick={() => {
|
||||
setSessionId('');
|
||||
setScreenName('');
|
||||
setStep(1);
|
||||
}}
|
||||
className="bg-gray-500 hover:bg-gray-600 text-white px-8 py-3 rounded-lg font-semibold transition-colors"
|
||||
>
|
||||
🔄 Try Again
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{pluginData && (
|
||||
<DisplayPluginData
|
||||
step={step}
|
||||
pluginData={pluginData}
|
||||
transcript={transcript}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<InstallExtensionPrompt />
|
||||
@@ -182,66 +308,12 @@ export default function Steps(): ReactElement {
|
||||
);
|
||||
}
|
||||
|
||||
function DisplayPluginData({
|
||||
step,
|
||||
pluginData,
|
||||
transcript,
|
||||
}: {
|
||||
step: number;
|
||||
pluginData: any;
|
||||
transcript: any;
|
||||
}): ReactElement {
|
||||
const [tab, setTab] = useState<'sent' | 'recv'>('sent');
|
||||
|
||||
return (
|
||||
<div className="flex justify-center items-center space-x-4 mt-8">
|
||||
<div className="w-96">
|
||||
<div className="p-2 bg-gray-200 border-t rounded-t-md text-center text-lg font-semibold">
|
||||
Attestation
|
||||
</div>
|
||||
<div className="p-4 bg-gray-100 border rounded-b-md h-96 text-left overflow-auto">
|
||||
<pre className="text-sm text-gray-700 whitespace-pre-wrap text-[12px]">
|
||||
{formatDataPreview(pluginData)}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-96">
|
||||
<div className="p-2 bg-gray-200 border-t rounded-t-md text-center text-lg font-semibold">
|
||||
Presentation
|
||||
</div>
|
||||
<div className="bg-gray-100 border rounded-b-md h-96 overflow-auto">
|
||||
<div className="flex border-b">
|
||||
<button
|
||||
onClick={() => setTab('sent')}
|
||||
className={`p-2 w-1/2 text-center ${tab === 'sent' ? 'bg-slate-500 text-white' : 'bg-white text-black'}`}
|
||||
>
|
||||
Sent
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setTab('recv')}
|
||||
className={`p-2 w-1/2 text-center ${tab === 'recv' ? 'bg-slate-500 text-white' : 'bg-white text-black'}`}
|
||||
>
|
||||
Received
|
||||
</button>
|
||||
</div>
|
||||
<div className="p-4 text-left">
|
||||
<pre className="text-[10px] text-gray-700 whitespace-pre-wrap">
|
||||
{transcript &&
|
||||
(tab === 'sent' ? transcript.sent : transcript.recv)}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ClaimPoap({
|
||||
screen_name,
|
||||
exploding,
|
||||
sessionId,
|
||||
}: {
|
||||
screen_name: string;
|
||||
exploding: boolean;
|
||||
sessionId?: string;
|
||||
}): ReactElement {
|
||||
const [poapLink, setPoapLink] = useState<string>('');
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -251,13 +323,13 @@ function ClaimPoap({
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
if (!screen_name) return;
|
||||
if (!sessionId) return;
|
||||
const response = await fetch('/poap-claim', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ screenName: screen_name }),
|
||||
body: JSON.stringify({ sessionId }),
|
||||
});
|
||||
if (response.status === 200) {
|
||||
const data = await response.json();
|
||||
@@ -301,36 +373,131 @@ function ClaimPoap({
|
||||
}
|
||||
|
||||
function InstallExtensionPrompt() {
|
||||
const handleRefresh = () => {
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col justify-center items-center gap-2">
|
||||
<div className="flex flex-col justify center items-center gap-2 pb-4">
|
||||
<h1 className="text-base font-light">
|
||||
<div className="flex flex-col items-center gap-8 max-w-4xl mx-auto p-6">
|
||||
{/* Header Section */}
|
||||
<div className="text-center space-y-4">
|
||||
<h1 className="text-3xl font-bold text-gray-900">
|
||||
Welcome to the TLSNotary Plugin Demo!
|
||||
</h1>
|
||||
<p className="text-base font-light">
|
||||
This demo shows how TLSNotary can be used to verify private user data
|
||||
in a webapp.
|
||||
</p>
|
||||
<p className="text-base font-light">
|
||||
In this demo you'll prove that you own a Twitter/X account to the
|
||||
webserver.
|
||||
</p>
|
||||
<p className="text-base font-light">
|
||||
The webserver will verify your attestation and give a POAP in return (
|
||||
<span className="font-semibold">while supplies last</span>)
|
||||
<p className="text-xl text-gray-600 leading-relaxed">
|
||||
Verify private user data in web applications using zero-knowledge proofs
|
||||
</p>
|
||||
<img className="mx-auto max-w-full h-auto mt-4" src={OverviewSvg} alt="TLSNotary Prover-Verifier Overview" />
|
||||
</div>
|
||||
|
||||
{/* Demo Description */}
|
||||
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 border border-blue-200 rounded-xl p-8 w-full">
|
||||
<h2 className="text-2xl font-semibold text-blue-900 mb-6 text-center">
|
||||
How this demo works
|
||||
</h2>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-6">
|
||||
<div className="text-center">
|
||||
<div className="bg-blue-500 text-white rounded-full w-12 h-12 flex items-center justify-center mx-auto mb-4 text-lg font-bold">
|
||||
1
|
||||
</div>
|
||||
<h3 className="font-semibold text-gray-900 mb-2">Connect Extension</h3>
|
||||
<p className="text-gray-600 text-sm">
|
||||
Install and connect the TLSNotary browser extension
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="text-center">
|
||||
<div className="bg-blue-500 text-white rounded-full w-12 h-12 flex items-center justify-center mx-auto mb-4 text-lg font-bold">
|
||||
2
|
||||
</div>
|
||||
<h3 className="font-semibold text-gray-900 mb-2">Prove Ownership</h3>
|
||||
<p className="text-gray-600 text-sm">
|
||||
Prove you own a Twitter/X account without revealing sensitive data
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="text-center">
|
||||
<div className="bg-blue-500 text-white rounded-full w-12 h-12 flex items-center justify-center mx-auto mb-4 text-lg font-bold">
|
||||
3
|
||||
</div>
|
||||
<h3 className="font-semibold text-gray-900 mb-2">
|
||||
{process.env.POAP === 'true' ? 'Get Rewarded' : 'Verification Complete'}
|
||||
</h3>
|
||||
<p className="text-gray-600 text-sm">
|
||||
{process.env.POAP === 'true'
|
||||
? 'Receive a POAP token as proof of verification'
|
||||
: 'Your Twitter screen name is verified'
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{process.env.POAP === 'true' && (
|
||||
<div className="mt-6 p-4 bg-yellow-50 border border-yellow-200 rounded-lg">
|
||||
<p className="text-center text-yellow-800">
|
||||
🎁 <strong>Special offer:</strong> Get a POAP (Proof of Attendance Protocol) token after verification!{' '}<br />
|
||||
<span className="font-semibold">(while supplies last)</span>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Installation Section */}
|
||||
<div className="bg-white border-2 border-gray-200 rounded-xl p-8 w-full shadow-sm">
|
||||
<div className="text-center space-y-6">
|
||||
<div className="space-y-2">
|
||||
<h2 className="text-xl font-semibold text-gray-900">
|
||||
Get Started
|
||||
</h2>
|
||||
<p className="text-gray-600">
|
||||
Install the TLSNotary extension to begin the verification process
|
||||
</p>
|
||||
{/* Add the manual refresh notice */}
|
||||
<div className="bg-blue-50 border border-blue-200 rounded-lg p-3 mt-4">
|
||||
<p className="text-sm text-blue-800">
|
||||
ℹ️ <strong>Note:</strong> This page cannot automatically detect when the extension is installed.
|
||||
You'll need to refresh the page after installation. We've added a refresh button below for your convenience.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
|
||||
<a
|
||||
href="https://chromewebstore.google.com/detail/tlsn-extension/gcfkkledipjbgdbimfpijgbkhajiaaph"
|
||||
target="_blank"
|
||||
className="button bg-blue-600 hover:bg-blue-700 text-white px-8 py-3 text-lg font-semibold min-w-[200px]"
|
||||
>
|
||||
📥 Install Extension
|
||||
</a>
|
||||
|
||||
<button
|
||||
onClick={handleRefresh}
|
||||
className="button bg-gray-100 hover:bg-gray-200 text-gray-700 px-8 py-3 text-lg font-semibold min-w-[200px]"
|
||||
>
|
||||
🔄 Refresh Page
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Additional Info */}
|
||||
<div className="text-center space-y-4 text-gray-600 max-w-2xl">
|
||||
<h3 className="text-lg font-semibold text-gray-900">
|
||||
What is TLSNotary?
|
||||
</h3>
|
||||
<p className="text-sm leading-relaxed">
|
||||
TLSNotary enables privacy-preserving verification of web data. Instead of sharing your actual data,
|
||||
you can prove specific facts about it using cryptographic proofs, keeping your sensitive information private.
|
||||
</p>
|
||||
|
||||
<div className="flex items-center justify-center gap-6 text-xs text-gray-500 pt-4">
|
||||
<a href="https://tlsnotary.org" className="hover:text-blue-600 transition-colors">Learn More</a>
|
||||
<a href="https://tlsnotary.org/docs/intro" className="hover:text-blue-600 transition-colors">Documentation</a>
|
||||
<a href="https://github.com/tlsnotary" className="hover:text-blue-600 transition-colors">GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
<p className="font-bold">Please install the extension to proceed </p>
|
||||
<p className="font-bold">
|
||||
You will need to refresh your browser after installing the extension
|
||||
</p>
|
||||
<a
|
||||
href="https://chromewebstore.google.com/detail/tlsn-extension/gcfkkledipjbgdbimfpijgbkhajiaaph"
|
||||
target="_blank"
|
||||
className="button"
|
||||
>
|
||||
Install TLSN Extension
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import 'isomorphic-fetch';
|
||||
import type {} from 'redux-thunk/extend-redux';
|
||||
import * as React from 'react';
|
||||
import { hydrateRoot } from 'react-dom/client';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import App from './pages/App';
|
||||
import { Provider } from 'react-redux';
|
||||
import configureAppStore from './store';
|
||||
|
||||
// @ts-ignore
|
||||
const store = configureAppStore(window.__PRELOADED_STATE__);
|
||||
|
||||
// @ts-ignore
|
||||
delete window.__PRELOADED_STATE__;
|
||||
@@ -16,11 +10,14 @@ delete window.__PRELOADED_STATE__;
|
||||
(async () => {
|
||||
hydrateRoot(
|
||||
document.getElementById('root')!,
|
||||
<Provider store={store}>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</Provider>,
|
||||
<BrowserRouter
|
||||
future={{
|
||||
v7_startTransition: true,
|
||||
v7_relativeSplatPath: true,
|
||||
}}
|
||||
>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
);
|
||||
})();
|
||||
|
||||
|
||||
@@ -2,9 +2,4 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
$fa-font-path: "~@fortawesome/fontawesome-free/webfonts";
|
||||
|
||||
@import "../../../node_modules/@fortawesome/fontawesome-free/scss/fontawesome";
|
||||
@import "../../../node_modules/@fortawesome/fontawesome-free/scss/brands";
|
||||
@import "../../../node_modules/@fortawesome/fontawesome-free/scss/solid";
|
||||
@import "../../../node_modules/@fortawesome/fontawesome-free/scss/regular";
|
||||
@import "~@fortawesome/fontawesome-free/css/all.css";
|
||||
@@ -1,51 +0,0 @@
|
||||
import { Attestation, AttestedData } from '../utils/types';
|
||||
|
||||
enum ActionType {
|
||||
SET_ATTESTATION = 'attestation/SET_ATTESTATION',
|
||||
}
|
||||
|
||||
export type Action<payload = any> = {
|
||||
type: ActionType;
|
||||
payload: payload;
|
||||
error?: boolean;
|
||||
meta?: any;
|
||||
};
|
||||
|
||||
type AttestationData = {
|
||||
raw: Attestation;
|
||||
};
|
||||
|
||||
export type State = {
|
||||
raw: Attestation;
|
||||
};
|
||||
|
||||
export const initState: State = {
|
||||
raw: {
|
||||
version: '0.1.0-alpha.12',
|
||||
data: '',
|
||||
meta: {
|
||||
notaryUrl: '',
|
||||
websocketProxyUrl: '',
|
||||
pluginUrl: '',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const setAttestation = (
|
||||
attestation: AttestationData,
|
||||
): Action<AttestationData> => ({
|
||||
type: ActionType.SET_ATTESTATION,
|
||||
payload: attestation,
|
||||
});
|
||||
|
||||
export default function attestation(state = initState, action: Action): State {
|
||||
switch (action.type) {
|
||||
case ActionType.SET_ATTESTATION:
|
||||
return {
|
||||
...state,
|
||||
raw: action.payload,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { applyMiddleware, combineReducers, createStore } from 'redux';
|
||||
import thunk from 'redux-thunk';
|
||||
import { createLogger } from 'redux-logger';
|
||||
import attestation from './attestation';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
attestation,
|
||||
});
|
||||
|
||||
export type AppRootState = ReturnType<typeof rootReducer>;
|
||||
|
||||
const createStoreWithMiddleware =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? applyMiddleware(
|
||||
thunk,
|
||||
createLogger({
|
||||
collapsed: true,
|
||||
}),
|
||||
)(createStore)
|
||||
: applyMiddleware(thunk)(createStore);
|
||||
|
||||
function configureAppStore(preloadedState?: AppRootState) {
|
||||
const { attestation } = preloadedState || {};
|
||||
return createStoreWithMiddleware(rootReducer, {
|
||||
attestation,
|
||||
});
|
||||
}
|
||||
|
||||
export default configureAppStore;
|
||||
@@ -1,20 +0,0 @@
|
||||
export interface AttestedData {
|
||||
version: '0.1.0-alpha.12';
|
||||
time: number;
|
||||
sent: string;
|
||||
recv: string;
|
||||
notaryUrl: string;
|
||||
notaryKey: string;
|
||||
websocketProxyUrl?: string;
|
||||
verifierKey?: string;
|
||||
}
|
||||
|
||||
export type Attestation = {
|
||||
version: '0.1.0-alpha.12';
|
||||
data: string;
|
||||
meta: {
|
||||
notaryUrl: string;
|
||||
websocketProxyUrl: string;
|
||||
pluginUrl?: string;
|
||||
};
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
import type { PresentationJSON } from 'tlsn-js/build/types';
|
||||
|
||||
export const formatDataPreview = (data: PresentationJSON) => {
|
||||
if (!data) return '';
|
||||
return Object.entries(data)
|
||||
.map(([key, value]) => {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
return `${key}: ${JSON.stringify(value, null, 2)}`;
|
||||
} else if (key === 'data') {
|
||||
const maxLength = 160;
|
||||
const previewData = value.toString().substring(0, maxLength);
|
||||
const formattedData = previewData.match(/.{1,20}/g)?.join('\n');
|
||||
return `${key}: ${formattedData}... ${value.length} more`;
|
||||
} else {
|
||||
return `${key}: ${value}`;
|
||||
}
|
||||
})
|
||||
.join('\n');
|
||||
};
|
||||
@@ -1,7 +0,0 @@
|
||||
import * as Comlink from 'comlink';
|
||||
import init, { Presentation } from 'tlsn-js';
|
||||
|
||||
Comlink.expose({
|
||||
init,
|
||||
Presentation,
|
||||
});
|
||||
Reference in New Issue
Block a user