84 lines
2.6 KiB
Python
84 lines
2.6 KiB
Python
"""Deterministic delta-sync and offline replay utilities."""
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from typing import Any, Dict, Iterable, List, Optional, Tuple
|
|
|
|
from .core import HedgeDelta
|
|
from .signing import sign_delta, verify_delta
|
|
|
|
|
|
@dataclass
|
|
class VersionVector:
|
|
clocks: Dict[str, int] = field(default_factory=dict)
|
|
|
|
def bump(self, actor: str) -> None:
|
|
self.clocks[actor] = self.clocks.get(actor, 0) + 1
|
|
|
|
def merge(self, other: "VersionVector") -> None:
|
|
for actor, counter in other.clocks.items():
|
|
self.clocks[actor] = max(self.clocks.get(actor, 0), counter)
|
|
|
|
def copy(self) -> "VersionVector":
|
|
return VersionVector(clocks=dict(self.clocks))
|
|
|
|
def to_dict(self) -> Dict[str, int]:
|
|
return dict(self.clocks)
|
|
|
|
|
|
@dataclass
|
|
class SignedDeltaEnvelope:
|
|
delta: HedgeDelta
|
|
actor: str
|
|
signature: str
|
|
version_vector: VersionVector
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
return {
|
|
"delta": self.delta.to_signable_dict(),
|
|
"actor": self.actor,
|
|
"signature": self.signature,
|
|
"version_vector": self.version_vector.to_dict(),
|
|
}
|
|
|
|
|
|
class DeltaSyncEngine:
|
|
"""Records and replays signed deltas deterministically."""
|
|
|
|
def __init__(self, signing_key: bytes) -> None:
|
|
self.signing_key = signing_key
|
|
self.version_vector = VersionVector()
|
|
self.envelopes: List[SignedDeltaEnvelope] = []
|
|
|
|
def record(self, delta: HedgeDelta, actor: str) -> SignedDeltaEnvelope:
|
|
self.version_vector.bump(actor)
|
|
delta.version_vector = self.version_vector.to_dict()
|
|
delta.signature = sign_delta(delta, self.signing_key)
|
|
envelope = SignedDeltaEnvelope(delta=delta, actor=actor, signature=delta.signature, version_vector=self.version_vector.copy())
|
|
self.envelopes.append(envelope)
|
|
return envelope
|
|
|
|
def ingest(self, envelope: SignedDeltaEnvelope) -> bool:
|
|
if not verify_delta(envelope.delta, self.signing_key, envelope.signature):
|
|
return False
|
|
self.version_vector.merge(envelope.version_vector)
|
|
self.envelopes.append(envelope)
|
|
return True
|
|
|
|
def replay(self) -> List[HedgeDelta]:
|
|
ordered = sorted(
|
|
self.envelopes,
|
|
key=lambda item: (
|
|
item.delta.timestamp,
|
|
item.actor,
|
|
item.signature,
|
|
),
|
|
)
|
|
return [envelope.delta for envelope in ordered]
|
|
|
|
def history(self) -> List[Dict[str, Any]]:
|
|
return [envelope.to_dict() for envelope in self.envelopes]
|
|
|
|
|
|
__all__ = ["DeltaSyncEngine", "SignedDeltaEnvelope", "VersionVector"]
|