fix: missing navigator object in server-side
This commit is contained in:
parent
5a112aeaee
commit
a31f702499
@ -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">
|
||||||
|
Loading…
Reference in New Issue
Block a user