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
tests/test_proof.py
DELETED
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Tests for PROOF primitive.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import pytest
|
|
6
|
-
import sys
|
|
7
|
-
import os
|
|
8
|
-
|
|
9
|
-
# Add parent to path
|
|
10
|
-
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
11
|
-
|
|
12
|
-
from proof import (
|
|
13
|
-
Proof, ProofType, Identity, Evidence, Signature,
|
|
14
|
-
Timestamp, Anchor, AnchorType, Consensus, ConsensusState,
|
|
15
|
-
SignatureAlgorithm
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class TestIdentity:
|
|
20
|
-
"""Test Identity generation and verification"""
|
|
21
|
-
|
|
22
|
-
def test_generate_agent_identity(self):
|
|
23
|
-
"""Test generating an agent identity"""
|
|
24
|
-
identity, private_key = Identity.generate("agent")
|
|
25
|
-
|
|
26
|
-
assert identity.did.startswith("did:pn:agent:")
|
|
27
|
-
assert len(identity.public_key) > 0
|
|
28
|
-
assert identity.algorithm == SignatureAlgorithm.DILITHIUM3
|
|
29
|
-
assert len(private_key) > 0
|
|
30
|
-
|
|
31
|
-
def test_generate_validator_identity(self):
|
|
32
|
-
"""Test generating a validator identity"""
|
|
33
|
-
identity, private_key = Identity.generate("validator")
|
|
34
|
-
|
|
35
|
-
assert identity.did.startswith("did:pn:validator:")
|
|
36
|
-
assert len(identity.public_key) > 0
|
|
37
|
-
|
|
38
|
-
def test_identity_uniqueness(self):
|
|
39
|
-
"""Test that each identity is unique"""
|
|
40
|
-
id1, _ = Identity.generate("agent")
|
|
41
|
-
id2, _ = Identity.generate("agent")
|
|
42
|
-
|
|
43
|
-
assert id1.did != id2.did
|
|
44
|
-
assert id1.public_key != id2.public_key
|
|
45
|
-
|
|
46
|
-
def test_generate_hybrid_identity(self):
|
|
47
|
-
"""Test generating a hybrid ML-DSA + Ed25519 identity (GPT-5.2 audit recommendation)"""
|
|
48
|
-
identity, private_key = Identity.generate("agent", SignatureAlgorithm.HYBRID_ML_DSA_ED25519)
|
|
49
|
-
|
|
50
|
-
assert identity.did.startswith("did:pn:agent:")
|
|
51
|
-
assert identity.algorithm == SignatureAlgorithm.HYBRID_ML_DSA_ED25519
|
|
52
|
-
# Hybrid public key = Dilithium (1952) + Ed25519 (32)
|
|
53
|
-
assert len(identity.public_key) == 1984
|
|
54
|
-
# Hybrid private key = Dilithium (~4000) + Ed25519 (32)
|
|
55
|
-
assert len(private_key) == 4032
|
|
56
|
-
|
|
57
|
-
def test_generate_ed25519_identity(self):
|
|
58
|
-
"""Test generating an Ed25519 identity (transitional)"""
|
|
59
|
-
identity, private_key = Identity.generate("agent", SignatureAlgorithm.ED25519)
|
|
60
|
-
|
|
61
|
-
assert identity.did.startswith("did:pn:agent:")
|
|
62
|
-
assert identity.algorithm == SignatureAlgorithm.ED25519
|
|
63
|
-
assert len(identity.public_key) == 32 # Ed25519 public key size
|
|
64
|
-
assert len(private_key) == 32 # Ed25519 private key size
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
class TestEvidence:
|
|
68
|
-
"""Test Evidence creation"""
|
|
69
|
-
|
|
70
|
-
def test_create_from_data(self):
|
|
71
|
-
"""Test creating evidence from raw data"""
|
|
72
|
-
data = b"Test evidence data"
|
|
73
|
-
evidence = Evidence.from_data(data)
|
|
74
|
-
|
|
75
|
-
assert evidence.content_hash is not None
|
|
76
|
-
assert "shake256:" in evidence.content_hash # Uses SHAKE256 prefix
|
|
77
|
-
assert evidence.content_size == len(data)
|
|
78
|
-
|
|
79
|
-
def test_embedded_data(self):
|
|
80
|
-
"""Test evidence with embedded data"""
|
|
81
|
-
data = b"Embedded content"
|
|
82
|
-
evidence = Evidence.from_data(data)
|
|
83
|
-
|
|
84
|
-
assert evidence.embedded == data
|
|
85
|
-
assert evidence.content_type == "application/octet-stream"
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
class TestProof:
|
|
89
|
-
"""Test PROOF creation and verification"""
|
|
90
|
-
|
|
91
|
-
def test_create_claim_proof(self):
|
|
92
|
-
"""Test creating a basic CLAIM proof"""
|
|
93
|
-
issuer, private_key = Identity.generate("agent")
|
|
94
|
-
|
|
95
|
-
proof = Proof.create(
|
|
96
|
-
proof_type=ProofType.CLAIM,
|
|
97
|
-
claim="Test claim",
|
|
98
|
-
issuer=issuer,
|
|
99
|
-
private_key=private_key,
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
assert proof.proof_id.startswith("proof:")
|
|
103
|
-
assert proof.claim == "Test claim"
|
|
104
|
-
assert proof.issuer.did == issuer.did
|
|
105
|
-
assert proof.signature is not None
|
|
106
|
-
assert proof.verify() is True
|
|
107
|
-
|
|
108
|
-
def test_create_work_proof(self):
|
|
109
|
-
"""Test creating a WORK proof"""
|
|
110
|
-
issuer, private_key = Identity.generate("agent")
|
|
111
|
-
|
|
112
|
-
proof = Proof.work(
|
|
113
|
-
issuer=issuer,
|
|
114
|
-
private_key=private_key,
|
|
115
|
-
description="Completed task",
|
|
116
|
-
result=b"Task output",
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
assert proof.proof_type == ProofType.WORK
|
|
120
|
-
assert proof.evidence is not None
|
|
121
|
-
assert proof.verify() is True
|
|
122
|
-
|
|
123
|
-
def test_create_identity_proof(self):
|
|
124
|
-
"""Test creating an IDENTITY proof"""
|
|
125
|
-
issuer, private_key = Identity.generate("agent")
|
|
126
|
-
|
|
127
|
-
proof = Proof.identity(
|
|
128
|
-
issuer=issuer,
|
|
129
|
-
private_key=private_key,
|
|
130
|
-
name="Test Agent",
|
|
131
|
-
role="tester",
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
assert proof.proof_type == ProofType.IDENTITY
|
|
135
|
-
assert proof.subject is not None # Subject is issuer for identity proof
|
|
136
|
-
assert proof.verify() is True
|
|
137
|
-
|
|
138
|
-
def test_proof_hash_stability(self):
|
|
139
|
-
"""Test that proof hash is stable"""
|
|
140
|
-
issuer, private_key = Identity.generate("agent")
|
|
141
|
-
|
|
142
|
-
proof = Proof.create(
|
|
143
|
-
proof_type=ProofType.CLAIM,
|
|
144
|
-
claim="Stable claim",
|
|
145
|
-
issuer=issuer,
|
|
146
|
-
private_key=private_key,
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
hash1 = proof.compute_hash()
|
|
150
|
-
hash2 = proof.compute_hash()
|
|
151
|
-
|
|
152
|
-
assert hash1 == hash2
|
|
153
|
-
assert hash1 == proof.proof_id
|
|
154
|
-
|
|
155
|
-
def test_proof_chain(self):
|
|
156
|
-
"""Test proof chaining"""
|
|
157
|
-
issuer, private_key = Identity.generate("agent")
|
|
158
|
-
|
|
159
|
-
proof1 = Proof.create(
|
|
160
|
-
proof_type=ProofType.CLAIM,
|
|
161
|
-
claim="First claim",
|
|
162
|
-
issuer=issuer,
|
|
163
|
-
private_key=private_key,
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
proof2 = Proof.create(
|
|
167
|
-
proof_type=ProofType.CLAIM,
|
|
168
|
-
claim="Second claim",
|
|
169
|
-
issuer=issuer,
|
|
170
|
-
private_key=private_key,
|
|
171
|
-
previous_proof=proof1.proof_id,
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
assert proof2.previous_proof == proof1.proof_id
|
|
175
|
-
|
|
176
|
-
def test_proof_to_dict(self):
|
|
177
|
-
"""Test proof serialization to dict"""
|
|
178
|
-
issuer, private_key = Identity.generate("agent")
|
|
179
|
-
|
|
180
|
-
proof = Proof.create(
|
|
181
|
-
proof_type=ProofType.CLAIM,
|
|
182
|
-
claim="Serialization test",
|
|
183
|
-
issuer=issuer,
|
|
184
|
-
private_key=private_key,
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
d = proof.to_dict()
|
|
188
|
-
|
|
189
|
-
assert "proof_id" in d
|
|
190
|
-
assert "claim" in d
|
|
191
|
-
assert "proof_type" in d
|
|
192
|
-
assert "issuer" in d
|
|
193
|
-
assert "timestamp" in d
|
|
194
|
-
|
|
195
|
-
def test_hybrid_signature_proof(self):
|
|
196
|
-
"""Test PROOF with hybrid ML-DSA + Ed25519 signature (GPT-5.2 audit recommendation)"""
|
|
197
|
-
issuer, private_key = Identity.generate("agent", SignatureAlgorithm.HYBRID_ML_DSA_ED25519)
|
|
198
|
-
|
|
199
|
-
proof = Proof.create(
|
|
200
|
-
proof_type=ProofType.CLAIM,
|
|
201
|
-
claim="Hybrid signature test",
|
|
202
|
-
issuer=issuer,
|
|
203
|
-
private_key=private_key,
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
assert proof.signature.algorithm == SignatureAlgorithm.HYBRID_ML_DSA_ED25519
|
|
207
|
-
# Verify MÕLEMAD allkirjad töötavad
|
|
208
|
-
assert proof.verify() is True
|
|
209
|
-
# Hybrid signature = Dilithium (~3293) + Ed25519 (64)
|
|
210
|
-
assert len(proof.signature.value) > 3000
|
|
211
|
-
|
|
212
|
-
def test_hybrid_signature_tamper_resistance(self):
|
|
213
|
-
"""Test that tampering with hybrid-signed PROOF is detected"""
|
|
214
|
-
issuer, private_key = Identity.generate("agent", SignatureAlgorithm.HYBRID_ML_DSA_ED25519)
|
|
215
|
-
|
|
216
|
-
proof = Proof.create(
|
|
217
|
-
proof_type=ProofType.CLAIM,
|
|
218
|
-
claim="Original claim",
|
|
219
|
-
issuer=issuer,
|
|
220
|
-
private_key=private_key,
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
# Create tampered proof with different claim
|
|
224
|
-
tampered = Proof(
|
|
225
|
-
proof_id=proof.proof_id,
|
|
226
|
-
proof_type=proof.proof_type,
|
|
227
|
-
claim="TAMPERED claim", # Modified!
|
|
228
|
-
issuer=proof.issuer,
|
|
229
|
-
signature=proof.signature,
|
|
230
|
-
timestamp=proof.timestamp,
|
|
231
|
-
)
|
|
232
|
-
|
|
233
|
-
# Original should verify
|
|
234
|
-
assert proof.verify() is True
|
|
235
|
-
# Tampered should fail (hash won't match)
|
|
236
|
-
assert tampered.verify() is False
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
class TestTimestamp:
|
|
240
|
-
"""Test Timestamp with nonce (replay protection)"""
|
|
241
|
-
|
|
242
|
-
def test_timestamp_has_nonce(self):
|
|
243
|
-
"""Test that timestamp includes nonce for replay protection"""
|
|
244
|
-
ts = Timestamp.now()
|
|
245
|
-
|
|
246
|
-
assert ts.unix_time > 0
|
|
247
|
-
assert ts.nonce is not None
|
|
248
|
-
assert len(ts.nonce) > 0
|
|
249
|
-
|
|
250
|
-
def test_timestamp_uniqueness(self):
|
|
251
|
-
"""Test that timestamps are unique even at same time"""
|
|
252
|
-
ts1 = Timestamp.now()
|
|
253
|
-
ts2 = Timestamp.now()
|
|
254
|
-
|
|
255
|
-
# Nonces should be different
|
|
256
|
-
assert ts1.nonce != ts2.nonce
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
class TestConsensus:
|
|
260
|
-
"""Test Consensus state"""
|
|
261
|
-
|
|
262
|
-
def test_consensus_state(self):
|
|
263
|
-
"""Test consensus state creation"""
|
|
264
|
-
consensus = Consensus(
|
|
265
|
-
state=ConsensusState.PENDING,
|
|
266
|
-
quorum_threshold=0.67
|
|
267
|
-
)
|
|
268
|
-
|
|
269
|
-
assert consensus.state == ConsensusState.PENDING
|
|
270
|
-
assert consensus.quorum_threshold == 0.67
|
|
271
|
-
|
|
272
|
-
def test_consensus_accepted_state(self):
|
|
273
|
-
"""Test accepted consensus state"""
|
|
274
|
-
consensus = Consensus(state=ConsensusState.ACCEPTED)
|
|
275
|
-
assert consensus.state == ConsensusState.ACCEPTED
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
if __name__ == "__main__":
|
|
279
|
-
pytest.main([__file__, "-v"])
|
tests/test_staking.py
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Tests for Staking & Sybil Resistance (GPT-5.2 audit)
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import pytest
|
|
6
|
-
import sys
|
|
7
|
-
import os
|
|
8
|
-
from decimal import Decimal
|
|
9
|
-
|
|
10
|
-
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
11
|
-
|
|
12
|
-
from staking import (
|
|
13
|
-
StakingRegistry,
|
|
14
|
-
ValidatorStake,
|
|
15
|
-
StakeRecord,
|
|
16
|
-
StakeType,
|
|
17
|
-
SlashReason,
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class TestValidatorStake:
|
|
22
|
-
"""Test validator stake operations"""
|
|
23
|
-
|
|
24
|
-
def test_create_validator(self):
|
|
25
|
-
"""Test creating a validator with initial stake"""
|
|
26
|
-
validator = ValidatorStake(validator_did="did:pn:validator:test")
|
|
27
|
-
validator.add_reputation(100)
|
|
28
|
-
|
|
29
|
-
assert validator.validator_did == "did:pn:validator:test"
|
|
30
|
-
assert validator.reputation_score == 100
|
|
31
|
-
assert validator.total_stake == Decimal("100")
|
|
32
|
-
|
|
33
|
-
def test_voting_power_calculation(self):
|
|
34
|
-
"""Test that voting power uses diminishing returns"""
|
|
35
|
-
v1 = ValidatorStake(validator_did="did:pn:validator:small")
|
|
36
|
-
v1.add_reputation(100)
|
|
37
|
-
|
|
38
|
-
v2 = ValidatorStake(validator_did="did:pn:validator:large")
|
|
39
|
-
v2.add_reputation(100)
|
|
40
|
-
# Add large economic stake
|
|
41
|
-
v2.stakes.append(StakeRecord(
|
|
42
|
-
stake_id="stake:1",
|
|
43
|
-
validator_did="did:pn:validator:large",
|
|
44
|
-
stake_type=StakeType.ESCROW,
|
|
45
|
-
amount=Decimal("10000"),
|
|
46
|
-
currency="USD",
|
|
47
|
-
))
|
|
48
|
-
|
|
49
|
-
# Large validator should have more power, but not 100x more
|
|
50
|
-
ratio = float(v2.voting_power) / float(v1.voting_power)
|
|
51
|
-
assert ratio > 1.5 # Larger should have more power
|
|
52
|
-
assert ratio < 100 # But not proportionally more (diminishing returns)
|
|
53
|
-
|
|
54
|
-
def test_slash_reduces_stake(self):
|
|
55
|
-
"""Test that slashing reduces effective stake"""
|
|
56
|
-
validator = ValidatorStake(validator_did="did:pn:validator:slashme")
|
|
57
|
-
validator.stakes.append(StakeRecord(
|
|
58
|
-
stake_id="stake:1",
|
|
59
|
-
validator_did="did:pn:validator:slashme",
|
|
60
|
-
stake_type=StakeType.ESCROW,
|
|
61
|
-
amount=Decimal("1000"),
|
|
62
|
-
currency="USD",
|
|
63
|
-
))
|
|
64
|
-
|
|
65
|
-
initial_stake = validator.total_stake
|
|
66
|
-
validator.slash(SlashReason.DOUBLE_SIGN, Decimal("0.5"))
|
|
67
|
-
|
|
68
|
-
assert validator.total_stake < initial_stake
|
|
69
|
-
assert validator.total_stake == Decimal("500")
|
|
70
|
-
|
|
71
|
-
def test_jail_prevents_validation(self):
|
|
72
|
-
"""Test that jailed validators cannot participate"""
|
|
73
|
-
validator = ValidatorStake(validator_did="did:pn:validator:jailed")
|
|
74
|
-
validator.add_reputation(500)
|
|
75
|
-
|
|
76
|
-
# Not jailed initially
|
|
77
|
-
assert validator.is_jailed is False
|
|
78
|
-
|
|
79
|
-
# Jail for 1 hour
|
|
80
|
-
validator.jail(3600)
|
|
81
|
-
assert validator.is_jailed is True
|
|
82
|
-
|
|
83
|
-
# Cannot unjail yet (time hasn't passed)
|
|
84
|
-
assert validator.unjail() is False
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
class TestStakingRegistry:
|
|
88
|
-
"""Test staking registry operations"""
|
|
89
|
-
|
|
90
|
-
def test_register_validator(self):
|
|
91
|
-
"""Test registering a new validator"""
|
|
92
|
-
registry = StakingRegistry()
|
|
93
|
-
validator = registry.register_validator("did:pn:validator:new")
|
|
94
|
-
|
|
95
|
-
assert validator is not None
|
|
96
|
-
assert validator.validator_did == "did:pn:validator:new"
|
|
97
|
-
|
|
98
|
-
def test_minimum_stake_requirement(self):
|
|
99
|
-
"""Test that minimum stake is required to validate"""
|
|
100
|
-
registry = StakingRegistry()
|
|
101
|
-
|
|
102
|
-
# Validator with no stake
|
|
103
|
-
registry.register_validator("did:pn:validator:poor")
|
|
104
|
-
assert registry.can_validate("did:pn:validator:poor") is False
|
|
105
|
-
|
|
106
|
-
# Validator with enough stake
|
|
107
|
-
v = registry.register_validator("did:pn:validator:rich")
|
|
108
|
-
v.add_reputation(200)
|
|
109
|
-
assert registry.can_validate("did:pn:validator:rich") is True
|
|
110
|
-
|
|
111
|
-
def test_reward_increases_reputation(self):
|
|
112
|
-
"""Test that validation rewards increase reputation"""
|
|
113
|
-
registry = StakingRegistry()
|
|
114
|
-
v = registry.register_validator("did:pn:validator:worker")
|
|
115
|
-
v.add_reputation(100) # Initial stake
|
|
116
|
-
|
|
117
|
-
initial_rep = v.reputation_score
|
|
118
|
-
registry.reward_validation("did:pn:validator:worker", "proof:123", True)
|
|
119
|
-
|
|
120
|
-
assert v.reputation_score > initial_rep
|
|
121
|
-
|
|
122
|
-
def test_slash_for_misbehavior(self):
|
|
123
|
-
"""Test slashing for various violations"""
|
|
124
|
-
registry = StakingRegistry()
|
|
125
|
-
v = registry.register_validator("did:pn:validator:bad")
|
|
126
|
-
v.stakes.append(StakeRecord(
|
|
127
|
-
stake_id="stake:1",
|
|
128
|
-
validator_did="did:pn:validator:bad",
|
|
129
|
-
stake_type=StakeType.ESCROW,
|
|
130
|
-
amount=Decimal("1000"),
|
|
131
|
-
currency="USD",
|
|
132
|
-
))
|
|
133
|
-
|
|
134
|
-
event = registry.slash_validator("did:pn:validator:bad", SlashReason.DOUBLE_SIGN)
|
|
135
|
-
|
|
136
|
-
assert event is not None
|
|
137
|
-
assert event.reason == SlashReason.DOUBLE_SIGN
|
|
138
|
-
assert v.is_jailed is True
|
|
139
|
-
|
|
140
|
-
def test_get_active_validators(self):
|
|
141
|
-
"""Test getting only active validators"""
|
|
142
|
-
registry = StakingRegistry()
|
|
143
|
-
|
|
144
|
-
# Active validator
|
|
145
|
-
v1 = registry.register_validator("did:pn:validator:active")
|
|
146
|
-
v1.add_reputation(200)
|
|
147
|
-
|
|
148
|
-
# Jailed validator
|
|
149
|
-
v2 = registry.register_validator("did:pn:validator:jailed")
|
|
150
|
-
v2.add_reputation(200)
|
|
151
|
-
v2.jail(3600)
|
|
152
|
-
|
|
153
|
-
active = registry.get_active_validators()
|
|
154
|
-
assert len(active) == 1
|
|
155
|
-
assert active[0].validator_did == "did:pn:validator:active"
|
|
156
|
-
|
|
157
|
-
def test_voting_power_distribution(self):
|
|
158
|
-
"""Test getting voting power for all active validators"""
|
|
159
|
-
registry = StakingRegistry()
|
|
160
|
-
|
|
161
|
-
v1 = registry.register_validator("did:pn:validator:a")
|
|
162
|
-
v1.add_reputation(300)
|
|
163
|
-
|
|
164
|
-
v2 = registry.register_validator("did:pn:validator:b")
|
|
165
|
-
v2.add_reputation(150)
|
|
166
|
-
|
|
167
|
-
powers = registry.get_voting_power_distribution()
|
|
168
|
-
|
|
169
|
-
assert len(powers) == 2
|
|
170
|
-
assert powers["did:pn:validator:a"] > powers["did:pn:validator:b"]
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if __name__ == "__main__":
|
|
174
|
-
pytest.main([__file__, "-v"])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|