From 403b674bf0509ac3973cad5af76867558e7a7277 Mon Sep 17 00:00:00 2001 From: agent-7e3bbc424e07835b Date: Mon, 20 Apr 2026 14:35:12 +0200 Subject: [PATCH] build(agent): new-agents-2#7e3bbc iteration --- cosmic_ledger/delta.py | 53 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/cosmic_ledger/delta.py b/cosmic_ledger/delta.py index 2905d27..567b008 100644 --- a/cosmic_ledger/delta.py +++ b/cosmic_ledger/delta.py @@ -18,6 +18,38 @@ def merkle_root(digests: List[str]) -> str: level = next_level return level[0].hex() +def merkle_path_for_index(digests: List[str], index: int) -> dict: + """Compute Merkle root and the Merkle path for a given leaf index. + + Returns a dict with keys: + - root: the Merkle root for the given leaves + - path: list of sibling digests (hex) from leaf to root + """ + if not digests: + return {"root": "", "path": []} + # convert hex digests to bytes for hashing + level = [bytes.fromhex(d) for d in digests] + idx = index + path = [] + while len(level) > 1: + # determine sibling index for current level + sib_index = idx ^ 1 # toggle last bit -> sibling index + if 0 <= sib_index < len(level): + path.append(level[sib_index].hex()) + else: + # no sibling, duplicate the current node + path.append(level[idx].hex()) + # build next level + next_level = [] + for i in range(0, len(level), 2): + left = level[i] + right = level[i+1] if i+1 < len(level) else level[i] + next_level.append(hashlib.sha256(left + right).digest()) + level = next_level + idx = idx // 2 + root = level[0].hex() + return {"root": root, "path": path} + class DeltaLog: def __init__(self): self.entries = [] # each entry is a dict with digest and payload @@ -43,8 +75,25 @@ class DeltaLog: self.anchor = anchor def delta_from_index(self, index: int) -> List[dict]: - # return full payloads for simplicity (MVP). In a real system you'd return compact digests with proofs. - return [e["payload"] for e in self.entries[index:]] + # Return full LedgerEntry dictionaries augmented with Merkle proofs. + # This enables recipients to reconstruct and verify the delta with + # compact proofs, while preserving compatibility with existing code. + leaves = [e["digest"] for e in self.entries] + result = [] + for i in range(index, len(self.entries)): + entry = self.entries[i] + # Compute Merkle path for this leaf with respect to all leaves + path_info = merkle_path_for_index(leaves, i) + # The payload is the LedgerEntry dict previously stored in 'payload' + full_entry = entry["payload"].copy() + # Attach meta for verification without altering the payload structure + full_entry.update({ + "digest": entry["digest"], + "delta_root": path_info["root"], + "merkle_path": path_info["path"], + }) + result.append(full_entry) + return result def root(self) -> str: digests = [e["digest"] for e in self.entries]