sd / src /pow.js
pigracing's picture
Upload 22 files
bd27f3c verified
Raw History Blame Contribute Delete
8.42 kB
import { pickToken } from './auth.js';
import { apiHeaders, proxiedFetch } from './headers.js';
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const BASE_URL = 'https://chat.deepseek.com';
// Load and instantiate WASM sha3 module (one-time init)
let wasmInstance = null;
async function getWasm() {
if (wasmInstance) return wasmInstance;
const wasmPath = join(dirname(fileURLToPath(import.meta.url)), '..', 'sha3_wasm_bg.wasm');
const wasmBuf = readFileSync(wasmPath);
const { instance } = await WebAssembly.instantiate(wasmBuf, { wbg: {} });
wasmInstance = instance;
return instance;
}
const encoder = new TextEncoder();
function passStringToWasm(instance, str) {
const { memory, __wbindgen_export_0 } = instance.exports;
const encoded = encoder.encode(str);
const ptr = __wbindgen_export_0(encoded.length, 1) >>> 0;
new Uint8Array(memory.buffer).set(encoded, ptr);
return [ptr, encoded.length];
}
function wasmSolve(instance, challengeHex, prefix, difficulty) {
const { memory, wasm_solve, __wbindgen_add_to_stack_pointer } = instance.exports;
const [chPtr, chLen] = passStringToWasm(instance, challengeHex);
const [pfPtr, pfLen] = passStringToWasm(instance, prefix);
const st = __wbindgen_add_to_stack_pointer(-16);
wasm_solve(st, chPtr, chLen, pfPtr, pfLen, difficulty);
const v = new DataView(memory.buffer);
const found = v.getInt32(st, true);
const answer = v.getFloat64(st + 8, true);
__wbindgen_add_to_stack_pointer(16);
if (!found) throw new Error('PoW solve failed: no solution within difficulty');
return answer;
}
// BigInt fallback
const RC = [
0x0000000000000001n, 0x0000000000008082n, 0x800000000000808an, 0x8000000080008000n,
0x000000000000808bn, 0x0000000080000001n, 0x8000000080008081n, 0x8000000000008009n,
0x000000000000008an, 0x0000000000000088n, 0x0000000080008009n, 0x000000008000000an,
0x000000008000808bn, 0x800000000000008bn, 0x8000000000008089n, 0x8000000000008003n,
0x8000000000008002n, 0x8000000000000080n, 0x000000000000800an, 0x800000008000000an,
0x8000000080008081n, 0x8000000000008080n, 0x0000000080000001n, 0x8000000080008008n,
];
const MASK = 0xffffffffffffffffn;
const RATE = 136;
function rotl64(v, k) { return ((v << BigInt(k)) | (v >> BigInt(64 - k))) & MASK; }
function keccakF23(s) {
let a = s.slice();
for (let r = 1; r < 24; r++) {
const c = [a[0]^a[5]^a[10]^a[15]^a[20], a[1]^a[6]^a[11]^a[16]^a[21], a[2]^a[7]^a[12]^a[17]^a[22],
a[3]^a[8]^a[13]^a[18]^a[23], a[4]^a[9]^a[14]^a[19]^a[24]];
const d = [c[4]^rotl64(c[1],1), c[0]^rotl64(c[2],1), c[1]^rotl64(c[3],1), c[2]^rotl64(c[4],1), c[3]^rotl64(c[0],1)];
for (let i = 0; i < 25; i++) a[i] = (a[i] ^ d[i%5]) & MASK;
const b = new Array(25);
b[0]=a[0];b[10]=rotl64(a[1],1);b[20]=rotl64(a[2],62);b[5]=rotl64(a[3],28);b[15]=rotl64(a[4],27);
b[16]=rotl64(a[5],36);b[1]=rotl64(a[6],44);b[11]=rotl64(a[7],6);b[21]=rotl64(a[8],55);b[6]=rotl64(a[9],20);
b[7]=rotl64(a[10],3);b[17]=rotl64(a[11],10);b[2]=rotl64(a[12],43);b[12]=rotl64(a[13],25);b[22]=rotl64(a[14],39);
b[23]=rotl64(a[15],41);b[8]=rotl64(a[16],45);b[18]=rotl64(a[17],15);b[3]=rotl64(a[18],21);b[13]=rotl64(a[19],8);
b[14]=rotl64(a[20],18);b[24]=rotl64(a[21],2);b[9]=rotl64(a[22],61);b[19]=rotl64(a[23],56);b[4]=rotl64(a[24],14);
for (let y = 0; y < 5; y++) {
const t = [b[y*5], b[y*5+1], b[y*5+2], b[y*5+3], b[y*5+4]];
for (let x = 0; x < 5; x++) a[y*5+x] = (t[x] ^ ((t[(x+1)%5] ^ MASK) & t[(x+2)%5])) & MASK;
}
a[0] = (a[0] ^ RC[r]) & MASK;
}
for (let i = 0; i < 25; i++) s[i] = a[i];
}
function readU64LE(buf, off) {
let v = 0n;
for (let i = 0; i < 8; i++) v |= BigInt(buf[off + i]) << BigInt(i * 8);
return v & MASK;
}
function writeU64LE(val, buf, off) {
const v = val & MASK;
for (let i = 0; i < 8; i++) buf[off + i] = Number((v >> BigInt(i * 8)) & 0xffn);
}
function preAbsorbPrefix(prefix) {
const s = new Array(25).fill(0n);
const buf = Buffer.from(prefix);
let off = 0;
while (off + RATE <= buf.length) {
for (let i = 0; i < RATE / 8; i++) s[i] = (s[i] ^ readU64LE(buf, off + i * 8)) & MASK;
keccakF23(s);
off += RATE;
}
const tailLen = buf.length - off;
const tail = Buffer.alloc(RATE);
buf.copy(tail, 0, off);
return { s, tail, tailLen };
}
function solvePowBigInt(challengeHex, salt, expireAt, difficulty) {
if (challengeHex.length !== 64) throw new Error('pow: challenge must be 64 hex chars');
const target = Buffer.from(challengeHex, 'hex');
const t0 = readU64LE(target, 0);
const t1 = readU64LE(target, 8);
const t2 = readU64LE(target, 16);
const t3 = readU64LE(target, 24);
const prefix = `${salt}_${expireAt}_`;
const { s: baseState, tail, tailLen } = preAbsorbPrefix(prefix);
for (let n = 0; n < difficulty; n++) {
const nonceStr = n.toString();
const nonceBuf = Buffer.from(nonceStr);
const totalTail = tailLen + nonceBuf.length;
const s = baseState.slice();
if (totalTail < RATE) {
const buf = Buffer.alloc(RATE);
tail.copy(buf, 0, 0, tailLen);
nonceBuf.copy(buf, tailLen);
buf[totalTail] = 0x06;
buf[RATE - 1] |= 0x80;
for (let i = 0; i < RATE / 8; i++) s[i] = (s[i] ^ readU64LE(buf, i * 8)) & MASK;
keccakF23(s);
} else {
const buf1 = Buffer.alloc(RATE);
tail.copy(buf1, 0, 0, tailLen);
nonceBuf.copy(buf1, tailLen, 0, RATE - tailLen);
for (let i = 0; i < RATE / 8; i++) s[i] = (s[i] ^ readU64LE(buf1, i * 8)) & MASK;
keccakF23(s);
const rem = totalTail - RATE;
const buf2 = Buffer.alloc(RATE);
nonceBuf.copy(buf2, 0, RATE - tailLen, RATE - tailLen + rem);
buf2[rem] = 0x06;
buf2[RATE - 1] |= 0x80;
for (let i = 0; i < RATE / 8; i++) s[i] = (s[i] ^ readU64LE(buf2, i * 8)) & MASK;
keccakF23(s);
}
if (s[0] === t0 && s[1] === t1 && s[2] === t2 && s[3] === t3) return n;
}
throw new Error('PoW solve failed: no solution within difficulty');
}
let useWasm = true;
async function solvePowInternal(token, targetPath = '/api/v0/chat/completion') {
const res = await proxiedFetch(`${BASE_URL}/api/v0/chat/create_pow_challenge`, {
method: 'POST',
headers: await apiHeaders(token),
body: JSON.stringify({ target_path: targetPath }),
});
if (res.status === 202) {
throw new Error('PoW challenge: WAF blocked (202)');
}
const text = await res.text();
if (!text) throw new Error('PoW challenge: empty response');
const json = JSON.parse(text);
const c = json.data?.biz_data?.challenge;
if (!c) throw new Error('PoW challenge failed: no challenge returned');
if (c.algorithm !== 'DeepSeekHashV1') throw new Error(`Unsupported PoW algorithm: ${c.algorithm}`);
const difficulty = c.difficulty || 144000;
const prefix = `${c.salt}_${c.expire_at}_`;
let answer;
if (useWasm) {
try {
const instance = await getWasm();
answer = wasmSolve(instance, c.challenge, prefix, difficulty);
} catch (e) {
console.warn('WASM PoW failed, falling back to BigInt:', e.message);
useWasm = false;
// Re-enable WASM after 30 minutes in case the failure was transient
setTimeout(() => { useWasm = true; }, 30 * 60 * 1000);
answer = solvePowBigInt(c.challenge, c.salt, c.expire_at, difficulty);
}
} else {
answer = solvePowBigInt(c.challenge, c.salt, c.expire_at, difficulty);
}
const powResponse = {
algorithm: c.algorithm,
challenge: c.challenge,
salt: c.salt,
answer,
signature: c.signature,
target_path: c.target_path,
};
return Buffer.from(JSON.stringify(powResponse)).toString('base64');
}
// For chat.js — takes explicit token (same token used for PoW and completion)
export async function solvePowChallengeWithToken(token, targetPath = '/api/v0/chat/completion') {
const powResponse = await solvePowInternal(token, targetPath);
return { powResponse };
}
// Legacy: picks a token automatically (used by upload.js)
export async function solvePowChallenge(preferVision = false) {
const token = pickToken(preferVision);
const powResponse = await solvePowInternal(token, '/api/v0/chat/completion');
return { powResponse, token };
}
// For upload.js — takes explicit token
export async function solvePowChallengeForUpload(token) {
return solvePowInternal(token, '/api/v0/file/upload_file');
}