refactor difficulty adjustment and ips
This commit is contained in:
parent
092b7a288f
commit
f7d0aa3213
|
@ -5,14 +5,10 @@ from src.consensus.pos_quality import quality_str_to_quality
|
|||
from src.consensus.constants import ConsensusConstants
|
||||
|
||||
|
||||
def is_overflow_sub_block(
|
||||
constants: ConsensusConstants, ips: uint64, required_iters: uint64
|
||||
) -> bool:
|
||||
def is_overflow_sub_block(constants: ConsensusConstants, ips: uint64, required_iters: uint64) -> bool:
|
||||
slot_iters: uint64 = calculate_slot_iters(constants, ips)
|
||||
if required_iters >= slot_iters:
|
||||
raise ValueError(
|
||||
f"Required iters {required_iters} is not below the slot iterations"
|
||||
)
|
||||
raise ValueError(f"Required iters {required_iters} is not below the slot iterations")
|
||||
extra_iters: uint64 = uint64(int(float(ips) * constants.EXTRA_ITERS_TIME_TARGET))
|
||||
return required_iters + extra_iters >= slot_iters
|
||||
|
||||
|
@ -21,14 +17,10 @@ def calculate_slot_iters(constants: ConsensusConstants, ips: uint64) -> uint64:
|
|||
return ips * constants.SLOT_TIME_TARGET
|
||||
|
||||
|
||||
def calculate_icp_iters(
|
||||
constants: ConsensusConstants, ips: uint64, required_iters: uint64
|
||||
) -> uint64:
|
||||
def calculate_sp_iters(constants: ConsensusConstants, ips: uint64, required_iters: uint64) -> uint64:
|
||||
slot_iters: uint64 = calculate_slot_iters(constants, ips)
|
||||
if required_iters >= slot_iters:
|
||||
raise ValueError(
|
||||
f"Required iters {required_iters} is not below the slot iterations"
|
||||
)
|
||||
raise ValueError(f"Required iters {required_iters} is not below the slot iterations")
|
||||
checkpoint_size: uint64 = uint64(slot_iters // constants.NUM_CHECKPOINTS_PER_SLOT)
|
||||
checkpoint_index: int = required_iters // checkpoint_size
|
||||
|
||||
|
@ -39,10 +31,8 @@ def calculate_icp_iters(
|
|||
return required_iters - required_iters % checkpoint_size
|
||||
|
||||
|
||||
def calculate_icp_index(
|
||||
constants: ConsensusConstants, ips: uint64, required_iters: uint64
|
||||
) -> uint8:
|
||||
icp_iters = calculate_icp_iters(constants, ips, required_iters)
|
||||
def calculate_icp_index(constants: ConsensusConstants, ips: uint64, required_iters: uint64) -> uint8:
|
||||
icp_iters = calculate_sp_iters(constants, ips, required_iters)
|
||||
slot_iters: uint64 = calculate_slot_iters(constants, ips)
|
||||
checkpoint_size: uint64 = uint64(slot_iters // constants.NUM_CHECKPOINTS_PER_SLOT)
|
||||
assert icp_iters % checkpoint_size == 0
|
||||
|
@ -50,15 +40,11 @@ def calculate_icp_index(
|
|||
return uint8(target_index)
|
||||
|
||||
|
||||
def calculate_ip_iters(
|
||||
constants: ConsensusConstants, ips: uint64, required_iters: uint64
|
||||
) -> uint64:
|
||||
def calculate_ip_iters(constants: ConsensusConstants, ips: uint64, required_iters: uint64) -> uint64:
|
||||
# Note that the IPS is for the block passed in, which might be in the previous epoch
|
||||
slot_iters: uint64 = calculate_slot_iters(constants, ips)
|
||||
if required_iters >= slot_iters:
|
||||
raise ValueError(
|
||||
f"Required iters {required_iters} is not below the slot iterations"
|
||||
)
|
||||
raise ValueError(f"Required iters {required_iters} is not below the slot iterations")
|
||||
extra_iters: uint64 = uint64(int(float(ips) * constants.EXTRA_ITERS_TIME_TARGET))
|
||||
return (required_iters + extra_iters) % slot_iters
|
||||
|
||||
|
@ -73,9 +59,7 @@ def calculate_iterations_quality(
|
|||
between 0 and 1, then divided by expected plot size, and finally multiplied by the
|
||||
difficulty.
|
||||
"""
|
||||
iters = uint64(
|
||||
uint128(int(difficulty) << 32) // quality_str_to_quality(quality, size)
|
||||
)
|
||||
iters = uint64(uint128(int(difficulty) << 32) // quality_str_to_quality(quality, size))
|
||||
return max(iters, uint64(1))
|
||||
|
||||
|
||||
|
@ -88,8 +72,6 @@ def calculate_iterations(
|
|||
Convenience function to calculate the number of iterations using the proof instead
|
||||
of the quality. The quality must be retrieved from the proof.
|
||||
"""
|
||||
quality: bytes32 = proof_of_space.verify_and_get_quality_string(
|
||||
constants, None, None
|
||||
)
|
||||
quality: bytes32 = proof_of_space.verify_and_get_quality_string(constants, None, None)
|
||||
assert quality is not None
|
||||
return calculate_iterations_quality(quality, proof_of_space.size, difficulty)
|
||||
|
|
|
@ -17,7 +17,7 @@ from src.types.vdf import VDFInfo
|
|||
from src.consensus.pot_iterations import (
|
||||
is_overflow_sub_block,
|
||||
calculate_ip_iters,
|
||||
calculate_icp_iters,
|
||||
calculate_sp_iters,
|
||||
calculate_slot_iters,
|
||||
calculate_iterations_quality,
|
||||
)
|
||||
|
@ -44,12 +44,42 @@ async def validate_unfinished_header_block(
|
|||
# 1. Check that the previous block exists in the blockchain
|
||||
if header_block.height == 0:
|
||||
prev_sb: Optional[SubBlockRecord] = None
|
||||
finishes_se = False
|
||||
finishes_epoch = False
|
||||
difficulty: uint64 = uint64(constants.DIFFICULTY_STARTING)
|
||||
ips: uint64 = uint64(constants.IPS_STARTING)
|
||||
else:
|
||||
prev_sb: Optional[SubBlockRecord] = sub_blocks[header_block.prev_header_hash]
|
||||
if prev_sb is not None:
|
||||
if prev_sb is None:
|
||||
return None, Err.DOES_NOT_EXTEND
|
||||
new_slot: bool = len(header_block.finished_sub_slots) > 0
|
||||
|
||||
finishes_se = finishes_sub_epoch(constants, prev_sb.height, prev_sb.deficit, False)
|
||||
finishes_epoch: bool = finishes_sub_epoch(constants, prev_sb.height, prev_sb.deficit, True)
|
||||
|
||||
difficulty: uint64 = get_next_difficulty(
|
||||
constants,
|
||||
sub_blocks,
|
||||
height_to_hash,
|
||||
header_block.prev_header_hash,
|
||||
prev_sb.height,
|
||||
prev_sb.deficit,
|
||||
uint64(prev_sb.weight - sub_blocks[prev_sb.prev_hash].weight),
|
||||
True,
|
||||
prev_sb.total_iters,
|
||||
)
|
||||
ips: uint64 = get_next_ips(
|
||||
constants,
|
||||
sub_blocks,
|
||||
height_to_hash,
|
||||
header_block.prev_header_hash,
|
||||
prev_sb.height,
|
||||
prev_sb.deficit,
|
||||
prev_sb.ips,
|
||||
True,
|
||||
prev_sb.total_iters,
|
||||
)
|
||||
|
||||
new_slot: bool = len(header_block.finished_sub_slots) > 0
|
||||
# 2. Check finished slots
|
||||
if new_slot:
|
||||
# Finished a slot(s) since previous block
|
||||
|
@ -148,21 +178,13 @@ async def validate_unfinished_header_block(
|
|||
return None, Err.SHOULD_HAVE_ICC
|
||||
# This is when deficit is <5 and thus we have challenge block and full empty slot of VDFs
|
||||
# Check full ICC vdf
|
||||
ips_empty_slots: uint64 = get_next_ips(
|
||||
constants,
|
||||
height_to_hash,
|
||||
sub_blocks,
|
||||
header_block.prev_header_hash,
|
||||
True,
|
||||
)
|
||||
|
||||
# 2g. Check infused challenge chain sub-slot VDF
|
||||
target_vdf_info = VDFInfo(
|
||||
header_block.finished_sub_slots[
|
||||
finished_sub_slot_n - 1
|
||||
].infused_challenge_chain.get_hash(),
|
||||
ClassgroupElement.get_default_element(),
|
||||
ips_empty_slots * constants.SLOT_TIME_TARGET,
|
||||
uint64(ips * constants.SLOT_TIME_TARGET),
|
||||
sub_slot.infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf.output,
|
||||
)
|
||||
if sub_slot.proofs.challenge_chain_slot_proof.is_valid(
|
||||
|
@ -201,7 +223,6 @@ async def validate_unfinished_header_block(
|
|||
# 2k. Check challenge chain sub-slot VDF
|
||||
# 2l. Check end of reward slot VDF
|
||||
if prev_sb is None:
|
||||
ips: uint64 = uint64(constants.IPS_STARTING)
|
||||
eos_vdf_iters: uint64 = calculate_slot_iters(constants, ips)
|
||||
cc_start_element: ClassgroupElement = ClassgroupElement.get_default_element()
|
||||
if finished_sub_slot_n == 0:
|
||||
|
@ -226,13 +247,6 @@ async def validate_unfinished_header_block(
|
|||
cc_start_element: ClassgroupElement = prev_sb.challenge_vdf_output
|
||||
else:
|
||||
# At least one empty slot, so use previous slot hash. IPS might change because it's a new slot
|
||||
ips: uint64 = get_next_ips(
|
||||
constants,
|
||||
height_to_hash,
|
||||
sub_blocks,
|
||||
header_block.prev_header_hash,
|
||||
True,
|
||||
)
|
||||
rc_eos_vdf_challenge: bytes32 = header_block.finished_sub_slots[
|
||||
finished_sub_slot_n - 1
|
||||
].reward_chain.get_hash()
|
||||
|
@ -294,13 +308,6 @@ async def validate_unfinished_header_block(
|
|||
if prev_sb is None:
|
||||
return None, Err.INVALID_SUB_EPOCH_SUMMARY
|
||||
|
||||
finishes_se = finishes_sub_epoch(
|
||||
constants, sub_blocks, header_block.prev_header_hash, False
|
||||
)
|
||||
finishes_epoch: bool = finishes_sub_epoch(
|
||||
constants, sub_blocks, header_block.prev_header_hash, True
|
||||
)
|
||||
|
||||
# 3b. Check that we finished a slot and we finished a sub-epoch
|
||||
if not new_slot or not finishes_se:
|
||||
return None, Err.INVALID_SUB_EPOCH_SUMMARY
|
||||
|
@ -309,32 +316,13 @@ async def validate_unfinished_header_block(
|
|||
while curr.sub_epoch_summary_included_hash is None:
|
||||
curr = sub_blocks[curr.prev_hash]
|
||||
|
||||
if finishes_epoch:
|
||||
next_diff = get_next_difficulty(
|
||||
constants,
|
||||
sub_blocks,
|
||||
height_to_hash,
|
||||
header_block.prev_header_hash,
|
||||
True,
|
||||
)
|
||||
next_ips = get_next_ips(
|
||||
constants,
|
||||
sub_blocks,
|
||||
height_to_hash,
|
||||
header_block.prev_header_hash,
|
||||
True,
|
||||
)
|
||||
else:
|
||||
next_diff = None
|
||||
next_ips = None
|
||||
|
||||
# 3c. Check the actual sub-epoch is correct
|
||||
expected_sub_epoch_summary = SubEpochSummary(
|
||||
curr.sub_epoch_summary_included_hash,
|
||||
curr.finished_reward_slot_hashes[-1],
|
||||
curr.height % constants.SUB_EPOCH_SUB_BLOCKS,
|
||||
next_diff,
|
||||
next_ips,
|
||||
difficulty if finishes_epoch else None,
|
||||
ips if finishes_epoch else None,
|
||||
)
|
||||
if expected_sub_epoch_summary.get_hash() != ses_hash:
|
||||
return None, Err.INVALID_SUB_EPOCH_SUMMARY
|
||||
|
@ -363,16 +351,6 @@ async def validate_unfinished_header_block(
|
|||
)
|
||||
if q_str is None:
|
||||
return None, Err.INVALID_POSPACE
|
||||
if prev_sb is None:
|
||||
difficulty: uint64 = uint64(constants.DIFFICULTY_STARTING)
|
||||
ips: uint64 = uint64(constants.IPS_STARTING)
|
||||
else:
|
||||
difficulty = get_next_difficulty(
|
||||
constants, sub_blocks, height_to_hash, prev_sb.header_hash, new_slot
|
||||
)
|
||||
ips: uint64 = get_next_ips(
|
||||
constants, sub_blocks, height_to_hash, prev_sb.header_hash, new_slot
|
||||
)
|
||||
|
||||
# Note that required iters might be from the previous slot (if we are in an overflow sub-block)
|
||||
required_iters: uint64 = calculate_iterations_quality(
|
||||
|
@ -381,7 +359,7 @@ async def validate_unfinished_header_block(
|
|||
difficulty,
|
||||
)
|
||||
|
||||
icp_iters: uint64 = calculate_icp_iters(constants, ips, required_iters)
|
||||
icp_iters: uint64 = calculate_sp_iters(constants, ips, required_iters)
|
||||
ip_iters: uint64 = calculate_ip_iters(constants, ips, required_iters)
|
||||
slot_iters: uint64 = calculate_slot_iters(constants, ips)
|
||||
overflow = is_overflow_sub_block(constants, ips, required_iters)
|
||||
|
@ -746,7 +724,11 @@ async def validate_finished_header_block(
|
|||
sub_blocks,
|
||||
height_to_hash,
|
||||
header_block.prev_header_hash,
|
||||
new_slot,
|
||||
prev_sb.height,
|
||||
prev_sb.deficit,
|
||||
prev_sb.ips,
|
||||
True,
|
||||
prev_sb.total_iters,
|
||||
)
|
||||
ip_iters: uint64 = calculate_ip_iters(constants, ips, required_iters)
|
||||
|
||||
|
@ -815,17 +797,14 @@ async def validate_finished_header_block(
|
|||
):
|
||||
return None, Err.INVALID_RC_IP_VDF
|
||||
|
||||
# 27. Check infused challenge chain infusion point VDF
|
||||
# 28. Check infused challenge chain infusion point VDF
|
||||
# TODO
|
||||
|
||||
# 28. Check reward block hash
|
||||
if (
|
||||
header_block.foliage_sub_block.reward_block_hash
|
||||
!= header_block.reward_chain_sub_block.get_hash()
|
||||
):
|
||||
# 29. Check reward block hash
|
||||
if header_block.foliage_sub_block.reward_block_hash != header_block.reward_chain_sub_block.get_hash():
|
||||
return None, Err.INVALID_REWARD_BLOCK_HASH
|
||||
|
||||
# 29. Check reward block is_block
|
||||
# 30. Check reward block is_block
|
||||
if (header_block.foliage_sub_block.foliage_block_hash is not None) != header_block.reward_chain_sub_block.is_block:
|
||||
return None, Err.INVALID_FOLIAGE_BLOCK_PRESENCE
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from typing import Dict, List
|
||||
|
||||
from src.consensus.constants import ConsensusConstants
|
||||
from src.consensus.pot_iterations import calculate_icp_iters, calculate_ip_iters
|
||||
from src.consensus.pot_iterations import calculate_sp_iters, calculate_ip_iters
|
||||
from src.types.sized_bytes import bytes32
|
||||
from src.full_node.sub_block_record import SubBlockRecord
|
||||
from src.util.ints import uint32, uint64, uint128, uint8
|
||||
|
@ -11,6 +11,88 @@ from src.util.significant_bits import (
|
|||
)
|
||||
|
||||
|
||||
def _get_blocks_at_height(
|
||||
height_to_hash: Dict[uint32, bytes32],
|
||||
sub_blocks: Dict[bytes32, SubBlockRecord],
|
||||
prev_sb: SubBlockRecord,
|
||||
target_height: uint32,
|
||||
max_num_blocks: uint32 = 1,
|
||||
) -> List[SubBlockRecord]:
|
||||
if height_to_hash[prev_sb.height] == prev_sb.header_hash:
|
||||
# Efficient fetching, since we are fetching ancestor blocks within the heaviest chain
|
||||
return [
|
||||
sub_blocks[height_to_hash[uint32(h)]]
|
||||
for h in range(target_height, target_height + max_num_blocks)
|
||||
if h in height_to_hash
|
||||
]
|
||||
# slow fetching, goes back one by one
|
||||
curr_b: SubBlockRecord = prev_sb
|
||||
target_blocks = []
|
||||
while curr_b.height >= target_height:
|
||||
if curr_b.height < target_height + max_num_blocks:
|
||||
target_blocks.append(curr_b)
|
||||
curr_b = sub_blocks[curr_b.prev_hash]
|
||||
return target_blocks
|
||||
|
||||
|
||||
def _get_last_block_in_previous_epoch(
|
||||
constants: ConsensusConstants,
|
||||
next_height: uint32,
|
||||
height_to_hash: Dict[uint32, bytes32],
|
||||
sub_blocks: Dict[bytes32, SubBlockRecord],
|
||||
prev_sb: SubBlockRecord,
|
||||
) -> SubBlockRecord:
|
||||
|
||||
# prev epoch surpassed prev epoch started epoch sur. epoch started
|
||||
# v v v v
|
||||
# |.B...B....B. B....B...|......B....B.....B...B.|.B.B.B..|..B...B.B.B...|.B.B.B. B.|........
|
||||
|
||||
# The sub-blocks selected for the timestamps are the last sub-block which is also a block, and which is infused
|
||||
# before the final sub-block in the epoch. Block at height 0 is an exception.
|
||||
height_epoch_surpass: uint32 = next_height % constants.EPOCH_SUB_BLOCKS
|
||||
if height_epoch_surpass > constants.MAX_SLOT_SUB_BLOCKS:
|
||||
raise ValueError(f"Height at {next_height} should not create a new slot, it is far past the epoch barrier")
|
||||
|
||||
# If the prev slot is the first slot, the iterations start at 0
|
||||
# We will compute the timestamps of the last block in epoch, as well as the total iterations at infusion
|
||||
first_sb_in_epoch: SubBlockRecord
|
||||
prev_slot_start_iters: uint128
|
||||
prev_slot_time_start: uint64
|
||||
|
||||
if height_epoch_surpass == 0:
|
||||
# The genesis block is an edge case, where we measure from the first block in epoch, as opposed to the last
|
||||
# block in the previous epoch
|
||||
return _get_blocks_at_height(height_to_hash, sub_blocks, prev_sb, uint32(0))[1]
|
||||
else:
|
||||
fetched_blocks = _get_blocks_at_height(
|
||||
height_to_hash,
|
||||
sub_blocks,
|
||||
prev_sb,
|
||||
uint32(height_epoch_surpass - constants.EPOCH_SUB_BLOCKS - constants.MAX_SLOT_SUB_BLOCKS - 1),
|
||||
uint32(2 * constants.MAX_SLOT_SUB_BLOCKS + 1),
|
||||
)
|
||||
# This is the last sb in the slot at which we surpass the height. The last block in epoch will be before this.
|
||||
last_sb_in_slot: SubBlockRecord = fetched_blocks[constants.MAX_SLOT_SUB_BLOCKS]
|
||||
fetched_index: int = constants.MAX_SLOT_SUB_BLOCKS + 1
|
||||
curr: SubBlockRecord = fetched_blocks[fetched_index]
|
||||
# Wait until the slot finishes with a challenge chain infusion at start of slot
|
||||
while not curr.deficit == constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK - 1:
|
||||
last_sb_in_slot = curr
|
||||
curr = fetched_blocks[fetched_index]
|
||||
fetched_index += 1
|
||||
|
||||
last_sb_ip_iters = calculate_ip_iters(constants, last_sb_in_slot.ips, last_sb_in_slot.required_iters)
|
||||
last_sb_sp_iters = calculate_sp_iters(constants, last_sb_in_slot.ips, last_sb_in_slot.required_iters)
|
||||
prev_signage_point_total_iters = last_sb_in_slot.total_iters - last_sb_ip_iters + last_sb_sp_iters
|
||||
|
||||
# Backtrack to find the last block before the signage point
|
||||
curr = sub_blocks[last_sb_in_slot.prev_hash]
|
||||
while curr.total_iters > prev_signage_point_total_iters or not curr.is_block:
|
||||
curr = sub_blocks[curr.prev_hash]
|
||||
|
||||
return curr
|
||||
|
||||
|
||||
def finishes_sub_epoch(
|
||||
constants: ConsensusConstants,
|
||||
height: uint32,
|
||||
|
@ -52,6 +134,7 @@ def get_next_ips(
|
|||
deficit: uint8,
|
||||
ips: uint64,
|
||||
new_slot: bool,
|
||||
signage_point_total_iters: uint128,
|
||||
) -> uint64:
|
||||
"""
|
||||
Returns the slot iterations required for the next block after header hash, where new_slot is true iff
|
||||
|
@ -71,78 +154,19 @@ def get_next_ips(
|
|||
if not new_slot or not finishes_sub_epoch(constants, height, deficit, True):
|
||||
return ips
|
||||
|
||||
# prev epoch surpassed prev epoch started epoch sur. epoch started
|
||||
# v v v v
|
||||
# |.B...B....B. B....B...|......B....B.....B...B.|.B.B.B..|..B...B.B.B...|.B.B.B. B.|........
|
||||
last_block_prev: SubBlockRecord = _get_last_block_in_previous_epoch(
|
||||
constants, next_height, height_to_hash, sub_blocks, prev_sb
|
||||
)
|
||||
|
||||
height_epoch_surpass: uint32 = next_height % constants.EPOCH_SUB_BLOCKS
|
||||
if height_epoch_surpass > constants.MAX_SLOT_SUB_BLOCKS:
|
||||
raise ValueError(
|
||||
f"Height at {next_height} should not create a new slot, it is far past the epoch barrier"
|
||||
)
|
||||
|
||||
# If the prev slot is the first slot, the iterations start at 0
|
||||
# We will compute the timestamps of the last block in epoch, as well as the total iterations at infusion
|
||||
first_sb_in_epoch: SubBlockRecord
|
||||
prev_slot_start_iters: uint128
|
||||
prev_slot_time_start: uint64
|
||||
in_heaviest_chain = height_to_hash[height - 1] == prev_header_hash
|
||||
|
||||
def get_blocks_at_height(target_height: uint32, max_num_blocks: uint32 = 1) -> List[SubBlockRecord]:
|
||||
if in_heaviest_chain:
|
||||
# Efficient fetching, since we are fetching ancestor blocks within the heaviest chain
|
||||
return [
|
||||
sub_blocks[height_to_hash[h]]
|
||||
for h in range(target_height, target_height + max_num_blocks)
|
||||
if h in height_to_hash
|
||||
]
|
||||
# slow fetching, goes back one by one
|
||||
curr_b: SubBlockRecord = prev_sb
|
||||
target_blocks = []
|
||||
while curr_b.height >= target_height:
|
||||
if curr_b.height < target_height + max_num_blocks:
|
||||
target_blocks.append(curr_b)
|
||||
curr_b = sub_blocks[curr.prev_hash]
|
||||
return target_blocks
|
||||
|
||||
if height_epoch_surpass == 0:
|
||||
prev_slot_start_iters = uint128(0)
|
||||
# The genesis block is an edge case, where we measure from the first block in epoch, as opposed to the last
|
||||
# block in the previous epoch
|
||||
prev_slot_time_start = get_blocks_at_height(uint32(0))[1].timestamp
|
||||
else:
|
||||
fetched_blocks = get_blocks_at_height(
|
||||
uint32(height_epoch_surpass - constants.EPOCH_SUB_BLOCKS - 1), uint32(constants.MAX_SLOT_SUB_BLOCKS + 1)
|
||||
)
|
||||
last_sb_in_prev_epoch: SubBlockRecord = fetched_blocks[0]
|
||||
fetched_index: int = 1
|
||||
curr: SubBlockRecord = fetched_blocks[fetched_index]
|
||||
# Wait until the slot finishes with a challenge chain infusion at start of slot
|
||||
while not curr.deficit == constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK - 1:
|
||||
last_sb_in_prev_epoch = curr
|
||||
curr = fetched_blocks[fetched_index]
|
||||
fetched_index += 1
|
||||
|
||||
# Ensure that we get a block (which has a timestamp)
|
||||
curr = last_sb_in_prev_epoch
|
||||
while not curr.is_block:
|
||||
curr = sub_blocks[curr.prev_hash]
|
||||
|
||||
prev_slot_start_iters = curr.total_iters
|
||||
prev_slot_time_start = curr.timestamp
|
||||
|
||||
# Ensure we get a block for the last block as well
|
||||
last_block_curr = sub_block
|
||||
while not last_block_curr.is_block:
|
||||
# Ensure we get a block for the last block as well, and that it is before the signage point
|
||||
last_block_curr = prev_sb
|
||||
while last_block_curr.total_iters > signage_point_total_iters or not last_block_curr.is_block:
|
||||
last_block_curr = sub_blocks[last_block_curr.prev_hash]
|
||||
|
||||
# This is computed as the iterations per second in last epoch, times the target number of seconds per slot
|
||||
new_ips_precise: uint64 = uint64(
|
||||
(last_block_curr.total_iters - prev_slot_start_iters)
|
||||
// (last_block_curr.timestamp - prev_slot_time_start)
|
||||
)
|
||||
new_ips = uint64(
|
||||
truncate_to_significant_bits(new_ips_precise, constants.SIGNIFICANT_BITS)
|
||||
(last_block_curr.total_iters - last_block_prev.total_iters)
|
||||
// (last_block_curr.timestamp - last_block_prev.timestamp)
|
||||
)
|
||||
assert count_significant_bits(new_ips) <= constants.SIGNIFICANT_BITS
|
||||
|
||||
|
@ -165,19 +189,6 @@ def get_next_ips(
|
|||
return max([uint64(1), new_ips, min_ips])
|
||||
|
||||
|
||||
def get_next_slot_iters(
|
||||
constants: ConsensusConstants,
|
||||
height_to_hash: Dict[uint32, bytes32],
|
||||
sub_blocks: Dict[bytes32, SubBlockRecord],
|
||||
header_hash: bytes32,
|
||||
new_slot: bool,
|
||||
) -> uint64:
|
||||
return (
|
||||
get_next_ips(constants, height_to_hash, sub_blocks, header_hash, new_slot)
|
||||
* constants.SLOT_TIME_TARGET
|
||||
)
|
||||
|
||||
|
||||
def get_difficulty(
|
||||
constants: ConsensusConstants,
|
||||
sub_blocks: Dict[bytes32, SubBlockRecord],
|
||||
|
@ -199,8 +210,10 @@ def get_next_difficulty(
|
|||
height_to_hash: Dict[uint32, bytes32],
|
||||
prev_header_hash: bytes32,
|
||||
height: uint32,
|
||||
header_hash: bytes32,
|
||||
deficit: uint8,
|
||||
current_difficulty: uint64,
|
||||
new_slot: bool,
|
||||
signage_point_total_iters: uint128,
|
||||
) -> uint64:
|
||||
"""
|
||||
Returns the difficulty of the next sub-block that extends onto sub-block.
|
||||
|
@ -209,72 +222,34 @@ def get_next_difficulty(
|
|||
"""
|
||||
next_height: uint32 = uint32(height + 1)
|
||||
|
||||
if prev_header_hash not in sub_blocks:
|
||||
raise ValueError(f"Header hash {prev_header_hash} not in sub blocks")
|
||||
|
||||
if next_height < constants.EPOCH_SUB_BLOCKS:
|
||||
# We are in the first epoch
|
||||
return uint64(constants.DIFFICULTY_STARTING)
|
||||
|
||||
if prev_header_hash not in sub_blocks:
|
||||
raise ValueError(f"Header hash {prev_header_hash} not in sub blocks")
|
||||
|
||||
# If we are in the same slot as previous sub-block, return same difficulty
|
||||
if not new_slot or not finishes_sub_epoch(constants, sub_block.height, sub_block.deficit, True):
|
||||
return get_difficulty(constants, sub_blocks, header_hash)
|
||||
if not new_slot or not finishes_sub_epoch(constants, height, deficit, True):
|
||||
return current_difficulty
|
||||
|
||||
height_epoch_surpass: uint32 = next_height % constants.EPOCH_SUB_BLOCKS
|
||||
if height_epoch_surpass > constants.MAX_SLOT_SUB_BLOCKS:
|
||||
raise ValueError(
|
||||
f"Height at {next_height} should not create a new slot, it is far past the epoch barrier"
|
||||
)
|
||||
prev_sb: SubBlockRecord = sub_blocks[prev_header_hash]
|
||||
|
||||
# We will compute the timestamps of the last block in epoch, as well as the total iterations at infusion
|
||||
first_sb_in_epoch: SubBlockRecord
|
||||
last_block_prev: SubBlockRecord = _get_last_block_in_previous_epoch(
|
||||
constants, next_height, height_to_hash, sub_blocks, prev_sb
|
||||
)
|
||||
|
||||
if height_epoch_surpass == 0:
|
||||
# The genesis block is a edge case, where we measure from the first block in epoch, as opposed to the last
|
||||
# block in the previous epoch
|
||||
prev_slot_start_timestamp = sub_blocks[height_to_hash[uint32(0)]].timestamp
|
||||
prev_slot_start_weight = 0
|
||||
else:
|
||||
last_sb_in_prev_epoch: SubBlockRecord = sub_blocks[
|
||||
height_epoch_surpass - constants.EPOCH_SUB_BLOCKS - 1
|
||||
]
|
||||
|
||||
curr: SubBlockRecord = sub_blocks[
|
||||
height_to_hash[last_sb_in_prev_epoch.height + 1]
|
||||
]
|
||||
while not curr.deficit == constants.MIN_SUB_BLOCKS_PER_CHALLENGE_BLOCK - 1:
|
||||
last_sb_in_prev_epoch = curr
|
||||
curr = sub_blocks[height_to_hash[curr.height + 1]]
|
||||
|
||||
last_sb_icp_iters = calculate_icp_iters(
|
||||
constants, last_sb_in_prev_epoch.ips, last_sb_in_prev_epoch.required_iters
|
||||
)
|
||||
last_sb_ip_iters = calculate_ip_iters(
|
||||
constants, last_sb_in_prev_epoch.ips, last_sb_in_prev_epoch.required_iters
|
||||
)
|
||||
last_sb_icp_total_iters = (
|
||||
last_sb_in_prev_epoch.total_iters - last_sb_ip_iters + last_sb_icp_iters
|
||||
)
|
||||
|
||||
# This fetches the last transaction block before the icp of the last sub-block
|
||||
# TODO: check equality
|
||||
while curr.total_iters > last_sb_icp_total_iters or not curr.is_block:
|
||||
curr = sub_blocks[curr.prev_hash]
|
||||
|
||||
prev_slot_start_timestamp = curr.timestamp
|
||||
prev_slot_start_weight = curr.weight
|
||||
|
||||
# Ensure we get a block for the last block as well
|
||||
last_block_curr = sub_block
|
||||
while not last_block_curr.is_block:
|
||||
# Ensure we get a block for the last block as well, and that it is before the signage point
|
||||
last_block_curr = prev_sb
|
||||
while last_block_curr.total_iters > signage_point_total_iters or not last_block_curr.is_block:
|
||||
last_block_curr = sub_blocks[last_block_curr.prev_hash]
|
||||
|
||||
actual_epoch_time = last_block_curr.timestamp - prev_slot_start_timestamp
|
||||
actual_epoch_time = last_block_curr.timestamp - last_block_prev.timestamp
|
||||
old_difficulty = get_difficulty(constants, sub_blocks, last_block_curr)
|
||||
|
||||
# Terms are rearranged so there is only one division.
|
||||
new_difficulty_precise = (
|
||||
(last_block_curr.weight - prev_slot_start_weight)
|
||||
(last_block_curr.weight - last_block_prev.weight)
|
||||
* constants.SLOT_TIME_TARGET
|
||||
// (constants.SLOT_SUB_BLOCKS_TARGET * actual_epoch_time)
|
||||
)
|
||||
|
|
|
@ -9,7 +9,7 @@ from chiapos import Verifier
|
|||
import dataclasses
|
||||
|
||||
from src.consensus.constants import ConsensusConstants
|
||||
from src.consensus.pot_iterations import calculate_icp_iters, calculate_ip_iters, is_overflow_sub_block
|
||||
from src.consensus.pot_iterations import calculate_sp_iters, calculate_ip_iters, is_overflow_sub_block
|
||||
from src.full_node.block_store import BlockStore
|
||||
from src.full_node.blockchain import Blockchain, ReceiveBlockResult
|
||||
from src.full_node.coin_store import CoinStore
|
||||
|
@ -800,7 +800,7 @@ class FullNode:
|
|||
|
||||
peak: Optional[SubBlockRecord] = self.blockchain.get_peak()
|
||||
if peak is not None:
|
||||
peak_icp = calculate_icp_iters(self.constants, peak.ips, peak.required_iters)
|
||||
peak_icp = calculate_sp_iters(self.constants, peak.ips, peak.required_iters)
|
||||
peak_ip_iters = calculate_ip_iters(self.constants, peak.ips, peak.required_iters)
|
||||
icp_iters = peak.total_iters - (peak_ip_iters - peak_icp)
|
||||
if block.total_iters < icp_iters:
|
||||
|
|
|
@ -24,8 +24,7 @@ from src.consensus.constants import ConsensusConstants
|
|||
from src.consensus.pot_iterations import (
|
||||
calculate_infusion_point_iters,
|
||||
calculate_iterations_quality,
|
||||
calculate_infusion_challenge_point_iters,
|
||||
calculate_min_iters_from_iterations,
|
||||
calculate_sp_iters,
|
||||
is_overflow_sub_block,
|
||||
)
|
||||
from src.full_node.difficulty_adjustment import (
|
||||
|
@ -411,7 +410,7 @@ class BlockTools:
|
|||
self.quality, self.curr_proof_of_space.size, test_constants.DIFFICULTY_STARTING
|
||||
)
|
||||
|
||||
icp_iters: uint64 = calculate_icp_iters(test_constants, uint64(test_constants.IPS_STARTING), required_iters)
|
||||
icp_iters: uint64 = calculate_sp_iters(test_constants, uint64(test_constants.IPS_STARTING), required_iters)
|
||||
|
||||
ip_iters: uint64 = calculate_ip_iters(test_constants, uint64(test_constants.IPS_STARTING), required_iters)
|
||||
number_iters: uint64 = pot_iterations.calculate_iterations(
|
||||
|
@ -539,7 +538,7 @@ class BlockTools:
|
|||
number_iters, std_hash(new_slot), cc_vdf_input, test_constants
|
||||
)
|
||||
|
||||
icp_iters: uint64 = calculate_icp_iters(test_constants, self.ips, required_iters)
|
||||
icp_iters: uint64 = calculate_sp_iters(test_constants, self.ips, required_iters)
|
||||
ip_iters: uint64 = calculate_ip_iters(test_constants, self.ips, required_iters)
|
||||
|
||||
cc_sp_vdf: VDFInfo = get_challenge_chain_sp_vdf(cc_vdf_challenge, icp_iters, cc_vdf_input, cc_sp_output)
|
||||
|
|
|
@ -2,7 +2,7 @@ from src.consensus.pot_iterations import (
|
|||
calculate_iterations_quality,
|
||||
is_overflow_sub_block,
|
||||
calculate_slot_iters,
|
||||
calculate_icp_iters,
|
||||
calculate_sp_iters,
|
||||
calculate_ip_iters,
|
||||
)
|
||||
from src.consensus.pos_quality import _expected_plot_size
|
||||
|
@ -23,123 +23,72 @@ test_constants = DEFAULT_CONSTANTS.replace(
|
|||
class TestPotIterations:
|
||||
def test_calculate_slot_iters(self):
|
||||
ips: uint64 = uint64(100001)
|
||||
assert (
|
||||
calculate_slot_iters(test_constants, ips)
|
||||
== test_constants.SLOT_TIME_TARGET * ips
|
||||
)
|
||||
assert calculate_slot_iters(test_constants, ips) == test_constants.SLOT_TIME_TARGET * ips
|
||||
|
||||
def test_is_overflow_sub_block(self):
|
||||
ips: uint64 = uint64(100001)
|
||||
with raises(ValueError):
|
||||
assert is_overflow_sub_block(
|
||||
test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips + 1)
|
||||
)
|
||||
assert is_overflow_sub_block(test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips + 1))
|
||||
with raises(ValueError):
|
||||
assert is_overflow_sub_block(
|
||||
test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips)
|
||||
)
|
||||
assert is_overflow_sub_block(test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips))
|
||||
|
||||
assert is_overflow_sub_block(
|
||||
test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips - 1)
|
||||
)
|
||||
assert is_overflow_sub_block(test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips - 1))
|
||||
|
||||
assert is_overflow_sub_block(
|
||||
constants,
|
||||
ips,
|
||||
uint64(
|
||||
test_constants.SLOT_TIME_TARGET * ips
|
||||
- int(test_constants.EXTRA_ITERS_TIME_TARGET * ips)
|
||||
),
|
||||
uint64(test_constants.SLOT_TIME_TARGET * ips - int(test_constants.EXTRA_ITERS_TIME_TARGET * ips)),
|
||||
)
|
||||
assert not is_overflow_sub_block(
|
||||
constants,
|
||||
ips,
|
||||
uint64(
|
||||
test_constants.SLOT_TIME_TARGET * ips
|
||||
- int(test_constants.EXTRA_ITERS_TIME_TARGET * ips)
|
||||
- 1
|
||||
),
|
||||
uint64(test_constants.SLOT_TIME_TARGET * ips - int(test_constants.EXTRA_ITERS_TIME_TARGET * ips) - 1),
|
||||
)
|
||||
assert not is_overflow_sub_block(constants, ips, uint64(0))
|
||||
|
||||
def test_calculate_icp_iters(self):
|
||||
def test_calculate_sp_iters(self):
|
||||
ips: uint64 = uint64(100001)
|
||||
with raises(ValueError):
|
||||
calculate_icp_iters(
|
||||
test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips + 1)
|
||||
)
|
||||
calculate_sp_iters(test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips + 1))
|
||||
with raises(ValueError):
|
||||
calculate_icp_iters(
|
||||
test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips)
|
||||
)
|
||||
calculate_sp_iters(test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips))
|
||||
one_checkpoint_iters = test_constants.SLOT_TIME_TARGET * ips // 32
|
||||
assert (
|
||||
calculate_icp_iters(
|
||||
test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips - 1)
|
||||
)
|
||||
calculate_sp_iters(test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips - 1))
|
||||
== 31 * one_checkpoint_iters
|
||||
)
|
||||
assert calculate_sp_iters(test_constants, ips, uint64(20 * one_checkpoint_iters)) == 20 * one_checkpoint_iters
|
||||
assert (
|
||||
calculate_icp_iters(test_constants, ips, uint64(20 * one_checkpoint_iters))
|
||||
== 20 * one_checkpoint_iters
|
||||
calculate_sp_iters(test_constants, ips, uint64(20 * one_checkpoint_iters) - 1) == 19 * one_checkpoint_iters
|
||||
)
|
||||
assert (
|
||||
calculate_icp_iters(
|
||||
test_constants, ips, uint64(20 * one_checkpoint_iters) - 1
|
||||
)
|
||||
== 19 * one_checkpoint_iters
|
||||
)
|
||||
assert (
|
||||
calculate_icp_iters(
|
||||
test_constants, ips, uint64(20 * one_checkpoint_iters) + 1
|
||||
)
|
||||
== 20 * one_checkpoint_iters
|
||||
)
|
||||
assert (
|
||||
calculate_icp_iters(test_constants, ips, uint64(1))
|
||||
== 0 * one_checkpoint_iters
|
||||
)
|
||||
assert (
|
||||
calculate_icp_iters(test_constants, ips, uint64(0))
|
||||
== 0 * one_checkpoint_iters
|
||||
calculate_sp_iters(test_constants, ips, uint64(20 * one_checkpoint_iters) + 1) == 20 * one_checkpoint_iters
|
||||
)
|
||||
assert calculate_sp_iters(test_constants, ips, uint64(1)) == 0 * one_checkpoint_iters
|
||||
assert calculate_sp_iters(test_constants, ips, uint64(0)) == 0 * one_checkpoint_iters
|
||||
|
||||
def test_calculate_ip_iters(self):
|
||||
ips: uint64 = uint64(100001)
|
||||
extra_iters = int(test_constants.EXTRA_ITERS_TIME_TARGET * int(ips))
|
||||
one_checkpoint_iters = (
|
||||
test_constants.SLOT_TIME_TARGET
|
||||
* ips
|
||||
// test_constants.NUM_CHECKPOINTS_PER_SLOT
|
||||
)
|
||||
one_checkpoint_iters = test_constants.SLOT_TIME_TARGET * ips // test_constants.NUM_CHECKPOINTS_PER_SLOT
|
||||
with raises(ValueError):
|
||||
calculate_ip_iters(
|
||||
test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips + 1)
|
||||
)
|
||||
calculate_ip_iters(test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips + 1))
|
||||
with raises(ValueError):
|
||||
calculate_ip_iters(
|
||||
test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips)
|
||||
)
|
||||
calculate_ip_iters(test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips))
|
||||
|
||||
assert calculate_ip_iters(
|
||||
test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips - 1)
|
||||
) == ((test_constants.SLOT_TIME_TARGET * ips - 1) + extra_iters) - (
|
||||
test_constants.SLOT_TIME_TARGET * ips
|
||||
)
|
||||
assert calculate_ip_iters(test_constants, ips, uint64(test_constants.SLOT_TIME_TARGET * ips - 1)) == (
|
||||
(test_constants.SLOT_TIME_TARGET * ips - 1) + extra_iters
|
||||
) - (test_constants.SLOT_TIME_TARGET * ips)
|
||||
assert (
|
||||
calculate_ip_iters(constants, ips, uint64(5 * one_checkpoint_iters))
|
||||
== 5 * one_checkpoint_iters + extra_iters
|
||||
)
|
||||
assert (
|
||||
calculate_ip_iters(
|
||||
test_constants, ips, uint64(5 * one_checkpoint_iters + 678)
|
||||
)
|
||||
calculate_ip_iters(test_constants, ips, uint64(5 * one_checkpoint_iters + 678))
|
||||
== 5 * one_checkpoint_iters + extra_iters + 678
|
||||
)
|
||||
assert (
|
||||
calculate_ip_iters(
|
||||
test_constants, ips, uint64(5 * one_checkpoint_iters - 567)
|
||||
)
|
||||
calculate_ip_iters(test_constants, ips, uint64(5 * one_checkpoint_iters - 567))
|
||||
== 5 * one_checkpoint_iters + extra_iters - 567
|
||||
)
|
||||
assert calculate_ip_iters(constants, ips, uint64(0)) == extra_iters
|
||||
|
@ -157,13 +106,9 @@ class TestPotIterations:
|
|||
uint8(35): 100,
|
||||
uint8(36): 50,
|
||||
}
|
||||
farmer_space = {
|
||||
k: _expected_plot_size(uint8(k)) * count for k, count in farmer_ks.items()
|
||||
}
|
||||
farmer_space = {k: _expected_plot_size(uint8(k)) * count for k, count in farmer_ks.items()}
|
||||
total_space = sum(farmer_space.values())
|
||||
percentage_space = {
|
||||
k: float(sp / total_space) for k, sp in farmer_space.items()
|
||||
}
|
||||
percentage_space = {k: float(sp / total_space) for k, sp in farmer_space.items()}
|
||||
wins = {k: 0 for k in farmer_ks.keys()}
|
||||
total_slots = 500
|
||||
slot_iters = uint64(100000000)
|
||||
|
@ -173,9 +118,7 @@ class TestPotIterations:
|
|||
total_wins_in_slot = 0
|
||||
for k, count in farmer_ks.items():
|
||||
for farmer_index in range(count):
|
||||
quality = std_hash(
|
||||
slot_index.to_bytes(32, "big") + bytes(farmer_index)
|
||||
)
|
||||
quality = std_hash(slot_index.to_bytes(32, "big") + bytes(farmer_index))
|
||||
required_iters = calculate_iterations_quality(
|
||||
quality,
|
||||
k,
|
||||
|
|
Loading…
Reference in New Issue