mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2024-09-21 00:24:37 +03:00
Weight proof optimizations (#1221)
* sub epoch samples optimizations, tests passing * refactor * fix sample validation * fix indentation * refactor * dynamic recent chain * logs * revert dynamic recent chain * dynamic recent chain * tidy * lint * fix merge
This commit is contained in:
parent
e50d14b0a9
commit
255bbb14af
@ -95,15 +95,19 @@ class WeightProofHandler:
|
||||
if recent_chain is None:
|
||||
return None
|
||||
|
||||
weight_to_check = _get_weights_for_sampling(random.Random(tip), tip_rec.weight, recent_chain)
|
||||
summary_heights = self.blockchain.get_ses_heights()
|
||||
# use second to last ses as seed
|
||||
seed = self.blockchain.get_ses(summary_heights[-2]).get_hash()
|
||||
rng = random.Random(seed)
|
||||
weight_to_check = _get_weights_for_sampling(rng, tip_rec.weight, recent_chain)
|
||||
|
||||
prev_ses_block = await self.blockchain.get_block_record_from_db(self.blockchain.height_to_hash(uint32(0)))
|
||||
if prev_ses_block is None:
|
||||
return None
|
||||
|
||||
sub_epoch_n = uint32(0)
|
||||
sample_n = uint32(0)
|
||||
summary_heights = self.blockchain.get_ses_heights()
|
||||
for idx, ses_height in enumerate(summary_heights):
|
||||
for sub_epoch_n, ses_height in enumerate(summary_heights):
|
||||
if ses_height > tip_rec.height:
|
||||
break
|
||||
# next sub block
|
||||
@ -112,11 +116,11 @@ class WeightProofHandler:
|
||||
log.error("error while building proof")
|
||||
return None
|
||||
|
||||
log.debug(f"handle sub epoch summary {idx} at height: {ses_height} weight: {ses_block.weight}")
|
||||
log.debug(f"handle sub epoch summary {sub_epoch_n} at height: {ses_height} weight: {ses_block.weight}")
|
||||
sub_epoch_data.append(_create_sub_epoch_data(ses_block.sub_epoch_summary_included))
|
||||
|
||||
# if we have enough sub_epoch samples, dont sample
|
||||
if sub_epoch_n >= self.MAX_SAMPLES:
|
||||
if sample_n >= self.MAX_SAMPLES:
|
||||
log.debug("reached sampled sub epoch cap")
|
||||
continue
|
||||
|
||||
@ -124,14 +128,19 @@ class WeightProofHandler:
|
||||
if _sample_sub_epoch(prev_ses_block.weight, ses_block.weight, weight_to_check): # type: ignore
|
||||
segments = await self.blockchain.get_sub_epoch_challenge_segments(ses_block.height)
|
||||
if segments is None:
|
||||
segments = await self.__create_sub_epoch_segments(ses_block, prev_ses_block, uint32(idx))
|
||||
segments = await self.__create_sub_epoch_segments(ses_block, prev_ses_block, uint32(sub_epoch_n))
|
||||
if segments is None:
|
||||
log.error(f"failed while building segments for sub epoch {idx}, ses height {ses_height} ")
|
||||
log.error(
|
||||
f"failed while building segments for sub epoch {sub_epoch_n}, ses height {ses_height} "
|
||||
)
|
||||
return None
|
||||
await self.blockchain.persist_sub_epoch_challenge_segments(ses_block.height, segments)
|
||||
log.debug(f"sub epoch {sub_epoch_n} has {len(segments)} segments")
|
||||
sub_epoch_segments.extend(segments)
|
||||
sub_epoch_n = uint32(sub_epoch_n + 1)
|
||||
# choose segment
|
||||
sampled_seg_index = rng.choice(range(len(segments)))
|
||||
log.debug(f"sub epoch {sub_epoch_n} sample index {sample_n} sampled segment {sampled_seg_index} ")
|
||||
# compress other segments by deleting redundant values
|
||||
sub_epoch_segments.extend(compress_segments(sampled_seg_index, segments))
|
||||
sample_n = uint32(sample_n + 1)
|
||||
prev_ses_block = ses_block
|
||||
log.debug(f"sub_epochs: {len(sub_epoch_data)}")
|
||||
return WeightProof(sub_epoch_data, sub_epoch_segments, recent_chain)
|
||||
@ -294,7 +303,7 @@ class WeightProofHandler:
|
||||
blocks: Dict[bytes32, BlockRecord],
|
||||
first_in_sub_epoch: bool,
|
||||
) -> Tuple[Optional[List[SubSlotData]], Optional[VDFInfo]]:
|
||||
# combine cc vdfs of all reward blocks from the start of the sub slot to end
|
||||
# append cc vdfs of all reward blocks from the start of the sub slot to end
|
||||
header_block_sub_rec = blocks[header_block.header_hash]
|
||||
# find slot start
|
||||
curr_sub_rec = header_block_sub_rec
|
||||
@ -326,7 +335,22 @@ class WeightProofHandler:
|
||||
curr_icc_info = sub_slot.infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf
|
||||
sub_slots_data.append(handle_finished_slots(sub_slot, curr_icc_info))
|
||||
tmp_sub_slots_data = []
|
||||
tmp_sub_slots_data.append(self.handle_block_vdfs(curr, blocks))
|
||||
ssd = SubSlotData(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
curr.reward_chain_block.signage_point_index,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
curr.reward_chain_block.challenge_chain_ip_vdf,
|
||||
curr.reward_chain_block.infused_challenge_chain_ip_vdf,
|
||||
curr.total_iters,
|
||||
)
|
||||
tmp_sub_slots_data.append(ssd)
|
||||
curr = header_blocks[self.blockchain.height_to_hash(uint32(curr.height + 1))]
|
||||
|
||||
if len(tmp_sub_slots_data) > 0:
|
||||
@ -375,8 +399,6 @@ class WeightProofHandler:
|
||||
tmp_sub_slots_data.append(self.handle_block_vdfs(curr, blocks))
|
||||
|
||||
curr = header_blocks[self.blockchain.height_to_hash(uint32(curr.height + 1))]
|
||||
if len(tmp_sub_slots_data) > 0:
|
||||
sub_slots_data.extend(tmp_sub_slots_data)
|
||||
log.debug(f"slot end vdf end height {curr.height} slots {len(sub_slots_data)} ")
|
||||
return sub_slots_data, curr.height
|
||||
|
||||
@ -440,11 +462,13 @@ class WeightProofHandler:
|
||||
return False, uint32(0)
|
||||
constants, summary_bytes, wp_bytes = vars_to_bytes(self.constants, summaries, weight_proof)
|
||||
log.info("validate sub epoch challenge segments")
|
||||
if not self.validate_sub_epoch_sampling(sub_epoch_weight_list, weight_proof):
|
||||
seed = summaries[-2].get_hash()
|
||||
rng = random.Random(seed)
|
||||
if not validate_sub_epoch_sampling(rng, sub_epoch_weight_list, weight_proof):
|
||||
log.error("failed weight proof sub epoch sample validation")
|
||||
return False, uint32(0)
|
||||
|
||||
if not _validate_segments(constants, wp_bytes, summary_bytes):
|
||||
if not _validate_sub_epoch_segments(constants, rng, wp_bytes, summary_bytes):
|
||||
return False, uint32(0)
|
||||
log.info("validate weight proof recent blocks")
|
||||
if not _validate_recent_blocks(constants, wp_bytes, summary_bytes):
|
||||
@ -465,14 +489,16 @@ class WeightProofHandler:
|
||||
log.error("weight proof failed sub epoch data validation")
|
||||
return False, uint32(0)
|
||||
|
||||
if not self.validate_sub_epoch_sampling(sub_epoch_weight_list, weight_proof):
|
||||
seed = summaries[-2].get_hash()
|
||||
rng = random.Random(seed)
|
||||
if not validate_sub_epoch_sampling(rng, sub_epoch_weight_list, weight_proof):
|
||||
log.error("failed weight proof sub epoch sample validation")
|
||||
return False, uint32(0)
|
||||
|
||||
executor = ProcessPoolExecutor(1)
|
||||
constants, summary_bytes, wp_bytes = vars_to_bytes(self.constants, summaries, weight_proof)
|
||||
segment_validation_task = asyncio.get_running_loop().run_in_executor(
|
||||
executor, _validate_segments, constants, wp_bytes, summary_bytes
|
||||
executor, _validate_sub_epoch_segments, constants, rng, wp_bytes, summary_bytes
|
||||
)
|
||||
|
||||
recent_blocks_validation_task = asyncio.get_running_loop().run_in_executor(
|
||||
@ -493,27 +519,6 @@ class WeightProofHandler:
|
||||
|
||||
return True, self.get_fork_point(summaries)
|
||||
|
||||
def validate_sub_epoch_sampling(self, sub_epoch_weight_list, weight_proof):
|
||||
tip = weight_proof.recent_chain_data[-1]
|
||||
weight_to_check = _get_weights_for_sampling(
|
||||
random.Random(tip.header_hash), tip.weight, weight_proof.recent_chain_data
|
||||
)
|
||||
sampled_sub_epochs: dict[int, bool] = {}
|
||||
for idx in range(1, len(sub_epoch_weight_list)):
|
||||
if _sample_sub_epoch(sub_epoch_weight_list[idx - 1], sub_epoch_weight_list[idx], weight_to_check):
|
||||
sampled_sub_epochs[idx - 1] = True
|
||||
if len(sampled_sub_epochs) == WeightProofHandler.MAX_SAMPLES:
|
||||
break
|
||||
curr_sub_epoch_n = -1
|
||||
for sub_epoch_segment in weight_proof.sub_epoch_segments:
|
||||
if curr_sub_epoch_n < sub_epoch_segment.sub_epoch_n:
|
||||
if sub_epoch_segment.sub_epoch_n in sampled_sub_epochs:
|
||||
del sampled_sub_epochs[sub_epoch_segment.sub_epoch_n]
|
||||
curr_sub_epoch_n = sub_epoch_segment.sub_epoch_n
|
||||
if len(sampled_sub_epochs) > 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_fork_point(self, received_summaries: List[SubEpochSummary]) -> uint32:
|
||||
# iterate through sub epoch summaries to find fork point
|
||||
fork_point_index = 0
|
||||
@ -652,6 +657,68 @@ def handle_finished_slots(end_of_slot: EndOfSubSlotBundle, icc_end_of_slot_info)
|
||||
)
|
||||
|
||||
|
||||
def handle_end_of_slot(
|
||||
sub_slot: EndOfSubSlotBundle,
|
||||
eos_vdf_iters: uint64,
|
||||
):
|
||||
assert sub_slot.infused_challenge_chain
|
||||
assert sub_slot.proofs.infused_challenge_chain_slot_proof
|
||||
if sub_slot.proofs.infused_challenge_chain_slot_proof.normalized_to_identity:
|
||||
icc_info = sub_slot.infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf
|
||||
else:
|
||||
icc_info = VDFInfo(
|
||||
sub_slot.infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf.challenge,
|
||||
eos_vdf_iters,
|
||||
sub_slot.infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf.output,
|
||||
)
|
||||
if sub_slot.proofs.challenge_chain_slot_proof.normalized_to_identity:
|
||||
cc_info = sub_slot.challenge_chain.challenge_chain_end_of_slot_vdf
|
||||
else:
|
||||
cc_info = VDFInfo(
|
||||
sub_slot.challenge_chain.challenge_chain_end_of_slot_vdf.challenge,
|
||||
eos_vdf_iters,
|
||||
sub_slot.challenge_chain.challenge_chain_end_of_slot_vdf.output,
|
||||
)
|
||||
|
||||
assert sub_slot.proofs.infused_challenge_chain_slot_proof is not None
|
||||
return SubSlotData(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
sub_slot.proofs.challenge_chain_slot_proof,
|
||||
sub_slot.proofs.infused_challenge_chain_slot_proof,
|
||||
cc_info,
|
||||
icc_info,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
|
||||
|
||||
def compress_segments(full_segment_index, segments: List[SubEpochChallengeSegment]) -> List[SubEpochChallengeSegment]:
|
||||
compressed_segments = []
|
||||
compressed_segments.append(segments[0])
|
||||
for idx, segment in enumerate(segments[1:]):
|
||||
if idx != full_segment_index:
|
||||
# remove all redundant values
|
||||
segment = compress_segment(segment)
|
||||
compressed_segments.append(segment)
|
||||
return compressed_segments
|
||||
|
||||
|
||||
def compress_segment(segment: SubEpochChallengeSegment) -> SubEpochChallengeSegment:
|
||||
# find challenge slot
|
||||
comp_seg = SubEpochChallengeSegment(segment.sub_epoch_n, [], segment.rc_slot_end_info)
|
||||
for slot in segment.sub_slots:
|
||||
comp_seg.sub_slots.append(slot)
|
||||
if slot.is_challenge():
|
||||
break
|
||||
return segment
|
||||
|
||||
|
||||
# wp validation methods
|
||||
def _validate_sub_epoch_summaries(
|
||||
constants: ConsensusConstants,
|
||||
@ -670,7 +737,7 @@ def _validate_sub_epoch_summaries(
|
||||
constants.DIFFICULTY_STARTING,
|
||||
)
|
||||
|
||||
log.info(f"validating {len(summaries)} summaries")
|
||||
log.info(f"validating {len(summaries)} sub epochs")
|
||||
|
||||
# validate weight
|
||||
if not _validate_summaries_weight(constants, total, summaries, weight_proof):
|
||||
@ -722,6 +789,8 @@ def _map_sub_epoch_summaries(
|
||||
# add to dict
|
||||
summaries.append(ses)
|
||||
ses_hash = std_hash(ses)
|
||||
# add last sub epoch weight
|
||||
sub_epoch_weight_list.append(uint128(total_weight + curr_difficulty))
|
||||
return summaries, total_weight, sub_epoch_weight_list
|
||||
|
||||
|
||||
@ -738,8 +807,20 @@ def _validate_summaries_weight(constants: ConsensusConstants, sub_epoch_data_wei
|
||||
return curr.reward_chain_block.weight == sub_epoch_data_weight
|
||||
|
||||
|
||||
def _validate_segments(
|
||||
def map_segments_by_sub_epoch(sub_epoch_segments) -> Dict[int, List[SubEpochChallengeSegment]]:
|
||||
segments: Dict[int, List[SubEpochChallengeSegment]] = {}
|
||||
curr_sub_epoch_n = -1
|
||||
for idx, segment in enumerate(sub_epoch_segments):
|
||||
if curr_sub_epoch_n < segment.sub_epoch_n:
|
||||
curr_sub_epoch_n = segment.sub_epoch_n
|
||||
segments[curr_sub_epoch_n] = []
|
||||
segments[curr_sub_epoch_n].append(segment)
|
||||
return segments
|
||||
|
||||
|
||||
def _validate_sub_epoch_segments(
|
||||
constants_dict: Dict,
|
||||
rng: random.Random,
|
||||
weight_proof_bytes: bytes,
|
||||
summaries_bytes: List[bytes],
|
||||
):
|
||||
@ -748,38 +829,35 @@ def _validate_segments(
|
||||
total_blocks, total_ip_iters = 0, 0
|
||||
total_slot_iters, total_slots = 0, 0
|
||||
total_ip_iters = 0
|
||||
curr_sub_epoch_n = -1
|
||||
prev_ses: Optional[SubEpochSummary] = None
|
||||
first_sub_epoch_segment = True
|
||||
sampled_sub_epochs = 0
|
||||
for idx, segment in enumerate(weight_proof.sub_epoch_segments):
|
||||
curr_difficulty, curr_ssi = _get_curr_diff_ssi(constants, segment.sub_epoch_n, summaries)
|
||||
if curr_sub_epoch_n < segment.sub_epoch_n:
|
||||
log.info(f"validate sub epoch {segment.sub_epoch_n}")
|
||||
sampled_sub_epochs += 1
|
||||
first_sub_epoch_segment = True
|
||||
# recreate RewardChainSubSlot for next ses rc_hash
|
||||
if segment.sub_epoch_n > 0:
|
||||
rc_sub_slot = __get_rc_sub_slot(constants, segment, summaries, curr_ssi)
|
||||
prev_ses = summaries[segment.sub_epoch_n - 1]
|
||||
rc_sub_slot_hash = rc_sub_slot.get_hash()
|
||||
if not summaries[segment.sub_epoch_n].reward_chain_hash == rc_sub_slot_hash:
|
||||
log.error(f"failed reward_chain_hash validation sub_epoch {segment.sub_epoch_n}")
|
||||
return False
|
||||
log.debug(f"validate segment {idx}")
|
||||
valid_segment, ip_iters, slot_iters, slots = _validate_segment_slots(
|
||||
constants, segment, curr_ssi, curr_difficulty, prev_ses, first_sub_epoch_segment
|
||||
)
|
||||
first_sub_epoch_segment = False
|
||||
prev_ses = None
|
||||
if not valid_segment:
|
||||
log.error(f"failed to validate sub_epoch {segment.sub_epoch_n} segment {idx} slots")
|
||||
segments_by_sub_epoch = map_segments_by_sub_epoch(weight_proof.sub_epoch_segments)
|
||||
curr_ssi = constants.SUB_SLOT_ITERS_STARTING
|
||||
for sub_epoch_n, segments in segments_by_sub_epoch.items():
|
||||
prev_ssi = curr_ssi
|
||||
curr_difficulty, curr_ssi = _get_curr_diff_ssi(constants, sub_epoch_n, summaries)
|
||||
log.debug(f"validate sub epoch {sub_epoch_n}")
|
||||
# recreate RewardChainSubSlot for next ses rc_hash
|
||||
sampled_seg_index = rng.choice(range(len(segments)))
|
||||
log.debug(f"sub epoch {sub_epoch_n} has {len(segments)} segments sampled segment {sampled_seg_index}")
|
||||
if sub_epoch_n > 0:
|
||||
rc_sub_slot = __get_rc_sub_slot(constants, segments[0], summaries, curr_ssi)
|
||||
prev_ses = summaries[sub_epoch_n - 1]
|
||||
rc_sub_slot_hash = rc_sub_slot.get_hash()
|
||||
if not summaries[sub_epoch_n].reward_chain_hash == rc_sub_slot_hash:
|
||||
log.error(f"failed reward_chain_hash validation sub_epoch {sub_epoch_n}")
|
||||
return False
|
||||
total_blocks += 1
|
||||
total_slot_iters += slot_iters
|
||||
total_slots += slots
|
||||
total_ip_iters += ip_iters
|
||||
curr_sub_epoch_n = segment.sub_epoch_n
|
||||
for idx, segment in enumerate(segments):
|
||||
valid_segment, ip_iters, slot_iters, slots = _validate_segment(
|
||||
constants, segment, curr_ssi, prev_ssi, curr_difficulty, prev_ses, idx == 0, sampled_seg_index == idx
|
||||
)
|
||||
if not valid_segment:
|
||||
log.error(f"failed to validate sub_epoch {segment.sub_epoch_n} segment {idx} slots")
|
||||
return False
|
||||
prev_ses = None
|
||||
total_blocks += 1
|
||||
total_slot_iters += slot_iters
|
||||
total_slots += slots
|
||||
total_ip_iters += ip_iters
|
||||
avg_ip_iters = total_ip_iters / total_blocks
|
||||
avg_slot_iters = total_slot_iters / total_slots
|
||||
if avg_slot_iters / avg_ip_iters < float(constants.WEIGHT_PROOF_THRESHOLD):
|
||||
@ -788,19 +866,21 @@ def _validate_segments(
|
||||
return True
|
||||
|
||||
|
||||
def _validate_segment_slots(
|
||||
def _validate_segment(
|
||||
constants: ConsensusConstants,
|
||||
segment: SubEpochChallengeSegment,
|
||||
curr_ssi: uint64,
|
||||
prev_ssi: uint64,
|
||||
curr_difficulty: uint64,
|
||||
ses: Optional[SubEpochSummary],
|
||||
first_segment_in_se: bool,
|
||||
validate_sub_slots=False, # todo almog remove after bluebox integration
|
||||
sampled: bool,
|
||||
) -> Tuple[bool, int, int, int]:
|
||||
ip_iters, slot_iters, slots = 0, 0, 0
|
||||
after_challenge = False
|
||||
sub_slots_since_prev = 0
|
||||
for idx, sub_slot_data in enumerate(segment.sub_slots):
|
||||
if sub_slot_data.is_challenge():
|
||||
if sub_slot_data.is_challenge() and sampled:
|
||||
after_challenge = True
|
||||
required_iters = __validate_pospace(constants, segment, idx, curr_difficulty, ses, first_segment_in_se)
|
||||
if required_iters is None:
|
||||
@ -812,12 +892,17 @@ def _validate_segment_slots(
|
||||
if not _validate_challenge_block_vdfs(constants, idx, segment.sub_slots, curr_ssi):
|
||||
log.error(f"failed to validate challenge slot {idx} vdfs")
|
||||
return False, uint64(0), uint64(0), uint64(0)
|
||||
elif validate_sub_slots and after_challenge:
|
||||
elif after_challenge and sampled:
|
||||
if not _validate_sub_slot_data(constants, idx, segment.sub_slots, curr_ssi):
|
||||
log.error(f"failed to validate sub slot data {idx} vdfs")
|
||||
return False, uint64(0), uint64(0), uint64(0)
|
||||
slot_iters = slot_iters + curr_ssi # type: ignore
|
||||
slots = slots + uint64(1) # type: ignore
|
||||
if not sub_slot_data.is_end_of_slot():
|
||||
sub_slots_since_prev = 0
|
||||
else:
|
||||
sub_slots_since_prev += 1
|
||||
slot_iters = slot_iters + curr_ssi # type: ignore
|
||||
slots = slots + uint64(1) # type: ignore
|
||||
log.debug(f"slot iters {slot_iters} slots {slots}")
|
||||
return True, ip_iters, slot_iters, slots
|
||||
|
||||
|
||||
@ -880,7 +965,6 @@ def _validate_sub_slot_data(
|
||||
if not sub_slot_data.icc_slot_end.is_valid(constants, input, sub_slot_data.icc_slot_end_info, None):
|
||||
log.error(f"failed icc slot end validation {sub_slot_data.icc_slot_end_info} ")
|
||||
return False
|
||||
assert sub_slot_data.cc_slot_end_info is not None
|
||||
assert sub_slot_data.cc_slot_end_info
|
||||
assert sub_slot_data.cc_slot_end
|
||||
input = ClassgroupElement.get_default_element()
|
||||
@ -1305,47 +1389,6 @@ def _get_ses_idx(recent_reward_chain: List[HeaderBlock]) -> List[int]:
|
||||
return idxs
|
||||
|
||||
|
||||
def handle_end_of_slot(
|
||||
sub_slot: EndOfSubSlotBundle,
|
||||
eos_vdf_iters: uint64,
|
||||
):
|
||||
assert sub_slot.infused_challenge_chain
|
||||
assert sub_slot.proofs.infused_challenge_chain_slot_proof
|
||||
if sub_slot.proofs.infused_challenge_chain_slot_proof.normalized_to_identity:
|
||||
icc_info = sub_slot.infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf
|
||||
else:
|
||||
icc_info = VDFInfo(
|
||||
sub_slot.infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf.challenge,
|
||||
eos_vdf_iters,
|
||||
sub_slot.infused_challenge_chain.infused_challenge_chain_end_of_slot_vdf.output,
|
||||
)
|
||||
if sub_slot.proofs.challenge_chain_slot_proof.normalized_to_identity:
|
||||
cc_info = sub_slot.challenge_chain.challenge_chain_end_of_slot_vdf
|
||||
else:
|
||||
cc_info = VDFInfo(
|
||||
sub_slot.challenge_chain.challenge_chain_end_of_slot_vdf.challenge,
|
||||
eos_vdf_iters,
|
||||
sub_slot.challenge_chain.challenge_chain_end_of_slot_vdf.output,
|
||||
)
|
||||
|
||||
assert sub_slot.proofs.infused_challenge_chain_slot_proof is not None
|
||||
return SubSlotData(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
sub_slot.proofs.challenge_chain_slot_proof,
|
||||
sub_slot.proofs.infused_challenge_chain_slot_proof,
|
||||
cc_info,
|
||||
icc_info,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
|
||||
|
||||
def get_deficit(
|
||||
constants: ConsensusConstants,
|
||||
curr_deficit: uint8,
|
||||
@ -1381,3 +1424,53 @@ def blue_boxed_end_of_slot(sub_slot: EndOfSubSlotBundle):
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def validate_sub_epoch_sampling(rng, sub_epoch_weight_list, weight_proof):
|
||||
tip = weight_proof.recent_chain_data[-1]
|
||||
weight_to_check = _get_weights_for_sampling(rng, tip.weight, weight_proof.recent_chain_data)
|
||||
sampled_sub_epochs: dict[int, bool] = {}
|
||||
for idx in range(1, len(sub_epoch_weight_list)):
|
||||
if _sample_sub_epoch(sub_epoch_weight_list[idx - 1], sub_epoch_weight_list[idx], weight_to_check):
|
||||
sampled_sub_epochs[idx - 1] = True
|
||||
if len(sampled_sub_epochs) == WeightProofHandler.MAX_SAMPLES:
|
||||
break
|
||||
curr_sub_epoch_n = -1
|
||||
for sub_epoch_segment in weight_proof.sub_epoch_segments:
|
||||
if curr_sub_epoch_n < sub_epoch_segment.sub_epoch_n:
|
||||
if sub_epoch_segment.sub_epoch_n in sampled_sub_epochs:
|
||||
del sampled_sub_epochs[sub_epoch_segment.sub_epoch_n]
|
||||
curr_sub_epoch_n = sub_epoch_segment.sub_epoch_n
|
||||
if len(sampled_sub_epochs) > 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def validate_total_iters(
|
||||
segment: SubEpochChallengeSegment,
|
||||
sub_slot_data_idx,
|
||||
expected_sub_slot_iters: uint64,
|
||||
finished_sub_slots_since_prev: int,
|
||||
prev_b: SubSlotData,
|
||||
prev_sub_slot_data_iters,
|
||||
genesis,
|
||||
) -> bool:
|
||||
sub_slot_data = segment.sub_slots[sub_slot_data_idx]
|
||||
if genesis:
|
||||
total_iters: uint128 = uint128(expected_sub_slot_iters * finished_sub_slots_since_prev)
|
||||
elif segment.sub_slots[sub_slot_data_idx - 1].is_end_of_slot():
|
||||
assert prev_b.total_iters
|
||||
total_iters = prev_b.total_iters
|
||||
# Add the rest of the slot of prev_b
|
||||
assert prev_b.cc_ip_vdf_info
|
||||
total_iters = uint128(total_iters + prev_sub_slot_data_iters - prev_b.cc_ip_vdf_info.number_of_iterations)
|
||||
# Add other empty slots
|
||||
total_iters = uint128(total_iters + (expected_sub_slot_iters * (finished_sub_slots_since_prev - 1)))
|
||||
else:
|
||||
# Slot iters is guaranteed to be the same for header_block and prev_b
|
||||
# This takes the beginning of the slot, and adds ip_iters
|
||||
assert prev_b.total_iters
|
||||
assert prev_b.cc_ip_vdf_info
|
||||
total_iters = uint128(prev_b.total_iters - prev_b.cc_ip_vdf_info.number_of_iterations)
|
||||
total_iters = uint128(total_iters + sub_slot_data.cc_ip_vdf_info.number_of_iterations)
|
||||
return total_iters == sub_slot_data.total_iters
|
||||
|
@ -1,13 +1,16 @@
|
||||
# flake8: noqa: F811, F401
|
||||
import asyncio
|
||||
|
||||
import sys
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
from typing import Dict, Optional, List, Tuple
|
||||
|
||||
import aiosqlite
|
||||
import pytest
|
||||
|
||||
from src.consensus.block_header_validation import validate_finished_header_block
|
||||
from src.consensus.block_record import BlockRecord
|
||||
from src.consensus.default_constants import DEFAULT_CONSTANTS
|
||||
from src.consensus.difficulty_adjustment import get_next_sub_slot_iters_and_difficulty
|
||||
from src.consensus.full_block_to_block_record import block_to_block_record
|
||||
from src.full_node.block_store import BlockStore
|
||||
from src.types.blockchain_format.sized_bytes import bytes32
|
||||
@ -23,18 +26,19 @@ except ImportError:
|
||||
|
||||
|
||||
from src.consensus.pot_iterations import calculate_iterations_quality
|
||||
|
||||
from src.full_node.weight_proof import ( # type: ignore
|
||||
WeightProofHandler,
|
||||
_map_sub_epoch_summaries,
|
||||
_validate_segment_slots,
|
||||
_validate_summaries_weight,
|
||||
_validate_segment,
|
||||
)
|
||||
from src.types.full_block import FullBlock
|
||||
from src.types.header_block import HeaderBlock
|
||||
from src.util.ints import uint32, uint64
|
||||
from tests.core.fixtures import (
|
||||
default_400_blocks,
|
||||
default_1000_blocks,
|
||||
default_400_blocks,
|
||||
default_10000_blocks,
|
||||
default_10000_blocks_compact,
|
||||
pre_genesis_empty_slots_1000_blocks,
|
||||
@ -149,12 +153,7 @@ async def _test_map_summaries(blocks, header_cache, height_to_hash, sub_blocks,
|
||||
|
||||
class TestWeightProof:
|
||||
@pytest.mark.asyncio
|
||||
async def test_weight_proof_map_summaries_1(self, default_400_blocks):
|
||||
header_cache, height_to_hash, sub_blocks, summaries = await load_blocks_dont_validate(default_400_blocks)
|
||||
await _test_map_summaries(default_400_blocks, header_cache, height_to_hash, sub_blocks, summaries)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_weight_proof_map_summaries_2(self, default_1000_blocks):
|
||||
async def test_weight_proof_map_summaries_1(self, default_1000_blocks):
|
||||
header_cache, height_to_hash, sub_blocks, summaries = await load_blocks_dont_validate(default_1000_blocks)
|
||||
await _test_map_summaries(default_1000_blocks, header_cache, height_to_hash, sub_blocks, summaries)
|
||||
|
||||
@ -211,6 +210,14 @@ class TestWeightProof:
|
||||
header_cache, height_to_hash, sub_blocks, summaries = await load_blocks_dont_validate(blocks)
|
||||
|
||||
wpf = WeightProofHandler(test_constants, BlockCache(sub_blocks, header_cache, height_to_hash, summaries))
|
||||
blockchain = BlockCache(sub_blocks, header_cache, height_to_hash, summaries)
|
||||
header = header_cache[height_to_hash[1]]
|
||||
prev_b = blockchain.block_record(header.prev_header_hash)
|
||||
sub_slot_iters, difficulty = get_next_sub_slot_iters_and_difficulty(
|
||||
test_constants, len(header.finished_sub_slots) > 0, prev_b, blockchain
|
||||
)
|
||||
validate_finished_header_block(test_constants, blockchain, header, False, difficulty, sub_slot_iters, False)
|
||||
|
||||
wp = await wpf.get_proof_of_weight(blocks[-1].header_hash)
|
||||
assert wp is not None
|
||||
wpf = WeightProofHandler(test_constants, BlockCache(sub_blocks, header_cache, height_to_hash, {}))
|
||||
|
Loading…
Reference in New Issue
Block a user