56 lines
1.5 KiB
Python
56 lines
1.5 KiB
Python
from __future__ import annotations
|
|
from dataclasses import dataclass, field
|
|
from typing import List, Optional
|
|
import hashlib
|
|
import time
|
|
import json
|
|
|
|
|
|
def _hash(data: str) -> str:
|
|
return hashlib.sha256(data.encode("utf-8")).hexdigest()
|
|
|
|
|
|
@dataclass
|
|
class Block:
|
|
index: int
|
|
timestamp: float
|
|
payload: str # JSON-serialized contract data or attestation
|
|
previous_hash: str
|
|
hash: str = field(init=False)
|
|
|
|
def __post_init__(self):
|
|
self.hash = _hash(self._block_string())
|
|
|
|
def _block_string(self) -> str:
|
|
return f"{self.index}:{self.timestamp}:{self.payload}:{self.previous_hash}"
|
|
|
|
|
|
class AttestationLedger:
|
|
def __init__(self):
|
|
self._chain: List[Block] = []
|
|
self._current_previous_hash: str = "0" * 64
|
|
|
|
def append(self, payload_json: str) -> Block:
|
|
index = len(self._chain)
|
|
block = Block(
|
|
index=index,
|
|
timestamp=time.time(),
|
|
payload=payload_json,
|
|
previous_hash=self._current_previous_hash,
|
|
)
|
|
self._chain.append(block)
|
|
self._current_previous_hash = block.hash
|
|
return block
|
|
|
|
def latest(self) -> Optional[Block]:
|
|
return self._chain[-1] if self._chain else None
|
|
|
|
def to_json(self) -> str:
|
|
return json.dumps([{
|
|
"index": b.index,
|
|
"timestamp": b.timestamp,
|
|
"payload": b.payload,
|
|
"previous_hash": b.previous_hash,
|
|
"hash": b.hash,
|
|
} for b in self._chain], sort_keys=True)
|