Skip to content

Commit bcdc0c6

Browse files
authored
CHIA-3520 CHIA-3656 CHIA-3665 Calculate iterations v2 (#19956)
* add v2 support to calculate_iterations_quality() and update the corresponding test * update _expected_plot_size() for v2 plots * address review comments
1 parent 8b49b06 commit bcdc0c6

File tree

3 files changed

+80
-48
lines changed

3 files changed

+80
-48
lines changed

chia/_tests/core/consensus/test_pot_iterations.py

Lines changed: 56 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import pytest
34
from chia_rs import PlotSize
45
from chia_rs.sized_ints import uint8, uint16, uint32, uint64, uint128
56
from pytest import raises
@@ -83,67 +84,96 @@ def test_calculate_ip_iters(self):
8384
assert ip_iters == (sp_iters + test_constants.NUM_SP_INTERVALS_EXTRA * sp_interval_iters + required_iters) % ssi
8485
assert sp_iters > ip_iters
8586

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):
8898
"""
8999
Tests that the percentage of blocks won is proportional to the space of each farmer,
90100
with the assumption that all farmers have access to the same VDF speed.
91101
"""
92102
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,
98111
}
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()}
102113
wins = {k: 0 for k in farmer_ks.keys()}
114+
115+
constants = test_constants.replace(DIFFICULTY_CONSTANT_FACTOR=uint128(2**25))
103116
total_slots = 50
104117
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)
106120
difficulty = uint64(500000000000)
107-
constants = test_constants.replace(DIFFICULTY_CONSTANT_FACTOR=uint128(2**25))
108121
for slot_index in range(total_slots):
109122
total_wins_in_slot = 0
110123
for sp_index in range(num_sps):
111124
sp_hash = std_hash(slot_index.to_bytes(4, "big") + sp_index.to_bytes(4, "big"))
112125
for k, count in farmer_ks.items():
113126
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+
)
115132
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
117134
)
118135
if required_iters < sp_interval_iters:
119136
wins[k] += 1
120137
total_wins_in_slot += 1
121138

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+
122150
win_percentage = {k: wins[k] / sum(wins.values()) for k in farmer_ks.keys()}
123151
for k in farmer_ks.keys():
124152
# Win rate is proportional to percentage of space
125153
assert abs(win_percentage[k] - percentage_space[k]) < 0.01
126154

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):
128157
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)
131159
# 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
133161
assert calculate_phase_out(constants, sub_slot_iters, constants.HARD_FORK2_HEIGHT) == 0
134162
# after HARD_FORK2_HEIGHT, should return value = delta/phase_out_period * sp_interval
135163
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))
137165
== sp_interval // constants.PLOT_V1_PHASE_OUT
138166
)
139167
assert (
140168
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)
142170
)
143171
== sp_interval // 2
144172
)
145173
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+
)
147177
== sp_interval
148178
)
149179

@@ -154,21 +184,16 @@ def test_calculate_phase_out(self):
154184

155185

156186
def test_expected_plot_size_v1() -> None:
157-
last_size = 4800000
187+
last_size = 2_400_000
158188
for k in range(18, 50):
159189
plot_size = _expected_plot_size(PlotSize.make_v1(k))
160-
assert plot_size > last_size
190+
assert plot_size > last_size * 2
161191
last_size = plot_size
162192

163193

164194
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):
167197
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
174199
last_size = plot_size

chia/consensus/pos_quality.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,18 @@
77
# This is not used in consensus, only for display purposes
88
UI_ACTUAL_SPACE_CONSTANT_FACTOR = 0.78
99

10-
# these values are from CHIP-48
10+
# TODO: todo_v2_plots these values prelimenary. When the plotter is complete,
11+
# replace this table with a closed form formula
1112
v2_plot_sizes: dict[int, uint64] = {
12-
28: uint64(1717986918),
13-
30: uint64(4509715660),
14-
32: uint64(11381663334),
13+
16: uint64(222_863),
14+
18: uint64(1_048_737),
15+
20: uint64(4_824_084),
16+
22: uint64(21_812_958),
17+
24: uint64(97_318_160),
18+
26: uint64(429_539_960),
19+
28: uint64(1_879_213_114),
20+
30: uint64(8_161_097_549),
21+
32: uint64(35_221_370_574),
1522
}
1623

1724

chia/consensus/pot_iterations.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -110,18 +110,18 @@ def calculate_iterations_quality(
110110
"""
111111
if size.size_v1 is not None:
112112
assert size.size_v2 is None
113-
sp_quality_string: bytes32 = std_hash(quality_string + cc_sp_output_hash)
114113
phase_out = calculate_phase_out(constants, ssi, prev_transaction_block_height)
115-
iters = uint64(
116-
(
117-
int(difficulty)
118-
* int(constants.DIFFICULTY_CONSTANT_FACTOR)
119-
* int.from_bytes(sp_quality_string, "big", signed=False)
120-
// (int(pow(2, 256)) * int(_expected_plot_size(size)))
121-
)
122-
+ phase_out
123-
)
124-
return max(iters, uint64(1))
125114
else:
126-
# TODO: todo_v2_plots support v2 plots
127-
raise NotImplementedError
115+
phase_out = uint64(0)
116+
117+
sp_quality_string: bytes32 = std_hash(quality_string + cc_sp_output_hash)
118+
iters = uint64(
119+
(
120+
int(difficulty)
121+
* int(constants.DIFFICULTY_CONSTANT_FACTOR)
122+
* int.from_bytes(sp_quality_string, "big", signed=False)
123+
// (int(pow(2, 256)) * int(_expected_plot_size(size)))
124+
)
125+
+ phase_out
126+
)
127+
return max(iters, uint64(1))

0 commit comments

Comments
 (0)