1
1
from __future__ import annotations
2
2
3
+ import pytest
3
4
from chia_rs import PlotSize
4
5
from chia_rs .sized_ints import uint8 , uint16 , uint32 , uint64 , uint128
5
6
from pytest import raises
@@ -83,67 +84,96 @@ def test_calculate_ip_iters(self):
83
84
assert ip_iters == (sp_iters + test_constants .NUM_SP_INTERVALS_EXTRA * sp_interval_iters + required_iters ) % ssi
84
85
assert sp_iters > ip_iters
85
86
86
- # TODO: todo_v2_plots test this for v2 plots as well
87
- def test_win_percentage (self ):
87
+ @pytest .mark .parametrize (
88
+ "height" ,
89
+ [
90
+ uint32 (0 ),
91
+ test_constants .HARD_FORK2_HEIGHT - 1 ,
92
+ test_constants .HARD_FORK2_HEIGHT ,
93
+ test_constants .HARD_FORK2_HEIGHT + test_constants .PLOT_V1_PHASE_OUT ,
94
+ test_constants .HARD_FORK2_HEIGHT + test_constants .PLOT_V1_PHASE_OUT + 1 ,
95
+ ],
96
+ )
97
+ def test_win_percentage (self , height : uint32 ):
88
98
"""
89
99
Tests that the percentage of blocks won is proportional to the space of each farmer,
90
100
with the assumption that all farmers have access to the same VDF speed.
91
101
"""
92
102
farmer_ks = {
93
- uint8 (32 ): 100 ,
94
- uint8 (33 ): 100 ,
95
- uint8 (34 ): 100 ,
96
- uint8 (35 ): 100 ,
97
- uint8 (36 ): 100 ,
103
+ PlotSize .make_v1 (32 ): 100 ,
104
+ PlotSize .make_v1 (33 ): 100 ,
105
+ PlotSize .make_v1 (34 ): 100 ,
106
+ PlotSize .make_v1 (35 ): 100 ,
107
+ PlotSize .make_v1 (36 ): 100 ,
108
+ PlotSize .make_v2 (28 ): 200 ,
109
+ PlotSize .make_v2 (30 ): 200 ,
110
+ PlotSize .make_v2 (32 ): 200 ,
98
111
}
99
- farmer_space = {k : _expected_plot_size (PlotSize .make_v1 (k )) * count for k , count in farmer_ks .items ()}
100
- total_space = sum (farmer_space .values ())
101
- percentage_space = {k : float (sp / total_space ) for k , sp in farmer_space .items ()}
112
+ farmer_space = {k : _expected_plot_size (k ) * count for k , count in farmer_ks .items ()}
102
113
wins = {k : 0 for k in farmer_ks .keys ()}
114
+
115
+ constants = test_constants .replace (DIFFICULTY_CONSTANT_FACTOR = uint128 (2 ** 25 ))
103
116
total_slots = 50
104
117
num_sps = 16
105
- sp_interval_iters = uint64 (100000000 // 32 )
118
+ sub_slot_iters = uint64 (100000000 )
119
+ sp_interval_iters = calculate_sp_interval_iters (constants , sub_slot_iters )
106
120
difficulty = uint64 (500000000000 )
107
- constants = test_constants .replace (DIFFICULTY_CONSTANT_FACTOR = uint128 (2 ** 25 ))
108
121
for slot_index in range (total_slots ):
109
122
total_wins_in_slot = 0
110
123
for sp_index in range (num_sps ):
111
124
sp_hash = std_hash (slot_index .to_bytes (4 , "big" ) + sp_index .to_bytes (4 , "big" ))
112
125
for k , count in farmer_ks .items ():
113
126
for farmer_index in range (count ):
114
- quality = std_hash (slot_index .to_bytes (4 , "big" ) + k .to_bytes (1 , "big" ) + bytes (farmer_index ))
127
+ plot_k_val = k .size_v1 if k .size_v2 is None else k .size_v2
128
+ assert plot_k_val is not None
129
+ quality = std_hash (
130
+ slot_index .to_bytes (4 , "big" ) + plot_k_val .to_bytes (1 , "big" ) + bytes (farmer_index )
131
+ )
115
132
required_iters = calculate_iterations_quality (
116
- constants , quality , PlotSize . make_v1 ( k ) , difficulty , sp_hash , uint64 ( 100000000 ), uint32 ( 0 )
133
+ constants , quality , k , difficulty , sp_hash , sub_slot_iters , height
117
134
)
118
135
if required_iters < sp_interval_iters :
119
136
wins [k ] += 1
120
137
total_wins_in_slot += 1
121
138
139
+ if height < test_constants .HARD_FORK2_HEIGHT + test_constants .PLOT_V1_PHASE_OUT :
140
+ total_space = sum (farmer_space .values ())
141
+ percentage_space = {k : float (sp / total_space ) for k , sp in farmer_space .items ()}
142
+ else :
143
+ # after the phase-out, v1 plots don't count
144
+ # all wins are by v2 plots
145
+ total_space = sum (0 if k .size_v2 is None else sp for k , sp in farmer_space .items ())
146
+ percentage_space = {
147
+ k : 0.0 if k .size_v2 is None else float (sp / total_space ) for k , sp in farmer_space .items ()
148
+ }
149
+
122
150
win_percentage = {k : wins [k ] / sum (wins .values ()) for k in farmer_ks .keys ()}
123
151
for k in farmer_ks .keys ():
124
152
# Win rate is proportional to percentage of space
125
153
assert abs (win_percentage [k ] - percentage_space [k ]) < 0.01
126
154
127
- def test_calculate_phase_out (self ):
155
+ @pytest .mark .parametrize ("sp_interval" , [uint64 (6250000000 ), uint64 (1 ), uint64 (2 ), uint64 (10 ), uint64 (10000000000 )])
156
+ def test_calculate_phase_out (self , sp_interval : uint64 ):
128
157
constants = test_constants
129
- sub_slot_iters = uint64 (100000000000 )
130
- sp_interval = calculate_sp_interval_iters (constants , sub_slot_iters )
158
+ sub_slot_iters = uint64 (sp_interval * constants .NUM_SPS_SUB_SLOT )
131
159
# Before or at HARD_FORK2_HEIGHT, should return 0
132
- assert calculate_phase_out (constants , sub_slot_iters , constants .HARD_FORK2_HEIGHT - 1 ) == 0
160
+ assert calculate_phase_out (constants , sub_slot_iters , uint32 ( constants .HARD_FORK2_HEIGHT - 1 ) ) == 0
133
161
assert calculate_phase_out (constants , sub_slot_iters , constants .HARD_FORK2_HEIGHT ) == 0
134
162
# after HARD_FORK2_HEIGHT, should return value = delta/phase_out_period * sp_interval
135
163
assert (
136
- calculate_phase_out (constants , sub_slot_iters , constants .HARD_FORK2_HEIGHT + 1 )
164
+ calculate_phase_out (constants , sub_slot_iters , uint32 ( constants .HARD_FORK2_HEIGHT + 1 ) )
137
165
== sp_interval // constants .PLOT_V1_PHASE_OUT
138
166
)
139
167
assert (
140
168
calculate_phase_out (
141
- constants , sub_slot_iters , constants .HARD_FORK2_HEIGHT + constants .PLOT_V1_PHASE_OUT // 2
169
+ constants , sub_slot_iters , uint32 ( constants .HARD_FORK2_HEIGHT + constants .PLOT_V1_PHASE_OUT // 2 )
142
170
)
143
171
== sp_interval // 2
144
172
)
145
173
assert (
146
- calculate_phase_out (constants , sub_slot_iters , constants .HARD_FORK2_HEIGHT + constants .PLOT_V1_PHASE_OUT )
174
+ calculate_phase_out (
175
+ constants , sub_slot_iters , uint32 (constants .HARD_FORK2_HEIGHT + constants .PLOT_V1_PHASE_OUT )
176
+ )
147
177
== sp_interval
148
178
)
149
179
@@ -154,21 +184,16 @@ def test_calculate_phase_out(self):
154
184
155
185
156
186
def test_expected_plot_size_v1 () -> None :
157
- last_size = 4800000
187
+ last_size = 2_400_000
158
188
for k in range (18 , 50 ):
159
189
plot_size = _expected_plot_size (PlotSize .make_v1 (k ))
160
- assert plot_size > last_size
190
+ assert plot_size > last_size * 2
161
191
last_size = plot_size
162
192
163
193
164
194
def test_expected_plot_size_v2 () -> None :
165
- last_size = 1700000000
166
- for k in range (18 , 32 , 2 ):
195
+ last_size = 100_000
196
+ for k in range (16 , 32 , 2 ):
167
197
plot_size = _expected_plot_size (PlotSize .make_v2 (k ))
168
- # TODO: todo_v2_plots remove this special case once we support smaller k-sizes
169
- if k < 28 :
170
- assert plot_size == 0
171
- continue
172
-
173
- assert plot_size > last_size
198
+ assert plot_size > last_size * 2
174
199
last_size = plot_size
0 commit comments