soft3/tru/audit/allocation-curve.md

allocation curve — PoW/PoS split and the security floor

property #27 in cyber/launch.md (the phase-1 tracker). date 2026-09-21, revision 39e0caf (base master), rs/allocation.rs this branch.

what this closes

specs/rewards.md §10 fixes the split formula and the neutral prior:

$$R_{\text{PoW}} = B(1-\theta^\alpha), \quad R_{\text{PoS}} = B\theta^\alpha, \quad \alpha \in [0.3, 0.7], \quad \alpha = 0.5 \text{ neutral}$$

and the security floor, floor ≥ c_sec · (TVL/M) · r_atk. Neither had code. rs/allocation.rs implements both over Fx (fixed-point, no floats on the path — specs/arithmetic.md), computing θ^α as exp(α · ln θ) from the existing Fx::exp/Fx::ln. c_sec and r_atk stay function parameters: §10 states both follow PID control on observable signals, so this slice does not fix their numeric values — only the formula and its properties.

simulation

cargo run --example allocation_sim, same revision:

-- R_PoW / R_PoS split of a unit security budget B, by staking ratio θ --
α=0.3 (PoW-favoring bound):
  θ=  0%  R_PoW=100.00%  R_PoS=0.00%
  θ= 10%  R_PoW=49.88%   R_PoS=50.12%
  θ= 25%  R_PoW=34.02%   R_PoS=65.98%
  θ= 50%  R_PoW=18.77%   R_PoS=81.23%
  θ= 75%  R_PoW=8.27%    R_PoS=91.73%
  θ= 90%  R_PoW=3.11%    R_PoS=96.89%
  θ=100%  R_PoW=0.00%    R_PoS=100.00%
α=0.5 (neutral prior):
  θ=  0%  R_PoW=100.00%  R_PoS=0.00%
  θ= 10%  R_PoW=68.38%   R_PoS=31.62%
  θ= 25%  R_PoW=50.00%   R_PoS=50.00%
  θ= 50%  R_PoW=29.29%   R_PoS=70.71%
  θ= 75%  R_PoW=13.40%   R_PoS=86.60%
  θ= 90%  R_PoW=5.13%    R_PoS=94.87%
  θ=100%  R_PoW=0.00%    R_PoS=100.00%
α=0.7 (PoS-favoring bound):
  θ=  0%  R_PoW=100.00%  R_PoS=0.00%
  θ= 10%  R_PoW=80.05%   R_PoS=19.95%
  θ= 25%  R_PoW=62.11%   R_PoS=37.89%
  θ= 50%  R_PoW=38.44%   R_PoS=61.56%
  θ= 75%  R_PoW=18.24%   R_PoS=81.76%
  θ= 90%  R_PoW=7.11%    R_PoS=92.89%
  θ=100%  R_PoW=0.00%    R_PoS=100.00%

-- security floor = c_sec · (TVL/M) · r_atk, per-epoch attacker cost of capital r_atk = 1% --
c_sec=1x:
  TVL/M= 10%  floor=0.001000 (of M per epoch)
  TVL/M= 25%  floor=0.002500 (of M per epoch)
  TVL/M= 50%  floor=0.005000 (of M per epoch)
  TVL/M=100%  floor=0.010000 (of M per epoch)
  TVL/M=200%  floor=0.020000 (of M per epoch)
c_sec=2x:
  TVL/M= 10%  floor=0.002000 (of M per epoch)
  TVL/M= 25%  floor=0.005000 (of M per epoch)
  TVL/M= 50%  floor=0.010000 (of M per epoch)
  TVL/M=100%  floor=0.020000 (of M per epoch)
  TVL/M=200%  floor=0.040000 (of M per epoch)
c_sec=3x:
  TVL/M= 10%  floor=0.003000 (of M per epoch)
  TVL/M= 25%  floor=0.007500 (of M per epoch)
  TVL/M= 50%  floor=0.015000 (of M per epoch)
  TVL/M=100%  floor=0.030000 (of M per epoch)
  TVL/M=200%  floor=0.060000 (of M per epoch)
c_sec=5x:
  TVL/M= 10%  floor=0.005000 (of M per epoch)
  TVL/M= 25%  floor=0.012500 (of M per epoch)
  TVL/M= 50%  floor=0.025000 (of M per epoch)
  TVL/M=100%  floor=0.050000 (of M per epoch)
  TVL/M=200%  floor=0.100000 (of M per epoch)

what it shows: the split crosses 50/50 exactly at θ = 0.5^(1/α) — for α=0.5 that is θ=0.25 (0.25^0.5 = 0.5), confirmed above; a higher α shifts the crossover down and shrinks the PoW share at every θ, matching §10's framing of α as which side absorbs marginal security cost. The floor is linear and separable in c_sec, TVL/M, and r_atk by construction (three multiplications), confirmed by floor_scales_linearly_in_each_factor in rs/allocation.rs.

remains

c_sec and r_atk are governance/PID outputs, not derived here — the next slice is the PID loop reading the observable signals §10 names (security margin, fee coverage, efficiency differential) and feeding them into security_floor and clamp_alpha's midpoint.

Graph