diff --git a/gravityweave/dtn.py b/gravityweave/dtn.py new file mode 100644 index 0000000..55621c7 --- /dev/null +++ b/gravityweave/dtn.py @@ -0,0 +1,59 @@ +"""DTN custody header and simple custody policy helpers. + +This module provides a minimal CustodyHeader dataclass used by DTN custody +holders to make decisions about storing/transferring PlanDelta payloads. +It also exposes a small helper that decides whether to accept custody based +on priority, remaining lifetime, and a node reputation score. + +The implementation is intentionally small and deterministic for unit testing. +""" +from dataclasses import dataclass, field +from typing import List +import time + + +@dataclass +class CustodyHeader: + delta_id: str + created_at: float = field(default_factory=time.time) + lifetime_secs: int = 3600 # how long the custody is expected to be valid + custody_chain: List[str] = field(default_factory=list) + priority_key: int = 0 # combined urgency/impact key (0..127) + + def is_expired(self, now: float = None) -> bool: + if now is None: + now = time.time() + return now > (self.created_at + self.lifetime_secs) + + def add_holder(self, node_id: str): + # append to custody chain (no dedupe to keep deterministic append-only) + self.custody_chain.append(node_id) + + +def should_accept_custody(header: CustodyHeader, node_reputation: float, min_reputation: float = 0.2) -> bool: + """Decide whether a node should accept custody for a delta. + + Decision factors (simple heuristic): + - If the header is already expired, refuse custody. + - Prefer accepting higher priority_key values. + - Gate acceptance by a minimum reputation threshold. + + Returns True if the node should accept custody. + """ + if header.is_expired(): + return False + + # Enforce a minimum reputation gate: nodes below min_reputation refuse custody + if node_reputation < min_reputation: + return False + + # decode urgency and impact from priority_key (urgency<<4)|impact + urgency = (header.priority_key >> 4) & 0x07 + impact = header.priority_key & 0x0F + + # compute a simple desirability score + score = urgency * 2 + impact + (node_reputation * 4) + + # threshold depends on min_reputation; higher min_reputation raises bar + threshold = 5.0 + (1.0 - min_reputation) * 3.0 + return score >= threshold diff --git a/tests/test_dtn.py b/tests/test_dtn.py new file mode 100644 index 0000000..e797ef6 --- /dev/null +++ b/tests/test_dtn.py @@ -0,0 +1,19 @@ +from gravityweave.dtn import CustodyHeader, should_accept_custody +import time + + +def test_custody_header_and_expiry(): + h = CustodyHeader(delta_id="d1", lifetime_secs=1) + assert not h.is_expired() + # wait for expiry + time.sleep(1.1) + assert h.is_expired() + + +def test_should_accept_custody(): + # fresh header with medium priority + h = CustodyHeader(delta_id="d2", lifetime_secs=60, priority_key=(2 << 4) | 8) + # low reputation should often reject + assert not should_accept_custody(h, node_reputation=0.0, min_reputation=0.2) + # decent reputation should accept + assert should_accept_custody(h, node_reputation=0.8, min_reputation=0.2)