1
0
cvsa/packages/frontend/src/components/VDFtester.svelte

290 lines
9.3 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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