nx_commons_sybilcost.nx source
↩ module page · 73 lines · 4113 B
1// nx_commons_sybilcost.nx -- what does an attack actually COST? The sybil bound, measured.
2//
3// WHY THIS EXISTS: every layer built today claims sybil resistance, and a claim is not a bound.
4// "The ring collapses because it cannot manufacture witnesses" is an ASSERTION until someone states
5// the quantity an attacker must spend and shows it does not shrink as the attack grows.
6// ★ SYBIL-RESISTANT IS NOT A PROPERTY, IT IS A PRICE. Name the price or you have said nothing.
7//
8// KNOWN GOOD: the trust-flow bound (Advogato's metric; SybilGuard / SybilLimit). Its theorem is the
9// one useful result in this area and it is counter-intuitive enough to be worth stating plainly:
10// an attacker's total influence is bounded by the number of ATTACK EDGES -- honest members who
11// vouch for a sybil -- and is INDEPENDENT of how many sybils the attacker creates.
12// Minting identities is free; being vouched for by someone honest is not. So the cost scales with
13// the only thing the attacker cannot manufacture: other people's genuine regard.
14//
15// NOTE ON SCOPE, because it is the honest half: there is no in-estate known good for proof-of-
16// personhood, and I looked -- nx_realperson is CONTENT SAFETY about real people (NCII, defamation,
17// right of publicity), not "is this account a human". I inferred otherwise from its NAME and was
18// wrong. Nor is there a good global answer: biometric uniqueness centralises and builds a honeypot,
19// government ID excludes the undocumented and is best-held by exactly the class we are excluding.
20// ★ BUT FOR A BOUNDED NEIGHBOUR COMMONS, PERSONHOOD IS THE EASY CASE, NOT THE HARD ONE -- neighbours
21// physically know each other, and the witness layer already built IS the mechanism. This organ does
22// not add a new check; it PRICES the one we have.
23//
24// Integer only. license_tier: ORIGINAL No hw writes (Rule 26).
25import "nx_syscalls.nx"
26
27const SC_PERMIL: i64 = 1000
28
29// ---- THE NAIVE RULE (negative control): standing counts vouches, whoever they come from ---------
30// This is what almost every reputation system does, and it is why almost every reputation system is
31// farmable: the attacker's return scales with identities minted, which cost nothing.
32func sc_standing_naive(n_sybils: i64, vouches_each: i64, unit: i64) -> i64 {
33 if n_sybils <= 0 { return 0 }
34 if vouches_each <= 0 { return 0 }
35 return n_sybils * vouches_each * unit
36}
37
38// ---- THE FLOW-BOUNDED RULE ---------------------------------------------------------------------
39// Standing entering the sybil region cannot exceed the total capacity of the edges CROSSING INTO it
40// from the honest region. Internal edges carry nothing across a cut they do not cross, so a ring
41// trading with itself moves exactly zero standing inward however dense it is.
42// caps[i] = capacity of attack edge i (the vouching honest member's own bounded capacity).
43func sc_standing_flowbound(caps: *i64, n_attack: i64) -> i64 {
44 if n_attack <= 0 { return 0 }
45 var total: i64 = 0
46 var i: i64 = 0
47 while i < n_attack {
48 if caps[i] > 0 { total = total + caps[i] }
49 i = i + 1
50 }
51 return total
52}
53
54// Per-identity share: the same fixed inflow divided among however many sybils exist. Minting more
55// identities DILUTES the attacker rather than multiplying it -- the property worth having.
56func sc_per_sybil(caps: *i64, n_attack: i64, n_sybils: i64) -> i64 {
57 if n_sybils <= 0 { return 0 }
58 return sc_standing_flowbound(caps, n_attack) / n_sybils
59}
60
61// The price: attack edges required per unit of standing obtained, in permil. Higher is safer.
62// An attacker must corrupt or earn this many honest vouches for each unit of standing it gains.
63func sc_cost_permil(n_attack: i64, standing: i64) -> i64 {
64 if standing <= 0 { return 0 }
65 return (n_attack * SC_PERMIL) / standing
66}
67
68// Does the honest region lose anything when the attack grows? It must not: a defence that degrades
69// honest members in order to bound an attacker has simply moved the damage.
70func sc_honest_unchanged(before: i64, after: i64) -> i64 {
71 if before == after { return 1 }
72 return 0
73}