fix: missing navigator object in server-side

This commit is contained in:
alikia2x (寒寒) 2025-04-28 06:15:06 +08:00
parent 5a112aeaee
commit a31f702499
Signed by: alikia2x
GPG Key ID: 56209E0CCD8420C6

View File

@ -1,44 +1,43 @@
<script lang="ts"> <script lang="ts">
import { N_ARRAY } from "src/const"; import {N_ARRAY} from "src/const";
import { fade } from "svelte/transition"; import {fade} from "svelte/transition";
import { UAParser } from 'ua-parser-js'; import {UAParser} from 'ua-parser-js';
import {onMount} from "svelte";
const ua = navigator ? navigator.userAgent : ""; let browserInfo: null | string = null;
const { browser } = UAParser(ua); let bigintSupported = typeof BigInt !== 'undefined';
let bigintSupported = typeof BigInt !== 'undefined'; function generateRandomBigInt(min: bigint, max: bigint) {
const range = max - min;
const bitLength = range.toString(2).length;
const byteLength = Math.ceil(bitLength / 8);
const mask = (1n << BigInt(bitLength)) - 1n; // 用于截断的掩码
let result;
do {
const randomBytes = new Uint8Array(byteLength);
crypto.getRandomValues(randomBytes);
result = 0n;
for (let i = 0; i < byteLength; i++) {
result = (result << 8n) | BigInt(randomBytes[i]);
}
result = result & mask; // 确保不超过 bitLength 位
} while (result > range);
return min + result;
}
function generateRandomBigInt(min: bigint, max: bigint) { function generateValidG(N: bigint) {
const range = max - min; if (N <= 4n) throw new Error("N must be > 4");
const bitLength = range.toString(2).length; while (true) {
const byteLength = Math.ceil(bitLength / 8); const r = generateRandomBigInt(2n, N - 1n);
const mask = (1n << BigInt(bitLength)) - 1n; // 用于截断的掩码 const g = (r * r) % N;
let result; if (g !== 1n && g !== 0n && g !== N - 1n) {
do { return g;
const randomBytes = new Uint8Array(byteLength); }
crypto.getRandomValues(randomBytes); }
result = 0n; }
for (let i = 0; i < byteLength; i++) {
result = (result << 8n) | BigInt(randomBytes[i]);
}
result = result & mask; // 确保不超过 bitLength 位
} while (result > range);
return min + result;
}
function generateValidG(N: bigint) { const workerContent = `addEventListener("message", async (event) => {
if (N <= 4n) throw new Error("N must be > 4");
while (true) {
const r = generateRandomBigInt(2n, N - 1n);
const g = (r * r) % N;
if (g !== 1n && g !== 0n && g !== N - 1n) {
return g;
}
}
}
const workerContent = `addEventListener("message", async (event) => {
const { g, N, difficulty } = event.data; const { g, N, difficulty } = event.data;
function pow(base, exponent, mod) { function pow(base, exponent, mod) {
let result = 1n; let result = 1n;
@ -75,86 +74,86 @@
}); });
`; `;
let isBenchmarking = false; let isBenchmarking = false;
interface BenchmarkResult { interface BenchmarkResult {
N: bigint; N: bigint;
difficulty: bigint; difficulty: bigint;
time: number; time: number;
} }
let benchmarkResults: BenchmarkResult[] = []; let benchmarkResults: BenchmarkResult[] = [];
let currentProgress = 0; let currentProgress = 0;
let currentN: bigint | null = null; let currentN: bigint | null = null;
let currentDifficulty: bigint | null = null; let currentDifficulty: bigint | null = null;
let worker: Worker | null = null; let worker: Worker | null = null;
let currentTestIndex = 0; let currentTestIndex = 0;
const difficulties = [BigInt(20000), BigInt(200000)]; const difficulties = [BigInt(20000), BigInt(200000)];
const testCombinations: { N: bigint; difficulty: bigint }[] = []; const testCombinations: { N: bigint; difficulty: bigint }[] = [];
// 创建需要测试的 N 和难度的组合 // 创建需要测试的 N 和难度的组合
N_ARRAY.forEach((n) => { N_ARRAY.forEach((n) => {
difficulties.forEach((difficulty) => { difficulties.forEach((difficulty) => {
testCombinations.push({ N: n, difficulty }); testCombinations.push({N: n, difficulty});
}); });
}); });
const speedSampleIndex = 1; const speedSampleIndex = 1;
let speedSample: BenchmarkResult; let speedSample: BenchmarkResult;
async function startBenchmark() { async function startBenchmark() {
if (testCombinations.length === 0) { if (testCombinations.length === 0) {
alert("No N values provided in src/const N_ARRAY."); alert("No N values provided in src/const N_ARRAY.");
return; return;
} }
isBenchmarking = true; isBenchmarking = true;
benchmarkResults = []; benchmarkResults = [];
currentTestIndex = 0; currentTestIndex = 0;
const { N, difficulty } = testCombinations[currentTestIndex]; const {N, difficulty} = testCombinations[currentTestIndex];
const g = generateValidG(N); const g = generateValidG(N);
let blob = new Blob([workerContent], { type: "text/javascript" }); let blob = new Blob([workerContent], {type: "text/javascript"});
worker = new Worker(window.URL.createObjectURL(blob)); worker = new Worker(window.URL.createObjectURL(blob));
worker.onmessage = (event) => { worker.onmessage = (event) => {
const { type, N: resultNStr, difficulty: resultDifficultyStr, time, progress } = event.data; const {type, N: resultNStr, difficulty: resultDifficultyStr, time, progress} = event.data;
const resultN = BigInt(resultNStr); const resultN = BigInt(resultNStr);
const resultDifficulty = BigInt(resultDifficultyStr); const resultDifficulty = BigInt(resultDifficultyStr);
if (type === "progress") { if (type === "progress") {
currentProgress = progress; currentProgress = progress;
currentN = resultN; currentN = resultN;
currentDifficulty = resultDifficulty; currentDifficulty = resultDifficulty;
} else if (type === "result") { } else if (type === "result") {
benchmarkResults = [...benchmarkResults, { N: resultN, difficulty: resultDifficulty, time }]; benchmarkResults = [...benchmarkResults, {N: resultN, difficulty: resultDifficulty, time}];
currentProgress = 0; currentProgress = 0;
currentTestIndex++; currentTestIndex++;
if (currentTestIndex < testCombinations.length) { if (currentTestIndex < testCombinations.length) {
// 继续下一个测试组合 // 继续下一个测试组合
const nextTest = testCombinations[currentTestIndex]; const nextTest = testCombinations[currentTestIndex];
const nextG = generateValidG(nextTest.N); const nextG = generateValidG(nextTest.N);
worker?.postMessage({ g: nextG, N: nextTest.N, difficulty: nextTest.difficulty }); worker?.postMessage({g: nextG, N: nextTest.N, difficulty: nextTest.difficulty});
} else { } else {
// 所有测试完毕 // 所有测试完毕
isBenchmarking = false; isBenchmarking = false;
worker?.terminate(); worker?.terminate();
worker = null; worker = null;
currentN = null; currentN = null;
currentDifficulty = null; currentDifficulty = null;
} }
} }
}; };
// 开始第一个测试 // 开始第一个测试
worker.postMessage({ g, N, difficulty }); worker.postMessage({g, N, difficulty});
} }
function getAccumulatedTime() { function getAccumulatedTime() {
return benchmarkResults.reduce((acc, result) => acc + result.time, 0); return benchmarkResults.reduce((acc, result) => acc + result.time, 0);
} }
function getSpeed() { function getSpeed() {
speedSample = benchmarkResults[speedSampleIndex]; speedSample = benchmarkResults[speedSampleIndex];
@ -163,13 +162,19 @@
} }
return Number(speedSample.difficulty) / speedSample.time * 1000; return Number(speedSample.difficulty) / speedSample.time * 1000;
} }
onMount(() => {
const ua = navigator ? navigator.userAgent : "";
const {browser} = UAParser(ua);
browserInfo = browser.name + " " + browser.version;
})
</script> </script>
<div <div
class="relative mt-8 md:mt-20 md:bg-surface-container-high md:dark:bg-dark-surface-container-high class="relative mt-8 md:mt-20 md:bg-surface-container-high md:dark:bg-dark-surface-container-high
p-6 rounded-md mb-6 md:w-2/3 lg:w-1/2 xl:w-[37%] md:mx-auto" p-6 rounded-md mb-6 md:w-2/3 lg:w-1/2 xl:w-[37%] md:mx-auto"
> >
<h2 class="text-xl font-[500] mb-4">VDF 基准测试</h2> <h2 class="text-xl font-[500] mb-4">VDF 基准测试</h2>
{#if !bigintSupported} {#if !bigintSupported}
<p class="text-error dark:text-dark-error"> <p class="text-error dark:text-dark-error">
@ -186,92 +191,94 @@
</button> </button>
{/if} {/if}
{#if isBenchmarking} {#if isBenchmarking}
<p class="mb-8"> <p class="mb-8">
正在测试: {currentTestIndex + 1}/{testCombinations.length} 正在测试: {currentTestIndex + 1}/{testCombinations.length}
</p> </p>
{#if currentN !== null && currentDifficulty !== null} {#if currentN !== null && currentDifficulty !== null}
<p class="mb-2">密钥长度: {currentN.toString(2).length} 比特</p> <p class="mb-2">密钥长度: {currentN.toString(2).length} 比特</p>
<p class="mb-2">难度: {currentDifficulty.toLocaleString()}</p> <p class="mb-2">难度: {currentDifficulty.toLocaleString()}</p>
<div class="w-full rounded-full h-1 relative overflow-hidden"> <div class="w-full rounded-full h-1 relative overflow-hidden">
<div <div
class="bg-primary dark:bg-dark-primary h-full rounded-full absolute" class="bg-primary dark:bg-dark-primary h-full rounded-full absolute"
style="width: {currentProgress}%" style="width: {currentProgress}%"
></div> ></div>
<div <div
class="bg-secondary-container dark:bg-dark-secondary-container h-full rounded-full absolute right-0" class="bg-secondary-container dark:bg-dark-secondary-container h-full rounded-full absolute right-0"
style="width: calc({100 - currentProgress}% - 0.25rem)" style="width: calc({100 - currentProgress}% - 0.25rem)"
></div> ></div>
<div class="bg-primary dark:bg-dark-primary h-full w-1 rounded-full absolute right-0"></div> <div class="bg-primary dark:bg-dark-primary h-full w-1 rounded-full absolute right-0"></div>
</div> </div>
{/if} {/if}
{/if} {/if}
{#if benchmarkResults.length > 0 && !isBenchmarking} {#if benchmarkResults.length > 0 && !isBenchmarking}
<h3 class="text-lg font-medium mt-4 mb-2">测试结果</h3> <h3 class="text-lg font-medium mt-4 mb-2">测试结果</h3>
<p class="mb-4 text-sm"> <p class="mb-4 text-sm">
测试在 {(getAccumulatedTime() / 1000).toFixed(3)} 秒内完成. <br/> 测试在 {(getAccumulatedTime() / 1000).toFixed(3)} 秒内完成. <br/>
速度: {Math.round(getSpeed()).toLocaleString()} 迭代 / 秒. <br/> 速度: {Math.round(getSpeed()).toLocaleString()} 迭代 / 秒. <br/>
<span class="text-sm text-on-surface-variant dark:text-dark-on-surface-variant"> <span class="text-sm text-on-surface-variant dark:text-dark-on-surface-variant">
速度是在 N = {speedSample.N.toString(2).length} bits, T = {speedSample.difficulty} 的测试中测量的. 速度是在 N = {speedSample.N.toString(2).length} bits, T = {speedSample.difficulty} 的测试中测量的.
</span> </span>
<br/> <br/>
浏览器版本:{browser} {#if browserInfo}
</p> 浏览器版本:{browserInfo}
<table class="w-full text-sm text-left rtl:text-right mt-4"> {/if}
<thead class="text-sm uppercase font-medium border-b border-outline dark:border-dark-outline"> </p>
<tr> <table class="w-full text-sm text-left rtl:text-right mt-4">
<th scope="col" class="px-6 py-3">耗时 (ms)</th> <thead class="text-sm uppercase font-medium border-b border-outline dark:border-dark-outline">
<th scope="col" class="px-6 py-3">N (bits)</th> <tr>
<th scope="col" class="px-6 py-3">T (迭代)</th> <th scope="col" class="px-6 py-3">耗时 (ms)</th>
</tr> <th scope="col" class="px-6 py-3">N (bits)</th>
</thead> <th scope="col" class="px-6 py-3">T (迭代)</th>
<tbody> </tr>
{#each benchmarkResults as result} </thead>
<tr class="border-b border-outline-variant dark:border-dark-outline-variant"> <tbody>
<td class="px-6 py-4 whitespace-nowrap"> {#each benchmarkResults as result}
{result.time.toFixed(2)} <tr class="border-b border-outline-variant dark:border-dark-outline-variant">
</td> <td class="px-6 py-4 whitespace-nowrap">
<td class="px-6 py-4 whitespace-nowrap"> {result.time.toFixed(2)}
{result.N.toString(2).length} </td>
</td> <td class="px-6 py-4 whitespace-nowrap">
<td class="px-6 py-4 whitespace-nowrap"> {result.N.toString(2).length}
{Number(result.difficulty)} </td>
</td> <td class="px-6 py-4 whitespace-nowrap">
</tr> {Number(result.difficulty)}
{/each} </td>
</tbody> </tr>
</table> {/each}
{/if} </tbody>
</table>
{/if}
</div> </div>
{#if !isBenchmarking} {#if !isBenchmarking}
<div <div
class={"md:w-2/3 lg:w-1/2 xl:w-[37%] md:mx-auto mx-6 mb-12 " + class={"md:w-2/3 lg:w-1/2 xl:w-[37%] md:mx-auto mx-6 mb-12 " +
(benchmarkResults.length > 0 && !isBenchmarking ? "" : "absolute left-1/2 -translate-x-1/2 top-72")} (benchmarkResults.length > 0 && !isBenchmarking ? "" : "absolute left-1/2 -translate-x-1/2 top-72")}
transition:fade={{ duration: 200 }} transition:fade={{ duration: 200 }}
> >
<h2 class="text-lg font-medium">关于本页</h2> <h2 class="text-lg font-medium">关于本页</h2>
<div class="text-sm text-on-surface-variant dark:text-dark-on-surface-variant"> <div class="text-sm text-on-surface-variant dark:text-dark-on-surface-variant">
<p> <p>
这是一个性能测试页面,<br /> 这是一个性能测试页面,<br/>
旨在测试我们的一个 VDF (Verifiable Delayed Function, 可验证延迟函数) 实现的性能。<br /> 旨在测试我们的一个 VDF (Verifiable Delayed Function, 可验证延迟函数) 实现的性能。<br/>
这是一个数学函数它驱动了整个网站的验证码CAPTCHA<br /> 这是一个数学函数它驱动了整个网站的验证码CAPTCHA<br/>
通过使用该函数,我们可以让您无需通过点选图片或滑动滑块既可完成验证, 同时防御我们的网站,使其免受自动程序的攻击。 通过使用该函数,我们可以让您无需通过点选图片或滑动滑块既可完成验证, 同时防御我们的网站,使其免受自动程序的攻击。
<br /> <br/>
</p> </p>
<p> <p>
点击 <i>Start Benchmark</i> 按钮,会自动测试并展示结果。<br /> 点击 <i>Start Benchmark</i> 按钮,会自动测试并展示结果。<br/>
</p> </p>
<p> <p>
你可以将结果发送至邮箱: <a href="mailto:contact@alikia2x.com">contact@alikia2x.com</a> 你可以将结果发送至邮箱: <a href="mailto:contact@alikia2x.com">contact@alikia2x.com</a>
或 QQ<a href="https://qm.qq.com/q/WS8zyhlcEU">1559913735</a>,并附上自己的设备信息 或 QQ<a href="https://qm.qq.com/q/WS8zyhlcEU">1559913735</a>,并附上自己的设备信息
(例如,手机型号、电脑的 CPU 型号等)。<br /> (例如,手机型号、电脑的 CPU 型号等)。<br/>
我们会根据测试结果,优化我们的实现,使性能更优。<br /> 我们会根据测试结果,优化我们的实现,使性能更优。<br/>
感谢你的支持!<br /> 感谢你的支持!<br/>
</p> </p>
</div> </div>
</div> </div>
{/if} {/if}
<style lang="postcss"> <style lang="postcss">