ds-free-api / src /lib /challenge.ts
kevin
ds2api
bf17fd9
import fs from 'fs-extra';
export class DeepSeekHash {
private wasmInstance: any;
private offset: number = 0;
private cachedUint8Memory: Uint8Array | null = null;
private cachedTextEncoder: TextEncoder = new TextEncoder();
// 编码字符串到 WASM 内存
private encodeString(
text: string,
allocate: (size: number, align: number) => number,
reallocate?: (ptr: number, oldSize: number, newSize: number, align: number) => number
): number {
// 简单情况:当没有 reallocate 函数时,直接编码整个字符串
if (!reallocate) {
const encoded = this.cachedTextEncoder.encode(text);
const ptr = allocate(encoded.length, 1) >>> 0;
const memory = this.getCachedUint8Memory();
memory.subarray(ptr, ptr + encoded.length).set(encoded);
this.offset = encoded.length;
return ptr;
}
// 复杂情况:分两步处理 ASCII 和非 ASCII 字符
const strLength = text.length;
let ptr = allocate(strLength, 1) >>> 0;
const memory = this.getCachedUint8Memory();
let asciiLength = 0;
// 首先尝试 ASCII 编码
for (; asciiLength < strLength; asciiLength++) {
const charCode = text.charCodeAt(asciiLength);
if (charCode > 127) break;
memory[ptr + asciiLength] = charCode;
}
// 如果存在非 ASCII 字符,需要重新分配空间并处理
if (asciiLength !== strLength) {
if (asciiLength > 0) {
text = text.slice(asciiLength);
}
// 为非 ASCII 字符重新分配空间(每个字符最多需要 3 字节)
ptr = reallocate(ptr, strLength, asciiLength + text.length * 3, 1) >>> 0;
// 使用 encodeInto 处理剩余的非 ASCII 字符
const result = this.cachedTextEncoder.encodeInto(
text,
this.getCachedUint8Memory().subarray(ptr + asciiLength, ptr + asciiLength + text.length * 3)
);
asciiLength += result.written;
// 最终调整内存大小
ptr = reallocate(ptr, asciiLength + text.length * 3, asciiLength, 1) >>> 0;
}
this.offset = asciiLength;
return ptr;
}
// 获取 WASM 内存视图
private getCachedUint8Memory(): Uint8Array {
if (this.cachedUint8Memory === null || this.cachedUint8Memory.byteLength === 0) {
this.cachedUint8Memory = new Uint8Array(this.wasmInstance.memory.buffer);
}
return this.cachedUint8Memory;
}
// DeepSeekHash 计算函数
public calculateHash(
algorithm: string,
challenge: string,
salt: string,
difficulty: number,
expireAt: number
): number | undefined {
if (algorithm !== 'DeepSeekHashV1') {
throw new Error('Unsupported algorithm: ' + algorithm);
}
// 拼接前缀
const prefix = `${salt}_${expireAt}_`;
try {
// 分配栈空间
const retptr = this.wasmInstance.__wbindgen_add_to_stack_pointer(-16);
// 获取编码后的指针和长度
const ptr0 = this.encodeString(
challenge,
this.wasmInstance.__wbindgen_export_0,
this.wasmInstance.__wbindgen_export_1
);
const len0 = this.offset;
const ptr1 = this.encodeString(
prefix,
this.wasmInstance.__wbindgen_export_0,
this.wasmInstance.__wbindgen_export_1
);
const len1 = this.offset;
// 传入正确的长度参数
this.wasmInstance.wasm_solve(retptr, ptr0, len0, ptr1, len1, difficulty);
// 获取返回结果
const dataView = new DataView(this.wasmInstance.memory.buffer);
const status = dataView.getInt32(retptr + 0, true);
const value = dataView.getFloat64(retptr + 8, true);
// 如果求解失败,返回 undefined
if (status === 0)
return undefined;
return value;
} finally {
// 释放栈空间
this.wasmInstance.__wbindgen_add_to_stack_pointer(16);
}
}
// 初始化 WASM 模块
public async init(wasmPath: string): Promise<any> {
const imports = { wbg: {} };
const wasmBuffer = await fs.readFile(wasmPath);
const { instance } = await WebAssembly.instantiate(wasmBuffer, imports);
this.wasmInstance = instance.exports;
return this.wasmInstance;
}
}
// 导出类
export default DeepSeekHash;