Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions publisher_verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -856,6 +856,31 @@ def _reactivate(conn):
pm_closed.status_code == 503
and pm_closed.json().get("detail") == "per_miner_seed_secret_missing")
os.environ["CATHEDRAL_PERMINER_SEED_SECRET"] = "publisher-verify-stable-seed"
_pm._instance_index.cache_clear()
os.environ["CATHEDRAL_PERMINER_ALLOTMENT_T1"] = "3"
cache_epoch = _pm.current_epoch()
cache_id = _pm.instance_id("cache-hotkey", cache_epoch, 1, 2)
cache_first = _pm.recover_tier_seq_for("cache-hotkey", cache_epoch, cache_id)
cache_info_first = _pm._instance_index.cache_info()
cache_second = _pm.recover_tier_seq_for("cache-hotkey", cache_epoch, cache_id)
cache_info_second = _pm._instance_index.cache_info()
cache_foreign = _pm.recover_tier_seq_for("cache-other-hotkey", cache_epoch, cache_id)
os.environ["CATHEDRAL_PERMINER_ALLOTMENT_T1"] = "2"
cache_reduced_allotment = _pm.recover_tier_seq_for("cache-hotkey", cache_epoch, cache_id)
os.environ["CATHEDRAL_PERMINER_ALLOTMENT_T1"] = "3"
os.environ["CATHEDRAL_PERMINER_SEED_SECRET"] = "publisher-verify-cache-rotated"
cache_rotated = _pm.recover_tier_seq_for("cache-hotkey", cache_epoch, cache_id)
ck("per-miner recovery index is cached and seed-scoped",
cache_first == (1, 2)
and cache_second == (1, 2)
and cache_info_first.misses >= 1
and cache_info_second.hits > cache_info_first.hits
and cache_foreign is None
and cache_reduced_allotment is None
and cache_rotated is None)
os.environ["CATHEDRAL_PERMINER_SEED_SECRET"] = "publisher-verify-stable-seed"
os.environ["CATHEDRAL_PERMINER_ALLOTMENT_T1"] = "1"
_pm._instance_index.cache_clear()
def _map_pm_coldkey(conn):
conn.execute(
"INSERT OR REPLACE INTO coldkey_map(hotkey, coldkey, updated_at_iso) "
Expand Down
38 changes: 31 additions & 7 deletions scaffold/publisher/per_miner.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ def allotment_for(tier: int) -> int:
{1: 10_000, 2: 10_000}.get(tier, 10_000)))


# Worst-case entries are roughly cache_size * allotment_for(tier).
_RECOVER_INDEX_CACHE_SIZE = max(
1, _env_int("CATHEDRAL_PERMINER_RECOVER_INDEX_CACHE", 64)
)


def assignment_page_limit_max() -> int:
return max(1, min(500, _env_int("CATHEDRAL_PERMINER_MAX_PAGE_LIMIT", 50)))

Expand Down Expand Up @@ -237,6 +243,10 @@ def _seed_secret_bytes() -> bytes:
return _EPHEMERAL_SEED_SECRET


def _seed_secret_fingerprint() -> str:
return hashlib.sha256(_seed_secret_bytes()).hexdigest()


# --------------------------------------------------------------------------
# Per-miner instance set generation
# --------------------------------------------------------------------------
Expand Down Expand Up @@ -369,19 +379,33 @@ def verify_miner_submission_for(


def recover_tier_seq_for(hotkey: str, epoch: int, challenge_id: str) -> tuple[int, int] | None:
"""Find (tier, seq) for a challenge_id by scanning the miner's allotment.
"""Find (tier, seq) for a challenge_id in the miner's allotment.
Returns None if the challenge_id was not generated for this hotkey+epoch.
"""
parsed = parse_challenge_id(challenge_id)
candidate_tiers = [parsed["tier"]] if parsed and parsed["tier"] in TIERS else TIERS
for tier in candidate_tiers:
for seq in range(allotment_for(tier)):
cid = instance_id(hotkey, epoch, tier, seq)
if cid == challenge_id:
return tier, seq
if not parsed or parsed["epoch"] != int(epoch) or parsed["tier"] not in TIERS:
return None
tier = parsed["tier"]
seq = _instance_index(_recover_index_key(hotkey, epoch, tier)).get(challenge_id)
if seq is not None:
return tier, seq
return None


def _recover_index_key(hotkey: str, epoch: int, tier: int) -> tuple[str, int, int, int, str]:
return hotkey, int(epoch), int(tier), allotment_for(tier), _seed_secret_fingerprint()


@lru_cache(maxsize=_RECOVER_INDEX_CACHE_SIZE)
def _instance_index(key: tuple[str, int, int, int, str]) -> dict[str, int]:
"""Shared read-only cid->seq cache; do not mutate the returned dict."""
hotkey, epoch, tier, allotment, _secret_fingerprint = key
return {
instance_id(hotkey, epoch, tier, seq): seq
for seq in range(allotment)
}


def recover_seq_for(hotkey: str, epoch: int, challenge_id: str) -> int | None:
"""Find the seq number for a challenge_id. Kept for backward compatibility."""
result = recover_tier_seq_for(hotkey, epoch, challenge_id)
Expand Down
Loading