mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-05-27 09:32:28 +02:00
25a98a9869
Allow local-operator DM invite import without requiring a full admin session. Prioritize bundled/bootstrap seed peers and shorten stale seed cooldowns for faster Infonet recovery. Replace raw DM invite dumps with copyable signed-address controls, contact request handling, and safer sealed-send behavior while the private delivery route connects.
147 lines
4.5 KiB
Python
147 lines
4.5 KiB
Python
from __future__ import annotations
|
|
|
|
import time
|
|
from dataclasses import asdict, dataclass
|
|
|
|
from services.mesh.mesh_peer_store import PeerRecord
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class SyncWorkerState:
|
|
last_sync_started_at: int = 0
|
|
last_sync_finished_at: int = 0
|
|
last_sync_ok_at: int = 0
|
|
next_sync_due_at: int = 0
|
|
last_peer_url: str = ""
|
|
last_error: str = ""
|
|
last_outcome: str = "idle"
|
|
current_head: str = ""
|
|
fork_detected: bool = False
|
|
consecutive_failures: int = 0
|
|
|
|
def to_dict(self) -> dict[str, object]:
|
|
return asdict(self)
|
|
|
|
|
|
def eligible_sync_peers(records: list[PeerRecord], *, now: float | None = None) -> list[PeerRecord]:
|
|
current_time = int(now if now is not None else time.time())
|
|
candidates = [
|
|
record
|
|
for record in records
|
|
if record.bucket == "sync" and record.enabled and int(record.cooldown_until or 0) <= current_time
|
|
]
|
|
|
|
def _seed_priority(record: PeerRecord) -> int:
|
|
role = str(record.role or "").strip().lower()
|
|
source = str(record.source or "").strip().lower()
|
|
if role == "seed" and source in {"bundle", "bootstrap_promoted"}:
|
|
return 0
|
|
return 1
|
|
|
|
return sorted(
|
|
candidates,
|
|
key=lambda record: (
|
|
-int(record.last_sync_ok_at or 0),
|
|
_seed_priority(record),
|
|
int(record.failure_count or 0),
|
|
int(record.added_at or 0),
|
|
record.peer_url,
|
|
),
|
|
)
|
|
|
|
|
|
def begin_sync(
|
|
state: SyncWorkerState,
|
|
*,
|
|
peer_url: str = "",
|
|
current_head: str = "",
|
|
now: float | None = None,
|
|
) -> SyncWorkerState:
|
|
timestamp = int(now if now is not None else time.time())
|
|
return SyncWorkerState(
|
|
last_sync_started_at=timestamp,
|
|
last_sync_finished_at=state.last_sync_finished_at,
|
|
last_sync_ok_at=state.last_sync_ok_at,
|
|
next_sync_due_at=state.next_sync_due_at,
|
|
last_peer_url=peer_url or state.last_peer_url,
|
|
last_error="",
|
|
last_outcome="running",
|
|
current_head=current_head or state.current_head,
|
|
fork_detected=False,
|
|
consecutive_failures=state.consecutive_failures,
|
|
)
|
|
|
|
|
|
def finish_sync(
|
|
state: SyncWorkerState,
|
|
*,
|
|
ok: bool,
|
|
peer_url: str = "",
|
|
current_head: str = "",
|
|
error: str = "",
|
|
fork_detected: bool = False,
|
|
now: float | None = None,
|
|
interval_s: int = 300,
|
|
failure_backoff_s: int = 60,
|
|
) -> SyncWorkerState:
|
|
timestamp = int(now if now is not None else time.time())
|
|
if ok:
|
|
return SyncWorkerState(
|
|
last_sync_started_at=state.last_sync_started_at,
|
|
last_sync_finished_at=timestamp,
|
|
last_sync_ok_at=timestamp,
|
|
next_sync_due_at=timestamp + max(0, int(interval_s or 0)),
|
|
last_peer_url=peer_url or state.last_peer_url,
|
|
last_error="",
|
|
last_outcome="ok",
|
|
current_head=current_head or state.current_head,
|
|
fork_detected=bool(fork_detected),
|
|
consecutive_failures=0,
|
|
)
|
|
|
|
return SyncWorkerState(
|
|
last_sync_started_at=state.last_sync_started_at,
|
|
last_sync_finished_at=timestamp,
|
|
last_sync_ok_at=state.last_sync_ok_at,
|
|
next_sync_due_at=timestamp + max(0, int(failure_backoff_s or 0)),
|
|
last_peer_url=peer_url or state.last_peer_url,
|
|
last_error=str(error or "").strip(),
|
|
last_outcome="fork" if fork_detected else "error",
|
|
current_head=current_head or state.current_head,
|
|
fork_detected=bool(fork_detected),
|
|
consecutive_failures=state.consecutive_failures + 1,
|
|
)
|
|
|
|
|
|
def finish_solo_sync(
|
|
state: SyncWorkerState,
|
|
*,
|
|
current_head: str = "",
|
|
now: float | None = None,
|
|
interval_s: int = 300,
|
|
) -> SyncWorkerState:
|
|
timestamp = int(now if now is not None else time.time())
|
|
return SyncWorkerState(
|
|
last_sync_started_at=state.last_sync_started_at,
|
|
last_sync_finished_at=timestamp,
|
|
last_sync_ok_at=state.last_sync_ok_at,
|
|
next_sync_due_at=timestamp + max(0, int(interval_s or 0)),
|
|
last_peer_url="",
|
|
last_error="",
|
|
last_outcome="solo",
|
|
current_head=current_head or state.current_head,
|
|
fork_detected=False,
|
|
consecutive_failures=0,
|
|
)
|
|
|
|
|
|
def should_run_sync(
|
|
state: SyncWorkerState,
|
|
*,
|
|
now: float | None = None,
|
|
) -> bool:
|
|
current_time = int(now if now is not None else time.time())
|
|
if state.last_outcome == "running":
|
|
return False
|
|
return int(state.next_sync_due_at or 0) <= current_time
|