khipu-consensus 0.1.0__tar.gz
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.
- khipu_consensus-0.1.0/PKG-INFO +42 -0
- khipu_consensus-0.1.0/README.md +20 -0
- khipu_consensus-0.1.0/khipu_consensus/__init__.py +196 -0
- khipu_consensus-0.1.0/khipu_consensus/adversary.py +679 -0
- khipu_consensus-0.1.0/khipu_consensus/cli.py +58 -0
- khipu_consensus-0.1.0/khipu_consensus/collusion.py +65 -0
- khipu_consensus-0.1.0/khipu_consensus/sdk.py +91 -0
- khipu_consensus-0.1.0/khipu_consensus/spine.py +298 -0
- khipu_consensus-0.1.0/khipu_consensus/witness.py +405 -0
- khipu_consensus-0.1.0/khipu_consensus.egg-info/PKG-INFO +42 -0
- khipu_consensus-0.1.0/khipu_consensus.egg-info/SOURCES.txt +22 -0
- khipu_consensus-0.1.0/khipu_consensus.egg-info/dependency_links.txt +1 -0
- khipu_consensus-0.1.0/khipu_consensus.egg-info/entry_points.txt +2 -0
- khipu_consensus-0.1.0/khipu_consensus.egg-info/requires.txt +12 -0
- khipu_consensus-0.1.0/khipu_consensus.egg-info/top_level.txt +1 -0
- khipu_consensus-0.1.0/pyproject.toml +38 -0
- khipu_consensus-0.1.0/setup.cfg +4 -0
- khipu_consensus-0.1.0/tests/test_adversary.py +255 -0
- khipu_consensus-0.1.0/tests/test_collusion.py +19 -0
- khipu_consensus-0.1.0/tests/test_collusion_admission.py +39 -0
- khipu_consensus-0.1.0/tests/test_service.py +115 -0
- khipu_consensus-0.1.0/tests/test_spine.py +167 -0
- khipu_consensus-0.1.0/tests/test_vectors.py +41 -0
- khipu_consensus-0.1.0/tests/test_witness.py +324 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: khipu-consensus
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Byzantine-fault-tolerant multi-party signed agreement (3-of-4 BFT) over DSSE/cosign per-witness signatures.
|
|
5
|
+
Author-email: Yachay <yachay@szlholdings.dev>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/szl-holdings/khipu-consensus
|
|
8
|
+
Project-URL: Source, https://github.com/szl-holdings/khipu-consensus
|
|
9
|
+
Project-URL: Issues, https://github.com/szl-holdings/khipu-consensus/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/szl-holdings/khipu-consensus/releases
|
|
11
|
+
Requires-Python: >=3.9
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: cryptography>=41.0
|
|
14
|
+
Provides-Extra: service
|
|
15
|
+
Requires-Dist: fastapi>=0.100; extra == "service"
|
|
16
|
+
Requires-Dist: uvicorn>=0.23; extra == "service"
|
|
17
|
+
Requires-Dist: pydantic>=2.0; extra == "service"
|
|
18
|
+
Provides-Extra: remote
|
|
19
|
+
Requires-Dist: requests>=2.28; extra == "remote"
|
|
20
|
+
Provides-Extra: spine
|
|
21
|
+
Requires-Dist: szl-receipt-dsse>=0.3.1; extra == "spine"
|
|
22
|
+
|
|
23
|
+
# khipu-consensus (Python)
|
|
24
|
+
|
|
25
|
+
Byzantine-fault-tolerant multi-party signed agreement — a 3-of-4 witness quorum over
|
|
26
|
+
DSSE/cosign per-witness signatures. This is the Python package for the
|
|
27
|
+
[szl-holdings/khipu-consensus](https://github.com/szl-holdings/khipu-consensus) repository;
|
|
28
|
+
the repository README carries the full doctrine, demo links, and evidence boundaries.
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install khipu-consensus
|
|
32
|
+
khipu-verify --help
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Doctrine boundary: safety and liveness of the quorum are **Conjecture 2 / Conjecture 3**
|
|
36
|
+
(proof-deferred, not proven). Λ remains Conjecture 1. Nothing in this package asserts a
|
|
37
|
+
theorem it does not carry.
|
|
38
|
+
|
|
39
|
+
Optional extras: `service` (FastAPI witness API), `remote` (HTTP SDK transport),
|
|
40
|
+
`spine` (fold a witnessed decision into one canonical `szl-receipt-dsse` receipt).
|
|
41
|
+
|
|
42
|
+
License: Apache-2.0 · Source: https://github.com/szl-holdings/khipu-consensus
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# khipu-consensus (Python)
|
|
2
|
+
|
|
3
|
+
Byzantine-fault-tolerant multi-party signed agreement — a 3-of-4 witness quorum over
|
|
4
|
+
DSSE/cosign per-witness signatures. This is the Python package for the
|
|
5
|
+
[szl-holdings/khipu-consensus](https://github.com/szl-holdings/khipu-consensus) repository;
|
|
6
|
+
the repository README carries the full doctrine, demo links, and evidence boundaries.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pip install khipu-consensus
|
|
10
|
+
khipu-verify --help
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Doctrine boundary: safety and liveness of the quorum are **Conjecture 2 / Conjecture 3**
|
|
14
|
+
(proof-deferred, not proven). Λ remains Conjecture 1. Nothing in this package asserts a
|
|
15
|
+
theorem it does not carry.
|
|
16
|
+
|
|
17
|
+
Optional extras: `service` (FastAPI witness API), `remote` (HTTP SDK transport),
|
|
18
|
+
`spine` (fold a witnessed decision into one canonical `szl-receipt-dsse` receipt).
|
|
19
|
+
|
|
20
|
+
License: Apache-2.0 · Source: https://github.com/szl-holdings/khipu-consensus
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# © 2026 Lutar, Stephen P. — SZL Holdings · ORCID 0009-0001-0110-4173
|
|
3
|
+
"""
|
|
4
|
+
khipu-consensus — Byzantine-fault-tolerant multi-party signed agreement.
|
|
5
|
+
|
|
6
|
+
The category: multi-party-witnessed AI. Each witness ("organ") signs an action
|
|
7
|
+
hash with its own ECDSA-P256 key. >= threshold valid `allow` signatures over the
|
|
8
|
+
same action hash ⇒ the action is CANONICAL (BFT n witnesses, f = n - threshold
|
|
9
|
+
tolerated faults; 3-of-4 tolerates 1). Every per-witness signature is
|
|
10
|
+
ECDSA-P256-SHA256 over the DSSE (Dead-Simple-Signing-Envelope) Pre-Authentication
|
|
11
|
+
Encoding, so it is verifiable by `cosign verify-blob --key <witness>.pub`.
|
|
12
|
+
|
|
13
|
+
Reference implementation. Mirrors typescript/ and go/ byte-for-byte on the same
|
|
14
|
+
deterministic test vectors (testdata/vectors.json).
|
|
15
|
+
"""
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import base64
|
|
19
|
+
import hashlib
|
|
20
|
+
import json
|
|
21
|
+
from dataclasses import dataclass, field
|
|
22
|
+
from typing import Optional
|
|
23
|
+
|
|
24
|
+
ORGAN_VERDICT_PAYLOAD_TYPE = "application/vnd.szl.khipu.organ-verdict+json"
|
|
25
|
+
__version__ = "0.1.0"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def canonical_json(obj) -> bytes:
|
|
29
|
+
"""Deterministic canonical JSON: sorted keys, compact separators, UTF-8."""
|
|
30
|
+
return json.dumps(obj, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def pae(payload_type: str, body: bytes) -> bytes:
|
|
34
|
+
"""DSSE Pre-Authentication Encoding (DSSEv1)."""
|
|
35
|
+
t = payload_type.encode("utf-8")
|
|
36
|
+
return b"DSSEv1 " + str(len(t)).encode() + b" " + t + b" " + str(len(body)).encode() + b" " + body
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class OrganVerdict:
|
|
41
|
+
organ: str
|
|
42
|
+
keyid: str
|
|
43
|
+
payload_type: str
|
|
44
|
+
payload_b64: str # base64 of the canonical signed statement
|
|
45
|
+
signature_b64: str # base64 of the ECDSA-P256-SHA256 signature over PAE
|
|
46
|
+
verdict: str = "allow" # informational; the authoritative verdict is inside payload
|
|
47
|
+
reason: str = ""
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def from_dict(d: dict) -> "OrganVerdict":
|
|
51
|
+
return OrganVerdict(
|
|
52
|
+
organ=d.get("organ", ""),
|
|
53
|
+
keyid=d.get("keyid", ""),
|
|
54
|
+
payload_type=d.get("payloadType", ORGAN_VERDICT_PAYLOAD_TYPE),
|
|
55
|
+
payload_b64=d.get("payload", ""),
|
|
56
|
+
signature_b64=d.get("signature", ""),
|
|
57
|
+
verdict=d.get("verdict", "allow"),
|
|
58
|
+
reason=d.get("reason", ""),
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass
|
|
63
|
+
class OrganCheck:
|
|
64
|
+
organ: str
|
|
65
|
+
keyid: str
|
|
66
|
+
valid: bool
|
|
67
|
+
verdict: Optional[str]
|
|
68
|
+
action_hash_match: bool
|
|
69
|
+
counts: bool
|
|
70
|
+
reason: str = ""
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass
|
|
74
|
+
class ConsensusResult:
|
|
75
|
+
action_hash: str
|
|
76
|
+
threshold: int
|
|
77
|
+
n: int
|
|
78
|
+
consensus_count: int
|
|
79
|
+
decision: str
|
|
80
|
+
checks: list = field(default_factory=list)
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def khipu_consensus(self) -> str:
|
|
84
|
+
return f"{self.consensus_count}-of-{self.n}"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def sign_verdict(organ: str, action_hash: str, verdict: str, private_key_pem: str,
|
|
88
|
+
reason: str = "", lean_sha: str = "", ts: str = "") -> dict:
|
|
89
|
+
"""Produce a DSSE-signed organ verdict dict (the wire shape)."""
|
|
90
|
+
from cryptography.hazmat.primitives.serialization import load_pem_private_key
|
|
91
|
+
from cryptography.hazmat.primitives.asymmetric import ec
|
|
92
|
+
from cryptography.hazmat.primitives import hashes
|
|
93
|
+
from datetime import datetime, timezone
|
|
94
|
+
|
|
95
|
+
keyid = f"{organ}-cosign"
|
|
96
|
+
statement = {
|
|
97
|
+
"schema": "szl.khipu.organ_verdict/v1", "organ": organ, "keyid": keyid,
|
|
98
|
+
"action_hash": action_hash, "verdict": verdict, "reason": reason,
|
|
99
|
+
"lean_sha": lean_sha, "ts": ts or datetime.now(timezone.utc).isoformat(),
|
|
100
|
+
}
|
|
101
|
+
body = canonical_json(statement)
|
|
102
|
+
priv = load_pem_private_key(private_key_pem.encode(), password=None)
|
|
103
|
+
sig = priv.sign(pae(ORGAN_VERDICT_PAYLOAD_TYPE, body), ec.ECDSA(hashes.SHA256()))
|
|
104
|
+
return {
|
|
105
|
+
"organ": organ, "keyid": keyid, "payloadType": ORGAN_VERDICT_PAYLOAD_TYPE,
|
|
106
|
+
"payload": base64.b64encode(body).decode(), "signature": base64.b64encode(sig).decode(),
|
|
107
|
+
"verdict": verdict, "reason": reason,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def verify_verdict(v: OrganVerdict, public_key_pem: str, action_hash: str) -> OrganCheck:
|
|
112
|
+
"""Verify one organ's signature against its public key, and check that the
|
|
113
|
+
signed action_hash and verdict are internally consistent."""
|
|
114
|
+
from cryptography.hazmat.primitives.serialization import load_pem_public_key
|
|
115
|
+
from cryptography.hazmat.primitives.asymmetric import ec
|
|
116
|
+
from cryptography.hazmat.primitives import hashes
|
|
117
|
+
from cryptography.exceptions import InvalidSignature
|
|
118
|
+
|
|
119
|
+
if not v.payload_b64 or not v.signature_b64:
|
|
120
|
+
return OrganCheck(v.organ, v.keyid, False, None, False, False, "missing payload/signature")
|
|
121
|
+
# A valid signature from the same key in a different DSSE domain is not a
|
|
122
|
+
# Khipu witness verdict. Bind both the protocol and expected wire identity.
|
|
123
|
+
if v.payload_type != ORGAN_VERDICT_PAYLOAD_TYPE:
|
|
124
|
+
return OrganCheck(v.organ, v.keyid, False, None, False, False, "payload-type-mismatch")
|
|
125
|
+
expected_keyid = f"{v.organ}-cosign"
|
|
126
|
+
if not v.organ or v.keyid != expected_keyid:
|
|
127
|
+
return OrganCheck(v.organ, v.keyid, False, None, False, False, "witness-identity-mismatch")
|
|
128
|
+
try:
|
|
129
|
+
body = base64.b64decode(v.payload_b64)
|
|
130
|
+
to_verify = pae(v.payload_type, body)
|
|
131
|
+
pub = load_pem_public_key(public_key_pem.encode())
|
|
132
|
+
try:
|
|
133
|
+
pub.verify(base64.b64decode(v.signature_b64), to_verify, ec.ECDSA(hashes.SHA256()))
|
|
134
|
+
except InvalidSignature:
|
|
135
|
+
return OrganCheck(v.organ, v.keyid, False, None, False, False, "signature mismatch")
|
|
136
|
+
decoded = json.loads(body)
|
|
137
|
+
if not isinstance(decoded, dict) or decoded.get("schema") != "szl.khipu.organ_verdict/v1":
|
|
138
|
+
return OrganCheck(v.organ, v.keyid, False, None, False, False, "statement-schema-mismatch")
|
|
139
|
+
if decoded.get("organ") != v.organ or decoded.get("keyid") != expected_keyid:
|
|
140
|
+
return OrganCheck(v.organ, v.keyid, False, None, False, False, "signed-witness-identity-mismatch")
|
|
141
|
+
ah_match = decoded.get("action_hash") == action_hash
|
|
142
|
+
verdict = decoded.get("verdict")
|
|
143
|
+
counts = ah_match and verdict == "allow"
|
|
144
|
+
return OrganCheck(v.organ, v.keyid, True, verdict, ah_match, counts)
|
|
145
|
+
except Exception as e:
|
|
146
|
+
return OrganCheck(v.organ, v.keyid, False, None, False, False, f"{type(e).__name__}: {e}")
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _valid_quorum_counts(threshold: int, n: int) -> bool:
|
|
150
|
+
return type(threshold) is int and type(n) is int and 1 <= threshold <= n
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def tally(action_hash: str, verdicts: list, pubkeys: dict,
|
|
154
|
+
threshold: int = 3, n: int = 4) -> ConsensusResult:
|
|
155
|
+
"""Count valid + allow signatures over `action_hash`; apply the BFT threshold.
|
|
156
|
+
|
|
157
|
+
Each witness ("organ") counts AT MOST ONCE: consensus is a property of
|
|
158
|
+
DISTINCT witnesses, not of raw verdict rows. A second valid `allow` from an
|
|
159
|
+
organ that already counted is recorded (``counts=False``, reason
|
|
160
|
+
"duplicate-witness (already counted)") but does NOT increment the tally, so
|
|
161
|
+
resubmitting one genuine verdict cannot inflate ``consensus_count`` past the
|
|
162
|
+
honest distinct-witness threshold. Mirrors typescript/ and go/ and the
|
|
163
|
+
distinctness the Lean ``validCount`` model assumes.
|
|
164
|
+
|
|
165
|
+
verdicts: list of dict|OrganVerdict|None (None = abstain/timeout).
|
|
166
|
+
pubkeys : {organ: PEM}.
|
|
167
|
+
Invalid quorum counts reject before verification: n and threshold must be
|
|
168
|
+
positive integers (not booleans), with threshold <= n. They are not coerced.
|
|
169
|
+
"""
|
|
170
|
+
if not _valid_quorum_counts(threshold, n):
|
|
171
|
+
return ConsensusResult(action_hash, threshold, n, 0, "rejected", [])
|
|
172
|
+
checks = []
|
|
173
|
+
counted: set = set()
|
|
174
|
+
count = 0
|
|
175
|
+
for item in verdicts:
|
|
176
|
+
if item is None:
|
|
177
|
+
checks.append(OrganCheck(None, None, False, None, False, False, "abstain/timeout"))
|
|
178
|
+
continue
|
|
179
|
+
v = item if isinstance(item, OrganVerdict) else OrganVerdict.from_dict(item)
|
|
180
|
+
pem = pubkeys.get(v.organ, "")
|
|
181
|
+
if not pem:
|
|
182
|
+
checks.append(OrganCheck(v.organ, v.keyid, False, None, False, False, "no public key"))
|
|
183
|
+
continue
|
|
184
|
+
chk = verify_verdict(v, pem, action_hash)
|
|
185
|
+
if chk.counts and v.organ in counted:
|
|
186
|
+
checks.append(OrganCheck(
|
|
187
|
+
v.organ, v.keyid, chk.valid, chk.verdict, chk.action_hash_match,
|
|
188
|
+
False, "duplicate-witness (already counted)",
|
|
189
|
+
))
|
|
190
|
+
continue
|
|
191
|
+
if chk.counts:
|
|
192
|
+
counted.add(v.organ)
|
|
193
|
+
count += 1
|
|
194
|
+
checks.append(chk)
|
|
195
|
+
decision = "canonical" if count >= threshold else "rejected"
|
|
196
|
+
return ConsensusResult(action_hash, threshold, n, count, decision, checks)
|