-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwitness_quorum.py
More file actions
59 lines (47 loc) · 2.33 KB
/
Copy pathwitness_quorum.py
File metadata and controls
59 lines (47 loc) · 2.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
"""
orchestrator/witness_quorum.py — Witness Quorum Gate (T4 Sybil Defense) for Phase 1.2.
This module implements the witness independence validation gate to prevent Sybil attacks.
A quorum requires:
1. At least 2 independent witnesses (by observer_id)
2. At least 2 distinct network provenances (by observer_provenance), compared
via subnet-aware buckets so multiple identities on the same /24 or /64 do
not count as independent origins.
Spec reference: D4 § T4 (Sybil Witnesses) + D1 § 3 (witness_multiplier) + G1
"""
from orchestrator.membership import PeerObservation
from orchestrator.provenance import provenance_bucket
def validate_witness_quorum(observation: PeerObservation) -> bool:
"""
Validate that an observation has sufficient independent witness quorum.
Quorum gate: ≥2 independent witnesses required for observation to count as consensus.
Independence = distinct observer_id AND distinct network provenance bucket.
Args:
observation: PeerObservation with witness_set to validate
Returns:
True if quorum is satisfied (≥2 distinct observers with ≥2 distinct
provenance buckets)
False if quorum fails (insufficient witnesses or all from same
observer/provenance bucket)
Sybil defense:
- observer_id dedup prevents a single observer claiming multiple identities
- observer_provenance is bucketed by subnet (/24 for IPv4, /64 for IPv6)
before deduplication, so multiple identities on the same subnet cannot
spoof independent provenance strings
- Both gates must pass for quorum to be satisfied
"""
# Minimum witness requirement: need at least 2 witnesses
if len(observation.witness_set) < 2:
return False
# Dedup by observer_id — need at least 2 distinct observers
unique_observers = set(w.observer_id for w in observation.witness_set)
if len(unique_observers) < 2:
return False
# Dedup by provenance bucket — need at least 2 distinct network origins.
# Subnet-aware bucketing prevents same-subnet Sybils with distinct
# observer_provenance strings from passing as independent witnesses.
unique_provenances = set(
provenance_bucket(w.observer_provenance) for w in observation.witness_set
)
if len(unique_provenances) < 2:
return False
return True