heimdall-stake: sybil note and golden vectors
Some checks failed
CultMesh QUIC native / Build win32-x64 (pull_request) Has been skipped
CultMesh QUIC native / Build linux-x64 (pull_request) Has been skipped
CultMesh QUIC native / Release native binaries (pull_request) Has been skipped
CultMesh portability and performance / ubuntu-latest one-command multiplayer onboarding (pull_request) Has been cancelled
CultMesh portability and performance / windows-latest one-command multiplayer onboarding (pull_request) Has been cancelled
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Has been cancelled
CultMesh portability and performance / windows-latest reactive semantics and leases (pull_request) Has been cancelled
CultNet cross-runtime interop / C# / TypeScript / Rust / Python / Kotlin (pull_request) Has been cancelled
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (pull_request) Has been cancelled
Some checks failed
CultMesh QUIC native / Build win32-x64 (pull_request) Has been skipped
CultMesh QUIC native / Build linux-x64 (pull_request) Has been skipped
CultMesh QUIC native / Release native binaries (pull_request) Has been skipped
CultMesh portability and performance / ubuntu-latest one-command multiplayer onboarding (pull_request) Has been cancelled
CultMesh portability and performance / windows-latest one-command multiplayer onboarding (pull_request) Has been cancelled
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Has been cancelled
CultMesh portability and performance / windows-latest reactive semantics and leases (pull_request) Has been cancelled
CultNet cross-runtime interop / C# / TypeScript / Rust / Python / Kotlin (pull_request) Has been cancelled
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (pull_request) Has been cancelled
README: the constant 1 in weight = 1 + log_b(amount/unit) is paid once per identity, so splitting always pays until the log goes negative and no base fixes it; the defence lives outside the function. Golden vectors (single and split k=1,2,10,100, three bases) computed with 60-digit decimal ln, pinned by tests/golden.rs. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
9adee90499
commit
6f68341edd
3 changed files with 70 additions and 0 deletions
|
|
@ -63,6 +63,17 @@ as a cliff, not a slope: with a 50 EUR minimum, 100 EUR split in two still gains
|
|||
in the example), and split in three weighs 0. So the minimum has to sit near the size of a typical
|
||||
budget to deter splitting, and the report is how to see where it does.
|
||||
|
||||
## Why the sybil defence cannot live here
|
||||
|
||||
`weight = 1 + log_b(amount / unit)` pays the constant 1 once per identity. Splitting a budget into
|
||||
k identities pays that 1 k times and costs only the logarithm's shrinkage, so splitting wins until
|
||||
the log term goes negative: with unit 1 EUR and b = 10, 100 EUR weighs 3 as one identity, 5.40 as
|
||||
two, 20 as ten and 100 as one hundred (`tests/vectors/stake_golden.csv`, pinned by
|
||||
`tests/golden.rs`). No choice of `b` fixes this: `b` scales the log term and never the constant.
|
||||
A cap or a per-identity minimum only moves the cliff (see Splitting gain). So the defence belongs
|
||||
outside the function: non-transferable or vesting stake, a per-person cap on total stake, and a
|
||||
cost to create an identity. This crate states the incentive; it cannot remove it.
|
||||
|
||||
## Where this sits in the estate rule
|
||||
|
||||
This crate does not normalize. The estate rule normalizes weights above it; the weights here are
|
||||
|
|
|
|||
30
packages/heimdall-stake/tests/golden.rs
Normal file
30
packages/heimdall-stake/tests/golden.rs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
use heimdall_stake::*;
|
||||
|
||||
const VECTORS: &str = include_str!("vectors/stake_golden.csv");
|
||||
|
||||
/// Pins `weight` and `total_weight` to values computed outside this crate (see the fixture header).
|
||||
/// Split rows are the sybil argument in numbers: the same budget in k identities weighs more.
|
||||
#[test]
|
||||
fn golden_vectors() {
|
||||
let mut rows = 0;
|
||||
let mut split_rows = 0;
|
||||
for line in VECTORS.lines().filter(|l| !l.starts_with('#') && !l.trim().is_empty()) {
|
||||
let f: Vec<&str> = line.split(',').collect();
|
||||
let (base, unit, amount, k, expected): (u32, u64, i64, usize, u64) =
|
||||
(f[1].parse().unwrap(), f[2].parse().unwrap(), f[3].parse().unwrap(), f[4].parse().unwrap(), f[5].parse().unwrap());
|
||||
let p = Params { base, unit_cents: unit, floor_cents: 0, per_identity_cap: None, min_amount_per_identity_cents: 0 };
|
||||
let got = match f[0] {
|
||||
"single" => weight(&p, amount).unwrap(),
|
||||
"split" => {
|
||||
split_rows += 1;
|
||||
let (q, r) = (amount / k as i64, (amount % k as i64) as usize);
|
||||
let shares: Vec<i64> = (0..k).map(|i| q + i64::from(i < r)).collect();
|
||||
total_weight(&p, &shares).unwrap()
|
||||
}
|
||||
other => panic!("unknown vector kind {other}"),
|
||||
};
|
||||
assert_eq!(got, expected, "vector `{line}`");
|
||||
rows += 1;
|
||||
}
|
||||
assert!(rows >= 20 && split_rows >= 12, "fixture shrank: {rows} rows, {split_rows} split");
|
||||
}
|
||||
29
packages/heimdall-stake/tests/vectors/stake_golden.csv
Normal file
29
packages/heimdall-stake/tests/vectors/stake_golden.csv
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# kind,base,unit_cents,amount_cents,k,expected_micro
|
||||
# single: weight of one identity holding amount. split: budget (amount) split evenly over k identities (first amount%k hold one extra cent), summed.
|
||||
# expected values computed independently with 60-digit decimal ln, rounded half up. No floor, cap or minimum.
|
||||
single,10,100,100,1,1000000
|
||||
single,10,100,1000,1,2000000
|
||||
single,10,100,10000,1,3000000
|
||||
single,10,100,1000000,1,5000000
|
||||
single,10,100,50,1,698970
|
||||
single,10,100,10,1,0
|
||||
single,10,100,1,1,0
|
||||
single,2,100,400,1,3000000
|
||||
single,2,100,100,1,1000000
|
||||
single,2,1,1,1,1000000
|
||||
single,3,500,123456,1,6014537
|
||||
single,7,1,987654321,1,11643268
|
||||
single,10,1,1,1,1000000
|
||||
single,16,250,99999,1,3160960
|
||||
split,10,100,10000,1,3000000
|
||||
split,10,100,10000,2,5397940
|
||||
split,10,100,10000,10,20000000
|
||||
split,10,100,10000,100,100000000
|
||||
split,2,100,10000,1,7643856
|
||||
split,2,100,10000,2,13287712
|
||||
split,2,100,10000,10,43219280
|
||||
split,2,100,10000,100,100000000
|
||||
split,10,100,100000,1,4000000
|
||||
split,10,100,100000,2,7397940
|
||||
split,10,100,100000,10,30000000
|
||||
split,10,100,100000,100,200000000
|
||||
|
Loading…
Reference in a new issue