proofnest 0.2.0__py3-none-any.whl → 0.2.1__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.
- PKG-INFO +1 -1
- __init__.py +1 -1
- {proofnest-0.2.0.dist-info → proofnest-0.2.1.dist-info}/METADATA +1 -1
- proofnest-0.2.1.dist-info/RECORD +21 -0
- pyproject.toml +18 -1
- .planning/PROJECT.md +0 -99
- .planning/ROADMAP.md +0 -152
- .planning/STATE.md +0 -91
- .planning/config.json +0 -18
- .planning/quick/001-artikkel-proofnest-vs-moltbook/001-PLAN.md +0 -190
- .planning/quick/001-artikkel-proofnest-vs-moltbook/001-SUMMARY.md +0 -85
- .planning/quick/002-twitter-thread-proofnest-moltbook/002-PLAN.md +0 -53
- .planning/quick/002-twitter-thread-proofnest-moltbook/002-SUMMARY.md +0 -58
- .planning/research/ARCHITECTURE.md +0 -585
- .planning/research/FEATURES.md +0 -192
- .planning/research/PITFALLS.md +0 -487
- .planning/research/STACK.md +0 -378
- .planning/research/SUMMARY.md +0 -385
- AUDIT_GPT4o_2026-02-03.md +0 -65
- AUDIT_GPT52_2026-02-03.md +0 -137
- AUDIT_GPT52_FULL_2026-02-03.md +0 -116
- AUDIT_GPT52_ROUND2_2026-02-03.md +0 -79
- README.md +0 -132
- content/articles/proofnest-vs-moltbook-en.md +0 -165
- content/social/twitter-thread-001.md +0 -168
- proofnest-0.2.0.dist-info/RECORD +0 -45
- tests/test_api.py +0 -270
- tests/test_did_registry.py +0 -206
- tests/test_proof.py +0 -279
- tests/test_staking.py +0 -174
- {proofnest-0.2.0.dist-info → proofnest-0.2.1.dist-info}/WHEEL +0 -0
- {proofnest-0.2.0.dist-info → proofnest-0.2.1.dist-info}/entry_points.txt +0 -0
- {proofnest-0.2.0.dist-info → proofnest-0.2.1.dist-info}/licenses/LICENSE +0 -0
proofnest-0.2.0.dist-info/RECORD
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
./.gitignore,sha256=jTP1a-5WZxB6VIjQDxnlgi21WAsvcRKZHqgzK6RoCaE,481
|
|
2
|
-
./AUDIT_GPT4o_2026-02-03.md,sha256=SIb0DTL8fgY8nDlFGCreUeBLsp9qNeAntls_dW2ImxY,2100
|
|
3
|
-
./AUDIT_GPT52_2026-02-03.md,sha256=sWC871lqSWdRYQdRK9Tp1uVKn2tbvr2V3hmb9GpT4Vk,4527
|
|
4
|
-
./AUDIT_GPT52_FULL_2026-02-03.md,sha256=gRG63eJLbUehRHzm76diJoWzgBufMSP9gx-BzhWyhQw,3192
|
|
5
|
-
./AUDIT_GPT52_ROUND2_2026-02-03.md,sha256=PWjsbcFCUmljk6aKQ4qFYgxJHcaJKqTDNXSRKr3BGKM,2037
|
|
6
|
-
./LICENSE,sha256=kJZdHD45cIKBd-xdBBGmxgesVMsUGFYKfPOQlHAFOZw,1071
|
|
7
|
-
./PKG-INFO,sha256=HKa30HTILzRbUbM8ELCsqZIbtsEkWB2ga3Ny2wofLBg,5926
|
|
8
|
-
./README.md,sha256=slBddYIxAp7i-s0HA3QKQ5XvJ5YTqVy637_0CF6aO44,4277
|
|
9
|
-
./__init__.py,sha256=wkkzIxUYFN30mixtvNSX66wU7iW7bp7pngRcI7Q-YY4,2760
|
|
10
|
-
./anchor.py,sha256=FRLy2nzDq0HJ9rT7J1GgOLcYynFY0ZhC-4i9RiuinmA,12160
|
|
11
|
-
./api.py,sha256=J6IFtQPb_kixwRp4H_CwaAQoxEx-IZMmd04PU9qjATM,24901
|
|
12
|
-
./blockchain.py,sha256=AoJXr8SeF5O7mCFg-DdpNvt0Io_Oi3hjowiMAPOLs1Q,12319
|
|
13
|
-
./cli.py,sha256=XfQktB8e329cPGxu5qE11IVmY16PkkwjEcrpcsuPWgI,12497
|
|
14
|
-
./consensus.py,sha256=U5UZy-bJRUQfk8U4e2vp1AzzTk-PrlzMsOnuEcGjQjo,13929
|
|
15
|
-
./demo.py,sha256=6SAO6PIwQ6kOaS4a8vKe7RXMEnYrC0qaWxRQzUmmHJQ,4567
|
|
16
|
-
./did_registry.py,sha256=9DXoTYfkUfuwp46QuzHNVpGZ2It_4Gits64SZ_REaqE,13404
|
|
17
|
-
./network.py,sha256=4Wd1_-cgfvo47oiop-SCc662BsiFQLQZ_ELPPgEdZ6U,20992
|
|
18
|
-
./proof.py,sha256=wRf5c5uEE3ALajCc_XSiVWdunoB26ARxr-hTqgrjeDM,37800
|
|
19
|
-
./pyproject.toml,sha256=8rQlJr1iSmix1pXTFp6JUhT1Ry0rW6sb2wSJ223L_QY,2041
|
|
20
|
-
./staking.py,sha256=T7iOdrWHCfvTocmeLejnHZ3SOVPgXEFJGkwmJX-ZcR0,16150
|
|
21
|
-
./storage.py,sha256=bUKHybjnDpIIqHNkbItRBlQrrRZIfTGVRIeAvYAePaQ,19836
|
|
22
|
-
./.planning/PROJECT.md,sha256=kLc52MDDFlCoBk4Gz7V2nFhPcRAyO1dhcmf2DVJhICU,4274
|
|
23
|
-
./.planning/ROADMAP.md,sha256=XcxnUeEd8V9nFmxsZzOzU3iiSQR6m4-vbOPlFgipV8Y,4867
|
|
24
|
-
./.planning/STATE.md,sha256=1rnqSvmyO0bwdyV1pvdf3La1z2NNDN_IwQ9FXTwifiQ,2184
|
|
25
|
-
./.planning/config.json,sha256=CjZFWDey7cSl02ASBg8gVhs6a7CqOLwQmYlncmMFXX8,446
|
|
26
|
-
./.planning/quick/001-artikkel-proofnest-vs-moltbook/001-PLAN.md,sha256=nqakgoBat0nCHvT0qoNbJFyroa4nRXHXRzc2r0knBsw,6623
|
|
27
|
-
./.planning/quick/001-artikkel-proofnest-vs-moltbook/001-SUMMARY.md,sha256=d8VE1rarKhOUiCoUg_a6n-Lm3C5LhpXerAAqG2ixVQU,2431
|
|
28
|
-
./.planning/quick/002-twitter-thread-proofnest-moltbook/002-PLAN.md,sha256=EXb7-TWgDu474iziCaBv9hKQ7hCsReGtUB1CK-JQsso,1524
|
|
29
|
-
./.planning/quick/002-twitter-thread-proofnest-moltbook/002-SUMMARY.md,sha256=24ACIVpluXv_n_MPhA4rJDkYx_1ZlmOdPLADobVDZD8,1572
|
|
30
|
-
./.planning/research/ARCHITECTURE.md,sha256=qnbZwYtaY8uhJF3mbGmRXLiNNP1WRhgpOz1pXBJ4gig,20215
|
|
31
|
-
./.planning/research/FEATURES.md,sha256=UFcq3ED4P_lJfvL2eKM8856NGXxiqL_C6KOQE9RJp1g,10727
|
|
32
|
-
./.planning/research/PITFALLS.md,sha256=6va5ieYb_RbH4c3PDAId7jVE9_AsXD9VXxKNzGz8HjI,19900
|
|
33
|
-
./.planning/research/STACK.md,sha256=Y0YbrWRDCPXTk5kXmOLMN-hdrVlXEnsLHggwJopWENI,13833
|
|
34
|
-
./.planning/research/SUMMARY.md,sha256=B6bEweIRqUDTx3r0pNrIhLWHBOhE5AfPXwsyDIe5ZpU,21686
|
|
35
|
-
./content/articles/proofnest-vs-moltbook-en.md,sha256=IV_7YwCKiqaXwrkF4mcAc04pvS_GiWu0bNAQlXrmsNc,10121
|
|
36
|
-
./content/social/twitter-thread-001.md,sha256=VYZ-4oZiF5aXsS0IiRz-3_0ZtPXSXVifvK_EXQApg1E,3228
|
|
37
|
-
./tests/test_api.py,sha256=67XoGcgqdV26KdBvqL67oAvGmHo1ZlSNLZt4SlQDrlQ,9141
|
|
38
|
-
./tests/test_did_registry.py,sha256=ehisZce4hu9QMur0_0mLC3uV0SVbSNHp2N4GDJAv8oE,6062
|
|
39
|
-
./tests/test_proof.py,sha256=2c12YZfmVTnKEemwRPCAbN5w6GpbrFeuwB5PRVddO-Y,8919
|
|
40
|
-
./tests/test_staking.py,sha256=MlcNnmZxkLNXUbibEq2DSKCZtVc_lllFDmKOEWkkZCQ,5852
|
|
41
|
-
proofnest-0.2.0.dist-info/METADATA,sha256=HKa30HTILzRbUbM8ELCsqZIbtsEkWB2ga3Ny2wofLBg,5926
|
|
42
|
-
proofnest-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
43
|
-
proofnest-0.2.0.dist-info/entry_points.txt,sha256=U2_jDn1Eg8j5Z3hQDks3CJ7qbe9auBsehyKT5RcGW-w,49
|
|
44
|
-
proofnest-0.2.0.dist-info/licenses/LICENSE,sha256=kJZdHD45cIKBd-xdBBGmxgesVMsUGFYKfPOQlHAFOZw,1071
|
|
45
|
-
proofnest-0.2.0.dist-info/RECORD,,
|
tests/test_api.py
DELETED
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Tests for PROOFNEST API.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import pytest
|
|
6
|
-
from fastapi.testclient import TestClient
|
|
7
|
-
import sys
|
|
8
|
-
import os
|
|
9
|
-
|
|
10
|
-
# Add parent to path
|
|
11
|
-
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
12
|
-
|
|
13
|
-
from api import app, API_KEY
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
@pytest.fixture
|
|
17
|
-
def client():
|
|
18
|
-
"""Create test client"""
|
|
19
|
-
return TestClient(app)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
@pytest.fixture
|
|
23
|
-
def auth_headers():
|
|
24
|
-
"""Authentication headers for protected endpoints"""
|
|
25
|
-
return {"X-API-Key": API_KEY}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class TestHealth:
|
|
29
|
-
"""Test health endpoints"""
|
|
30
|
-
|
|
31
|
-
def test_health(self, client):
|
|
32
|
-
"""Test health endpoint"""
|
|
33
|
-
response = client.get("/health")
|
|
34
|
-
assert response.status_code == 200
|
|
35
|
-
data = response.json()
|
|
36
|
-
assert data["status"] == "ok"
|
|
37
|
-
|
|
38
|
-
def test_root(self, client):
|
|
39
|
-
"""Test root endpoint"""
|
|
40
|
-
response = client.get("/")
|
|
41
|
-
assert response.status_code == 200
|
|
42
|
-
data = response.json()
|
|
43
|
-
assert "ProofNest" in data["name"]
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
class TestIdentities:
|
|
47
|
-
"""Test identity endpoints"""
|
|
48
|
-
|
|
49
|
-
def test_create_identity(self, client, auth_headers):
|
|
50
|
-
"""Test creating an identity"""
|
|
51
|
-
response = client.post("/v1/identities", json={
|
|
52
|
-
"identity_type": "agent",
|
|
53
|
-
"name": "Test Agent"
|
|
54
|
-
}, headers=auth_headers)
|
|
55
|
-
assert response.status_code == 200
|
|
56
|
-
data = response.json()
|
|
57
|
-
assert data["did"].startswith("did:pn:agent:")
|
|
58
|
-
assert data["name"] == "Test Agent"
|
|
59
|
-
|
|
60
|
-
def test_list_identities(self, client, auth_headers):
|
|
61
|
-
"""Test listing identities"""
|
|
62
|
-
# Create one first
|
|
63
|
-
client.post("/v1/identities", json={"identity_type": "agent"}, headers=auth_headers)
|
|
64
|
-
|
|
65
|
-
response = client.get("/v1/identities")
|
|
66
|
-
assert response.status_code == 200
|
|
67
|
-
data = response.json()
|
|
68
|
-
assert "count" in data
|
|
69
|
-
assert "identities" in data
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
class TestProofs:
|
|
73
|
-
"""Test proof endpoints"""
|
|
74
|
-
|
|
75
|
-
def test_create_proof(self, client, auth_headers):
|
|
76
|
-
"""Test creating a proof"""
|
|
77
|
-
# First create identity
|
|
78
|
-
identity = client.post("/v1/identities", json={
|
|
79
|
-
"identity_type": "agent"
|
|
80
|
-
}, headers=auth_headers).json()
|
|
81
|
-
|
|
82
|
-
# Then create proof
|
|
83
|
-
response = client.post("/v1/proofs", json={
|
|
84
|
-
"issuer_did": identity["did"],
|
|
85
|
-
"claim": "Test claim",
|
|
86
|
-
"proof_type": "CLAIM"
|
|
87
|
-
}, headers=auth_headers)
|
|
88
|
-
assert response.status_code == 200
|
|
89
|
-
data = response.json()
|
|
90
|
-
assert data["proof_id"].startswith("proof:")
|
|
91
|
-
assert data["is_valid"] is True
|
|
92
|
-
|
|
93
|
-
def test_get_proof(self, client, auth_headers):
|
|
94
|
-
"""Test getting a proof"""
|
|
95
|
-
# Create identity and proof
|
|
96
|
-
identity = client.post("/v1/identities", json={
|
|
97
|
-
"identity_type": "agent"
|
|
98
|
-
}, headers=auth_headers).json()
|
|
99
|
-
|
|
100
|
-
proof = client.post("/v1/proofs", json={
|
|
101
|
-
"issuer_did": identity["did"],
|
|
102
|
-
"claim": "Get test",
|
|
103
|
-
"proof_type": "CLAIM"
|
|
104
|
-
}, headers=auth_headers).json()
|
|
105
|
-
|
|
106
|
-
# Get it (public endpoint - no auth needed)
|
|
107
|
-
response = client.get(f"/v1/proofs/{proof['proof_id']}")
|
|
108
|
-
assert response.status_code == 200
|
|
109
|
-
|
|
110
|
-
def test_verify_proof(self, client, auth_headers):
|
|
111
|
-
"""Test verifying a proof"""
|
|
112
|
-
# Create identity and proof
|
|
113
|
-
identity = client.post("/v1/identities", json={
|
|
114
|
-
"identity_type": "agent"
|
|
115
|
-
}, headers=auth_headers).json()
|
|
116
|
-
|
|
117
|
-
proof = client.post("/v1/proofs", json={
|
|
118
|
-
"issuer_did": identity["did"],
|
|
119
|
-
"claim": "Verify test",
|
|
120
|
-
"proof_type": "CLAIM"
|
|
121
|
-
}, headers=auth_headers).json()
|
|
122
|
-
|
|
123
|
-
# Verify (public endpoint - no auth needed)
|
|
124
|
-
response = client.post(f"/v1/proofs/{proof['proof_id']}/verify")
|
|
125
|
-
assert response.status_code == 200
|
|
126
|
-
data = response.json()
|
|
127
|
-
assert data["is_valid"] is True
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
class TestValidators:
|
|
131
|
-
"""Test validator endpoints"""
|
|
132
|
-
|
|
133
|
-
def test_add_validator(self, client, auth_headers):
|
|
134
|
-
"""Test adding a validator"""
|
|
135
|
-
response = client.post("/v1/validators", headers=auth_headers)
|
|
136
|
-
assert response.status_code == 200
|
|
137
|
-
data = response.json()
|
|
138
|
-
assert data["did"].startswith("did:pn:validator:")
|
|
139
|
-
assert data["stake"] == 1000
|
|
140
|
-
|
|
141
|
-
def test_list_validators(self, client, auth_headers):
|
|
142
|
-
"""Test listing validators"""
|
|
143
|
-
# Add one first
|
|
144
|
-
client.post("/v1/validators", headers=auth_headers)
|
|
145
|
-
|
|
146
|
-
response = client.get("/v1/validators")
|
|
147
|
-
assert response.status_code == 200
|
|
148
|
-
data = response.json()
|
|
149
|
-
assert "count" in data
|
|
150
|
-
assert "validators" in data
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
class TestConsensus:
|
|
154
|
-
"""Test consensus flow"""
|
|
155
|
-
|
|
156
|
-
def test_full_consensus_flow(self, client, auth_headers):
|
|
157
|
-
"""Test complete consensus flow"""
|
|
158
|
-
# 1. Create identity
|
|
159
|
-
identity = client.post("/v1/identities", json={
|
|
160
|
-
"identity_type": "agent"
|
|
161
|
-
}, headers=auth_headers).json()
|
|
162
|
-
|
|
163
|
-
# 2. Create proof
|
|
164
|
-
proof = client.post("/v1/proofs", json={
|
|
165
|
-
"issuer_did": identity["did"],
|
|
166
|
-
"claim": "Consensus test",
|
|
167
|
-
"proof_type": "WORK"
|
|
168
|
-
}, headers=auth_headers).json()
|
|
169
|
-
proof_id = proof["proof_id"]
|
|
170
|
-
|
|
171
|
-
# 3. Add validators
|
|
172
|
-
v1 = client.post("/v1/validators", headers=auth_headers).json()
|
|
173
|
-
|
|
174
|
-
# 4. Submit to consensus
|
|
175
|
-
submit = client.post(f"/v1/proofs/{proof_id}/submit-consensus", headers=auth_headers)
|
|
176
|
-
assert submit.status_code == 200
|
|
177
|
-
|
|
178
|
-
# 5. Vote (single validator with quorum_threshold=1 for test)
|
|
179
|
-
vote1 = client.post(f"/v1/proofs/{proof_id}/vote", json={
|
|
180
|
-
"proof_id": proof_id,
|
|
181
|
-
"validator_did": v1["did"],
|
|
182
|
-
"approve": True
|
|
183
|
-
}, headers=auth_headers)
|
|
184
|
-
assert vote1.status_code == 200
|
|
185
|
-
# With 1 validator, quorum is reached immediately
|
|
186
|
-
vote_data = vote1.json()
|
|
187
|
-
assert vote_data["vote"] == "APPROVE"
|
|
188
|
-
# Quorum may be reached depending on threshold
|
|
189
|
-
if vote_data.get("quorum_reached"):
|
|
190
|
-
assert vote_data.get("consensus_state") in ["ACCEPTED", "FINALIZED"]
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
class TestChain:
|
|
194
|
-
"""Test blockchain endpoints"""
|
|
195
|
-
|
|
196
|
-
def test_chain_status(self, client):
|
|
197
|
-
"""Test chain status"""
|
|
198
|
-
response = client.get("/v1/chain/status")
|
|
199
|
-
assert response.status_code == 200
|
|
200
|
-
data = response.json()
|
|
201
|
-
assert "chain_id" in data
|
|
202
|
-
assert "height" in data
|
|
203
|
-
|
|
204
|
-
def test_chain_validators(self, client):
|
|
205
|
-
"""Test chain validators"""
|
|
206
|
-
response = client.get("/v1/chain/validators")
|
|
207
|
-
assert response.status_code == 200
|
|
208
|
-
data = response.json()
|
|
209
|
-
assert "validators" in data
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
class TestAuthentication:
|
|
213
|
-
"""Test API authentication (GPT-5.2 audit fix)"""
|
|
214
|
-
|
|
215
|
-
def test_unauthenticated_create_identity_rejected(self, client):
|
|
216
|
-
"""Verify unauthenticated identity creation is rejected"""
|
|
217
|
-
response = client.post("/v1/identities", json={
|
|
218
|
-
"identity_type": "agent"
|
|
219
|
-
})
|
|
220
|
-
assert response.status_code == 401
|
|
221
|
-
assert "Missing API key" in response.json()["detail"]
|
|
222
|
-
|
|
223
|
-
def test_unauthenticated_create_proof_rejected(self, client, auth_headers):
|
|
224
|
-
"""Verify unauthenticated proof creation is rejected"""
|
|
225
|
-
# First create identity (authenticated)
|
|
226
|
-
identity = client.post("/v1/identities", json={
|
|
227
|
-
"identity_type": "agent"
|
|
228
|
-
}, headers=auth_headers).json()
|
|
229
|
-
|
|
230
|
-
# Try to create proof without auth
|
|
231
|
-
response = client.post("/v1/proofs", json={
|
|
232
|
-
"issuer_did": identity["did"],
|
|
233
|
-
"claim": "Test",
|
|
234
|
-
"proof_type": "CLAIM"
|
|
235
|
-
})
|
|
236
|
-
assert response.status_code == 401
|
|
237
|
-
|
|
238
|
-
def test_invalid_api_key_rejected(self, client):
|
|
239
|
-
"""Verify invalid API key is rejected"""
|
|
240
|
-
response = client.post("/v1/identities", json={
|
|
241
|
-
"identity_type": "agent"
|
|
242
|
-
}, headers={"X-API-Key": "invalid-key"})
|
|
243
|
-
assert response.status_code == 403
|
|
244
|
-
assert "Invalid API key" in response.json()["detail"]
|
|
245
|
-
|
|
246
|
-
def test_public_endpoints_no_auth_needed(self, client, auth_headers):
|
|
247
|
-
"""Verify public endpoints work without authentication"""
|
|
248
|
-
# Create some data first (authenticated)
|
|
249
|
-
identity = client.post("/v1/identities", json={
|
|
250
|
-
"identity_type": "agent"
|
|
251
|
-
}, headers=auth_headers).json()
|
|
252
|
-
|
|
253
|
-
proof = client.post("/v1/proofs", json={
|
|
254
|
-
"issuer_did": identity["did"],
|
|
255
|
-
"claim": "Public test",
|
|
256
|
-
"proof_type": "CLAIM"
|
|
257
|
-
}, headers=auth_headers).json()
|
|
258
|
-
|
|
259
|
-
# These should work without auth
|
|
260
|
-
assert client.get("/").status_code == 200
|
|
261
|
-
assert client.get("/health").status_code == 200
|
|
262
|
-
assert client.get("/v1/identities").status_code == 200
|
|
263
|
-
assert client.get(f"/v1/identities/{identity['did']}").status_code == 200
|
|
264
|
-
assert client.get("/v1/proofs").status_code == 200
|
|
265
|
-
assert client.get(f"/v1/proofs/{proof['proof_id']}").status_code == 200
|
|
266
|
-
assert client.get("/v1/chain/status").status_code == 200
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if __name__ == "__main__":
|
|
270
|
-
pytest.main([__file__, "-v"])
|
tests/test_did_registry.py
DELETED
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Tests for DID Registry (GPT-5.2 audit - key rotation support)
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import pytest
|
|
6
|
-
import sys
|
|
7
|
-
import os
|
|
8
|
-
import secrets
|
|
9
|
-
|
|
10
|
-
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
11
|
-
|
|
12
|
-
from did_registry import (
|
|
13
|
-
DIDDocument,
|
|
14
|
-
DIDRegistry,
|
|
15
|
-
VerificationMethod,
|
|
16
|
-
KeyPurpose,
|
|
17
|
-
KeyStatus,
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class TestDIDDocument:
|
|
22
|
-
"""Test DID Document operations"""
|
|
23
|
-
|
|
24
|
-
def test_create_document(self):
|
|
25
|
-
"""Test creating a DID Document with initial key"""
|
|
26
|
-
pk = secrets.token_bytes(1952)
|
|
27
|
-
did = "did:pn:agent:test123"
|
|
28
|
-
|
|
29
|
-
doc = DIDDocument.create(did, pk)
|
|
30
|
-
|
|
31
|
-
assert doc.id == did
|
|
32
|
-
assert len(doc.verification_methods) == 1
|
|
33
|
-
assert doc.verification_methods[0].status == KeyStatus.ACTIVE
|
|
34
|
-
|
|
35
|
-
def test_create_with_recovery_key(self):
|
|
36
|
-
"""Test creating a DID Document with recovery key"""
|
|
37
|
-
pk = secrets.token_bytes(1952)
|
|
38
|
-
recovery_pk = secrets.token_bytes(1952)
|
|
39
|
-
did = "did:pn:agent:test456"
|
|
40
|
-
|
|
41
|
-
doc = DIDDocument.create(did, pk, recovery_public_key=recovery_pk)
|
|
42
|
-
|
|
43
|
-
assert len(doc.verification_methods) == 2
|
|
44
|
-
assert len(doc.get_recovery_keys()) == 1
|
|
45
|
-
|
|
46
|
-
def test_add_key(self):
|
|
47
|
-
"""Test adding a new key"""
|
|
48
|
-
pk1 = secrets.token_bytes(1952)
|
|
49
|
-
pk2 = secrets.token_bytes(1952)
|
|
50
|
-
did = "did:pn:agent:test789"
|
|
51
|
-
|
|
52
|
-
doc = DIDDocument.create(did, pk1)
|
|
53
|
-
initial_version = doc.version
|
|
54
|
-
|
|
55
|
-
doc.add_key(pk2, "Dilithium3VerificationKey2024", [KeyPurpose.ASSERTION])
|
|
56
|
-
|
|
57
|
-
assert len(doc.verification_methods) == 2
|
|
58
|
-
assert doc.version > initial_version
|
|
59
|
-
|
|
60
|
-
def test_key_rotation(self):
|
|
61
|
-
"""Test key rotation (revoke old, add new)"""
|
|
62
|
-
pk1 = secrets.token_bytes(1952)
|
|
63
|
-
pk2 = secrets.token_bytes(1952)
|
|
64
|
-
did = "did:pn:agent:rotation_test"
|
|
65
|
-
|
|
66
|
-
doc = DIDDocument.create(did, pk1)
|
|
67
|
-
old_key_id = doc.verification_methods[0].id
|
|
68
|
-
|
|
69
|
-
# Rotate
|
|
70
|
-
new_key = doc.rotate_key(old_key_id, pk2, "Dilithium3VerificationKey2024")
|
|
71
|
-
|
|
72
|
-
# Old key should be revoked
|
|
73
|
-
old_key = doc.get_key(old_key_id)
|
|
74
|
-
assert old_key.status == KeyStatus.REVOKED
|
|
75
|
-
|
|
76
|
-
# New key should be active
|
|
77
|
-
assert new_key.status == KeyStatus.ACTIVE
|
|
78
|
-
|
|
79
|
-
# Only one active auth key
|
|
80
|
-
assert len(doc.get_active_keys(KeyPurpose.AUTHENTICATION)) == 1
|
|
81
|
-
|
|
82
|
-
def test_revoke_key(self):
|
|
83
|
-
"""Test key revocation"""
|
|
84
|
-
pk = secrets.token_bytes(1952)
|
|
85
|
-
did = "did:pn:agent:revoke_test"
|
|
86
|
-
|
|
87
|
-
doc = DIDDocument.create(did, pk)
|
|
88
|
-
key_id = doc.verification_methods[0].id
|
|
89
|
-
|
|
90
|
-
result = doc.revoke_key(key_id)
|
|
91
|
-
|
|
92
|
-
assert result is True
|
|
93
|
-
assert doc.get_key(key_id).status == KeyStatus.REVOKED
|
|
94
|
-
|
|
95
|
-
def test_to_dict(self):
|
|
96
|
-
"""Test W3C DID Document serialization"""
|
|
97
|
-
pk = secrets.token_bytes(1952)
|
|
98
|
-
did = "did:pn:agent:serialize_test"
|
|
99
|
-
|
|
100
|
-
doc = DIDDocument.create(did, pk)
|
|
101
|
-
d = doc.to_dict()
|
|
102
|
-
|
|
103
|
-
assert "@context" in d
|
|
104
|
-
assert d["id"] == did
|
|
105
|
-
assert "verificationMethod" in d
|
|
106
|
-
assert "authentication" in d
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
class TestDIDRegistry:
|
|
110
|
-
"""Test DID Registry operations"""
|
|
111
|
-
|
|
112
|
-
def test_register_and_resolve(self):
|
|
113
|
-
"""Test registering and resolving a DID"""
|
|
114
|
-
registry = DIDRegistry()
|
|
115
|
-
pk = secrets.token_bytes(1952)
|
|
116
|
-
did = "did:pn:agent:registry_test"
|
|
117
|
-
|
|
118
|
-
doc = DIDDocument.create(did, pk)
|
|
119
|
-
result = registry.register(doc)
|
|
120
|
-
|
|
121
|
-
assert result is True
|
|
122
|
-
resolved = registry.resolve(did)
|
|
123
|
-
assert resolved is not None
|
|
124
|
-
assert resolved.id == did
|
|
125
|
-
|
|
126
|
-
def test_no_duplicate_registration(self):
|
|
127
|
-
"""Test that duplicate DIDs cannot be registered"""
|
|
128
|
-
registry = DIDRegistry()
|
|
129
|
-
pk = secrets.token_bytes(1952)
|
|
130
|
-
did = "did:pn:agent:duplicate_test"
|
|
131
|
-
|
|
132
|
-
doc = DIDDocument.create(did, pk)
|
|
133
|
-
registry.register(doc)
|
|
134
|
-
|
|
135
|
-
# Second registration should fail
|
|
136
|
-
result = registry.register(doc)
|
|
137
|
-
assert result is False
|
|
138
|
-
|
|
139
|
-
def test_update_document(self):
|
|
140
|
-
"""Test updating a DID Document"""
|
|
141
|
-
registry = DIDRegistry()
|
|
142
|
-
pk1 = secrets.token_bytes(1952)
|
|
143
|
-
pk2 = secrets.token_bytes(1952)
|
|
144
|
-
did = "did:pn:agent:update_test"
|
|
145
|
-
|
|
146
|
-
doc = DIDDocument.create(did, pk1)
|
|
147
|
-
registry.register(doc)
|
|
148
|
-
|
|
149
|
-
# Add key and update
|
|
150
|
-
doc.add_key(pk2, "Dilithium3VerificationKey2024", [KeyPurpose.ASSERTION])
|
|
151
|
-
result = registry.update(doc)
|
|
152
|
-
|
|
153
|
-
assert result is True
|
|
154
|
-
resolved = registry.resolve(did)
|
|
155
|
-
assert len(resolved.verification_methods) == 2
|
|
156
|
-
|
|
157
|
-
def test_deactivate(self):
|
|
158
|
-
"""Test DID deactivation"""
|
|
159
|
-
registry = DIDRegistry()
|
|
160
|
-
pk = secrets.token_bytes(1952)
|
|
161
|
-
did = "did:pn:agent:deactivate_test"
|
|
162
|
-
|
|
163
|
-
doc = DIDDocument.create(did, pk)
|
|
164
|
-
registry.register(doc)
|
|
165
|
-
|
|
166
|
-
result = registry.deactivate(did)
|
|
167
|
-
|
|
168
|
-
assert result is True
|
|
169
|
-
resolved = registry.resolve(did)
|
|
170
|
-
assert resolved.deactivated is True
|
|
171
|
-
|
|
172
|
-
def test_version_history(self):
|
|
173
|
-
"""Test DID Document version history"""
|
|
174
|
-
registry = DIDRegistry()
|
|
175
|
-
pk1 = secrets.token_bytes(1952)
|
|
176
|
-
pk2 = secrets.token_bytes(1952)
|
|
177
|
-
did = "did:pn:agent:history_test"
|
|
178
|
-
|
|
179
|
-
doc = DIDDocument.create(did, pk1)
|
|
180
|
-
registry.register(doc)
|
|
181
|
-
|
|
182
|
-
# Update
|
|
183
|
-
doc.add_key(pk2, "Dilithium3VerificationKey2024", [KeyPurpose.ASSERTION])
|
|
184
|
-
registry.update(doc)
|
|
185
|
-
|
|
186
|
-
history = registry.get_history(did)
|
|
187
|
-
assert len(history) == 2
|
|
188
|
-
|
|
189
|
-
def test_verify_controller(self):
|
|
190
|
-
"""Test controller verification"""
|
|
191
|
-
registry = DIDRegistry()
|
|
192
|
-
pk1 = secrets.token_bytes(1952)
|
|
193
|
-
pk2 = secrets.token_bytes(1952)
|
|
194
|
-
did = "did:pn:agent:verify_test"
|
|
195
|
-
|
|
196
|
-
doc = DIDDocument.create(did, pk1)
|
|
197
|
-
registry.register(doc)
|
|
198
|
-
|
|
199
|
-
# Valid controller
|
|
200
|
-
assert registry.verify_controller(did, pk1) is True
|
|
201
|
-
# Invalid controller
|
|
202
|
-
assert registry.verify_controller(did, pk2) is False
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if __name__ == "__main__":
|
|
206
|
-
pytest.main([__file__, "-v"])
|