11import Linleios
2+ import LSpec
23import Std.Data.HashMap
34
5+ open LSpec
46open Std.HashMap (singleton_eq_insert)
57
68
9+ -- Lemmas, in lieu of tests
10+
711/--
812The genesis state starts with zero counts.
913-/
@@ -14,11 +18,145 @@ theorem genesis : (default : State).clock = 0 ∧ (default : State).rbCount = 0
1418 rfl
1519 rfl
1620
17- /-
18- The initial probability is unity.
21+
22+ -- Generators
23+
24+ structure RangedNat (lo hi : Nat) where
25+ value: Nat
26+ deriving Repr
27+
28+ instance {lo hi : Nat} : SlimCheck.Shrinkable (RangedNat lo hi) where
29+ shrink _ := []
30+
31+ instance {lo hi : Nat} : SlimCheck.SampleableExt (RangedNat lo hi) :=
32+ SlimCheck.SampleableExt.mkSelfContained $
33+ RangedNat.mk <$> SlimCheck.Gen.choose Nat lo hi
34+
35+ instance : SlimCheck.Random Float where
36+ randomR lo hi :=
37+ do
38+ let res := 1000
39+ let lo' := (lo * res).ceil.toUInt16.toNat
40+ let hi' := (hi * res).floor.toUInt16.toNat
41+ let i ← SlimCheck.Random.randomR lo' hi'
42+ pure $ i.toFloat / res
43+
44+ structure RangedFloat (lo hi : Float) where
45+ value: Float
46+ deriving Repr
47+
48+ instance {lo hi : Float} : SlimCheck.Shrinkable (RangedFloat lo hi) where
49+ shrink _ := []
50+
51+ instance {lo hi : Float} : SlimCheck.SampleableExt (RangedFloat lo hi) :=
52+ SlimCheck.SampleableExt.mkSelfContained $
53+ RangedFloat.mk <$> SlimCheck.Gen.choose Float lo hi
54+
55+ instance : SlimCheck.Shrinkable Environment where
56+ shrink _ := []
57+
58+ instance : SlimCheck.SampleableExt Environment :=
59+ SlimCheck.SampleableExt.mkSelfContained $
60+ do
61+ let Lheader : Nat ← SlimCheck.Gen.choose Nat 1 3
62+ let Lvote ← SlimCheck.Gen.choose Nat 2 6
63+ let Ldiff ← SlimCheck.Gen.choose Nat 0 8
64+ let activeSlotCoefficient : Float ← SlimCheck.Gen.choose Float 0 .01 0 .05
65+ let committeeSize ← SlimCheck.Gen.choose Nat 100 800
66+ let τ ← SlimCheck.Gen.choose Float 0 .51 0 .99
67+ let pRbHeaderArrives ← SlimCheck.Gen.choose Float 0 .90 1 .00
68+ let pEbValidates ← SlimCheck.Gen.choose Float 0 .85 1 .00
69+ let pEbUnvalidated ← SlimCheck.Gen.choose Float 0 .00 0 .15
70+ let fAdversary ← SlimCheck.Gen.choose Float 0 .00 0 .49
71+ pure $
72+ makeEnvironment
73+ Lheader Lvote Ldiff
74+ activeSlotCoefficient
75+ committeeSize.toFloat τ
76+ pRbHeaderArrives pEbValidates pEbUnvalidated
77+ fAdversary
78+
79+ instance : SlimCheck.Shrinkable State where
80+ shrink _ := []
81+
82+ instance : SlimCheck.SampleableExt State :=
83+ SlimCheck.SampleableExt.mkSelfContained $
84+ do
85+ let clock ← SlimCheck.Gen.choose Nat 0 1000
86+ let rbCount ← SlimCheck.Gen.choose Nat 0 clock
87+ let ebCount ← SlimCheck.Gen.choose Nat 0 rbCount
88+ let hasRb ← SlimCheck.Gen.chooseAny Bool
89+ let canCertify ← Bool.and hasRb <$> SlimCheck.Gen.chooseAny Bool
90+ pure $ State.mk clock rbCount ebCount hasRb canCertify
91+
92+
93+ -- Property-based tests.
94+
95+ /--
96+ Tolerance for floating-point operations.
1997-/
20- #guard totalProbability (default : Probabilities) == 1
98+ private def ε := 1e-6
2199
100+ /--
101+ Check that a probability is close to unity.
102+ -/
103+ private def nearUnity (x : Float) : Bool := (x - 1 ).abs < ε
22104
105+ /--
106+ Check that the total outcome is near unity.
107+ -/
108+ private def outcomeNearUnity (os : Outcomes) : Bool :=
109+ nearUnity $ os.foldl (fun acc sp ↦ acc + sp.snd) 0
110+
111+ #lspec
112+ group "Probabilities" (
113+ test "Initial probability is unity" (
114+ totalProbability (default : Probabilities) == 1
115+ )
116+ )
117+ $ group "Stake distribution" (
118+ check "Stake fractions are positive" (
119+ ∀ nPools : RangedNat 500 5000 ,
120+ ((stakeDistribution nPools.value).map (fun x ↦ decide $ x > 0 )).and
121+ )
122+ $ check "Stake fractions sum to unity" (
123+ ∀ nPools : RangedNat 500 5000 ,
124+ (nearUnity $ (stakeDistribution nPools.value).sum)
125+ )
126+ )
127+ $ group "Quorum" (
128+ check "All voters vote and all votes are received" (
129+ ∀ τ : RangedFloat 1 999 ,
130+ ∀ committeeSize : RangedNat 100 nPools,
131+ (nearUnity $ pQuorum 1 committeeSize.value.toFloat τ.value)
132+ )
133+ )
134+ $ group "Conservation of probability" (
135+ check "Forging RB" (
136+ ∀ env : Environment,
137+ ∀ state : State,
138+ (outcomeNearUnity $ @forgeRb env state)
139+ )
140+ $ check "Certifying" (
141+ ∀ env : Environment,
142+ ∀ state : State,
143+ (outcomeNearUnity $ @certify env state)
144+ )
145+ $ check "Forging EB" (
146+ ∀ env : Environment,
147+ ∀ state : State,
148+ (outcomeNearUnity $ @forgeEb env state)
149+ )
150+ $ check "Voting" (
151+ ∀ env : Environment,
152+ ∀ state : State,
153+ (outcomeNearUnity $ @vote env state)
154+ )
155+ )
156+
157+
158+ /--
159+ Testing is done elsewhere in this file.
160+ -/
23161def main : IO Unit :=
24162 pure ()
0 commit comments