refactor difficulty adjustment and ips

This commit is contained in:
Mariano Sorgente 2020-11-03 14:39:20 +09:00 committed by Yostra
parent 092b7a288f
commit f7d0aa3213
6 changed files with 197 additions and 319 deletions

View File

@ -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)

View File

@ -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

View File

@ -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)
)

View File

@ -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:

View File

@ -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)

View File

@ -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,