from __future__ import annotations import hashlib import json from typing import List from .contracts import Signal class MerkleProvenance: @staticmethod def hash_item(item: dict) -> str: return hashlib.sha256(json.dumps(item, sort_keys=True).encode()).hexdigest() @staticmethod def merkle_root(values: List[Signal]) -> str: # Build a simple Merkle tree over serialized signals leaves = [MerkleProvenance.hash_item(signal_to_dict(s)) for s in values] if not leaves: return "" # empty root level = leaves while len(level) > 1: next_level = [] for i in range(0, len(level), 2): left = level[i] right = level[i + 1] if i + 1 < len(level) else left next_level.append(hashlib.sha256((left + right).encode()).hexdigest()) level = next_level return level[0] def signal_to_dict(s: Signal) -> dict: return { "venue": s.venue, "timestamp": s.timestamp, "metrics": s.metrics, "version": s.version, }