67 lines
2.4 KiB
Python
67 lines
2.4 KiB
Python
import json
|
|
import hashlib
|
|
import time
|
|
from typing import Dict, List
|
|
|
|
|
|
class GovernanceBlock:
|
|
def __init__(self, version: int, policy_blob: str, approvals: Dict[str, str], timestamp: float = None):
|
|
self.version = version
|
|
self.policy_blob = policy_blob
|
|
self.approvals = approvals # signer_id -> signature (simulated)
|
|
self.timestamp = timestamp or time.time()
|
|
self.block_hash = self.compute_hash()
|
|
|
|
def compute_hash(self) -> str:
|
|
m = hashlib.sha256()
|
|
m.update(str(self.version).encode())
|
|
m.update(self.policy_blob.encode())
|
|
m.update(json.dumps(self.approvals, sort_keys=True).encode())
|
|
m.update(str(self.timestamp).encode())
|
|
return m.hexdigest()
|
|
|
|
def to_dict(self) -> Dict:
|
|
return {
|
|
"version": self.version,
|
|
"policy_blob": self.policy_blob,
|
|
"approvals": self.approvals,
|
|
"timestamp": self.timestamp,
|
|
"block_hash": self.block_hash,
|
|
}
|
|
|
|
|
|
class GovernanceLedger:
|
|
def __init__(self, quorum: int = 1):
|
|
self.blocks: List[GovernanceBlock] = []
|
|
self.quorum = quorum
|
|
self._latest_hash = None
|
|
|
|
def append_block(self, policy_blob: str, approvals: Dict[str, str]) -> GovernanceBlock:
|
|
version = len(self.blocks) + 1
|
|
block = GovernanceBlock(version, policy_blob, approvals)
|
|
if not self._validate_approvals(approvals):
|
|
raise ValueError("Approvals do not meet quorum or have invalid signers")
|
|
self.blocks.append(block)
|
|
self._latest_hash = block.block_hash
|
|
return block
|
|
|
|
def _validate_approvals(self, approvals: Dict[str, str]) -> bool:
|
|
# Simple quorum check: number of approvals >= quorum
|
|
return len(approvals) >= self.quorum
|
|
|
|
def verify_chain(self) -> bool:
|
|
# Basic chain integrity: each block hash must equal the recomputed hash
|
|
for i, b in enumerate(self.blocks):
|
|
if b.block_hash != b.compute_hash():
|
|
return False
|
|
if i > 0 and self.blocks[i-1].block_hash != self.blocks[i].block_hash:
|
|
# In a real DAG/log, you'd verify hashes linking; here we ensure determinism
|
|
continue
|
|
return True
|
|
|
|
def last_block(self) -> GovernanceBlock:
|
|
return self.blocks[-1] if self.blocks else None
|
|
|
|
def to_list(self) -> List[Dict]:
|
|
return [b.to_dict() for b in self.blocks]
|