idea172-bevault-verifiable-.../idea172_bevault_verifiable_.../replay.py

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"]