Difficulty adjustment fix

This commit is contained in:
Mariano Sorgente 2020-11-09 15:08:15 +09:00 committed by Yostra
parent 88e49d6230
commit e463782b6f
6 changed files with 40 additions and 41 deletions

View File

@ -340,17 +340,15 @@ async def validate_unfinished_header_block(
)
if expected_sub_epoch_summary.get_hash() != ses_hash:
return None, Err.INVALID_SUB_EPOCH_SUMMARY
print(f"Verified sub epoch: {expected_sub_epoch_summary}")
else:
# 3d. Check that we don't have to include a sub-epoch summary
if new_sub_slot and not genesis_block:
print("New sub slot")
finishes = finishes_sub_epoch(
constants, prev_sb.height, prev_sb.deficit, False, sub_blocks, prev_sb.prev_hash
)
if finishes:
return None, Err.INVALID_SUB_EPOCH_SUMMARY
else:
print("Not new sub-slot")
# 4. Check proof of space
if header_block.reward_chain_sub_block.challenge_chain_sp_vdf is None:
@ -680,7 +678,7 @@ async def validate_unfinished_header_block(
# For blocks 1 to 10, average timestamps of all previous blocks
assert curr_sb.height == 0
prev_time: uint64 = uint64(int(sum(last_timestamps) // len(last_timestamps)))
if header_block.foliage_block.timestamp < prev_time:
if header_block.foliage_block.timestamp <= prev_time:
return None, Err.TIMESTAMP_TOO_FAR_IN_PAST
if header_block.foliage_block.timestamp > int(time.time() + constants.MAX_FUTURE_TIME):
return None, Err.TIMESTAMP_TOO_FAR_IN_FUTURE

View File

@ -49,8 +49,10 @@ def _get_last_block_in_previous_epoch(
# 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:
# TODO: check edge cases here
height_epoch_surpass: uint32 = next_height - (next_height % constants.EPOCH_SUB_BLOCKS)
height_prev_epoch_surpass: uint32 = height_epoch_surpass - constants.EPOCH_SUB_BLOCKS
if (next_height - 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
@ -59,24 +61,27 @@ def _get_last_block_in_previous_epoch(
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]
if height_prev_epoch_surpass == 0:
# The genesis block is an edge case, where we measure from the first block in epoch (height 0), as opposed to
# the last sub-block in the previous epoch, which would be height -1
return _get_blocks_at_height(height_to_hash, sub_blocks, prev_sb, uint32(0))[0]
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(height_prev_epoch_surpass - 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
fetched_index: int = constants.MAX_SLOT_SUB_BLOCKS
last_sb_in_slot: SubBlockRecord = fetched_blocks[fetched_index]
fetched_index += 1
assert last_sb_in_slot.height == height_prev_epoch_surpass - 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:
# Note that there are no overflow blocks at the start of new epochs
while not curr.is_challenge_sub_block(constants):
last_sb_in_slot = curr
curr = fetched_blocks[fetched_index]
fetched_index += 1
@ -130,7 +135,7 @@ def finishes_sub_epoch(
# For checking new epoch, make sure the epoch sub blocks are aligned
if also_finishes_epoch:
if height + 1 % constants.EPOCH_SUB_BLOCKS > constants.MAX_SLOT_SUB_BLOCKS:
if (height + 1) % constants.EPOCH_SUB_BLOCKS > constants.MAX_SLOT_SUB_BLOCKS:
return False
return True
@ -138,8 +143,8 @@ def finishes_sub_epoch(
def get_next_ips(
constants: ConsensusConstants,
height_to_hash: Dict[uint32, bytes32],
sub_blocks: Dict[bytes32, SubBlockRecord],
height_to_hash: Dict[uint32, bytes32],
prev_header_hash: bytes32,
height: uint32,
deficit: uint8,
@ -200,21 +205,6 @@ def get_next_ips(
return max([uint64(1), new_ips, min_ips])
def get_difficulty(
constants: ConsensusConstants,
sub_blocks: Dict[bytes32, SubBlockRecord],
header_hash: bytes32,
) -> uint64:
"""
Returns the difficulty of the sub-block referred to by header_hash
"""
sub_block = sub_blocks[header_hash]
if sub_block.height == 0:
return uint64(constants.DIFFICULTY_STARTING)
return uint64(sub_block.weight - sub_blocks[sub_block.prev_hash].weight)
def get_next_difficulty(
constants: ConsensusConstants,
sub_blocks: Dict[bytes32, SubBlockRecord],
@ -256,7 +246,7 @@ def get_next_difficulty(
last_block_curr = sub_blocks[last_block_curr.prev_hash]
actual_epoch_time = last_block_curr.timestamp - last_block_prev.timestamp
old_difficulty = get_difficulty(constants, sub_blocks, last_block_curr)
old_difficulty = uint64(prev_sb.weight - sub_blocks[prev_sb.prev_hash].weight)
# Terms are rearranged so there is only one division.
new_difficulty_precise = (

View File

@ -857,8 +857,8 @@ class FullNode:
prev_sp_iters = calculate_sp_iters(self.constants, prev_sb.ips, prev_sb.required_iters)
ips = get_next_ips(
self.constants,
self.blockchain.height_to_hash,
self.blockchain.sub_blocks,
self.blockchain.height_to_hash,
block.prev_header_hash,
prev_sb.height,
prev_sb.deficit,

View File

@ -214,14 +214,14 @@ class BlockTools:
fees: uint64 = uint64(0),
transaction_data_at_height: Dict[int, Tuple[Program, G2Element]] = None,
seed: bytes = b"",
timestamp: Optional[uint64] = None,
time_per_sub_block: Optional[float] = None,
force_overflow: bool = False,
force_empty_slots: uint32 = uint32(0), # Force at least this number of empty slots before the first SB
) -> List[FullBlock]:
if transaction_data_at_height is None:
transaction_data_at_height = {}
if timestamp is None:
timestamp = time.time()
if time_per_sub_block is None:
time_per_sub_block = constants.SLOT_TIME_TARGET / constants.SLOT_SUB_BLOCKS_TARGET
if block_list is None or len(block_list) == 0:
genesis = self.create_genesis_block(
constants,
@ -229,6 +229,7 @@ class BlockTools:
seed,
force_overflow=force_overflow,
force_empty_slots=force_empty_slots,
timestamp=uint64(int(time.time())),
)
block_list = [genesis]
num_blocks -= 1
@ -239,6 +240,11 @@ class BlockTools:
height_to_hash, difficulty, sub_blocks = load_block_list(block_list, constants)
latest_sub_block: SubBlockRecord = sub_blocks[block_list[-1].header_hash]
curr = latest_sub_block
while not curr.is_block:
curr = sub_blocks[curr.prev_hash]
start_timestamp = curr.timestamp
start_height = curr.height
curr = latest_sub_block
while not curr.first_in_sub_slot:
@ -279,6 +285,7 @@ class BlockTools:
if force_overflow and not is_overflow_block:
continue
assert latest_sub_block.header_hash in sub_blocks
unfinished_block = self.create_unfinished_block(
constants,
sub_slot_start_total_iters,
@ -292,7 +299,9 @@ class BlockTools:
farmer_reward_puzzle_hash,
pool_reward_puzzle_hash,
fees,
timestamp,
uint64(
start_timestamp + int((latest_sub_block.height + 1 - start_height) * time_per_sub_block)
),
seed,
transaction_data_at_height.get(latest_sub_block.height + 1, None),
latest_sub_block,

View File

@ -92,12 +92,14 @@ class TestGenesisBlock:
class TestAddingMoreBlocks:
@pytest.mark.asyncio
async def test_non_genesis(self, empty_blockchain):
blocks = bt.get_consecutive_blocks(test_constants, 150, force_overflow=False, force_empty_slots=0)
blocks = bt.get_consecutive_blocks(test_constants, 500, force_overflow=False, force_empty_slots=0)
for block in blocks:
result, err, _ = await empty_blockchain.receive_block(block)
assert err is None
assert result == ReceiveBlockResult.NEW_PEAK
print(f"Added block {block.height}")
print(
f"Added block {block.height} total iters {block.total_iters} new slot? {len(block.finished_sub_slots)}"
)
assert empty_blockchain.get_peak().height == len(blocks) - 1

View File

@ -29,8 +29,8 @@ test_constants = constants.replace(
**{
"DIFFICULTY_STARTING": 1,
"DISCRIMINANT_SIZE_BITS": 32,
"SUB_EPOCH_SUB_BLOCKS": 128,
"EPOCH_SUB_BLOCKS": 512,
"SUB_EPOCH_SUB_BLOCKS": 70,
"EPOCH_SUB_BLOCKS": 140,
"IPS_STARTING": 10 * 1,
"NUMBER_ZERO_BITS_PLOT_FILTER": 1, # H(plot signature of the challenge) must start with these many zeroes
"NUMBER_ZERO_BITS_SP_FILTER": 1, # H(plot signature of the challenge) must start with these many zeroes