19 KiB
19 KiB
Use Case 4: Sybil-Resistant Identity Networks
The Identity Problem in Web3
Decentralized systems face a fundamental challenge: how to establish identity and reputation without central authorities while preventing Sybil attacks.
Current Identity System Failures
-
Centralized Identity Providers
- Single points of failure (KYC providers, social logins)
- Privacy violations through data aggregation
- Censorship and deplatforming risks
- Geographic and political exclusion
-
Token-Based Sybil Resistance
- Plutocratic (wealthy users have more influence)
- Doesn't represent real human relationships
- Vulnerable to borrowing/renting attacks
- Excludes users without capital
-
Proof of Personhood Ceremonies
- Require synchronous participation
- Exclude users in certain timezones
- Technical barriers for non-technical users
- Still vulnerable to sophisticated attacks
The BFT-CRDT Identity Solution
A web of trust that grows organically through attestations, resistant to Sybil attacks through social graph analysis:
Traditional Identity:
[Central Authority] → [Identity Verification] → [Single Identity Record]
↓ ↓ ↓
[Can be hacked] [Privacy violation] [Can be revoked]
BFT-CRDT Identity Network:
[User A] ←→ [User B]
↓ × ↓
[User C] ←→ [User D]
↓
[Attestations merge via CRDT]
↓
[Emergent trust graph]
Key Innovation: Trust Without Consensus
- No global agreement on who is "verified"
- Each participant maintains their own trust graph
- Applications interpret the graph based on their needs
- Sybil resistance emerges from graph topology
Architecture
Core Data Structures
pub struct IdentityCRDT {
// All attestations in the network
attestations: Map<AttestationId, Attestation>,
// Revocations (tombstones)
revocations: Set<AttestationId>,
// Computed trust paths
trust_cache: TrustCache,
// Anti-Sybil metrics
sybil_scores: Map<IdentityId, SybilScore>,
}
pub struct Attestation {
pub id: AttestationId,
pub issuer: IdentityId,
pub subject: IdentityId,
pub claim_type: ClaimType,
pub claim_value: ClaimValue,
pub confidence: u8, // 0-100
pub context: AttestationContext,
pub timestamp: Timestamp,
pub expiry: Option<Timestamp>,
pub signature: Signature,
}
pub enum ClaimType {
// Social attestations
KnowsPersonally,
MetInPerson,
WorkedWith,
FamilyMember,
// Skill attestations
TechnicalSkill(String),
ProfessionalRole(String),
EducationCredential(String),
// Behavior attestations
TrustedTrader,
ReliableCounterparty,
GoodCitizen,
// Verification attestations
VerifiedEmail(Hash),
VerifiedPhone(Hash),
VerifiedAddress(Hash),
BiometricHash(Hash),
}
pub struct AttestationContext {
// Where/how the attestation was made
pub location: Option<Location>,
pub event: Option<String>,
pub proof_of_interaction: Option<InteractionProof>,
pub metadata: Map<String, String>,
}
Trust Computation
impl IdentityCRDT {
// Calculate trust between two identities
pub fn calculate_trust(
&self,
source: IdentityId,
target: IdentityId,
claim_type: ClaimType,
params: TrustParams,
) -> TrustScore {
// Find all paths from source to target
let paths = self.find_trust_paths(source, target, params.max_depth);
if paths.is_empty() {
return TrustScore::Unknown;
}
// Weight paths by:
// - Length (shorter = better)
// - Attestation confidence
// - Recency
// - Path diversity
let weighted_scores: Vec<f64> = paths
.iter()
.map(|path| self.score_path(path, claim_type, params))
.collect();
// Aggregate using params.aggregation_method
let final_score = match params.aggregation_method {
AggregationMethod::Maximum => weighted_scores.max(),
AggregationMethod::Average => weighted_scores.average(),
AggregationMethod::Median => weighted_scores.median(),
AggregationMethod::WeightedByDiversity => {
self.diversity_weighted_aggregate(paths, weighted_scores)
}
};
TrustScore {
value: final_score,
confidence: self.calculate_confidence(paths.len(), weighted_scores.variance()),
paths_found: paths.len(),
computation_time: timestamp(),
}
}
fn score_path(
&self,
path: &TrustPath,
claim_type: ClaimType,
params: &TrustParams,
) -> f64 {
let mut score = 1.0;
for edge in &path.edges {
let attestation = &self.attestations[&edge.attestation_id];
// Confidence factor
score *= (attestation.confidence as f64) / 100.0;
// Recency factor
let age = timestamp() - attestation.timestamp;
score *= params.recency_decay.decay_factor(age);
// Claim type relevance
score *= params.claim_relevance(attestation.claim_type, claim_type);
// Penalize long paths
score *= params.path_length_penalty.pow(path.edges.len());
}
score
}
}
Sybil Resistance Mechanisms
pub struct SybilDetector {
// Network topology analysis
pub min_clustering_coefficient: f64,
pub max_betweenness_centrality: f64,
pub min_attestation_diversity: f64,
// Temporal analysis
pub min_account_age: Duration,
pub max_attestation_rate: f64,
// Behavioral analysis
pub interaction_requirements: InteractionRequirements,
}
impl SybilDetector {
pub fn analyze_identity(&self, id: IdentityId, graph: &IdentityCRDT) -> SybilScore {
let mut score = SybilScore::default();
// 1. Graph topology checks
score.clustering = self.check_clustering(id, graph);
score.centrality = self.check_centrality(id, graph);
// 2. Attestation pattern checks
score.attestation_diversity = self.check_attestation_diversity(id, graph);
score.temporal_distribution = self.check_temporal_patterns(id, graph);
// 3. Interaction proof checks
score.interaction_quality = self.check_interactions(id, graph);
// 4. Economic cost analysis
score.attack_cost = self.estimate_attack_cost(id, graph);
score
}
fn check_clustering(&self, id: IdentityId, graph: &IdentityCRDT) -> f64 {
// Real social networks have high clustering
// Sybil networks tend to be tree-like
let neighbors = graph.get_neighbors(id);
let interconnections = graph.count_edges_between(neighbors);
let possible_connections = neighbors.len() * (neighbors.len() - 1) / 2;
interconnections as f64 / possible_connections as f64
}
}
Use Cases
1. Decentralized KYC/AML
pub struct DecentralizedKYC {
required_attestations: Vec<RequiredAttestation>,
trust_threshold: f64,
approved_issuers: Option<Set<IdentityId>>,
}
pub struct RequiredAttestation {
claim_types: Vec<ClaimType>,
min_confidence: u8,
max_age: Duration,
min_paths: usize,
}
impl DecentralizedKYC {
pub fn verify_identity(
&self,
identity: IdentityId,
graph: &IdentityCRDT,
verifier: IdentityId,
) -> KYCResult {
let mut results = Vec::new();
for requirement in &self.required_attestations {
let attestations = graph.find_attestations(
identity,
&requirement.claim_types,
verifier,
);
let valid_attestations = attestations
.filter(|a| a.confidence >= requirement.min_confidence)
.filter(|a| a.age() <= requirement.max_age)
.filter(|a| self.is_approved_issuer(a.issuer))
.collect::<Vec<_>>();
results.push(RequirementResult {
requirement: requirement.clone(),
found: valid_attestations.len(),
required: requirement.min_paths,
passed: valid_attestations.len() >= requirement.min_paths,
});
}
KYCResult {
identity,
passed: results.iter().all(|r| r.passed),
details: results,
timestamp: timestamp(),
}
}
}
2. Reputation-Based Governance
pub struct ReputationGovernance {
// Different reputation types have different weights
reputation_weights: Map<ClaimType, f64>,
// Minimum reputation for participation
participation_threshold: f64,
// How reputation translates to voting power
power_curve: PowerCurve,
}
impl ReputationGovernance {
pub fn calculate_voting_power(
&self,
voter: IdentityId,
graph: &IdentityCRDT,
context: &GovernanceContext,
) -> VotingPower {
let mut weighted_reputation = 0.0;
// Aggregate different types of reputation
for (claim_type, weight) in &self.reputation_weights {
let reputation = graph.calculate_reputation(
voter,
claim_type.clone(),
&context.reputation_params,
);
weighted_reputation += reputation.value * weight;
}
// Check participation threshold
if weighted_reputation < self.participation_threshold {
return VotingPower::Ineligible;
}
// Apply power curve (e.g., quadratic)
let power = self.power_curve.apply(weighted_reputation);
VotingPower::Eligible {
power,
reputation_score: weighted_reputation,
calculation_method: self.power_curve.description(),
}
}
}
3. Social Recovery
pub struct SocialRecovery {
pub identity: IdentityId,
pub recovery_threshold: usize,
pub guardians: Vec<Guardian>,
pub time_delay: Duration,
}
pub struct Guardian {
pub identity: IdentityId,
pub relationship: ClaimType,
pub min_relationship_age: Duration,
pub weight: u32,
}
impl SocialRecovery {
pub fn initiate_recovery(
&self,
new_key: PublicKey,
guardian_signatures: Vec<GuardianSignature>,
graph: &IdentityCRDT,
) -> Result<RecoveryRequest> {
// Verify guardians
let mut total_weight = 0u32;
let mut verified_guardians = Vec::new();
for sig in guardian_signatures {
// Check guardian is valid
let guardian = self.guardians
.iter()
.find(|g| g.identity == sig.guardian)
.ok_or("Unknown guardian")?;
// Verify relationship still exists
let relationship = graph.verify_relationship(
self.identity,
guardian.identity,
guardian.relationship.clone(),
)?;
// Check relationship age
if relationship.age() < guardian.min_relationship_age {
return Err("Relationship too new");
}
// Verify signature
sig.verify(&new_key)?;
total_weight += guardian.weight;
verified_guardians.push(sig.guardian);
}
// Check threshold
if verified_guardians.len() < self.recovery_threshold {
return Err("Insufficient guardians");
}
Ok(RecoveryRequest {
identity: self.identity,
new_key,
guardians: verified_guardians,
initiated_at: timestamp(),
executable_at: timestamp() + self.time_delay,
})
}
}
4. Skill-Based Matching
pub struct SkillMarketplace {
pub skill_graph: IdentityCRDT,
pub matching_params: MatchingParams,
}
impl SkillMarketplace {
pub fn find_providers(
&self,
seeker: IdentityId,
required_skills: Vec<Skill>,
preferences: MatchingPreferences,
) -> Vec<SkillMatch> {
let mut candidates = Vec::new();
// Find all identities with required skills
for skill in &required_skills {
let providers = self.skill_graph.find_by_claim(
ClaimType::TechnicalSkill(skill.name.clone()),
preferences.min_confidence,
);
for provider in providers {
// Calculate trust path from seeker
let trust = self.skill_graph.calculate_trust(
seeker,
provider.identity,
ClaimType::TechnicalSkill(skill.name.clone()),
preferences.trust_params.clone(),
);
// Check if meets minimum trust
if trust.value >= preferences.min_trust {
candidates.push(SkillMatch {
provider: provider.identity,
skill: skill.clone(),
trust_score: trust.value,
attestations: provider.attestations,
estimated_rate: self.estimate_rate(&provider, skill),
});
}
}
}
// Sort by preference
candidates.sort_by(|a, b| {
preferences.ranking_function(a, b)
});
candidates
}
}
Privacy Features
1. Selective Disclosure
pub struct PrivateAttestation {
// Public part
pub id: AttestationId,
pub issuer: IdentityId,
pub subject_commitment: Commitment,
pub claim_type: ClaimType,
pub timestamp: Timestamp,
// Private part (revealed selectively)
pub private_data: EncryptedData,
pub reveal_key: Option<RevealKey>,
}
impl PrivateAttestation {
pub fn reveal_to(&self, recipient: IdentityId) -> RevealToken {
// Generate reveal token for specific recipient
let token = RevealToken {
attestation_id: self.id,
recipient,
expiry: timestamp() + Duration::hours(24),
scope: RevealScope::FullClaim,
};
token.encrypt_for(recipient)
}
}
2. Zero-Knowledge Proofs
pub struct ZKIdentityClaim {
// Prove properties without revealing identity
pub proof: ZKProof,
pub public_inputs: PublicInputs,
pub nullifier: Nullifier, // Prevent double-usage
}
impl IdentityCRDT {
pub fn prove_reputation_threshold(
&self,
identity: IdentityId,
threshold: f64,
claim_type: ClaimType,
) -> ZKIdentityClaim {
// Generate proof that reputation > threshold
// without revealing actual reputation or identity
let witness = self.gather_reputation_witness(identity, claim_type);
let proof = generate_zk_proof(witness, threshold);
ZKIdentityClaim {
proof,
public_inputs: PublicInputs { threshold, claim_type },
nullifier: derive_nullifier(identity, timestamp()),
}
}
}
Implementation Guide
Starting an Identity Network
// 1. Initialize identity
const identity = new Identity({
publicKey: await generateKeypair(),
profile: {
displayName: "Alice",
avatar: "ipfs://...",
},
});
// 2. Create initial attestations
await identity.attestTo({
subject: "bob.eth",
claimType: "KnowsPersonally",
confidence: 95,
context: {
event: "ETH Denver 2024",
proof: interactionProof,
},
});
// 3. Join identity network
const network = new IdentityNetwork({
bootstrapPeers: ["peer1", "peer2"],
storage: new IPFSStorage(),
});
await network.publishIdentity(identity);
await network.syncAttestations();
Building Trust Relationships
// Organic trust building
async function buildTrust() {
// 1. Attend events and meet people
const eventAttestations = await collectEventAttestations("ETH Denver");
// 2. Work on projects together
const projectAttestations = await collaborateOnProject({
project: "DeFi Protocol",
teammates: ["carol.eth", "dave.eth"],
duration: "3 months",
});
// 3. Trade/interact on-chain
const onChainAttestations = await generateFromOnChain({
interactions: getOnChainInteractions(),
threshold: 5, // minimum interactions
});
// 4. Publish attestations
await network.publishBatch([
...eventAttestations,
...projectAttestations,
...onChainAttestations,
]);
}
Consuming Identity Data
// For applications
class IdentityConsumer {
constructor(
private network: IdentityNetwork,
private requirements: TrustRequirements,
) {}
async verifyUser(userId: string): Promise<VerificationResult> {
// 1. Calculate trust from your perspective
const trust = await this.network.calculateTrust(
this.identity,
userId,
this.requirements.claimTypes,
);
// 2. Check Sybil resistance
const sybilScore = await this.network.getSybilScore(userId);
// 3. Verify specific claims if needed
const claims = await this.network.getAttestations(
userId,
this.requirements.requiredClaims,
);
return {
trusted: trust.value > this.requirements.minTrust,
sybilRisk: sybilScore.risk,
verifiedClaims: claims,
};
}
}
Conclusion
BFT-CRDT identity networks solve the fundamental paradox of decentralized identity:
- No Central Authority: Trust emerges from the network
- Sybil Resistant: Graph analysis detects fake identities
- Privacy Preserving: Selective disclosure and ZK proofs
- Contextual: Different apps interpret trust differently
- Organic Growth: Builds on natural human relationships
This enables:
- Truly decentralized social networks
- Reputation-based lending without credit scores
- Skills marketplaces without centralized platforms
- Democratic governance beyond token-voting
- Social recovery that actually works
The key insight: Identity is not a binary state but a graph of relationships that can be interpreted contextually while remaining resistant to attacks.