Upload 4 files
Browse files- secret_word_seeds.json +0 -0
- secret_words.txt +0 -0
- secret_words_meta.json +0 -0
- server.py +47 -15
secret_word_seeds.json
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
secret_words.txt
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
secret_words_meta.json
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
server.py
CHANGED
|
@@ -10,8 +10,9 @@ Usage:
|
|
| 10 |
"""
|
| 11 |
|
| 12 |
import hashlib
|
| 13 |
-
import
|
| 14 |
-
import
|
|
|
|
| 15 |
import random
|
| 16 |
import re
|
| 17 |
import secrets
|
|
@@ -94,18 +95,41 @@ CANDIDATE_SET_BY_DIFFICULTY: dict[str, set[str]] = {
|
|
| 94 |
|
| 95 |
SECRET_WORD_SET = set(SECRET_CANDIDATES)
|
| 96 |
SECRET_WORD_SEEDS_PATH = Path(__file__).parent / "secret_word_seeds.json"
|
| 97 |
-
DEFAULT_CANONICAL_SEED_PREFIX = "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
|
| 99 |
|
| 100 |
def _build_default_secret_seed_map(words: list[str], seed_prefix: str) -> dict[str, str]:
|
| 101 |
unique_words = sorted(set(words))
|
| 102 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 103 |
|
| 104 |
|
| 105 |
def _load_secret_seed_mapping() -> tuple[str, dict[str, str]]:
|
| 106 |
"""
|
| 107 |
Load precomputed canonical seed mapping if available and valid.
|
| 108 |
-
Falls back to deterministic
|
| 109 |
"""
|
| 110 |
fallback = _build_default_secret_seed_map(SECRET_CANDIDATES, DEFAULT_CANONICAL_SEED_PREFIX)
|
| 111 |
if not SECRET_WORD_SEEDS_PATH.exists():
|
|
@@ -124,10 +148,7 @@ def _load_secret_seed_mapping() -> tuple[str, dict[str, str]]:
|
|
| 124 |
if word in SECRET_WORD_SET and seed:
|
| 125 |
loaded[word] = seed
|
| 126 |
# Ensure complete coverage for current secret candidate set.
|
| 127 |
-
complete = {
|
| 128 |
-
word: loaded.get(word, f"{prefix}{word}")
|
| 129 |
-
for word in sorted(SECRET_WORD_SET)
|
| 130 |
-
}
|
| 131 |
# Enforce 1:1 seed uniqueness; fallback if duplicates are present.
|
| 132 |
if len(set(complete.values())) != len(complete):
|
| 133 |
print("secret_word_seeds.json has duplicate seeds; using default canonical mapping")
|
|
@@ -497,14 +518,25 @@ def _word_from_canonical_seed(seed: str) -> str | None:
|
|
| 497 |
mapped = CANONICAL_SEED_TO_WORD.get(raw)
|
| 498 |
if mapped:
|
| 499 |
return mapped
|
| 500 |
-
# Backward-compatible prefix parsing.
|
| 501 |
-
if raw.startswith(CANONICAL_SEED_PREFIX):
|
| 502 |
-
maybe = raw[len(CANONICAL_SEED_PREFIX):].strip().lower()
|
| 503 |
-
if maybe in SECRET_WORD_SET:
|
| 504 |
-
return maybe
|
| 505 |
return None
|
| 506 |
|
| 507 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 508 |
def seed_to_word_filtered(seed: str, difficulty: str | None = None) -> str:
|
| 509 |
"""Deterministically pick a secret word, optionally filtered by difficulty."""
|
| 510 |
if difficulty:
|
|
@@ -556,7 +588,7 @@ def seed_for_word(word: str = Query(...), difficulty: str | None = Query(None)):
|
|
| 556 |
difficulty=difficulty,
|
| 557 |
)
|
| 558 |
|
| 559 |
-
seed_value = canonical_seed_for_word(normalized) or
|
| 560 |
return SeedLookupResponse(
|
| 561 |
word=normalized,
|
| 562 |
found=True,
|
|
|
|
| 10 |
"""
|
| 11 |
|
| 12 |
import hashlib
|
| 13 |
+
import base64
|
| 14 |
+
import json
|
| 15 |
+
import math
|
| 16 |
import random
|
| 17 |
import re
|
| 18 |
import secrets
|
|
|
|
| 95 |
|
| 96 |
SECRET_WORD_SET = set(SECRET_CANDIDATES)
|
| 97 |
SECRET_WORD_SEEDS_PATH = Path(__file__).parent / "secret_word_seeds.json"
|
| 98 |
+
DEFAULT_CANONICAL_SEED_PREFIX = ""
|
| 99 |
+
DEFAULT_CANONICAL_SEED_NAMESPACE = "semantick:secret-seed:v2"
|
| 100 |
+
DEFAULT_CANONICAL_SEED_TOKEN_LEN = 6
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def _seed_for_word_with_nonce(word: str, seed_prefix: str, nonce: int) -> str:
|
| 104 |
+
payload = f"{DEFAULT_CANONICAL_SEED_NAMESPACE}|{word}|{nonce}".encode("utf-8")
|
| 105 |
+
digest = hashlib.blake2b(payload, digest_size=16).digest()
|
| 106 |
+
token = base64.b32encode(digest).decode("ascii").rstrip("=").lower()
|
| 107 |
+
return f"{seed_prefix}{token[:DEFAULT_CANONICAL_SEED_TOKEN_LEN]}"
|
| 108 |
|
| 109 |
|
| 110 |
def _build_default_secret_seed_map(words: list[str], seed_prefix: str) -> dict[str, str]:
|
| 111 |
unique_words = sorted(set(words))
|
| 112 |
+
result: dict[str, str] = {}
|
| 113 |
+
used: set[str] = set()
|
| 114 |
+
for word in unique_words:
|
| 115 |
+
nonce = 0
|
| 116 |
+
while True:
|
| 117 |
+
seed = _seed_for_word_with_nonce(word, seed_prefix, nonce)
|
| 118 |
+
if len(word) >= 4 and word in seed:
|
| 119 |
+
nonce += 1
|
| 120 |
+
continue
|
| 121 |
+
if seed not in used:
|
| 122 |
+
used.add(seed)
|
| 123 |
+
result[word] = seed
|
| 124 |
+
break
|
| 125 |
+
nonce += 1
|
| 126 |
+
return result
|
| 127 |
|
| 128 |
|
| 129 |
def _load_secret_seed_mapping() -> tuple[str, dict[str, str]]:
|
| 130 |
"""
|
| 131 |
Load precomputed canonical seed mapping if available and valid.
|
| 132 |
+
Falls back to deterministic opaque hash mapping.
|
| 133 |
"""
|
| 134 |
fallback = _build_default_secret_seed_map(SECRET_CANDIDATES, DEFAULT_CANONICAL_SEED_PREFIX)
|
| 135 |
if not SECRET_WORD_SEEDS_PATH.exists():
|
|
|
|
| 148 |
if word in SECRET_WORD_SET and seed:
|
| 149 |
loaded[word] = seed
|
| 150 |
# Ensure complete coverage for current secret candidate set.
|
| 151 |
+
complete = {word: loaded.get(word, fallback[word]) for word in sorted(SECRET_WORD_SET)}
|
|
|
|
|
|
|
|
|
|
| 152 |
# Enforce 1:1 seed uniqueness; fallback if duplicates are present.
|
| 153 |
if len(set(complete.values())) != len(complete):
|
| 154 |
print("secret_word_seeds.json has duplicate seeds; using default canonical mapping")
|
|
|
|
| 518 |
mapped = CANONICAL_SEED_TO_WORD.get(raw)
|
| 519 |
if mapped:
|
| 520 |
return mapped
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 521 |
return None
|
| 522 |
|
| 523 |
|
| 524 |
+
def _canonical_seed_fallback(word: str) -> str:
|
| 525 |
+
normalized = word.strip().lower()
|
| 526 |
+
if not normalized:
|
| 527 |
+
return ""
|
| 528 |
+
nonce = 0
|
| 529 |
+
while True:
|
| 530 |
+
seed = _seed_for_word_with_nonce(normalized, DEFAULT_CANONICAL_SEED_PREFIX, nonce)
|
| 531 |
+
if len(normalized) >= 4 and normalized in seed:
|
| 532 |
+
nonce += 1
|
| 533 |
+
continue
|
| 534 |
+
owner = CANONICAL_SEED_TO_WORD.get(seed)
|
| 535 |
+
if owner is None or owner == normalized:
|
| 536 |
+
return seed
|
| 537 |
+
nonce += 1
|
| 538 |
+
|
| 539 |
+
|
| 540 |
def seed_to_word_filtered(seed: str, difficulty: str | None = None) -> str:
|
| 541 |
"""Deterministically pick a secret word, optionally filtered by difficulty."""
|
| 542 |
if difficulty:
|
|
|
|
| 588 |
difficulty=difficulty,
|
| 589 |
)
|
| 590 |
|
| 591 |
+
seed_value = canonical_seed_for_word(normalized) or _canonical_seed_fallback(normalized)
|
| 592 |
return SeedLookupResponse(
|
| 593 |
word=normalized,
|
| 594 |
found=True,
|