39 lines
1.1 KiB
Python
39 lines
1.1 KiB
Python
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,
|
|
}
|