157 lines
4.6 KiB
Python
157 lines
4.6 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field, asdict
|
|
from typing import Any, Dict, List, Optional
|
|
import hashlib
|
|
import json
|
|
import time
|
|
|
|
|
|
def _canonical_json(payload: Any) -> str:
|
|
return json.dumps(payload, sort_keys=True, separators=(",", ":"), default=str)
|
|
|
|
|
|
def _fingerprint(payload: Any) -> str:
|
|
return hashlib.sha256(_canonical_json(payload).encode("utf-8")).hexdigest()
|
|
|
|
|
|
@dataclass
|
|
class LocalProblem:
|
|
name: str
|
|
region: Optional[str] = None
|
|
objective: str = ""
|
|
constraints: Dict[str, Any] = field(default_factory=dict)
|
|
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
created_at: float = field(default_factory=lambda: time.time())
|
|
|
|
def __post_init__(self) -> None:
|
|
if not self.name.strip():
|
|
raise ValueError("LocalProblem.name must not be empty")
|
|
|
|
def __repr__(self) -> str:
|
|
return f"LocalProblem(name={self.name!r}, region={self.region!r}, objective={self.objective!r})"
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
return asdict(self)
|
|
|
|
def fingerprint(self) -> str:
|
|
return _fingerprint(self.to_dict())
|
|
|
|
|
|
@dataclass
|
|
class SharedVariables:
|
|
description: str = ""
|
|
data: Dict[str, Any] = field(default_factory=dict)
|
|
aggregate_type: str = "sum"
|
|
privacy_budget_tag: Optional[str] = None
|
|
source_regions: List[str] = field(default_factory=list)
|
|
created_at: float = field(default_factory=lambda: time.time())
|
|
|
|
def __repr__(self) -> str:
|
|
return f"SharedVariables(desc={self.description!r}, keys={list(self.data.keys())}, aggregate={self.aggregate_type!r})"
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
return asdict(self)
|
|
|
|
def fingerprint(self) -> str:
|
|
return _fingerprint(self.to_dict())
|
|
|
|
|
|
@dataclass
|
|
class PlanDelta:
|
|
delta_id: str
|
|
actions: List[Dict[str, Any]] = field(default_factory=list)
|
|
timestamp: float = field(default_factory=lambda: time.time())
|
|
actor_did: Optional[str] = None
|
|
causality: Dict[str, int] = field(default_factory=dict)
|
|
policy_tags: List[str] = field(default_factory=list)
|
|
privacy_budget_tag: Optional[str] = None
|
|
annotations: Dict[str, Any] = field(default_factory=dict)
|
|
signature: Optional[str] = None
|
|
|
|
def __post_init__(self) -> None:
|
|
if not self.delta_id.strip():
|
|
raise ValueError("PlanDelta.delta_id must not be empty")
|
|
if not isinstance(self.actions, list):
|
|
raise TypeError("PlanDelta.actions must be a list")
|
|
|
|
def __repr__(self) -> str:
|
|
return f"PlanDelta(delta_id={self.delta_id!r}, actions={len(self.actions)} actions, actor={self.actor_did!r})"
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
return asdict(self)
|
|
|
|
def fingerprint(self) -> str:
|
|
return _fingerprint(self.to_dict())
|
|
|
|
|
|
@dataclass
|
|
class Policy:
|
|
policy_id: str
|
|
rules: Dict[str, Any] = field(default_factory=dict)
|
|
effective_at: float = field(default_factory=lambda: time.time())
|
|
scope: str = "global"
|
|
approved_by: Optional[str] = None
|
|
|
|
def __repr__(self) -> str:
|
|
return f"Policy(id={self.policy_id!r}, rules={len(self.rules)}, scope={self.scope!r})"
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
return asdict(self)
|
|
|
|
|
|
@dataclass
|
|
class AttestationHint:
|
|
hint_id: str
|
|
scheme: str = "dummy"
|
|
payload: Dict[str, Any] = field(default_factory=dict)
|
|
expires_at: Optional[float] = None
|
|
|
|
def __repr__(self) -> str:
|
|
return f"AttestationHint(id={self.hint_id!r}, scheme={self.scheme!r})"
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
return asdict(self)
|
|
|
|
|
|
@dataclass
|
|
class AuditLog:
|
|
entry_id: str
|
|
message: str
|
|
timestamp: float = field(default_factory=lambda: time.time())
|
|
signature: Optional[str] = None
|
|
anchor: Optional[str] = None
|
|
|
|
def __repr__(self) -> str:
|
|
return f"AuditLog(id={self.entry_id!r}, ts={self.timestamp})"
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
return asdict(self)
|
|
|
|
|
|
@dataclass
|
|
class PrivacyBudget:
|
|
budget_id: str
|
|
limit: float
|
|
used: float = 0.0
|
|
|
|
def consume(self, amount: float) -> bool:
|
|
if amount < 0:
|
|
raise ValueError("PrivacyBudget.consume requires a non-negative amount")
|
|
if self.used + amount > self.limit:
|
|
return False
|
|
self.used += amount
|
|
return True
|
|
|
|
def can_consume(self, amount: float) -> bool:
|
|
return amount >= 0 and self.used + amount <= self.limit
|
|
|
|
def remaining(self) -> float:
|
|
return self.limit - self.used
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
return asdict(self)
|
|
|
|
def __repr__(self) -> str:
|
|
return f"PrivacyBudget(id={self.budget_id!r}, remaining={self.remaining()})"
|