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,7 +84,17 @@ 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
- 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 ):
87
98
"""
88
99
Tests that the percentage of blocks won is proportional to the space of each farmer,
89
100
with the assumption that all farmers have access to the same VDF speed.
@@ -94,61 +105,75 @@ def test_win_percentage(self):
94
105
PlotSize .make_v1 (34 ): 100 ,
95
106
PlotSize .make_v1 (35 ): 100 ,
96
107
PlotSize .make_v1 (36 ): 100 ,
97
- PlotSize .make_v2 (28 ): 100 ,
98
- PlotSize .make_v2 (30 ): 100 ,
99
- PlotSize .make_v2 (32 ): 100 ,
108
+ PlotSize .make_v2 (28 ): 200 ,
109
+ PlotSize .make_v2 (30 ): 200 ,
110
+ PlotSize .make_v2 (32 ): 200 ,
100
111
}
101
112
farmer_space = {k : _expected_plot_size (k ) * count for k , count in farmer_ks .items ()}
102
- total_space = sum (farmer_space .values ())
103
- percentage_space = {k : float (sp / total_space ) for k , sp in farmer_space .items ()}
104
113
wins = {k : 0 for k in farmer_ks .keys ()}
114
+
115
+ constants = test_constants .replace (DIFFICULTY_CONSTANT_FACTOR = uint128 (2 ** 25 ))
105
116
total_slots = 50
106
117
num_sps = 16
107
- sp_interval_iters = uint64 (100000000 // 32 )
118
+ sub_slot_iters = uint64 (100000000 )
119
+ sp_interval_iters = calculate_sp_interval_iters (constants , sub_slot_iters )
108
120
difficulty = uint64 (500000000000 )
109
- constants = test_constants .replace (DIFFICULTY_CONSTANT_FACTOR = uint128 (2 ** 25 ))
110
121
for slot_index in range (total_slots ):
111
122
total_wins_in_slot = 0
112
123
for sp_index in range (num_sps ):
113
124
sp_hash = std_hash (slot_index .to_bytes (4 , "big" ) + sp_index .to_bytes (4 , "big" ))
114
125
for k , count in farmer_ks .items ():
115
126
for farmer_index in range (count ):
116
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
117
129
quality = std_hash (
118
130
slot_index .to_bytes (4 , "big" ) + plot_k_val .to_bytes (1 , "big" ) + bytes (farmer_index )
119
131
)
120
132
required_iters = calculate_iterations_quality (
121
- constants , quality , k , difficulty , sp_hash , uint64 ( 100000000 ), uint32 ( 0 )
133
+ constants , quality , k , difficulty , sp_hash , sub_slot_iters , height
122
134
)
123
135
if required_iters < sp_interval_iters :
124
136
wins [k ] += 1
125
137
total_wins_in_slot += 1
126
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
+
127
150
win_percentage = {k : wins [k ] / sum (wins .values ()) for k in farmer_ks .keys ()}
128
151
for k in farmer_ks .keys ():
129
152
# Win rate is proportional to percentage of space
130
153
assert abs (win_percentage [k ] - percentage_space [k ]) < 0.01
131
154
132
- 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 ):
133
157
constants = test_constants
134
- sub_slot_iters = uint64 (100000000000 )
135
- sp_interval = calculate_sp_interval_iters (constants , sub_slot_iters )
158
+ sub_slot_iters = uint64 (sp_interval * constants .NUM_SPS_SUB_SLOT )
136
159
# Before or at HARD_FORK2_HEIGHT, should return 0
137
- 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
138
161
assert calculate_phase_out (constants , sub_slot_iters , constants .HARD_FORK2_HEIGHT ) == 0
139
162
# after HARD_FORK2_HEIGHT, should return value = delta/phase_out_period * sp_interval
140
163
assert (
141
- 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 ) )
142
165
== sp_interval // constants .PLOT_V1_PHASE_OUT
143
166
)
144
167
assert (
145
168
calculate_phase_out (
146
- 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 )
147
170
)
148
171
== sp_interval // 2
149
172
)
150
173
assert (
151
- 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
+ )
152
177
== sp_interval
153
178
)
154
179
0 commit comments