godcode-engine 4.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
godcode/chain.py ADDED
@@ -0,0 +1,210 @@
1
+ """Blockchain-anchored seals for God Code v4.0 -- "Intent & Chain".
2
+
3
+ A ChainAdapter anchors a payload hash into a tamper-evident chain and
4
+ later verifies the receipt it handed back. The bundled
5
+ SimulatedChainAdapter keeps the anchor chain as local JSONL
6
+ (``anchors.chain``), mirroring :class:`godcode.ledger.CovenantLedger`;
7
+ real chain adapters (Ethereum, and the like) can be registered later
8
+ with :func:`register_adapter` -- that work belongs to the founder, not
9
+ to this module. There are no network calls here, no wallets, no keys.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import hashlib
15
+ import json
16
+ from datetime import datetime, timezone
17
+ from pathlib import Path
18
+
19
+ GENESIS_PREV_HASH = "GENESIS"
20
+ DEFAULT_CHAIN_NAME = "simulated"
21
+
22
+
23
+ def _canonical(payload: dict) -> bytes:
24
+ """Canonical JSON bytes used for hashing (stable key order, no whitespace)."""
25
+ return json.dumps(payload, sort_keys=True, separators=(",", ":"),
26
+ ensure_ascii=False).encode("utf-8")
27
+
28
+
29
+ def _block_hash(index: int, timestamp: str, record: dict, prev_hash: str) -> str:
30
+ return hashlib.sha256(
31
+ _canonical(
32
+ {"index": index, "timestamp": timestamp,
33
+ "record": record, "prev_hash": prev_hash}
34
+ )
35
+ ).hexdigest()
36
+
37
+
38
+ class ChainAdapter:
39
+ """The contract every chain adapter honors.
40
+
41
+ ``anchor(payload_hash)`` writes the hash into the chain and returns a
42
+ receipt dict; ``verify(receipt)`` returns True only when the receipt
43
+ matches a block that is still intact.
44
+ """
45
+
46
+ name = "adapter"
47
+
48
+ def anchor(self, payload_hash: str) -> dict:
49
+ raise NotImplementedError
50
+
51
+ def verify(self, receipt: dict) -> bool:
52
+ raise NotImplementedError
53
+
54
+
55
+ class SimulatedChainAdapter(ChainAdapter):
56
+ """A local tamper-evident anchor chain (JSONL), standing in for a chain.
57
+
58
+ Blocks look like ``{index, timestamp, record: {payload_hash},
59
+ prev_hash, hash}``; the chain begins at ``"GENESIS"``. Receipts look
60
+ like ``{chain, anchor_hash, height, timestamp, payload_hash}``.
61
+ """
62
+
63
+ name = "simulated"
64
+
65
+ def __init__(self, path: str | Path | None = "anchors.chain") -> None:
66
+ self.path = Path(path) if path is not None else None
67
+ self._memory: list[dict] = []
68
+ if self.path is not None and str(self.path.parent) not in ("", "."):
69
+ self.path.parent.mkdir(parents=True, exist_ok=True)
70
+
71
+ # ---------------------------------------------------------- persistence
72
+
73
+ def read_all(self) -> list[dict]:
74
+ """Every anchor block in chain order (empty when no chain yet)."""
75
+ if self.path is None:
76
+ return list(self._memory)
77
+ if not self.path.exists():
78
+ return []
79
+ blocks: list[dict] = []
80
+ with open(self.path, encoding="utf-8") as f:
81
+ for line in f:
82
+ line = line.strip()
83
+ if line:
84
+ blocks.append(json.loads(line))
85
+ return blocks
86
+
87
+ def _append(self, block: dict) -> None:
88
+ if self.path is None:
89
+ self._memory.append(block)
90
+ else:
91
+ with open(self.path, "a", encoding="utf-8") as f:
92
+ f.write(json.dumps(block, ensure_ascii=False) + "\n")
93
+
94
+ # ------------------------------------------------------------- adapter
95
+
96
+ def anchor(self, payload_hash: str) -> dict:
97
+ """Anchor a payload hash; return the receipt dict."""
98
+ blocks = self.read_all()
99
+ index = len(blocks)
100
+ prev_hash = blocks[-1]["hash"] if blocks else GENESIS_PREV_HASH
101
+ timestamp = datetime.now(timezone.utc).isoformat()
102
+ record = {"payload_hash": payload_hash}
103
+ block = {
104
+ "index": index,
105
+ "timestamp": timestamp,
106
+ "record": record,
107
+ "prev_hash": prev_hash,
108
+ "hash": _block_hash(index, timestamp, record, prev_hash),
109
+ }
110
+ self._append(block)
111
+ return {
112
+ "chain": self.name,
113
+ "anchor_hash": block["hash"],
114
+ "height": block["index"],
115
+ "timestamp": block["timestamp"],
116
+ "payload_hash": payload_hash,
117
+ }
118
+
119
+ def verify(self, receipt: dict) -> bool:
120
+ """True when the receipt names a block that is present and intact."""
121
+ try:
122
+ height = receipt["height"]
123
+ anchor_hash = receipt["anchor_hash"]
124
+ payload_hash = receipt["payload_hash"]
125
+ except (KeyError, TypeError, AttributeError):
126
+ return False
127
+ if not isinstance(height, int) or isinstance(height, bool):
128
+ return False
129
+ blocks = self.read_all()
130
+ if height < 0 or height >= len(blocks):
131
+ return False
132
+ block = blocks[height]
133
+ if block.get("hash") != anchor_hash:
134
+ return False
135
+ if block.get("record", {}).get("payload_hash") != payload_hash:
136
+ return False
137
+ return _block_hash(
138
+ block.get("index"),
139
+ block.get("timestamp"),
140
+ block.get("record"),
141
+ block.get("prev_hash"),
142
+ ) == block.get("hash")
143
+
144
+ def verify_chain(self) -> tuple[bool, str]:
145
+ """Recompute the whole anchor chain.
146
+
147
+ (True, 'N anchors intact') or (False, 'anchor chain broken at
148
+ block K').
149
+ """
150
+ blocks = self.read_all()
151
+ prev_hash = GENESIS_PREV_HASH
152
+ for expected, block in enumerate(blocks):
153
+ payload_ok = (
154
+ block.get("index") == expected
155
+ and block.get("prev_hash") == prev_hash
156
+ and _block_hash(
157
+ block.get("index"),
158
+ block.get("timestamp"),
159
+ block.get("record"),
160
+ block.get("prev_hash"),
161
+ )
162
+ == block.get("hash")
163
+ )
164
+ if not payload_ok:
165
+ return False, f"anchor chain broken at block {block.get('index')} ⚓💔"
166
+ prev_hash = block["hash"]
167
+ return True, f"{len(blocks)} anchors intact ⚓"
168
+
169
+
170
+ class MemoryChainAdapter(SimulatedChainAdapter):
171
+ """An ephemeral in-memory anchor chain: the sandbox's answer to ANCHOR.
172
+
173
+ Same interface as the simulated chain, but nothing is written to
174
+ disk. Anchors made here vanish when the run ends -- which is exactly
175
+ what a deny-by-default sandbox requires.
176
+ """
177
+
178
+ name = "simulated"
179
+
180
+ def __init__(self) -> None:
181
+ super().__init__(path=None)
182
+
183
+
184
+ # ------------------------------------------------------------- registry
185
+
186
+ _ADAPTERS: dict[str, ChainAdapter] = {}
187
+
188
+
189
+ def register_adapter(name: str, adapter: ChainAdapter) -> None:
190
+ """Register a chain adapter under *name* (e.g. a future real chain)."""
191
+ if not isinstance(name, str) or not name:
192
+ raise ValueError("a chain adapter needs a non-empty string name")
193
+ if not isinstance(adapter, ChainAdapter):
194
+ raise ValueError(f"'{name}' is not a ChainAdapter")
195
+ _ADAPTERS[name] = adapter
196
+
197
+
198
+ def get_adapter(name: str) -> ChainAdapter | None:
199
+ """The adapter registered under *name*, or None."""
200
+ return _ADAPTERS.get(name)
201
+
202
+
203
+ def default_adapters() -> dict[str, ChainAdapter]:
204
+ """A fresh per-interpreter registry holding the bundled adapters."""
205
+ adapters = dict(_ADAPTERS)
206
+ adapters.setdefault(DEFAULT_CHAIN_NAME, SimulatedChainAdapter())
207
+ return adapters
208
+
209
+
210
+ register_adapter(DEFAULT_CHAIN_NAME, SimulatedChainAdapter())