GEO Task Worker v2 免安装版 (含嵌入式 Python + 全部依赖)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

251 lines
9.9 KiB

"""
DeepSeek PoW - 直接调用 WASM 文件
依赖: pip install wasmtime
用法:
python deepseek_wasm.py
"""
import struct
import time
import urllib.request
from pathlib import Path
import base64
from wasmtime import Engine, Instance, Module, Store
import json
import requests
WASM_URL = "https://fe-static.deepseek.com/chat/static/sha3_wasm_bg.7b9ca65ddd.wasm"
WASM_CACHE = Path(__file__).resolve().parent / "js_data" / "sha3_wasm_bg.wasm"
# ── 下载 / 缓存 WASM ─────────────────────────────────────────────────────────
def load_wasm(url: str = WASM_URL, cache: Path = WASM_CACHE) -> bytes:
if cache.exists():
print(f"[+] 读取缓存 {cache}")
return cache.read_bytes()
print(f"[+] 下载 {url}")
with urllib.request.urlopen(url) as r:
data = r.read()
cache.write_bytes(data)
print(f"[+] 已缓存 ({len(data):,} bytes)")
return data
# ── WASM 运行时 ──────────────────────────────────────────────────────────────
class DeepSeekWasm:
"""
封装 sha3_wasm_bg.wasm,提供与 JS get_answer() 等价的接口。
WASM 导出 (wasm-bindgen 约定):
memory WebAssembly.Memory
__wbindgen_add_to_stack_pointer (i32) -> i32
__wbindgen_export_0 malloc(size: i32, align: i32) -> i32
__wbindgen_export_1 realloc(ptr, old, new, align: i32) -> i32
wasm_solve(ret_ptr, ch_ptr, ch_len, salt_ptr, salt_len, difficulty) -> void
"""
def __init__(self, wasm_bytes: bytes):
self.engine = Engine()
self.store = Store(self.engine)
self.module = Module(self.engine, wasm_bytes)
self.instance = Instance(self.store, self.module, [])
exp = self.instance.exports(self.store)
self._memory = exp["memory"]
self._stack_ptr = exp["__wbindgen_add_to_stack_pointer"]
self._malloc = exp["__wbindgen_export_0"]
self._realloc = exp["__wbindgen_export_1"]
self._wasm_solve = exp["wasm_solve"]
# ── 内存读写 ──────────────────────────────────────────────────────────────
def _mem_write(self, ptr: int, data: bytes):
self._memory.write(self.store, data, ptr)
def _mem_read(self, ptr: int, length: int) -> bytes:
return bytes(self._memory.read(self.store, ptr, ptr + length))
# ── 字符串写入(对应 JS h() 函数)────────────────────────────────────────
def _write_str(self, s: str) -> tuple[int, int]:
"""将字符串 UTF-8 编码后写入 WASM 内存,返回 (ptr, length)"""
encoded = s.encode("utf-8")
length = len(encoded)
ptr = self._malloc(self.store, length, 1)
self._mem_write(ptr, encoded)
return ptr, length
# ── 核心接口 ──────────────────────────────────────────────────────────────
def solve(self, challenge: str, salt: str, difficulty: int) -> float:
"""
对应 JS:
get_answer(challenge, salt, difficulty) -> float
参数:
challenge challenge hex 字符串
salt 完整 salt,通常为 "{salt}_{timestamp_ms}_"
difficulty 难度整数
返回:
满足难度的 nonce(float)
"""
# 分配 16 字节栈空间存放返回值
ret_ptr = self._stack_ptr(self.store, -16)
try:
c_ptr, c_len = self._write_str(challenge)
s_ptr, s_len = self._write_str(salt)
self._wasm_solve(
self.store,
ret_ptr,
c_ptr, c_len,
s_ptr, s_len,
float(difficulty), # wasm_solve 的 difficulty 参数为 f64
)
# ret_ptr+0 : i32 状态码(忽略)
# ret_ptr+8 : f64 nonce 结果
nonce_bytes = self._mem_read(ret_ptr + 8, 8)
return int(struct.unpack("<d", nonce_bytes)[0])
finally:
self._stack_ptr(self.store, 16) # 恢复栈指针
# ── 便捷函数:从 challenge 字典直接求解 ────────────────────────────────────
def solve_challenge(wasm: DeepSeekWasm, challenge_obj: dict) -> float:
"""
接收 /api/v0/chat/challenge 返回的 challenge 字典,自动拼接 salt 并求解。
示例 challenge_obj:
{
"challenge": "645f91f8...",
"salt": "98fe06400b75ab5d4b0e",
"difficulty": 144000,
}
"""
ts_ms = int(time.time() * 1000)
salt_full = f"{challenge_obj['salt']}_{ts_ms}_"
print(f" challenge : {challenge_obj['challenge']}")
print(f" salt+ts : {salt_full}")
print(f" difficulty: {challenge_obj['difficulty']}")
t0 = time.time()
nonce = wasm.solve(challenge_obj["challenge"], salt_full, challenge_obj["difficulty"])
print(f" nonce : {nonce} ({time.time() - t0:.3f}s)")
return nonce
wasm_bytes = load_wasm()
wasm = DeepSeekWasm(wasm_bytes)
def get_biz_data(authorization, target_path):
headers = {
"accept": "*/*",
"accept-language": "zh-CN,zh;q=0.9",
"authorization": authorization,
"cache-control": "no-cache",
"content-type": "application/json",
"origin": "https://chat.deepseek.com",
"pragma": "no-cache",
"priority": "u=1, i",
"referer": "https://chat.deepseek.com/",
"sec-ch-ua": "\"Chromium\";v=\"9\", \"Not?A_Brand\";v=\"8\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 SLBrowser/9.0.8.3131 SLBChan/10 SLBVPV/64-bit",
"x-app-version": "2.0.0",
"x-client-locale": "zh_CN",
"x-client-platform": "web",
"x-client-timezone-offset": "28800",
"x-client-version": "2.0.0"
}
url = "https://chat.deepseek.com/api/v0/chat/create_pow_challenge"
data = {
"target_path": target_path
}
data = json.dumps(data, separators=(',', ':'))
response = requests.post(url, headers=headers, data=data).json()
# print(response)
return response['data']['biz_data']
def get_ds_pow(authorization="Bearer GL6ePPNDwuPU9UCpOchZh06IhBE+2JprhF+zKZ20ApnZ/X+LdH3SQgeMfbEou1qe",
target_path="/api/v0/chat/completion", biz_data=None):
if not biz_data:
biz_data = get_biz_data(authorization, target_path)
challenge_data = biz_data["challenge"]
challenge = challenge_data['challenge']
salt2 = challenge_data["salt"]
difficulty = challenge_data["difficulty"]
expire_at = challenge_data['expire_at']
signature = challenge_data['signature']
salt = f'{salt2}_{expire_at}_'
answer = wasm.solve(
challenge=challenge,
salt=salt,
difficulty=difficulty,
)
sign_data = {"algorithm": "DeepSeekHashV1", "challenge": challenge, "salt": salt2, "answer": answer,
"signature": signature, "target_path": target_path}
# print(sign_data)
res = base64.b64encode(json.dumps(sign_data, ensure_ascii=False, separators=(',', ':')).encode()).decode()
# print(res)
return res
# ── 示例 ──────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
# 1. 加载 WASM
# get_ds_pow()
authorization = "Bearer GL6ePPNDwuPU9UCpOchZh06IhBE+2JprhF+zKZ20ApnZ/X+LdH3SQgeMfbEou1qe"
headers = {
"accept": "*/*",
"accept-language": "zh-CN,zh;q=0.9",
"authorization": authorization,
"cache-control": "no-cache",
"content-type": "application/json",
"sec-fetch-site": "same-origin",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 SLBrowser/9.0.8.3131 SLBChan/10 SLBVPV/64-bit",
"x-app-version": "2.0.0",
"x-client-locale": "zh_CN",
"x-client-platform": "web",
"x-client-timezone-offset": "28800",
"x-client-version": "2.0.0",
"x-ds-pow-response": get_ds_pow(authorization),
}
url = "https://chat.deepseek.com/api/v0/chat/completion"
data = {
"chat_session_id": "03f2255a-d58f-4010-b2bc-6ee58e99b6c0",
"parent_message_id": None,
"model_type": "default",
"prompt": "今天星期几",
"ref_file_ids": [],
"thinking_enabled": False,
"search_enabled": True,
"preempt": False
}
data = json.dumps(data, separators=(',', ':'))
response = requests.post(url, headers=headers, data=data, stream=True)
for i in response.iter_lines(decode_unicode=True):
print(i, end=' ')
# {"algorithm":"DeepSeekHashV1","challenge":"a9c77bb053d25365f6d2e292a4eddfb6b969486a004d008fc6dffa229b25b142","salt":"fe903bc4cde529d3f67d","answer":30474,"signature":"41014e3c1324d7f85c4493c931c0e9c24b6afac1e9df36b98dce336cf5d16f7c","target_path":"/api/v0/chat/completion"}
# {"algorithm":"DeepSeekHashV1","challenge":"e5c90381316ec79fb0160fc3f7b9d7e0380a876a35852e4f85346a67c50d60de","salt":"f84419c32756da175814","answer":68071,"signature":"f009b60446b4fc5931e533a5ea9c6fa14834586aaf5aa334ea9a6d2fa9b9cd4d","target_path":"/api/v0/chat/completion"}